Using FreeRTOS to Improve Run Time Efficiency
Simple updates to change to an event driven structure
The previous example demonstrated how simple it was to convert the stand alone USB example into a new RTOS task for incorporation into the FreeRTOS TCP example. The demonstrated technique was however rather crude, and viewing the executing tasks in the FreeRTOS Eclipse State Viewer plug-in reveals one of the reasons why; one of the TCP related tasks and the Virtual COM / CDC task are each consuming about 50% of the total CPU time available.
Viewing the RTOS task’s run-time statistics in the Eclipse plug-in
Both tasks are polling their respective interfaces without ever entering the Blocked state (if a task is in the Blocked state then it is not using any CPU time), so the real time scheduler is sharing processing time between them. This has a number of disadvantages, including:
CPU time is being wasted because the tasks are running even when there
is nothing to do. The wasted CPU time could be better utilised by
adding more functionality into the application, or the CPU could
simply be placed into a low power mode when there was no real
processing that needed to be performed.
The tasks have to run at the lowest priority, otherwise they will
starve all lower priority tasks of getting any CPU time at all.
The execution pattern and behaviour of the tasks is dependent on the
both the hardware speed and other software that is running on the
As the behaviour of the tasks is dependent on other software that is
running on the same device the interfaces they implement cannot be
classified as exhibiting deterministic behaviour.
The steps on this page show how, through the addition of some simple FreeRTOS primitives, the exact same TCP and USB functionality can be obtained in a system in which the TCP and USB tasks consume a tiny fraction of the available CPU time. The image below shows the task’s run time statistics after the modifications have been made. This time nearly all the CPU time is being consumed by the Idle task – which is the task that runs when there are no application tasks that need to run. Implementing an Idle task hook function that places the CPU into a low power state will save a lot of power – using FreeRTOS’s tickless idle mode will save a great deal more power.
After the changes, the application has the same functionality, but the Idle task is receiving nnn% of the total run time
Two techniques are demonstrated by the workflow steps below. The first technique is very simple and just places a task into the Blocked state for a fixed period when it has nothing to do. That is effective at reducing the amount of CPU time the task uses, but does not make the task truly event driven. The second technique is more sophisticated as it makes the task truly event driven and maximises responsiveness to real world events – it places the task into the Blocked state until it receives notification from an interrupt that something has happened that requires the task’s attention.
It is assumed that the workflow steps
on the previous pages have
already been completed.
Open the freertos_tcpecho.c source file, and search for a function called vSetupIFTask().
The function contains a while( 1 ) loop that is monitoring the network’s link status, and unless the link status has changed, it has nothing to do. The link status is not going to change very often, so does not need to be polled continuously.
Add an ‘else’ part that contains a call to vTaskDelay() to the existing ‘if (physts & PHY_LINK_CHANGED)’ statement. As shown below the vTaskDelay() call will place the task into the Blocked state for 500ms each time it determines the link status has not changed.
Delaying the PHY polling task when it has no actions to perform
In the previous step a simple vTaskDelay() call was used to place
a task into the Blocked state when it had nothing to do. That
technique cannot be used with the USB task because virtual COM / CDC
drivers must be very responsive. This time an RTOS semaphore is going
to be used to unblock the USB task immediately that the USB task has
actions to perform. The USB task is then free to Block on the
RTOS semaphore, knowing it will not miss any important events.
First the RTOS semaphore is declared by adding the following line somewhere in cdc_main.c:
static xSemaphoreHandle xCDCEventSemaphore;Next the RTOS semaphore is created by adding the following line at the start of the task that implements the USB Virtual COM / CDC functionality (note xSemaphoreCreateBinary() is preferred over vSemaphoreCreateBinary() from FreeRTOS V8.0.0).
vSemaphoreCreateBinary( xCDCEventSemaphore );
cdc_main.c is now making use of FreeRTOS semaphores, so the following two lines must also be added to the top of the file:
#include "FreeRTOS.h" #include "semphr.h"
Creating the RTOS semaphore
Now the RTOS semaphore has been created the implementation of the
virtual COM task is going to be updated to block on the RTOS
semaphore using a call to xSemaphoreTake().
Add the call to xSemaphoreTake() to the top of the while( 1 ) loop within the implementation of cdc_task() (still in the cdc_main.c source file).
Updating the USB task’s implementation so it blocks on the semaphore
Now the RTOS semaphore has been declared and created, and
the task is blocking on the RTOS semaphore, the final edit is to
ensure the USB CDC interrupt gives the RTOS semaphore by calling
xSemaphoreGiveFromISR() each time it
is called. Giving the RTOS semaphore unblocks the USB task to allow
the task to perform any processing necessitated by the interrupt.
Update the USB_IRQHandler() function in cdc_main.c as per the image below.
Updating the interrupt handler to ‘give’ the semaphore
Finally, verify that the updated application provides the exact
same functionality, but using a fraction of the CPU resources,
by building, downloading and executing the example for one last
Typing text into a Tera Term window that is connected to the virtual COM/CDC port
Receiving ping replies from the LPC1834 target