When a co-routine is actually executing it is said to be in the Running state. It is currently utilising the processor.
Ready co-routines are those that are able to execute (they are not blocked) but are not currently executing. A co-routine may be in the Ready state because:
A co-routine is said to be in the Blocked state if it is currently waiting for either a temporal or external event. For example, if a co-routine calls crDELAY() it will block (be placed into the Blocked state) until the delay period has expired - a temporal event. Blocked co-routines are not available for scheduling.

Valid co-routine state transitions
Low priority numbers denote low priority co-routines.
Co-routine priorities are only with respect to other co-routines. Tasks will always take priority over co-routines should you mix tasks and co-routines within the same application.
void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
{
crSTART( xHandle );
for( ;; )
{
-- Co-routine application code here. --
}
crEND();
}
The type crCOROUTINE_CODE is defined as a function that returns void and takes an xCoRoutineHandle and an index as it's parameters. All functions that
implement a co-routine should be of this type (demonstrated above).
Co-routines are created by calling xCoRoutineCreate().
Points to note:
The stack of a co-routine is not maintained when a co-routine blocks. This means variables allocated on the stack will most
probably loose their values. To overcome this a variable that must maintain its value across a blocking call must be declared
as static. For example:
void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
{
static char c = 'a';
// Co-routines must start with a call to crSTART().
crSTART( xHandle );
for( ;; )
{
// If we set c to equal 'b' here ...
c = 'b';
// ... then make a blocking call ...
crDELAY( xHandle, 10 );
// ... c will only be guaranteed to still
// equal 'b' here if it is declared static
// (as it is here).
}
// Co-routines must end with a call to crEND().
crEND();
}
Another consequence of sharing a stack is that calls to API functions that could cause the co-routine to block can only be made from the
co-routine function itself - not from within a function called by the co-routine. For example:
void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
{
// Co-routines must start with a call to crSTART().
crSTART( xHandle );
for( ;; )
{
// It is fine to make a blocking call here,
crDELAY( xHandle, 10 );
// but a blocking call cannot be made from within
// vACalledFunction().
vACalledFunction();
}
// Co-routines must end with a call to crEND().
crEND();
}
void vACalledFunction( void )
{
// Cannot make a blocking call here!
}
The default co-routine implementation included in the FreeRTOS download does not permit a blocking call to be made from
within a switch statement. For example:
void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
{
// Co-routines must start with a call to crSTART().
crSTART( xHandle );
for( ;; )
{
// It is fine to make a blocking call here,
crDELAY( xHandle, 10 );
switch( aVariable )
{
case 1 : // Cannot make a blocking call here!
break;
default: // Or here!
}
}
// Co-routines must end with a call to crEND().
crEND();
}
The following code defines a very simple co-routine that does nothing but periodically flashes an LED.
void vFlashCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
{
// Co-routines must start with a call to crSTART().
crSTART( xHandle );
for( ;; )
{
// Delay for a fixed period.
crDELAY( xHandle, 10 );
// Flash an LED.
vParTestToggleLED( 0 );
}
// Co-routines must end with a call to crEND().
crEND();
}
Thats it!
Co-routines are scheduled by repeated calls to vCoRoutineSchedule(). The best place to do this is from within the idle task by writing
an idle task hook. First ensure that configUSE_IDLE_HOOK is set to 1 within FreeRTOSConfig.h. Then write the idle task hook as:
void vApplicationIdleHook( void )
{
vCoRoutineSchedule( void );
}
Alternatively, if the idle task is not performing any other function it would be more efficient to call vCoRoutineSchedule() from within a loop as:
void vApplicationIdleHook( void )
{
for( ;; )
{
vCoRoutineSchedule( void );
}
}
The co-routine can be created within main().
#include "task.h"
#include "croutine.h"
#define PRIORITY_0 0
void main( void )
{
// In this case the index is not used and is passed
// in as 0.
xCoRoutineCreate( vFlashCoRoutine, PRIORITY_0, 0 );
// NOTE: Tasks can also be created here!
// Start the scheduler.
vTaskStartScheduler();
}
Now assume that we want to create 8 such co-routines from the same function. Each co-routine will flash a different LED at a different rate. The index parameter can be used to distinguish between the co-routines from within the co-routine function itself.
This time we are going to create 8 co-routines and pass in a different index to each.
#include "task.h"
#include "croutine.h"
#define PRIORITY_0 0
#define NUM_COROUTINES 8
void main( void )
{
int i;
for( i = 0; i < NUM_COROUTINES; i++ )
{
// This time i is passed in as the index.
xCoRoutineCreate( vFlashCoRoutine, PRIORITY_0, i );
}
// NOTE: Tasks can also be created here!
// Start the scheduler.
vTaskStartScheduler();
}
The co-routine function is also extended so each uses a different LED and flash rate.
const int iFlashRates[ NUM_COROUTINES ] = { 10, 20, 30, 40, 50, 60, 70, 80 };
const int iLEDToFlash[ NUM_COROUTINES ] = { 0, 1, 2, 3, 4, 5, 6, 7 }
void vFlashCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
{
// Co-routines must start with a call to crSTART().
crSTART( xHandle );
for( ;; )
{
// Delay for a fixed period. uxIndex is used to index into
// the iFlashRates. As each co-routine was created with
// a different index value each will delay for a different
// period.
crDELAY( xHandle, iFlashRate[ uxIndex ] );
// Flash an LED. Again uxIndex is used as an array index,
// this time to locate the LED that should be toggled.
vParTestToggleLED( iLEDToFlash[ uxIndex ] );
}
// Co-routines must end with a call to crEND().
crEND();
}
This is functionally equivalent to the standard demo file flash.c but uses co-routines instead of tasks. In addition, and just for demonstration purposes, instead of directly toggling an LED from within a co-routine (as per the quick example above) the number of the LED that should be toggled is passed on a queue to a higher priority co-routine.
Demonstrates passing data from a interrupt to a co-routine. A tick hook function is used as the data source.
The PC and ARM Cortex-M3 demo applications are already pre-configured to use these sample co-routine files and can be used as a reference. All the other demo applications are configured to use tasks only, but can be easily converted to demonstrate co-routines by following the procedure below. This replaces the functionality implemented within flash.c with that implemented with crflash.c:
void vApplicationIdleHook( void )
{
vCoRoutineSchedule( void );
}
If main() already contains an idle hook then simply add a call to vCoRoutineSchedule() to the existing hook function.
Any and all data, files, source code, html content and documentation included in the FreeRTOS distribution or available on this site are the exclusive property of Richard Barry.
See the files license.txt (included in the distribution) and this copyright notice for more information. FreeRTOSTM and FreeRTOS.orgTM are trade marks of Richard Barry.