Creating a "Timebase" in your program.


1. Introduction

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:

or, a more concrete example: in a 1 minute (repeated) interval the following activities have to be done:
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:
[code]
...
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
...
procedure interrupt
begin
  if TimerX.InterruptFlag = 1 then	// replace this name by the real name of the timer interrupt flag you use
  begin


    // -----  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).
  end;
end
[/code]

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:
[code]
while true do begin	// endless loop
  ...

  if Flag1 = true then
  begin
    ...					// 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)
  end;

  if Flag2 = true then
  begin
    ...					// 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)
  end;

  if Flag3 = true then
  begin
    ...					// ...
    Flag3 := false;			// ...
  end;

  if Flag... = true then
  begin
    ...
    Flag... := false;
  end;

  ...

end;
[/code]
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 .


(*) 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.

-------------------------------------------