Creating a "Timebase" in your program.
Sometimes a (high) number of different activities has to be performed on a regular timebasis, in a well defined order,
with a (sometimes) well defined time in between.
Example: in a 1 hour (repeated) interval the following activities have to be done:
- on second 5 activity 1 has to be done
- on minute 2 activity 2 has to be done
- on minute 30 activity 3 has to be done
or, a more concrete example: in a 1 minute (repeated) interval the following activities have to be done:
- on second 10 a temperature conversion of a DS1820 has to be started
- on second 11 the converted temperature is to be read from the DS1820
- on second 30 the temperature read from the DS1820 is used to control the heater
Additionally it is possible that a number of activities are to be performed on a regular basis, e.g. every 10 seconds, in contrast with the
above ones, which were to be executed "on a certain moment in the time base interval".
All these "timed activities" can of course not be done by using each time a different timer, the number of available timers in a PIC is very limited.
The idea now is to use only one timer, and let it (or better: its interrupt service routine) signal to the main loop when certain moments in time are reached.
The "signalling" is done with boolean variables that are "set" by the timer interrupt routine and read (and reset) by the main loop.
There is one condition: the main loop must be fast enough to not mis any signalling (or the signalling coming from the timer must be slow enough).
A project where this method is applied is e.g. An application example is e.g. the project PIC controlled thermostat.
2. How to do it?
a. The timer interrupt routine
As mentioned before, here the "signals" are given to the main loop that the "time" is there to do an activity.
Of course, for each "activity" to do on a different moment in time, we do need a separate "signal flag": a boolean variable (initiated to false of course).
So, this will look like this:
var Flag1, Flag2, Flag3, ... : boolean; // Signalling flag "GLOBAL" variables (declared outside any procedure or function)
// You should give more meaningfull names to the signalling flags!
TimeCounter : word; // This is the extra "Counter Variable" as mentioned in the introduction
if TimerX.InterruptFlag = 1 then // replace this name by the real name of the timer interrupt flag you use
// ----- handle the Timebase ----- //
Inc(TimeCounter); // time goes by...
if TimeCounter > MaxTime // timecounter is with wrap around, the steps of it are from zero to MaxTime
then TimeCounter := 0; // e.g. if your timer interrupt is there every 0,5 secs, you can count from
// zero to 7200 (MaxTime in this case) to cover exactly one hour in steps of
// 0,5 secs.
// ----- do signalling to the main loop ----- //
if TimeCounter = 10 then Flag1 := true; // if the same values are used as above (0,5 sec timer interrupts) then
// this signal flag will be set after 5 seconds in the "timing sequence"
if TimeCounter = 240 then Flag2 := true; // if the same values are used as above (0,5 sec timer interrupts) then
// this signal flag will be set after 120 seconds (2 minutes) in the "timing sequence"
if TimeCounter = 3600 then Flag3 := true; // if the same values are used as above (0,5 sec timer interrupts) then
// this signal flag will be set after 1800 seconds (0,5 hours) in the "timing sequence"
if (TimeCounter mod 20) = 0 then Flag4 := true; // (*) if the same values are used as above (0,5 sec timer interrupts) then
// this signal flag will be set every 10 seconds
if (TimeCounter mod 600) = 0 then Flag5 := true; // (*) if the same values are used as above (0,5 sec timer interrupts) then
// this signal flag will be set every 5 minutes
if ... etc... // so depending on the timer interrupt rate and the value of the "TimeCounter"
// one can define easily when to set certain flags and in which order.
TimerX.InterruptFlag = 0; // reset the timer interrupt flag before returning from the interrupt service routine. (do not forget).
b. The main loop
Its task is to check the flags, to execute what should be executed, and to reset the "Flag" when it is done. So, it will look like this:
while true do begin // endless loop
if Flag1 = true then
... // here come the actions to be done when Flag1 has been set by the timer
Flag1 := false; // reset the flag (otherwise the actions would be executed every loop round, do not forget)
if Flag2 = true then
... // here come the actions to be done when Flag2 has been set by the timer
Flag2 := false; // reset the flag (otherwise the actions would be executed every loop round, do not forget)
if Flag3 = true then
... // ...
Flag3 := false; // ...
if Flag... = true then
Flag... := false;
As simple as that! Again: make sure that the main loop goes round fast enough to not miss any Flag settings.
c. Program initialization
Of course, till now only the "operational" parts of the code have been shown, but there is also the program and PIC initialization to take care of.
It looks like this:
(1) Several tools have been developed to calculate the settings of timers, see Tools .
- Initialisation of all "Flags" to false
- Initialization of the "Counter Variable" ("TimeCounter" in above example) to a certain value (usually zero)
- Setting the correct registers for the timer, so it will overflow at the desired timing rate (1)
- Enabling the timer interrupt of the timer used, the enabling of the peripheral interrupts, plus the general enabling of interrupts (if not already the case)
(*) Thanks to Ed Anderson for his contribution: discovering a discrepancy between introduction and
the actual signalling method, and the suggestion to use "mod" to make signals on a repetitive base.