My First PIC Project


David Tait, david.tait@man.ac.uk
The PIC16C84 (or PIC16F84) from Microchip is a really great little processor. Being based on EEPROM (or "flash") technology means that it can be programmed in a matter of seconds and typically it can be reprogrammed around 1000 times. Of its 18 pins 13 can be used as general purpose I/O. When programmed as outputs the I/O pins are able to source 20mA and sink 25mA (more than enough to drive LEDs directly for example). It is inexpensive and can be programmed with simple DIY hardware. Obviously these features make the '84 attractive for many projects but they also mean that it is an ideal processor for anyone wanting to learn about microcontrollers.

This short document is meant for people who have just built or purchased a PIC programmer and are itching to get their '84 doing something if only to convince themselves that their programmer, PIC or both are working. To do this we obviously need to lash together some simple hardware and this means knowing a little about the PIC. Here's a pinout diagram (looking from above):

Схема PIC

The RA* and RB* pins are I/O pins associated with the PIC registers PORTA and PORTB respectively (RA4 can also be used as an input to the internal timer and RB0 can be used as an interrupt). VDD and VSS are the power pins. The '84 works over a wide range of voltages but typically VSS is connected to 0V and VDD to +5V. The main reset pin, /MCLR, can simply be tied to VDD (either directly or through a resistor) because the PIC includes a reliable power-on reset circuit - all you need to do to reset the PIC is cycle its power. The processor needs a clock and the OSC1 and OSC2 pins can be configured for a variety of different options including crystal and low cost RC oscillator modes. A simple circuit that you can use as the basis of your first PIC16C84 project is shown here:

Тест-схема

(Grab http://www.man.ac.uk/~mbhstdj/files/test.gif for a more readable version). The circuit uses an RC oscillator and one I/O pin (RB4) attached to a LED. This is all you need to get the PIC to do something and see it happening. Charles Manning (Electronics Australia, April 1996) wrote an amazingly short (6 word) LED flasher program that you can use with this circuit:

LIST    P=16C84
        
MOVLW   0
TRIS    6
OPTION
LOOP    SLEEP
INCF    6,F
GOTO    LOOP
END
The program is written for MPASM (Microchip's free assembler available from http://www.microchip.com). To use the program you'll need to extract it from this file using a text editor (DOS EDIT is fine), save it to another file (LIGHTS.ASM for example) then assemble it with MPASM (using the command "MPASM LIGHTS.ASM") to produce a hex file LIGHTS.HEX which can then be downloaded to the PIC using your programmer. Ignore the warnings from MPASM about TRIS and OPTION being "not recommended". Make sure you program the PIC with the watchdog enabled and the RC oscillator selected. If don't have MPASM yet here is a hex representation of the program I prepared earlier:

:0C0000000030660062006300860A0328DE :00000001FF

You can save these two hex records to the file LIGHTS.HEX and skip the MPASM step. If you are using one of my PIC programmers you can download this hex file with the correct configuration by using one of the following commands:

PP -RW8 LIGHTS.HEX (PP V-0.3) PP -RW LIGHTS.HEX (PP V-0.4) TOPIC -RWG LIGHTS.HEX (TOPIC V-0.2)

The program uses the watchdog timeout as a timing source to decide when to turn the LED on or off; in fact you can get the LED to flash at different rates by connecting it to a different bit of PORTB (RB0-RB7, pins 6-13). This is an unusual use of the watchdog. Normally the watchdog is used to make sure the PIC is behaving itself and, unless your program is specifically designed to use it, enabling the watchdog is a big mistake. The simple LIGHTS program uses the watchdog to wake it from "sleep" (i.e. power down) mode; on waking, the PIC increments the PORTB register - thus changing the states of RB0-RB7 - and promptly goes back to sleep awaiting the next watchdog timeout. The watchdog timer is clocked by an internal RC oscillator which has nominally the same period on all PICs therefore a consequence of using the watchdog for timing is that the program will still work correctly no matter what PIC oscillator configuration or frequency is actually used (well, the frequency should be at least a few kHz). This feature makes the LIGHTS program very useful for initial testing of almost any PIC protoboard. The circuit can be modified to give a slightly more entertaining effect by adding more LEDs. Connect the first LED to RB0 (pin 6), a second to RB1 (pin 7), a third to RB2 (pin 8) and so on; it's best to use at least four LEDs and you can use up to eight (the last one connected to RB7, i.e. pin 13). Each LED should be connected in series with a 470 ohm resistor and wired between the PIC pin and the -ve battery connection (VSS) just like the one in the schematic above. The following program will illuminate each LED in turn obeying a to-and-fro pattern (remember the display on the car featured in the old "Knight Rider" TV series?):

        LIST P=16C84
;    
PORTB   EQU     6 
TRISB   EQU     86H
OPTREG  EQU     81H
STATUS  EQU     3
CARRY   EQU     0
RP0     EQU     5
MSB     EQU     3               ;BIT POSITION OF LEFTMOST LED
;
        CLRF    PORTB           ;ALL LEDS OFF
        BSF     STATUS,RP0      ;SELECT REGISTER BANK 1   
        CLRF    TRISB^80H       ;SET PORTB TO ALL OUTPUTS
        MOVLW   0AH
        MOVWF   OPTREG^80H      ;ASSIGN PRESCALER (1:4) TO WDT
        BCF     STATUS,RP0      ;SELECT REGISTER BANK 0
        INCF    PORTB,F         ;TURN ON RIGHTMOST LED
        BCF     STATUS,CARRY    ;CLEAR CARRY
LEFT    SLEEP                   ;WAIT FOR WDT TIMEOUT
        RLF     PORTB,F         ;TURN ON LED TO LEFT
        BTFSS   PORTB,MSB       ;REACHED LEFTMOST?
        GOTO    LEFT            ;LOOP IF NOT
RIGHT   SLEEP                   ;WAIT FOR WDT TIMEOUT
        RRF     PORTB,F         ;TURN ON LED TO RIGHT
        BTFSS   PORTB,0         ;REACHED RIGHTMOST?
        GOTO    RIGHT           ;LOOP IF NOT
        GOTO    LEFT            ;START NEW CYCLE
        END
MPASM should assemble the program to give this hex representation:

:100000008601831686010A3081008312860A031056
:100010006300860D861D08286300860C061C0C28CC
:020020000828AE
:00000001FF

Again you need to tell your programmer to enable the watchdog timer and RC oscillator. If you save the four hex records to a file (WALKLEDS.HEX say) you can download the program using my programmers by running one of these commands:

PP -RW8 WALKLEDS.HEX (PP V-0.3)
PP -RW WALKLEDS.HEX (PP V-0.4)
TOPIC -RWG WALKLEDS.HEX (TOPIC V-0.2)

As it stands the "LED walking" program is suitable for four LEDs but you can change the value of MSB if you want to use more - MSB should be 4, 5, 6 or 7 for 5, 6, 7 or 8 LEDs.

The program avoids using the deprecated TRIS and OPTION instructions (Microchip don't want you to use them because they may not be supported by future PICs). Therefore, unlike the previous program, no warnings are generated when the program is assembled. To prevent MPASM generating annoying messages about the correct use of bank selection bits I have inverted the most significant bit of any bank 1 register address (e.g. I use TRISB^80H rather than simply TRISB where the "^" operator denotes bitwise exclusive-OR). This is just a trick I've picked up and there are several other ways to silence MPASM; in fact MPASM allows specific messages to be suppressed. However, I like my programs to assemble without generating warnings or messages even if many of them can be safely ignored. Getting MPASM to shut up without resorting to deliberately suppressing warnings and messages takes a little effort.

As a final example, the following program will give much the same effect as the 4-LED WALKLEDS program. You'll notice that even though it does the same as the previous program this one is much longer and it's certainly not meant as an example of efficient programming. Instead it is designed to illustrate a few key PIC idioms and techniques. Amongst other things it contains an interrupt handler, routines to read and write data EEPROM, and shows how table lookups are implemented on the PIC. The program contains examples of some of the more useful MPASM features such as two kinds of macro. It also shows such things as how to override the default radix (hex) for numbers and embed PIC configuration information. Stylistically at least it looks more like a "real" PIC program. ; PATTERN.ASM ; ; A program designed to illustrate reading/writing data EEPROM ; and timer interrupts. A table of values is written to EEPROM ; and the processor then executes a "do nothing" loop. When the ; timer overflows it interrupts the processor and the next value ; in sequence is read from EEPROM then written to port B where it ; is displayed on LEDs. By changing the table any pattern of up ; to 64 values can be displayed. ; ; Copyright (C) 1997 David Tait (david.tait@man.ac.uk)

	PROCESSOR 16C84
	__CONFIG  03FF3                 ;RC oscillator      
	
PCL     equ     2
STATUS  equ     3                       ;standard register files
PORTB   equ     6
EEDATA  equ     8
EEADR   equ     9
INTCON  equ     0BH
OPTREG  equ     081H
TRISB   equ     086H
EECON1  equ     088H
EECON2  equ     089H

RP0     equ     5                       
Z       equ     2
GIE     equ     7
T0IE    equ     5
T0IF    equ     2
WREN    equ     2
WR      equ     1
RD      equ     0

#define bank0   bcf STATUS,RP0          ;select bank 0
#define bank1   bsf STATUS,RP0          ;select bank 1

magic   macro                           ;magic EEPROM write sequence
	movlw   55H            
	movwf   EECON2^80H
	movlw   0AAH
	movwf   EECON2^80H
	endm

	cblock  0CH                     ;variable block  
	n_vals                        
	n_tmp
	endc

	;**************************;
	; Main program entry point ;
	;**************************; 

	org     0
	goto    start


	;***********************;
	; Interrupt entry point ;
	;***********************;

	org     4

; Normally context should be saved before the interrupt service
; routine and restored after but that's not necessary in this program
; because the processor is doing nothing between interrupts.  See
; the PIC datasheet for the recommended procedure.
 
	movf    EEADR,w
	xorwf   n_vals,w
	btfsc   STATUS,Z                ;EEADR == n_vals?            
	clrf    EEADR                   ;yes, start again at 0
	call    ee_rd                   
	movf    EEDATA,w                ;read EEPROM
	movwf   PORTB                   ;display byte
	incf    EEADR,f                 ;new address
	bcf     INTCON,T0IF             ;clear interrupt flag
	retfie


start   clrf    PORTB
	bank1
	clrf    TRISB^80H               ;port B all outputs
	movlw   B'00000111'
	movwf   OPTREG^80H              ;timer 0 prescale 256:1    
	bsf     EECON1^80,WREN          ;allow writing to EEPROM
	bank0
	call    ee_init                 ;transfer table to EEPROM
	bank1
	bcf     EECON1^80H,WREN         ;disallow writing to EEPROM
	bank0
	bsf     INTCON,T0IE             ;enable timer interrupt      
	bsf     INTCON,GIE              ;globally allow interrupts

loop    goto    loop                    ;do nothing forever


; ee_init
;
; initialise EEPROM from table

ee_init clrw 
	call    lut                     ;get number of table entries 
	movwf   n_vals                  ;and save
	movwf   n_tmp                   ;and again
	clrf    EEADR
	decf    EEADR,f                 ;EEADR = -1
ee_in1  incf    EEADR,f                 ;next address
	movf    EEADR,w
	addlw   1
	call    lut                     ;get associated table entry
	movwf   EEDATA
	call    ee_wr                   ;write to EEPROM
	decfsz  n_tmp,f                 ;another?
	goto    ee_in1                  ;yes
	clrf    EEADR                   ;no, then finished
	return                          
	

; lut
;
; look up table

lut     addwf   PCL,f                   ;add W to PCL to get table entry
	retlw   D'12'                   ;number of entries in table
	retlw   B'1000'                 ;first entry
	retlw   B'1000'
	retlw   B'0100'
	retlw   B'0100'
	retlw   B'0010'
	retlw   B'0010'
	retlw   B'0001'
	retlw   B'0001'
	retlw   B'0010'
	retlw   B'0010'
	retlw   B'0100'
	retlw   B'0100'                 ;last entry
	
   
; ee_wr
;
; Writes byte in EEDATA to EEPROM location at EEADR.  Interrupts
; should be disabled before calling ee_wr.

ee_wr   bank1
	magic				;invoke magic sequence
	bsf     EECON1^80H,WR           ;start write
ee_wr1  btfsc   EECON1^80H,WR           ;write complete?
	goto    ee_wr1                  ;no 
	bank0
	return


; ee_rd 
; 
; Reads EEPROM byte at EEPROM location EEADR into EEDATA

ee_rd   bank1
	bsf     EECON1^80H,RD           ;start read
	bank0
	return                          ;read will be complete on return
	
	end


Here is the MPASM generated hex file (save as PATTERN.HEX):

:020000000E28C8
:0800080009080C060319890127
:10001000442008088600890A0B110900860183160E
:10002000860107308100081583121C2083160811F1
:1000300083128B168B171B2800012C208C008D003F
:1000400089018903890A0908013E2C2088003A2089
:100050008D0B22288901080082070C3408340834EB
:1000600004340434023402340134013402340234DE
:1000700004340434831655308900AA30890088146A
:100080008818402883120800831608148312080079
:02400E00F33F7E
:00000001FF

With my programmers this can be downloaded using:

PP PATTERN.HEX                               (PP V-0.4)
TOPIC -G PATTERN.HEX                         (TOPIC V-0.2)
These projects may not seem very exciting but if you have just built or bought a PIC programmer and have hurriedly put together the simple test circuit then seeing a LED flash on and off is exceedingly gratifying. I hope you find that out for yourself. Good luck.

2nd Edition 4/Feb/97

Hosted by uCoz