Demo Project Files
The files that implement the tasks that are used by all the demo applications are
located in the FreeRTOS/Demo/Common/Minimal directory. The files in the
FreeRTOS/Demo/Common/Full directory are no longer used, but are still
supplied to ensure very old examples can still be built.
The documentation below is historic as this page is no longer fully maintained. Refer to the comments at the top of the source files contained in the FreeRTOS/Demo/Common/Minimal directory for more information.
BlockQ.cCreates six tasks that operate on three queues as follows:
The first two tasks send and receive an incrementing number to/from a queue. One task acts as a producer and the other as the consumer. The consumer is a higher priority than the producer and is set to block on queue reads. The queue only has space for one item – as soon as the producer posts a message on the queue the consumer will unblock, pre-empt the producer, and remove the item.
The second two tasks work the other way around. Again the queue used only has enough space for one item. This time the consumer has a lower priority than the producer. The producer will try to post on the queue blocking when the queue is full. When the consumer wakes it will remove the item from the queue, causing the producer to unblock, pre-empt the consumer, and immediately re-fill the queue.
The last two tasks use the same queue producer and consumer functions. This time the queue has enough space for lots of items and the tasks operate at the same priority. The producer will execute, placing items into the queue. The consumer will start executing when either the queue becomes full (causing the producer to block) or a context switch occurs (tasks of the same priority will time slice).
blocktim.cTests different scenarios to ensure tasks do not exit the Blocked state prematurely. A description of the tests is included within the code itself.
The first task repeatedly sends a string to a queue, character at a time. The serial port interrupt will empty the queue and transmit the characters. The task blocks for a pseudo random period before resending the string.
The second task blocks on a queue waiting for a character to be received. Characters received by the serial port interrupt routine are posted onto the queue – unblocking the task making it ready to execute. If this is then the highest priority task ready to run it will run immediately – with a context switch occurring at the end of the interrupt service routine. The task receiving characters is spawned with a higher priority than the task transmitting the characters.
With the loop back connector in place, one task will transmit a string and the other will immediately receive it. The receiving task knows the string it expects to receive so can detect an error.
This also creates a third task. This is used to test semaphore usage from an ISR and does nothing interesting.
Note: Sending characters into and out of an ISR using a queue is very inefficient. It is done in this demo to deliberately load the system, and in so doing, test the RTOS ports. A real application should use a DMA, RAM buffer, or other more efficient technique.
Two of the created suicidal tasks delete one other suicidal task before deleting themselves – leaving just the original task remaining.
The creator task must be spawned after all of the other demo application tasks as it keeps a check on the number of tasks under the RTOS scheduler control. The number of tasks it expects to see running should never be greater than the number of tasks that were in existence when the creator task was spawned, plus one set of four suicidal tasks, plus the Idle task, and optionally the timer service task. If this number is exceeded an error is flagged.
One counter task loops indefinitely, incrementing the shared count variable on each iteration. To ensure it has exclusive access to the variable it raises its priority above that of the controller task before each increment, lowering it again to its original priority before starting the next iteration.
The other counter task increments the shared count variable on each iteration of its loop until the count has reached a limit of 0xff – at which point it suspends itself. It will not start a new loop until the controller task has made it “ready” again by calling vTaskResume (). This second counter task operates at a higher priority than controller task so does not need to worry about mutual exclusion of the counter variable.
The controller task is in two sections. The first section controls and monitors the continuous count task. When this section is operational the limited count task is suspended. Likewise, the second section controls and monitors the limited count task. When this section is operational the continuous count task is suspended.
In the first section the controller task first takes a copy of the shared count variable. To ensure mutual exclusion on the count variable it suspends the continuous count task, resuming it again when the copy has been taken. The controller task then sleeps for a fixed period – during which the continuous count task will execute and increment the shared variable. When the controller task wakes it checks that the continuous count task has executed by comparing the copy of the shared variable with its current value. This time, to ensure mutual exclusion, the RTOS scheduler itself is suspended with a call to vTaskSuspendAll (). This is for demonstration purposes only and is not a recommended technique due to its inefficiency.
After a fixed number of iterations the controller task suspends the continuous count task, and moves on to its second section.
At the start of the second section the shared variable is cleared to zero. The limited count task is then woken from its suspension by a call to vTaskResume (). As this counter task operates at a higher priority than the controller task the controller task should not run again until the shared variable has been counted up to the limited value causing the counter task to suspend itself. The next line after vTaskResume () is therefore a check on the shared variable to ensure everything is as expected.
The second test consists of a couple of very simple tasks that post onto a queue while the RTOS scheduler is suspended. This test was added to test parts of the RTOS scheduler not exercised by the first test.
The LED flash tasks provide instant visual feedback. They show that the RTOS scheduler is still operational.
All the tasks run at the idle priority and never block or yield. This causes all the tasks to time slice with the idle task. Running at the idle priority means that these tasks will get pre-empted any time another task is ready to run or a time slice occurs. More often than not the pre-emption will occur mid calculation, creating a good test of the RTOS schedulers context switch mechanism – a calculation producing an unexpected result could be a symptom of a corruption in the context of a task.
Note: Newer demos create more sophisticated “register test” tasks for a more robust test of the context switch mechanism.
The test ensures that, while being accessed from three tasks and two interrupts, all the data sent to the queues is also received from the same queue, and that no duplicate items are either sent or received. The tests also ensure that a low priority task is never able to successfully read from or write to a queue when a task of higher priority is attempting the same operation.
Creates two tasks that communicate over a single queue. One task acts as a producer, the other a consumer.
The producer loops for three iteration, posting an incrementing number onto the queue each cycle. It then delays for a fixed period before doing exactly the same again.
The consumer loops emptying the queue. Each item removed from the queue is checked to ensure it contains the expected value. When the queue is empty it blocks for a fixed period, then does the same again.
All queue access is performed without blocking. The consumer completely empties the queue each time it runs so the producer should never find the queue full.
An error is flagged if the consumer obtains an unexpected value or the producer find the queue is full.
This demo creates three tasks all of which access the same recursive mutex:
- prvRecursiveMutexControllingTask() has the highest priority so executes first and grabs the mutex. It then performs some recursive accesses – between each of which it sleeps for a short period to let the lower priority tasks execute. When it has completed its demo functionality it gives the mutex back before suspending itself.
- prvRecursiveMutexBlockingTask() attempts to access the mutex by performing a blocking ‘take’. The blocking task has a lower priority than the controlling task so by the time it executes the mutex has already been taken by the controlling task, causing the blocking task to block. It does not unblock until the controlling task has given the mutex back, and it does not actually run until the controlling task has suspended itself (due to the relative priorities). When it eventually does obtain the mutex all it does is give the mutex back prior to also suspending itself. At this point both the controlling task and the blocking task are suspended.
- prvRecursiveMutexPollingTask() runs at the idle priority. It spins round a tight loop attempting to obtain the mutex with a non-blocking call. As the lowest priority task it will not successfully obtain the mutex until both the controlling and blocking tasks are suspended. Once it eventually does obtain the mutex it first unsuspends both the controlling task and blocking task prior to giving the mutex back – resulting in the polling task temporarily inheriting the controlling tasks priority.
Each task starts by attempting to obtain the semaphore. On obtaining a semaphore a task checks to ensure that the guarded variable has an expected value. It then clears the variable to zero before counting it back up to the expected value in increments of 1. After each increment the variable is checked to ensure it contains the value to which it was just set. When the starting value is again reached the task releases the semaphore giving the other task in the set a chance to do exactly the same thing. The starting value is high enough to ensure that a tick is likely to occur during the incrementing loop.
An error is flagged if at any time during the process a shared variable is found to have a value other than that expected. Such an occurrence would suggest an error in the mutual exclusion mechanism by which access to the variable is restricted.
The first set of two tasks poll their semaphore. The second set use blocking calls.