Homepage  

Co-routines
[FreeRTOS Fundamentals]

Co-Routine States

A co-routine can exist in one of the following states: There is currently no co-routine equivalent to a tasks Suspended state. This feature may be added in later releases.

Valid co-routine state transitions



Co-Routine Priorities

Each co-routine is assigned a priority from 0 to ( configMAX_CO_ROUTINE_PRIORITIES - 1 ). configMAX_CO_ROUTINE_PRIORITIES is defined within FreeRTOSConfig.h and can be set on an application by application basis. The higher the value given to configMAX_CO_ROUTINE_PRIORITIES the more RAM the FreeRTOS kernel will consume.

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.



Implementing a Co-Routine

A co-routine should have the following structure:
    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:



Scheduling Co-Routines

Co-routines are scheduled by repeated calls to vCoRoutineSchedule(). The best place to call vCoRoutineSchedule() is from within the idle task hook. This is the case even if your application only uses co-routines as the idle task will still automatically be created when the scheduler is started. See the example below.



Mixing Tasks and Co-Routines

Scheduling co-routines from within the idle task allows tasks and co-routines to be easily mixed within the same application. When this is done the co-routines will only execute when there are no tasks of priority higher than the idle task that are able to execute. See the example below. In addition the ARM Cortex-M3 and the PC demo applications in the FreeRTOS download demonstrate this behaviour.



Limitations and Restrictions

The benefit of a co-routines lower RAM usage when compared to an equivalent task comes at the cost of some restrictions on how a co-routine can be used. Co-routines are more restrictive and complex to use than tasks.



Quick Example

This quick example demonstrates the use of co-routines.
  1. Creating a simple co-routine to flash an LED

    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!

  2. Scheduling the co-routine

    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 );
            }
        }
    

  3. Create the co-routine and start the scheduler

    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();
        }
    

  4. Extending the example: Using the index parameter

    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();
        }
    



Demo Application Examples

Two files are included in the download that demonstrate using co-routines with queues:
  1. crflash.c

    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.

  2. crhook.c

    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:

  1. In FreeRTOSConfig.h set configUSE_CO_ROUTINES and configUSE_IDLE_HOOK to 1.
  2. In the IDE project or project makefile (depending on the demo project being used):

    1. Replace the reference to file FreeRTOS/Demo/Common/Minimal/flash.c with FreeRTOS/Demo/Common/Minimal/crflash.c.
    2. Add the file FreeRTOS/Source/croutine.c to the build.
  3. In main.c:

    1. Include the header file croutine.h which contains the co-routine macros and function prototypes.
    2. Replace the inclusion of flash.h with crflash.h.
    3. Remove the call to the function that creates the flash tasks vStartLEDFlashTasks() ....
    4. ... and replace it with the function that creates the flash co-routines vStartFlashCoRoutines( n ), where n is the number of co-routines that should be created. Each co-routine flashes a different LED at a different rate.
    5. Add an idle hook function that schedules the co-routines as:
          void vApplicationIdleHook( void )
          {
              vCoRoutineSchedule( void );
          }
      	
      If main() already contains an idle hook then simply add a call to vCoRoutineSchedule() to the existing hook function.

  4. Replacing the flash tasks with the flash co-routines means there are at least two less stacks that need allocating and less heap space can therefore be set aside for use by the scheduler. If your project has insufficient RAM to include croutine.c in the build then simply reduce the definition of portTOTAL_HEAP_SPACE by ( 2 * portMINIMAL_STACK_SIZE ) within FreeRTOSConfig.h.





Copyright (C) 2003 - 2008 Richard Barry
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.