On Mon, 4 Oct 1999, William J. Kitchen wrote: > > > I've got to set a pin every 24 hours plus or minus 10 minutes. I need the > > > timer to work while I'm doing a bunch of other things (I guess this means > > > I've got to use interrupts). > > Or you have to have a main loop that always takes the same amount > > of time, that is, the same amount of instructions, no matter what > > "else" it is doing. Sometimes that's practical, usually not. > > Make a subroutine that adds the contents of the RTCC to several registers > then resets the RTCC. The subroutine has to be called often enough to > keep the RTCC from overflowing. This means including a call to it inside all > loops. The tricky part is accounting for the time it takes for the subroutine > to execute. > > Here's an example that creates a 24 bit timer: > > TIMER > ; add RTCC contents to 24 bit value (RTIMEL, RTIMEM, RTIMEH) > ; This strange looking addition is designed to execute in exactly > ; the same number of cycles regardless of the number of carries. > movfw RTCC ; > addwf RTIMEL,F ; > btfss STATUS,C ; > goto $+2 ; > incfsz RTIMEM,F ; > goto $+2 ; > incf RTIMEH,F ; > ;* reinitialize RTCC > movlw $+3-TIMER ; this the number of instruction cycles between > ; the time that the RTCC is read and the time when > ; it is written, plus 2 more to adjust for the > ; time that is required for the RTCC to restart > ; after being written > movwf RTCC ; > ;* return > retlw 0 ; > > This routine will always take exactly the same time to execute. A slightly easier to read (IMO) and slightly faster, yet still isochronous is: movfw RTCC addwf RTIMEL,F rlf known_zero,W addwf RTIMEM,F rlf known_zero,W addwf RTIMEH,F ;followed by the RTCC fix-up code where known_zero is a variable that has been initialized to zero. A slighty more obscure version that doesn't require the known_zero: incf RTIMEH,F movfw RTCC addwf RTIMEL,F skpnc incfsz RTIMEM,F decf RTIMEH,F I'm not sure if this one is more obscure or not: movfw RTCC addwf RTIMEL,F skpnc incf RTIMEM,F skpz incf RTIMEH,F On the 18cxxx you could do this: movf RTCC,w ;or whatever the tmr register is called... addwf RTIMEL,F clrf wreg addwfc RTIMEM,F addwfc RTIMEH,F But on the 18cxxx parts, tmr0 can be 16bits wide. So there maybe other tricks to try there... > Unfortunately, using a prescaler with this technique will introduce error > because subroutine can be called at varying times relative to the > prescaler's interval. > > I've used this with great success to generate long and accurate timers on > PICS without interrupts. It can also be used to get very high resolution (as > fine as 0.2 microsecond with a 20mhz pic) measurement of external events > on PICS that do have interrupts, while still maintaining long counts. > > It is also sometimes convenient to generate slow, non-critical timings by > monitoring single bits within this 24 bit timer. For example, you might > blink an LED by finding a bit that changes at a frequency close enough to > what you want (like maybe every 1/2 second) and writing it to an IO pin > whenever convenient (like maybe once in each iteration of your main loop, > or maybe attach this to the TIMER routine itself). The timing won't be > perfectly consistent, but the inconsistency will be too small for human > perception. > > You can modify this technique to create longer times. The 24 bits in this > example won't be enough for your 24 hour timer, unless you run your pic > extremely slow. OTOH, you can easily add more stages to the counter. And since only +/- 10 minute (and not +/- 10 microsecond) resolution out of 24 hours is needed, then you could use the prescaler and simply count the tmr0 rollovers (either by polling or within an interrupt routine). Then you could do something like (I believe Dmitry first wrote this): incf timel,f skpnz incf timem,f skpnz incf timeh,f or on the 18cxxx parts: incf timeh,f infsnz timel,f incfsz timem,f decf timeh,f Scott