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