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

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

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

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

Добавлен: 15.06.2025

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

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

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

184 The Quintessential PIC Microcontroller

This process of saving and restoring the state of internal registers (this internal state is called the context) on entry and exit is known as context switching. Of course care must be taken that the ISR does not use these locations in Data memory for any other purpose.

Core function

The interrupt flag, INTF in INTCON, is cleared to ensure that on return to the background program another interrupt request is not immediately auctioned…indefinitely. In situations where there is more than one source of interrupts, the various interrupt flags would be polled at this stage of the program – see page 179. There would then be several core routines, each of which would commence by clearing the appropriate interrupt flag.

The functional sector of the core function simply increments the datum EVENT. This is then checked against decimal 23. If it is higher then EVENT is zeroed. The background program looks for this zero as a sign that 24 cans have passed.

Restoring the context

The exit process first restored the original state of the Status register by swapping out of memory into W. This cancels the original swapf which was used to save the Status register on entry to the ISR. It is then copied from W into STATUS.

Finally, the Working register has to be restored from Data memory. This process must not alter the newly restored STATUS flag settings. Thus swapf is used twice; the first to twist the nybbles out in memory and the second to copy the datum into W and to untwist in the process. The final exit instruction retfie does not alter the flag state.

Although our example showed a context change involving STATUS and W, other SPRs can be saved in the same way. For example, if the File Select Register is used by both background and foreground routines then it should be saved and retrieved at the beginning and end of the ISR – see Example 7.4. In general, if the ISR alters any SPR then its original state should be restored on exit. In all cases W needs to be saved first, as it is used as an intermediary for the other transfers, and similarly restored last.

In common with all interrupt-driven systems, special care has to be taken where multiple sources of interrupt are being handled. As an example, consider a certain system receiving interrupt requests from the Timer at, say, 1000 times per second and externally through the INT pin at an irregular rate. If the INT handler takes, say, 4 ms to execute, then on return three Timer requests will have been lost! Some MCUs have interrupt priority logic so that a higher-priority request (the Timer here) will interrupt a lower-priority process (the INT handler). Here the only solu-

7. Interrupt Handling 185

tion is to ensure that the latter never takes more than 1 ms to execute.3 In situations where interrupts from several sources occur, a worst-case scenario budget of execution times (including latency) and interrupt rates must be made. As some of these parameters are related to external events beyond the control of the processor, this can be a non-trivial exercise.

Another problem which frequently arises is dealing with events where multiple-precision data are monitored and changed by both background and foreground routines. Consider as an example a real-time clock (RTC) which updates four register files holding time in the 4-byte multipleprecision format HOURS:MINUTES:SECONDS:JIFFY, where the JIFFY byte holds tenths of seconds – see Example 7.4. We assume an external 10 Hz oscillator interrupts the PIC ten times per second, and the ISR updates the time-array.

Consider now that this RTC is part of a central heating controller. At 09:00:00:00 hours the water pump is to be toggled from on to o by the background program. One day this has been done and the time is now 09:59:59:09. The background program, which spends most of its time just looking at the time, reads the hours as 09. Getting interested, it is just going to read the minutes when the Ji y oscillator ‘ticks’. The MCU is interrupted and the RTC now is updated to 10:00:00:00. On return the background program now reads in succession 00, 00, 00. Thinking that it is now 09:00:00:00 it toggles the pump o and thereafter the on and o periods are interchanged indefinitely!

Of course it is bad design practice to use a toggle action; instead the pump should be switched o at 9 am rather than toggled. At least in this latter case the harm would be time limited. In general the interrupt handler should be disabled by clearing the appropriate mask where such multiple-precision data manipulation routines are being executed in the background – see also Example 7.2. Any interrupts occurring during this time will be acknowledged when the mask is set, although events could be missed if the masked-out period is too long.

In conclusion, ISRs are similar to subroutines, but keep in mind the following points:

The ISR should be terminated by retfie instead of return.

Any SPRs that are to be altered should be saved on entry and retrieved on exit.

Parameters cannot be passed to and from the ISR via the Working register. Instead, global variables (data in known memory locations) should be used as required.

ISRs should be as short as possible, with minimal functionality. This helps in debugging, and helps ensure that other events are not missed.

3Rather inelegantly the latter could poll the Timer’s interrupt flag T0IF at regular intervals.


186 The Quintessential PIC Microcontroller

Where multiple-byte data objects are being processed by an ISR consideration should be given to disabling the interrupt system during any background access.

Examples

Example 7.1

In a food processing factory, cans of baked beans on a conveyer belt continually pass through a tunnel oven, as shown top of Fig. 7.5, where the contents are sterilized. Photocell detectors are used to sense cans, both entering and leaving the oven. The output of the sensors are logic 1 when the beam is broken.

You are asked to design an interrupt-driven interface for this system, combining the two signals to activate the PIC’s one INT input. A buzzer connected to Port B’s bit RB0 is to be sounded if the number of tins in the oven exceeds four, indicating that a jam has occurred.

Solution

The hardware aspect of this example presents two problems. The first of these involves distinguishing which cell, IN or OUT, generates a request. In Fig. 7.5 each cell clocks an associated D flip flop when the beam is broken. As the D input is permanently tied to logic 1, the clocked flip flop output goes to logic 1. NORing both of these interrupt flags together generates a falling edge at the INT pin if any beam is broken.

Both the IN and OUT external flags can be read at Port A pins RA0 and RA1, and this allows the ISR software to distinguish between the two events (can-in and can-out). The appropriate flag can then be reset by toggling the appropriate flip flop reset using two further port lines RA2 and RA3 for Cancel_in and Cancel_out respectively.

One problems remains: If one event follows another before the ISR software has time to reset the appropriate external flip flop, that second event will be missed, as the OR gate will hold INT low. In this situation no further edge can occur and the interrupt system will be permanently disabled! This can be circumvented in software by polling both external flags before exiting the ISR and taking the appropriate action if both bits are not clear.

The interrupt service routine for this hardware configuration is given in Program 7.3. As described on page 181 the context is saved on entry and restored on exit.

The meat of the code simply resets the internal INTF flag and checks each of the external flip flops in turn. Depending on the state of these flip flops, one of three paths through the code is followed:

7. Interrupt Handling 187

Program 7.3 Oven safety.

OVEN movwf

_work

; Save current

W reg. in Data memory

swapf

STATUS,w

;

Get

Status, don’t change flags

movf

_status

;

and

put away

in Data memory

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

CHECK bcf

INTCON,INTF

; Clear

the

hardware

interrupt flag

btfsc

PORTA,0

; Check, IN

signal?

goto

IN

; IF

non zero,

a can

has just gone in

btfsc

PORTA,1

;

Check

for

OUT signal

goto

OUT

;

IF

non zero,

a can

has just gone out

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

;The exit point

swapf

_status,w

; Untwist

& get

old Status from memory

movwf

STATUS

swapf

_work,f

; Now

get

original W register from

swapf

_work,w

;

Data memory without altering flags

retfie

;

and

return to

interrupted background

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

;The ISR core

IN

incf

EVENT,f

; Record a can gone in (count up)

bcf

PORTA,2

; Clear external IN flag

bsf

PORTA,2

; by pulsing its reset

goto

ALARM

; and check for alarm situation

OUT

decf

EVENT,f

; Record a can gone out (count down)

bcf

PORTA,3

; Clear external OUT flag

bsf

PORTA,3

; by pulsing its reset

ALARM

movf

EVENT,w

; Get Can_count

addlw

-5

; Can_count - 5

btfsc

STATUS,NB

; IF no borrow THEN sound the alarm

goto

BUZ_OFF

; ELSE OK, turn the buzzer off

bcf

PORTB,0

; Turn buzzer alarm on

goto

CHECK

; and repeat poll of cells flags

BUZ_OFF

bsf

PORTB,0

; Turn buzzer off

goto

CHECK

; and repeat poll of cell flags

1.If pin RA0 is high then a can has broken the IN beam and one is added to the event counter kept in a General-Purpose Register (GPR) in the File store. The external IN flip flop is reset. If the total is greater than four, the buzzer is turned on by bringing RB0 low, otherwise it is turned o . Repeat.

2.If pin RA1 is high then a can has broken the OUT beam and one is taken away from the event counter. This time the external OUT flip flop is reset. Again the total is checked against the boundary of four and the buzzer set to its appropriate state. Repeat.

3.If neither flip flop is set then the ISR exits after restoring the context.


188 The Quintessential PIC Microcontroller

This sequence is repeated whenever actions 1 or 2 have been completed. This ensures that the situation where both beams are broken simultaneously or within a short time window, will be properly serviced.

OUT cell

Oven

IN cell

Interrupt request

INT/RB0

IN

flag

’1’

1D

RA0

C1

R

Cancel_IN

RA2

OUT

flag

’1’

1D

RA1

C1

R

Cancel_OUT

RA3

Buzzer

RB7

Fig. 7.5 Oven safety hardware.

The main background program is not shown here. It will be similar to that of Program 7.1 in that the various ports will be set up, the event count file register cleared and interrupts enabled. It is likely that this background program will be in charge of sounding the alarm and other consequent tasks rather than implementing this as part of the ISR, in keeping with the philosophy of reducing the size of the foreground code. In a practical system the background program would probably drive a numeric display showing the aggregrate of cans (four was a ridiculous value, chosen for illustrative purposes only) in the oven. Also some means of resetting to a non-zero value after a jam and some sign in the (unlikely) event of a sub-zero count being computed must be facilitated.

7. Interrupt Handling 189

Example 7.2

Sensitive routines in the background software, eg. see page 185, may be protected by clearing the GIE (General Interrupt Enable) mask and reenabling it at the conclusion. However, if an interrupt occurs in the middle of the actual instruction clearing GIE (eg. bcf INTCON,GIE) then the interrupt will still be recognized at the instruction’s completion and the ISR entered! What e ect would this have on the following protected routine and how could this problem be countered?

Solution

On return from the ISR the retfie instruction will automatically reenable the interrupt system by setting GIE, so the supposedly protected routine will be left vulnerable to a further interrupt. Any error will be extremely rare as it depends on the conjunction of the following events:

An interrupt request occurring coincidentally with the clearing of GIE.

Another interrupt request happening during execution of the protected code.

The interrupt service routine causing an error in the protected background process.

Such a rare combination would be di cult to reproduce and thus would be virtually impossible to debug.

To avoid this remote possibility of error, the instruction clearing GIE should be followed with a check that it really is clear. For example:

PROTECT bcf

INTCON,GIE

; Clear the GIE mask

btfsc

INTCON,GIE

; Test, is it really clear?

goto

PROTECT

; IF not THEN do it again

ROUTINE ..... .....

; Protected code

..... .....

..... .....

bsf

INTCON,GIE

; Unprotect code

..... .....

Example 7.3

Interrupt handling anomalies can occur in PICs which do not shadow their general-purpose files registers between banks. We see from Fig. 4.6 on page 92 that all GPRs in the PIC16F84’s Bank 0 are shadowed in Bank 1; for instance File 20h and File A0h are the same. As an opposite example, the PIC16C74 has GPRs between File 20h…File 7Fh in Bank 0 and between File A0h…File FFh in Bank 1; a total of 192 bytes – as shown in Appendix B. These register files are bank specific; for instance File 20h is not the same

as File A0h.

The problem arises because the context at the beginning of the ISR will copy W and STATUS into memory in Bank 0 to Bank 3 depending on which bank the processor is in when the background code is interrupted.


190 The Quintessential PIC Microcontroller

If the core of the ISR subsequently switches Bank and then goes back to Bank 0 then an error may occur when restoring the context at the end of the ISR if the bank at this point is not that which the processor was in on entry. For example, if the background program was in Bank 1 at the time of interrupt and then moves to Bank 0, then the restoration at the termination of the ISR will retrieve the incorrect data from Bank 0. Discuss how this problem can be circumvented.

Solution

Microchip suggest two approaches to the context switching problem for cases where the ISR has to change banks and GPRs are bank specific. The first is to keep the background program in Bank 0 at any point of the code where the interrupt is enabled. Access to another bank can of course be made indirectly using the INDF file register – see page 109.

The alternative approach is to save the Working register in whatever bank the interrupt occurs, but the Status register always in Bank 0. When STATUS is retrieved at the end of the ISR, the entry bank state (that is RP1:0 in STATUS – see Fig. 5.1 on page 109) will be restored and the Working (and any other saved) register can then be pulled in from the bank they were actually saved in.

Program 7.4 uses this approach with context change entry and exit routines making no assumptions concerning the entry bank and which enters the core of the ISR code always in Bank 0. Here on entry the state of the Working register is copied into memory in the usual way ignoring the bank in which the processor enters. Although we have defined _work in the assembler as File 20h, if the PIC is in Bank 1 on entry the copy will actually be made at File A0h.

The next step is the more complex and depends on the entry bank state.

Bank 0

If the PIC is in Bank 0, as determined by the state of the Status register’s RP0 bit, then STATUS is copied into memory at file register _status in the normal way using the two instructions at IN_BANK0.

Bank 1

If the PIC is in Bank 1 (i.e. RP0 is logic 1) then RP0 is cleared thus moving the processor to bank 0. STATUS is then copied in Data memory at File 21h, that is _status. Before entering the core code, bit 5 of File 21h is then set to logic 1, restoring the copied version of the original state of RP0 out in memory. This means that on exit when STATUS is restored the PIC will move back into Bank 1.

The core code is always entered with the PIC in Bank 0. At the end of this code the programmer must ensure that the processor is back in Bank 0. In this situation restoring the context is done in the normal way, with STATUS being brought back from _status in Bank 0. With this done,

7. Interrupt Handling 191

Program 7.4 Saving and restoring the context for the PIC16C74 processor.

_work

equ

20h

;

Safekeeping for W @ 20h or A0h

_status

equ

21h

;

Likewise for STATUS

ISR

movwf

_work

;

Save W in Bank0 or Bank1

btfsc

STATUS,RP0

;

Check which bank PIC is in

goto

IN_BANK0

;

IF == 0 THEN already in Bank0

; Continue here

if PIC is in

Bank1 on entry

bcf

STATUS,RP0

;

Change into Bank0

swapf

STATUS,w

;

Save STATUS in Bank0

movwf

_status

;

in the usual way

bsf

_status,RP0;

Set back saved RP0 in memory

goto

BEGIN

;

and begin the core code

IN_BANK0

swapf

STATUS,w

;

If already in Bank0

movwf

_status

;

save STATUS in the usual way

; Core code ***************************************************

BEGIN

..... .....

; Always starts in bank0

..... .....

..... .....

..... .....

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

;Restore context. Processor in Bank0

swapf

_status,w

; Untwist

& get

old Status from memory

movwf

STATUS

; which also

restores original bank

swapf

_work,f

; Now

get

original W register from

swapf

_work,w

;

interrupted bank

retfie

;

and

return

to

interrupted background

the PIC is now in its original Bank state and W can be restored in the usual manner from whatever bank it was stored. As this could either be in File 20h or File A0h then neither file should be used for any other purpose in the ISR to avoid inadvertent corruption.

Example 7.4

On page 185 a central heating real-time clock was discussed. Write a ISR to add one onto the array of file registers holding the four time bytes on each 0.1 s interrupt. Each byte location is to hold its data in a packed binary-coded decimal (BCD) format (see Program 4.1 on page 101) and a 24-hour time representation is to be adopted.

Solution

Each time the PIC enters the ISR one Ji y must be added to the array of bytes HOURS:MINUTES:SECONDS:JIFFY. The base of each byte count di ers in that JIFFY rolls over at a count of ten (i.e. modulo-10), SECONDS


192 The Quintessential PIC Microcontroller

and MINUTES have a modulo-60 count and HOURS is modulo-24. Based on this scenario we have as a task list:

1.Add one onto the JIFFY count.

2.IF this gives 10 THEN zero JIFFY and add one onto the SECONDS count; ELSE goto EXIT.

3.IF this gives 60 THEN zero SECONDS and add one onto the MINUTES count; ELSE goto EXIT.

4.IF this gives 60 THEN zero MINUTES and add one onto the HOURS count; ELSE goto EXIT.

5.IF this gives 24 THEN zero HOURS.

6.EXIT

Coding for this task list is given in Program 7.5. Saving and restoring the context is implemented in the normal way. However, as the File Select Register (FSR) is used in the core of the ISR, it too is saved in a spare file register and retrieved on exit.

Rather than using equ directives to specify the file registers for each of the time array elements and those used to save the STATUS, W and FSR registers I have used the cblock – endc directive (Code BLOCK – END Code block). cblock 20h followed by a list of label names allocates each datum to a successive file register until terminated by endc. Thus _work is allocated to File 20h up to Jiffy at File 26h. Apart from brevity, the main advantage of a single code block over individual equ directives is that when several program sections are concatenated, each with their own code block, the additional variables are simply added to the list. Such additional cblock directives do not specify an explicit start address.

The core of the ISR is sectioned as shown to follow the task list. After each incrementation, the base literal is subtracted from the datum. If they are equal, then the datum is zeroed and the next datum incremented. The alternative of checking the Carry/Not Borrow flag would implement this task if the datum was equal or higher than the base literal, btfss STATUS,C.4

The example specified that the datum format should be packed BCD. Thus, 59 minutes should be stored as 0101 1001b or 59h. This means that the incrementation process has to preserve this BCD format. This can be done after a normal increment by checking that the least significant nybble has not gone above nine. If it has, then six is added to correct the situation. As no datum should be above 59 this process is not needed for the upper nybble – Example 4.3 on page 100 gives a task list for a complete packed BCD increment.

As this process needs to be carried out four times (for all except the Ji y byte which is never greater than nine) then it is best implemented as a subroutine. This is shown in Program 7.6. Here the FSR is pointing to the packed-BCD datum that has to be incremented. This datum is

4This is more robust than equality as it is conceivable that due to a software bug a time datum could be set to a value outside the legitimate range.

7. Interrupt Handling 193

Program 7.5 Coding the real-time clock ISR.

cblock

20h

; Reserve space for the following variables

_work,

_status, _fsr, HOURS, MINUTES, SECONDS, JIFFY

endc

; First save the

context *********************************************

RTC

movwf

_work

; Put away W

swapf

STATUS,w

; and the Status register

movwf

_status

movf

FSR,w

; and the File Select Register

movwf

_fsr

bcf

INTCON,INTF ; Clear the hardware int flag

;The core code ******************************************************

;Task1

incf

JIFFY,f

; Add one onto Jiffy count

; Task2

movlw

0Ah

; Compare to ten

subwf

JIFFY,w

btfss

STATUS,Z

; IF equal THEN continue

goto

EXIT

; ELSE finished

clrf

JIFFY

; ELSE clear Jiffy count

; Task3

movlw

SECONDS

; Point FSR to Seconds count

movwf

FSR

call

BCD_INC

; and increment in BCD

movlw

60h

; Compare with 0110 0000 (60 BCD)

subwf

SECONDS,w

btfss

STATUS,Z

; IF equal THEN continue

goto

EXIT

; ELSE finished

clrf

SECONDS

; ELSE clear Seconds count

; Task4

decf

FSR,f

; Point FSR to Minutes count

call

BCD_INC

; and increment in BCD

movlw

60h

; Compare with 0110 0000 (60 BCD)

subwf

MINUTES,w

btfss

STATUS,Z

; IF equal THEN continue

goto

EXIT

; ELSE finished

clrf

MINUTES

; ELSE clear Minutes count

; Task5

decf

FSR,f

; Point FSR to Hours count

call

BCD_INC

; and increment in BCD

movlw

24h

; Compare with 0010 0100 (24 BCD)

subwf

HOURS,w

btfsc

STATUS,Z

; IF not equal THEN continue

clrf

HOURS

; ELSE zero Hours count

; Retrieve the context ***********************************************

EXIT

movf

_fsr,w

; Get the original FSR back

movwf

FSR

swapf

_status,w

; Untwist the original Status reg

movwf

STATUS

swapf

_work,f

; Get the original W reg back

swapf

_work,w

; leaving STATUS unchanged

retfie

; and return from interrupt