選自<<項目驅動-單片機應用設計基礎>>
/* ** 一般來說,操作系統的調度算法主要有三類:時間片輪詢,優先級與帶優先級的時間片輪詢調度 ** 不是任務主動放棄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 (); }
