当一个联合程序正在执行时它就是处于运行状态,它就占用了处理器。
就绪状态的联合程序是那些可以执行(没有被阻塞)但是还没有被执行的程序。一个联合程序可能处于就绪状态是因为:
如果一个联合程序正处于暂时等待或等待外部事件,它就是处于阻塞状态。例如,联合程序调用 crDELAY() 它就会被阻塞(放入到阻塞状态)直到达到延时时间 - 一个临时事件。被阻塞的联合程序不会被调度。
有效的联合程序状态转换
低优先级联合程序使用小数值。
联合程序优先级只针对其他联合程序,任务的优先级总是高于联合程序。
void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex ) { crSTART( xHandle ); for( ;; ) { -- Co-routine application code here. -- } crEND(); }类型 crCOROUTINE_CODE 定义为返回值为 void 的函数,使用 xCoRoutineHandle 和一个索引值作为参数。所有执行一个联合程序的函数都是这个类型(如上面所示)。
调用 xCoRoutineCreate() 创建联合程序。
要点说明:
当联合程序阻塞时,它的堆栈不再继续保持。这意味着变量的数值很可能会丢失。为了解决这个问题,一个需要保持参数的变量必须声明为静态变量,例如:
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(); }
共享堆栈的另外一个后果是调用 API 函数引起联合程序阻塞只可能是联合程序本身 - 而不是从联合程序中调用函数。例如:
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! }
包括在 FreeRTOS 下载的缺省的联合程序不允许在 switch 状态中执行阻塞调用。例如:
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(); }
下面代码定义了一个简单的联合程序,除了周期性改变 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(); }就是这样!
联合程序由反复调用 vCoRoutineSchedule() 进行调度。最佳的位置是在空闲任务中放置空闲任务钩子。首先保证 FreeRTOSConfig.h 中的宏 configUSE_IDLE_HOOK 设置为 1 ,然后在空闲任务钩子中写入:
void vApplicationIdleHook( void ) { vCoRoutineSchedule( void ); }或者,如果空闲任务没有执行其他任何函数,更有效率的做法是在循环中调用 vCoRoutineSchedule() :
void vApplicationIdleHook( void ) { for( ;; ) { vCoRoutineSchedule( void ); } }
联合程序可以在 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(); }
现在假定我们希望从同一个函数中创建 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(); }
这个文件功能上等同于标准的演示文件 flash.c,但是使用了联合程序取代了任务。此外,因为只是为了用于演示目的,没有在联合程序中直接改变 LED(如同上面的快速例子那样),需要改变 LED 的数量放入到高优先级的联合程序队列中。
演示了从中断到联合程序交换数据。使用一个节拍钩子函数作为数据源。
PC 和 ARM Cortex-M3 的演示程序已经预先配置好使用这些相同的联合程序文件,可以作为参考。其他的演示程序只使用了任务,但是可以很容易的按照下面步骤转换为联合程序。主要是使用 crflash.c 替换 flash.c :
void vApplicationIdleHook( void ) { vCoRoutineSchedule( void ); }如果 main() 已经包含了一个空闲钩子,那么简单的添加 vCoRoutineSchedule() 函数调用即可。
翻译: 邵子扬
EMail: shaoziyang@126.com
Blog:
http://blog.ednchina.com/shaoziyang
2008年10月