Myke's Home Page

Book CD-ROM Home

File Copying/Harddrive Setup

Development Tools

Experiments

Projects

Useful Code Snippets and Macros

Introduction to Electronics

Introduction to Programming

Datasheets

PCBs

Links

McGraw-Hill Professional Publishing

Useful Snippets and Macros

Here are a collection of code Snippets and Macros that can be used in your applications to solve the various problems that you will encounter. Many of these "snippets" of code are so clever that I shake my head each time I see them. I want to thank everybody that has contributed these code items as they have made my life easier quite a few times.

If you have some that you think should be included, please send an email to me and I will add them to my web page as well as add them to this CD-ROM in later editions of the book.

Note that these pieces of code can be "cut and pasted" into your applications.

1. Sleep Instruction Sequence 7. PIC16F84 EEPROM Access
2. Accessing the "TRIS" and "OPTION_REG" Registers in the Mid-Range PICmicro MCUs 8. PIC16F87X EEPROM Access
3. Accessing all pins in the PIC12C50X and PIC16C505 Devices 9. PIC16F87X program memory Flash Access
4. Accessing all pins in the PIC12C67X Devices 10. USART Operation
5. PICmicro MCU Interrupt Handler Skeletons 11. Reading a value using the ADC
6. "Safe" TMR0 Polling Loop 12. PIC17CXX Program Memory Read/Write

1. Sleep Instruction Sequence

Any time the "sleep" instruction is used, it should be followed by a "nop" as shown below. The reason for doing this is to avoid any potential problems when the PICmicro is "awakened" and continues executing. Anytime the PICmicro® MCU restarts from sleep, the next instruction is always executed - even if the restart was caused by an interrupt request. The "nop" will ensure that no incorrect instruction is inadvertently executed before the interrupt handler is invoked.

  sleep                   ;  Put the PICmicro into a low-power mode
  nop                     ;  Provide "nop" to make sure no inadvertent 
                          ;   Instruction Executed
              

2. Accessing the "TRIS" and "OPTION_REG" Registers in the Mid-Range PICmicro MCUs

This is an action that many people have trouble understanding. In the mid-range PICmicro MCUs, the "TRIS" and "OPTION_REG" registers (along with some other special purpose registers) are located in "Bank 1" and can only be accessed when the eight bit address is in the FSR register (and the "INDF" register is accessed) or when the "RP0" bit of "STATUS" is set. If the latter method is used, then the most significant bit of the eight bit address should be inverted to avoid MPASM assembler messages. The "TRIS" and "OPTION" instructions should not be used in the mid-range PICmicro devices.

To access Bank 1 Registers using the FSR register:

  movlw  Reg              ;  Load FSR with the Register Address
  movf   INDF, w          ;  Access the Register with "movf" or "movwf"
                          ;   Instruction Executed
              

To access Bank 1 Registers by selecting the correct Bank:

  bsf    STATUS, RP0      ;  Execute in "Bank 1"
  movf   Reg ^ 0x080, w   ;  Access the register with bit 7 inverted
  bcf    STATUS, RP0      ;  Return to "Bank 0"
              

I recommend that execution should normally take place in Bank 0 at all times to avoid confusion over which bank is active in subroutines or interrupt handlers.


3. Accessing all pins in the PIC12C50X and PIC16C505 Devices

The PIC12C50x and PIC16C505 microcontrollers have the ability to use internal reset and clocking functions. To take advantage of these features, the programmer that is used should be able to set the "Calibration Value" (a "movlw" instruction at the end of program memory). With the calibration value set, the application should start with the following code:

 __CONFIG _MCLRE_OFF & _IntRC_OSC   ;  Add Application Specific 
;   “CP” and “WDT” parameters
 org 0
  movf   OSCCAL
  movlw  0x0FF ^ (1 << T0CS)
  option
;  All I/O pins are NOW Available and Internal 4 MHz Clock is Running
;   - Start Application
              

4. Accessing all pins in the PIC12C67X Devices

The mid-range processor architecture offer similar capabilities as the low-end processor architecture PIC12C5XX and PIC16C505. To enable all the I/O pins as output, a programmer with the capability of creating the "Calibration Subroutine" (simply a "retlw" instruction at the end of program memory) should be used with the start of the application having the code:

 __CONFIG _MCLRE_OFF & _CP_OFF & _PWRTE_ON  & _WDT_OFF & _INTRC_OSC
 org      0
  bsf     STATUS, RP0			;  Setup the PICMicro
 ifndef Debug
  call    0x03FF			;  Read the Calibration Value
 else
  nop
 endif
 errorlevel 1, -219
  movwf   OSCCAL ^ 0x080
 errorlevel 1, +219
              

In MPLAB, the "Calibration Subroutine" is not automatically set up for the PIC12C67X device, so the "Debug" define is specified to skip over the call to the "OSCCAL" register.


5. PICmicro MCU Interrupt Handler Skeletons

The mid-range PICmicro MCU Interrupt Handler uses the following "skeleton" where "_w" can be accessed in any bank and "_status", "_fsr" and "_pclath" can be accessed only in Bank 0. "FSR" and "PCLATH" are optionally saved depending on how the application executes.

 org 4
Int
  movwf  _w                ;  Save the Context Registers
  movf   STATUS, w
  bcf    STATUS, RP0       ;  Change to Bank 0
  bcf    STATUS, RP1
  movwf  _status
  movf   FSR, w
  movwf  _fsr
  movf   PCLATH, w
  movwf  _pclath
  clrf   PCLATH

   :                       ;  Interrupts are Handled Here

  movf   _pclath, w        ;  Restore the Context Registers
  movwf  PCLATH
  movf   _fsr, w
  movwf  FSR
  movf   _status, w
  movwf  STATUS
  swapf  _w, f
  swapf  _w, w
  retfie
              

The PIC17CXXX uses the Interrupt Handler Skeleton shown below. Note that the PIC17CXXX has different interrupt vector addresses and this code may be placed at different locations or repeated.

Int
  movpf  ALUSTA, _alusta
  movpf  BSR, _bsr
  movpf  WREG, _wreg
  movpf  PCLATH, _pclath

   :                       ;  Interrupt Handler Code.

  movfp  _pclath, PCLATH
  movfp  _wreg, WREG
  movfp  _bsr, BSR
  movfp  _alusta, ALUSTA
  retfie
              

The PIC18CXXX can access memory anywhere within the device using the "movff" instruction (which doesn't affect the "STATUS" register). This makes the PIC18CXXX interrupt handler skeleton quite simple:

Int	
  movwf  _w                ;  Save Context Registers
  movff  STATUS, _status
  movff  BSR, _bsr

   :                       ;  Interrupt Handler Code

  movff  _bsr, BSR         ;  Restore Context Registers
  movf   _w, w
  movff  _status, STATUS
  retfie
              

6. "Safe" TMR0 Polling Loop

When polling TMR0 to reach Zero, it is important to use the following code which does not return the contents of TMR0. If TMR0 is written to, the prescaler will be reset and if the prescaler is set to 2:1, then TMR0 will increment at half the expected speed. If the prescaler is greater than 2:1, then TMR0 will never change.

  movf   TMRO, w           ; Load “w” with the contents of TMRO
  btfss  STATUS, Z         ;  Skip if the Result is Equal to Zero
   goto  $ - 2  
              

7. PIC16F84 EEPROM Access

The following macros will allow you to read and write to the PIC16F84's data EEPROM. Note that the Address/Data values are constants. If Variable values are to be used, then the "movlw Address"/"movlw Data" instructions should be changed to "movf Address, w"/"movf Data, w" instructions.

EEPROMRead Macro Address
  movlw  Address           ;  Address is a Constant
  movwf  EEADR             ;  "EEADR" in Bank 0
  bsf    STATUS, RPO
  bsf    EECON1, ^ 0x08, RD
  bcf    STATUS, RPO
  movf   EEDATA, w         ;  w = EEPROM [Address] 
 endm

EEPROMWrite Macro Address, Data
  movlw  Data
  movwf  EEDATA
  movlw  Address
  movwf  EEADR
  bsf    STATUS,RPO
  bsf    EECON1 ^ 0x080, WREN
  bcf    STATUS, C         ;  Save the INTCON, GIE in the Carry Flag
  btfsc  INTCON, GIE
   bsf   STATUS, C
  bcf    INTCON,GIE
  movlw  0x055             ;  Critical Code
  movwf  EECON2 ^ 0x080    ;  Critical Code
  movlw  0x0AA             ;  Critical Code
  movwf  EECON2 ^ 0x080    ;  Critical Code
  bsf    EECON1 ^ 0x080, WR;  Critical Code
  btfsc  STATUS, C         ;  Restore Interrupts if Required
   bsf   INTCON, GIE			
  btfsc  EECON1 ^ 0x080, WR;  Poll for Operation Ended
   goto  $ - 1
  bcf    EECON1 ^ 0x080, WREN
  bcf    STATUS, RPO
 endm
              

8. PIC16F87X EEPROM Access

The PIC16F87X accesses data EEPROM similarly, but with small differences due to the inclusion of the program memory Flash access capability built into the Data EEPROM Access Registers. The following macros provide the same function as the ones in the PIC16F84 macros.

EEPROMRead Macro Address
  movlw  Address
  bsf    STATUS, RP1       ;  EEPROM Registers in Banks 2 and 3
  bcf    STATUS, RP0
  movwf  EEADR ^ 0x0100
  bsf    STATUS,  RPO
  bcf    EECON1 ^ 0x0180, EEPGD
  bsf    EECON1 ^ 0x0180, RD
  bcf    STATUS, RPO
  movf   EEDATA ^ 0x0100, w
  bcf    STATUS, RP1
 endm

EEPROMWrite Macro Address, Data
  movlw  Data
  bsf    STATUS, RP1
  movwf  EEDATA
  movlw  Address
  movwf  EEADR
  bsf    STATUS,RPO
  bcf    EECON1 ^ 0x0180, EEPGD
  bsf    EECON1 ^ 0x0180, WREN
  bcf    STATUS, C         ;  Save the INTCON, GIE in the Carry Flag
  btfsc  INTCON, GIE
   bsf   STATUS, C
  bcf    INTCON,GIE
  movlw  0x055             ;  Critical Code
  movwf  EECON2 ^ 0x0180   ;  Critical Code
  movlw  0x0AA             ;  Critical Code
  movwf  EECON2 ^ 0x0180   ;  Critical Code
  bsf    EECON1 ^ 0x0180, WR  ;  Critical Code
  btfsc  STATUS, C         ;  Restore Interrupts if Required
   bsf   INTCON, GIE			
  btfsc  EECON1 ^ 0x0180, WR  ;  Poll for Operation Ended
   goto  $ - 1
  bcf    EECON1 ^ 0x0180, WREN
  bcf    STATUS, RPO
  bcf    STATUS, RP1
 endm
              

9. PIC16F87X program memory Flash Access

The PIC16F87X microcontrollers can read and write their program memory in a similar manner to the Data Memory except that the address range is 8,192 instructions and the data size is fourteen bits. This requires the inclusion of second registers for these values. Note that in the "FlashRead" macro, "Data" is a sixteen bit variable for the instruction contents.

FlashRead Macro Address, Data
  bsf    STATUS, RP1
  movlw  LOW Address
  movwf  EEADR ^ 0x0100
  movlw  HIGH Address
  movwf  EEADRH ^ 0x0100
  bsf    STATUS, RPO
  bsf    EECON1 ^ 0x0180, EEPGD
  bsf    EECON1 ^ 0x0180, RD
  nop                      ;  Instruction Read In Here
  nop
  bcf    STATUS, RPO
  movf   EEDATA, w
  movwf  Data              ;  Store Lo Byte of Program Memory
  movwf  EEDATAH, w
  movwf  Data + 1          ;  Store Hi Byte of Program Memory
  bcf    STATUS, RP1
 endm

FlashWrite Macro Address, Data
  bsf    STATUS, RP1
  movlw  LOW Address
  movwf  EEADR
  movlw  HIGH Address
  movwf  EEADRH
  movlw  LOW Data
  movwf  EEDATA
  movlw  HIGH Data         ;  Maximum 0x03F for 14 bit Word Size
  movwf  EEDATAH
  bsf    STATUS, RPO
  bsf    EECON1 ^ 0x0180, EEPGO
  bsf    EECON1 ^ 0x0180, WREN
  bcf    INTCON, GIE       ;  Critically Timed Code
  movlw  0x055             ;  Critically Timed Code
  movwf  EECON2 ^ 0x0180   ;  Critically Timed Code
  movlw  0x0AA             ;  Critically Timed Code
  movwf  EECON2 ^ 0x0180, OR  ;  Critically Timed Code
  nop                      ;  Operation Executes
  nop                      ;  Operation Executes
  bcf    EECON1 ^ 0x0180, WREN
  bsf    INTCON, GIE
  bcf    STATUS, RP1
 endm
              

10. USART Operation

Before setting up the USART for use with the PICmicro, the clock divisor value has to be calculated. This is done using the formula listed below. The BRGH bit is used to specify fast or slow operations. Due to problems with early PICmicro MCUs, the BRGH bit should always be reset to ensure the code will work in all devices.

SPBRG = (Fosc / (Data Rate x 16 x (4 ** (1 - BRGH)))) - 1
              

The following Macro is used to Initialize the PICmicro MCU's USART and enable the receiver and transmitter:

USARTSetup Macro DataRate  ;  "DataRate" is the SPBRG Value  
  bsf    STATUS, RP0
  bcf    TXSTA, SYNCH      ;  Not in Synchronous mode
  bcf    TXSTA, BRGH       ;  BRGH =0
  movlw  DataRate          ;  Set USART Data Rate
  movwf  SPBRG				
  bcf    STATUS, RP0       ;  Enable serial port
  bsf    RCSTA, SPEN       ;  Enable the Receiver  
  bsf    STATUS, RP0
  bcf    TXSTA, TX9        ;  Only 8 bits to send
  bsf    TXSTA, TXEN       ;  Enable Data Transmit
  bcf    TCSTA ^ 0x080, RX9   ;  Eight Bits to Receive
  bcf    STATUS, RPO 
 endm
              

To transmit a byte, the TXIF bit of PIR1 is polled, waiting for the holding register to be empty as is shown in the "USARTtx" macro below. In the MPLAB IDE simulator, the operation of the USART is not simulated, so when simulating the application, the "Debug" label must be defined, else the application will loop forever waiting for the TXIF bit to become set.

USARTtx Macro              ;  Send the Character in "w"
 ifndef Debug
  btfss  PIR1, TXIF        ;  Wait for Holding Register to be Empty
   goto  $ - 1
 else
  nop
  nop
 endif
  movwf  TXREG             ;  Send the Byte
  bcf    PIR1, TXIF        ;  Reset the Interrupt Request Flag
 endm
              

To receive a bit, the RCIF bit of PIR1 is polled as is shown in the macro below:

USARTrx Macro              ;  Wait for a Character and return in "w"
  btfss  PIR1, RCIF        ;  Wait for Received Character Set
   goto  $ - 1
  movf   RCREG, w          ;  Get the Received Character
  bcf    PIR1, RCIF	   ;  Clear the Character Present Flag
 endm
              

11. Reading a value using the ADC

Using the ADC in the PIC16C7XX parts is actually quite easy to do. First the appropriate pin operation is selected by writing to the "ADCON1" register as specified in the PICmicro MCU's data sheet. Next, the "TAD" value is selected in such a way that it will have a period of 1.6 to 6.4 usecs. I have not included code for these operations as they are different for different PICmicro MCU part numbers and their values can be found in the data sheets.

With the ADC parameters set up, the following macro can be used to read an eight bit ADC value:

ADCRead Macro Speed        ;  Read the ADC for the PICmicro running at "Speed"
 variable DlayCount
DlayCount = 250000000 / Speed  ;  Calculate the number of Cycles in 20 msecs
DlayCount = 375 / DlayCount
  movlw  DlayCount
  addlw  0x0FF             ;  Delay 20 usec for Holding
  btfss  STATUS, Z         ;   Capacitor to Stabilize
   goto  $ - 2
  bsf    ADCON0, GO        ;  start the ADC conversion
  btfsc  ADCON0, GO        ;  Wait for the ADC Conversion
   goto  $ - 1             ;   to End
  movf   ADRES, w          ;  Read the ADC result
              

12. PIC17CXX Program Memory Read/Write

The feature that I really like in the PIC17CXX is its ability connect to external devices and read and write to them. In the book, I discuss the PIC17CXX's ability to program its own program memory, but in the following two macros, I show how the PIC17CXX can access external memory using the "Table I/O" instructions:

TABLERead Macro Address
  movlw  HIGH Address      ;  Set up Table Pointer
  movwf  TBLPTRH
  movlw  LOW Address
  movwf  TABLPTRL
  tablrd 0, 0, WREG        ;  Update Latch Register
  tlrd   1, WREG           ;  Read High Byte
  movwf  Destination + 1
  tablrd 0, 0, WREG        ;  Read Low Byte
  movwf  Destination
 endm

TABLEWrite Macro Address, Data
  movlw   HIGH Address
  movwf   TBLPTRH
  movlw   LOW Address
  movwf   TBLPTRL
  movlw   HIGH Data
  tlwt    1, WREG
  movlw   LOW Data
  tablwt  0,0,WREG
 endm