This page describes the functionality of the files within the standard RTOS demo projects.
The descriptions relate to the files in the Demo/Common/Full directory. Their equivalents in the
Demo/Common/Minimal directory will have similar functionality but use less RAM and not contain any console IO.
Creates 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).
Tests different scenarios to ensure tasks do not exit queue send or receive functions prematurely. A description of the tests is included within the code itself.
Creates two tasks that operate on an interrupt driven serial port. A loopback connector should be used so that everything that is transmitted is also received. The serial port does not use any flow control. On a standard 9way 'D' connector pins two and three should be connected together.
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.
A simple demonstration of the use of counting semaphores.
This demo application file demonstrates the use of queues to pass data
between co-routines and the use of the co-routine index parameter.
N 'fixed delay' co-routines are created that just block for a fixed
period then post the number of an LED onto a queue. Each such co-routine uses its index parameter as an index into array in order to obtain
the block period and LED that is flashed. A single 'flash' co-routine is also created
that blocks on the same queue, waiting for the number of the next LED it
should flash. Upon receiving a number it simply toggle the instructed LED
then blocks on the queue once more. In this manner each LED from LED 0 to
LED N-1 is caused to flash at a different rate.
The 'fixed delay' co-routines are created with co-routine priority 0. The
flash co-routine is created with co-routine priority 1. This means that
the queue should never contain more than a single item. This is because
posting to the queue will unblock the higher priority 'flash' co-routine
which will only block again when the queue is empty. An error is indicated
if an attempt to post data to the queue fails - indicating that the queue is
This demo file demonstrates how to send data between an ISR and a
co-routine. A tick hook function is used to periodically pass data between
the RTOS tick and a set of 'hook' co-routines.
hookNUM_HOOK_CO_ROUTINES co-routines are created. Each co-routine blocks
to wait for a character to be received on a queue from the tick ISR, checks
to ensure the character received was that expected, then sends the number
back to the tick ISR on a different queue.
The tick ISR checks the numbers received back from the 'hook' co-routines
matches the number previously sent.
If at any time a queue function returns unexpectedly, or an incorrect value
is received either by the tick hook or a co-routine then an error is
This demo relies on each 'hook' co-routine to execute between each
hookTICK_CALLS_BEFORE_POST tick interrupts. This and the heavy use of
queues from within an interrupt may result in an error being detected on
slower targets simply due to timing.
Create a single persistent task which periodically dynamically creates another four tasks. The original task is called the creator task, the four tasks it creates are called suicidal tasks.
Two of the created suicidal tasks kill one other suicidal task before killing 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. If this number is exceeded an error is flagged.
The first test creates three tasks - two counter tasks (one continuous count and one limited count) and one controller. A "count" variable is shared between all three tasks. The two counter tasks should never be in a "ready" state at the same time. The controller task runs at the same priority as the continuous count task, and at a lower priority than the limited count task.
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.
Creates eight tasks, each of which flash an LED at a different rate. The first LED flashes every 125ms, the second every 250ms, the third every 375ms, etc.
The LED flash tasks provide instant visual feedback. They show that the RTOS scheduler is still operational.
The PC port uses the standard parallel port for outputs, the Flashlite 186 port uses IO port F.
Creates eight tasks, each of which loops continuously performing an (emulated) floating point calculation.
All the tasks run at the idle priority and never block or yield. This causes all eight 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.
Tests the extra queue functionality introduced in FreeRTOS.org V4.5.0 - including xQueueSendToFront(), xQueueSendToBack(), xQueuePeek() and mutex behaviour.
Much more information is provided in the file itself.
This does the same as flop.c, but uses variables of type long instead of type double.
As with flop.c, the tasks created in this file are a good test of the RTOS scheduler context switch mechanism. The processor has to access 32bit variables in two or four chunks (depending on the processor). The low priority of these tasks means there is a high probability that a context switch will occur mid calculation. See the flop.c documentation for more information.
Defines one of the more complex set of demo/test tasks. They are designed to stress test the queue implementation though pseudo simultaneous multiple reads and
multiple writes from both tasks of varying priority and interrupts. The interrupts are prioritised such to ensure that nesting occurs (for those ports that support it).
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.
This is a very simple queue test. See the BlockQ.c documentation for a more comprehensive version.
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.
Manages a queue of strings that are waiting to be displayed. This is used to ensure mutual exclusion of console output.
A task wishing to display a message will call vPrintDisplayMessage (), with a pointer to the string as the parameter. The pointer is posted onto the xPrintQueue queue.
The task spawned in main.c blocks on xPrintQueue. When a message becomes available it calls pcPrintGetNextMessage () to obtain a pointer to the next string, then uses the functions defined in the portable layer FileIO.c to display the message.
NOTE: Using console IO can disrupt real time performance - depending on the port. Standard C IO routines are not designed for real time applications. While standard IO is useful for demonstration and debugging an alternative method should be used if you actually require console IO as part of your application.
Creates three tasks that demonstrate queue peeking behaviour. Each task is given a different priority to demonstrate the order in which tasks are woken as data is peeked from a queue.
Demonstrates the use of recursive mutexes.
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
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.
Creates two sets of two tasks. The tasks within a set share a variable, access to which is guarded by a semaphore.
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.
Copyright (C) 2004-2010 Richard Barry. Copyright (C) 2010-2015 Real Time Engineers Ltd.
Any and all data, files, source code, html content and documentation included in the FreeRTOSTM distribution or available on this site are the exclusive property of Real Time Engineers Ltd..
See the files license.txt (included in the distribution) and this copyright notice for more information. FreeRTOSTM and FreeRTOS.orgTM are trade marks of Real Time Engineers Ltd.