Файл: The quintessential PIC microcontroller (S. Katzen, 2000).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 5347
Скачиваний: 0
THE ESSENCE OF THE PIC MICROCONTROLLER 125
Program 5.5 Triple-precision shifting to find the number of set bits.
COUNT_BIT clrw ; Zero the count
; WHILE data is not zero, shift right and increment counter
BLOOP |
bcf |
STATUS,C |
; Clear Carry flag |
rrf |
30h,f |
; Shift right all three bytes |
|
rrf |
31h,f |
||
rrf |
32h,f |
||
btfsc |
STATUS,C |
; IF no carry then skip |
|
addlw |
1 |
; ELSE add one to count |
|
; Now check for a triple-byte zero |
|||
movf |
32h,f |
; Test rightmost byte |
|
btfss |
STATUS,Z |
; IF zero THEN try middle byte |
|
goto |
BLOOP |
; ELSE shift again |
|
movf |
31h,f |
; Test middle byte |
|
btfss |
STATUS,Z |
; IF zero THEN try leftmost byte |
|
goto |
BLOOP |
; ELSE shift again |
|
movf |
30h,f |
; Test leftmost byte |
|
btfss |
STATUS,Z |
; IF zero THEN try middle byte |
|
goto |
BLOOP |
; ELSE shift again |
|
EXIT |
..... |
...... |
; Exit with the count in W |
by testing each byte in turn for zero and going back to the top of the loop if any byte test gives a non-zero outcome.
Shifting can be used to multiply and divide data by powers of two. For example, to divide by eight shift left three times:
00011000 |
(24) |
rrf 20h,f |
00001100 |
(12) |
÷2 |
|||||||
File 20h |
File 20h |
|||||||||||
00001100 |
(12) |
rrf 20h,f |
00000110 |
(6) |
÷4 |
|||||||
File 20h |
File 20h |
|||||||||||
00000110 |
(6) |
rrf 20h,f |
00000011 |
(3) |
÷8 |
|||||||
File 20h |
File 20h |
|||||||||||
where we are assuming that the Carry flag is cleared before each shift. In general shifting n places right gives a 2n division and similarly to the left gives multiplication by the same factor – see page 11.
As an example consider that the byte in File 22h (called MULTIPLICAND) is to be multiplied by 3 to give a 2-byte product in File 24:5h (PRODUCT_H and PRODUCT_L respectively).
The implementation of Program 5.6 relies on factoring ×3 as ×2+×1. The former is implemented by shifting a 16-bit extension of the byte multipicand (upper byte zeroed) once left. The single byte multiplicand is then added to the 2-byte subproduct to give the desired outcome. Example 5.5 gives the more complex case of multiplying by ten.
Program Counter instructions
The instructions listed in Table 5.4 modify in some way the setting of the Program Counter. The most elementary of these is nop. No OPer-
126 The Quintessential PIC Microcontroller
Program 5.6 Multiplying by three.
STATUS |
equ |
3 |
; |
The Status register |
|||
C |
equ |
0 |
; |
Bit0 of which is the Carry bit |
|||
MULTIPLICAND |
equ |
22h ; |
File 22h |
is the multiplicand |
|||
PRODUCT_H |
equ |
24h ; |
File 24h |
is the High byte of the product |
|||
PRODUCT_L |
equ |
25h ; |
File 25h |
is the Low |
byte of the product |
||
MULT_3 movf |
MULTIPLICAND,w |
; |
Get xplicand from File 22h |
||||
movwf |
PRODUCT_L |
; |
and put as |
lower product byte |
|||
clrf |
PRODUCT_H |
; |
and extend |
to a 16-bit datum |
|||
; Now shift left 16 |
bits |
||||||
bcf |
STATUS,C |
; |
Clear carry |
||||
rlf |
PRODUCT_L,f |
||||||
rlf |
PRODUCT_H,f |
; |
giving x2 |
||||
; Now add multiplicand (still in |
W) to give |
x2 + x1 = x3 |
|||||
addwf |
PRODUCT_L,f |
; |
Lower byte |
||||
btfsc |
STATUS,C |
; |
plus any carry |
||||
incf |
PRODUCT_H,f |
||||||
.... |
.... |
||||||
ation does not alter the state of the system in any way, but the PC will increment as a consequence of the instruction code being fetched from the Instruction store. Thus its sole outcome is [PC] <- [PC] + 1. This takes one bus cycle, so its main use is to implement a short delay, 1 µs for a 4 MHz clock rate. For example, to pulse Port A’s pin low for 2 µs and then high we have:
bcf |
PORTA,0 |
; Pin |
RA0 |
low |
|
nop |
; |
for |
2 us |
||
nop |
|||||
bsf |
PORTA,0 |
; |
and now |
high |
|
with the assumption that bit 0 of Port A has been set up as an output (see page 95) and that bit 0 (pin RA0) was high before entering the routine.
The goto instruction is an absolute jump instruction allowing the program to transfer to the specified instruction anywhere in the Program store. The process has been described in Fig. 5.4.
The remaining four instructions can skip over the following command if some condition is met. The pair decfsz (DECrement File and Skip on Zero) and incfsz (INCrement File and Skip on Zero) augment the specified file contents and then if the outcome is zero the PC is further incremented. Strangely, the Z flag is not a ected by these instructions.
A typical use for these instructions is to count the number of passes through a loop. For example, suppose it is necessary to pulse Port A pin RA0 low 20 times.
THE ESSENCE OF THE PIC MICROCONTROLLER 127
Table 5.4: Program Counter instructions.
Flags |
||||||||||
Operation |
Mnemonic Z DC C |
Description |
||||||||
Absolute jump |
• |
• |
• |
Goto a fixed instruction |
||||||
Goto an instruction |
goto |
aaa |
[PC] <- aaa |
|||||||
No operation |
• |
• |
• |
Do nothing |
||||||
nop |
[PC] <- [PC] + 1 |
|||||||||
Bit test and skip |
• |
• |
• |
Check bit in file and skip if true |
||||||
Bit clear in File |
btfsc |
f,n |
PC++ IF fn == 0 |
|||||||
Bit set in File |
btfss |
f,n |
• |
• |
• |
PC++ IF fn == 1 |
||||
Decrement and skip on zero |
• |
• |
• |
Decrement & skip if result is #00 |
||||||
File |
decfsz f,d |
d <- f--, PC++ IF [f] == #00 |
||||||||
Increment and skip on zero |
• |
• |
• |
Increment & skip if result is #00 |
||||||
File |
incfsz f,d |
d <- f++, PC++ IF [f] == #00 |
||||||||
++ Increment contents |
-- |
Decrement contents |
||||||||
aaa Absolute 11-bit instruction address |
||||||||||
movlw |
d’20’ |
; Put decimal 20 into W |
||||||||
movwf |
30h |
; & initialize File 30 as a loop counter |
||||||||
LOOP bcf |
PORTA,0 |
; Pin RA0 low |
||||||||
nop |
; for 2 us |
|||||||||
nop |
||||||||||
bsf |
PORTA,0 |
; and now high |
||||||||
nop |
; for 2 us |
|||||||||
nop |
||||||||||
decfsz |
30h,f |
; Count down |
||||||||
goto |
LOOP |
; Repeat loop if not zero |
||||||||
..... ...... |
; ELSE escape the loop |
|||||||||
Notice the assembler notation d’20’ for decimal 20 – see page 223. This is equivalent to 14h but more readily understood by the programmer.
The btfsc (Bit Test File and Skip if Clear) and btfss (Bit Test File and Skip if Set) instructions have been used extensively in programs both here and in Chapter 3. Besides their obvious use in changing the program flow based on the state of a specified bit in any register file, they allow decisions to be make on the state of the various flags in the Status register. Thus in Program 5.5 the series of btfss STATUS,Z (or the more unreadable btfss 3,2) instructions enable the program loop to be exited when the Z flag is set, that is on a series of zero outcomes. Similarly btfsc STATUS,C allows the count action to be skipped over when the C flag is clear. Neither instruction a ects the flags.
All four skip instructions take two cycles to execute when the skip occurs but only one when the condition is not met. As described in Example 4.1 on page 99, the former situation needs to flush the instruction pipeline. This is to reflect the fact that the pre-fetched instruction code
128 The Quintessential PIC Microcontroller
sitting in stage one of the pipeline is not going to be the next instruction executed. This accounts for the additional bus cycle execution time as the true destination instruction has now to be fetched.
Examples
Example 5.1
Code a program to decrement a 2-byte number at File 26:27h ordered as high:low byte, remembering that decf does not alter the Carry/Borrow flag.
Solution
The task list to implement this job is:
1.IF the least significant byte in File 27h is zero THEN decrement the most significant byte.
2.Always decrement the least significant byte.
Program 5.7 gives one possible implementation based directly on this algorithm. Extension to an n-byte word is obvious.
Program 5.7 Double-precision decrement.
STATUS |
equ 3 |
; The Status register |
Zequ 2 ; Bit2 of which is the Zero bit
MSB |
equ |
26h |
; The Most Significant byte |
LSB |
equ |
27h |
; The Least Significant Byte |
movf |
LSB,f |
; Test for LSbyte zero. |
|
btfsc |
STATUS,Z |
; IF not THEN ship MSbyte decrement |
|
decf |
MSB,f |
; ELSE must decrement MSByte |
|
decf |
LSB,f |
; Always decrement LSbyte |
|
Example 5.2
Some early computers used a bi-quinary code to represent BCD digits. This is a 7-bit code with only two bits set to one for any combination:
THE ESSENCE OF THE PIC MICROCONTROLLER 129
01 |
00001 |
0 |
01 |
00010 |
1 |
01 |
00100 |
2 |
01 |
01000 |
3 |
01 |
10000 |
4 |
10 |
00001 |
5 |
10 |
00010 |
6 |
10 |
00100 |
7 |
10 |
01000 |
8 |
10 |
10000 |
9 |
Although this is highly ine cient (with only ten out of a possible 128 code combinations being used as compared to the 16 combinations of the 4-bit natural code) it does have the advantage that it is very easy to determine when an error has occurred. Determine an error-detection routine to check the byte in File 20h. Assume that the most-significant bit is zero. If an error occurs then the Working register is to be set to FFh, otherwise zero.
Solution
All we need to do here is to determine when there are more or less than two bits set to one. Based on this approach we have the task list:
1.Count the number of ones in the bi-quinary byte.
2.Zero W.
3.IF count is not two THEN make FFh to signal an error.
Program 5.8 shows a possible coding implementing this algorithm. Here the loop continually shifts the bi-quinary byte left until the residue is zero. When the carry bit is set, the bit count is incremented. On exit from the loop, two is subtracted from the bit tally after moving into W. If it is zero then the routine is completed and the 00h setting of W shows a correct outcome. Otherwise FFh is placed in W to show an error situation. This is equivalent to decimal −1 and is traditionally used to note an error situation. As there are only ten legal combinations out of 128 possibilities used in this code the likelihood of an undetected error is rather small.
Example 5.3
Write a routine to convert a binary number of magnitude no greater than 63h (decimal 99) in File 20h to two BCD digits in File 21:2h ordered as Tens:Units – see page 6.
Solution
A possible algorithm to implement this binary to BCD conversion is to divide by ten; this generates a quotient between 0 and 9 (remember the
130 The Quintessential PIC Microcontroller
Program 5.8 Bi-quinary error detection.
STATUS |
equ |
3 |
; Status register is File 3 |
C |
equ |
0 |
; Carry flag is bit0 |
Z |
equ |
2 |
; Zero flag is bit2 |
BI_QUIN |
equ |
20h |
; Bi-quinary byte is in File 20h |
COUNT |
equ |
21h |
; The bit count is put here |
BI_QUINARY |
clrf |
COUNT |
; Bit count is cleared |
LOOP |
bcf |
STATUS,C |
; Clear carry flag |
rlf |
BI_QUIN,f |
; Rotate code left |
|
btfsc |
STATUS,C |
; IF no Carry popped out THEN skip |
|
incf |
COUNT,f |
; the count increment |
|
movf |
BI_QUIN,f |
; Test if residue is zero |
|
btfss |
STATUS,Z |
; IF zero THEN skip out of loop |
|
goto |
LOOP |
; ELSE repeat shift and count |
|
movf |
COUNT,w |
; Get count |
|
sublw |
2 |
; Compare with two |
|
btfss |
STATUS,Z |
; IF ZERO finished with W == 00 |
|
movlw 0FFh |
; ELSE put FFh (-1) in W |
||
..... ...... |
; and exit |
||
maximum value is 99) and a remainder. The quotient is the number of tens and the remainder will be the number of units.
We have already seen how to divide by an arbitrary variable datum byte in Program 5.3. Here in Program 5.9 we wish to divide by a constant ten. This simplifies the coding somewhat with the addlw -d’10’ (or addlw -0Ah) instruction used to take away the literal ten. Keeping a count in the TENS file register gives the number of subtractions until a borrow is generated. The required number of tens is one less than this tally; that is the number of successful subtractions. Adding that one extra ten back again to the residue gives the remainder, which is the units tally.
Example 5.4
Using Program 5.2 as a template write a program to evaluate the average temperature over the 24 hours.
Solution
Finding the average involves walking through the array adding each element to a 2-byte grand total. On completion divide by 24 to give the function:
23 |
Temp[i] |
i=0 |
24