Файл: 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: