<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 設(shè)計(jì)應(yīng)用 > 一個(gè)嵌入式軟件定時(shí)器的實(shí)現(xiàn)!

          一個(gè)嵌入式軟件定時(shí)器的實(shí)現(xiàn)!

          作者: 時(shí)間:2024-05-14 來(lái)源: 收藏

          1. 什么是

          是用程序模擬出來(lái)的,可以由一個(gè)硬件定時(shí)器模擬出成千上萬(wàn)個(gè)定時(shí)器,這樣程序在需要使用較多定時(shí)器的時(shí)候就不會(huì)受限于硬件資源的不足,這是軟件定時(shí)器的一個(gè)優(yōu)點(diǎn),即數(shù)量不受限制。

          本文引用地址:http://www.ex-cimer.com/article/202405/458698.htm

          但由于軟件定時(shí)器是通過(guò)程序?qū)崿F(xiàn)的,其運(yùn)行和維護(hù)都需要耗費(fèi)一定的CPU資源,同時(shí)精度也相對(duì)硬件定時(shí)器要差一些。

          2. 軟件定時(shí)器的實(shí)現(xiàn)原理

          在Linux,uC/OS,F(xiàn)reeRTOS等操作系統(tǒng)中,都帶有軟件定時(shí)器,原理大同小異。典型的實(shí)現(xiàn)方法是:通過(guò)一個(gè)硬件定時(shí)器產(chǎn)生固定的時(shí)鐘節(jié)拍,每次硬件定時(shí)器中斷到,就對(duì)一個(gè)全局的時(shí)間標(biāo)記加一,每個(gè)軟件定時(shí)器都保存著到期時(shí)間。

          程序需要定期掃描所有運(yùn)行中的軟件定時(shí)器,將各個(gè)到期時(shí)間與全局時(shí)鐘標(biāo)記做比較,以判斷對(duì)應(yīng)軟件定時(shí)器是否到期,到期則執(zhí)行相應(yīng)的回調(diào)函數(shù),并關(guān)閉該定時(shí)器。

          以上是單次定時(shí)器的實(shí)現(xiàn),若要實(shí)現(xiàn)周期定時(shí)器,即到期后接著重新定時(shí),只需要在執(zhí)行完回調(diào)函數(shù)后,獲取當(dāng)前時(shí)間標(biāo)記的值,加上延時(shí)時(shí)間作為下一次到期時(shí)間,繼續(xù)運(yùn)行軟件定時(shí)器即可。

          3. 基于STM32的軟件定時(shí)器

          3.1 時(shí)鐘節(jié)拍

          軟件定時(shí)器需要一個(gè)硬件時(shí)鐘源作為基準(zhǔn),這個(gè)時(shí)鐘源有一個(gè)固定的節(jié)拍(可以理解為秒針的每次滴答),用一個(gè)32位的全局變量tickCnt來(lái)記錄這個(gè)節(jié)拍的變化:

          static volatile uint32_t tickCnt = 0;    //軟件定時(shí)器時(shí)鐘節(jié)拍

          每來(lái)一個(gè)節(jié)拍就對(duì)tickCnt加一(記錄滴答了多少下):

          /* 需在定時(shí)器中斷內(nèi)執(zhí)行 */
          void tickCnt_Update(void){
           tickCnt++;
          }

          一旦開(kāi)始運(yùn)行,tickCnt將不停地加一,而每個(gè)軟件定時(shí)器都記錄著一個(gè)到期時(shí)間,只要tickCnt大于該到期時(shí)間,就代表定時(shí)器到期了。

          3.2 數(shù)據(jù)結(jié)構(gòu)

          軟件定時(shí)器的數(shù)據(jù)結(jié)構(gòu)決定了其執(zhí)行的性能和功能,一般可分為兩種:數(shù)組結(jié)構(gòu)和鏈表結(jié)構(gòu)。什么意思呢?這是(多個(gè))軟件定時(shí)器在內(nèi)存中的存儲(chǔ)方式,可以用數(shù)組來(lái)存,也可以用鏈表來(lái)存。

          兩者的優(yōu)劣之分就是兩種數(shù)據(jù)結(jié)構(gòu)的特性之分:數(shù)組方式的定時(shí)器查找較快,但數(shù)量固定,無(wú)法動(dòng)態(tài)變化,數(shù)組大了容易浪費(fèi)內(nèi)存,數(shù)組小了又可能不夠用,適用于定時(shí)事件明確且固定的系統(tǒng);鏈表方式的定時(shí)器數(shù)量可動(dòng)態(tài)增減,易造成內(nèi)存碎片(如果沒(méi)有內(nèi)存管理),查找的時(shí)間開(kāi)銷相對(duì)數(shù)組大,適用于通用性強(qiáng)的系統(tǒng),Linux,uC/OS,F(xiàn)reeRTOS等操作系統(tǒng)用的都是鏈表式的軟件定時(shí)器。

          本文使用數(shù)組結(jié)構(gòu):

          static softTimer timer[TIMER_NUM];        //軟件定時(shí)器數(shù)組

          數(shù)組和鏈表是軟件定時(shí)器整體的數(shù)據(jù)結(jié)構(gòu),當(dāng)具體到單個(gè)定時(shí)器時(shí),就涉及軟件定時(shí)器結(jié)構(gòu)體的定義,軟件定時(shí)器所具有的功能與其結(jié)構(gòu)體定義密切相關(guān),以下是本文中軟件定時(shí)器的結(jié)構(gòu)體定義:

          typedef struct softTimer {
           uint8_t state;           //狀態(tài)
           uint8_t mode;            //模式
           uint32_t match;          //到期時(shí)間
           uint32_t period;         //定時(shí)周期
           callback *cb;            //回調(diào)函數(shù)指針
           void *argv;              //參數(shù)指針
           uint16_t argc;           //參數(shù)個(gè)數(shù)
          }softTimer;

          定時(shí)器的狀態(tài)共有三種,默認(rèn)是停止,啟動(dòng)后為運(yùn)行,到期后為超時(shí)。

          typedef enum tmrState {
           SOFT_TIMER_STOPPED = 0,  //停止
           SOFT_TIMER_RUNNING,      //運(yùn)行
           SOFT_TIMER_TIMEOUT       //超時(shí)
          }tmrState;

          模式有兩種:到期后就停止的是單次模式,到期后重新定時(shí)的是周期模式。

          typedef enum tmrMode {
           MODE_ONE_SHOT = 0,       //單次模式
           MODE_PERIODIC,           //周期模式
          }tmrMode;

          不管哪種模式,定時(shí)器到期后,都將執(zhí)行回調(diào)函數(shù),以下是該函數(shù)的定義,參數(shù)指針argv為void指針類型,便于傳入不同類型的參數(shù)。

          typedef void callback(void *argv, uint16_t argc);

          上述結(jié)構(gòu)體中的模式state和回調(diào)函數(shù)指針cb是可選的功能,如果系統(tǒng)不需要周期執(zhí)行的定時(shí)器,或者不需要到期后自動(dòng)執(zhí)行某個(gè)函數(shù),可刪除此二者定義。

          3.3 定時(shí)器操作

          3.3.1 初始化

          首先是軟件定時(shí)器的初始化,對(duì)每個(gè)定時(shí)器結(jié)構(gòu)體的成員賦初值,雖說(shuō)static變量的初值為0,但個(gè)人覺(jué)得還是有必要保持初始化變量的習(xí)慣,避免出現(xiàn)一些奇奇怪怪的BUG。

          void softTimer_Init(void){
           uint16_t i;
           for(i=0; i<timer_num; i++) {
            timer[i].state = SOFT_TIMER_STOPPED;
            timer[i].mode = MODE_ONE_SHOT;
            timer[i].match = 0;
            timer[i].period = 0;
            timer[i].cb = NULL;
            timer[i].argv = NULL;
            timer[i].argc = 0;
           }
          }

          3.3.2 啟動(dòng)

          啟動(dòng)一個(gè)軟件定時(shí)器不僅要改變其狀態(tài)為運(yùn)行狀態(tài),同時(shí)還要告訴定時(shí)器什么時(shí)候到期(當(dāng)前tickCnt值加上延時(shí)時(shí)間即為到期時(shí)間),單次定時(shí)還是周期定時(shí),到期后執(zhí)行哪個(gè)函數(shù),函數(shù)的參數(shù)是什么,交代好這些就可以開(kāi)跑了。

          void softTimer_Start(uint16_t id, tmrMode mode, uint32_t delay, callback *cb, void *argv, uint16_t argc){
           assert_param(id < TIMER_NUM);
           assert_param(mode == MODE_ONE_SHOT || mode == MODE_PERIODIC);
           
           timer[id].match = tickCnt_Get() + delay;
           timer[id].period = delay;
           timer[id].state = SOFT_TIMER_RUNNING;
           timer[id].mode = mode;
           timer[id].cb = cb;
           timer[id].argv = argv;
           timer[id].argc = argc;
          }

          上面函數(shù)中的assert_param()用于參數(shù)檢查,類似于庫(kù)函數(shù)assert()。

          3.3.3 更新

          本文中軟件定時(shí)器有三種狀態(tài):停止,運(yùn)行和超時(shí),不同的狀態(tài)做不同的事情。停止?fàn)顟B(tài)最簡(jiǎn)單,啥事都不做;運(yùn)行狀態(tài)需要不停地檢查有沒(méi)有到期,到期就執(zhí)行回調(diào)函數(shù)并進(jìn)入超時(shí)狀態(tài);超時(shí)狀態(tài)判斷定時(shí)器的模式,如果是周期模式就更新到期時(shí)間,繼續(xù)運(yùn)行,如果是單次模式就停止定時(shí)器。

          這些操作都由一個(gè)更新函數(shù)來(lái)實(shí)現(xiàn):

          void softTimer_Update(void){
           uint16_t i;
           
           for(i=0; i<timer_num; i++) {
             switch (timer[i].state) {
                 case SOFT_TIMER_STOPPED:
               break;
            
              case SOFT_TIMER_RUNNING:
               if(timer[i].match <= tickCnt_Get()) {
                timer[i].state = SOFT_TIMER_TIMEOUT;
                timer[i].cb(timer[i].argv, timer[i].argc);       //執(zhí)行回調(diào)函數(shù)
               }
               break;
             
              case SOFT_TIMER_TIMEOUT:
               if(timer[i].mode == MODE_ONE_SHOT) {
                   timer[i].state = SOFT_TIMER_STOPPED;
               } else {
                timer[i].match = tickCnt_Get() + timer[i].period;
                   timer[i].state = SOFT_TIMER_RUNNING;
               }
               break;
            
              default:
               printf("timer[%d] state error!rn", i);
               break;
             }
            }
          }

          3.3.4 停止

          如果定時(shí)器跑到一半,想把它停掉,就需要一個(gè)停止函數(shù),操作很簡(jiǎn)單,改變目標(biāo)定時(shí)器的狀態(tài)為停止即可:

          void softTimer_Stop(uint16_t id){
           assert_param(id < TIMER_NUM);
           timer[id].state = SOFT_TIMER_STOPPED;
          }

          3.3.5 讀狀態(tài)

          又如果想知道一個(gè)定時(shí)器是在跑著呢還是已經(jīng)停下來(lái)?也很簡(jiǎn)單,返回它的狀態(tài):

          uint8_t softTimer_GetState(uint16_t id){
           return timer[id].state;
          }

          或許這看起來(lái)很怪,為什么要返回,而不是直接讀?別忘了在前面3.2節(jié)中定義的定時(shí)器數(shù)組是個(gè)靜態(tài)全局變量,該變量只能被當(dāng)前源文件訪問(wèn),當(dāng)外部文件需要訪問(wèn)它的時(shí)。候只能通過(guò)函數(shù)返回,這是一種簡(jiǎn)單的封裝,保持程序的模塊化。

          3.4 測(cè)試

          最后,當(dāng)然是來(lái)驗(yàn)證一下我們的軟件定時(shí)器有沒(méi)達(dá)到預(yù)想的功能。

          定義三個(gè)定時(shí)器:

          定時(shí)器TMR_STRING_PRINT只執(zhí)行一次,1s后在串口1打印一串字符;

          定時(shí)器TMR_TWINKLING為周期定時(shí)器,周期為0.5s,每次到期都將取反LED0的狀態(tài),實(shí)現(xiàn)LED0的閃爍;

          定時(shí)器TMR_DELAY_ON執(zhí)行一次,3s后點(diǎn)亮LED1,跟第一個(gè)定時(shí)器不同的是,此定時(shí)器的回調(diào)函數(shù)是個(gè)空函數(shù)nop(),點(diǎn)亮LED1的操作通過(guò)主循環(huán)中判斷定時(shí)器的狀態(tài)來(lái)實(shí)現(xiàn),這種方式在某些場(chǎng)合可能會(huì)用到。

          static uint8_t data[] = {1,2,3,4,5,6,7,8,9,0};
          int main(void){
           USART1_Init(115200);
           TIM4_Init(TIME_BASE_MS);
           TIM4_NVIC_Config();
           LED_Init();
           
           printf("I just grabbed a spoon.rn");
           
           softTimer_Start(TMR_STRING_PRINT, MODE_ONE_SHOT, 1000, stringPrint, data, 5);
           softTimer_Start(TMR_TWINKLING, MODE_PERIODIC, 500, LED0_Twinkling, NULL0);
           softTimer_Start(TMR_DELAY_ON, MODE_ONE_SHOT, 3000, nop, NULL0);
           
           while(1) {
            softTimer_Update();
            if(softTimer_GetState(TMR_DELAY_ON) == SOFT_TIMER_TIMEOUT) {
             LED1_On();
            }
           }
          }


          本文來(lái)源網(wǎng)絡(luò),免費(fèi)傳達(dá)知識(shí),版權(quán)歸原作者所有。如涉及作品版權(quán)問(wèn)題,請(qǐng)聯(lián)系進(jìn)行刪除。



          關(guān)鍵詞: 嵌入式 軟件 定時(shí)器

          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();