開局分割線:接著我們上一篇,其實小小調(diào)度器最不同于其它實時任務框架的點就在于他的核心是基于:_LINE_的應用(純C的實現(xiàn),并沒有用到和cpu相關(guān)的寄存器操作,所以他是可以忽略硬件進行移植的,只要編譯器支持),當然這并不是原作者的原創(chuàng)(真要是我怕你也不敢用),使用_LINE_行號記錄程序運行位置,最早是有PT thread采用的,感興趣的童鞋可以自行度娘,言歸正傳,先從LINE用法開始下刀:
ANSI C 規(guī)定了預定義宏,__LINE__:表示當前源代碼的行號;
#define WaitX(tickets) do { _lc=(__LINE__%255)+1; return (tickets) ;case (__LINE__%255)+1:;} while(0);
int func(void)
{
while(1)
{
WaitX(50); //假定該行代碼在文件中的行號是18
LED0=!LED0;
}
}
這里有兩個需要注意的,define WaitX宏為什么寫為一行,因為寫一行__LINE__的值就是行號而且是相同的,雖然宏寫一行看起來很難理解,但是這的確是必須的。
接下來解釋下_lc=(__LINE__%255)+1; _lc類型為char,取值范圍在0-255,而0為剛進入函數(shù)開始直接,所以需要避過該號,所以實際取值應該在1-255之間,__LINE__的實際值可能超過255,所以需對其取余操作,防止跳轉(zhuǎn)沖突(該錯誤可在編譯階段查找出來,并不需要太過擔心,一旦出現(xiàn)沖突,解決方案也很簡單,再任務中多打一行空格即可錯開行號沖突)。
到這里我們再來看WaitX(50);這個宏到底執(zhí)行了什么,其實就以下三點:
1.記錄該行號(因為后面程序要跳轉(zhuǎn)到該行繼續(xù)執(zhí)行)。
2.跳走,返回值50(返回值是給到調(diào)度任務,調(diào)度任務計時到后,主動調(diào)用當前任務)
3.進入當前任務,從記錄行號開始執(zhí)行。
接下來,隆重介紹下即將登場的調(diào)度任務:本質(zhì)上其就是一個定時器任務(框架任務,必不可少),運行環(huán)境為:中斷運行,代碼如下:
void INTT0(void) interrupt 1 using 1 //看這個樣子明顯是個中斷函數(shù)
{
TL0=0Xff; //寄存器操作,10ms執(zhí)行一次中斷
TH0=0XDB; //寄存器操作,這不是重點
UpdateTimers(); //這里是重點,看著像函數(shù),實際這也是個宏。
}
重點就是UpdateTimers()宏展開的實現(xiàn):
//用最簡單的方式:純C實現(xiàn)調(diào)度器核心代碼。
do{
unsigned char i;
for(i=MAXTASKS;i>0 ;i--)
{
if((timers[i-1]!=0)&&(timers[i-1]!=65535)) timers[i-1]--;
}
} while(0);
這段宏完成了定時器任務最主要的功能,主要是對timers數(shù)組進行--操作,timers數(shù)組里面記錄的每個任務的延時時間(專業(yè)一點叫做阻塞時間),當一個任務被阻塞時,return返回值就會被寫入對應的timers數(shù)組中。
每一個任務都有一個自己的timers,所以timers數(shù)組的最大值就是MAXTASKS(最大任務數(shù))。一個任務有三中狀態(tài):
1. timers為0,這個時候的任務要么是就緒態(tài)(即將運行),要么就是運行態(tài)。
2. timers不為0,也不是最大值(65535),這個時候的任務為阻塞態(tài)(也就是延時時間還沒到)。
3. timers為65535(最大值),這個時候的任務為停止態(tài)(也就是生命周期結(jié)束了,不再參與系統(tǒng)調(diào)度)。
有了這個調(diào)度框架(定時任務)再配合程序猿自己定義的任務函數(shù),那么小小os就可以簡簡單單的跑起來了。