利用 ProtoThreads實現Arduino多線程處理(2)


轉載請注明:@小五義http://www.cnblogs.com/xiaowuyi  QQ群:64770604

感謝小V分享給大家的博文。

       我在做產品設計的課題的時候,小五義推薦我使用Protothread這個庫來進行編寫,研究了之后應用於自己的設計上效果還不錯,應小五義的請寫了這個Protothread的介紹,談不上懂,就淺淺談一談我的理解,幫助大家應用,如果有錯誤的,歡迎指教。

     Protothread(以下簡稱PT),是牛逼哄哄的來自瑞典皇家啥啥啥的Adam Dunkels大師編寫的用於協程的一個庫,你說協程是啥,簡單的理解就是類似多任務處理器。當你開始做有關硬件C語言的編程之后,你會發現你的核心芯片不是無敵的,資源是有限的,這個時候合理安排資源就極為重要了,對於8位16位的單片機來說,用真正的多任務處理幾乎是無法承受的,這種時候就需要PT的出馬了。

        舉個例子,你有兩個觸發端,分別是你老媽和你老婆,當你老婆生氣的時候,你要跪鍵盤10秒,你老媽叫你的時候,你就要去給她捶背15秒,問題是你的腦子只有8位或者16位,一個時間點只能做一件事情。現在你老婆生氣了,你就跪在鍵盤上,用最基礎的delay函數數數:1秒,2秒,3秒……這時候你老媽叫你給她捶背,可是你聽不見,因為你在數數,如果你老媽耐心好還好,等你跪完再叫你給她捶背,如果她老人家耐心差,叫一遍就不叫了,那可就完蛋了,直接跟你老婆開干,當然你唯一收獲的就是不用跪了,准備完蛋吧。

        PT的有一個能力就是你跪下之后,不用你數數,它幫你數數,然后你膝蓋就保持跪的狀態,然后雙手給你老媽捶背,10秒到后,它跟你講:“誒,氣管炎,10秒到了。”這個時候你想不想起來看你自己了,反正老婆給的任務完成了。

        當然這只是一個很狹窄的例子,不要狹隘的理解為PT只是給你計數,timer只是PT的一個方便的應用,這是使PT提醒的原因不是計時多久,而是一個條件,條件是數完十秒,所以說PT很靈活,你可以給把觸發端給PT,比如你為了向老婆展示你的誠心,就一直跪着鍵盤,這時候不是叫PT數數,告訴PT,如果老媽叫你,就提醒你去敲背,這樣都是可以的。

        原理大致如此,然后上幾個常用的宏定義吧:

PT_INIT(pt)   初始化任務變量,只在初始化函數中執行一次就行 
PT_BEGIN(pt)  啟動任務處理,放在函數開始處 
PT_END(pt)   結束任務,放在函數的最后 
PT_WAIT_UNTIL(pt, condition) 等待某個條件(條件可以為時鍾或其它變量,IO等)成立,否則直接退出本函數,下一次進入本函數就直接跳到這個地方判斷 

        想要真正的理解PT,自然是需要看懂源代碼的,我不會那么無聊給你一個一個羅列,我就給你看幾個精髓的,

#define PT_BEGIN(pt) switch((pt)->lc) {case 0;
#define PT_WAIT_UNTIL(pt,condition)   (pt)->lc = __LINE__;   case __LINE__:                                       if(!(condition))  return  

        啊哈哈哈哈,是不是沒看懂!!!我一開始也是這樣的!!!如果你看懂了你現在真的可以離開了,我沒啥可以告訴你的了。。。

     如果你一頭霧水,心生怯意,那我來給你打一級強心針吧!

#define PT_END(pt) }

     怎么樣!是不是不敢相信!就一個“}”,所以那些大神跟你講你PT_BEGIN之后,一定要一個PT_END,知道為啥了吧!因為你看看PT_BEGIN,里面一開始有個“{”。

     其實你懂了PT_WAIT_UNTIL之后,你對PT就算是基本的了解,我們一句一句來,首先pt是啥,pt就是一個struct變量,里面有什么呢?里面就一個lc,lc就是用來記住當前的位置,然后 下次進入函數的時候就直接從當前位置開始的,那又怎么做到從當前位置開始呢!這個時候就是展現Adam牛逼哄哄的時候了,_LINE_閃亮登場,_LINE_的作用就是能取到當前_LINE_所在位置的行數,現在你知道了當前的位置,你怎么到這個位置呢,就用的switch case語句,用static pt記錄_LINE_的行數,然后通過switch((pt)->lc)跳到case _LINE_,怎么樣,理解了之后是不是感嘆Adam機智!

     最后就簡單的我下面列一個示例吧,是按照arduino的編譯器格式寫的,就按照上面說的老媽老婆的例子,我個人不敢給老媽老婆分高下,一個管我一生,一個管我下半身,所以就老婆叫跪就跪10秒,不然不跪,老媽叫敲背就敲背15秒,否則浪費。

#include<pt.h>//聲明protothread
#include<PT_timer.h>//聲明pt.timer
#define ANGEROFWIFE 1//老婆的憤怒
#define MOTHERCALL 2//老母的呼喚
#define KNEE 3//遭殃的膝蓋
#define HAND 4//幸福的雙手
static struct pt pt1,pt2;
PT_timer servotimer1;
PT_timer servotimer2;//定義兩個計時器
void setup()
{
    pinMode(ANGEROFWIFE,INPUT);
    pinMode(MOTHERCALL,INPUT);
    pinMode(KNEE,OUTPUT);
    pinMode(HAND,OUTPUT);//定義端口輸入輸出
    PT_INIT(&pt1);
    PT_INIT(&pt2);//兩個線程初始化,其實就是pt->lc歸零
}
static int mission1(struct pt *pt)
{
    PT_BEGIN(pt);//線程開始
    while(1)
    {
        digitalWrite(KNEE,LOW);//老婆不生氣我不跪
        PT_WAIT_UNTIL(pt,digitalRead(ANGEROFWIFE));//等老婆生氣
        digitalWrite(KNEE,HIGH);//我擦真生氣了,趕緊跪
        servotimer1.setTimer(10000);//設定跪10秒,這里單位1為一微妙
        PT_WAIT_UNTIL(pt,(servotimer1.Expired()));//當時間溢出,這里是10秒就結束
}
PT_END(pt);//線程結束
}
Static int mission2(struct pt *pt)
{
    PT_BEGIN(pt);
    While(1)
{
    digitalWrite(HAND,LOW);
    PT_WAIT_UNTIL(pt,digitalRead(MOTHERCALL));
    digitalWrite(HAND,HIGH);
    servotiemr2.setTimer(15000);
    PT_WAIT_UNTIL(pt,(servotimer2.Expired()));
}
    PT_END(pt);
}

void loop()
{
    mission1(&pt1);
    mission2(&pt2);//循環執行,特步,永不停息
}

        上述只是關於PT一個很簡單的例子,有一點需要提醒的是,盡量不要在線程里聲明變量,如果要用也要用static變量,不然很容易出錯也不易察覺,更多的關於protothread詳細的源代碼和理解可以參照REFERENCE,TV領進門,修行在個人。多多應用,勇於嘗試,你就能熟練應用這一神器。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM