选自<<项目驱动-单片机应用设计基础>>
/* ** 一般来说,操作系统的调度算法主要有三类:时间片轮询,优先级与带优先级的时间片轮询调度 ** 不是任务主动放弃CPU而造成的任务调用就是抢占式任务调度 ** 在使用时间片轮询调度算法的操作系统中,会在2种情况下进行任务切换 ** (1)任务在调用操作系统提供的"等待"类服务( 如延时,获得信号量,等待消息等 ),会主动请求调度 ** (2)对于完全基于优先级调度算法的操作系统来说,调用任何一个系统函数,或任何一个中断服务程序 ** 结束时,都可能让高优先级的任务处于可执行状态,都可能进行任务调度 ** TinyOS51V1.1已经是时间片轮询多任务操作系统,因此不再给用户提供任务切换函数,它仅提供给TinyOS51 ** 内核使用 */ //任务控制块( tiny_os_51_core.c ) #define __TN_TASK_FLG_DEL 0x00 //任务被删除 #define __TN_TASK_FLG_RDY 0x01 //任务就绪 #define __TN_TASK_FLG_DLY 0x02 //任务延时 struct tn_os_tcb { jmp_buf jbTaskContext; //用于存储上下文信息 unsigned char ucTaskStat; //任务状态子 unsigned int uiTicks; //任务延时 }; typedef struct tn_os_tcb TN_OS_TCB; //TN_OS_TCB等效于struct tn_os_tcb static data TN_OS_TCB __GtcbTasks[ TN_OS_MAS_TASKS ]; //任务控制块的结构体数组 //OS初始化( tiny_os_51_core.c ) void tnOsInit ( void ) { TN_OS_TASK_HANDLE tnTask; //操作的任务 for ( tnTask = 0; tnTask < TN_OS_MAX_TASKS; tnTask ++ ) { __GtcbTasks[ tnTask ].ucTaskStat = __TN_TASK_FLG_DEL; //使任务处于删除状态 __GtcbTasks[ thTask ].uiTasks = 0; //设置初值 } __GthTaskCur = 0; //初始化任务号为0 } //创建任务( tiny_os_51_core.c //对于时间片轮询多任务操作系统来说,在时钟节拍中断服务的最后,可能会发生任务切换,二时钟节拍中断可能 //中断可能中断可能发生在任何时候,假设此时系统切换到一个任务控制块,而且想要创建一个任务,可想而知,两个 //任务的任务句柄是一样的,且占用同一个任务控制块,这样势必会引起系统混乱. //因此,在创建任务的过程中,必须禁止任务切换( 如先禁止中断,然后再允许中断 ) TN_OS_TASK_HANDLE tnOSTaskGreat ( void ( *pfuncTask )( void ), //指向任务函数的函数指针 idata unsigned char *pucStk //指向任务堆栈的指针 ) { TN_OS_TASK_HANDLE thRt; //返回值 //搜索是否有空闲的任务控制块 for ( thRt = 0; thRt < TN_OS_MAX_TASKS; thRt ++ ) { EA = 0; if ( __GtcbTasks[ thRt ].ucTaskStat == __TN_TASK_FLG_DEL ) { //如果搜索到有空闲的TCB,则创建任务 setTaskJmp ( pfuncTask, pucStk, __GtcbTask[ thRt ].jbTaskContext ); __GtcbTasks[ thRt ].ucTaskStat = __TN_TASK_FLG_RDY; //任务就绪 EA = 1; return thRt; } EA = 1; } return -1; //如果没有空闲的TCB,则创建任务失败,即任务句柄的返回值为-1 } //启动OS( tiny_os_51_core.c ) //TinyOsV1.1中,如果不允许中断,则时钟节拍中断服务程序不会运行,所以要使能中断 void tnOsStart ( void ) { EA = 1; longjmp ( __GtcbTasks[ 0 ].jbTaskContext ); //执行0号任务 } //任务主动切换( tiny_os_51_core.c ) // 任务切换的设计的思想:当发生任务切换时,首先搜索下一个将要执行的任务是否处于 // 就绪状态,如果是的话,则将当前正在运行的任务的上下文保存到该任务的TCB中,然后 // 再从相应的TCB中恢复下一个将要运行的上下文.如果所有的任务都未处于就绪状态,则 // 等待本任务知道就绪为止 //TinyOsV1.1中,这里的任务切换仅提供内核使用 static void __tnOsSched ( void ) { TN_OS_TASK_HANDLE tnTask; //任务句柄即操作的任务 char cTmp1; TN_OS_TASK_HANDLE thTmp2; volatile data char *pucTmp3 = ( void * )0; thTmp2 = __GthTaskCur; //执行下一个任务 EA = 0; for ( thTask = 0; thTask < TN_OS_MAX_TASKS; tnTask ++ ) { thTmp2 ++; //首次运行时thTmp2 = 1 if ( thTmp2 > TN_OS_MAX_TASKS ) { thTmp2 = 0; } if ( ( __GtcbTask[ thTmp2 ].ucTaskStat & __TN_TASK_FLG_RDY ) != 0 ) { cTmp1 = setjmp ( __GtcbTasks[ __GthTaskCur ].jbTaskContext ); //保存当前任务的上下文,cTtmp1 = 0 if ( cTmp1 == 0 ) //如果cTmp1 = 0,往下执行 { __GthTaskCur = thTmp2; //更新当前任务句柄 longjmp ( __GtcbTasks[ thTmp2 ].jbTaskContext ); } EA = 1; return; //如果cTmp1 = 1,则返回函数 } } EA = 1; //如果所有的任务都未就绪,则等待本任务就绪,相当于一般操纵系统的空闲任务 pucTmp3 = ( volatile data char * )( &( __GtcbTasks[ thTmp2 ].ucTaskStat ) ); while ( ( *pucTmp3 & __TN_TASK_FLG_RDY ) == 0 ) //任务未就绪,直到就绪为止 { } } //时间片用完切换( tiny_os_51_core.c ) // TinyOsV1.1是纯粹的时间片轮询多任务操作系统,除了在time0ISR()时钟节拍中断服务程序中 // 切换任务外,在其他的这段服务程序中不进行任务切换操作 // 由于longjmp()函数是有RET指令返回的,如果继续使用longjmp(),则任务切换后CPU会认为中断仍未退 // 出,同级中断(包括自身)依旧被屏蔽,从而造成整个系统执行错误,所以要用longjmpInIsr(); // 由于tnOsTimeTick()函数是由time0ISR()调用的,一次,当执行time0ISR()s时,由于CPU已经处于中断 // 状态,所以不会执行__tnOsSched()函数,所以不用先禁止中断,然后再允许中断. void tnOsTimeTick ( void ) { TN_OS_TASK_HANDLE tnTask; //任务句柄即操作的任务 char cTmp1; TN_OS_TASK_HANDLE thTmp2; volatile data char *pucTmp3 = ( void * )0; //缩短任务等待时间( 延时管理 ) for ( thTask = 0; tnTask < TN_OS_MAX_TASKS; thTask ++ ) { if ( __GtcbTasks[ thTask ].uiTicks != 0 ) { __GtcbTasks[ thTask ].uiTicks --; if ( __GtcbTasks[ thTask ].uiTicks == 0 ) { __GtcbTasks[ thTask ].uiTaskStat |= __TN_TASK_FLG_RDY; } } } thTmp2 = __GthTaskCur; //执行下一个任务 for ( thTask = 0; thTask < TN_OS_MAX_TASKS; tnTask ++ ) { thTmp2 ++; //首次运行时thTmp2 = 1 if ( thTmp2 > TN_OS_MAX_TASKS ) { thTmp2 = 0; } if ( ( __GtcbTask[ thTmp2 ].ucTaskStat & __TN_TASK_FLG_RDY ) != 0 ) { cTmp1 = setjmp ( __GtcbTasks[ __GthTaskCur ].jbTaskContext ); //保存当前任务的上下文,cTtmp1 = 0 if ( cTmp1 == 0 ) //如果cTmp1 = 0,往下执行 { __GthTaskCur = thTmp2; //更新当前任务句柄 longjmpInIsr ( __GtcbTasks[ thTmp2 ].jbTaskContext ); } return; //如果cTmp1 = 1,则返回函数 } } } //longjmpInISR()定义( _setjmp.c ) //在SDCC51编译器中,若使用__naked修饰函数,则说明此函数无保护函数 char longjmpInISR( jmp_buf jbBuf ) __naked { unsigned char ucSpSave; //用于保存堆栈指针的变量 data unsigned char *pucBuf = ( data void * )0; //指向上下文信息存储位置的指针 pucBuf = ( data unsigned char * )jbBuf; ucSpSave = *pucBuf ++; bp = *pucBuf ++; *( ( data unsigned char * )ucSpaSave ) = *pucBuf ++; *( ( data unsigned char * )( ( char )( unSpSave - 1 ) ) ) = *pucBuf; SP = ucSpSave; DPL = 1; __asm RETI __endasm; } //任务延时 //任务在延时期间,其他任务仍然可以继续运行 void delay ( unsigned int uiDly ) { unsigned int i, j; for ( i = 0; i < uiDly; i ++ ) { for ( j = 0; j < 1000; j ++ ) { } } } //任务延时( tiny_os_51_core.c ) //由于在延时期间还要继续运行任务,最好的办法是设置一个周期性的中断,然后用这个中断服务程序 //来记录当前任务的剩余延时时间 //由于所有的任务可能同时延时,所以将记录当前任务剩余延时时间的变量uiTicks放在TCB中 void tnOsTimeDly ( unsigned int uiTick ) { //设置任务为等待时间状态 if ( uiTick != 0 ) { EA = 0; __GtcbTasks[ __GtcbTaskCur ].ucTaskStat = __TN_TASK_FLG_DLY; __GtcbTasks[ __GtcbTaskCur ].uiTicks = uiTIck; EA = 1; } __tnOsSched (); __GtcbTasks[ __GthTaskCur ].ucTaskStat = __TN_TASK_FLG_RDY; //等待结束 } //删除任务( tiny_os_51_core.c ) //( 句柄为-1时删除自身,并且要转换为真实的句柄在合法范围内,才能进行任务调度 ) void tnOSTaskDel ( TN_OS_TASK_HANDLE tnTask ) { //检查参数 if ( thTask == -1 ) { thTask = __GthTaskCur; //转换为真实的句柄 if ( thTask >= TN_OS_MAX_TASKS || thTask < 0 ) //检查参数是否合法 { return; //不合法不执行 } } EA = 0; __GtcbTasks[ thTask ].ucTaskStat = __TN_TASK_FLG_DEL; //删除任务 __GtcbTasks[ thTask ].uiTicks = 0;
EA = 1; if ( thTask == GthTaskCur ) //删除自身,则执行下一个任务 { __thOsSched(); } }
举例:
//时间片轮询多任务操作系统范例( main.c ) //必须将时钟节拍中断设置为最低优先级,只有这样才能保证在其他中断服务程序的执行过程中禁止 //任务切换,以保证其他中断服务程序的完整性 static idata unsigned char __GucTaskStks[2][32]; //分配任务堆栈 static unsigned char __GucTask0; //任务0测试变量 static unsigned char __GucTask1; //任务1测试变量 void task0 ( void ) { TMOD = ( TMOD & 0xF0 ) | 0x01; TL0 = 0x00; TH0 = 0x00; TR0 = 1; ET0 = 1; TF0 = 0; //允许time0中断 while ( 1 ) { __GucTask0 ++; } } void task1 ( void ) { while ( 1 ) { __GucTask1 ++; } } void timer0ISR( void ) __interrupt 1 //时钟节拍中断服务程序 { tnOSTimeTick(); //时钟节拍处理程序 } void main ( void ) { tnOsInit (); tnOsTaskGreate ( task0, __GucTaskStks[0] ); tnOsTaskGreate ( task1, __GucTaskStks[1] ); tnOsStart (); }