When Should Software Timers Be Used Instead of vTaskDelay()

I have a question regarding FreeRTOS software timers. If I can simply call vTaskDelay() to block a task for a specific duration, then what additional benefit do software timers provide? Both seem to rely on the FreeRTOS tick count for timing. Am I missing a key difference here?

My current understanding is that vTaskDelay() pauses an existing task until the delay expires. are there other important use cases where software timers are preferred over vTaskDelay() ?

The big advantage of Software Timers is that you can have many timers, each using just a small control block for the timer, and they all share a single task, and thus have a much smaller memory footprint per timer. The related disadvantage is that since they are sharing a task, they shouldn’t do anything that takes much time or they might alter the behavior of another timer.

Thus, one use could be a simple “loop” that checks something easy to read periodically, and if the results are notable, send a message to a task to process. That task might be collecting messages from multiple timers and thus can avoid needing to be polling all the inputs all the time, allowing it to do something more substantial with a results without worrying about missing another event.

Another operation would be if the task does something and is expecting a result to happen within some period, but it has other things to do and can’t just wait. It can start a one shot timer, and then kill it if it sees the results, but it the timer expires, it can send a “timeout” message.

Hi @richard-damon , how many maximum timers we can create using software timers?

Because software timers using timer deamon task to shedule them will it overload if I create more number of timers?

And how much short do I need to keep the timer handler like how much longer I can block the timer handler in any loop?

Best regards

There is no limit on the number of timers that you can create other than the amount of memory your system has. The number of simultaeous active timer commands depend on the timer queue length which is controlled by configTIMER_QUEUE_LENGTH.

More details here: FreeRTOS software timers - FreeRTOS™

I would not EVER block in a timer handler, and (at least it used to, need to see if the problem is still there) there was code that would “assert” at times if a block crossed the timer rollover period.

Operations that block should be reserved for actual tasks. Timer callbacks are designed to be very light weight.

As for overloading the timer deamon, that is somewhat hard to do as long if you don’t block and account for the CPU usage of other timer callbacks (as you would even if you used tasks) you will tend to be ok. One issue is that timers don’t have a “priority” order, but run in time expiration order, and at the priority of the timer daemon, which is normally very high, so operations that use enough CPU themselves to affect things might want to be put into a task with a lower priority.

As to the timer queue mentioned by @aggarg since the timer daemon tends to be very high priority, commands tend not to build up in its queue, so that length tends not to be a limiting condition. A timer being “active” doesn’t use queue slots, only the COMMAND does, until the daemon processes it and adjusts the timer.

Having a VERY large number of active timers can slow down timer commands in the active timers are stored in a linked list, which may need to be scanned to find the timer and/or where to put the timer.

I see timer handlers as a kind of software interrupts.

So you indeed never ever block in a timer handler and also make sure the processing time is limited. Things like FFT transformations or other expansive computational tasks do not belong in a timer handler.

What I do use the timer handler for is for simple things like a blinking LED (a status LED blinking with a specific pattern), updating a (timed) progress bar on an LCD or other simple things.

Anything that needs more computation time or that can block on I/O or reading/writing queues I do in separate tasks.

don’t vTaskDelay() if you really really have to.

if your tasks for example ‘managaes’ some dataset, and this dataset requires cleanup on regular base. (pick your reason).

then a timer could instructs the task to do the cleanup, or a timer to do extra work.

If you would have blocked the task, then your ‘cleanup’ is required to wait.

Always consider adding a delay to be code smell.

I would disagree that vTaskDelay() is “code smell”, as it is often an essential part of a design. It is not an infrequent requirement to assert a signal for a given length of time, and if the task doesn’t have something else to do, a vTaskDelay may be the best option. Yes, sometimes you may need to have that cycle aborted, but we do have vTaskAbortDelay for very special cases, or you can do a wait for notification with a timeout to be a delay.

As to your example of a task that “manages” a dataset, that is VERY much a function of what manage means here. If it is collecting data on a specific time rate, then your “cleanup” would normally (or at least could be defined) as every Nth sample, and no need for a timer.

If the managing is adding data per requests that come in, then there won’t be a vTaskDelay call anyway, but the use of a Queue, and we could add a “Cleanup” message to that Queue.

Perhaps you see vTaskDelay as code smell, as you code already has a strong aroma that conflicts with it, or the smell isn’t from the vTaskDelay itself, but from something else that the vTaskDelay just points to.

thanks for your answer.

you are 100% right of course. I tried to make my answer simple for the user so the person has something to think about. The gist is, don’t ‘block’ a task when you don’t have to, at least not for the sake of having a delay..

I have seen libraries that uses a vTaskDelay to ‘wait’ for a AD conversion to complete, or a messages to be send. All these devices support interrupts and they should be used as such and not add a ‘one liner’ to delay, because that is initially much easer.

Having these type of libraries inserting a delay all over the place makes running a few of these libraries a true hassle to work with…

Ofcourse, I have used vTaskDelay(..) myself, but only if there was really no other solution… Or using ‘startup’ style code… For example finding the GPS baudrate and finally configure..