374 The Quintessential PIC Microcontroller
Program 13.3 Generating a 15 minute data logger timebase.
include "p16C74b.inc" __config _WDT_OFF & _CP_OFF
|
cblock |
20h |
|
|
_work:1, _status:1, JIFFY:1 |
|
endc |
|
|
|
org |
0 |
|
|
goto |
MAIN |
|
|
org |
4 |
|
|
goto |
ISR |
|
MAIN |
movlw |
b’00011011’ |
; Timer on, external clock, synched |
|
movwf |
T1CON |
; Oscillator enabled, PS ratio 2:1 |
|
clrf |
JIFFY |
; Zero Jiffy count |
|
bsf |
STATUS,RP0 |
; To Bank1 |
|
bsf |
PIE1,TMR1IE |
; Enable the Timer1 interrupt |
|
bcf |
STATUS,RP0 |
; Back to Bank0 |
DOOZE |
sleep |
|
; Remember, the 1st instruction |
|
movlw |
d’225’ |
; Check, 225 Jiffies = 15 minutes? |
|
subwf |
JIFFY,w |
|
|
btfss |
STATUS,Z |
|
|
goto |
DOOZE |
; IF not THEN go back to sleep |
|
clrf |
JIFFY |
; ELSE reset Jiffy count |
|
call |
SAMPLE |
; Sample temperature and transmit |
|
goto |
DOOZE |
; and go back to sleep |
;*************************************************************
;First save context in usual way
ISR |
movwf |
_work |
; |
Put |
away W |
|
swapf |
STATUS,w |
; |
and |
the Status register |
|
movwf |
_status |
|
|
|
;************************************************************
;The core code
btfss |
PIR1,TMR1IF |
; Was it |
a Timer1 |
interrupt? |
goto |
ISR_EXIT |
; IF no |
THEN |
false alarm |
incf |
JIFFY,f |
; |
Record |
one |
more |
Jiffy |
bcf |
PIR1,TMR1IF |
; |
Reset |
interrupt |
flag |
; ************************************************************
ISR_EXIT 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 |
|
|
|
|
|
|
13. Time is of the Essence 375
oscillator with a prescale ratio of 2:1, giving our 4 s ji y. In addition, both TMR1IE, PEIE and GIE mask bits are set to enable the interrupt on Timer 1 overflow.
The ISR simply adds one onto the Ji y count. This is tested for 225 in the background program after sleep and if equal it is zeroed, the temperature taken and transmitted to base.
Timer 1 can be reset to zero by any Compare/Compare/PWM CCP module. Some PIC devices have two CCP modules sharing the same timer, such as the PIC16C74, and in such cases the second module CCP2 is virtually identical to CCP1 and can share the same timer. With this in mind we will look just at CCP1 for convenience, pointing out any di erences at the relevant point. All CCP operations require Timer 1 to be configured in its synchronous mode; that is SYNCH = 0.
Each CCP module has an associated control register. For CCP1 this is CCP1CON at File 1Dh in which the lower four bits CCP1M[3:0] set the module mode. A setting of 0000, the reset value, disables the CCP module, resets the CCP output latch and clears the Capture mode prescaler. Modes 1000 – 1011 listed in Fig. 13.6 give four Compare modes. Here an equality comparator detects when the 16-bit Timer 1 datum equals the setting in the 16-bit CCPR1H:L (CCP Register 1) at File 15:16h respectively. When an equality match occurs the CCP1IF interrupt flag in PIR1[2] will be set and this can cause an interrupt if the corresponding CCP1IE mask bit in PIE1[2] is set.
Besides setting CCP1IF and depending on the setting of the CCP1M[3:0] mode bits, one of four actions are possible on Timer 1 matching CCPR1:
|
0000 |
CCP1 off |
|
3 |
2 |
1 |
0 |
CCP1CON |
|
|
|
1000 |
CCP1 pin high on match |
|
CCP1M3 |
CCP1M2 |
CCP1M1 |
CCP1M0 |
File 17h |
File 0Ch |
1001 |
CCP1 pin low on match |
|
|
|
|
|
|
1010 |
CCP1 pin unchanged on match |
|
|
|
|
0 |
1011 |
Reset Timer 1 on match |
|
|
|
|
|
|
TMR1IF |
PIR1 |
|
|
|
|
|
|
|
|
|
|
|
CCP latch |
RC2 |
|
|
|
|
R |
|
|
|
O/P |
CCP1 |
|
Overflow
|
1R TMR1H |
1RTMR1L |
logic |
S |
C1 |
|
|
File 0Fh |
File 0Eh |
TRISC[2] |
|
|
|
|
|
|
|
|
|
Timer 1 |
PIR1
|
|
|
Comparator |
|
Match |
2 CCP1IF
|
|
|
|
= |
|
|
CCP Register 1
|
CCPR1H |
|
CCPR1L |
|
|
|
|
|
|
|
File 16h |
|
File 15h |
Fig. 13.6 The CCP1 module set to Compare mode.
376 The Quintessential PIC Microcontroller
1000:
Pin RC2/CCP1 is forced high.
1001:
Pin RC2/CCP1 is forced low.
1010:
Pin RC2/CCP1 unchanged, but CCP1IF still set.
1011:
Timer 1 is cleared and with CCP2 only7 an analog module conversion is initialized by setting GO/DONE – see Fig. 14.8 on page 404.
Where RC2/CPP1 or RC1/CCP2 are to be used as CCP outputs then the appropriate TRISC bit(s) should be cleared to set the pin direction to output. There is no way to directly reset or set the CCP latch other than zeroing the CCPCON register which resets the latch and disables the CCP module. In this case the state of the RC2/CCP1 pin will be that of PORTC[2] until the module is set to an appropriate mode, in which case the pin will reflect the state specified above when the match occurs.
As an example consider that we wish to set up Timer 1 as configured in the last example to generate an interrupt each 10 seconds. To do this we need set the timer to time-out after 16 s (prescale ratio 8:1) and then shorten the cycle. This is implemented by loading the CCPR1 register with the fraction 1016 −1, which translates to 9FFFh. Whenever Timer 1 reaches this value it will automatically be reset on the next clock input (that would have normally incremented the timer to A000h) and an interrupt will occur if the CCP1IE mask bit (and global PEIE and GIE masks) are set.
Initialization code for this is:
movlw |
9Fh |
; Set up CCPR1 to 9FFFh |
movwf |
CCPR1H |
|
movlw |
FFh |
|
movwf |
CCPR1L |
|
movlw |
b’00001011’ |
; CCP Compare mode 1011 |
movwf |
CCP1CON |
|
movlw |
b’00111011’ |
; Timer1 on (1), external clock (1) |
movwf |
T1CON |
; Synched (0), oscillator (1) 8:1 (111) |
bsf |
STATUS,RP0 |
; Change to Bank 1 |
bsf |
PIE1,CCP1IE |
; Enable CCP1 interrupts |
bcf |
STATUS,RP0 |
; Change back to Bank 0 |
bsf |
INTCON,PEIE |
; Enable Timer/CCP interrupts |
bsf |
INTCON,GIE |
; Enable all interrupts |
|
|
|
The PIC will then automatically be interrupted every ten seconds.
As CCP1 is not changed by Compare mode 1011 this pin can be used as a normal Port C input/output independently of the CCP1 module.
Where there are two CCP modules they can work in tandem using di erent modes, but the timebase will be common – see Example 13.3.
7This is the only functional di erence between CCP1 and CCP2.
13. Time is of the Essence 377
Modes 0100 – 0111 configure the appropriate CCP module to capture the state of Timer 1 when an ‘event’ occurs at the appropriate CCP pin. We can see from Fig. 13.7 that an event can be a falling or rising edge on the RC2/CCP1 pin or every 4th or 16th rising edge according to the CCP1M[3:0] mode bits. This Event prescaler is cleared when the mode bits are set to 0000.
Once a defined event has taken place the 16-bit state of Timer 1 is parallel loaded into the CCP register 1 and CCP1IF set. The processor can then subsequently read this frozen value – that is the time. If Timer 1 is reset after each capture then the sampled datum is the time since the last event. Alternatively, as Timer 1 continues to increment, its captured value can be subtracted from the previous reading to give the di erence. As the mode may be altered on the fly, the time between rising and falling edge on CCP1 can be measured by toggling CCP1M[0] between captures. This may cause the CCP1IF flag to be set. To prevent false interrupts, CCP1IE should be cleared before the change-over and CCP1IF after the change-over. Alternatively, the CCP1 module can be used to capture the rising edge and CCP2 the falling edge – see Example 13.3. There is no room for the CCP2IF and the associated interrupt mask CCP2IE mask bit in PIR1/PIE1. Instead bit 0 of PIR2/PIE2 are pressed into service and in many mid-range processors is the only occupant of these registers.
As our example, consider that we wish to measure the period of our ECG signal with the peak detector connected to pin CCP1. If we assume Timer 1 is clocked by its own 32.768 kHz watch crystal, our set up code is something like this:
|
File
|
|
|
|
PIR1 h0C
|
|
|
|
CCP1IF
|
2
|
|
|
|
|
TMR2IF
|
|
|
|
1
|
|
|
|
TMR1IF
|
0
|
|
|
|
|
|
|
CCP1CON |
|
File 17h |
|
|
3 |
2 |
1 |
0 |
0100 |
Every falling edge |
CCP1M3 CCP1M2 CCP1M1 CCP1M0 |
0101 |
Every rising edge |
|
|
|
|
0110 |
Every 4th rising edge |
CCP1 Control register |
0111 |
Every 16th rising edge |
|
|
|
|
|
Prescaler |
CCP Register 1 |
÷1, ÷4, ÷16 |
File 16h |
File 15h |
C CCPR1H |
C CCPR1L |
Timer 1 |
CCP1
|
|
TMR1H |
TMR1L |
File 0Fh |
File 0Eh |
Fig. 13.7 Capturing the time of an event.
378 The Quintessential PIC Microcontroller
movlw |
b’00001011’ |
; Timer on, external clock, synched |
movwf |
T1CON |
; Oscillator enabled, PS ratio 1:1 |
movlw |
b’00000100’ |
; Capture mode, event = falling edge |
movwf |
CCP1CON |
|
clrf |
NEW |
; Zero NEW flag |
bsf |
STATUS,RP0 |
; To Bank1 |
bsf |
PIE1,CCP1IE |
; Enable the CCP1 interrupt |
bcf |
STATUS,RP0 |
; Back to Bank0 |
bsf |
INTCON,PEIE |
; Enable Timer/CCP interrupts |
bsf |
INTCON,GIE |
; Global interrupts enabled |
|
|
|
The ISR simply reads the contents of the CCP register and stores it away in two temporary locations, setting the file register NEW to indicate to background program that a new time datum exists. Timer 1 is then reset ready for the next event.
With a crystal of 32.768 kHz the time resolution of the captured datum is 30.5 µs with our 1:1 prescale setting. Timer 1 will overflow in 2 s, which is su cient to record a heart rate of 30 beats per minute.
Program 13.4 Capturing the instant of time an ECG R-point occurs.
;*************************************************************
;First save context in usual way
ISR |
movwf |
_work |
; |
Put |
away W |
|
swapf |
STATUS,w |
; |
and |
the Status register |
|
movwf |
_status |
|
|
|
;************************************************************
;The core code
btfss |
PIR1,CCP1IF |
; Was it a CCP1 interrupt? |
goto |
ISR_EXIT |
; IF no THEN false alarm |
incf |
NEW,f |
; Signal a new capture |
bcf |
PIR1,CCP1IF |
; Reset interrupt flag |
movf |
CCPR1L,w |
; Get captured low byte |
movwf |
TEMP+1 |
; Store away |
movf |
CCPR1H,w |
; Get captured high byte |
movwf |
TEMP |
; Store away |
clrf |
TMR1L |
; Zero Timer1 |
clrf |
TMR1H |
|
; ************************************************************
ISR_EXIT 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 |
|
|
|
|
|
|