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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 11538|回復: 20
打印 上一主題 下一主題
收起左側

小小調度器V2.0簡易版源碼與分析

  [復制鏈接]
跳轉到指定樓層
樓主
本文作者:Gthgth

注意:小小調度器V2.0 作者:  兔子、smset

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

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

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

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

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

一.主函數分析

voidmain(){
while(1){

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

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

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

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

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

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

LED 的I/O 管腳初始化其實可以寫在專門的初始化函數里面,這里是為了更好的說明,寫在了runtasks()函數里。編程很靈活。
一般來說有幾個任務,就有幾個對應更新頂級任務的時間函數和對應的執行頂級任務。
2把UpdateTimer(led1); 函數展開。
宏#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 就去執行對應的函數;等于65535 就掛起。這個和1.1
版本是一樣的。
led1是個結構體變量。在led1結構體里包含了一個task個結構體變量,要引用里面的元素,用.分隔開寫到里面的最小元素。
task結構體里面有兩個變量:unsignedshort  timer; 和unsignedchar lc; (有關結構體變量展開看后面的第5部分 :有關結構體及其宏的展開。)

3把RunTask(LedTask, led1);函數展開
宏#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,就執行后面的函數,執行LedTask(&(led1))函數,執行完把結果賦值給led1.task.timer這個變量。這個和1.0版本是一樣的。
就是延時時間到,就去執行任務函數,然后把新的延時時間賦值給自己的timer,開始下一輪的循環。
因為小小調度器是協作式的,假如某個任務的時間延時到了,并不意味著要馬上執行這個任務函數,要等上一個任務釋放掉CPU后才執行本任務函
數;這樣就意味著,我們在編制任務函數的時候對任務函數的執行,時間要求不是那么的嚴格,在一定范圍內執行就可以了;同時也意味著CPU 只有把
某個任務函數執行完,把本任務該做的事做完后,然后再做其他的事情;因為cpu運行速度是比較快的,一般情況下占用CPU資源比較多的是等待條件
滿足和延時(來個數學運算或者什么的占用cpu時間較長怎么破?查表??,這和cpu有關,和調度器沒關?);對于等待條件滿足的可以用宏#define
WaitUntil(A) ,對于延時的可以用宏#defineWaitX(ticks),這樣可以在本任務等待或者延時的時候,做其他事情,提高效率。
我們看一下這個函數:執行LedTask(&(led1))的結果是一個值,這個函數的原型是LedTask(C_LedTask*cp) 。
也就是取led1結構體變量的首地址(&( led1))傳遞到函數的原型中定義的結構體變量指針(C_LedTask*cp)。把它們對應起來,就是定義一個結構體指針,并指向led1 的首地址,這樣兩者對應起來 (結構體變量led1和結構體指針C_LedTask*cp類型都是一樣的;有關結構體變量展開看后面的第5 部分 :有關
結構體及其宏的展開。)。這里用結構體指針主要的目的就是把彼此剝離,為實現重入做好準備。實現任務函數多次調用,彼此沒有影響。
為了書寫方便,作者做了一個宏#defineTaskFun(TaskName) TimeDefTaskName(C_##TaskName*cp){switch(me.task.lc){default:
因為這個函數有返回值,所以函數前面加了類型限制符unsignedshort (宏為:#defineTimeDef unsignedshort)。
為了書寫或者閱讀方便作者就做了一個語法糖 (就是一個宏#define me  (*cp),為了防止出錯,指針一定要加括號,涉及到優先級的問題)。
其實所用的宏定義都可以認為是語法糖,用糖把語法包裹著,就是為了方便書寫、理解等等。
4,把TaskFun(LedTask);函數展開
宏#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 毫秒。//一般在沒有進入循環前,可以對一些變量賦值。V1.1版本用到私有變量一般是在這里定義的,為局部
pinMode(me.pin, OUTPUT);//設置管腳輸出 // 靜態變量;2.0版本用到的變量在前面統一定義,變量的應用是通過指針進行的。當然了平
// 時怎么用就怎么寫。
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

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

5.有關結構體及其宏的展開。
(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可以定義結構體變量。
b.藍色部分 因為structC_task沒有定義,它的定義在下面,告訴上面不是沒定義嘛,在這定義了 。
c.一般來說類型定義typedefstructC_taskC_task;,應該放在它所重定義的類型的后面,就是應該在結構體定義后面,像u8,u16那樣。
d.先做typedef 類型定義,也就是說,這屬于事先聲明,之后才有具體定義,跟函數聲明一樣 。
(3).等價于
typedefstructC_taskC_task;
structC_task{
TimeDeftimer;
LineDeflc;
};

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

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

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

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

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

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


二.呼吸燈

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

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

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

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

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

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

三.子任務分析
涉及到的宏#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;

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

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
根據上面分析的經驗,執行完任務函數的有關指令,返回一個延時函數給timer。
我們看一下有關語句:

Serial.begin(9600);Serial.println("start");不用關心,串口的波特率和起始位什么的,(猜的)。
程序執行到me.waitsec1.seconds=1;很簡單,給自己里面子任務中的變量賦了一個值,看清楚是要求子任務延時1個單位。
接著繼續執行到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)

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

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

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

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

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

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


四.總結

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



單片機源程序如下:
  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循環點亮的周期
  28. }led1;

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

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

  40. TaskFun(WaitsecTask){//實現指定的秒數延遲,之后再加上2秒延遲
  41.      for (me.i=0;me.i<me.seconds;me.i++){
  42.         WaitX(1000);      
  43.       }
  44.      CallSub(Wait2Task,me.wait2);//這里通過調用2秒固定延遲子任務,實現額外的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){//實現呼吸等效果
  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任務驅動的IO管腳
  85.   led1.pin=13;

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

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

所有資料51hei提供下載:
小小調度器V2.0 簡化版.zip (1.93 KB, 下載次數: 164)
小小調度器V2.0 Simple 整理說明2.pdf (212.7 KB, 下載次數: 127)


評分

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

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏17 分享淘帖 頂1 踩
回復

使用道具 舉報

沙發
ID:389812 發表于 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 發表于 2019-4-14 22:04 | 只看該作者
學習一下對比protothreads有哪些改進。
回復

使用道具 舉報

地板
ID:428114 發表于 2019-7-6 21:51 | 只看該作者
學習了
回復

使用道具 舉報

5#
ID:43342 發表于 2019-9-14 19:07 | 只看該作者
謝謝樓主!
回復

使用道具 舉報

6#
ID:641609 發表于 2019-11-14 00:46 | 只看該作者
整理得不錯
回復

使用道具 舉報

7#
ID:641609 發表于 2019-11-14 19:24 | 只看該作者
本帖最后由 CSM_Min 于 2019-11-14 23:14 編輯


1, 代碼使用pic編譯出錯,  HI-TECH Software\PICC\9.83, 不知道到底是哪里不支持?

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


回復

使用道具 舉報

8#
ID:641609 發表于 2019-11-15 11:44 | 只看該作者


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



回復

使用道具 舉報

9#
ID:115836 發表于 2019-12-9 19:24 | 只看該作者
這種寫法源于Linux內核代碼。
do{...}while(0)這樣的寫法可以避免宏展開時的一些坑。
回復

使用道具 舉報

10#
ID:40043 發表于 2021-4-13 12:56 | 只看該作者
這個資料整理的很全面,真的不錯啊!
回復

使用道具 舉報

11#
ID:40043 發表于 2021-4-13 12:57 | 只看該作者
這個小調度器,真心不錯,尤其適合哪種資源太小的MCU;
回復

使用道具 舉報

12#
ID:105845 發表于 2022-11-8 15:09 | 只看該作者
不錯  好好研究一下
回復

使用道具 舉報

13#
ID:67839 發表于 2023-1-8 16:12 | 只看該作者
下載研究一下
回復

使用道具 舉報

14#
ID:433219 發表于 2023-1-9 08:41 | 只看該作者
時間輪片?
回復

使用道具 舉報

15#
ID:87000 發表于 2023-1-9 09:00 | 只看該作者
先收藏再說。覺得還可以
回復

使用道具 舉報

16#
ID:87000 發表于 2023-2-13 11:21 | 只看該作者
學習了,一直在找可用的小系統
回復

使用道具 舉報

17#
ID:898721 發表于 2023-2-20 20:46 | 只看該作者
編譯沒有通過,好像是宏不支持這種寫法,或者哪里沒搞對,大神幫忙看看有沒有遇到這種問題

Snipaste_2023-02-20_20-43-25.jpg (414.8 KB, 下載次數: 157)

Snipaste_2023-02-20_20-43-25.jpg
回復

使用道具 舉報

18#
ID:339654 發表于 2023-3-21 19:03 | 只看該作者
我覺得調度器還是用定時器來控制時基
回復

使用道具 舉報

19#
ID:1109308 發表于 2024-1-18 10:12 | 只看該作者
學習一下思想,看能不能移植
回復

使用道具 舉報

20#
ID:1130560 發表于 2024-11-16 08:31 | 只看該作者
大佬們,多任務操作同一硬件,譬如串口1,怎么做互斥處理呢?
回復

使用道具 舉報

21#
ID:899151 發表于 2025-1-24 09:57 | 只看該作者
不是真的2.0吧,
回復

使用道具 舉報

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

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 男人天堂999 | 中文字幕一区二区三区在线观看 | 一区影视| 91av在线影院| 欧美久久久久 | 精品国产欧美一区二区三区成人 | 天天插天天射天天干 | 国产精品美女久久久久aⅴ国产馆 | 国产剧情一区 | 久久综合伊人 | 亚洲电影成人 | 午夜视频免费在线观看 | 天天射影院 | 欧美久久精品一级黑人c片 91免费在线视频 | 少妇精品久久久久久久久久 | 国产一区二区精品在线 | 中文字幕 亚洲一区 | 久久久久久久久久久久久9999 | 日韩中文一区二区三区 | 亚洲精品电影在线观看 | 男女又爽又黄视频 | 日日操夜夜操天天操 | 51ⅴ精品国产91久久久久久 | 欧美成人h版在线观看 | 日韩在线不卡视频 | 亚洲欧美视频一区 | 亚洲一区二区三区四区五区中文 | 久久精品欧美一区二区三区麻豆 | 特级黄一级播放 | 97视频精品 | 午夜电影福利 | 日韩成人在线网址 | 免费a网站 | 91精品国产一区二区 | 99国产精品久久久久老师 | 97久久精品 | 欧美一区视频 | 国产在线精品一区二区 | 成人在线免费视频观看 | 国产乱码精品一品二品 | 亚洲欧美自拍偷拍视频 |