3 years, 1 month ago.

Is it possible to call RtosTimer::start()/stop() inside of a ISR?

Hi, just trying to confirm my findings and maybe to save some time to other developers. I had the intention of triggering a RtosTimer (i.e. calling start/stop method) inside of a ISR function. As it didn't seem to work properly(the timer wasn't started/stoped at all), I prepared a test case. In it, I'm trying the RtosTimer::start()/stop() methods in the main() function where it works properly. Also I'm trying both methods in the ISR and they didn't work.

Due to problems with the spam filter, I'm posting it in fragments :-D

This the test case I'm trying

#include "mbed.h"
#include "rtos.h"

DigitalOut myled(LED1);
Serial pc(SERIAL_TX, SERIAL_RX);

volatile int count_timer = 0;
volatile int state_timer = 0;
volatile int count_button = 0;

RtosTimer * timer;

void increment() {
   count_button ++;
        
    if(state_timer==0)
    {
        myled = 1 - myled;
        timer->start(1000);
    }
    else{
        timer->stop();
        myled = 1 - myled;
    }
    state_timer = 1 - state_timer;
}
  
InterruptIn interrupt(PC_9);

void timeout(void const * arg){
    count_timer += 1;
    //myled = 1 - myled;
    }

int main() {

  myled = 1;
  timer = new RtosTimer(timeout, osTimerPeriodic, (void *) 0);
  interrupt.rise(&increment);
  interrupt.mode(PullUp);
  
  while(1){
    Thread::wait(2100);
    if (count_button==10) timer->start(1000);
    if (count_button==20) timer->stop();
    pc.printf("count_timer: %i count_button %i\r\n", count_timer, count_button);
  }
}

posted by Carlos López Molina 04 Jan 2017

And this is the result:

  • count_timer: 0 count_button 0
  • count_timer: 0 count_button 0
  • count_timer: 0 count_button 0
  • count_timer: 0 count_button 0
  • count_timer: 0 count_button 1 <== there is one interrupt(count_button increments), and the timer should start but it doesn't
  • count_timer: 0 count_button 1
  • count_timer: 0 count_button 2
  • count_timer: 0 count_button 4
  • count_timer: 0 count_button 5
  • count_timer: 0 count_button 6
  • count_timer: 0 count_button 7
  • count_timer: 0 count_button 8
  • count_timer: 0 count_button 9
  • count_timer: 0 count_button 10 <==when count_button hits 10, the main loop starts the timer. count_timer is incremented accordingly
  • count_timer: 2 count_button 10
  • count_timer: 4 count_button 10
  • count_timer: 6 count_button 11 <==although there are further interrupts, they fail to call the stop method, as *count_timer keeps on incrementing
  • count_timer: 8 count_button 12
  • count_timer: 10 count_button 12
  • count_timer: 12 count_button 12
  • count_timer: 14 count_button 14
  • count_timer: 16 count_button 14
  • count_timer: 18 count_button 14
  • count_timer: 21 count_button 15
  • count_timer: 23 count_button 15
  • count_timer: 25 count_button 17
  • count_timer: 27 count_button 18
  • count_timer: 29 count_button 20 <== when count_button reaches 20, the timer is stopped at the main loop
  • count_timer: 29 count_button 20
  • count_timer: 29 count_button 20
  • count_timer: 29 count_button 21
  • count_timer: 29 count_button 21
  • count_timer: 29 count_button 24
posted by Carlos López Molina 04 Jan 2017

I got the answer digging in rtos code of RtosTimer constructor() and start(). They are using mbed's osTimerCreate and osTimerStart, and if you read the lines 3, 14, 20 of that code, there states clearly that it is not allowed in ISR. Lesson learned: one can find many answers and learn a lot reading the source code of mbed. Also it's worth checking the return codes of system calls :-D

This is the code

/// Create timer
osTimerId osTimerCreate (osTimerDef_t *timer_def, os_timer_type type, void *argument) {
  if (__get_CONTROL() == MODE_IRQ) return NULL;           // Not allowed in ISR
  if ((__get_CONTROL() == MODE_SUPERVISOR) && (os_running == 0)) {
    // Privileged and not running
    return   svcTimerCreate(timer_def, type, argument);
  } else {
    return __svcTimerCreate(timer_def, type, argument);
  }
}

/// Start or restart timer
osStatus osTimerStart (osTimerId timer_id, uint32_t millisec) {
  if (__get_CONTROL() == MODE_IRQ) return osErrorISR;     // Not allowed in ISR
  return __svcTimerStart(timer_id, millisec);
}

/// Stop timer
osStatus osTimerStop (osTimerId timer_id) {
  if (__get_CONTROL() == MODE_IRQ) return osErrorISR;     // Not allowed in ISR
  return __svcTimerStop(timer_id);
}
posted by Carlos López Molina 08 Jan 2017

1 Answer

3 years, 1 month ago.

May be another way of doing it is :

Set as volatile int global flag.

Create an another thread that loops checking the value of this volatile int.

Inside your isr, juste set the volatile int to an value or an other.

pseudo code

volatile int startStop=0;

void increment() 
{
    count_button ++;
        
    if(state_timer==0)
    {
        myled = 1 - myled;
       startStop=1;
    }
    else{
       startStop=0;
        myled = 1 - myled;
    }
    state_timer = 1 - state_timer;
}

void CheckStartStop( void const *arg)
{
  while(1)
  {
   if (startStop==0)
      timer->stop();
   if (startStop==1)
     timer->start(1000);
 }
}

int main()
{
   ....

   Thread thread(CheckStartStop);

}

Thanks a lot Raph that's a practical way of overcoming this issue, just I see one caveat: it's actively checking the status of startStop, and I would like to save cpu cycles.

For the record, I resorted to using an EventQueue (mbed_events supported in mbed OS >= 5.2), quite a useful stuff. It is simple and doesn't require active polling. With it you can force a call of any function or method in a safe way from an ISR, and it also supports passing arguments. I found an example here: https://developer.mbed.org/blog/entry/Simplify-your-code-with-mbed-events/ Thanks a lot for your help.

posted by Carlos López Molina 04 Jan 2017