<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > 中斷多任務(wù)+狀態(tài)機 單片機軟件結構設計

中斷多任務(wù)+狀態(tài)機 單片機軟件結構設計

作者: 時(shí)間:2016-11-27 來(lái)源:網(wǎng)絡(luò ) 收藏
mcu由于內部資源的限制,軟件設計有其特殊性,程序一般沒(méi)有復雜的算法以及數據結構,代碼量也不大,通常不會(huì )使用OS (Operating System),因為對于一個(gè)只有若干K ROM,一百多byte RAM的mcu來(lái)說(shuō),一個(gè)簡(jiǎn)單OS也會(huì )吃掉大部分的資源。

本文引用地址:http://dyxdggzs.com/article/201611/322212.htm

對于無(wú)os的系統,流行的設計是主程序(主循環(huán)) +(定時(shí))中斷,這種結構雖然符合自然想法,不過(guò)卻有很多不利之處,首先是中斷可以在主程序的任何地方發(fā)生,隨意打斷主程序。其次主程序與中斷之間的耦合性(關(guān)聯(lián)度)較大,這種做法使得主程序與中斷纏繞在一起,必須仔細處理以防不測。

那么換一種思路,如果把主程序全部放入(定時(shí))中斷中會(huì )怎么樣?這么做至少可以立即看到幾個(gè)好處:系統可以處于低功耗的休眠狀態(tài),將由中斷喚醒進(jìn)入主程序;如果程序跑飛,則中斷可以拉回;沒(méi)有了主從之分(其他中斷另計),程序易于模塊化。

(題外話(huà):這種方法就不會(huì )有何處喂狗的說(shuō)法,也沒(méi)有中斷是否應該盡可能的簡(jiǎn)短的爭論了)

為了把主程序全部放入(定時(shí))中斷中,必須把程序化分成一個(gè)個(gè)的模塊,即任務(wù),每個(gè)任務(wù)完成一個(gè)特定的功能,例如掃描鍵盤(pán)并檢測按鍵。設定一個(gè)合理的時(shí)基(tick),例如5, 10或20 ms,每次定時(shí)中斷,把所有任務(wù)執行一遍,為減少復雜性,一般不做動(dòng)態(tài)調度(最多使用固定數組以簡(jiǎn)化設計,做動(dòng)態(tài)調度就接近os了),這實(shí)際上是一種無(wú)優(yōu)先級時(shí)間片輪循的變種。來(lái)看看主程序的構成:

void main()

{

….// Initialize

while (true) {

IDLE;//sleep

}

}

這里的IDLE是一條sleep指令,讓mcu進(jìn)入低功耗模式。中斷程序的構成

void Timer_Interrupt()

{

SetTimer();

ResetStack();

Enable_Timer_Interrupt;

….

進(jìn)入中斷后,首先重置Timer,這主要針對8051, 8051自動(dòng)重裝分頻器只有8-bit,難以做到長(cháng)時(shí)間定時(shí);復位stack,即把stack指針賦值為棧頂或棧底(對于pic,TI DSP等使用循環(huán)棧的mcu來(lái)說(shuō),則無(wú)此必要),用以表示與過(guò)去決裂,而且不準備返回到中斷點(diǎn),保證不會(huì )保留程序在跑飛時(shí)stack中的遺體。Enable_Timer_Interrupt也主要是針對8051。8051由于中斷控制較弱,只有兩級中斷優(yōu)先級,而且使用了如果中斷程序不用reti返回,則不能響應同級中斷這種偷懶方法,所以對于8051,必須調用一次reti來(lái)開(kāi)放中斷:

_Enable_Timer_Interrupt:

acall_reti

_reti:reti

下面就是任務(wù)的執行了,這里有幾種方法。第一種是采用固定順序,由于mcu程序復雜度不高,多數情況下可以采用這種方法:

Enable_Timer_Interrupt;

ProcessKey();

RunTask2();

RunTaskN();

while (1) IDLE;

可以看到中斷把所有任務(wù)調用一遍,至于任務(wù)是否需要運行,由程序員自己控制。另一種做法是通過(guò)函數指針數組:

#define CountOfArray(x) (sizeof(x)/sizeof(x[0]))

typedef void (*FUNCTIONPTR)();

const FUNCTIONPTR[] tasks = {

ProcessKey,

RunTask2,

RunTaskN

};

void Timer_Interrupt()

{

SetTimer();

ResetStack();

Enable_Timer_Interrupt;

for (i=0; i

(*tasks[i])();

while (1) IDLE;

}

使用const是讓數組內容位于code segment(ROM)而非data segment (RAM)中,8051中使用code作為const的替代品。

(題外話(huà):關(guān)于函數指針賦值時(shí)是否需要取地址操作符&的問(wèn)題,與數組名一樣,取決于compiler.對于熟悉匯編的人來(lái)說(shuō),函數名和數組名都是常數地址,無(wú)需也不能取地址。對于不熟悉匯編的人來(lái)說(shuō),用&取地址是理所當然的事情。Visual C++ 2005對此兩者都支持)

這種方法在匯編下表現為散轉,一個(gè)小技巧是利用stack獲取跳轉表入口:

movA, state

acallMultiJump

ajmpstate0

ajmpstate1

...

MultiJump:popDPH

popDPL

rlA

jmp@A+DPTR

還有一種方法是把函數指針數組(動(dòng)態(tài)數組,鏈表更好,不過(guò)在mcu中不適用)放在data segment中,便于修改函數指針以運行不同的任務(wù),這已經(jīng)接近于動(dòng)態(tài)調度了:

FUNCTIONPTR[COUNTOFTASKS] tasks;

tasks[0] = ProcessKey;

tasks[0] = RunTaskM;

tasks[0] = NULL;

...

FUNCTIONPTR pFunc;

for (i=0; i< COUNTOFTASKS; i++){

pFunc = tasks[i]);

if (pFunc != NULL)

(*pFunc)();

}

通過(guò)上面的手段,一個(gè)中斷驅動(dòng)的框架形成了,下面的事情就是保證每個(gè)tick內所有任務(wù)的運行時(shí)間總和不能超過(guò)一個(gè)tick的時(shí)間。為了做到這一點(diǎn),必須把每個(gè)任務(wù)切分成一個(gè)個(gè)的時(shí)間片,每個(gè)tick內運行一片。這里引入了狀態(tài)機(state machine)來(lái)實(shí)現切分。關(guān)于state machine,很多書(shū)中都有介紹,這里就不多說(shuō)了。

(題外話(huà):實(shí)踐升華出理論,理論再作用于實(shí)踐。我很長(cháng)時(shí)間不知道我一直沿用的方法就是state machine,直到學(xué)習UML/C++,書(shū)中介紹tachniques for identifying dynamic behvior,方才豁然開(kāi)朗。功夫在詩(shī)外,掌握C++,甚至C# JAVA,對理解嵌入式程序設計,會(huì )有莫大的幫助)

狀態(tài)機的程序實(shí)現相當簡(jiǎn)單,第一種方法是用swich-case實(shí)現:

void RunTaskN()

{

switch (state) {

case 0: state0(); break;

case 1: state1(); break;

case M: stateM(); break;

default:

state = 0;

}

}

另一種方法還是用更通用簡(jiǎn)潔的函數指針數組:

const FUNCTIONPTR[] states = { state0, state1, …, stateM };

void RunTaskN()

{

(*states[state])();

}

下面是state machine控制的例子:

void state0() { }

void state1() { state++; }//next state;

void state2() { state+=2; }//go to state 4;

void state3() { state--; }//go to previous state;

void state4() { delay = 100; state++; }

void state5() { delay--; if (delay <= 0) state++; }//delay 100*tick

void state6() { state=0; }//go to the first state

一個(gè)小技巧是把第一個(gè)狀態(tài)state0設置為空狀態(tài),即:

void state0() { }

這樣,state =0可以讓整個(gè)task停止運行,如果需要投入運行,簡(jiǎn)單的讓state = 1即可。


上一頁(yè) 1 2 下一頁(yè)

評論


技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>