Paul B. Webster VK2BZC
There are *THREE* ways to achieve this functionality. The three ways are:
1} Isosynchronous code - loops performing all functions take a measured time to cycle. Very tedious, virtually impossible in "C".
2} Interrupt-driven. On interrupt, counters are incremented/ decremented and overflow to other counters. The main routine can "peek" at these counters and determine when to perform the necessary task which it does outside of "interrupt time", thus avoiding problems with interrupts overlapping.
To resolve whether the task has been performed at each required interval however, the interrupt routine can set a (series of) flag(s) on certain overflows e.g., each hour. The mainline code then checks if the flag is set *and* the hour is right, if so clears the flag (so it doesn't perform the task twice) and performs the action.
Hint: These flags are all bit flags within a byte; the interrupt routine sets the whole byte. Easier if worked the other way - it clears all the bits at once.
Along this line, it simplifies things a bit if the interrupt routine only deals with say, seconds, setting a flag each second which the main routine uses to advance the second count and count minutes, hours, days etc. But this does lead to the next proposal:
3} Timer-polling. This is the simplest and easiest to code, and can be done without interrupt. That makes it a very *mature* approach!
The basis is to set up the timer and prescaler so that the timer overflows every so many thousand clock cycles, more than enough to perform either the whole of the "other" functions, or a substantial module of those functions. After performing a function group known to take such a time, you wait polling for the overflow, perform the timing count when it happens and go on to the next function group. Thus for one function:While TRUE Do main_function wait_for_rollover timer_count Loop
and for multiple parts of code each taking less *on average* than the overflow time:While TRUE Do main_function1 wait_for_rollover timer_count main_function2 wait_for_rollover timer_count main_function3 wait_for_rollover timer_count main_function4 wait_for_rollover timer_count Loop
For example, function 1 can take 1½ times the overflow time while function 2 is very short, the first overflow is serviced before another overflow occurs, and the short second function brings the next overflow in on time. You can even cut it slightly finer than that.
The "rollover" code (should be a subroutine) on the devices with an interrupt flag (RTCCOV in T1CNTB, SX48/52) consists of polling for that flag *and clearing it when found*, but note: no interrupt, specific or global is enabled. (Other interrupts can however be enabled as long as they will not make the mainline code modules run significantly over-time as above.)
The timing cycles are each of the form:timer macro unit,unit_max,end_time decsz unit jmp end_time mov W, #unit_max mov unit, W endm ;e.g., for seconds, minutes decsz seconds jmp done_time mov W, #60 mov seconds, W decsz minutes jmp done_time mov W, #60 mov minutes, W ;could be macro coded as: timer seconds,60,done_time timer minutes,60,done_time ; ..more timers.. ; ..payload; i.e. what you do at the end.. done_time
Further hint on using interrupts and timer in general: IMNSHO don't even *think* of setting/ re-setting/ altering the timer value within the interrupt or poll routine. Always let it count un-impeded, use "nice" numbers. *Far* less heartache that way. KISS!
Øyvind Kaurstad was having a problem with jitter in his timing routine and said:
Immediately after I read RTCC I also clear it, and this will prevent a new overflow for some time.
But Nikolai warns:
There is a slim chance that RTCC overflows in the same instruction when you read it. As I understand from datasheets, read is made in the beginning of instruction cycle, and write - in the end. So actually, there is still some room for error. I don't know if this can matter. Best way is compare with this (INTF part of your routine):
William J. Kitchen says
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 ;[ed: this was changed from the original to exclude jumps, ;this is actually the Scott's version below]. TIMER mov W, RTCC add RTIMEL, W snb C incsz RTIMEM dec RTIMEH inc RTIMEH ;* reinitialize RTCC mov W, #$+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 mov RTCC, W ; ;* ret retw #0 ;
This routine will always take exactly the same time to execute.
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.
Scott Dattalo says:
A slightly easier to read (IMO) and slightly faster yet still isochronous is:mov W, RTCC add RTIMEL, W mov W, <<known_zero add RTIMEM, W mov W, <<known_zero add RTIMEH, W ;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:inc RTIMEH mov W, RTCC add RTIMEL, W snb C incsz RTIMEM dec RTIMEH
I'm not sure if this one is more obscure or not:mov W, RTCC add RTIMEL, W snb C inc RTIMEM snb Z inc RTIMEH
If RTCC = 0 and RTIMEL = 0 then C = 0, Z = 1, which causes increment of RTIMEH! But it's okay, because RTCC will increment before the end of this routine (prescaler isn't used).
|file: /Techref/scenix/lib/flow/timenint_sx.htm, 7KB, , updated: 2001/4/17 12:12, local time: 2023/3/25 14:15,
|©2023 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?|
<A HREF="http://massmind.org/techref/scenix/lib/flow/timenint_sx.htm"> SX long term timers without interrupts</A>
|Did you find what you needed?|
Welcome to massmind.org!
Welcome to massmind.org!