Embedded Programming Pattern: Synctimers

BeRTOS gives you a simple yet powerful tool to execute recurrent actions: synctimers.

With BeRTOS' synctimers it's easy to create callbacks (simple functions) and let them execute completely synchronously and at regular time intervals. This is really useful to simplify the management of all timed activities.

To use them, first you need to encapsulate each job into an independent function, which in our case are temperature management and voltage control:

List timer_list;
Timer temp_timer, voltage_timer;

void temp_handler(void)
{
    /* Check current temperature state. */
    temp = adc_read(TEMP_CH);
    temp *= T_SCALE + T_OFFSET;
    temp_check(temp);
    prm.temp = temp;
    synctimer_add(&temp_timer, &timer_list);
}

void voltage_handler(void)
{
    /* Acquire and check voltages */
    v = adc_read(VOLT_CH);
    v = v * V_SCALE + V_OFFSET;
    valve = voltage_check(v);
    /* Update output */
    setvalve(valve)
    prm.valve = valve;
    synctimer_add(&voltage_timer, &timer_list);
}

The last line calls the funtion synctimer_add that adds again the event into the task list. Without that, the callback would be called only once.

We still need to initialize the synctimer module:

void init(void)
{
    timer_init();
    
    LIST_INIT(&timer_list);

    /* Set period for temperature control */
    timer_setDelay(&temp_timer, TEMP_PERIOD);
    /* Set callback for temperature control */
    timer_setSoftint(&temp_timer, temp_handler, NULL);
    /* Add temperature control to event list */   
    synctimer_add(&temp_timer, &timer_list);


    /* Same here for voltage check */
    ....
}

Note that this actions must be done just once, at application startup.

If there are many jobs, it's possible to split for clarity the application into many files, in which you can confine all the logic tied to a particular module.

So, for example, temperature management operations can be put into a separate module, so they are more manageable if the number of jobs increases. Initialization can also be done into the separate file, it's enough to pass the timer list (timer_list) to the init function of the module, which we call for example temp.c:

void temp_init(List *list)
{
    ...module init...

    /* Set period for temperature control */
    timer_setDelay(&temp_timer, TEMP_PERIOD);
    /* Set callback for temperature control */
    timer_setSoftint(&temp_timer, temp_handler, NULL);
    /* Add temperature control to event list */   
    synctimer_add(&temp_timer, list);
}

void temp_handler(void)
{
   ...
}

...

The original file remains extremely clean:

void init(void)
{
    timer_init();
    
    LIST_INIT(&timer_list);

    /* Init temp check driver */
    temp_init(&timer_list);


    /* Init voltage check driver */
    voltage_init(&timer_list);

   ...
}

In the application's main loop it's enough to call synctimer_poll() to automatically execute all callbacks with the set delay:

/* Global system status */
Parameter prm;
List timer_list;

init();

for (;;)
{
    /* Handle events. */
    synctimer_poll(&timer_list);

    /* Handle parameter save/load */
    if (load_pressed())
    {
        /* Load */
        load_preset(&prm);
    }

    if (save_pressed())
    {
        /* Save */
        save_preset(&prm);
    }

    /* Check motor alarm */
    if (motor_alarmOn())
    {
        a = adc_read(SPEED_CH);
        a *= SPEED_SCALE;
        motor_setSpeed(a);
    }

}

BeRTOS' synctimers can be considered a real synchronous scheduler. They have a very low RAM and ROM overhead and they don't require the kernel. However they have a high latency because they are completely synchronous.

Next time we'll see how to increase the application's response time using the kernel.

Written by Francesco Sacchi in programming the 02 July 2010. , tag kernelprogramming patternsembedded programmingsynctimers
 Download as PDF