Flow of control after a timer callback function or ISR clarification

So as the title suggests I’m interested to learn about the where the flow of control goes after the timer callback function is called or after an ISR(interrupt service routine) takes place. I was hoping it would look like the following:

Timer ends -----> Callback Function -----> flow of control returns to same line where the timer ended.
or
Interrupt ---------> ISR Function -----------> Flow of control returns to the same line where the interrupt occurred.

This is what I’m hoping my program does because I have the callback function go to sleep and wake up based on the WKP pin for the timer portion of the program. And the interrupts are basically to reset the timers based on events on a pin. I guess a timer ending counts as an interrupt and should work like one wherein it returns back to the line it was on where it stopped but I’m unsure of what it does if I put the device to sleep while in the callback function.

I would ordinarily test this on my electron and answer the question myself, however I don’t have mine with me while I’m travelling(3wks :frowning: ). So thanks in advance to anyone who knows the answer to this!

It has to work the way you have described otherwise it would be absolute chaos! Proceed with confidence!

@darthreya, software timers are run in a separate thread from the user thread. As such, the callback can be called at any time in the user code (except in an ISR). When the callback ends, control does return to where the user code was “interrupted”.

Calling sleep from the timer thread is a new one for me! Are you using deep sleep or sleep on pin?

Thanks for the responses @peekay123 and @UMD
@UMD : I was hoping so, I just wanted to clarify since I couldn’t really perform a sanity check without my device :smile:
@peekay123: I’m using both depending on the situation really. I want to make sure that when I have a consistent power source I put it on SLEEP_NETWORK_STANDBY with it waking on an event in a sensor on one of the pins but if I don’t I have deep sleep.

I’m collecting data for a short duration and I just thought I could put the thing to sleep on the interrupt/callback function. (Although as I type this I figure I could just have a flag variable and have the sleep happen after the callback function ends).

As a followup question: Do interrupts pool when you call nointerrupts(). i.e. Would calling interrupts() instantly cause an interrupt if one had in fact occurred when interrupts were turned off? And if multiple occurred, which one would go first if at all?

To answer the last part of my own question I’m assuming the priority of the interrupts would determine which one would go first as is in the documentation

@darthreya, without looking at the ref. docs for the STM32F205, I believe when multiple interrupt flags are set, they are serviced in their order of priority. However, there is no queueing of each individual interrupt (ie can only be triggered once).

I'd say that interrupts will be lost while they are disabled. Usually the reason for using interrupts is that you need near instant attention and responding too late will not have the desired effect (e.g. zero-cross detection).
This would also line up with the docs
https://docs.particle.io/reference/firmware/electron/#interrupts-

And about going to sleep in an ISR/timer callback is something I'd rather not try - better go with the flag idea.

1 Like

@peekay123: so does that mean if I have interrupt1 and interrupt2 setup and both go off(in whatever order) only one of the ISRs will be called? So ISR1 xor ISR2 && !(ISR1 and ISR2)? I guess I'll have to rank my interrupt priorities then :confused: Thanks though!

@ScruffR: Hmm, lets say you have a flag that is dependent on the timer interrupts and will change if an interrupt occurs. And I say:

nointerrupts();
if(flag){
interrupts();

Wouldn't having the timer interrupt be pooled be important if the interrupt occurs as the if statement is executed? This way the flag will change and the next time the if statement is checked it'll work as expected? I guess what I'm wondering is if there is a way to delay a timer interrupt or even have a way of at least knowing that one was triggered even though you temporarily turned them "off".

Yeah I figured it would make my life easier to just have a flag. Thanks!

@darthreya, you may be overthinking this perceived problem. To be clear, what are you calling a timer interrupt? Do you mean a hardware timer or a software timer?

@peekay123: To clarify when I say timer interrupt, I mean when the timer ends and calls the callback function so that would be the software timer.
(Also I haven’t yet had the 2 interrupt problem, I’m currently planning my code and I thought that was a potential issue and was wondering how I would get around it, I had a finite state machine that I drew out based on my previous incorrect understanding of the interrupt system, which I am now working on fixing)

If you'd rely on interrupts to get reeanbled by another interrupt setting that flag, you've got the wrong logic and interrupt handling is not meant for that use case :wink:

About your question about queued interrupts, what Paul meant is that multiple triggers of the same interrupt (while "trapped" in another ISR) will result in only one call of the respective ISR(s) after the trapping ISR finished.

@darthreya, software timers are NOT interrupts. Think of these timers running in a separate thread, much like millis() based timers. When they expire, they call the specified callback in the user code. If the user code never returns, then the other timers in the timer thread will not be updated. When a software timer expires and does the callback, unless it is a oneshot timer or you disable it, it will reset itself and start timing again.

The timers in the timer thread are called “co-operative” or “round-robin” because they will run one after the other. Much like an ISR, keeping the callback short will prevent problems with the other timers.

Maybe, I guess I'm mixing up the logic I use for interrupt handling in multithreading and whatever logic I should be using for this. Also thanks for clearing up that! I appreciate the help and the clarifications as I stumble through this lol. I feel like if I had my device I'd be testing most of this myself without sounding as ridiculous as I probably did :grimacing: Any tips on how I can improve my knowledge on the subject? (Apart from documentation)

@peekay123: So do the callbacks also occur on the separate thread? I'm not sure how exactly I'm supposed to think of it(major case of inexperience), I thought it was like an interrupt because I figured it would stop user code and then run the callback function and when done return to it. (Also I didn't realize that timers would reset themselves and start on their own once they were done, I'll add timer.stop() to the callback function).

That's a tough one via a board like this, since we don't really know what your current status is.
The quickest way to fill the gaps would be a one-to-one tutorship with someone who has some embedded experience (or some low level computer insight).
The other longer way is digging through the whole topic the standard step-by-step way.

About the question you asked Paul, multi tasking (and multi threading in some way too) is still somehow synchronized by the OS which (simplified) just cycles through all the processes that require some "CPU" time and assigns a time slice to the next in the queue, then stops that process and assigns the next slice to the next process and so on.
On the other hand interrupts can occure absolutely async and would interrupt any process there and then - which is not the case with the former "processes", they have to wait till it's their turn. And if one task/process is incooperative and hoggs time slices others tasks/processes will be impacted, but not interrupts - they can force their way in to get CPU cycles.

That's probably what I'm going to end up doing, I don't know anyone with embedded experience who has the time to tutor me on all of this. Also thanks for the clarification about multi-tasking and the interrupts, that helps a lot!

I'll post an update in a few days if I do manage to make headway on my project. Thanks for all the help!

@darthreya, the FreeRTOS environment used by Particle uses preemptive multi-tasking based on an interrupt and time-slice method. This is done using supervisory code which runs at the highest priority. This supervisor will basically, at 1ms intervals, cycle through threads based on their priority and whether they can run or not. For example, a thread waiting on an event will not be given processing time if that event has not occurred yet. A good RTOS programmer will create code that does not unnecessarily use processing time by leveraging events and other mechanisms so as to not hog cpu time. So essentially, each thread gets a slice of time at regular intervals, with each thread “appearing” to run independently of the others. In a multi-processor environment, different threads can run on different processors.

In FreeRTOS, interrupts are also managed with priorities to work cooperatively with threads. Another big thing with any RTOS is the management of shared resources like SPI, I2C and Serial. To avoid “collisions” where one thread tries to use a resource that another thread is already using, the RTOS provides mechanisms including MUTEX (mutual exclusion) flags.

In the case of software timers on FreeRTOS, the quick answer to the callback is that it runs in the timer thread. This means that a callback using a lot of local variables may exceed the timer thread’s stack so care has to be taken. When the timer thread detects a timer has expired and it calls its callback function, the user thread is already not running (since the timer thread is!). The only things that can preempt the callback are higher priority threads (system) or interrupts.

I have given you a somewhat simplistic view of an RTOS. To get a better understanding of different types of RTOS (preemptive vs cooperative for example), I suggest you do a Google search.