A C++ template library for embedded applications
Designed and maintained by
Aster Consulting Ltd

Callback Timer for C

A software timer framework for C that can manage up to 254 timers. Each one may be repeating or single shot.
When a timer triggers it will call the selected callback function.
The timers are driven from a call to ecl_timer_tick(uint32_t count). This call would normally be made from a high
priority interrupt routine. The destination function will receive the callback in the same context as the tick call.
The call to tick has a low overhead when a timer is not 'due'. Internally the timers are stored in 'first timeout' order so only
the head of the list needs to be checked.

Each timer may have a period of up to 232-2 ticks (4,294,967,294).
At 1ms per tick this would equate to just over 49 days.

Defines the following structure:-
ecl_timer_config

Uses definitions from ecl_timer.h

An example Keil project for the Nucleo 401RE may be found in examples\ArmTimerCallbacks - C

Requirements

The framework relies on the availability of an atomic counter type.
The user must supply a header named ecl_user.h in the header search path.

In this header, the user must define the following:-

ECL_TIMER_TIMER_SEMAPHORE        The type that will be used for the semaphore.
ECL_TIMER_DISABLE_PROCESSING(x)  Atomically increments 'x'.
ECL_TIMER_ENABLE_PROCESSING(x)   Atomically decrements 'x'.
ECL_TIMER_PROCESSING_ENABLED(x)  Atomically reads 'x' and compares to zero.

For the GCC compiler, the file could contain the following:-
#define ECL_TIMER_TIMER_SEMAPHORE       uint32_t
#define ECL_TIMER_DISABLE_PROCESSING(x) __sync_fetch_and_add(&x, 1)
#define ECL_TIMER_ENABLE_PROCESSING(x)  __sync_fetch_and_sub(&x, 1)
#define ECL_TIMER_PROCESSING_ENABLED(x) (__sync_fetch_and_add(&x, 0) == 0)

Important:
The atomic actions must invoke a full memory barrier.

For correct operation of the timer framework, the routine that calls ecl_timer_tick must not be pre-emptible by another
routine that calls a timer function. Also, calls to the timer framework may only be made from the caller of tick and one
other, lower priority, thread of execution

Functions

void ecl_timer_init(struct ecl_timer_config* ptimers, uint_least8_t max_timers)
Initialises the timer framework.
ptimers    A pointer to the array of timer config structures.
max_timers The maximum number of timers that can be contained in the array.

ecl_timer_id_t ecl_timer_register(void             (*p_callback)(),
                                  ecl_timer_time_t period,                                       
                                  ecl_timer_mode_t repeating)
Registers a timer calling a free or static function.
p_callback A pointer to the callback free funtion that will be callled when the timer expires.
period     The timer period in ticks.
repeating  ECL_TIMER_SINGLE_SHOT if single shot, ECL_TIMER_REPEATING if repeating.

Returns the allocated timer id or ECL_TIMER_NO_TIMER if one was not available.

ecl_timer_result_t ecl_timer_unregister(ecl_timer_id_t id)
Unregisters a timer.
If the timer is active then it will be stopped.
Returns ECL_TIMER_PASS if a timer with the id was successfully unregistered, otherwise ECL_TIMER_FAIL.

void ecl_timer_enable(ecl_timer_enable_t state)
Enables or disables the timer manager according to the state.
ECL_TIMER_ENABLED to enable, ECL_TIMER_DISABLED to disable.

ecl_timer_result_t ecl_timer_is_running()
Returns ECL_TIMER_ENABLED if the timer manager is enabled, otherwise ECL_TIMER_DISABLED.

void clear()
Clears the callback timer back to the initial state. All timers will be stopped and unregistered.

ecl_timer_result_t ecl_timer_tick(uint32_t count)
This function updates the internal tick counter (if enabled) and must pass the number of ticks that have occurred since
the last call. If the count encompasses more than one period of a repeating timer then the timer will be triggered
multiple times in one call to tick.
Returns ECL_TIMER_PASS if the tick counter was updated, otherwise ECL_TIMER_FAIL. This may be used by the calling
routine to accumulate unprocessed tick counts.

ecl_timer_result_t ecl_timer_start(ecl_timer_id_t id, ecl_timer_start_t immediate)
Starts the timer with the specified id.
If the timer is already running then the timer Is restarted from the current tick count.
If immediate is ECL_TIMER_START_IMMEDIATE then the timer is triggered on the next call to ecl_timer_tick().
ECL_TIMER_START_DELAYED will cause the timer to be triggered after the configured delay.
Note: Single shot timers will only trigger once.
If the id does not correspond to a registered timer then returns ECL_TIMER_FAIL.

ecl_timer_result_t ecl_timer_stop(ecl_timer_id_t id)
Stops the timer with the specified id.
Does nothing if the timer is already stopped.
if the id does not correspond to a registered timer then returns ECL_TIMER_FAIL.

ecl_timer_result_t set_period(ecl_timer_id_t id, ecl_timer_time_t period)
Sets a new timer period.
Restarts the timer.
Returns ECL_TIMER_PASS if successful, otherwise ECL_TIMER_FAIL.

ecl_timer_result_t ecl_timer_set_mode(ecl_timer_id_t id, ecl_timer_mode_t repeating)
Sets a new timer mode.
Restarts the timer.
Returns ECL_TIMER_PASS if successful, otherwise ECL_TIMER_FAIL.

Example


#include "ecl_timer.h"

int count1 = 0;

void callback1()
{
  ++count1;
}

int count2 = 0;

void callback2()
{
  ++count2;
}

int count3 = 0;

void callback3()
{
  ++count3;
}

#define NTIMERS 3
struct ecl_timer_config[NTIMERS];

//***************************************************************************
// The main loop.
//***************************************************************************
int main()
{
  ecl_timer_init(timers, NTIMERS);

  ecl_timer_id_t id1 = ecl_timer_register(callback1,
                                          1000,
                                          ECL_TIMER_SINGLE_SHOT);

  ecl_timer_id_t id2 = ecl_timer_register(callback2,
                                          100,
                                          ECL_TIMER_REPEATING);

  ecl_timer_id_t id3 = ecl_timer_register(callback3,
                                          10,
                                          ECL_TIMER_REPEATING);

  ecl_timer_start(id1, ECL_TIMER_START_DELAYED);
  ecl_timer_start(id2, ECL_TIMER_START_DELAYED);
  ecl_timer_start(id3, ECL_TIMER_START_DELAYED);

  ecl_timer_enable(ECL_TIMER_ENABLED);

  // Start timer interrupts here.

  while (1)
  {
    // Loop forever.
  }

  return 0;
}

//***************************************************************************
// The interrupt timer callback.
//***************************************************************************
void timer_interrupt(void)
{
  const uint32_t TICK = 1;
  static uint32_t nticks = TICK;

  if (ecl_timer_tick(nticks))
  {
    nticks = TICK;
  }
  else
  {
    nticks += TICK;
  }
}
ecl_timer.h