新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 中斷多任務+狀態機 單片機軟件結構設計

        中斷多任務+狀態機 單片機軟件結構設計

        作者: 時間:2016-11-27 來源:網絡 收藏

        本文引用地址:http://www.104case.com/article/201611/322212.htm

        以下是一個鍵盤掃描的例子,這里假設tick = 20 ms, ScanKeyboard()函數控制口線的輸出掃描,并檢測輸入轉換為鍵碼,利用每個state之間20 ms的間隔去抖動。

        enum EnumKey {

        EnumKey_NoKey =0,

        };

        struct StructKey {

        intkeyValue;

        boolkeyPressed;

        } ;

        struct StructKeyProcess key;

        void ProcessKey() { (*states[state])(); }

        void state0() { }

        void state1() { key.keyPressed = false; state++; }

        void state2() { if (ScanKey() != EnumKey_NoKey) state++; }//next state if a key pressed

        void state3()

        {//debouncing state

        key.keyValue = ScanKey();

        if (key.keyValue == EnumKey_NoKey)

        state--;

        else {

        key.keyPressed = true;

        state++;

        }

        }

        void state4() {if (ScanKey() == EnumKey_NoKey) state++; }//next state if the key released

        void state5() {ScanKey() == EnumKey_NoKey? state = 1 : state--; }

        上面的鍵盤處理過程顯然比通常使用標志去抖的程序簡潔清晰,而且沒有軟件延時去抖的困擾。以此類推,各個任務都可以劃分成一個個的state,每個state實際上占用不多的處理時間。某些任務可以劃分成若干個子任務,每個子任務再劃分成若干個狀態。

        (題外話:對于常數類型,建議使用enum分類組織,避免使用大量#define定義常數)

        對于一些完全不能分割,必須獨占的任務來說,比如我以前一個低成本應用中紅外遙控器的軟件解碼任務,這時只能犧牲其他的任務了。兩種做法:一種是關閉中斷,完全的獨占;

        void RunTaskN()

        {

        Disable_Interrupt;

        Enable_Interrupt;

        }

        第二種,允許定時中斷發生,保證某些時基register得以更新;

        void Timer_Interrupt()

        {

        SetTimer();

        Enable_Timer_Interrupt;

        UpdateTimingRegisters();

        if (watchDogCounter = 0) {

        ResetStack();

        for (i=0; i

        (*tasks[i])();

        while (1) IDLE;

        }

        else

        watchDogCounter--;

        }

        只要watchDogCounter不為0,那么中斷正常返回到中斷點,繼續執行先前被中斷的任務,否則,復位stack,重新進行任務循環。這種狀況下,中斷處理過程極短,對獨占任務的影響也有限。

        中斷驅動多任務配合狀態機的使用,我相信這是mcu下無os系統較好的設計結構。對于絕大多數mcu程序設計來說,可以極大的減輕程序結構的安排,無需過多的考慮各個任務之間的時間安排,而且可以讓程序簡潔易懂。缺點是,程序員必須花費一定的時間考慮如何切分任務。

        下面是一段用C改寫的CD Player中檢測disc是否存在的偽代碼,用以展示這種結構的設計技巧,原源代碼為Z8 mcu匯編,基于Sony的DSP, Servo and RF處理芯片,通過送出命令字來控制主軸/滑板/聚焦/尋跡電機,并讀取狀態以及CD的sub Q碼。這個處理任務只是一個大任務下用state machine切開的一個二級子任務,tick = 20 ms。

        state1() { InitializeMotor(); state++; }

        state2() {

        if (innerSwitch != ON) {

        SendCommand(EnumCommand_SlidingMotorBackward);

        timeout = MILLISECOND(10000);

        state++;//滑板電機向內運動,直至觸及最內開關。

        }

        else

        state +=2;

        }

        state3() {

        if ((--timeout) == 0) {//note: some C compliers do not support (--timeout) ==

        SendCommand(EnumCommand_SlidingMotorStop)

        systemErrorCode = EnumErrorCode_InnerSwitch;

        state = 0;// 10 s超時錯誤,

        }

        else {

        if (innerSwitch == ON) {

        SendCommand(EnumCommand _SlidingMotorStop)

        timeout = MILLISECOND(200);// 200ms電機停止時間

        state++;

        }

        }

        }

        state4() { if ((--timeout) == 0) state++; }//等待電機完全停止

        state5() {

        SendCommand(EnumCommand_SlidingMotorForward);

        timeout = MILLISECOND(2000);

        state++;

        }//滑板電機向外運動,脫離inner switch

        state6() {

        if ((--timeout) == 0) {

        SendCommand(EnumCommand_SlidingMotorStop)

        systemErrorCode = EnumErrorCode_InnerSwitch;

        state = 0;// 2 s超時錯誤,

        }

        else {

        if (innerSwitch == OFF) {

        SendCommand(EnumCommand_SlidingMotorStop)

        timeout = MILLISECOND(200);// 200ms電機停止時間

        state++;

        }

        }

        }

        state7() { state4(); }

        state8() { LaserOn(); state++; retryCounter = 3;}//打開激光器

        state9() {

        SendCommand(FocusUp);

        state++;

        timeout = MILLISECOND(2000);

        }//光頭上舉,檢測聚焦過零3次,判斷cd是否存在

        state10() {

        if (FocusCrossZero){

        systemStatus.Disc = EnumStatus_DiscExist;

        SendCommand(EnumCommand_AutoFocusOn);//有cd,打開自動聚焦。

        state = 0;//本任務結束。

        playProcess.state = 1;//啟動play任務

        }

        else if ((--timeout) == 0) {

        SendCommand(EnumCommand_ FocusClose);//光頭聚焦復位

        if ((--retryCounter) == 0) {

        systemStatus.Disc = EnumStatus_Nodisc;//無盤

        displayProcess.state = EnumDisplayState_NoDisc;//顯示閃爍的無盤

        LaserOff();

        state = 0;//任務停止

        }

        else

        state--;//再試

        }

        }

        stateStop() {

        SendCommand(EnumCommand_SlidingMotorStop);

        SendCommand(EnumCommand_FocusClose);

        state = 0;

        }


        上一頁 1 2 下一頁

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 江华| 共和县| 桃江县| 甘德县| 丰顺县| 信阳市| 嘉黎县| 威海市| 华容县| 封开县| 织金县| 江安县| 巧家县| 达州市| 惠州市| 铜梁县| 宜川县| 安仁县| 白玉县| 万安县| 邯郸县| 紫阳县| 卢氏县| 汽车| 昭觉县| 阿拉善盟| 方山县| 盈江县| 肥乡县| 修文县| 德惠市| 南溪县| 邮箱| 无锡市| 益阳市| 宜君县| 岫岩| 长海县| 安远县| 天长市| 孟州市|