|
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
|