久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計實(shí)例 >> 瀏覽文章

非搶占式調(diào)度算法的實(shí)現(xiàn)(非搶占式、不可剝奪式)

作者:huqin   來源:本站原創(chuàng)   點(diǎn)擊數(shù):  更新時間:2014年04月20日   【字體:

 

時間如沖沖流水,一轉(zhuǎn)眼間都畢業(yè)快一年了。這一年里忙忙碌碌,卻又碌碌無為。有時又總想,生亦何苦,死亦何哀。

 
也好久沒更新博文了,之前做了個STM8的脫機(jī)編程器,使用了EMWIN,學(xué)習(xí)到了人家的消息機(jī)制。覺得自己編程在無法移植系統(tǒng)的情況下能不能自己設(shè)計個模塊呢?就有了標(biāo)題中的思想。

 
以下是我在論壇上發(fā)的。

 

 
    今天我也來個湊熱鬧講講操作系統(tǒng),理論嘛,人人都懂,所以不講了。

          首先想問大家,平時在8位機(jī)上會用到操作系統(tǒng)嗎?還是一直都是裸奔?當(dāng)然了我一直都是在祼奔。
  背景:
          為什么要裝B還說要設(shè)計個OS,這么簡單還不人人都去設(shè)計。事實(shí)也的確如此,難。不知道各位有沒有遇到這樣的情況:

        給公司寫軟件的時候,其核心內(nèi)容設(shè)計好后公司總會有一些需要特別功能的產(chǎn)品,但仍基于設(shè)計好的核心,因此需要將此COPY兩份,在其中一份中增加功能。如果公司這樣的產(chǎn)品較多,那么你的文件夾中出現(xiàn)各種復(fù)件,如果核心軟件優(yōu)化了將對所有文件全部一一優(yōu)化。自然有人說可以有預(yù)處理指令來增加功能模塊,但這樣的代碼還是難于維護(hù)。。

重點(diǎn)來了,當(dāng)軟件初步完成后,維護(hù)時往往并沒有設(shè)計時的激情,一是并沒有設(shè)計時的思路,二總想換個思路重新寫,或者壓根就不想再碰!我是這樣的,你呢?

因此我一直在找基于8位機(jī)的RTOS。

        再后來我使用了EMWIN設(shè)計了一個產(chǎn)品,它的消息機(jī)制真的很棒,我就想能不能通過消息來驅(qū)動任務(wù)。emwin的消息是針對窗口對象的。我只要將各個任務(wù)看作對象不也應(yīng)該可以設(shè)計么?針對這樣的想法就開始了設(shè)計之路。

===============================================================

____:  初步想法:
        給每個任務(wù)建立一個消息池,任務(wù)等待消息,等到消息后執(zhí)行該任務(wù)。消息由其它任務(wù)或中斷中給出。

____:  又來了想法:
        如果同時有任務(wù)等到消息到底誰先運(yùn)行,所以在任務(wù)中建立優(yōu)先級,以使同時等到消息的任務(wù)中優(yōu)先級高的得到CPU的使用權(quán)?
        那么這樣就得有一個調(diào)度器,在任務(wù)間進(jìn)行切換。
        為此我給每個函數(shù)統(tǒng)一名稱叫消息進(jìn)程,以MP_xxx開頭。

        即然用到優(yōu)先級就會想到ucos中優(yōu)先級查找機(jī)制,翻開ucos的書,其中一段讓我的想法步入了正軌——不可剝奪型內(nèi)核。

____:    什么是非搶占式優(yōu)先級調(diào)度操作系統(tǒng)也叫不可剝奪型內(nèi)核(來自ucosii ……)

        不可剝奪型內(nèi)核要求每個任務(wù)自我放棄 CPU 的所有權(quán)。不可剝奪型調(diào)度法也稱作合作型多任務(wù),各個任務(wù)彼此合作共享一個 CPU。異步事件還是由中斷服務(wù)來處理。中斷服務(wù)可以使一個高優(yōu)先級的任務(wù)由掛起狀態(tài)變?yōu)榫途w狀態(tài)。但中斷服務(wù)以后控制權(quán)還是回到原來被中斷了的那個任務(wù), 直到該任務(wù)主動放棄 CPU 的使用權(quán)時,那個高優(yōu)先級的任務(wù)才能獲得 CPU的使用權(quán)。
        不可剝奪型內(nèi)核的一個優(yōu)點(diǎn)是響應(yīng)中斷快。在討論中斷響應(yīng)時會進(jìn)一步涉及這個問題。在任務(wù)級,不可剝奪型內(nèi)核允許使用不可重入函數(shù)。 函數(shù)的可重入性以后會討論。每個任務(wù)都可以調(diào)用非可重入性函數(shù),而不必?fù)?dān)心其它任務(wù)可能正在使用該函數(shù), 從而造成數(shù)據(jù)的破壞。 因為每個任務(wù)要運(yùn)行到完成時才釋放 CPU 的控制權(quán)。 當(dāng)然該不可重入型函數(shù)本身不得有放棄 CPU 控制權(quán)的企圖。使用不可剝奪型內(nèi)核時,任務(wù)級響應(yīng)時間比前后臺系統(tǒng)快得多。 此時的任務(wù)級響應(yīng)時間取決于最長的任務(wù)執(zhí)行時間。
(省略部分請參閱ucos系統(tǒng)概念部分)

        好吧,我臉皮子就厚了,將MP_XXX命名修改成OS_XXX,畢竟這也叫操作系統(tǒng)嘛。重新整理了思緒如下:


整個設(shè)計為了節(jié)約內(nèi)存,畢竟不是實(shí)時系統(tǒng)要求的不是響應(yīng)速度。        
        1、既然是非搶占式OS,那么每個任務(wù)必須執(zhí)行完后才會交出CPU的使用權(quán)。所以任務(wù)不應(yīng)該是超級循環(huán),并盡量控制任務(wù)執(zhí)行時間。

        2、任務(wù)有就緒態(tài)、掛起態(tài),運(yùn)行態(tài)。

        3、每一個任務(wù)需要等待被執(zhí)行的消息,可以接收多個消息,消息間可使用邏輯運(yùn)算and or進(jìn)行操作。并給每個任務(wù)分配消息等待超時時長。任務(wù)在規(guī)定時間內(nèi)未等到消息也將進(jìn)入就緒狀態(tài)。如果建立任務(wù)時即未給出等待消息也未給出超時時長則任務(wù)立即進(jìn)入就緒狀態(tài),否則進(jìn)入掛起態(tài)。

        4、任務(wù)的調(diào)度由任務(wù)查找就緒表中最高優(yōu)先級的任務(wù)并執(zhí)行任務(wù),等待該任務(wù)執(zhí)行完后再重新調(diào)用調(diào)度器查找更高優(yōu)先級的任務(wù)。

        5、需要在一個定時器刷新每個任務(wù)超時時間。并且就緒已超時的高優(yōu)先級的任務(wù)。當(dāng)然中斷返回后返回到被中斷的任務(wù),在該任務(wù)結(jié)束后才會執(zhí)行更高優(yōu)先級任務(wù)。

 
即然發(fā)這個貼了自然是實(shí)現(xiàn)了,并不是連載,而是分開寫有層次感,看得不會累。系統(tǒng)的命名仿照UCOS,讓人不會那么陌生。

設(shè)計好的內(nèi)核有如下功能:
1、支持最多16個任務(wù),并且支持多少個任務(wù)就支持多少個優(yōu)先級。如果系統(tǒng)中規(guī)定只有5個任務(wù),那么只有0-4的優(yōu)先級,因為未使用鏈表,使用數(shù)組,由優(yōu)先級作為索引(只考慮節(jié)約內(nèi)存)。

2、系統(tǒng)在初始化時建立一個空閑任務(wù),優(yōu)先級最低,并且一直處于就緒態(tài),不得刪除,不然會出現(xiàn)沒有就緒任務(wù)時調(diào)試器會調(diào)用優(yōu)先級為0的任務(wù),不管該任務(wù)是否存在。后果無法預(yù)測。

3、系統(tǒng)可使能統(tǒng)計任務(wù),優(yōu)先級次低,用戶可設(shè)定調(diào)用周期,如果使用統(tǒng)計函數(shù),系統(tǒng)在初始化時會阻塞周期時長用于統(tǒng)計規(guī)定時間內(nèi)總空閑計數(shù)值。cpu使用率=1-本次采樣空閑計數(shù)器/周期內(nèi)總計數(shù)值(阻塞時獲得)


6、初次設(shè)計,沒有給任務(wù)分配參數(shù)。也沒有修改優(yōu)先級,有刪除任務(wù),但請不要在中斷中使用(如果刪除被中斷的任務(wù),中斷返回后將執(zhí)行一個不存在的任務(wù))。
即使是非搶占式操作系統(tǒng)也需要考慮重入問題,本文使用臨界來解決。
(待補(bǔ)充)


系統(tǒng)性能:
        測試條件51單片機(jī)12MHZ仿真
        如果在任務(wù)建立時就計算好就緒任務(wù)的位置則任務(wù)切換時間為固定時長48us。為了節(jié)約內(nèi)存并未計算好這些值,所以切換任務(wù)時長為52us。
        更多測試請看下面更新。



思想大概就是這樣,接下來就是實(shí)現(xiàn)細(xì)節(jié):
========================================================================================
先看數(shù)據(jù)組成:
typedef struct
{
  void                 (*Task)(void);                         //任務(wù)
  OS_MSG         OSTCBMsg;                        //任務(wù)所需要的消息  8位
  OS_MSG         OSTCBRecMsg;                        //任務(wù)接收到的消息
  int                 OSTCBOverTime;                        //任務(wù)超時時長
  int                 OSTCBCopyOverTime;                //備份超時時長,或者叫影子
  u8                  OSTCBLogicl;                        //接收到的消息邏輯操作   and  or
  u8                  OSTCBPrio;                        //任務(wù)優(yōu)先級
}OS_TCB;
任務(wù)控制塊,為了節(jié)約內(nèi)存未使用鏈表,結(jié)合之前描述的消息使任務(wù)就續(xù)再看一下各個數(shù)據(jù)的注釋大體能明白它們在做什么。



OS_TCB                         OSTCBTbl [OS_TASK_NUM];        //申請任務(wù)控制塊
u8                          OSPrioTbl[OS_TASK_NUM];        //任務(wù)注冊池,為0/1分別代碼任務(wù)不存在和存在,刪除任務(wù)只是將該任務(wù)注冊池置0而已

========================================================================================

任務(wù)是如何建立的?初始化任務(wù)的各個參數(shù),并將任務(wù)地址傳到內(nèi)核,由內(nèi)核管理。
這里要注意的是哪些地方要進(jìn)入臨界,為什么?在注釋中也寫出了,
當(dāng)然也有可能有些未考慮到的。大家注重思想就好。

暫時沒有實(shí)現(xiàn)給任務(wù)傳遞參數(shù),和任務(wù)這間的通信,但不難實(shí)現(xiàn),就好像教科書上給出馬的框框就可以讓同學(xué)畫徐悲鴻的馬了。。。。
========================================================================================

OS_STA OS_CreateTask(void(*Task)(void),OS_MSG OSTCBMsg,char OSTCBLogicl,int OverTime,u8 OSTCBPrio)
{
        u8 OS_ITSTATUS;
        OS_TCB *PTcb=&OSTCBTbl[OSTCBPrio];                        //使用指針,節(jié)省建立時間
        
        OS_ENTER_CRITICAL();                                                  //防止在任務(wù)在中斷中刪除了此任務(wù)(想實(shí)現(xiàn)中斷中不可刪除任務(wù))
        if((OSPrioTbl[OSTCBPrio]==1)||(OSTCBPrio>=OS_TASK_NUM))
        {   
                OS_EXIT_CRITICAL();
                return OS_PRIO_INVALID;                                          //該優(yōu)先級被注冊過,或者超過了任務(wù)數(shù)
        }
        
        OSPrioTbl[OSTCBPrio]                        =        1;                         //注冊任務(wù),進(jìn)入臨界,以防止被中斷函數(shù)注冊                                                        
        PTcb->OSTCBMsg                        =        OSTCBMsg;                //需要接收到的消息
        PTcb->OSTCBRecMsg                        =        0;                        //接收的消息
        PTcb->OSTCBLogicl                        =        OSTCBLogicl;        //接收消息方式
        PTcb->OSTCBOverTime                =        OverTime;                //超時時間
        PTcb->OSTCBCopyOverTime                =        OverTime;                //備份超時時間

        OS_EXIT_CRITICAL();                                                          //以上參數(shù)在其它函數(shù)中都可能被修改,所以需要關(guān)中斷嘍                                

        PTcb->Task                                =        Task;                        //用戶任務(wù)
        PTcb->OSTCBPrio                        =        OSTCBPrio;                //任務(wù)優(yōu)先級

        if((PTcb->OSTCBMsg==0)&&(PTcb->OSTCBOverTime)==0)OS_TaskRdy(OSTCBPrio);        //沒有要等待的事件或者時間則任務(wù)立即進(jìn)入就緒態(tài)
        
        return OS_OK;
}

注釋詳盡(有嗎?),具體細(xì)節(jié)請下載代碼查看。




========================================================================================
既然稱這個為系統(tǒng),那么任務(wù)的調(diào)度自然是少不了,給它響亮的名字叫調(diào)度器,但這個函數(shù)叫任務(wù)切換。再給它包個皮就叫調(diào)度器了。
========================================================================================
OS_STA OS_TaskSw(void)
{
        u8 OSTCBPrio,y;
        u8 OS_ITSTATUS;
        y=OSUnMapTbl[OSRdyGrp];
        OSTCBPrio=(y<<2)+OSUnMapTbl[OSRdyTbl[y]];        //沒有任務(wù)時返回0,0是最高優(yōu)先級,使用同ucos,只是優(yōu)先級只有16個,節(jié)約內(nèi)存
        OS_ENTER_CRITICAL();                                        //進(jìn)入臨界防止剛判斷任務(wù)存在就在中斷函數(shù)中被刪除
          if((OSPrioTbl[OSTCBPrio]==0)||(OSTCBPrio>=OS_TASK_NUM))
        {
                OS_EXIT_CRITICAL();
                return OS_PRIO_INVALID;
        }
        OSPrioSelf=OSTCBPrio;                                //當(dāng)前運(yùn)行的任務(wù)
        OS_EXIT_CRITICAL();
        OSTCBTbl[OSTCBPrio].Task();                //此處調(diào)用任務(wù)是開中斷的,如果此時來了中斷并將該任務(wù)刪除,返回到這里將執(zhí)行被刪除的任務(wù),
        return OS_OK;                                        //所以在中斷中不能掉用刪除任務(wù)函數(shù)
}
需要注意OSTCBTbl[OSTCBPrio].Task();調(diào)用任務(wù)后的中斷是打開的,當(dāng)運(yùn)行該任務(wù)時進(jìn)入中斷,在中斷中掛起該任務(wù),或者刪除該任務(wù),那么中斷返回后應(yīng)該去哪?所以在中斷中不允許使用掛起和刪除函數(shù),但未在軟件加入限制,也會在今后修改

========================================================================================
不管哪個系統(tǒng)都需要用戶編寫部分代碼。而非搶占式的不需要用戶修改寄存器SP PC LR等。但任務(wù)是需要心跳的,這部分是和硬件有關(guān)的,不同的處理器自然也不一樣。
這里是在51中結(jié)合protues仿真的,定時中斷為1ms1次,具體多少可以在OS_CONFIG.h中進(jìn)行設(shè)計每秒鐘的中斷次數(shù)。
========================================================================================

定時器的初始化函數(shù)則由用戶編寫。根據(jù)不同的MCU代碼自然是不一樣的。51如下:
void Timer_Init(void)
{
        u16 TimerValue;
        TimerValue=0xffff-1000000/OS_TICK_PRE_SEC;                         //使用定時方式1,最大時長65ms。所以用戶情況設(shè)置此值,在.h文件中
        th=TimerValue>>8;
        tl=TimerValue;
        TMOD=0x01;
        TH0=th;
        TL0=tl;
        ET0=1;
        TR0=1;
        EA=1;
}

OS_TICK_PRE_SEC 留給用戶設(shè)置的,即每秒應(yīng)該中斷的次數(shù)


void Timer_Exception(void) interrupt 1
{        
        TR0=0;
        TH0=th;
        TL0=tl;
        OS_TimeMsgPost();                                        //刷新每個任務(wù)的超時時長,遞減的方式,并且就緒高優(yōu)先級任務(wù)
        TR0=1;
}

void OS_TimeMsgPost(void)
{
        u8 i;
        u8 OS_ITSTATUS;
        OS_TCB *Ptr=OSTCBTbl;                                //用指針可加快速度
        for(i=0;i
        {
                OS_ENTER_CRITICAL();
                if(Ptr->OSTCBOverTime>0)                        //超時值大于0時刷新變量
                {
                        Ptr->OSTCBOverTime--;                  //防止其它任務(wù)將此值變成0后減成0xFF
                        OS_EXIT_CRITICAL();
                        if(Ptr->OSTCBOverTime==0)
                        {
                                OS_TaskRdy(i);                        //超時時間到,就緒對應(yīng)任務(wù)        
                        }
                }
                OS_EXIT_CRITICAL();
                Ptr++;                                                   //刷新到下一個任務(wù)的超時標(biāo)志
        }        
}

 

 
主要提高自己的編程樂趣。
========================================================================================
以下列出函數(shù),具體細(xì)節(jié)請查看源代碼:
extern OS_STA         OS_CreateTask(void(*Task)(void),OS_MSG OSTCBMsg,char OSTCBLogicl,int OverTime,u8 OSTCBPrio);//任務(wù)建立
extern OS_STA         OS_DeleteTask(u8 OSTCBPrio);                        //刪除任務(wù)
extern void         OS_ResumeTime(u8 OSTCBPrio);                        //重新將恢復(fù)任務(wù)超時時長
extern OS_STA         OS_TaskRdy(u8 OSTCBPrio)reentrant;                //將任務(wù)就緒,不可重入,不同的編譯器可能處理不一樣
extern OS_MSG         OS_MsgGet(u8 OSTCBPrio,OS_STA *err);        //獲取指定任務(wù)消息
extern OS_STA         OS_MsgPost(u8 OSTCBPrio,OS_MSG OSMsg);        //發(fā)送消息給指定任務(wù),并判斷是否需要就緒
extern void         OS_TimeMsgPost(void);                                //刷新超時時長,就緒超時的任務(wù),由定時器調(diào)用
extern OS_STA         OS_TaskSuspend(u8 OSTCBPrio);                        //掛起任務(wù)
extern OS_STA         OS_MsgClear(u8 OSTCBPrio,OS_MSG OSMsg);        //清除指定任務(wù)消息
extern OS_STA         OS_TaskSw(void);                                        //任務(wù)切換
extern OS_STA         OS_TaskSched(void);                                        //任務(wù)調(diào)度
extern void         OS_Start(void);                                                //開始系統(tǒng)運(yùn)行
extern OS_STA         OS_Init(void);                                                //系統(tǒng)初始化,用于建立空閑任務(wù)和統(tǒng)計任務(wù)(可選)
extern void         OS_StatInit(void);                                        //統(tǒng)計任務(wù)初始化(需要使能)


========================================================================================

沒有實(shí)踐就沒有發(fā)言權(quán):請看例子:

sbit LED1=P2^0;
sbit LED2=P2^1;
sbit LED3=P2^2;

#define TASK_PRI_LED1 0                                 //定義任務(wù)的優(yōu)先級
#define TASK_PRI_LED2 1
#define TASK_RPI_LED3 2


void LED1_Task(void)                                                //任務(wù)1就是閃爍LED1,運(yùn)行它的條件由任務(wù)建立時給出,200ms 1次
{
        LED1=~LED1;
        OS_MsgPost(TASK_PRI_LED3,MBit1);                //給LED3發(fā)送消息0x01
        OS_TaskSuspend(OSPrioSelf);                        //將自身掛起
}

void LED2_Task(void)                                                //任務(wù)2就是閃爍LED2,運(yùn)行它的條件由任務(wù)建立時給出,200ms 1次
{
        LED2=~LED2;
        OS_MsgPost(TASK_PRI_LED3,MBit2);                //給LED3發(fā)送消息0x02
        OS_TaskSuspend(OSPrioSelf);                        //將自身掛起
}

void LED3_Task(void)                                                //任務(wù)3就是閃爍LED3,運(yùn)行它的條件由任務(wù)建立時給出,接收由任務(wù)1和任務(wù)2的消息才被調(diào)用
{
        LED3=~LED3;
        OS_TaskSuspend(OSPrioSelf);                        //將自身掛起
}

void main(void)
{
        OS_Init();
        Timer_Init();
        OS_CreateTask(LED1_Task,         MBitNop,                0,                200,        TASK_PRI_LED1);                         //200ms后進(jìn)入就緒態(tài)                
        OS_CreateTask(LED2_Task,         MBitNop,                0,                200,        TASK_PRI_LED2);                         //200ms后進(jìn)入就緒態(tài)                
        OS_CreateTask(LED3_Task,        MBit1|MBit2,        OS_AND,                0,                TASK_PRI_LED3);         //等到Mbit1且還要等到Mbit2即0x03。因為是OS_AND與操作
        OS_Start();
}

任務(wù)3由任務(wù)1和任務(wù)2一起驅(qū)動,在任務(wù)1或者任務(wù)2中的任一1個OS_MsgPost注釋掉任務(wù)3將不會被執(zhí)行。燈自然也不會閃爍,雖然是三個獨(dú)立任務(wù),并且不是同時點(diǎn)亮,但確是同步閃爍。
一共運(yùn)行了四個任務(wù)(+空閑任務(wù))和1個1ms中斷的定時器的代碼量和運(yùn)行效果如下:1.5K左右的ROM空間和84字節(jié)的RAM空間。

 
 
 
代碼量
 
 
效果

 

 
========================================================================================
上面的例子可以看出任務(wù)是同步運(yùn)行的,但看不出CPU的效率,接下來的例子則是測試一下CPU的使用率,使用數(shù)碼管顯示,并且使用1個按鍵故意阻塞CPU查看效率
一共運(yùn)行了7個任務(wù),至于ROM和RAM的空間后面將列舉(因為統(tǒng)計任務(wù)和數(shù)碼顯示中有乘除運(yùn)算+數(shù)碼管取碼等使ROM量大些)。
========================================================================================

接下來我們需要增加幾個任務(wù)了,統(tǒng)計任務(wù)+數(shù)碼管顯示+按鍵任務(wù)。

主要功能有:
        數(shù)碼管顯示CPU的利用率,由按鍵按下阻塞任務(wù)的運(yùn)行查看CPU利用率的的變化。任務(wù)中去了1個LED1燈。實(shí)現(xiàn)如下:


sbit LED1=P2^0;
sbit LED2=P2^1;
sbit LED3=P2^2;
sbit KEY1=P2^3;

#define TASK_PRI_LED2 1                                        //定義優(yōu)先級
#define TASK_PRI_LED3 2
#define TASK_PRI_MAIN 3        
#define TASK_PRI_TUBE 4
#define TASK_PRI_KEY1 0

unsigned char CONST distab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,  //共陽數(shù)碼管段選碼表,無小數(shù)點(diǎn)
                               0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff};

unsigned char UsageBuf[4];

//數(shù)碼管任務(wù),顯示CPU利用率
void Tube_Task(void)
{
        static u8 Bsel=0x10,i=0;
        UsageBuf[0]=0x0b;
        UsageBuf[3]=CPUUsage/100;                        //取出CPU利用率
        UsageBuf[2]=CPUUsage/10;
        UsageBuf[1]=CPUUsage;
        if(Bsel==0)Bsel=0x10;
        if(i==4)i=0;
        P3=0xff;
        P1=Bsel;
        P3=distab[UsageBuf[i++]];
        Bsel<<=1;
        OS_TaskSuspend(OSPrioSelf);                        
}


//點(diǎn)亮LED2,并發(fā)消息給LED3
void LED2_Task(void)
{
        LED2=~LED2;
        OS_MsgPost(TASK_PRI_LED3,MBit1);                //給LED3發(fā)送消息        
        OS_TaskSuspend(OSPrioSelf);                        //將自身掛起
}

//按鍵任務(wù)阻塞CPU,并發(fā)消息給LED3
//有些狀態(tài)機(jī)的意思
void KEY1_Task(void)
{
        u16 Hold; 
        static u8 Statue=0;
        if(KEY1==0)
        {
                if(Statue<4)Statue++;                                //用于去抖動
                
        }
        else
        {
                Statue=0;                                                //松手后
        }        
        if(Statue>2)                                                //簡單的去抖動
        {
                OS_MsgPost(TASK_PRI_LED3,MBit2);        //給LED3發(fā)送消息
                Hold=TL0+1000;                                        //阻塞CPU,理論上是一隨機(jī)數(shù),但長按后則是一常數(shù)
                while(Hold--);        
        }
        OS_TaskSuspend(OSPrioSelf);                        //將自身掛起

}

//接收消息才運(yùn)行,現(xiàn)象即為按鍵長按后同LED1同時閃爍
void LED3_Task(void)                                        
{
        LED3=~LED3;
        OS_TaskSuspend(OSPrioSelf);        
}


//開始任務(wù),在采樣CPU空閑時總計數(shù)值后運(yùn)行,超時時長為采樣時長,這里取200,也可取1S UCOS為1S
void Start_Task(void)
{
#if IS_ENABLE_STAT
        OS_StatInit();                                                                                        //采樣1s中空閑任務(wù)的計數(shù)值
#endif
        OS_CreateTask(LED2_Task,         MBitNop,        0,                200,        TASK_PRI_LED2);                //每400ms閃爍一次
        OS_CreateTask(LED3_Task,         MBit1|MBit2,OS_AND,        0,                TASK_PRI_LED3);                //等待兩個消息同時有效才被運(yùn)行,即按鍵按下LED2任務(wù)被執(zhí)行
        OS_CreateTask(Tube_Task,        MBitNop,        0,                5,        TASK_PRI_TUBE);                //5ms刷新1位數(shù)碼管,刷新四位需要20ms
        OS_CreateTask(KEY1_Task,        MBitNop,        0,                20,        TASK_PRI_KEY1);                //按鍵任務(wù)20ms對按鍵進(jìn)行一次掃描                

        OS_DeleteTask(OSPrioSelf);                                                                        //刪除本任務(wù)
}

void main( void )  
{
        OS_Init();
        Timer_Init();
#if IS_ENABLE_STAT
        OS_CreateTask(Start_Task,0,0,STAT_SAMP_TIME,TASK_PRI_MAIN);         //用于統(tǒng)計任務(wù) STAT_SAMP_TIME后再注冊用戶軟件
#else
        OS_CreateTask(Start_Task,0,0,                          0,TASK_PRI_MAIN);//創(chuàng)建開始任務(wù),主要為了在調(diào)用OS_Start()后建立用戶任務(wù)                         
#endif                                                                                                        //費(fèi)1個任務(wù)內(nèi)存,但統(tǒng)計任務(wù)必須在沒有其它任務(wù)運(yùn)行時先運(yùn)行         
        OS_Start();                                                                                        //啟動任務(wù)
}

========================================================================================
任務(wù)中維持了1個統(tǒng)計任務(wù),在開機(jī)時阻塞STAT_SAMP_TIME時長獲取總空閑時長。再獲取后才能建立用戶任務(wù),所以建立了個Start_Task的任務(wù)用作等待。
========================================================================================

運(yùn)行了7個任務(wù)后的內(nèi)存使用情況 rom 2.2k ram 127b。
cpu的使用率如下(200ms進(jìn)行1次測試):
       
 
 
容量
 
 
沒有阻塞時7個任務(wù)的CPU使用率
 
 
阻塞后的CPU使用率

 

 

 
依舊懷念學(xué)校課堂上點(diǎn)亮LED燈的樂趣,學(xué)校用的東西都不便宜,13元左右的8位MCU。可以想想在學(xué)校的實(shí)驗儀上點(diǎn)亮led燈 運(yùn)行個數(shù)碼管再加上個按鍵這樣的系統(tǒng)cpu使用率其實(shí)不到10%,而大部分時間都在delay中。
所以并不是真去設(shè)計個什么OS。能夠在不可能移植到RTOS的情況下能不能換種編程思維,使軟件模塊化,提高編程樂趣,降低維護(hù)成本。

========================================================================================
改進(jìn):

這只是消息觸發(fā)任務(wù)就緒,任務(wù)在掛起時將會清除觸發(fā)事件和重新裝載超時時長。當(dāng)然還要增加功能如下:
        給每個任務(wù)增加事件標(biāo)志,用于任務(wù)之間的通信(或都叫郵箱也行)。
        給每個任務(wù)增加一參數(shù)指針
        可以修改任務(wù)等待的消息
        可以修改任務(wù)優(yōu)先級
        在中斷中不能調(diào)用的函數(shù)在軟件上加上限制,而不是口頭約定(如掛起任務(wù)函數(shù),刪除任務(wù)函數(shù)等)。

當(dāng)然了這只是一個非搶占式的調(diào)度,用于低內(nèi)存的MCU管理用戶任務(wù),優(yōu)缺點(diǎn)也是共知的。所以不一定要多復(fù)雜。越簡單越好,夠用就好。在內(nèi)存足夠時,移植一RTOS才是最爽的。
        


說明:

當(dāng)然了,并不是說讓你用這個東西,而是提出一種思想。不是提到操作系統(tǒng)就會只想到搶占式。提到非搶占式只會想到書上的那幾道算法題——建立的任務(wù)的順序和任務(wù)優(yōu)先級,求任務(wù)調(diào)試次序。要是真來個中斷,掛起了某個任務(wù),順序不就變了么?哈哈。


祝大家好運(yùn)!!!

 

 
以上是針對非搶占式調(diào)度算法的設(shè)計,代碼并不完整。該調(diào)度也被我用到了產(chǎn)品中去。真的有在設(shè)計的時候就會有一種動力。樂趣是自己找出來的。
 
關(guān)閉窗口

相關(guān)文章

主站蜘蛛池模板: 2019天天操 | 就操在线 | 日本福利视频 | 日韩视频免费在线 | 国产亚洲精品久久久久动 | 午夜精品久久久久久久久久久久久 | av毛片在线播放 | 超碰网址 | 天天操夜夜操免费视频 | 亚洲国产中文字幕 | 日韩影院在线 | 免费在线观看一区二区三区 | 久久99精品久久久久久国产越南 | 国产精品a久久久久 | 一区二区三区在线免费观看 | 麻豆精品国产91久久久久久 | 中文字幕一区在线观看视频 | 亚洲精品国产电影 | 亚洲一区二区三区视频 | 免费黄色的视频 | 日韩一区二区久久 | 人人精品| 999久久久免费精品国产 | 日韩欧美在线一区 | 韩国av一区二区 | 成人精品一区二区三区中文字幕 | 亚洲精品久久久久久一区二区 | 99小视频| 欧州一区二区三区 | 国产精品免费在线 | 成年人的视频免费观看 | 久久久久一区二区 | 欧美亚洲视频在线观看 | 久久成人免费 | 久久这里有精品 | 国产小视频在线 | 视频一区二区在线观看 | 国产91一区 | 99福利视频| 中文字幕av一区 | 欧美日韩午夜精品 |