Timer in ISR using System Thread

Hello All,

I’m having trouble using a software timer from an ISR with System Thread enabled.

The below is a simplified version of what I’m trying to achieve.

Timer timer(50, timeOutCallback());
volatile int isActive = 0;

void timeOutCallback(){

Serial.println("Stopping timer");
isActive = 0;
timer.stopFromISR();

}

void checkForConditionISR(){

isActive = 1;
timer.startFromISR();
   /* while(!wakeUpButtonDetect.isActive()) // wait till timer has started
   while(wakeUpButtonDetect.isActive()){
    // log this while timer is active
    cycles +=1;
} */
while(isActive){

    // Checking for a condition
    delayMicroseconds(500000);
}

}

I tried to use isActive() first but that remained false. So I tried to use a flag “isActive” but that too never reset.

I tried waitUntil(isAvailable()) but it caused a system error.

In all cases the condition in the ISR while loop never changes meaning the callback is never executed.

Many thanks

Hayden

You seem to have timer.startFromISR() in a non-ISR function (ISRs don’t return anything and shall not trap the code for extended periods, particularly not repeatedly 50000µs in a while()).
And if timeOutCallback() is the timer function it’s also not an ISR, so timer.stopFromISR() seems wrong there too.

Also is your isActive marked as volatile?

1 Like

To add to what @ScruffR said, posting your code would be helpful. Putting Serial.print() statements in an ISR is not good form.

1 Like

Thankyou both.

I re-edited it to remove the bool from the ISR function. It was because my code called a function from withing the ISR and I missed removing it when copying it over and simplifying it. Yes my isActive is marked as Volatile but it’s never actioned by the timer.

When an ISR is executing will the program task switch to the System thread to run the timer? How is it intended to use the Timer from ISR? My original thoughts were startFromISR() -> check isActive() in a while loop -> end ISR on condition from timer callback.

This ISR is to debounce a button press. So i was using delay inside the loop and counting the button reads over 50ms at first. But when it wasn’t working I put it to 50000us to confirm the loop would not exit. I need the timeout to change the condition in the loop.

while(isActive){ // or timer.isActive();
buttonDetects += pinReadFast(WKP);
reads += 1;
delayMicroseconds(5000); // 5ms
}

Could you explain why using Serial inside a ISR is not a good idea? I was trying to debug it and that’s the only way I could see it in real-time but maybe that was causing issues. I’ll try removing it now. How should I go about debuggin inside the ISR?

Thankyou Hayden

Hello again,

I’ve confirmed that removing Serial.println() results in the same behaviour.

In both cases the program locks up in the while loop using the condition as either isActive or timer.isActive().

I’m trying to debounce a dirty button, while the system is active, by taking the intergral for 50ms, or potentially some other period. Obviously I could do this using a local counter but I was trying to do it with the timer. But it seems the the ISR stops task switching so the timer can never start until the ISR has finished.

Thankyou

Hayden

An interrupt does interrupt normal code execution and will not allow any other code (but higher priority interrupts) run while it is executing - including the FreeRTOS thread switching process. While the interrupt runs “normal” code has lost any right of µC time. An interrupt is not a thread nor is it running on a seperate thread. ISRs must be kept short: Trigger. Execute. Leave!
Interrupts should under no circumstance trap code flow, so no prolonged (more than a few µs) loops nor delays to be used.
Debouncing should be done between ISR triggers not within (e.g. set a block flag on first entry, check flag on subsequent).

About Serial.print(): Since HW interfaces are also interrupt driven calling them from within ISRs may cause cascading/nesting issues and since an interrupt can happen anytime, it might even be that the ISR interrupts another statement of the same kind causing reentrency issues.

If an ISR is as short as it should be, debugging should be reducable to a minimum of only checking whether it fires at all and maybe set some flags to be checked outside the ISR (including GPIOs, global strings, …).
Even with in-circuit debugging interrupts need special care to be debugged.

1 Like

Thank you detailed response. much appreciated.

Instead of using the timer I’ll just set a flag and let a thread consume it afterwards. It will rely on the thread becoming active in a short time after the event so that it can read the current state of the input pin. But in the standard single threaded application using loop(); how can you debounce and register a button press event so that when the program reaches a certain point the condition will be true for button press.

And still the timer.startFromISR() what is it’s main usage? if it can’t be started until the ISR has returned?

Hayden

@hdekker, running threads on a limited resources platform is not the same as on an RPi or larger linux system. On Particle devices running FreeRTOS, threads can be created but not destroyed and the minimum slicing time is 1ms. In a single processor environment, it is common to use queues for thread communications and prevent blocking. As such, FreeRTOS has startFromISR to queue a request to the timer thread in a non-blocking and quick way to put the least load on the calling ISR. The timer thread will consume its command queue when it is given a time slice by the FreeRTOS scheduler.

You may want to do a search in the forum for “isr debouce” or “button interrupt” as others have used interrupts for debouncing buttons. If your loop() code is non-blocking, you may want to look at the clickButton library on the web IDE. It detect single, double and triple button presses along with long press events. :wink:

2 Likes