|
恩。。好久沒(méi)發(fā)帖了,不過(guò)這幾天也沒(méi)閑著
前幾天我在求助區(qū)發(fā)了個(gè)關(guān)于定時(shí)器的問(wèn)題,結(jié)果沒(méi)有讓我滿意的答案
于是自己百度+谷歌,終于找到了一個(gè)非常強(qiáng)大的庫(kù)--ProtoThreads!
一個(gè)非常強(qiáng)大的多任務(wù)庫(kù),非常適合arduino這種資源非常有限的單片機(jī)
別急,先上一段簡(jiǎn)單的代碼look look
- #include <pt.h>
-
- static int counter1,counter2,state1=0,state2=0;
-
- static int protothread1(struct pt *pt)
- {
- PT_BEGIN(pt);
- while(1)
- {
- PT_WAIT_UNTIL(pt, counter1==1);
- digitalWrite(12,state1);
- state1=!state1;
- counter1=0;
- }
- PT_END(pt);
- }
-
-
- static int protothread2(struct pt *pt)
- {
- PT_BEGIN(pt);
- while(1) {
- PT_WAIT_UNTIL(pt, counter2==5);
- counter2=0;
- digitalWrite(13,state2);
- state2=!state2;
- }
- PT_END(pt);
- }
-
-
- static struct pt pt1, pt2;
- void setup()
- {
-
- pinMode(12,OUTPUT);
- pinMode(13,OUTPUT);
- PT_INIT(&pt1);
- PT_INIT(&pt2);
- }
-
- void loop ()
- {
- protothread1(&pt1);
- protothread2(&pt2);
- delay(1000);
- counter1++;
- counter2++;
- }
復(fù)制代碼
此段代碼演示了如何使用PT庫(kù)來(lái)實(shí)現(xiàn)12、13腳led分別隔1秒、5秒閃爍,已經(jīng)在arduino09上測(cè)試通過(guò)
sorry,無(wú)注釋。。別急,這只是個(gè)演示
這篇文章會(huì)不斷更新,分別講述PT庫(kù)的原理和應(yīng)用
讓大家能開發(fā)出更復(fù)雜的程序
好介紹開始了~
Protothread是專為資源有限的系統(tǒng)設(shè)計(jì)的一種耗費(fèi)資源特別少并且不使用堆棧的線程模型,其特點(diǎn)是:
◆ 以純C語(yǔ)言實(shí)現(xiàn),無(wú)硬件依賴性;
◆ 極少的資源需求,每個(gè)Protothread僅需要2個(gè)額外的字節(jié);
◆ 可以用于有操作系統(tǒng)或無(wú)操作系統(tǒng)的場(chǎng)合;
◆ 支持阻塞操作且沒(méi)有棧的切換。
使用Protothread實(shí)現(xiàn)多任務(wù)的最主要的好處在于它的輕量級(jí)。每個(gè)Protothread不需要擁有自已的堆棧,所有的Protothread 共享同一個(gè)堆?臻g,這一點(diǎn)對(duì)于RAM資源有限的系統(tǒng)尤為有利。相對(duì)于操作系統(tǒng)下的多任務(wù)而言,每個(gè)任務(wù)都有自已的堆棧空間,這將消耗大量的RAM資源,而每個(gè)Protothread僅使用一個(gè)整型值保存當(dāng)前狀態(tài)。
咱們來(lái)結(jié)合一個(gè)最簡(jiǎn)單的例子來(lái)理解ProtoThreads的原理吧,就拿上面的閃爍燈代碼來(lái)說(shuō)
- #include <pt.h>//ProtoThreads必須包含的頭文件
- static int counter1,counter2,state1=0,state2=0; //counter為定時(shí)計(jì)數(shù)器,state為每個(gè)燈的狀態(tài)
- static int protothread1(struct pt *pt) //線程1,控制燈1
- {
- PT_BEGIN(pt); //線程開始
- while(1) //每個(gè)線程都不會(huì)死
- {
- PT_WAIT_UNTIL(pt, counter1==1); //如果時(shí)間滿了1秒,則繼續(xù)執(zhí)行,否則記錄運(yùn)行點(diǎn),退出線程1
- digitalWrite(12,state1);
- state1=!state1;//燈狀態(tài)反轉(zhuǎn)
- counter1=0; //計(jì)數(shù)器置零
- }
- PT_END(pt); //線程結(jié)束
- }
- static int protothread2(struct pt *pt) //線程2,控制燈2
- {
- PT_BEGIN(pt); //線程開始
- while(1) { //每個(gè)線程都不會(huì)死
- PT_WAIT_UNTIL(pt, counter2==5); //如果時(shí)間滿了5秒,則繼續(xù)執(zhí)行,否則記錄運(yùn)行點(diǎn),退出線程2
- counter2=0; //計(jì)數(shù)清零
- digitalWrite(13,state2);
- state2=!state2; //燈狀態(tài)反轉(zhuǎn)
- }
- PT_END(pt); //線程結(jié)束
- }
- static struct pt pt1, pt2;
- void setup()
- {
- pinMode(12,OUTPUT);
- pinMode(13,OUTPUT);
- PT_INIT(&pt1); //線程1初始化
- PT_INIT(&pt2); //線程2初始化
- }
- void loop () //這就是進(jìn)行線程調(diào)度的地方
- {
- protothread1(&pt1); //執(zhí)行線程1
- protothread2(&pt2); //執(zhí)行線程2
- delay(1000); //時(shí)間片,每片1秒,可根據(jù)具體應(yīng)用設(shè)置大小
- counter1++;
- counter2++;
- }
復(fù)制代碼
看上面的代碼,你會(huì)發(fā)現(xiàn)很多大寫的函數(shù),其實(shí)那些都是些宏定義(宏定義用大寫是約定俗成的..),如果把這些宏都展開你就能更好的理解他的原理了:
- #include <pt.h>//ProtoThreads必須包含的頭文件
- static int counter1,counter2,state1=0,state2=0; //counter為定時(shí)計(jì)數(shù)器,state為每個(gè)燈的狀態(tài)
- static int protothread1(struct pt *pt) //線程1,控制燈1
- {
- { char PT_YIELD_FLAG = 1;
- switch((pt)->lc) {//用switch來(lái)選擇運(yùn)行點(diǎn)
- case 0: //此乃初始運(yùn)行點(diǎn),線程正常退出或剛開始都從這開始運(yùn)行
- while(1) //每個(gè)線程都不會(huì)死
- {
- do {
- (pt)->lc=12;//記錄運(yùn)行點(diǎn)
- case 12:
- if(!(counter1==1))
- {
- return PT_WAITING; //return 0
- }
- } while(0)
- digitalWrite(12,state1);
- state1=!state1;//燈狀態(tài)反轉(zhuǎn)
- counter1=0; //計(jì)數(shù)器置零
- }
- }
- PT_YIELD_FLAG = 0;
- pt->lc=0;
- return PT_ENDED; // return 1
- }
- }
- static int protothread2(struct pt *pt) //線程2,控制燈2
- {
- { char PT_YIELD_FLAG = 1;
- switch((pt)->lc) {//用switch來(lái)選擇運(yùn)行點(diǎn)
- case 0: //線程開始
- while(1) //每個(gè)線程都不會(huì)死
- {
- do {
- (pt)->lc=39;
- case 39://記錄運(yùn)行點(diǎn)
- if(!(counter2==5))
- {
- return PT_WAITING; //return 0
- }
- } while(0)
- counter2=0; //計(jì)數(shù)清零
- digitalWrite(13,state2);
- state2=!state2; //燈狀態(tài)反轉(zhuǎn)
- }
- }
- PT_YIELD_FLAG = 0;
- pt->lc=0;
- return PT_ENDED; // return 1
- }
- }
- static struct pt pt1, pt2;
- void setup()
- {
- pinMode(12,OUTPUT);
- pinMode(13,OUTPUT);
- pt1->lc=0; //線程1初始化
- pt2->lc=0; //線程2初始化
- }
- void loop () //這就是進(jìn)行線程調(diào)度的地方
- {
- protothread1(&pt1); //執(zhí)行線程1
- protothread2(&pt2); //執(zhí)行線程2
- delay(1000); //時(shí)間片,每片1秒,可根據(jù)具體應(yīng)用設(shè)置大小
- counter1++;
- counter2++;
- }
復(fù)制代碼
好了,終于擴(kuò)展完了。。
分析一下上面的代碼,就知道其實(shí)ProtoThreads是利用switch case 來(lái)選擇運(yùn)行點(diǎn)的,每個(gè)線程中的堵塞,其實(shí)就是判斷條件是否成立,不成立則return,所以說(shuō)每個(gè)線程都很有雷鋒精神,舍己為人,呵呵。有一點(diǎn)要注意那就是每個(gè)線程只能夠在我們指定的地方堵塞,至于堵塞點(diǎn),那就要看具體應(yīng)用了。
由于線程是反復(fù)被調(diào)用的,因此,寫程序的時(shí)候不能像寫一般的函數(shù)一樣使用局部變量,因?yàn)槊看沃匦抡{(diào)用都會(huì)把變量初始化了,如果要保持變量,可以把它定義為static的
在pt.h中定義了很多功能:
PT_INIT(pt) 初始化任務(wù)變量,只在初始化函數(shù)中執(zhí)行一次就行
PT_BEGIN(pt) 啟動(dòng)任務(wù)處理,放在函數(shù)開始處
PT_END(pt) 結(jié)束任務(wù),放在函數(shù)的最后
PT_WAIT_UNTIL(pt, condition) 等待某個(gè)條件(條件可以為時(shí)鐘或其它變量,IO等)成立,否則直接退出本函數(shù),下一次進(jìn)入本 函數(shù)就直接跳到這個(gè)地方判斷
PT_WAIT_WHILE(pt, condition) 和上面一個(gè)一樣,只是條件取反了
PT_WAIT_THREAD(pt, thread) 等待一個(gè)子任務(wù)執(zhí)行完成
PT_SPAWN(pt, child, thread) 新建一個(gè)子任務(wù),并等待其執(zhí)行完退出
PT_RESTART(pt) 重新啟動(dòng)某個(gè)任務(wù)執(zhí)行
PT_EXIT(pt) 任務(wù)后面的部分不執(zhí)行,直接退出重新執(zhí)行
PT_YIELD(pt) 鎖死任務(wù)
PT_YIELD_UNTIL(pt, cond) 鎖死任務(wù)并在等待條件成立,恢復(fù)執(zhí)行
在pt中一共定義四種線程狀態(tài),在任務(wù)函數(shù)退出到上一級(jí)函數(shù)時(shí)返回其狀態(tài)
PT_WAITING 等待
PT_EXITED 退出
PT_ENDED 結(jié)束
PT_YIELDED 鎖死
比如PT_WAIT_UNTIL(pt, condition) ,通過(guò)改變condition可以運(yùn)用的非常靈活,如結(jié)合定時(shí)器的庫(kù),把condition改為定時(shí)器溢出,那就是個(gè)時(shí)間觸發(fā)系統(tǒng)了,再把condition改為其他條件,就是事件觸發(fā)系統(tǒng)了
暫時(shí)寫這么多吧
***2014.3.16 這里有一另一篇關(guān)于protothread的文章,寫的挺好,感謝網(wǎng)友vincent_jie 的推薦:
http://www.kankanews.com/ICkengine/archives/107079.shtml
Devuino代表在 Arduino基礎(chǔ)上的開發(fā)平臺(tái),Muti代表多線程

ProtoThread相比其他RTOS有什么突出的優(yōu)點(diǎn)呢?貌似它已經(jīng)停止更新了。
優(yōu)點(diǎn)挺多的
1、超輕量級(jí),他的庫(kù)基本上都是些宏定義,大小可以忽略不計(jì),而且每個(gè)線程只占用2個(gè)字節(jié)
2、無(wú)機(jī)器碼,純c實(shí)現(xiàn),所以可移植性很好
3、無(wú)堆棧
4、簡(jiǎn)單使用可以只把他當(dāng)個(gè)調(diào)度程序,復(fù)雜點(diǎn)也能把他當(dāng)操作系統(tǒng)來(lái)使
恩。。其實(shí)我也才接觸。。了解不全
現(xiàn)在邊研究邊更新吧,主要是這幾天太忙了。。
PT作者的首頁(yè)http://www.sics.se/~adam/pt/
問(wèn):新手,求教一下,看了下示例,不知這定時(shí)準(zhǔn)確度如何?執(zhí)行中斷后,這定時(shí)器時(shí)間是否會(huì)延長(zhǎng)?
答:上面的示例只是用了一個(gè)delay()來(lái)設(shè)定時(shí)間片,用來(lái)演示一個(gè)簡(jiǎn)單的程序罷了
寫程序最好不要用delay,一delay程序就不能干別的事了,最好的辦法是一遇到要delay就去干別的事,時(shí)間到了再接著運(yùn)行剛才的代碼,所以PT結(jié)合定時(shí)器可以很大的提高程序的實(shí)時(shí)性
上面的定時(shí)器庫(kù)采用的是內(nèi)部定時(shí)器,至于是TCN幾我也沒(méi)仔細(xì)看,不過(guò)他們與PT結(jié)合的不是很好,下次我再發(fā)一個(gè)定時(shí)器庫(kù)吧
中斷肯定會(huì)有影響的,只是影響的大小罷了,所以在中斷里一般都不會(huì)放大的程序,我用中斷一般都是用來(lái)計(jì)數(shù)、改變狀態(tài),這點(diǎn)時(shí)間可以忽略
附錄:
官方原版庫(kù)1.4
pt-1.4.zip
(256.5 KB, 下載次數(shù): 50)
2016-4-9 20:05 上傳
點(diǎn)擊文件名下載附件
arduino版1.4.01
ProtoThreads.rar
(12.31 KB, 下載次數(shù): 56)
2016-4-9 20:05 上傳
點(diǎn)擊文件名下載附件
新發(fā)現(xiàn)了幾個(gè)定時(shí)器庫(kù),還沒(méi)來(lái)得及試用,都是arduino Library上下載的,大家可以先拿過(guò)去試試
SimpleTimer.rar
(4.27 KB, 下載次數(shù): 18)
2016-4-9 20:05 上傳
點(diǎn)擊文件名下載附件
TimedAction-1_6.zip
(3.92 KB, 下載次數(shù): 20)
2016-4-9 20:05 上傳
點(diǎn)擊文件名下載附件
TimerOne-v9.zip
(5.71 KB, 下載次數(shù): 18)
2016-4-9 20:05 上傳
點(diǎn)擊文件名下載附件
|
評(píng)分
-
查看全部評(píng)分
|