Task blocking "paradigm"


This has come up a couple of times recently, I’ve searched for an elegant solution and I’ve been down several avenues and I have a solution, but it does make me wonder why it’s not possible to achieve using more lightweight solutions.

Essentially I have this kind of arrangement:

“Work Task”.

The work task consists of a loop, that continually runs (yielding as necessary), it has to run in a “spin loop” because it’s accepting data via SPI and the bus is running at 24MHZ and there’s a reasonable amount of data coming in that I have to shift out over USB.

I’ll use CDC as an example, but any bulk interface will do with the appropriate “scaffolding”.

When there is no host connected to the CDC port I want the task to effectively sleep, I don’t need to know what’s coming over the SPI because I’m bot sending it on.

This obviously minimises the load, while CDC is not connected, the worker task is not looping.

The worker task needs to loop because of the speed of the SPI bus, I’m using an STM32FX07 CPU and because of the data rate, I’m using DMA to transfer it into a circular buffer.

Now the STM32 only has half and full complete messages and as I don’t know how many bytes are going to be sent, my only real way of dealing with this is to have a large buffer which the DMA writes to, and for me to continually check to see if new data is available and if so, put it into a packet and shove over USB.

All good.

But, it’s the case where the CDC port is not connected, I want the task to block, there’s no need for it to run, no need to attempt to put anything over the CDC port or read.

Now vTaskSuspect and vTaskResume go out the window, I’ve read plenty about them and lots of posts from Richard about using them and potential race conditions, so they’re out.

The next best solution looked to be Task Notifications, they’re lightweight and they look ideal, however, there’s no “xTaskNotifyStateClearIndexedFromISR” which means I can’t block the task from an ISR.

My intention was to have a notification bit for “Connected”, this bit could be set and cleared from an ISR (i.e the CDC line state packet) and then all that the worker task needs to do is check if that bit is set, in the check it would be set to not clear it.

So I got stuck there.

Finally I did manage to use an event group to create this logic because I can both set and clear from an ISR context and in my worker task I can wait for that bit to be set and at the same time not have it automatically cleared, so in this case the task simply waits for that bit and as long as the host is connected to CDC then the task will run, the moment it disconnects, the ISR clears that bit in the event group and back to sleep we go.

This works really well, but I can’t help but wonder if there is some underlying reason why the more lightweight notifications don’t have the ability to clear the notification from an ISR context, since the behaviour is similar to event groups.

Have I missed something obvious?
Is there a better way of approaching this paradigm?


I think part of the issue is you haven’t defined your problem quite right.

First, you should NOT need to move the data from SPI to the USB as soon as it arrives, as the USB bus its that tight of a connection. The task can block until either a sufficient block of data has arrived or a significant amount of time has passed (like milliseconds).

Second, the task notifications should work just fine, the ISRs set a notification if it thinks the task has something to do (data comes in), and the task waits and clear on that notification. If there is no USB CDC connection, then the task blocks on a different notification waiting for the CDC connection (perhaps shutting down the SPI transfers at the same time).

You don’t need a notification bit for “Connected”, but just a global status word, with a notification on the transition from not connected to connected. The task can just check this flag each time it processes data and when it sees the connection down it stops processing and waits for a connection event.

Hi, thanks for the reply.

I probably should have been explained a little more about the exact conditions of this, the STM32 running FreeRTOS in this case is the slave and when I say SPI, I really meant synchronous serial rather than a typical SPI peripheral, this is not a normal SPI peripheral type arrangement where you might send a few bytes to and from it reading and writing to registers in the SPI peripheral.

This is instead a stream of data coming out of a CPLD at 24MHZ where NSS might return high for a handful of cycles before the CPLD asserts it again, 24MHZ is also not currently fast enough for the data stream, it should actually be 32MHZ but I forgot to get the correct oscillator I’m using 24MHZ as a stop-gap just to get the CPLD HDL done and communicating with the STM32.

Which leads around to the actual question I probably trying to ask (in a very roundabout way) was to satisfy my curiosity more than anything else, which was why it’s not possible to clear a notification from an ISR. I can probably guess the answer already and I’ve donned my flame suit….and it’s because probably makes zero sense, “hey task, do something….oh hang on false alarm, move along, nothing to see here”, although I’m curious why the behaviour is possible on event groups and not notifications.

The final part of your reply is of course the clean answer, you split the two parts and deal with wakeup with one FreeRTOS blocking call and then the spin loop can monitor a variable to exit the spin loop, I was just trying to “rtosify” it all into a single call, probably because I had the situation recently where I wanted to clear a notification from an ISR within the last 6 months, although I can’t remember why, or what I did or anything.

I appreciate the reply, I’ve used FreeRTOS since V2 and it still amazes me with just how great it is.

Yes, I think the reason it isn’t supported is that it doesn’t really make sense. You might want one task to clear another’s notification if you have a ”set” of tasks defined to handle something, so the first one that gets it clears the others, but this doesn’t usually happen in an ISR.

Note, I think you idea was flawed, in that the “Notification” is (I believe) always cleared when received, the VALUE of the word isn’t, but the presence of a notification is (which is what xTaskNotifyWait checks).

If you are just trying to set the VALUE to zero, that can be done with xTaskNotifyFromISR with an action of eSetValueWithOverwrite.

Yeah, I tried that and it didn’t work, I at least have an explanation as to why now.

I’ve read and re-read the xNotifyWaitIndexed API page over and over and I can’t see any mention that the bit is cleared, I interpreted that page differently to how it actually works and assumed that setting ulBitsToClearOnEntry to zero and ulBitsToClearOnExit would result in the bit not being cleared, but that isn’t the behaviour, the bit as you mention is always cleared.