1、傳統(tǒng)編程結(jié)構(gòu)的局限性 當不使用RTOS時, 嵌入式軟件通常采用兩種傳統(tǒng)的編程結(jié)構(gòu)進行編程,一種叫“前后臺結(jié)構(gòu)”或者叫“超級循環(huán)結(jié)構(gòu)”,本質(zhì)上是事件觸發(fā)的編程方式,另一種叫時間觸發(fā)的編程方式,Michael J.Pont 的“基于時間觸發(fā)的編程模式”即屬于此。
在實際工作中,當系統(tǒng)稍微復雜時,會發(fā)現(xiàn)這兩種方式都有一定局限性,下面以一個實際產(chǎn)品設(shè)計中遇到的問題為例來說明。
在設(shè)計一個用于配電柜的壁裝式智能配電儀表時,CPU的程序設(shè)計需完成以下任務(wù): (1)每半秒對前顯示屏的顯示數(shù)據(jù)進行一次刷行。 (2)每0.1秒對DI/DO進行一次刷新。 (3)每0.2秒對鍵盤進行一次掃描。 (4)每半秒對測量數(shù)據(jù)進行一次重新采集和計算。 (5)異步串行口與上位機使用Modbus通信,速率最高19200bps。 (6)CPU通過IIC 總線與時鐘芯片和EEprom通信。 (7)CPU通過SPI總線與 LED數(shù)碼管及采集芯片通信。 (8)CPU要對所采集的6路信號進行FFT變換。 (9)當系統(tǒng)掉電時,CPU要能快速響應(yīng)以把當前的電度底數(shù)寫入EEprom中。
上述任務(wù)中,任務(wù)(5)和任務(wù)(9)是強實時性的,如果對串口的收發(fā)事件得不到及時響應(yīng),接收時會導致字節(jié)丟失,發(fā)送時會導致字節(jié)間時間間隔太大造成接收方的Modbus幀定界錯誤,對系統(tǒng)掉電事件如果不能及時響應(yīng)會造成EEprom的寫入失敗。其它任務(wù)只要在指定的周期內(nèi)能得到執(zhí)行就行,但是任務(wù)(8)比較特殊,使用通常的8位CPU進行6種信號的FFT變換,哪怕每種信號只做128點的FFT,運算一次也要好幾秒。下面來看用傳統(tǒng)編程結(jié)構(gòu)實現(xiàn)上述設(shè)計時遇到的困擾。
1.1、使用“前后臺方式”進行編程 使用“前后臺方式”進行編程時,為保證任務(wù)(5)的及時性,使用了UART中斷,當UART完成一個字節(jié)的收發(fā)后產(chǎn)生中斷,在中斷程序中將接收到的字符保存在接收緩沖區(qū)或從發(fā)送緩沖區(qū)取下一個待發(fā)字符裝入UART進行發(fā)送,對Modbus協(xié)議的處理可以單獨用一個任務(wù)在中斷外處理,這保證了中斷程序的簡短。為保證任務(wù)(9)響應(yīng)的及時性,也必須為它安排一個中斷。因為當系統(tǒng)掉電時,系統(tǒng)只有不到10ms的過渡時間,系統(tǒng)如果不能在這個時間內(nèi)完成相關(guān)的操作,系統(tǒng) 電壓將跌落至有效電壓以下而喪失工作能力。
安排好了后臺的中斷任務(wù)后再來看看前臺的任務(wù)如何完成。這里遇到的最大的挑戰(zhàn)是對任務(wù)(8)的處理,因為任務(wù)(8)需要的執(zhí)行時間太長了,簡單的把它當成一個任務(wù)處理將影響系統(tǒng)對其它任務(wù)的響應(yīng),在超級循環(huán)中的代碼結(jié)構(gòu)如下: while(1) { 任務(wù)(1); 任務(wù)(2); ……… 任務(wù)(8); } 由于任務(wù)(8)執(zhí)行一次要幾秒鐘的時間,整個超級循環(huán)執(zhí)行一次至少大于任務(wù)(8)需要的時間,也就是說這個超級循環(huán)循環(huán)一次要幾秒鐘時間,將滿足不了各任務(wù)響應(yīng)時間的要求。
要解決這個問題,只有把任務(wù)(8)拆分成很多個子任務(wù),將每個子任務(wù)的耗時壓縮到10個毫秒左右,并定義好各子任務(wù)完成后的狀態(tài),在超級大循環(huán)中每次根據(jù)狀態(tài)只執(zhí)行一個子任務(wù),程序結(jié)構(gòu)如下 while(1) { 任務(wù)(1); 任務(wù)(2); ……… Switch (子任務(wù)狀態(tài)) { case 子任務(wù)狀態(tài)1: 子任務(wù)1; break; case 子任務(wù)狀態(tài)2: 子任務(wù)2; break; ………… case 子任務(wù)狀態(tài)n: 子任務(wù)n; break; } } 這樣,就需要把一個耗時幾秒的FFT運算任務(wù)拆分成幾百個耗時10ms左右的子任務(wù),這顯然是不可接受的。
除此之外,超級大循環(huán)結(jié)構(gòu)隱含地一個缺點就是隨著任務(wù)的增加,循環(huán)體的執(zhí)行時間是線性增加的,在實際設(shè)計中即使沒有象任務(wù)(8)那樣的高耗時任務(wù),當系統(tǒng)功能增加時要保證系統(tǒng)響應(yīng)的及時性也是一個不小的挑戰(zhàn)。
1.1、使用“時間觸發(fā)編程模式”進行編程 “時間觸發(fā)編程模式”的核心是建立一個基于時間觸發(fā)的合作式的任務(wù)調(diào)度器,在系統(tǒng)中盡量減少事件觸發(fā)(減少中斷的使用),系統(tǒng)通過任務(wù)調(diào)度器完成各任務(wù)的調(diào)度執(zhí)行,下面是“時間觸發(fā)編程模式”的典型程序結(jié)構(gòu): /*--------------------主函數(shù)-----------------------*/
Void main(void) { SCH_Init();//設(shè)置調(diào)度器 SCH_Add_Task(任務(wù)函數(shù)名,任務(wù)調(diào)度延遲,任務(wù)調(diào)度周期);//將任務(wù)加入調(diào)度器的任務(wù)隊列 SCH_Start();//刷新任務(wù)隊列
while(1) { SCH_Dispatch_Tasks(); //執(zhí)行任務(wù)調(diào)度器 } } /*-------------------定時中斷函數(shù)---------------------*/ Void SCH_Update(void) interrupt { //刷新任務(wù)隊列 } 系統(tǒng)中每個任務(wù)都定義了優(yōu)先級、任務(wù)循環(huán)周期和任務(wù)延遲時間,系統(tǒng)時器中斷程序SCH_Update()按設(shè)定的節(jié)拍對任務(wù)隊列進行刷新,在超級大循環(huán)中只執(zhí)行任務(wù)調(diào)度器SCH_Dispatch_Tasks(),根據(jù)任務(wù)隊列的狀態(tài)安排任務(wù)的執(zhí)行。
這種編程結(jié)構(gòu)避免了超級大循環(huán)結(jié)構(gòu)循環(huán)時間隨代碼量的增加而線性增加的問題,但是,由于任務(wù)是不可剝奪的,一旦任務(wù)啟動執(zhí)行,任務(wù)調(diào)度器只有在當前任務(wù)完成后才有機會執(zhí)行,這就要求每個任務(wù)占用CPU的時間不能太長,否則將影響整個系統(tǒng)的響應(yīng)速度。所以,F(xiàn)FT運算在這種編程模式下還是必須進行有效的拆分,否則就必須提高CPU的檔次或使用可剝奪型的搶先式RTOS,這勢必造成系統(tǒng)成本的增加。那么有沒有更好的解決辦法呢?
下面的編程結(jié)構(gòu)對“時間觸發(fā)編程模式”進行了改進,使之在不提高硬件成本的情況下,使編程人員更直觀地定義任務(wù),減少任務(wù)特性對系統(tǒng)程序結(jié)構(gòu)的沖擊,使程序結(jié)構(gòu)簡單明了并提高系統(tǒng)的實時響應(yīng)速度。
2、對“時間觸發(fā)編程模式”的改進 根據(jù)多年嵌入式系統(tǒng)編程的經(jīng)驗,通常嵌入系統(tǒng)的任務(wù)可以劃分成3種類型: (1)及時型任務(wù); (2)周期型任務(wù); (3)背景型任務(wù);
及時型任務(wù)的特點:這類任務(wù)是事件觸發(fā)型的,一旦事件發(fā)生,系統(tǒng)必須在限定的時間內(nèi)進行響應(yīng),對這類任務(wù),最自然的方法就是使用中斷來完成,即定義成“前后臺方式”中的后臺任務(wù)。
周期型任務(wù)的特點:這類任務(wù)是時間觸發(fā)式周期型的,系統(tǒng)必須保證在指定的周期內(nèi)執(zhí)行任務(wù),“時間觸發(fā)編程模式”可以很好地滿足這類任務(wù)的需求。
背景型任務(wù)的特點:這類任務(wù)是非實時型的,實時性不是非常重要,系統(tǒng)在運行過程中可隨時中斷這類任務(wù)以便執(zhí)行前兩類任務(wù),系統(tǒng)只要能充分利用資源盡最大可能快速完成這類任務(wù)即可,這類任務(wù)最適合定義成“前后臺方式”中的前臺任務(wù)。
根據(jù)以上任務(wù)分類,對“時間觸發(fā)編程模式”的改進可概括成以下需求: (1)任務(wù)分3類,1類任務(wù)優(yōu)先級最高,3類任務(wù)優(yōu)先級最低; (2)高優(yōu)先級的任務(wù)可中斷低優(yōu)先級任務(wù)的執(zhí)行,同級的任務(wù)之間不可相互剝奪。 (3)實際設(shè)計中為提高系統(tǒng)的可預測性,應(yīng)盡量減少1類任務(wù)的數(shù)量及1類任務(wù)的執(zhí)行時間。 (4)為降低系統(tǒng)資源的占用,系統(tǒng)不給任務(wù)劃分單獨的堆棧空間。
以上改進的本質(zhì)是設(shè)計一個3優(yōu)先級的簡單的任務(wù)調(diào)度機制,高優(yōu)先級的任務(wù)可中斷低優(yōu)先級的任務(wù),同優(yōu)先級的任務(wù)之間不能相互剝奪,該調(diào)度機制不為每個單獨的任務(wù)保存任務(wù)上下文和單獨的堆棧,這樣可以減少該編程模式對系統(tǒng)資源的需求。
可剝奪式RTOS中的一個高優(yōu)先級任務(wù)中斷一個低優(yōu)先級的任務(wù)時,會保存好低優(yōu)先級任務(wù)的上下文并把該低優(yōu)先級任務(wù)的局部變量保存在本任務(wù)單獨的堆棧中,如果系統(tǒng)不給任務(wù)分配單獨的堆棧,如何保證高優(yōu)先級任務(wù)退出后低優(yōu)先級任務(wù)執(zhí)行環(huán)境的恢復呢?
對這個問題,可以借鑒中斷的處理機制用以下辦法予以解決: (1)在系統(tǒng)中設(shè)計一個定時中斷函數(shù),該函數(shù)的功能就是執(zhí)行周期性任務(wù)的調(diào)度,該定時中斷在所有中斷中優(yōu)先級最低; (2)在系統(tǒng)中設(shè)計另一個定時中斷函數(shù),該函數(shù)的功能是刷新周期型任務(wù)的任務(wù)管理隊列,為任務(wù)調(diào)度提供支持,本定時中斷函數(shù)的優(yōu)先級在系統(tǒng)中次低; (3)周期型任務(wù)就是一個函數(shù),該函數(shù)入口的第一個操作是開中斷(問:這個中斷指的是觸發(fā)及時性任務(wù)的中斷,那么在周期性任務(wù)外,是開還是關(guān)?如果是開,),允許任務(wù)執(zhí)行期間被中斷以便響應(yīng)及時型任務(wù)。 (4)背景型任務(wù)就是在主函數(shù)超級循環(huán)中執(zhí)行的代碼,該代碼可隨時被及時型和周期型任務(wù)中斷,當系統(tǒng)沒有及時型任務(wù)和周期型任務(wù)時才循環(huán)執(zhí)行背景型任務(wù)的代碼。
通過以上措施,“改進型時間觸發(fā)編程模式”的程序結(jié)構(gòu)如下: /*--------------------主函數(shù)-----------------------*/ Void main(void) { SCH_Init();//設(shè)置調(diào)度器 SCH_Add_Task(任務(wù)函數(shù)名,任務(wù)調(diào)度延遲,任務(wù)調(diào)度周期);//將任務(wù)加入調(diào)度器的任務(wù)隊列 SCH_Start();//刷新任務(wù)隊列
while(1) { 背景型任務(wù)1; ……… 背景型任務(wù)n; } } /*-------------------次低優(yōu)先級定時中斷函數(shù)---------------------*/ Void SCH_Update(void) interrupt { //刷新任務(wù)隊列 }
/*-------------------最低優(yōu)先級的定時中斷函數(shù)---------------------*/ Void SCH_Dispatch_Tasks(void) interrupt { //調(diào)度周期型任務(wù) }
/*-------------------周期型任務(wù)典型結(jié)構(gòu)---------------------*/ Void SCH_Cycle_Task1(void) { //開中斷 /*此函數(shù)中可以靠中斷觸發(fā)來執(zhí)行及時性任務(wù)*/ //執(zhí)行任務(wù) return;//任務(wù)返回 }
結(jié) 語 使用“改進型時間觸發(fā)編程模式”進行小型嵌入式系統(tǒng)編程,就像使用RTOS進行編程一樣,設(shè)計者規(guī)劃好任務(wù)后,就可以專心于每個任務(wù)的設(shè)計,任務(wù)對處理器時間的占用可以由系統(tǒng)統(tǒng)一管理,減少任務(wù)之間的耦合,使產(chǎn)品的程序設(shè)計和改動都變得簡潔清楚。使用該編程模式很好地解決了壁裝式智能配電儀表所面臨的復雜的設(shè)計問題,證明該方法簡單有效。
目前該設(shè)計模式僅僅設(shè)計了任務(wù)調(diào)度器,任務(wù)間的變量傳遞還需要使用全局變量,如果能加入信號量和消息機制,那么該模式將更加完善,會使低成本的小型嵌入式系統(tǒng)的編程更加方便和清晰。 本文轉(zhuǎn)自網(wǎng)絡(luò),版權(quán)歸原作者
|