Homepage  

联合程序
[FreeRTOS 基础]

Co-Routine States

一个联合程序可以以下面的状态中的一种存在: 目前没有联合程序等价于任务的暂停状态,这个特点将在以后的版本中加入。

有效的联合程序状态转换

 


联合程序属性

每个联合程序被分配一个从 0 到 ( configMAX_CO_ROUTINE_PRIORITIES - 1 ) 的优先级。configMAX_CO_ROUTINE_PRIORITIES 在 FreeRTOSConfig.h 中定义并在基本系统中设置。configMAX_CO_ROUTINE_PRIORITIES 的数值越大 FreeRTOS 消耗的 RAM 越多。

低优先级联合程序使用小数值。

联合程序优先级只针对其他联合程序,任务的优先级总是高于联合程序。



执行联合程序

联合程序使用下面的结构:
    void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
    {
        crSTART( xHandle );

        for( ;; )
        {
            -- Co-routine application code here. --
        }

        crEND();
    }
 
类型 crCOROUTINE_CODE 定义为返回值为 void 的函数,使用 xCoRoutineHandle 和一个索引值作为参数。所有执行一个联合程序的函数都是这个类型(如上面所示)。

调用 xCoRoutineCreate() 创建联合程序。

要点说明:



调度联合程序

联合程序由反复调用 vCoRoutineSchedule() 进行调度,调用 vCoRoutineSchedule() 的最佳位置是在空闲任务钩子中。这是因为即使你的系统只使用联合程序,当调度开始后仍将自动创建空闲任务。参考下面的例子。



混合任务和联合程序

从空闲任务调度联合程序就很容易在同一个系统中混合使用任务和联合程序。这样只有在没有比空闲任务优先级更高的任务运行时联合程序才会执行,查看下面的例子。此外 ARM Cortex-M3 和 PC 演示例子中演示了这个方式。



限制和约束

联合程序和任务相比来说,优点是 RAM 占用率更低,但是联合程序相比任务来说更加复杂和有更多限制。

 


快速例子

这个快速例子演示了联合程序的使用。
  1. 创建一个简单的联合程序闪动一个 LED

    下面代码定义了一个简单的联合程序,除了周期性改变 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();
        }
    
    就是这样!

  2. 调度联合程序

    联合程序由反复调用 vCoRoutineSchedule() 进行调度。最佳的位置是在空闲任务中放置空闲任务钩子。首先保证 FreeRTOSConfig.h 中的宏 configUSE_IDLE_HOOK 设置为 1 ,然后在空闲任务钩子中写入:

        void vApplicationIdleHook( void )
        {
            vCoRoutineSchedule( void );
        }
    
    或者,如果空闲任务没有执行其他任何函数,更有效率的做法是在循环中调用 vCoRoutineSchedule() :
        void vApplicationIdleHook( void )
        {
            for( ;; )
            {
                vCoRoutineSchedule( void );
            }
        }
    

  3. 创建联合程序并启动调度

    联合程序可以在 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. 扩展例子:使用索引参数

    现在假定我们希望从同一个函数中创建 8 个这样的联合程序。每个联合程序将以不同速率闪动一个不同的 LED。索引参数可以用于在联合程序之间进行区分。

    这次我们将创建 8 个联合程序并给每个指定不同的索引值。

        #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();
        }
    
    同时扩展联合程序函数,使得每个使用不同的速率显示 LED。
        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();
        }
    

 


例子演示程序

下载的演示程序中包括两个使用联合程序的文件:
  1. crflash.c

    这个文件功能上等同于标准的演示文件 flash.c,但是使用了联合程序取代了任务。此外,因为只是为了用于演示目的,没有在联合程序中直接改变 LED(如同上面的快速例子那样),需要改变 LED 的数量放入到高优先级的联合程序队列中。

  2. crhook.c

    演示了从中断到联合程序交换数据。使用一个节拍钩子函数作为数据源。

PCARM Cortex-M3 的演示程序已经预先配置好使用这些相同的联合程序文件,可以作为参考。其他的演示程序只使用了任务,但是可以很容易的按照下面步骤转换为联合程序。主要是使用 crflash.c 替换 flash.c :

  1. FreeRTOSConfig.h 中设置 configUSE_CO_ROUTINES 和 configUSE_IDLE_HOOK 为 1.
  2. 在 IDE 项目或项目的 makefile (与使用的演示程序有关):

    1. 替换文件 FreeRTOS/Demo/Common/Minimal/flash.cFreeRTOS/Demo/Common/Minimal/crflash.c.
    2. 添加文件 FreeRTOS/Source/croutine.c.

  3. main.c:

    1. 包含头文件 croutine.h,它定义了联合程序的宏和函数声明。
    2. 替换包含的头文件 flash.hcrflash.h.
    3. 删除创建 flash 任务的函数调用 vStartLEDFlashTasks() ....
    4. ... 并替换为创建 flash 联合程序 vStartFlashCoRoutines( n ), 这里 n 是需要创建联合程序的数目。每个联合程序以不同的速率控制一个不同的 LED。
    5. 添加空闲钩子函数调度联合程序:
          void vApplicationIdleHook( void )
          {
              vCoRoutineSchedule( void );
          }
      	
      如果 main() 已经包含了一个空闲钩子,那么简单的添加 vCoRoutineSchedule() 函数调用即可。

  4. 用 flash 联合程序替换 flash 任务意味着至少减少分配了两个堆栈以及更少用于调度器的堆空间。如果你的项目的 RAM 不足以包含 croutine.c 那么在 FreeRTOSConfig.h 中减少 portTOTAL_HEAP_SPACE 为 ( 2 * portMINIMAL_STACK_SIZE ) .
  5.  

     


翻译:   邵子扬
EMail:   shaoziyang@126.com
Blog:    http://blog.ednchina.com/shaoziyang
2008年10月

 

Site Meter