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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 11299|回復: 20
收起左側(cè)

小小調(diào)度器V2.0簡易版源碼與分析

  [復制鏈接]
ID:389812 發(fā)表于 2018-8-24 23:37 | 顯示全部樓層 |閱讀模式
本文作者:Gthgth

注意:小小調(diào)度器V2.0 作者:  兔子、smset

在作者和“兔子”大蝦的努力下,小小調(diào)度器迎來一個激動人心的新版本。(2.0正式版,為了大家方便學習才有V2.0簡易版)

在作者和兔子的幫助下,開始學習V2.0簡易版。

V1.1版本和V2.0 簡易版本不沖突,是兩個相對獨立的版本,各有各的優(yōu)點,V1.1突出強調(diào)小,省資源。并不是v2.0 的存在就取代了V1.1;和V1.1版本

相比,  簡易版,在 的基礎上,支持任務重入;當然了 也是一如既往的小。
v2.0 V1.1 V2.0
在百度中查了一下:可重入代碼指可被多個函數(shù)或程序凋用的一段代碼(通常是一個函數(shù)),而且它保證在被任何一個函數(shù)調(diào)用時都以同樣的方式運行。
在小小調(diào)度器V2.0 中:
子任務可以被多個主任務調(diào)用,主任務可以給子任務傳遞參數(shù)。子任務也可以訪問主任務的數(shù)據(jù)。每個任務之間可以相互訪問數(shù)據(jù)。

具體反映在:
1.把每個任務函數(shù)的私有變量和行號、延時時間等都獨立出去,保存在自己的結(jié)構(gòu)體變量里面了;
2.在運行任務函數(shù)時,有關數(shù)據(jù)不能直接傳給結(jié)構(gòu)體,而是地址進去,進去后轉(zhuǎn)換回結(jié)構(gòu)體。

一.主函數(shù)分析

voidmain(){
while(1){

delay_ms(1);//延時1毫秒
runtasks();
}
}

分析:很簡單,延時1ms執(zhí)行runtasks()函數(shù);這樣就相當于每隔1ms 掃描一次runtasks()函數(shù)。沒有用到定時器中斷,這個1ms 時基可以根據(jù)要求修改;
如果用定時器,時基寫的很小就會頻繁的打斷CPU。用延時感覺時基選擇小一點這樣更節(jié)省CPU資源,如果延時太長,就會占用太長CPU。(問:作者為
什么用延時作為時基沒用定時器?答:那種都行,看情況;在示例中用延時作為時基是考慮到調(diào)度器中統(tǒng)一沒有涉及到中斷。)
(smset補充:一是由于以arduino為例,arduino默認代碼沒有提供中斷,因此沒有采用中斷時基。
另一個原因是V2.0簡易版默認使用short類型的任務Timer變量,如果使用中斷進行UpdateTimer更新,是存在隱患的,所以
如果在中斷里進行UpdateTimer更新,則必須使用unsignedchar類型的任務Timer變量)。

1,展開 runtasks();函數(shù)
voidruntasks(){

//指定led1任務驅(qū)動的IO管腳
led1.pin=13;

//更新頂級任務的時間
UpdateTimer(led1);
UpdateTimer(breath1);
UpdateTimer(serial1);

//執(zhí)行頂級任務
RunTask(LedTask, led1);
RunTask(BreathTask, breath1);
RunTask(SerialTask,serial1);
}

LED 的I/O 管腳初始化其實可以寫在專門的初始化函數(shù)里面,這里是為了更好的說明,寫在了runtasks()函數(shù)里。編程很靈活。
一般來說有幾個任務,就有幾個對應更新頂級任務的時間函數(shù)和對應的執(zhí)行頂級任務。
2把UpdateTimer(led1); 函數(shù)展開。
宏#define UpdateTimer(TaskVar) do{ if((TaskVar.task.timer!=0)&&(TaskVar.task.timer!=END))TaskVar.task.timer--; }  while(0)
帶入展開:{if((led1.task.timer!=0)&&(led1.task.timer!=END)) led1.task.timer--; }
延時時間unsignedshort timer; 變量值不等于0,也不等于END (65535),它的值就減一。等于0 就去執(zhí)行對應的函數(shù);等于65535 就掛起。這個和1.1
版本是一樣的。
led1是個結(jié)構(gòu)體變量。在led1結(jié)構(gòu)體里包含了一個task個結(jié)構(gòu)體變量,要引用里面的元素,用.分隔開寫到里面的最小元素。
task結(jié)構(gòu)體里面有兩個變量:unsignedshort  timer; 和unsignedchar lc; (有關結(jié)構(gòu)體變量展開看后面的第5部分 :有關結(jié)構(gòu)體及其宏的展開。)

3把RunTask(LedTask, led1);函數(shù)展開
宏#define RunTask(TaskName,TaskVar)  do{ if(TaskVar.task.timer==0)TaskVar.task.timer=TaskName(&(TaskVar)); }  while(0)
展開帶入:{if( led1.task.timer==0)  led1.task.timer=LedTask(&(led1)); }
假如延時時間到了timer==0,就執(zhí)行后面的函數(shù),執(zhí)行LedTask(&(led1))函數(shù),執(zhí)行完把結(jié)果賦值給led1.task.timer這個變量。這個和1.0版本是一樣的。
就是延時時間到,就去執(zhí)行任務函數(shù),然后把新的延時時間賦值給自己的timer,開始下一輪的循環(huán)。
因為小小調(diào)度器是協(xié)作式的,假如某個任務的時間延時到了,并不意味著要馬上執(zhí)行這個任務函數(shù),要等上一個任務釋放掉CPU后才執(zhí)行本任務函
數(shù);這樣就意味著,我們在編制任務函數(shù)的時候?qū)θ蝿蘸瘮?shù)的執(zhí)行,時間要求不是那么的嚴格,在一定范圍內(nèi)執(zhí)行就可以了;同時也意味著CPU 只有把
某個任務函數(shù)執(zhí)行完,把本任務該做的事做完后,然后再做其他的事情;因為cpu運行速度是比較快的,一般情況下占用CPU資源比較多的是等待條件
滿足和延時(來個數(shù)學運算或者什么的占用cpu時間較長怎么破?查表??,這和cpu有關,和調(diào)度器沒關?);對于等待條件滿足的可以用宏#define
WaitUntil(A) ,對于延時的可以用宏#defineWaitX(ticks),這樣可以在本任務等待或者延時的時候,做其他事情,提高效率。
我們看一下這個函數(shù):執(zhí)行LedTask(&(led1))的結(jié)果是一個值,這個函數(shù)的原型是LedTask(C_LedTask*cp) 。
也就是取led1結(jié)構(gòu)體變量的首地址(&( led1))傳遞到函數(shù)的原型中定義的結(jié)構(gòu)體變量指針(C_LedTask*cp)。把它們對應起來,就是定義一個結(jié)構(gòu)體指針,并指向led1 的首地址,這樣兩者對應起來 (結(jié)構(gòu)體變量led1和結(jié)構(gòu)體指針C_LedTask*cp類型都是一樣的;有關結(jié)構(gòu)體變量展開看后面的第5 部分 :有關
結(jié)構(gòu)體及其宏的展開。)。這里用結(jié)構(gòu)體指針主要的目的就是把彼此剝離,為實現(xiàn)重入做好準備。實現(xiàn)任務函數(shù)多次調(diào)用,彼此沒有影響。
為了書寫方便,作者做了一個宏#defineTaskFun(TaskName) TimeDefTaskName(C_##TaskName*cp){switch(me.task.lc){default:
因為這個函數(shù)有返回值,所以函數(shù)前面加了類型限制符unsignedshort (宏為:#defineTimeDef unsignedshort)。
為了書寫或者閱讀方便作者就做了一個語法糖 (就是一個宏#define me  (*cp),為了防止出錯,指針一定要加括號,涉及到優(yōu)先級的問題)。
其實所用的宏定義都可以認為是語法糖,用糖把語法包裹著,就是為了方便書寫、理解等等。
4,把TaskFun(LedTask);函數(shù)展開
宏#defineTaskFun(TaskName) TimeDefTaskName(C_##TaskName*cp){switch(me.task.lc){default:
展開,替換后:
//TaskFun(LedTask){

unsignedshort LedTask(C_LedTask*cp){switch(me.task.lc){default:{ //編譯器初始化的時候給lc賦值為0。?
me.timelen=20;//LEDPWM總周期為20 毫秒。//一般在沒有進入循環(huán)前,可以對一些變量賦值。V1.1版本用到私有變量一般是在這里定義的,為局部
pinMode(me.pin, OUTPUT);//設置管腳輸出 // 靜態(tài)變量;2.0版本用到的變量在前面統(tǒng)一定義,變量的應用是通過指針進行的。當然了平
// 時怎么用就怎么寫。
while(1)
{

digitalWrite(me.pin,HIGH);//點亮LED
//WaitX(me.timeon);
//#defineWaitX(ticks) do{ me.task.lc=LINE; return(ticks);case LINE:;}while(0)
{ me.task.lc=LINE; return(me.timeon);caseLINE:;}

digitalWrite(me.pin,LOW);//關閉LED
WaitX(me.timelen-me.timeon);
}
}EndFun

展開后可以看到,里面用到的變量都是通過結(jié)構(gòu)體指針,指向我們當初在“任務類及任務變量”那里定義的結(jié)構(gòu)體變量。這樣有關任務函數(shù)的操作
其實所有的數(shù)據(jù)都是存在任務自己所定義的變量里面;這樣函數(shù)重入就不出現(xiàn)問題,多次調(diào)用任務函數(shù)彼此不影響;當然了運行任務函數(shù)前要對先對任
務用到的變量定義,全部都是全局變量。這個也是V2.0 簡易版和v1.1板的一個區(qū)別。
返回me.timeon 是個unsignedchar類型的。返回去的時候類型被轉(zhuǎn)換為unsignedshort 型。其余和1.0版本一樣,在記錄行號的時候沒有用到靜態(tài)局
部變量,用的是每個任務變量里 task結(jié)構(gòu)體里面unsignedchar  lc;。lc 默認為uchar 也就是說TaskFun(TaskName) 任務函數(shù)里面WaitX(ticks)的個數(shù)(不
包擴它調(diào)用的子任務)不能超過256 (0-255)個,這個和1.1版本是一樣的。每個任務函數(shù)里面所寫語句的行數(shù)是沒有限制的,每個任務函數(shù)里面只能
有256個WaitX(ticks),如果發(fā)現(xiàn)編譯錯誤,也是在前面增加空行。在V1.1版本的時候,有位網(wǎng)友在編制任務函數(shù)的時候,有一個里面用的WaitX(ticks)個
數(shù)比較多,編寫代碼的行數(shù)也比較多,他發(fā)現(xiàn)編譯錯誤,就在WaitX(ticks)前面加空行,可是又和其他的WaitX(ticks)沖突,到后面每個WaitX(ticks)前面都
有數(shù)量不等的空行;為了避免這種事情出現(xiàn),一個是修改lc 變量的類型,這個在2.0版本是非常方便的,只要修改宏就行(#define LineDef unsignedchar)。
在V1.1也可以修改,只不過要修改兩三處地方。另外一個就是把函數(shù)優(yōu)化或者拆分等等,讓任務函數(shù)里面不要出現(xiàn)這么多的WaitX(ticks)。
執(zhí)行這個函數(shù),返回一個延時的數(shù)值,下次執(zhí)行的時候,通過SWITCH語句跳轉(zhuǎn)到上次執(zhí)行的位置,繼續(xù)執(zhí)行相關語句,并返回一個延時數(shù)值。這個
也是PT 的精華所在。如果不明白請參考1.0 版本的分解。

5.有關結(jié)構(gòu)體及其宏的展開。
(1).原型:
#defineClass(type) typedefstructC_##typeC_##type;struct C_##type
Class(task)
{
TimeDeftimer;
LineDeflc;
};

(2).把宏替換掉
typedefstructC_taskC_task;structC_task{
TimeDeftimer;
LineDeflc;
};

把宏替換掉后對于typedefstructC_taskC_task;structC_task{ 這句的理解分兩部分
a.紅色部分,用C_task代替structC_task。用C_task可以定義結(jié)構(gòu)體變量。
b.藍色部分 因為structC_task沒有定義,它的定義在下面,告訴上面不是沒定義嘛,在這定義了 。
c.一般來說類型定義typedefstructC_taskC_task;,應該放在它所重定義的類型的后面,就是應該在結(jié)構(gòu)體定義后面,像u8,u16那樣。
d.先做typedef 類型定義,也就是說,這屬于事先聲明,之后才有具體定義,跟函數(shù)聲明一樣 。
(3).等價于
typedefstructC_taskC_task;
structC_task{
TimeDeftimer;
LineDeflc;
};

在這里要注意,宏展開后可以看到,可以用C_task 定義結(jié)構(gòu)體,這個結(jié)構(gòu)體變量里面只包含 unsignedshorttimer; 和unsignedcharlc;。
(4).任務類及任務變量展開:
//Class(LedTask)
typedefstructC_LedTaskC_LedTask;struct C_LedTask
{

C_tasktask;//每個任務類都必須有task變量,里面只包含timer和lc 變量
unsignedcharpin;//LED對應的管腳
unsignedchartimeon;//LED點亮的時長
unsignedchartimelen;  //LED循環(huán)點亮的周期
}led1;

定義了一個名為led1 的結(jié)構(gòu)體變量。
在這里需要注意一點:用C_LedTask可以定義結(jié)構(gòu)體變量。用如果是C_LedTaskled2; 這是定義了一個名為led2 的結(jié)構(gòu)體,里面的元素和led1里面的一樣;
要區(qū)分用C_LedTask和用C_task 定義結(jié)構(gòu)體的區(qū)別。

在這個任務類里面定義了每個任務函數(shù)所用到的私有變量,及每個任務函數(shù)用到的記錄執(zhí)行地址的lc變量和記錄需要延時的變量timer;是個完整的獨立的個體。用到子任務時,在父任務結(jié)構(gòu)體中用 C_***task  定義一個子任務結(jié)構(gòu)體變量 ,從某種意義上講任務重入也是需要代價的。

有時候感覺繞來繞去,其實就是這個任務類的問題,
1.因為任務類及任務變量定義中用Class 定義任務所用到的私有變量和一個獨立的C_tasktask,里面放著這個任務函數(shù)的行號和延時時間變量。用宏
C_***Task可以定義和本任務相關所有變量的結(jié)構(gòu)體。
2.如果任務類里面包含了其他的子任務。一般包含一個或者幾個用 C_***task  定義的結(jié)構(gòu)體變量;(其實就是相當于把子任務中用到的所有變量在這
個父任務中又重新定義了一下)。父任務調(diào)用這個子任務,會把數(shù)據(jù)放到這個子任務的結(jié)構(gòu)體里;同理其他父任務調(diào)用同一個子任務也會把數(shù)據(jù)放到自
己的子任務結(jié)構(gòu)體里。
3.父任務函數(shù)調(diào)用子任務函數(shù)時,用到的數(shù)據(jù)是通過指針傳遞的,把這些變量傳遞給子任務函數(shù)。當然了父任務函數(shù)變量的傳遞也是通過指針的。這樣結(jié)
合每個任務定義的結(jié)構(gòu)體變量,就能解決任務重入的問題了。子任務可以被多個主任務調(diào)用,主任務可以給子任務傳遞參數(shù)。主任務彼此獨立互不影響。

V1.1版本,記錄行號的變量是局部靜態(tài)變量,涉及到跨任務的變量都是靜態(tài)局部變量或者靜態(tài)全局變量;延時變量是個全局變量。
V2.0版本,每個任務函數(shù)用到的變量都是自己的私有全局變量,在調(diào)用的時候通過指針傳遞。


二.呼吸燈

在上面LED控制的基礎上設計一個呼吸燈,指示燈從暗到亮變化,分20個階段;再從亮到暗變化,也分20個階段,每個階段保持100ms
分析:
作為一個獨立的頂級任務,設置的時候,就需要有自己的任務變量和任務函數(shù)。
把呼吸燈用到的變量統(tǒng)一放在一個結(jié)構(gòu)體中,起名:breath1;呼吸燈對應的任務函數(shù)定義為BreathTask。編寫任務函數(shù)的時候,注意把他們定義的結(jié)構(gòu)體
名和函數(shù)名對應起來,這樣就不容易弄混了。
1.呼吸燈任務類及任務變量
Class(BreathTask)//LED 呼吸燈控制任務
{

C_tasktask;//每個任務類都必須有task變量,里面存放著延時變量和行號。
unsignedchari;//呼吸燈變量,
}breath1;  //呼吸燈的結(jié)構(gòu)體變量名
2.呼吸燈任務函數(shù) (呼吸燈的具體動作)
TaskFun(BreathTask){//實現(xiàn)呼吸燈效果
while(1)
{

//從暗到亮變化
for(me.i=0;me.i<20;me.i++){  //這個變量i就是我們在結(jié)構(gòu)體breath1 中定義的unsignedchari;//呼吸燈變量。
WaitX(100); //和1.0版本原理一樣,釋放CPU,過100ms 再往下執(zhí)行。
led1.timeon=me.i; //把呼吸燈的變量值,賦值給了LED任務函數(shù)里的變量了,因為定義的結(jié)構(gòu)體都是全局變量的,可以相互調(diào)用,賦值。
}

//再從亮到暗變化
for(me.i=20;me.i>0;me.i--){
WaitX(100);

led1.timeon=me.i; //用到的本任務函數(shù)的變量是通過指針;在這里引用其他任務的變量,是直接引用的。執(zhí)行到LED任務函數(shù)的時候值變了。
}
}
}EndFun

3.任務函數(shù)里的變量,都是通過指針傳遞的,和各自定義的結(jié)構(gòu)體對應起來。由于2.0版本需要支持重入,任務函數(shù)值不能直接傳給結(jié)構(gòu)體,
而是地址進去,進去后轉(zhuǎn)換回結(jié)構(gòu)體。
4.在這個呼吸燈的任務函數(shù)中用到了其他任務函數(shù)中的變量:led1.timeon=me.i;,通過賦值,下次執(zhí)行LED 函數(shù)的時候就會發(fā)生變化。也就是說
這個調(diào)度器支持任務之間的數(shù)據(jù)互訪。
5.如果這個呼吸燈任務函數(shù)里面不用數(shù)據(jù)互訪賦值,而用子任務調(diào)用,怎么寫?假如沒有這個呼吸燈的任務函數(shù),執(zhí)行l(wèi)ed任務函數(shù),led燈的狀態(tài)是什
么?

三.子任務分析
涉及到的宏#defineCallSub(SubTaskName,SubTaskVar) do{WaitX(0);SubTaskVar.task.timer=SubTaskName(&(SubTaskVar)); \
if(SubTaskVar.task.timer!=END)returnSubTaskVar.task.timer;}while(0)

看串口任務類及任務變量,
Class(SerialTask)
{

C_tasktask;  //每個任務類都必須有task變量
C_WaitsecTaskwaitsec1;//串口任務擁有一個秒延時子任務
Stringcomdata;//串口任務自己用的變量
}serial1;

定義了一個結(jié)構(gòu)體變量serial1,里面除了自己用的變量外,增加了一個C_WaitsecTaskwaitsec1; (定義了一個結(jié)構(gòu)體,里面包含了WaitsecTask任務函數(shù)所
用到的全部變量)接下來我們看一下串口的任務函數(shù)。

TaskFun(SerialTask){//串口任務,定時輸出hello
Serial.begin(9600);
Serial.println("start");
while(1){

me.waitsec1.seconds=1;//總共延遲1+2=3秒
CallSub(WaitsecTask,me.waitsec1);
Serial.println("hello");
}
}EndFun

分解開來看一看
TaskFun(SerialTask){//串口任務,定時輸出hello
根據(jù)上面分析的經(jīng)驗,執(zhí)行完任務函數(shù)的有關指令,返回一個延時函數(shù)給timer。
我們看一下有關語句:

Serial.begin(9600);Serial.println("start");不用關心,串口的波特率和起始位什么的,(猜的)。
程序執(zhí)行到me.waitsec1.seconds=1;很簡單,給自己里面子任務中的變量賦了一個值,看清楚是要求子任務延時1個單位。
接著繼續(xù)執(zhí)行到CallSub(WaitsecTask,me.waitsec1);我們看一下它的宏
#defineCallSub(SubTaskName,SubTaskVar) do{WaitX(0);SubTaskVar.task.timer=SubTaskName(&(SubTaskVar)); \
if(SubTaskVar.task.timer!=END)returnSubTaskVar.task.timer;}while(0)

把有關參數(shù)帶進去。
{WaitX(0);me.waitsec1.task.timer=WaitsecTask(&(me.waitsec1)); if(me.waitsec1.task.timer!=END)returnme.waitsec1.task.timer;}

展開分析:
執(zhí)行WaitX(0);在這里設置一個“斷點”,讓任務下次從這里執(zhí)行。記錄當前LC 位置,這樣如果子任務有WAIT(X),出來以后下次能順利進去。(分析一下
如果沒有WaitX(0);會發(fā)生什么問題?)
執(zhí)行自己的子任務WaitsecTask(&(me.waitsec1)把結(jié)果賦值給自己定義的子任務變量里的timer變量,
在程序中找找到WaitsecTask(),函數(shù)的原型:
#defineTaskFun(TaskName) TimeDefTaskName(C_##TaskName *cp){switch(me.task.lc){default:

TaskFun(WaitsecTask){//實現(xiàn)指定的秒數(shù)延遲 (me.waitsec1.seconds=1;在本例中賦值為1S),之后再加上2秒延遲
for(me.i=0;me.i<me.seconds;me.i++){
WaitX(1000);
}

CallSub(Wait2Task,me.wait2);//這里通過調(diào)用2秒固定延遲子任務,實現(xiàn)額外的2秒延遲。
}EndFun

執(zhí)行完自己指定的延時后,繼續(xù)執(zhí)行自己子任務里面子任務調(diào)用的它的子任務,CallSub(Wait2Task,me.wait2),再實現(xiàn)2S 的延時,展開略。

通過上面的分析,我們很清楚的看到用Class(task)定義結(jié)構(gòu)體用起來是很方便的,除了考慮自己父任務函數(shù)里必須的變量外,對于子函數(shù)的調(diào)用只要
定義一個宏,(其實是把每一層的變量都放在了自己定義的宏里面了),用CallSub(SubTaskName,SubTaskVar)函數(shù)調(diào)用就可以了。只要你的內(nèi)存大你可以無限的調(diào)用,無論子程序怎么調(diào)用,彼此互不影響。
定義了任務類(Class(task)),在函數(shù)變量應用和子程序變量定義的時候很靈活,減少我們的書寫量,每個任務函數(shù)用到的數(shù)據(jù),都保持在自己獨立
定義的變量中;函數(shù)調(diào)用用指針;這樣,函數(shù)就可以實現(xiàn)重入。任務函數(shù)可以相互調(diào)用;只要你的內(nèi)存足夠大,就可以無限調(diào)用。
以上展開后都在強調(diào)為任務重入做準備,其實如果不用到任務重入功能,把time變量改為uchar感覺V2.0 簡易版和V1.1所用的資源相差不多,V2.0
用到的變量全部是全局變量,V1.1用到的變量涉及到任務之間的切換都是局部靜態(tài)變量。其實v2.0 簡易版這種寫法感覺比V1.1 的更加清晰。


四.總結(jié)

通過上面的分解,我們再回頭看一下作者smset 對V2.0 的評價
主要改進:
1)徹底解決了任務重入問題
2)很好的解決了任務之間的通信問題
3)引入面向任務對象的概念
4)任務具有自己的變量,提高了程序封裝程度

0.png

單片機源程序如下:
  1. #include "arduino.h"
  2. #include "xxddq.h"

  3. //-----任務類及任務變量在這里定義----------------
  4. Class(Wait2Task) //一個固定延時2秒的子任務
  5. {
  6.   C_task task; //每個任務類都必須有task變量
  7.   unsigned char i;
  8. };

  9. Class(WaitsecTask)
  10. {
  11.   C_task task; //每個任務類都必須有task變量
  12.   C_Wait2Task wait2; //waitsectask擁有一個Wait2Task的子任務
  13.   unsigned char seconds;
  14.   unsigned char i;
  15. };

  16. Class(SerialTask)
  17. {
  18.   C_task task;  //每個任務類都必須有task變量
  19.   C_WaitsecTask waitsec1;//串口任務擁有一個秒延時子任務
  20.   String comdata; //串口任務自己用的變量
  21. }serial1;

  22. Class(LedTask)
  23. {
  24.   C_task task;//每個任務類都必須有task變量
  25.   unsigned char pin;//LED對應的管腳
  26.   unsigned char timeon;//LED點亮的時長
  27.   unsigned char timelen;  //LED循環(huán)點亮的周期
  28. }led1;

  29. Class(BreathTask)//LED呼吸燈控制任務
  30. {
  31.   C_task task;//每個任務類都必須有task變量,通過控制ledtask的timeon來實現(xiàn)呼吸燈亮滅效果
  32.   unsigned char i;
  33. }breath1;

  34. //------------------任務函數(shù)在這里實現(xiàn)------------------------------------------
  35. TaskFun(Wait2Task){//實現(xiàn)固定兩秒延遲
  36.      for (me.i=0;me.i<20;me.i++){
  37.         WaitX(100);      
  38.       }
  39. }EndFun

  40. TaskFun(WaitsecTask){//實現(xiàn)指定的秒數(shù)延遲,之后再加上2秒延遲
  41.      for (me.i=0;me.i<me.seconds;me.i++){
  42.         WaitX(1000);      
  43.       }
  44.      CallSub(Wait2Task,me.wait2);//這里通過調(diào)用2秒固定延遲子任務,實現(xiàn)額外的2秒延遲。
  45. }EndFun

  46. TaskFun(LedTask){
  47.    me.timelen=20;//LED PWM總周期為20毫秒。
  48.    pinMode(me.pin, OUTPUT);//設置管腳輸出

  49.    while(1)
  50.    {
  51.       digitalWrite(me.pin,HIGH);//電亮LED
  52.       WaitX(me.timeon);   
  53.       digitalWrite(me.pin,LOW);//關閉LED
  54.       WaitX(me.timelen-me.timeon);  
  55.    }
  56. }EndFun

  57. TaskFun(BreathTask){//實現(xiàn)呼吸等效果
  58.   while(1)
  59.   {
  60.     //從暗到亮變化
  61.    for (me.i=0;me.i<20;me.i++){
  62.       WaitX(100);
  63.       led1.timeon=me.i;
  64.    }
  65.    //再從亮到暗變化
  66.    for (me.i=20;me.i>0;me.i--){
  67.       WaitX(100);
  68.       led1.timeon=me.i;
  69.    }
  70.   }
  71. }EndFun

  72. TaskFun(SerialTask){ //串口任務,定時輸出hello
  73.    Serial.begin(9600);
  74.    Serial.println("start");
  75.    while(1){
  76.       me.waitsec1.seconds=1;//總共延遲1+2 =3秒
  77.       CallSub(WaitsecTask,me.waitsec1);
  78.       Serial.println("hello");      
  79.    }
  80. }EndFun
  81. //------------------------------------------------------------------------

  82. #define BUILTIN_LED 13
  83. void runtasks(){
  84.   //指定led1任務驅(qū)動的IO管腳
  85.   led1.pin=13;

  86.   //更新頂級任務的時間
  87.   UpdateTimer(led1);
  88.   UpdateTimer(breath1);   
  89.   UpdateTimer(serial1);
  90.      
  91.   //執(zhí)行頂級任務
  92.   RunTask(LedTask, led1);
  93. ……………………

  94. …………限于本文篇幅 余下代碼請從51黑下載附件…………
復制代碼

所有資料51hei提供下載:
小小調(diào)度器V2.0 簡化版.zip (1.93 KB, 下載次數(shù): 164)

評分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎勵!

查看全部評分

回復

使用道具 舉報

ID:389812 發(fā)表于 2018-8-24 23:38 | 顯示全部樓層
main.c

#include <limits.h>
#include <reg52.h>
#include "OS.h"

TaskFun(led)
{
        while(1)
        {
                if(me.pin==5)
                {
                        P0_5=0;
                        WaitX(1_s);
                        P0_5=1;
                        WaitX(1_s);
                }
                if(me.pin==6)
                {
                        P0_6=0;
                        WaitX(2_s);
                        P0_6=1;
                        WaitX(2_s);
                }
        }
}EndFun

TaskFun(display)
{
                CallSub(led,me.led);
}EndFun


void timer1() interrupt 3
{                       
        TR1 = 0;                        //暫停定時器
        n.b[0] = TH1;
        n.b[1] = TL1;
        n.a = n.a + CT;
        TH1 = n.b[0];
        TL1 = n.b[1];
        TR1 = 1;                        //啟動定時器

        UpdateTimers();
               
}

void main(void)
{       
        TMOD = 0x15;
        ET1 = 1;
        EA = 1;
        TR1 = 1;

        display_1.led.pin=5;
        display_2.led.pin=6;

        while(1)
        {
                RunTask(display,display_1);
                RunTask(display,display_2);
        }
}

回復

使用道具 舉報

ID:445062 發(fā)表于 2019-4-14 22:04 | 顯示全部樓層
學習一下對比protothreads有哪些改進。
回復

使用道具 舉報

ID:428114 發(fā)表于 2019-7-6 21:51 | 顯示全部樓層
學習了
回復

使用道具 舉報

ID:43342 發(fā)表于 2019-9-14 19:07 | 顯示全部樓層
謝謝樓主!
回復

使用道具 舉報

ID:641609 發(fā)表于 2019-11-14 00:46 | 顯示全部樓層
整理得不錯
回復

使用道具 舉報

ID:641609 發(fā)表于 2019-11-14 19:24 | 顯示全部樓層
本帖最后由 CSM_Min 于 2019-11-14 23:14 編輯

Snipaste_2019-11-14_19-18-59.png
1, 代碼使用pic編譯出錯,  HI-TECH Software\PICC\9.83, 不知道到底是哪里不支持?

2, 用stm32的keil編譯,仿真功能正常


回復

使用道具 舉報

ID:641609 發(fā)表于 2019-11-15 11:44 | 顯示全部樓層
Snipaste_2019-11-15_11-40-19.png

弄到現(xiàn)在終于搞定了, 但是我還有疑問,就是
1, 那兩個子程序結(jié)尾的do whlie 有什么具體作用呢?   我認為可以不要do while



回復

使用道具 舉報

ID:115836 發(fā)表于 2019-12-9 19:24 | 顯示全部樓層
這種寫法源于Linux內(nèi)核代碼。
do{...}while(0)這樣的寫法可以避免宏展開時的一些坑。
回復

使用道具 舉報

ID:40043 發(fā)表于 2021-4-13 12:56 | 顯示全部樓層
這個資料整理的很全面,真的不錯啊!
回復

使用道具 舉報

ID:40043 發(fā)表于 2021-4-13 12:57 | 顯示全部樓層
這個小調(diào)度器,真心不錯,尤其適合哪種資源太小的MCU;
回復

使用道具 舉報

ID:105845 發(fā)表于 2022-11-8 15:09 | 顯示全部樓層
不錯  好好研究一下
回復

使用道具 舉報

ID:67839 發(fā)表于 2023-1-8 16:12 | 顯示全部樓層
下載研究一下
回復

使用道具 舉報

ID:433219 發(fā)表于 2023-1-9 08:41 | 顯示全部樓層
時間輪片?
回復

使用道具 舉報

ID:87000 發(fā)表于 2023-1-9 09:00 | 顯示全部樓層
先收藏再說。覺得還可以
回復

使用道具 舉報

ID:87000 發(fā)表于 2023-2-13 11:21 | 顯示全部樓層
學習了,一直在找可用的小系統(tǒng)
回復

使用道具 舉報

ID:898721 發(fā)表于 2023-2-20 20:46 | 顯示全部樓層
編譯沒有通過,好像是宏不支持這種寫法,或者哪里沒搞對,大神幫忙看看有沒有遇到這種問題
Snipaste_2023-02-20_20-43-25.jpg
回復

使用道具 舉報

ID:339654 發(fā)表于 2023-3-21 19:03 | 顯示全部樓層
我覺得調(diào)度器還是用定時器來控制時基
回復

使用道具 舉報

ID:1109308 發(fā)表于 2024-1-18 10:12 | 顯示全部樓層
學習一下思想,看能不能移植
回復

使用道具 舉報

ID:1130560 發(fā)表于 2024-11-16 08:31 | 顯示全部樓層
大佬們,多任務操作同一硬件,譬如串口1,怎么做互斥處理呢?
回復

使用道具 舉報

ID:899151 發(fā)表于 2025-1-24 09:57 | 顯示全部樓層
不是真的2.0吧,
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 网色 | 亚洲欧洲精品在线 | www.日韩免费 | 国产美女在线观看 | 亚洲成人免费视频在线观看 | 正在播放国产精品 | 欧洲一区二区三区 | 国精产品一区一区三区免费完 | 亚洲精品久久久一区二区三区 | 99久久夜色精品国产亚洲96 | 亚洲国产欧美精品 | 中文字幕欧美在线观看 | www.久久 | 久久久精品在线 | 亚洲成人一区二区 | 中文字幕在线精品 | 国产精品一区二区福利视频 | 久久久久成人精品免费播放动漫 | 欧美日韩三级 | 91精品一区二区三区久久久久 | 99r在线| 一级片成人 | 99热激情 | 欧美高清免费 | 日韩欧美一级精品久久 | 精品久久久久久久久久久久久久 | 国产一区二区三区四区五区3d | 国产精品美女久久久久久免费 | tube国产 | 国产精品一区二区三区久久 | 酒色成人网 | www.久久| 久久久精品网 | 久久噜噜噜精品国产亚洲综合 | 国产成人高清成人av片在线看 | 亚洲精品中文字幕在线观看 | 91精品在线播放 | 在线一区视频 | 一区二区免费高清视频 | 久久精品网 | 日韩av美女电影 |