Файл: Microcontroller Programming. Thi Micro Chip PIC (Julio Sanchez, 2007).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 14.06.2025
Просмотров: 8515
Скачиваний: 0
Timers and Counters |
253 |
The routine can be implemented to varying degrees of sophistication. One extreme would be a procedure that receives the desired time lapse as a parameter. Another option would be a procedure that reads the desired time lapse from program constants. In the program named LapseTimer contained in the book’s online software we develop a procedure in which the calling code passes the desired time delay in three variables containing the number of machine cycles necessary for the desired wait period. By using machine cycles instead of time units (such as microseconds or milliseconds), the procedure becomes easily adaptable to devices running at different clock speeds. Since each instruction requires four clock cycles, the device’s clock speed in Hz is divided by four in order to determine the number of machine cycles per time unit.
For example, a processor equipped with an 8 Mhz clock executes at a rate of
8,000,000/4 machine cycles per second; that is, 2,000,000 instruction cycles per second. To produce a one-quarter second delay requires a wait period of 2,000,000/4 or 500,000 instruction cycles. By the same token, the 16F84 running at 4 Mhz executes 1,000,000 instructions per second. In this case a one-quarter second delay would require waiting 250,000 instruction cycles.
The program titled lapseTimer in the book’s online software package uses Timer0 to produce a variable-lapse delay. The delay is calculated based on the number of machine cycles necessary for the desired wait period, as described in the preceding paragraph. The program uses the Black-Ammerman methods, which require a prescaler of 1:2 so that each timer iteration takes place at one-half the clock rate. The program initializes the OPTION register and the ports as follows:
main:
;Clear the Watchdog Timer and reset prescaler clrf tmr0
clrwdt
;Set up the OPTION regiser bit map
movlw |
b’11010000’ |
|||||||||
; |
7 |
6 |
5 |
4 |
3 |
2 1 0 <= OPTION bits |
||||
; |
| |
| |
| |
| |
| |
|__|__|_____ |
PS2-PS0 (prescaler bits) |
|||
; |
| |
| |
| |
| |
| |
Values for Timer0 |
||||
; |
| |
| |
| |
| |
| |
*000 |
= 1:2 |
001 = 1:4 |
||
; |
| |
| |
| |
| |
| |
010 |
= 1:8 |
011 = 1:16 |
||
; |
| |
| |
| |
| |
| |
100 |
= 1:32 |
101 = 1:64 |
||
; |
| |
| |
| |
| |
| |
110 |
= 1:128 |
*111 = 1:256 |
||
; |
| |
| |
| |
| |
|______________ |
PSA |
(prescaler assign) |
|||
; |
| |
| |
| |
| |
1 |
= |
to WDT |
|||
; |
| |
| |
| |
| |
*0 |
= |
to Timer0 |
|||
; |
| |
| |
| |
|_________________ |
TOSE (Timer0 edge select) |
|||||
; |
| |
| |
| |
0 |
= |
increment on low-to-high |
||||
; |
| |
| |
| |
*1 |
= |
increment in high-to-low |
||||
; |
| |
| |
|____________________ |
TOCS (TMR0 clock source) |
||||||
; |
| |
| |
*0 |
= |
internal clock |
|||||
; |
| |
| |
1 |
= |
RA4/TOCKI bit source |
|||||
; |
| |
|_______________________ |
INTEDG (Edge select) |
|||||||
254 |
Chapter 12 |
|
; |
| |
*0 = falling edge |
;|__________________________ RBPU (Pullup enable)
; |
0 |
= enabled |
|
; |
*1 |
= disabled |
|
option |
|||
; Set up ports |
|||
movlw |
0x00 |
; Set Port-B to output |
|
tris |
portb |
||
clrf |
portb |
; All Port-B to 0 |
; Port-A is not used in this program
The LapseTimer program is designed to produce a one-half second delay on a 16F84 running at 4 Mhz; therefore, the delay requires 500,000 clock beats. The value is converted to hexadecimal and stored in a 3-byte counter, as follows:
500,000 = 0x07a120 or
countL = 0x20
countM = 0xa1
countH = 0x07
The variables countL, countM, and countH are defined locally and initialized by a procedure named onehalfSec, as follows:
;Procedure to initialize local variables for a
;delay of one-half second on a 16F84 at 4 Mhz.
;Timer is set up for 500,000 clock beats as
;follows: 500,000 = 0x07 0xa1 0x20
;500,000 = 0x07 0xa1 0x20
;—— —— ——
; |
| |
| |
|___ |
countL) |
; |
| |
|________ |
countM |
|
; |
|_____________ |
countH |
||
onehalfSec: |
|
movlw |
0x07 |
movwf |
countH |
movlw |
0xa1 |
movwf |
countM |
movlw |
0x20 |
movwf |
countL |
return |
The delay routine uses the Timer0 register to provide the low-order level of the count. Since the counter counts up from zero to ensure that the initial low-level delay count is correct—the value 128 - (xx/2) must be calculated, where xx is the value in the original countL register. The program performs the division by 2 by shifting bits to the right by one position. The resulting value is subtracted from 128 and the result stored in TMR0, as follows:
Timers and Counters |
255 |
; First calculate xx/2 by bit shifting |
|
bcf |
status,c ; Clear carry flag |
rrf |
countL,f ; Divide by 2 |
; now subtract 128 - (xx/2) |
|
movf |
countL,w ; w holds low-order byte |
sublw |
d’128’ |
; Now w has adjusted result. Store in TMR0 |
|
movwf |
tmr0 |
The delay routine detects timer overflow by testing bit 7 of the TMR0 register. If the bit is set, then 256 time cycles have elapsed and the mid-order counter register is decremented. If the mid-order register underflows when it is decremented, then the high-order register is decremented. If it underflows, the counter has gone to zero and the delay routine ends. Processing is as follows:
cycle:
btfss |
tmr0,7 |
; Is bit 7 set? |
goto |
cycle |
; Wait if not set |
;
;At this point TMR0 bit 7 is set
;Clear the bit
bcf |
tmr0,7 |
; All other bits are preserved |
;Subtract 256 from beat counter by decrementing the
;mid-order byte
;
decfsz countM,f
goto |
cycle |
; Continue if mid-byte not zero |
;At this point the mid-order byte has overflowed.
;High-order byte must be decremented.
decfsz countH,f
goto cycle
; At this point the time cycle has elapsed
return
The circuit in Figure 12-3 can be used to test the lapseTimer program.
Interrupt-driven Timer
Interrupt-driven timers and counters have several advantages over polled routines: first, the time lapse counting takes place in the background so that the application can continue to do other work in the foreground. Another advantage of an interrupt-driven counter is that the prescaler is unnecessary and can be used for the Watchdog Timer. Developing a timer routine that is interrupt-driven presents no additional challenges over the conventional interrupt-driven examples covered in Chapter 11. The initialization consists of configuring the OPTION and the INTCON register bits for the task at hand. In the particular case of an interrupt-driven timer, the following are necessary:
256 |
Chapter 12 |
1.The external interrupt flag (INTF in the INTCON Register) must be initially cleared.
2.Global interrupts must be enabled by setting the GIE bit in the INTCON Register.
3.The Timer0 overflow interrupt must be enabled by setting the TOIE bit in the INTCON register.
In this example program, named LapseTmrInt, the prescaler is not used with the timer, so the initialization code sets the PSA bit in the OPTION register and the prescaler is assigned to the Watchdog Timer. The following code fragment is from the LapseTmrInt program:
main:
;Clear the Watchdog Timer and reset prescaler clrf tmr0
clrwdt
;Set up the OPTION register bit map
movlw |
b’11011000’ |
|||||||||
; |
7 |
6 |
5 |
4 |
3 |
2 1 0 <= OPTION bits |
||||
; |
| |
| |
| |
| |
| |
|__|__|_____ |
PS2-PS0 (prescaler bits) |
|||
; |
| |
| |
| |
| |
| |
Values for Timer0 |
||||
; |
| |
| |
| |
| |
| |
000 |
= 1:2 |
001 = 1:4 |
||
; |
| |
| |
| |
| |
| |
010 |
= 1:8 |
011 = 1:16 |
||
; |
| |
| |
| |
| |
| |
100 |
= 1:32 |
101 = 1:64 |
||
; |
| |
| |
| |
| |
| |
110 |
= 1:128 |
*111 = 1:256 |
||
; |
| |
| |
| |
| |
|______________ |
PSA |
(prescaler assign) |
|||
; |
| |
| |
| |
| |
*1 |
= |
to WDT |
|||
; |
| |
| |
| |
| |
0 |
= |
to Timer0 |
|||
; |
| |
| |
| |
|_________________ |
TOSE (Timer0 edge select) |
|||||
; |
| |
| |
| |
0 |
= |
increment on low-to-high |
||||
; |
| |
| |
| |
*1 |
= |
increment in high-to-low |
||||
; |
| |
| |
|____________________ |
TOCS (TMR0 clock source) |
||||||
; |
| |
| |
*0 |
= |
internal clock |
|||||
; |
| |
| |
1 |
= |
RA4/TOCKI bit source |
|||||
; |
| |
|_______________________ |
INTEDG (Edge select) |
|||||||
; |
| |
*0 |
= |
falling edge |
||||||
;|__________________________ RBPU (Pullup enable)
; |
0 |
= enabled |
|
; |
*1 |
= disabled |
|
option |
|||
; Set up ports |
|||
movlw |
0x00 |
; Set Port-B to output |
|
tris |
portb |
||
clrf |
portb |
; All Port-B to 0 |
;Port-A is not used in this program ;============================
;set up interrupts ;============================
;Clear external interrupt flag (intf = bit 1)
bcf |
INTCON,intf |
; Clear flag |
Timers and Counters |
257 |
;Enable global interrupts (gie = bit 7)
;Enable RB0 interrupt (inte = bit 4)
bsf |
INTCON,gie |
; Enable |
global int (bit 7) |
|
bsf |
INTCON,toie |
; |
Enable |
TMR0 overflow |
; |
interrupt |
|||
As in the program LapseTimer, developed previously in this chapter, the timer operates by decrementing a 3-byte counter that holds the number of timer beats required for the programmed delay. In the case of the LapseTmrInt program, the routine that initializes the register variables for a one-half second delay also correctly adjusts the initial value loaded into the TMR0 register. The code is as follows:
;==============================
;set register variables for
;one-half second delay ;==============================
;Procedure to initialize local variables for a delay of
;one-half second on a 16F84 at 4 Mhz. Timer is set up for a
;500,000 clock beats as follows: 500,000 = 0x07 0xa1 0x20
;500,000 = 0x07 0xa1 0x20
;—— —— ——
; |
| |
| |
|___ |
countL) |
; |
| |
|________ |
countM |
|
; |
|_____________ |
countH |
||
onehalfSec: |
||||
movlw 0x07 movwf countH movlw 0xa1 movwf countM movlw 0x20 movwf countL
;The TMR0 register provides the low-order level of
;the count. Since the counter counts up from zero,
;in order to ensure that the initial low-level delay
;count is correct, the value 256 - xx must be calculated
;where xx is the value in the original countL variable.
movf |
countL,w ; w holds low-order byte |
sublw d’256’
; Now w has adjusted result. Store in TMR0
movwf tmr0
return
The interrupt service routine in the LapseTmrInt program receives control when the TMR0 register underflows, that is, when the count goes from 0xff to 0x00. The service routine then proceeds to decrement the mid-range counter register and adjust, if necessary, the high-order counter. If the count goes to zero, the handler toggles the LED on Port-B, line 0, and re-initializes the counter variables by calling the onehalfSec procedure described previously. The interrupt handler is coded as follows: