Protothreads & timers

How to build a simple timer system in protothreads

Often in an embedded application there is the need to wait for a certain time at a certain point in the program. Usually it is advantageous if the application can carry on doing other things while the time at the waiting point elapses. Protothreads offer very simple and elegant way of achieving this. In this article I will show you how I did it.

In an application where you would like the application to wait at a certain point, you could achieve this at a certain point by doing this in a protothread:

PT_THREAD(task1(PT_HANDLE_PTR pt))
{
PT_BEGIN(pt);
....
    PT_TIMER_WAIT(pt,timer_var,1000);
    ....
PT_END(pt);
}

The PT_TIMER_WAIT macro waits for 1000 ticks at the waiting point, using variable timer_wait. At this point it might be useful to explain how I usually implement timers. I declare an array of timers. Then, in a seperate timer task, I do the following:

PT_THREAD(MVUTIMER_ServiceFunc(PT_HANDLE_PTR pt))
{
    PT_THREADVAR bool fDone;
    PT_THREADVAR bool fTick;
    PT_THREADVAR int i;

    PT_BEGIN(pt);

    fDone = false;
    fTick = false;

    while (!fDone)
    {
        NVIC_DisableIRQ(TIMER_32_0_IRQn); // disable interrupt
        if (MVUTIMER_u32Counter1ms != timer32_0_counter)
        {
            fTick = true;
            fDone = false;
            //dprintf(DEBUG_DEBUG,"MVUTIMER_ServiceFunc (1): %d %d\n",
MVUTIMER_u32Counter1ms,timer32_0_counter);
            MVUTIMER_u32Counter1ms++;
            //dprintf(DEBUG_DEBUG,"MVUTIMER_ServiceFunc (2): %d %d\n",
MVUTIMER_u32Counter1ms,timer32_0_counter);
            // MVULPCHW_Toggle_PIO0_1();
        }
        else
        {
            fTick = false;
            fDone = true;
        }
        NVIC_EnableIRQ(TIMER_32_0_IRQn); // enable the interrupt

        if (fTick)
        {
            for (i = 0; i < APPCONFIG_MVUTIMER_NUM_TIMERS; i++)
            {
                if ((MVUTIMER_u32Timer[i] != MVUTIMER_STATE_STOPPED) &&
                    (MVUTIMER_u32Timer[i] != 0uL))
                {
                    MVUTIMER_u32Timer[i]--;
                }
            }
        }
        fTick = false;
    }

    PT_END(pt);
}

The example code was written on a LPC1124 from NXP, but it should be easy to adapt for other microcontrollers. This task makes use of a variable MVUTIMER_u32Counter1ms to determine when the interrupt handler has taken place. Whenever the task is run, it performs timer countdown on all elements of the array. Elements of the array that are equal to MVUTIMER_STATE_STOPPED are considered to be stopped. Usually I defined MVUTIMER_STATE_STOPPED equal to 0xFFFFFFFF.

This is the timer interrupt handler:

void TIMER32_0_IRQHandler(void)
{  
  timer32_0_counter++;
  if ( LPC_TMR32B0->IR & 0x01 )
  {  
    LPC_TMR32B0->IR = 1;                /* clear interrupt flag */
    //timer32_0_counter++;
  }
  if ( LPC_TMR32B0->IR & (0x1<<4) )
  {  
    LPC_TMR32B0->IR = 0x1<<4;            /* clear interrupt flag */
    //timer32_0_capture++;
  }
  return;
}

I usually choose to have a 1ms system timer tick, so that all across my application I work in ms units. (That will depend on your specific requirements). I then declare the following macro:

#define PT_TIMER_WAIT(pt, timer, time)    \
  do {                        \
    (timer) = (time);       \
    PT_WAIT_UNTIL(pt, (timer) == 0);        \
  } while(0)

This macro will assign the required time to the timer variable timer, and block until the timer has expired. It will give other protothreads (like for instance the MVUTIMER_ServiceFunc task) the oppurtunity to execute while the timer has not expired.

I hope somebody finds this useful. If you have any questions or suggestions please comment.

armand Saturday 19 April 2014 - 11:23 am | | Programming, Embedded

No comments

(optional field)
(optional field)
Remember personal info?
Small print: All html tags except <b> and <i> will be removed from your comment. You can make links by just typing the url or mail-address.