官术网_书友最值得收藏!

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ù),以滿足需求。

主站蜘蛛池模板: 屏南县| 始兴县| 台东县| 上杭县| 普兰县| 攀枝花市| 乌什县| 奉贤区| 鹿泉市| 青川县| 伊吾县| 灵寿县| 昌都县| 宁蒗| 读书| 三江| 文山县| 吴桥县| 延寿县| 新化县| 剑阁县| 沙洋县| 奎屯市| 咸阳市| 拜城县| 武平县| 三都| 疏附县| 金华市| 铜川市| 白城市| 河源市| 卓尼县| 苍南县| 新乡市| 泸西县| 丁青县| 旬阳县| 平山县| 溧水县| 湟中县|