- 嵌入式微系統(tǒng)
- 王紹偉 鄭德智 吳玉勇
- 2242字
- 2019-01-03 20:54:12
1.3.4 軟件定時(shí)器
MCU51的硬件定時(shí)器通常只有三路:Timer1一般用于串口的時(shí)鐘;Timer2用于系統(tǒng)節(jié)拍后;Timer0用于其他一路,這顯然不能滿足實(shí)際使用。為了解決硬件定時(shí)器的不足,基于系統(tǒng)節(jié)拍基礎(chǔ)上,派生出軟件定時(shí)器來(lái)模擬多路定時(shí)器,其效果類似硬件定時(shí)器,但因?yàn)槭擒浖?shí)現(xiàn)的,靈活多變,應(yīng)用場(chǎng)合廣泛,是MS的重要功能,軟件定時(shí)器本質(zhì)是延時(shí)執(zhí)行,有以下場(chǎng)合需要用到:
1)鬧鐘,需要延時(shí)多長(zhǎng)時(shí)間后執(zhí)行。
2)按鍵音,按鍵檢測(cè)到后,蜂鳴器發(fā)聲,延時(shí)200ms后關(guān)閉。
3)進(jìn)入某一個(gè)菜單,超過設(shè)定時(shí)間沒有檢測(cè)到按鍵,自動(dòng)回到默認(rèn)界面。
4)開啟某一項(xiàng)測(cè)試,工作一定時(shí)間后關(guān)閉。
軟件定時(shí)器本質(zhì)是利用系統(tǒng)節(jié)拍作為計(jì)數(shù)時(shí)鐘,每一路計(jì)數(shù)器都有一個(gè)開關(guān),負(fù)責(zé)是否開啟這一路軟件定時(shí)器,它占用一個(gè)bit,最多8路也就是8 bit,恰好一個(gè)字節(jié),對(duì)應(yīng)狀態(tài)變量State。
再模擬多路計(jì)數(shù)器Delay,最多8路,每一路的計(jì)數(shù)器的位數(shù)是16 bit,相當(dāng)于最大計(jì)數(shù)65536,假設(shè)節(jié)拍為10ms,則最長(zhǎng)延時(shí)時(shí)間為655.36s。Delay由TimerStart函數(shù)初始化時(shí)賦初值,之后利用節(jié)拍倒計(jì)時(shí),倒計(jì)數(shù)為零時(shí),產(chǎn)生一個(gè)軟件定時(shí)器事件,這個(gè)時(shí)候,根據(jù)TimerStart函數(shù)初始化時(shí)設(shè)定的條件來(lái)執(zhí)行注冊(cè)的回調(diào)函數(shù),執(zhí)行的方式有兩種,一種是直接在節(jié)拍中執(zhí)行,一般適合執(zhí)行開銷低的函數(shù);另外一種是拋出軟件定時(shí)器消息,在大循環(huán)中處理注冊(cè)的回調(diào)函數(shù),這個(gè)適合執(zhí)行開銷高的函數(shù)。圖1-13為軟件定時(shí)器的原理架構(gòu)圖。

圖1-13 軟件定時(shí)器原理架構(gòu)圖
TimerStart(TimerMessage,1000,TimerCallBack);這個(gè)函數(shù)表示延時(shí)1000個(gè)節(jié)拍,時(shí)間到了之后,調(diào)用執(zhí)行TimerCallBack()這個(gè)函數(shù)。第一個(gè)參數(shù)TimerMessage表示處理的方式,在大循環(huán)消息中執(zhí)行。
假如延時(shí)之后,再自我調(diào)用延時(shí)函數(shù),則可實(shí)現(xiàn)自我循環(huán)功能。類似節(jié)拍功能,只是這個(gè)節(jié)拍時(shí)間比較長(zhǎng)。假如采用兩個(gè)軟件定時(shí)器嵌套使用,則可以根據(jù)延時(shí)時(shí)間實(shí)現(xiàn)不等長(zhǎng)度的周期性工作,比如工作3秒、停7秒,模擬工作狀態(tài),老化測(cè)試設(shè)備高。軟件定時(shí)器可根據(jù)需求應(yīng)用靈活,讀者多多體會(huì)。以下是一個(gè)軟件定時(shí)器間隔1000個(gè)節(jié)拍,自我循環(huán)打印的例子。
代碼清單1-11:軟件定時(shí)器示例
void TimerCallBack(void)
{
printf("軟件定時(shí)器延時(shí)執(zhí)行\n");
TimerStart(TimerMessage, 1000, TimerCallBack);
}
軟件定時(shí)器由注冊(cè)開始函數(shù)TimerStart、系統(tǒng)節(jié)拍例行函數(shù)TimerSystickRoutine和停止函數(shù)TimerStop組成。首先TimerStart函數(shù)需要把軟件定時(shí)器的工作模式、延時(shí)時(shí)間、回調(diào)函數(shù)注冊(cè)到相關(guān)的靜態(tài)變量中,注冊(cè)之后,TimerSystickRoutine函數(shù)被系統(tǒng)節(jié)拍例行調(diào)用,每調(diào)用一次,延時(shí)時(shí)間減一,當(dāng)減到為零時(shí),按存儲(chǔ)的處理方式,開始執(zhí)行存儲(chǔ)的回調(diào)函數(shù),之后把這一路定時(shí)器清空。延時(shí)時(shí)間及回調(diào)函數(shù),大家很容易理解,工作模式難以理解,這是因?yàn)檐浖〞r(shí)器是基于節(jié)拍模擬出來(lái)的,那么回調(diào)函數(shù)的執(zhí)行,一種是直接在節(jié)拍中運(yùn)行即可,但若這個(gè)回調(diào)函數(shù)速度比較慢,執(zhí)行時(shí)間比較長(zhǎng),故不宜在節(jié)拍中執(zhí)行,于是就增加了另一種模式,即把這個(gè)回調(diào)函數(shù)的地址,通過消息傳遞到大循環(huán)中,在大循環(huán)中執(zhí)行,這部分內(nèi)容在消息機(jī)制中已有講解。
軟件定時(shí)器處理模式有兩種:一種是直接在節(jié)拍例行函數(shù)中直接執(zhí)行,因?yàn)樵诠?jié)拍中執(zhí)行,也就是在中斷中執(zhí)行,所以執(zhí)行的回調(diào)函數(shù)不能太長(zhǎng),否則占用中斷時(shí)間;但有些確實(shí)需要占用較長(zhǎng)時(shí)間的,則采用另外一種處理模式,即通過消息拋出回調(diào)函數(shù)的函數(shù)入口地址,再在大循環(huán)中執(zhí)行。處理模式枚舉定義如下。
代碼清單1-12:定時(shí)器處理類型枚舉
typedef enum { TimerSystick = 0; // 系統(tǒng)節(jié)拍中處理 TimerMessage = 1; // 大循環(huán)消息中處理 }TimerModeEnum;
軟件定時(shí)器的數(shù)據(jù)結(jié)構(gòu)比較簡(jiǎn)單,TimerSum定義了定時(shí)器數(shù)量,State是一個(gè)8 bit類型的定時(shí)器就緒表,總共可以標(biāo)記8路定時(shí)器就緒狀態(tài)。Timer struct定義了延時(shí)時(shí)間Times和回調(diào)函數(shù)CallBackFunction類型變量,與后面的TimerBlock數(shù)組一起,用于存儲(chǔ)延時(shí)時(shí)間和回調(diào)函數(shù)地址,Mode定義了工作模式。具體代碼如下。
代碼清單1-13:定時(shí)器數(shù)據(jù)類型
typedef struct { ushort Times; function Function; }TimerStruct; #define TimerSum 0x4; // 定時(shí)器數(shù)量,不超過8 bit static Byte idata State = 0; // 工作標(biāo)記寄存器,共8路 static TimerModeEnum idata Mode; // 工作模式 static TimerStruct idata TimerBlock[TimerSum]; // 注冊(cè)函數(shù)及次數(shù)存儲(chǔ)數(shù)組
軟件定時(shí)器啟動(dòng)函數(shù)TimerStart包括三個(gè)參數(shù):mode(工作模式)、TimerModeEnum(枚舉類型)、times(16 bit ushort類型),最大65535,MS默認(rèn)節(jié)拍10ms,也就是說(shuō)最大延時(shí)是655.35s,registerFunction是注冊(cè)函數(shù),直接是無(wú)參數(shù)的函數(shù)名,往往也叫回調(diào)函數(shù)。軟件定時(shí)器,不推薦在中斷中被調(diào)用啟動(dòng)。代碼如下。
代碼清單1-14:定時(shí)器啟動(dòng)
Byte TimerStart(TimerModeEnum mode, ushort times, function registerFunction) { Byte i; EnterCritical(); for(i = 0; i < TimerSum; i++) { if(!GetBit(State, i)) { TimerBlock[i].Times = times; // 注冊(cè)節(jié)拍 TimerBlock[i].Function = registerFunction; // 注冊(cè)函數(shù) if(mode) // 工作模式 SetBit(Mode, i); else ResetBit(Mode, i); SetBit(State, i); // 置位開啟 ExitCritical(); return(i); } } ExitCritical(); return(invalid); }
軟件定時(shí)器停止函數(shù)為TimerStop,參數(shù)為id,從TimerStart中獲得,值為0,1,2…,代碼如下。
代碼清單1-15:定時(shí)器停止
void TimerStop(Byte id) { if (id >= TimerSum) return; EnterCritical(); ResetBit(State, id); ExitCritical(); }
軟件定時(shí)器的運(yùn)行是基于系統(tǒng)節(jié)拍的,實(shí)現(xiàn)計(jì)時(shí),所以必須要有一個(gè)節(jié)拍例行處理函數(shù)。代碼如下。
代碼清單1-16:定時(shí)器系統(tǒng)節(jié)拍例行程序
void TimerSystickRoutine(void) { Byte i = 0, stateBackup; if (State == 0x00) return; // 狀態(tài)表為空,跳出 stateBackup = State; // 復(fù)制一份狀態(tài)表 while (stateBackup) { if ((stateBackup & 0x01) == 1) { if ((--TimerBlock[i].Times) == 0) { // 計(jì)數(shù)遞減到零時(shí) if (GetBit(Mode, i)) // 獲取工作模式 PostMessage(MessageTimer,(ushort)TimerBlock[i].Function); // 大循環(huán)中處理 else (TimerBlock[i].Function)(); // 系統(tǒng)節(jié)拍中處理 ResetBit(State, i); // 關(guān)閉這一路定時(shí)器 } } stateBackup = stateBackup >> 1; i++; } }
MS的軟件定時(shí)器是動(dòng)態(tài)的,注冊(cè)建立一路定時(shí)器后,用完就回收,這種定時(shí)器適合RAM資源少的芯片,尤其是MCU51這類,以便于多次使用,滿足大部分需求。但有些需求希望軟件定時(shí)器是靜態(tài)的,可以開始,可以結(jié)束,但不會(huì)釋放這一路定時(shí)器,這樣id號(hào)可一直不變,若再加入復(fù)位功能,可以比較簡(jiǎn)單地用于處理一些超時(shí)等待功能。比如,界面等待一個(gè)數(shù)字按鍵,若時(shí)間超過某一個(gè)值,返回默認(rèn)界面,但在超時(shí)時(shí)間內(nèi)再獲得一個(gè)按鍵,超時(shí)重新復(fù)位,等待下一個(gè)按鍵,而這一功能目前動(dòng)態(tài)定時(shí)器完成起來(lái)較麻煩。此外,模擬軟件定時(shí)器的自我循環(huán),采用消息的是大循環(huán)模式,當(dāng)它停止時(shí),會(huì)出現(xiàn)一個(gè)臨界態(tài),在大循環(huán)中執(zhí)行時(shí),id已經(jīng)被釋放,這時(shí)外界想關(guān)閉這個(gè)自我循環(huán)已關(guān)閉不了,要想解決這個(gè)問題,最好在大循環(huán)中引入一個(gè)bool變量,根據(jù)這個(gè)變量決定是否退出。軟件定時(shí)器的使用非常靈活,一定要在分析透徹后使用,若理解不夠,反而會(huì)引起不必要的問題。同時(shí)在理解的基礎(chǔ)上,可以自己修改或增加功能函數(shù),以滿足需求。
- Kubernetes修煉手冊(cè)
- Learning OpenDaylight
- Linux實(shí)戰(zhàn)
- Windows Phone 7.5 Data Cookbook
- Windows Server 2019 Administration Fundamentals
- 計(jì)算機(jī)系統(tǒng):基于x86+Linux平臺(tái)
- ElasticSearch Cookbook
- Red Hat Enterprise Linux 6.4網(wǎng)絡(luò)操作系統(tǒng)詳解
- 跟老男孩學(xué)Linux運(yùn)維:Shell編程實(shí)戰(zhàn)
- Heroku Cloud Application Development
- Linux內(nèi)核分析及應(yīng)用
- 計(jì)算機(jī)應(yīng)用基礎(chǔ)(Windows 7+Office 2010)
- Windows 11使用方法與技巧從入門到精通
- 鴻蒙應(yīng)用開發(fā)實(shí)戰(zhàn)
- 從零開始學(xué)Windows 7