I have a I2C port expander with an interrupt output so that when any of the expander’s inputs change the interrupt is generated. That way I don’t need to keep polling the I2C channel to detect changes.
The I2C comm has its own thread.
Using freeRTOS, what is the correct/best way to implement the I2C querying the expander IC only when the interrupt happens?
I was thinking to have the I2C thread sitting in a loop monitoring a variable that is set only when an interrupt happens. And that variable is set by the ISR in the classic mode. Is that a good way to do it? Or is it preferrable to use some other freeRTOS API and/or functionality?
Polling a flag variable is usually not the best way when using FreeRTOS.
Busy polling eats up the CPU and other tasks with lower prio can’t run or tasks with equal prio need a Systick preemption to get a time slice to run.
I’m using a task notification signaled in the ISR to the corresponding task. After getting signaled I’m fetching the status from the I/O expander in this task. A binary semaphore could also used in a similar way. This keeps the ISR short as desired.
Meanwhile the task waits/blocks on the task notification giving up the CPU letting other tasks do their work.
What I do is connect the flag to an interrupt pin, and in the ISR I send a notification to a task responsible for processing that interurpt. So that task will wait block on a wait for that notification.
Thank you Richard,
so if I understand correctly it is like I suggested? Or am I missing perhaps some subtle detail?
For example when I mean by wait/block I was thinking to have a state machine and entering a state where with a
while(int_flag == 0); [where int_flag is actually a global variable and not sampling the actual INT flag]
so that the while loops exits only after that variable is set to 1 by the ISR.
Is that what you are also suggesting or when you say “the ISR I send a notification to a task” do you mean using some RTOS functionality?
Thank you again
I think Richard uses a similar approach as I described before.
In general with an multitasking OS resp. RTOS a task should wait for an event allowing other tasks to run and use the CPU, do the processing when the event was signaled (by an ISR or an other task) and wait again.
FreeRTOS offers a variety of mechanisms to signal/wait for events. I find task notifications pretty versatile and useful. Also it’s a very lean / fast mechanism.
Thank you Hartmut,
not sure how but I saw Richard reply but not your previous one.
I am just starting to learn about task notifications. I see there are many. Could you please point me to the ones that would be relevant for this application so I can prioritise the learning and understanding of them while also progressing with my code.
There are a couple of basic ways of using the direct to task notifications. One makes it a counting Semaphore, where you can ‘take’ it as many times as you ‘give’ it. This is perhaps the simplest method, but does ‘use up’ the whole of the notification (but you can in recent versions use indexed versions allowing multiple notification words).
The second method allows you to send/receive word and acts like a very short queue.
The third method allows you to set specific bits, and wait for (and/or clear) those bits on receiving. A sort of special case of this is you can set no bits, and wait for any notification, even if it doesn’t set any bits.
There is also an API function that basically can do any of these (and the simplified cases actually just use it and fill in the values to make it work)
For a simple interrupt signaling any of these would work. The give/take is probably the simplest.
I’d start with the basic usage RTOS Task Notifications used as light weight binary semaphore
That should be fine for your initial use case with an ISR triggered by the I/O expander INT signal connected to a GPIO pin with a corresponding task reading the status from the I/O expander and doing something with it.
In the ISR vTaskNotifyGiveFromISR is used. Note the suffix
FromISR is used for all FreeRTOS functions which can be called in ISRs.
In the task (loop) use ulTaskNotifyTake to wait for the notification signaled by the ISR.
The variant dealing with bits to set/clear can be used to handle multiple events e.g. multiple, different interrupt events in case you need that later on.
Thank you both again. Much useful and appreciated infor and help!
We do exactly this, using direct-to-task notifications to record input changes on a SX1509B I2C expander. The code is at RepRapFirmware/DueXn.cpp at 3.3-dev · Duet3D/RepRapFirmware · GitHub lines 68-95, however we have a C++ wrapper around the actual FreeRTOS calls.