Файл: The quintessential PIC microcontroller (S. Katzen, 2000).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 15.06.2025

Просмотров: 5328

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

144 The Quintessential PIC Microcontroller

The maximum delay is when COUNT is initialized to zero which gives an e ective value for N of 256.6 In this situation the number of cycles in this code fragment is 767, plus the few cycles calling the subroutine (2˜), clearing COUNT (1˜) and returning from the subroutine (2˜), giving a total of 772˜. With a clock rate of 4 MHz, each cycle takes 1 µs (see Fig. 4.4 on page 87) giving a total ceiling 772 µs delay.

Program 6.1 A 100 ms delay subroutine.

; ************************************************************

; *

FUNCTION:

Delays for around 100ms with a 4MHz crystal

*

;

*

ENTRY

:

None

*

;

*

EXIT

:

Flags and W altered; Files 30:1h zero

*

; ************************************************************

COUNT_H

equ

30h

;

2-byte counter

COUNT_L

equ

31h

;

at File 30:1h

Nequ d’130’ ; Delay parameter

DELAY_100MS

movlw

N

; Set up high count to 130, 1˜

movwf

COUNT_H

; 1˜

clrf

COUNT_L

; and low count to 256, 1˜

D_LOOP

decfsz

COUNT_L,f

; Decrement LS count to zero

goto

D_LOOP

; taking in all H*[(256*3)-1]˜

decfsz

COUNT_H,f

; Repeat for the MS byte

goto

D_LOOP

; taking in all (H*3)-1˜

FINI

return

Program 6.1 uses two register files to extend this count. Whenever COUNT_L has decremented to zero, COUNT_H is decremented. This inner loop takes (256 × 3) − 1 cycles in the normal way and is repeated H times, where H is the initial setting of COUNT_H. Only when this second byte reaches zero is the outer loop exited and execution returned to the caller. This second count contributes (H × 3) − 1 cycles to the total. We thus need to determine the unknown value of H to give a total of 100, 000 − 7 cycles, taking into account the call, return and the three setting up instructions.

H × [(256 × 3) − 1] + (H × 3) − 1

= 100, 000 − 7

767H + 3H − 1

=

99, 993

H

130

This gives an actual delay of 100.107 ms; an error of less than 0.11%. Each incremental change in H gives an alteration of ±770 µs.

The maximum delay available with this structure is 197 ms; however, adding one or more nop instructions at the beginning of the inner loop

6If N is zero then it will decrement 00 → FF → FE → · · · 01 → 00 and exit.


6. Subroutines and Modules 145

will increase the total delay by H × 256 cycles and the same technique used in the inner loop adds H cycles for fine tuning. Example 6.3 gives an example of a very long triple count delay subroutine.

Our 100 ms delay program is an example of a double-void subroutine, in that no parameters (cf. signals in our hardware analog) are sent to it and nothing is returned – just the side e ect of a delay (and the alteration of two register files, W and some Status register flags). Most subroutines process parameters made available at entry time and provide data at return time.

As a simple example, consider the extension of Program 6.1 to give a delay of K × 100 ms, where K is a byte parameter ‘sent’ by the caller. The system view of this function is shown in Fig. 6.5 as a single input signal of range 1– 256, with no output signal – that is with a void output. This diagram also documents the location of all local variables used internally by the subroutine. This latter attribute is useful in checking for multiple usage of a file register between di erent subroutines and callers. Notice the double line vertical borders commonly used in flow diagrams to denote modules or subroutines.

Internal workspace

COUNT_H

COUNT_L

K

File 30h

File 31h

File 32h

K (1 -- 256) in W

DELAY_K100MS

Fig. 6.5 System view of K × 100 ms delay subroutine.

As there is only one input byte-sized parameter, the most convenient place to place K is in the Working register. Thus to call up a 5-second delay, the caller could use the sequence:

movlw

50

;

50

x 0.1s

gives 5 seconds

call

DELAY_K100MS

;

Go

to it!

The actual subroutine itself in Program 6.2 implements the task list:

1.DO:

(a)Delay 100ms.

(b)Decrement K.

2.WHILE (K > 0).

3.End.

The actual coding simply copies the parameter from W into File 32h before entering the following delineated coding, which is identical to Program 6.1 and gives a single 100 ms delay. On completion of the delay K

146 The Quintessential PIC Microcontroller

Program 6.2 A K × 100 ms delay subroutine.

; ************************************************************

; *

FUNCTION:

Delays for around

K x 100 ms @ 4MHz

*

; *

EXAMPLE

:

K

= 100, delays

10 seconds

*

;

*

ENTRY

:

K

in W, range 1

-

256

*

;

*

EXIT

:

Flags and W altered; Files 30:1:2h zero

*

; ************************************************************

COUNT_H

equ

30h

; 2-byte counter

COUNT_L

equ

31h

; at File 20:1h

K

equ

32h

; Temporary storage for K

Nequ d’130’ ; Delay parameter

DELAY_K100MS

movwf

K

; Put K away in a register file

; Task 1: DO 100ms delay

movlw

N

; Set up high count to 130

movwf

COUNT_H

clrf

COUNT_L

; and low count to 256

DK_LOOP

decfsz

COUNT_L,f

; Decrement LS count to zero

goto

DK_LOOP

; taking in all H*[(256*3)-1]˜

decfsz

COUNT_H,f

; Repeat for the MS byte

goto

DK_LOOP

; taking in all (H*3)-1˜

; Task 2: Decrement K

decfsz

K,f

; Task 3: WHILE

K > 0

goto

DK_LOOP

; REPEAT WHILE K > 0

FINI

return

is decremented in situ and the delay block repeated until K reaches zero. Thus the 100 ms block is repeated K times.

As K is tested for zero after the 100 ms delay is executed7 an initial value of K = 0 will be treated as K = 256, giving a delay range of 00.1– 25.6 s. Testing before the loop8 would give a range 0–25.5 s. Actually, the delay will be a few µs longer than the plain 100 ms delay subroutine, due to the three additional instructions outside the delineated code block.

As W is needed to set up COUNT_H it could not be used directly to hold K during the subroutine. In fact, if the caller had known that File 32h was used by the subroutine to hold K then it could have been passed directly through this register file. However, the less the caller has to know about the ‘innards’ of its subroutines the better it will be, on the basis

7Known to C programmers as a DO-WHILE loop. 8Known to C programmers as a WHILE loop.


6. Subroutines and Modules 147

that a subroutine should disturb its environment as little as possible. DELAY_K100MS is not very good in this respect, using three file registers for its internal use and altering the Working register. As an example of what could go wrong, Program 6.3 shows an implementation of the task list but calling the 100 ms block as the existing Program 6.1 subroutine; that is a nested subroutine. Here File 30h is used as a store for K oblivious to the fact the this register file is used by subroutine DELAY_100MS as one of its counters. The e ect of this interaction is to make K zero on return from DELAY_100MS, which when decremented at Task 2 will always give a non-zero outcome. Thus the delay is infinite and the system locks up! Simply changing K equ 30h to K equ 32h fixes the problem; but if another member of the team with responsibility for the DELAY_100MS subroutine alters its internal storage map without communicating this to other team members then catastrophe may occur! Thus even though each subroutine could have been passed when tested on its own, certain combinations of calling sequences could cause failure. We will return to this problem later.

Program 6.2 is still void in that no data was returned to the caller on exit. For our next example we will code a subroutine that will activate a decimal readout. Many numeric electronic displays are based on a selective activation of seven segments in the manner shown in Fig. 6.6.

Program 6.3 An alternative K × 100 ms delay subroutine.

; ************************************************************

; *

FUNCTION:

Delays for around

K x 100 ms @ 4MHz

*

; *

EXAMPLE :

K

= 100, delays

10

seconds

*

; *

RESOURCE:

DELAY_100MS

called

*

;

*

ENTRY

:

K

in W, range 1

-

256

*

;

*

EXIT

:

Flags and W

altered; Files 30:1h zero

*

;

************************************************************

K

equ

30h

; Temporary storage for K

DELAY_K100MS

movwf

K

; Put K away in a register file

; Task 1: DO 100ms delay DK_LOOP call DELAY_100MS

; Task 2: Decrement K

decfsz

K,f

; Decrement K

; Task 3: WHILE

K >

0

goto

DK_LOOP

; REPEAT WHILE K > 0

FINI

return


148 The Quintessential PIC Microcontroller

These segments are typically implemented using light-emitting diodes (see Fig. 11.13 on page 298) or electrodes in a liquid-crystal cell.

N (0 -- 9) in W

S (7-segment) in W

SVN_SEG

(a) System view

a

f b

g

e c

d

1000000 1111011 10100100 0110000 0011001 0010010 0000010 1111000 0000000 0010000

gfedcba

(b) The 7-segment font

Fig. 6.6 The 7-segment display.

The system description of our subroutine is shown in Fig. 6.6(a). Here the input signal is a 4-bit binary code representing the ten decimal digits as 0000 – 1001b in the Working register. The output, also in W, is the corresponding 7-segment code to activate the digit as listed in Table 6.2. This code assumes that a segment is lit/opaque on a binary 0 and unlit/clear on a binary 1.

Most MPU/MCUs deal with look-up tables by storing the codes as part of the program memory and copying the Nth byte out of the table as the mapping function. In the 12and 14-bit core PICs the Havard structure makes code in the Program store inaccessible to the program – but see Fig. 15.6 on page 445 for an exception. Instead, look-up tables are implemented as a series of retlw instructions, each returning a constant byte. This structure is shown in Table 6.2. As each retlw places an 8-bit code in W, I have arbitrarily made the unused bit 7 a logic 1.

In developing a coding based on this table structure, the mechanism for element N extraction is to execute the Nth retlw instruction. This will place the instruction literal in the Working register and then do a normal return form subroutine back to the caller. In the example shown, if N is seven then the 7th retlw is executed returning with the code

11111000b for in W.

The coding shown in Program 6.4 implements this selection mechanism by simply adding N, which is in W, to the lower byte of the Program Counter – that is PCL in File 2. PC then points to the Nth retlw as desired.

6. Subroutines and Modules 149

Table 6.2: The 7-segment lookup table showing byte[N] being extracted.

0

retlw b’11000000’

;

1

retlw b’11111001’

;

2

retlw b’10100100’

;

3

retlw b’10110000’

;

4

retlw b’10011001’

;

5

retlw b’10010010’

;

6

retlw b’10000010’

;

N 7

retlw b’11111000’

;

= Table[N]

8

retlw b’10000000’

;

9

retlw b’10010000’

;

Although this approach does work, there are limitations. Any alteration of the PCL register will cause both these eight bits together with the lowermost five bits of the PCLATH to be moved into the 13-bit PC

– as described in Fig. 4.3 on page 86. This means that if the instruction addwf PCL,f causes the 8-bit PCL to overflow or if the contents of the PCLATH does not match the upper bits in the full PC, then the outcome stored into the PC will not be as the programmer wished – see Example 6.7. This is not easy to check, as the programmer is unlikely to know in advance where the subroutine is located in memory, that is what value the PC will have at the beginning of the subroutine. Even if he/she checks the assembler listing file (see Table 8.1 on page 206) for the value of SVN_SEG, this can change if subsequent alterations are made to other parts of the

Program 6.4 The software 7-segment decoder.

PCL

equ

2

; Low byte of PC is at File 2

SVN_SEG addwf

PCL,f

; Add N to PCL giving PC + N

;

xgfedcba

retlw

b’11000000’

; Code for 0

retlw

b’11111001’

; Code for 1

retlw

b’10100100’

; Code for 2

retlw

b’10110000’

; Code for 3

retlw

b’10011001’

; Code for 4

retlw

b’10010010’

; Code for 5

retlw

b’10000010’

; Code for 6

retlw

b’11111000’

; Code for 7

retlw

b’10000000’

; Code for 8

retlw

b’10010000’

; Code for 9


150 The Quintessential PIC Microcontroller

program. It is possible to devise code to allow this address boundary to be crossed, but at the expense of complexity – see Example 6.7. The Microchip application note AN556 Implementing a Table Read gives techniques for dealing with these problems.

The code in Program 6.4 takes no account of the possibility that the datum in W is greater than 09h. Of course it shouldn’t be, but robust code should cope with all contingencies even if it is technically erroneous. This is especially true if the code module is to be reusable for general-purpose applications. What would happen if this situation arose and how could you add to the code to gracefully return an error code, say −1, in this eventuality?

Using W to transfer information to and fro a subroutine is limited to a single byte datum each way. Where several pieces of information of byte or greater sizes are to be passed, then file registers must be pressed into service for this conduit. An example of this is shown in Program 6.5 where two byte datums, labelled MULTIPLICAND and MULTIPLIER are to be multiplied giving a 16-bit outcome labelled PRODUCT_L:PRODUCT_H.

Internal workspace

MULTIPLICAND_H

File 30h

MULTIPLIER

(0 -- 255) in

File 20h

PRODUCT_L:PRODUCT_H

MUL

MULTIPLICAND

(0 -- 255) in

File 21h

(0 -- 65,535) in File 2E:Fh

Fig. 6.7 System diagram for the byte multiplication subroutine.

The principle of the multiplication algorithm coded in Program 6.5 is a generalized version of that used by previous multiplication routines, such as Program 5.11 on page 133. Here the multiplier ten was decomposed to ×2+×8 which could be implemented by shifting once and three times respectively. In the more general case the multiplicand is shifted left and the nth shifted word added to the product if bit n of the multiplier is 1. Doing this eight times gives:

7

Product = (multiplicand<<n) × bit n

n=0

where the << operator denotes shift left.

Using this shift and add algorithm gives the task list:

1.Zero double-byte product.

2.Extend Multiplicand to 16 bits.

3.DO

(a)Shift Multiplier right once.