時間片輪詢多任務操作系統( TinyOS51 V1.1 )


選自<<項目驅動-單片機應用設計基礎>>

 

/*    
**        一般來說,操作系統的調度算法主要有三類:時間片輪詢,優先級與帶優先級的時間片輪詢調度
**        不是任務主動放棄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 ();
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM