Looking 
at a 
PROGRAM-1

UNDERSTANDING INTERRUPTS


Page 27
INDEX

Programs can be written in many different ways and unless it is laid-out very clearly and fully documented, it is very difficult to follow. 
In our PIC Programming series, we have made a special point to  keep programs simple and easy-to-follow, but there will come a time when you have to diagnose some-one else's program. 
Here is a typical example. 
The operation of the program below is very simple.
It increments a 2-digit display, each time the push-button is pressed. 
The feature that makes the program complex is the interrupt routine. 
The program is normally outputting to two displays in a process called multiplexing. This involves outputting to each display in turn and repeating the process very quickly so that both appear to be on at the same time. 
When the push button is pressed, the micro goes to a special location (location 004) where the interrupt hander routine is placed by the programmer. This can be a single goto instruction. A sub-routine then deals with the interrupt. 
The circuit for the program is shown below. Two common-cathode 7-segment displays are driven from port B (bits 0 to 6). Bit 7 is the input for the push button. 
The displays are alternately turned on via bits "0" and "1" of port A, through sinking transistors.



Here is the program, exactly as it was presented by a University, to the students in a second-year electronics course:
PIC 2-DIGIT COUNTER



      list p=16F84

      INCLUDE "P16F84.INC"      



      acc      equ  0
      same     equ  1
      units    equ  0x20
      tens     equ  0x21
      w_keep   equ  0x22    



	org 0
	goto init      


	org 4
	goto incr      



 init   bsf STATUS,RP0
        movlw 0x80           ; set portb(6:0) as outputs
        movwf TRISB          ;and portb(7) as input (interrupt) 
        clrf PORTA           ; porta as output
        bcf STATUS,RP0
        bcf OPTION_REG,7
        movlw 0x00
        movwf units
        movlw 0x00
        movwf tens            ; start counting from 00
        clrf PORTB
        clrf PORTA
        bcf INTCON,RBIF
        bsf INTCON,GIE
        bsf INTCON,RBIE    



    mplx  bsf PORTA,0         ; enable units display
          movf units,acc
          call table
          movwf PORTB         ; show number
          bcf PORTA,0
          bsf PORTA,1         ; enable tens display
          movf tens,acc
          call table
          movwf PORTB         ; show number
          bcf PORTA,1
          goto mplx      



   next   clrf units
          incf tens,same
          movlw 0xF6
	  bcf	STATUS, C
          addwf tens,acc
          btfsc STATUS,C
          clrf tens
          return      



   table   addwf PCL,same    ;format= gfedcba
           retlw 0x3F        ;0
           retlw 0x06        ;1
           retlw 0x5B        ;2
           retlw 0x4F        ;3
           retlw 0x66        ;4
           retlw 0x6D        ;5
           retlw 0x7D        ;6
           retlw 0x07        ;7
           retlw 0x7F        ;8
           retlw 0x6F        ;9      



    incr    bcf INTCON,RBIE
            movwf w_keep
            btfss PORTB,7             ; rising edge only
            goto exit
            incf units,same
            bcf STATUS,C
            movlw 0xF6               ; one >= 10?
            addwf units,acc
            btfsc STATUS,C
            call next                ; if yes     



      exit  bcf INTCON,RBIF
            bsf INTCON,GIE
            bsf INTCON,RBIE
            movf w_keep,acc
            retfie      

            end 
HOW THE PROGRAM RUNS
The program starts at 000 with goto init.  It executes init sub-routine and goes to mplx sub-routine where the displays are multiplexed. The micro loops mplx sub-routine until an interrupt is detected on line 8 of port B. When bit 7 of port B changes from HIGH to LOW (switched pressed - change-of-state detected), an interrupt is detected and the GIE bit is cleared to disable any further interrupts.  The return address is pushed onto the stack and the micro goes to location 004h.
At location 004 it finds the instruction  goto incr. This sub-routine firstly disables the RB port-change interrupt with the bcf INTCON,RBIE instruction. The purpose of the next instruction: movwf w_keep is unknown. 
btfss PORTB,7 detects the press of the button. The instruction should be: btfsc PORTB,7 as the input is LOW when the button is pressed. 
The following 5 instructions increment the units file to see if it is 10:
        incf units,same
        bcf STATUS,C
        movlw 0xF6               ; units = 10?
        addwf units,acc
        btfsc STATUS,C
If the file = 10, the program CALLs next sub-routine. If not, the program executes exit sub-routine and ends with a retfie instruction that takes the micro back to the mplx loop. 

THE PROGRAM
The program starts by defining (naming) the equates. This is a list of items (such as the files to be used in the program and the names of "bits").  Each time the assembler encounters a particular word in the program, it will use the associated file or bit.  
For instance, every time the assembler sees w_keep, it will use the hex file 22 (22h) as the storage file.
There are advantages with this. If you want to change a particular file or value, a single alteration in the "equates section" will alter all the values in a program. You don't have to search through the program to make the changes.
The disadvantage is you have to create a name for each equate and this takes additional thinking.
The meaning of each equate in the program above is:

acc   
same 
units
tens
w_keep
equ  0
equ  1
equ  0x20
equ  0x21
equ  0x22
acc = W register (short for accumulator or working register).
same = put value into file
units = file 20h
tens = file21h
w_keep = put W into file 22h

The program starts at Origin 0. This is location 000 in the PROGRAM-memory of the microcontroller. At location 000, the instruction goto init takes the micro to the sub-routine "init." This is the INITIATE or START sub-routine and sets up the port-lines as input/output. The first 5 instructions take the micro to bank1 where the TRIS file makes the port lines input or output. 
(Instead of using the following 4 instructions: go to Bank1, load W with a value, set the in/out bits, then go to Bank0, you can set the in/out bits of a port with two instructions: load W with a value, movwf TRISB.)
The 7 lowest bits of port B are outputs and the highest bit is an input. 
Bits 0 and 1 of port A are outputs.
The next instruction: bcf OPTION_REG,7 can be written: bcf OPTION,7 . It turns ON weak pull-up resistors on port B. Any input line will see a HIGH if nothing is connected to it. 
This means a switch on an input must be wired to create a LOW when pushed. 
The next 4 lines . . .
        movlw 0x00
        movwf units
        movlw 0x00
        movwf tens
clear the units and tens file. This could be done with 2 instructions: CLRF units    CLRF tens.
The next two instructions:    clrf PORTB     clrf PORTA    turn off the two displays (removes junk).
bcf INTCON,RBIF This bit is cleared via the program so that it can be set when the micro detects a change of state on pin RB 7. 
bsf INTCON,GIE  - enables all un-masked interrupts.
bsf INTCON,RBIE - enables the RB port-change interrupt
The interrupts are now set-up to detect a change on any of the high 4 bits of port B (in our case RB7).
The micro then advances to a Multiplex routine and loops this routine until an interrupt is detected. 
When a change on line 8 of port B is detected, the micro goes to address 0004h and executes 
goto incr
. The program disables port-change interrupt to prevent any further interrupts being detected then increments the units file. If the units file is ten, the micro goes to next and increments the tens file. The micro then goes to exit sub-routine where it clears port-change interrupt flag, sets port-change interrupt enable bit and sets INTCON,GIE via the RETFIE instruction. The micro then returns to Mplx Loop.

FAULTS
The program above has some major faults.  The displays show the wrong information.  The units information shows on the units display for 1 microsecond then on the tens display for 3 microseconds and the same with the tens information.  The result is the information is jumping from one display to the other and the displays do not produce their full brightness. 
The secret to producing a multiplex routine is to display the information 49% of the time for each display and do all the "house-work" in the background while a display is showing a numeral. This involves a delay routine or "do-nothing" routine while each display is illuminated, so that the "house-work" takes up on a very small portion of the overall time of the sub-routine.
These improvements are shown in the routine below.
The second major problem is the lack of switch-debounce and key-press identification. If the button is kept pressed, the display will increment very quickly. 
The program has obviously never been tested! Maybe the University is giving the faulty program to the students for them to modify and get operational. What a clever trick!

TESTING FOR 10
The program above tests for ten by adding F6 to the file and seeing if the answer is greater than FF. 
The carry bit in the status register is cleared, and tested after the operation. If "C" is 1, a carry occurred. This is a very clever approach, but an alternate method (requiring one less instruction) uses the XOR instruction. A file can be tested for any number (such as ten) with a COMPARE (XOR) instruction. The value "10" is 0Ah.
INCF units,same
MOVLW 0Ah
XORWF units,acc
BTFSS STATUS,Z
xxxxxx
xxxxxx
;Increment the units file
;Put 0A (ten) into W
;Perform XOR. Answer will be 0000 0000 if all digits match
;Z flag will be set (1) if all digits match.
;Micro goes here if NOT ten
;Micro goes here if ten

AN IMPROVED PROGRAM
File 1Eh (bit0) is the button-press flag. File 22h is a short delay file.






SetUp













Table











Mplx



Mplx1






Mplx2






Incr









Exit



Next
ORG 0
GOTO SetUp

ORG 4
GOTO Incr

BSF 03,5
MOVLW 80h
MOVWF 06
CLRF 05
BCF 03,5
BCF Option,7
CLRF 20h
CLRF 21h
CLRF 05
CLRF 06
BCF INTCON,RBIF
BSF INTCON,GIE
BSF INTCON,RBIE

ADDWF 02h,1
RETLW 3Fh
RETLW 06h
RETLW 5Bh
RETLW 4Fh
RETLW 66h
RETLW 6Dh
RETLW 7Dh
RETLW 07h
RETLW 7Fh
RETLW 6Fh

MOVF 20h,0
CALL Table
MOVWF 06
BSF 05,0
DECFSZ 22h,1
GOTO Mplx1
BCF 05,0
MOVF 21h,0
CALL Table
MOVWF 06
BSF 05,1
DECFSZ 22h,1
GOTO Mplx2
BCF 05,1
BTFSC 06,7
BCF 1Eh,0
GOTO Mplx

BCF INTCON,RBIE
BTFSC 1Eh,0
GOTO Exit
BSF 1Eh,0
INCF 20h,1
MOVLW 0Ah
XORWF 20h,0
BTFSC 03,2
CALL Next

BCF INTCON,RBIF
BSF INTCON,RBIE
RETFIE

CLRF 20h
INCF 21h,1
MOVLW 0Ah
XORWF 21h,0
BTFSC 03,2
CLRF 21h
RETURN 
;This is the start of memory for the program.


;This is where the micro goes after an interrupt is detected.


;Go to Bank 1
;Put 1000 0000 into W
;Put 80h into TRISB register - NOT Port 06! 
;Make TRISA all output
;Go to Bank 0 - the program memory area.
;Enable pull-up resistors on port B
;Zero units file
;Zero tens file
;Clear Port A
;Clear Port B
;Clear port-change interrupt flag
;Set Global Interrupt Enable bit
;Set port-change interrupt enable

;format= gfedcba
;0      If any table value has a leading letter, it must be 
;1       preceded with a "0."   E.g: 0A3h, 0FFh, 0CCh
;2
;3
;4
;5
;6
;7
;8
;9 

;Put units value into W

;Output display value to 7-segment display
;Turn on units display
;Decrement a delay file
;Create short delay to display units
;Turn off display
;Put tens value into W

;Output display value to 7-segment display
;Turn on tens display
;Decrement a delay file
;Create short delay to display tens
;Turn off display
;Test for button
;Button not pushed. Clear the button-press flag
;Loop multiplex routine

;Disable port-change interrupt
;Test button-press flag. First time?
;Not first pass of routine. File already incremented!
;First time pushed. Set the button-press flag.
;Increment units file
;Put ten into W
;Compare W with units file
;Look at zero flag
;Units file is ten.

;Clear port-change interrupt flag
;Set port-change interrupt enable bit
;Sets INTCON,GIE and returns to Mplx Loop

;Clear the units file
;Increment the tens file
;Load W with ten
;Perform a compare operation
;Test the zero flag. Z flag is SET when both files SAME.
;Tens file is "10."  Zero tens file
;Tens file is not ten. Return

Compare the layout of the two programs above and decide on which is easier to follow. 
Some programmers prefer the use of equates and the program contains names for each file. Others prefer to use the file directly in the program as they are know what each file is doing. 
It all depends on how you want to lay out a program. 
Other differences revolve around the length of sub-routines. Some programmers prefer lots of small sub-routines while others create fewer, longer sub-routines. 
It all revolves around making a program easy to analyse and diagnose, if a fault develops. 

One word of warning
Don't think a debugging program will always come to your rescue it a fault develops. 
It can be very handy to see the contents of various registers during the running of a program and it will sometimes show you where the micro jumps to after an instruction, but some faults elude a single-step analysis because the effect of an input is not taken into account, and some delay routines are too long to analyse in slow-motion. 
For instance, the faulty, first program has been released to the students because it may run perfectly on a simulator (single-stepper program). 
The only way to be sure a program is operational is to test it in a project. You may be surprised. Sometimes amazing faults creep in. Like mirroring on the displays (the ghost of one number appearing on the other display) or a switch not being detected or producing double counting. 
Even the above programs are not final and tested. Neither has switch-debounce and you may find it  necessary, after running the program, to fully debounce the switch and limit the number of presses per second or increase the pulse-detection to allow high speed counting to be accepted. 
You can't do much with two displays, but the program starts you in the right direction and covers the concept of interrupts. 
 
WRITING THE PROGRAM 
The program can be written on a template in WordPad or TEXTPAD. Do not use Notepad as it produces hidden commands that upset the assembler. 
To get a blank template click HERE for the .zip file. Download the file and extract it with WinZip to a folder and load it into your text editor program, such as WordPad or TEXTPAD.  
The .zip is called: Blank_F84.zip and the file is called Blank_F84.asm    Load and save it as 2DC-AF84.asm (for 2 Digit Counter - or some other name), but make sure it is not saved as a .txt file as the assembler wants to see a .asm file
Every time you make changes and improvements to the file, save it with a new letter such as: 
2DC-BF84.asm   2DC-CF84.asm   2DC-DF84.asm   - only 8 letters in the file name are allowed in some programs. This forces your assembler and burning program to pick up a new file. 
Read through the program below and follow through the CALLs and RETURNs to understand how the micro executes the instructions. 
You have to understand how a program "runs" if you want to produce your own. As you work through the program, you must refer to the circuit diagram to see exactly what is happening. This is especially important to prevent "mirroring" or "shadowing" of the digits on the displays.   

                    ;2DC-AF84.asm
                    ;Project: 2 Digit Counter
List P = 16F84
#include <p16F84.inc>
__CONFIG 1Bh        ;_CP_OFF & _PWRTE_ON & _WDT_OFF & _RC_OSC






SetUp













Table











Mplx



Mplx1






Mplx2






Incr









Exit



Next
ORG 0
GOTO SetUp

ORG 4
GOTO Incr

BSF 03,5
MOVLW 80h
MOVWF 06
CLRF 05
BCF 03,5
BCF Option,7
CLRF 20h
CLRF 21h
CLRF 05
CLRF 06
BCF INTCON,RBIF
BSF INTCON,GIE
BSF INTCON,RBIE

ADDWF 02h,1
RETLW 3Fh
RETLW 06h
RETLW 5Bh
RETLW 4Fh
RETLW 66h
RETLW 6Dh
RETLW 7Dh
RETLW 07h
RETLW 7Fh
RETLW 6Fh

MOVF 20h,0
CALL Table
MOVWF 06
BSF 05,0
DECFSZ 22h,1
GOTO Mplx1
BCF 05,0
MOVF 21h,0
CALL Table
MOVWF 06
BSF 05,1
DECFSZ 22h,1
GOTO Mplx2
BCF 05,1
BTFSC 06,7
BCF 1Eh,0
GOTO Mplx

BCF INTCON,RBIE
BTFSC 1Eh,0
GOTO Exit
BSF 1Eh,0
INCF 20h,1
MOVLW 0Ah
XORWF 20h,0
BTFSC 03,2
CALL Next

BCF INTCON,RBIF
BSF INTCON,RBIE
RETFIE

CLRF 20h
INCF 21h,1
MOVLW 0Ah
XORWF 21h,0
BTFSC 03,2
CLRF 21h
RETURN 

END
;This is the start of memory for the program.


;This is where the micro goes after an interrupt is detected.


;Go to Bank 1
;Put 1000 0000 into W
;Put 80h into TRISB register - NOT Port 06! 
;Make TRISA all output
;Go to Bank 0 - the program memory area.
;Enable pull-up resistors on port B
;Zero units file
;Zero tens file
;Clear Port A
;Clear Port B
;Clear port-change interrupt flag
;Set Global Interrupt Enable bit
;Set port-change interrupt enable

;format= gfedcba
;0      If any table value has a leading letter, it must be 
;1       preceded with a "0."   E.g: 0A3h, 0FFh, 0CCh
;2
;3
;4
;5
;6
;7
;8
;9 

;Put units value into W

;Output display value to 7-segment display
;Turn on units display
;Decrement a delay file
;Create short delay to display units
;Turn off display
;Put tens value into W

;Output display value to 7-segment display
;Turn on tens display
;Decrement a delay file
;Create short delay to display tens
;Turn off display
;Test for button
;Button not pushed. Clear the button-press flag
;Loop multiplex routine

;Disable port-change interrupt
;Test button-press flag. First time?
;Not first pass of routine. File already incremented!
;First time pushed. Set the button-press flag.
;Increment units file
;Put ten into W
;Compare W with units file
;Look at zero flag
;Units file is ten.

;Clear port-change interrupt flag
;Set port-change interrupt enable bit
;Sets INTCON,GIE and returns to Mplx Loop

;Clear the units file
;Increment the tens file
;Load W with ten
;Perform a compare operation
;Test the zero flag. Z flag is SET when both files SAME.
;Tens file is "10."  Zero tens file
;Tens file is not ten. Return

NEXT