,,,,
(1.齊魯工業(yè)大學(xué)(山東省科學(xué)院),濟(jì)南 250353;2.山東省科學(xué)院自動化研究所;3.山東省汽車電子技術(shù)重點(diǎn)實(shí)驗室)
在嵌入式軟件設(shè)計中,時間是一個很重要的參數(shù),很多控制邏輯和協(xié)議都有時間約束,定時是實(shí)現(xiàn)這些控制邏輯和協(xié)議的關(guān)鍵手段[1]。在嵌入式系統(tǒng)中,一般而言,硬件定時器的數(shù)量少于定時應(yīng)用的數(shù)量,因此,不可能為每一個定時應(yīng)用單獨(dú)分配一個硬件定時器。在這種情況下,設(shè)計一種軟件定時器,使用單個硬件定時器模擬多個軟件定時器來滿足應(yīng)用中的定時需要,成為一種比較通用的做法。
參考文獻(xiàn)[2]提出了一種基于單個硬件定時器實(shí)現(xiàn)多個定時應(yīng)用的方法,根據(jù)定時值從小到大的順序維護(hù)定時項鏈表,每次加入一個新的定時項,都需要更新鏈表,而且需要在硬件定時器中斷處理程序中不斷更新硬件定時周期設(shè)置,這種方式需要考慮中斷處理程序執(zhí)行時間對各個定時項的影響。參考文獻(xiàn)[3]定義了全局軟件定時器數(shù)組,但是沒有描述軟件定時器的數(shù)據(jù)結(jié)構(gòu),并且只是針對單次定時,沒有考慮多次定時和循環(huán)定時,而且在硬件定時器中斷處理函數(shù)中對全局軟件定時器數(shù)組進(jìn)行操作容易引起中斷處理程序執(zhí)行時間過長,導(dǎo)致中斷嵌套,引發(fā)堆棧溢出等問題。參考文獻(xiàn)[4]提出了一種采用單個定時器管理進(jìn)程取代多個定時任務(wù)進(jìn)程的方式,解決了多個定時進(jìn)程消耗內(nèi)存資源的問題。但是它采用鏈表的形式管理定時任務(wù),鏈表以指針形式動態(tài)分配軟件定時器節(jié)點(diǎn)的內(nèi)存,在功能安全上不如靜態(tài)分配,而且加入一個新的定時器可能需要更新硬件定時器的中斷觸發(fā)時間,會造成激活定時器的計時時間誤差,多次更新會造成相當(dāng)可觀的累積誤差。
本文設(shè)計了一種嵌入式軟件定時器管理方法[5],實(shí)現(xiàn)了在單個硬件定時器的基礎(chǔ)上模擬多個軟件定時器,將前臺硬件定時器中斷服務(wù)程序和后臺軟件定時器管理程序結(jié)合,實(shí)現(xiàn)了單次定時、多次定時和循環(huán)定時應(yīng)用,同時保證了中斷服務(wù)程序的短小,避免了中斷嵌套等問題。
根據(jù)定時應(yīng)用的特點(diǎn)及分類,以結(jié)構(gòu)體的形式設(shè)計一種涵蓋單次定時、多次定時和循環(huán)定時三種類型定時的軟件定時器節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu),使用一個靜態(tài)分配的軟件定時器節(jié)點(diǎn)數(shù)組來定義所有的定時任務(wù)[6]。
系統(tǒng)上電后,首先初始化硬件定時器和所有軟件定時器節(jié)點(diǎn),硬件定時器以固定的時間間隔觸發(fā)MCU,執(zhí)行中斷服務(wù)程序(記為HT_ISR),軟件定時器管理程序(記為ST_MG)在主循環(huán)體中循環(huán)執(zhí)行,處理系統(tǒng)中所有軟件定時器節(jié)點(diǎn)。通過HT_ISR和ST_MG的結(jié)合,實(shí)現(xiàn)對軟件定時器節(jié)點(diǎn)狀態(tài)的管理。同時提供可以在系統(tǒng)的任何位置自由調(diào)用的API,以啟動、停止、查詢某個軟件定時器節(jié)點(diǎn)。整個系統(tǒng)執(zhí)行過程如圖1所示。
圖1 定時器管理系統(tǒng)流程圖
定義一個相對計時時間,記為Timer_ticked,表示未被軟件定時器計時的時間。HT_ISR以累加方式更新相對計時時間,軟件定時器管理程序完成對軟件定時器節(jié)點(diǎn)數(shù)組的操作后,清零相對計時時間。通過相對計時時間,保證了所有軟件定時器節(jié)點(diǎn)的同步計時和準(zhǔn)確計時。
軟件定時器節(jié)點(diǎn)Timer_Node以{軟件定時器ID, 激活狀態(tài), 定時次數(shù), 超時次數(shù), 循環(huán)標(biāo)識,定時值, 計時值, 回調(diào)函數(shù)}表示,其中的狀態(tài)變量即結(jié)構(gòu)體成員變量,各個成員變量的標(biāo)識、含義、取值范圍如表1所列。
表1 軟件定時器數(shù)據(jù)結(jié)構(gòu)
MCU上電初始化時,設(shè)置硬件定時器的中斷周期為T并使能中斷,硬件定時器以T為時間間隔,觸發(fā)MCU執(zhí)行HT_ISR。在HT_ISR中,累加相對計時時間的數(shù)值,累加值為T,即Timer_ticked += T。
軟件定時器管理程序在主循環(huán)體中循環(huán)執(zhí)行,處理所有軟件定時器節(jié)點(diǎn),執(zhí)行完所有軟件定時器節(jié)點(diǎn)的處理程序后,清零相對計時值。程序流程如圖2所示。
圖2 軟件定時器管理程序流程圖
軟件定時器節(jié)點(diǎn)處理程序流程如圖3所示,具體描述如下:
① 查詢該軟件定時器節(jié)點(diǎn)是否處于激活狀態(tài)。如果處于空閑狀態(tài),不進(jìn)行任何處理,進(jìn)入步驟⑥;如果處于激活狀態(tài),將軟件定時器節(jié)點(diǎn)的計時值累加,增量為相對計時時間的數(shù)值。
② 比較軟件定時器節(jié)點(diǎn)的計時值和定時值,如果計時值小于定時值。進(jìn)入步驟⑥;如果計時值大于或等于定時值,判斷軟件定時器節(jié)點(diǎn)是否為循環(huán)定時器。
③ 判斷軟件定時器節(jié)點(diǎn)是否為循環(huán)定時器,如果是循環(huán)定時器。重啟本軟件定時器節(jié)點(diǎn),進(jìn)入步驟⑤;如果不是循環(huán)定時器,將定時次數(shù)減1。
④ 判斷定時次數(shù)是否為0。如果定時次數(shù)為0,停止軟件定時器節(jié)點(diǎn);如果定時次數(shù)不為0,重啟本軟件定時器節(jié)點(diǎn)。
⑤ 查詢該軟件定時器節(jié)點(diǎn)是否存在定時回調(diào)函數(shù)。如果存在定時回調(diào)函數(shù),調(diào)用定時回調(diào)函數(shù);如果不存在定時回調(diào)函數(shù),不進(jìn)行任何處理。
⑥ 退出軟件定時器節(jié)點(diǎn)處理程序。
圖3 軟件定時器節(jié)點(diǎn)處理程序流程圖
以筆者所設(shè)計的BCM為例,軟件定時器包括檢測輸入信號的周期性定時器、喂看門狗的周期性定時器、監(jiān)測系統(tǒng)狀態(tài)的周期性定時器、蜂鳴器報警的多次定時器、總線busoff后恢復(fù)通信的單次定時器等,首先以可讀性較強(qiáng)的枚舉類型定義定時器ID如下:
typedef enum{
INPUT_DETECT_PTMR = 0,
FEED_WATCHDOG_PTMR,
SYS_MONITOR_PTMR,
BEEPTWEET_TTMR,
……
BUSOFF_TTMR,
……
MAX_TMR,
}e_TimerId;
軟件定時器ID的枚舉值取值范圍為[0,N-1],其中,N為軟件定時器節(jié)點(diǎn)的數(shù)量。枚舉值根據(jù)各個定時應(yīng)用的具體邏輯命名。在初始化階段,將軟件定時器ID初始化為對應(yīng)的數(shù)組成員的下標(biāo),即Timer[i].timer_id=i,在系統(tǒng)運(yùn)行階段,便可以可讀性強(qiáng)的枚舉類型的軟件定時器ID作為數(shù)組下標(biāo)引用相應(yīng)的軟件定時器,比如以Timer[INPUT_DETECT_PTMR]引用輸入信號檢測定時器,以Timer[FEED_WATCHDOG_PTMR]引用喂看門狗的周期性定時器,避免了以整型變量為下標(biāo)引用特定定時器時需要查找出該定時器對應(yīng)的下標(biāo)的麻煩。
提供啟動單次定時器、啟動多次定時器、啟動周期性定時器、停止定時器、重啟定時器、查詢定時器狀態(tài)的API函數(shù)如下:
void SingleTimerStart(e_TimerId timer_id,uint32_t timeout,TMR_CALLBACK callback);
void MultiTimerStart(e_TimerId timer_id,uint32_t timeout,uint8_t cnt_times,TMR_CALLBACK callback);
void CycleTimerStart(e_TimerId timer_id,uint32_t timeout,TMR_CALLBACK callback);
void TimerStop(e_TimerId timer_id);
void TimerReStart(e_TimerId timer_id);
uint8_t TimerIsActive(e_TimerId timer_id);
假如,BCM以10 ms為周期檢測輸入信號,輸入信號檢測函數(shù)為InputDetect(),需要調(diào)用的函數(shù)為:
CycleTimerStart(INPUT_DETECT_PTMR,10,InputDetect);
在軟件定時器管理程序、硬件定時器中斷服務(wù)程序的作用下,BCM就會以10 ms為周期調(diào)用InputDetect()函數(shù)。