Understanding & Programming the PIC16F84 |
A beginners' tutorial
by
Jim Brown
BSc(Eng), HDipEdAd, GDE (Wits)
To
understand how to program a microcontroller, you need input from many
different sources. This includes ideas and discussions from different instructors. Everyone provides a different point of view with different terminology to describe a feature, especially something as complex as programming. This article by Jim Brown covers many of the features of the PIC16F84 microcontroller and helps you get an overall picture of this amazing device. |
Contents Introduction What you need What you should know Introducing the PIC16F84 Architecture Instruction Set A simple PIC16F84 program Using MPLAB to debug a program Pause to reflect The Instruction Set Instruction format The STATUS register 1: Move instructions MOVF f,d (Move f) MOVWF f (Move W to f) MOVLW k (Move literal to W) 2: Clear instructions CLRF f (Clear f) CLRW (Clear W) 3: Arithmetic instructions ADDWF f,d (Add W & f) SUBWF f,d (Subtract W from f) ADDLW k (Add literal & W) SUBLW k (Subtract W from literal) 4: Logical functions ANDWF f,d (AND W with f) IORWF f,d (Inclusive OR W with f) XORWF f,d (Exclusive OR W with f) ANDLW k (AND literal with W) IORLW k (Inclusive OR literal with W) XORLW k (Exclusive OR literal with W) COMF f,d (Complement f) 5: Decrementing & Incrementing DEC f,d (Decrement f) INC f,d (Increment f) 6: Bit setting & clearing BCF f,b (Bit clear f) BSF f,b (Bit set f) 7: Program control GOTO k (Go to address) CALL k (Call subroutine) RETURN (Return from subroutine) RETLW k (Return with literal in W) RETFIE (Return from Interrupt) 8: Skipping instructions DECFSZ f,d (Decrement f, skip if 0) INCFSZ f,d (Increment f, skip if 0) BTFSC f,b (Bit test f, skip if clear) BTFSS f,b (Bit test f, skip if set) 9: Rotations & Swap RRF f,d (Rotate right f thru carry) RLF f,d (Rotate left f thru carry) SWAPF f,d (Swap nibbles in f) 10: Sleep & Watchdog Timer SLEEP (Sleep,) CLRWDT (Clear watchdog timer,) 11: Miscellaneous NOP (No operation) OPTION (Not recommended) TRIS (Not recommended) Pause to reflect Interrupts on the PIC16F84 What's an interrupt? Types of Interrupt and the INTCON register Servicing an Interrupt Timers on the PIC16F84 The basic idea The TIMER0 module Using the timer's overflow Using the EEPROM data memory |
Program 1: Simple.asm ;simple.asm ;to demonstrate the look of a program ; and introduce some instructions & directives ;***************** setup *************************** processor 16F84 ;processor type org 0010h ;origin of program in memory w equ 0 ;for byte instructions, use w & f f equ 1 ; instead of 0 & 1, it's much clearer MyReg_1 equ H'10' ;position MyRegisters in memory MyReg_2 equ H'15' ; at h'10' & H'15' ;***************** program ****************************** ;We are going to load the accum (reg w) with ; values and perform some arithmetic operations in w ; using MyReg_1&2 movlw H'08' ;put value H'08' into w register movwf MyReg_1 ;move contents of w to MyReg_1 ; note - same as movwf 10h, since ; MyReg_1 and 10h are the same thing. movlw 32h ;put 32h into w reg addwf MyReg_1,f ;add contents of w to that of MyReg_1 ; answer goes into MyReg_1, due to f. movlw 92h ;put value 92h into w register movwf MyReg_2 ;move contents of w to MyReg_2 ; note - same as movwf 15h, since ; MyReg_2 and 15h are same thing. movlw 26h ;put 26h into w reg subwf MyReg_2,w ;subtract w from MyReg_2 ; answer goes into w endLet's examine this sample program. You can verify the following by finding each directive or instruction in the PIC Programming section of this e-magazine.
MOVF f,d (Move f)
(f) ® (dest)
MOVWF f (Move W to f)
(w) ® (f)
MOVLW k (Move literal to W)
k ® (W)
Exercise: Write a program to use these commands to (for instance) put
something into W, move it from there to another register,
put something else into W, and then move the original thing back to W. See moves
.asm.
Convert to binary: 27 ® 11011 . . . . x 20 ® 10100 . . . . y Make 2's complement of y: complement: 01011 add 1: 01100 . . . . z Add x and z: + 11011 . . . . x (1) 00111 = 7 The 1 in brackets is the carry, which gets discarded.4: Logical functions
Inputs A B | A AND B | A OR B | A XOR B | A NAND B | A NOR B |
both | either, both | either, not both | not both | neither, not both | |
00 | 0 | 0 | 0 | 1 | 1 |
01 | 0 | 1 | 1 | 1 | 0 |
10 | 0 | 1 | 1 | 1 | 0 |
11 | 1 | 1 | 0 | 0 | 0 |
The function AND means the result is 1 only when both inputs are 1. The OR
means that either input may be a 1 for output to be 1, but so may both. The function
XOR means exclusive or, and means that either input as 1 will cause a 1 to be output,
and specifically excludes the situation where both inputs are 1. Lastly, the
NAND and NOR are the negations of AND and OR respectively:
compare the columns and you'll see what this means.
By the way, the OR function (as opposed to the XOR function) is
sometimes known as the inclusive or
(IOR). The PIC16F84 uses this term.
The PIC16F84 provides a number of logical operations which act on two, 8-bit
values; these values are compared bit for bit. For
example - without looking at an '84
instruction - consider the ANDing of the numbers H'5F' (equivalent to D'95'
or B'01011111') and H'A3' (which is D'163' or B'10100011'); resulting in H'03'
(which is D'3' or B'00000011').
5F: 01011111 A3: 10100011 and: 00000011Clearly, only in the rightmost 2 positions is the AND satisfied. The result is 1, and 0 elsewhere.
Exercise: In a program, perhaps by adding to one of the previous ones in which you do some
arithmetic or some logic, check out that these commands do work. Keep an eye on the Zero flag, which
is set if either command causes the register in question to go to zero: we'll rely on this fact in
two more commands later. See dec_int
.asm
GOTO k (Go to address)
k ® (PC)
This instruction 'goes to k', which it does this by loading the address of k
into the program counter, PC. In order to use GOTO you should start where you want to go, with a
Label. Then you GOTO Label.
Exercise: Modify one of the programs you have already written. You could put a label
near the top, and a GOTO later on. As you step, you'll see from the highlighted line
your program is looping. Look at the program
counter (PCL) in a watch window and you'll verify this. See GOTO .asm
Now we shall examine subroutines. We need to understand the concept of the stack.
The stack - which has 8 levels in the PIC16F84 - is the place where the address
of the next instruction is placed, when a CALL instruction is met. As each
instruction is executed, it is the job of the Program Counter (PC) to know where
the microcontroller is, at any point in time. The placement of the next address
in the Stack tells the microcontroller where to go, after a subroutine
is finished.
We refer
to loading the stack as pushing and taking a value off later as popping. The stack is
only accessible at the top: it's like a pile of plates in a hopper. The
top of the stack is abbreviated to TOS.
There are 2 instructions associated with any subroutine - one to send the microcontroller
to the sub-routine,
the other to bring it back:
Program 2: Moves.asm ;moves.asm to show how MOVF, MOVWF & MOVLW work ;*********************************** simulator *** ;watch window: reg1, w, pcl ;*********************************** setup *** processor 16F84 reg1 equ h'10' ;*********************************** program *** start: movlw h'05' ;load w movwf reg1 ;move (w) to reg1 movlw h'82' ;change w movf reg1,0 ;restore w end Program 3: Clears.asm ;clears.asm to show how clrf & clrw work ;based on moves.asm ;*********************************** simulator ;watch window: reg1, w, pcl ;*********************************** setup processor 16F84 reg1 equ h'10' ;*********************************** program start: movlw h'05' ;load w movwf reg1 ;move (w) to reg1 movlw h'82' ;change w movf reg1,0 ;restore w clear: clrf reg1 ;clear reg1 clrw ;clear w end Program 4: Arith.asm ;arith.asm to show using ADDWF, SUBWF, ADDLW, SUBLW ;************************************* simulator ;watch window: reg1,reg2,status,w,pcl ;************************************* setup processor 16F84 reg1 equ h'10' reg2 equ h'12' ;************************************* program loads: movlw d'20' ;load w movwf reg1 ;load reg1 movlw d'80' ;load w anew movwf reg2 ;load reg2 arith: addlw d'05' ;add d'05' to w sublw d'100' ;sub w from d'100' addwf reg1,1 ;add w to reg1, into reg1 subwf reg2,1 ;sub w from reg2, into reg2 end Program 5: Inter1.asm ;inter1.asm is a simple interrupt handler- ; it does not save the state of the machine ; nor does it determine the kind of interrupt. ;*********************************************simulator ;watch window: intcon, pcl ;stack: have this open too - see the return address come & go ;asynch stimulus: have a toggle on rb4 ;********************************************* setup processor 16F84 movlw h'0' movwf h'0b' ;clear intcon goto main ;hop over the isr ;********************************************** isr start isr: org h'0004' ;interrupts always vector to here nop ;here we actually do the stuff nop ; of the interrupt bcf h'0b',0 ;clear int before going back retfie ;this re-sets intcon gie ;*********************************************** isr ends ;*********************************************** main start main org h'0020' ; leave enough room for the isr! bsf h'0b',7 ; set global int enable bsf h'0b',3 ; set change on b int enable payrol nop ; loop here until interrupted, nop ; doing payroll stuff goto payrol nop nop end Program 6: Inter2.asm ;inter2.asm saves the state of the machine ; but does not determine the kind of interrupt. ; ;****************************************************simulator ;watch window: intcon, pcl, portb, w, w_saved ;stack: have this open too - see the return address come & go ;asynch stimulus: have a toggle on rb4 ;**************************************************** setup processor 16F84 w_saved equ h'10' ;a place to keep w movlw h'0' movwf h'0b' ;clear intcon goto main ;hop over the isr at the beginning ;**************************************************** isr start isr org h'0004' ;interrupts always vector to here movwf w_saved ;save w as it was in main movlw h'65' ;do something to change w nop ;do more isr stuff ;now, restore w: there is ; no "movfw"- 2 swapf's seems to be ; the way to do this.... swapf w_saved,1 ; first, swap w_saved into itself swapf w_saved,0 ; then, swap it into w bcf h'0b',0 ;clear int on b before going back retfie ;this re-sets intcon gie ;***************************************************** isr ends ;****************************************************** main start main: org h'0020' ; leave room for the isr! bsf h'0b',7 ; set global int enable bsf h'0b',3 ; set change on b int enable payrol nop ; loop here until interrupted, nop ; doing payroll stuff movlw h'0f' ; simulate a calc by loading w goto payrol nop nop end Program 7: Inter3.asm ;inter3.asm saves the state of the machine ; and determines the kind of interrupt. ; ;**************************************************simulator ;watch window: intcon, pcl, portb, w_saved, w ;stack: have this open too - see the return address come & go ;asynch stimulus: have a toggle on rb4(for rbi) and rb0(int) ;************************************************* setup processor 16F84 w_saved equ h'10' ;a place to keep w movlw h'0' movwf h'0b' ;clear intcon goto main ;hop over the isr at the beginning ;*************************************************** isr start isr org h'0004' ;interrupts always vector to here movwf w_saved ;save w as it was in main ;find out what kind of interrupt btfsc h'0b',0 ; is it an rbi? call _rbi ; yes- call the rbi 'sub-handler' btfsc h'0b',1 ; is it an int? call _int ; yes- call the int 'sub-handler' ;end up here after sub-handler ;now, restore w swapf w_saved,1 ; first, swap w_saved into itself swapf w_saved,0 ; then, swap it into w bcf h'0b',0 ;clear rbif before going back bcf h'0b',1 ;clear intf before going back retfie ; this re-sets intcon gie _rbi movlw h'65' ;do something to change w return _int movlw h'24' ;do something different to w return ;***************************************************** isr ends ; ;**************************************************** main start main org h'0020' ; leave room for the isr! bsf h'0b',7 ; set global int enable bsf h'0b',3 ; set change on b int enable bsf h'0b',4 ; set ext int on pin6 enable ; we've got 2 types of int! payrol nop ; loop here until interrupted, nop ; doing payroll movlw h'0f' ;simulate a calc by loading w goto payrol nop nop end Program 8: Time0.asm ;time0.asm to see how timer0 works ;************************************ setup processor 16F84 STATUS EQU H'03' OPTIO EQU H'81' tmr0 EQU H'01' BANK_ EQU H'05' T0CS EQU H'05' PSA EQU H'03' clocked equ h'10' INTCON equ h'0B' T0IE equ h'05' GIE EQU H'07' goto top ;************************************ isr isr ORG h'0004' incf clocked ;clocked is the 'wall clock' BCF INTCON,2 retfie ;************************************ program top org h'0010' clrf tmr0 ;clear the tmr0 register clrf clocked mode bsf STATUS,BANK_ ;switch to page 1 bcf OPTIO,T0CS ;go from counter to timer bsf OPTIO,PSA ;no prescaler yet bsf INTCON,GIE bsf INTCON,T0IE loop nop nop goto loop END