<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è) > 嵌入式系統 > 設計應用 > 基于51單片機的多任務(wù)機制及應用

基于51單片機的多任務(wù)機制及應用

作者: 時(shí)間:2010-11-18 來(lái)源:網(wǎng)絡(luò ) 收藏

1 引言

傳統的單片機程序一般采用單任務(wù)機制,單任務(wù)系統具有簡(jiǎn)單直觀(guān)、易于控制的優(yōu)點(diǎn)。然而由于程序只能按順序依次執行,缺乏靈活性,只能使用中斷函數實(shí)時(shí)地處理一些較短的任務(wù),在較復雜的應用中使用極為不便。嵌入式多任務(wù)操作系統的出現解決了這個(gè)問(wèn)題。在多任務(wù)系統中可以同時(shí)執行多個(gè)并行任務(wù),任務(wù)之間可以相互跳轉。但是嵌入式操作系統在提供強大功能的同時(shí),也帶來(lái)了代碼量大、結構復雜、對硬件要求較高、開(kāi)發(fā)難度大且成本高等問(wèn)題。而很多時(shí)候只需要實(shí)現簡(jiǎn)單的多任務(wù)操作就可以滿(mǎn)足實(shí)際需要,本文設計的這種簡(jiǎn)單的,在只增加極少量C語(yǔ)言代碼的前提下,不需使用匯編,無(wú)需對原本的程序進(jìn)行大改動(dòng),就可以實(shí)現多任務(wù)操作。

實(shí)時(shí)操作系統的核心是中斷,利用中斷進(jìn)行任務(wù)切換。在大部分如μC/OS-II中,每個(gè)任務(wù)都有自己的堆棧,用來(lái)保存任務(wù)的一些信息,任務(wù)之間通過(guò)信號量、郵箱、消息隊列等傳遞信息。在很多情況下并不需要這些功能,只需要使單片機在接收到控制信號后,切換到不同的工作狀態(tài),也就是只要進(jìn)行任務(wù)切換,不需要保存任務(wù)的相關(guān)信息。舍棄這些復雜的功能可以使程序結構變得簡(jiǎn)潔易用。

2 兩種機制在應用實(shí)例中的比較

下面用一個(gè)應用實(shí)例來(lái)說(shuō)明本設計的思路。要設計一個(gè)智能安防系統,它的功能包括:當有人入侵時(shí)執行報警工作;用戶(hù)可以通過(guò)鍵盤(pán)板進(jìn)行功能設置;主板能與管理中心進(jìn)行通訊,當發(fā)生火災、地震等災情時(shí),管理中心能通知用戶(hù)。其結構如圖1所示。平時(shí)狀態(tài)下,主板的CPU不斷地掃描各個(gè)傳感器的狀態(tài)。當檢測到傳感器的異常信號(有人闖入)時(shí),CPU進(jìn)入入侵報警狀態(tài),執行響警鈴、撥打戶(hù)主電話(huà)、通知管理中心等工作。當發(fā)生火災地震時(shí),管理中心發(fā)送一個(gè)串口代碼給主板CPU,使CPU進(jìn)入災難報警狀態(tài),執行響警鈴、語(yǔ)音報警等操作。用戶(hù)需要進(jìn)行功能設置時(shí)可以通過(guò)鍵盤(pán)板使主板CPU進(jìn)入功能設置狀態(tài)。因此主板的CPU有4種不同的工作狀態(tài)。



圖1 智能安防系統結構示意圖

如果采用單任務(wù)機制, 主板的程序流程如圖2所示。在主函數中循環(huán)檢測傳感器狀態(tài),如有異常則調用報警函數,災難報警和功能設置在串口中斷中完成。這種單任務(wù)結構有兩個(gè)缺點(diǎn)。首先,在各種非平時(shí)狀態(tài)中,程序需要不停地檢測是否收到撤除信號,這個(gè)要求在程序代碼量大、執行工作較多的情況下很難實(shí)現。其次,各狀態(tài)之間的切換十分困難,用C語(yǔ)言寫(xiě)的程序為求模塊化,一般函數數量較多,函數調用的嵌套層數也多,要從一個(gè)較深的嵌套立刻跳出到主函數,是非常困難的。一般的解決方法或是使用C51的庫函數setjmp()和longjmp()實(shí)現長(cháng)跳轉,但是這兩個(gè)函數在中斷函數內部是無(wú)能為力的;再或是在C函數中嵌入匯編指令。雖然用匯編指令可以實(shí)現程序的長(cháng)距離跳轉,但是這種方法的調試過(guò)程十分煩瑣,而且程序的可移植性差。對于習慣用C51編程而不想用匯編的設計者,該部分程序是一個(gè)難題。



圖2 單任務(wù)機制程序流程

3 實(shí)現的程序結構

本文提供了一種方法,可以在完全不使用匯編指令的前提下實(shí)現可移植性強的多任務(wù)程序,程序流程如圖3所示。

基于51單片機的多任務(wù)機制及應用
圖3 多任務(wù)結構程序流程

實(shí)現這個(gè)的完整源代碼如下:

word idata PC_Value, SP_Value;//儲存中斷返回點(diǎn)、SP初值的全局變量

byte idata Ctrl_Code; //控制任務(wù)切換的全局變量,在中斷函數里被賦值

void main()

{

Initial();  //初始化函數,與程序結構無(wú)關(guān)

SP_Value=SP;//獲?。樱械某跏贾?BR>
PC_Value=Get_Next_PC();//獲取下一條指令的地址

EA=1;//獲取PC、SP初值后再開(kāi)中斷保證穩定性

if(Ctrl_Code!=0)

SP=SP_Value;//重置堆棧指針,防止堆棧溢出

switch( Ctrl_Code)//任務(wù)入口地址,即中斷的返回點(diǎn)

{

case 1: goto TASK1;

case 2: goto TASK2;

case 3: goto TASK3;

default: break;

}

TASK1: for( ; ; )

 { //任務(wù)1代碼 }

TASK2: for( ; ; )

 { //任務(wù)2代碼 }

TASK3: for( ; ; )

 { //任務(wù)2代碼}

}

word Get_Next_PC(void);//獲取下一條指令的地址

{

?word address;

?address=*((unsigned char *)SP); //PC的高字節

?address = 8;

?address+=*((unsigned char *)(SP-1)); //PC的低字節

?return address+4; //查看反匯編代碼,計算所得

}

void Chuan_Kou_Interrupt(void) interrupt 4 using 0

{

byte a1,a2;

a1=a1*a2;

*((unsigned char *)(SP-5))=PC_Value>>8;

*((unsigned char *)(SP-6))=PC_Value 0x00ff;

{

//接收串口代碼并根據代碼修改Ctrl_Code的值

//其他操作

}

}

4 原理與實(shí)現

程序的整體思路是在主函數main中依次放置幾個(gè)死循環(huán)作為任務(wù)框架,即每個(gè)任務(wù)都是一個(gè)死循環(huán),利用中斷進(jìn)行任務(wù)切換。以剛才所說(shuō)的安防系統為例,由于主板、鍵盤(pán)、管理中心之間是通過(guò)串口通訊的,因此串口是用來(lái)觸發(fā)任務(wù)切換的理想中斷源。程序為所有任務(wù)設置一個(gè)總入口并放在主函數中,串口中斷每次返回時(shí)必須先經(jīng)過(guò)這個(gè)總入口,在總入口處檢查任務(wù)控制變量(全局變量)的值,任務(wù)控制變量已在串口中斷中被賦值,其值決定要切換到哪個(gè)任務(wù)。

設計中可以把平時(shí)狀態(tài)、入侵報警狀態(tài)、危機報警狀態(tài)、功能設置狀態(tài)分別作為任務(wù)1、任務(wù)2、任務(wù)3、任務(wù)4。主板CPU平常工作在平時(shí)狀態(tài),即任務(wù)1;當串口收到管理中心的危機代碼,在串口中斷函數中令Ctrl_Code = 3,中斷返回后會(huì )切換到任務(wù)3;同樣,接收到鍵盤(pán)的功能設置代碼后,會(huì )切換到任務(wù)4;由于入侵檢測是由主板CPU自己負責,因此如果檢測到有人入侵需要切換到入侵報警狀態(tài)時(shí),可以借由鍵盤(pán)中轉產(chǎn)生串口中斷,即向鍵盤(pán)發(fā)送一串口數據并要求鍵盤(pán)回送。這樣就實(shí)現了各個(gè)狀態(tài)的切換。

實(shí)現需要解決3個(gè)關(guān)鍵問(wèn)題:

① 獲取任務(wù)入口點(diǎn)的程序地址。由于使用C語(yǔ)言不能直接獲取和修改程序計數器PC的值,而在調用函數時(shí)會(huì )將PC值入棧,利用這個(gè)特點(diǎn)在任務(wù)入口處之前調用Get_Next_PC函數即可從堆棧中獲得入口地址。Get_Next_PC中,SP為堆棧指針,得到的PC值要加4才是任務(wù)入口地址,因為查看反匯編窗口可知,將函數返回值傳給全局變量PC_Value需要兩條2字節長(cháng)的mov指令。

② 修改中斷返回地址。修改中斷返回地址的操作與獲取PC值類(lèi)似,都是通過(guò)修改堆棧中的內容實(shí)現。但是由于編譯器自身的特點(diǎn),在進(jìn)入中斷時(shí),編譯器除了把返回地址入棧外,還會(huì )計算自身及它所調用的函數對寄存器ACC、 B、 DPH、 DPL、 PSW、 R0 ~ R7的改變,并將它認為被改變了的寄存器也入棧保護。如果堆棧結構會(huì )隨中斷函數內容改變而變化,就沒(méi)辦法計算中斷返回地址堆棧中的位置。解決方法是,在中斷函數定義時(shí)加上關(guān)鍵字using 0 告訴編譯器中斷函數及其調用的函數將使用寄存器組0,這樣工作寄存器R0~R7將不會(huì )被保存。ACC、PSW、DPH、DPL在對PC_Value操作時(shí)已經(jīng)用到,在中斷函數開(kāi)頭定義兩個(gè)變量a1、b1并令它們相乘,使B寄存器也被入棧,這樣堆棧的結構就是固定的了。

?、鄯乐苟褩R绯?。由于在調用函數時(shí)編譯器會(huì )將當前地址入棧,返回時(shí)再出棧,當任務(wù)切換即中斷多次發(fā)生在函數調用過(guò)程中時(shí),堆棧會(huì )因為只入不出而最終導致溢出。這是不能容許的。因此,應在主函數開(kāi)頭初始化后立刻將SP值保存,再在每次任務(wù)切換后都將SP恢復為初值,這可以有效防止堆棧溢出。

5 結語(yǔ)

根據以上的比較與分析可以看出這種實(shí)現多任務(wù)機制的方法具有如下優(yōu)點(diǎn):與采用單任務(wù)機制的程序相比,其結構簡(jiǎn)單清晰,易于控制;利用中斷和堆棧實(shí)現任務(wù)切換時(shí)的長(cháng)跳轉,完全不需使用匯編語(yǔ)言,可移植性強;增加的代碼量極小,實(shí)時(shí)性好,節省程序開(kāi)發(fā)時(shí)間。

以上介紹的方法已經(jīng)通過(guò)測試并應用于幾個(gè)實(shí)際項目中,包括智能小區安防系統、汽車(chē)CAN總線(xiàn)控制系統等,取得了良好效果。只要根據具體的硬件與編譯環(huán)境稍作修改,亦可應用于其他的單片機系統中。



評論


相關(guān)推薦

技術(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>