portEND_SWITCHING_ISR when HigherPriorityTaskWoken is always FALSE

groger57 wrote on Saturday, July 29, 2017:

I’ve read the section of the manual on using the variables and macros related to using FreeRTOS in an ISR, still a question remains.
Based on one of the examples provided for Cortex-M4, I am using a non-blocking binary sempahore to synchronize a timer interrupt. The interrupt has the HigherPriorityTaskWoken set to pdFALSE. At the required number of 512 buffer samples, I call the macro xSemaphoreGiveFromISR( xTimerSemaphore, &lHigherPriorityTaskWoken ).
What is not clear at this point what the effect of not calling the “portEND_SWITCHING_ISR( HigherPriorityTaskWoken )” macro at the end of the interrupt, since HigherPriorityTaskWoken is set to pdFALSE and no context switch was requested.
The reason I am asking is, when I coded it, I (mistakenly) did not have the portEND_SWITCHING_ISR macro in there, and the code appears to be running OK. Based on what I am seeing, can it be stated that if HigherPriorityTaskWoken set to pdFALSE, that using portEND_SWITCHING_ISR is optional? I will put the portEND_SWITCHING_ISR in the code, and in future apps, I do not have access to it now but would like to know before the next time I update.


richard_damon wrote on Saturday, July 29, 2017:

At the begining of the ISR, you set the flag false. When you call the xSemaphoreGiveFromISR function, IF a higher priority task than is currently executing is woken, then that function will set the flag true (that is why you had to pass a pointer to the variable). If the ISR did not call portEND_SWITCHING_ISR() with the flag, then that woken up task will NOT get switched to at that time, but at some later time when the scheduler gets activated (like the timer tick).

groger57 wrote on Saturday, July 29, 2017:

I have 2 temporal tasks created that have explicitly set priorities of 1, and 2 respectively.
You said “… IF a higher priority task than is currently executing is woken”. Does an interrupt count as a “task” when executing?
You said: “…will NOT get switched to at that time, but at some later time when the scheduler gets activated (like the timer tick).” Sorry, I do not quite follow. Isn’t the scheduler always activated after “vTaskStartScheduler()” ? Or is it pre-empted by the Timer interrupt? (looking at the Figure 12 in the "Mastering the FreeRTOS…)
So to be clear, what difference will I see in behaviour if I now add the portEND_SWITCHING_ISR() macro?
Again -sorry for the barrage of questions, but I don’t quite follow the manual in trying to understand these relationships.

hs2sf wrote on Saturday, July 29, 2017:

Nested interrupts are a different thing. An ISR is NOT a task.
Or in other words ISRs are scheduled by HW (interrupt controller).
If the scheduler does not auto-switch to the woken up task (with curr. highest prio) from ISR, the scheduler just switches back to the preempted task which was running when the interrupt kicked in.
The next trigger for the scheduler is either the next SysTick interrupt preemption or a system call causing a re-schedule. This might be a blocking call of the currently running task or signalling a semaphore/queue to another task. Then the task with the highest prio in ready (unblocked) state is activated.

richard_damon wrote on Saturday, July 29, 2017:

An Interrupt is not a task, its running is not controlled by the scheduler, but by the interrupt hardware.

The scheduling system is started by vTaskStartScheduler, buut that doesn’t mean the scheduler is always at every instant changing who is running, because if it did, it wouldn’t leave time for your tasks to run. Instead, that function setups up things and then the scheduler gets activated a particular points in time to change from one task to another, and withouut the scheduler being run, you can’t change which task is execution at the moment.

There are several things that call the scheduler to run:

  1. The current task does a vTaskYeild()
  2. The current task does some action that blocks or suspends itself.
  3. The current task does something to wake up another task that has a higher priority then itself.
  4. An interrupt occurs that activates the scheduler. The Timer Tick interrupt will do this if you have preemption turned on, or another interrupt makes a portEND_SWITCHING_ISR call with a true parameter.

If your ISR did not call the portEND_SWITCHING_ISR macro, then the task that it woke up would start to run until one of the other things happened, and the Tick interrupt will happen in a short while, so if the woken task doesn’t need to do something critically fast, you might not notice the delay. If you add the call, the woken up task will run sooner, and you will get bettter response times (but you may not need them, yet)

My guess is that this is what is happening in your current system, my other guess is that you also have the Tick interrupt set at 1000Hz like the demos, which is faster than most system need it, which is hiding much of the issue.

groger57 wrote on Saturday, July 29, 2017:

Thank you for the thorough explanation. Now that it is quite clear, I will add the call to macro portEND_SWITCHING_ISR as it is part of a correct setup.
Your assumption on the Tick interrupt, it is at 1000Hz. I have checked in the documentation and it indicates 100Hz is typical. If I understand your comment, if it was at 100Hz, I would likely see the effect of not calling portEND_SWITCHING_ISR.
Last question - can you provide an example of where a 1000Hz tick rate would be required?

Thanks, appreciate your help!

richard_damon wrote on Sunday, July 30, 2017:

At 100Hz, there is a possible 10ms delay between when the ISR wakes the task and when the task starts (if you don’t use the portEND_SWITCHING_ISR call) which may or may not be noticable, it depends on the applica

What I use as the key to what frequency to set the tick rate is to look at several factors determined by it:

  1. What is the highest rate that I need a periodic function to run at that can’t be controlled by an interrupt. I find most higher speed periodic operations are actually tied to some periodic device interrupt. Note that for reading user interface controls, 20-50Hz tend to be more than enough (if I can’t get them to generate an interrupt when the user activates them).
  2. What is the shortest ‘Timeout’ I need to be able to handle. It isn’t uncommon to signal a device and then want to wait for a response, but if something goes wrong, you don’t want to wait indefinately. Note that this is shortest I NEED to use. Even if I expect a device to respond in under a milli-second, I normally can afford a 10-20 ms timeout in case of problems.
  3. What is the finest precision I need for a timeout. (Note, that any timeout specified starts with a 1 tick slop in the time depending on when in the tick you are, a 1 tick delay could expire in a very short time)

Thus, if you needed to trigger a sensor to make a measurement every milli-second, and you couldn’t tie that to a hardware clock, you might need a 1000Hz tick rate (but be aware that there will be some jitter in this trigger depending on how long the tick interrupt takes to run)), or if you needed to sequence something with delays, and really needed delays of 1 ms and could tolerate that delay being say 5 ms.

The cost of having the tick interrupt too high is that running the scheduler that often will eat up some of your processing time.

groger57 wrote on Sunday, July 30, 2017:

Thanks so much for a great explanation, it now makes a lot more sense. As I continue reading the manuals this will help greatly !