軟件定時器應用較普遍,在嵌入式開發時使用OS時,通常可以使用OS提供的軟件定時器,裸機開發時,可能會須要本身造輪子,本文設計了一種基於有序鏈表的軟件定時器。複雜度爲 執行:O(1); 插入:O(n); 刪除:O(1)函數
維護1個定時器鏈表,表頭爲最先觸發的定時器,後續每一個定時器均包含距離上一個定時器的間隔時間;執行時只需判斷表頭定時器是否知足執行條件,不須要遍歷全部定時器;添加定時器時,只需從表頭向後遍歷,將定時器插入合適的位置,同時將下1個定時器的間隔時間減去新增定時器間隔時間便可;刪除定時器,則只需將定時器從鏈表移除,同時將下1個定時器間隔時間加上刪除定時器間隔時間。爲實現週期定時功能,須要新增一些指示定時器參數的成員變量。
例如:當定時器鏈表爲空,順序添加5個定時器,定時時間分別爲定時器a:三、定時器b:五、c:十二、d:八、e:8,則鏈表最終表現爲 定時器n:間隔時間 a:3 -> b:2 -> d:3 -> e:0 -> c->4。 可是定時器任務不可能同時添加,可能會在任什麼時候間添加或刪除,所以須要維護一個參數記錄最終觸發時間,與當前時間求差便可得到已逝去時間,將這個差值加在新增定時器間隔時間便可。
定時器主處理函數,可放於中斷中,或者直接放在主循環或者IDLE中便可。放於中斷內,須要注意內部函數執行時間不能過長,不然影響定時精度。ui
頭文件: m_timeouts.h設計
/** * @file m_timeouts.h * Timer implementations */ #ifndef M_TIMEOUTS_H #define M_TIMEOUTS_H #include "m_common.h" //定時器回調函數 typedef void (* m_timeout_handler)(void *arg); //定時器結構體 typedef struct m_tm_tcb_struct { uint32_t time; //初次觸發時間 uint32_t period; //週期時間,若是是隻執行1次,則設爲0 void *pdata; //定時器私有參數 m_timeout_handler phandler; //定時器回調函數 struct m_tm_tcb_struct *next;//鏈表 }m_tm_tcb; //定時器初始化 void m_timeout_init(void); //添加定時器 int8_t m_timeout_add(m_tm_tcb *tm); //刪除定時器 int8_t m_timeout_delete(m_tm_tcb *tm); //定時器處理函數 void m_timeout_process(void); #endif
源文件: m_timeouts.ccode
/** * @file m_timeouts.c * Timer implementations */ #include "m_timeouts.h" static m_tm_tcb *ptm_list_header; static uint32_t m_timeouts_last_time; //上次觸發的時間。 uint32_t tm_get_now(void) { return HAL_GetTick(); } //定時器初始化 void m_timeout_init(void) { ptm_list_header = NULL; } //添加定時器,單次運行; int8_t m_timeout_add(m_tm_tcb *tm) { uint32_t diff=0,now,msecs; m_tm_tcb *p; now = tm_get_now(); //鏈表爲空 M_ENTER_CRITICAL(); if(ptm_list_header == NULL) { m_timeouts_last_time = now; ptm_list_header = tm; tm->next = NULL; M_EXIT_CRITICAL(); return 0; } else { diff = now - m_timeouts_last_time; msecs = tm->time; tm->time += diff; } if(ptm_list_header->time > tm->time) { ptm_list_header->time -= tm->time; tm->next = ptm_list_header; ptm_list_header = tm; } else { for(p = ptm_list_header; p!=NULL; p=p->next) { tm->time -= p->time; if(p->next == NULL || p->next->time > tm->time) { if(p->next != NULL) { p->next->time -= tm->time; } else if(tm->time > msecs) { tm->time = msecs+ptm_list_header->time; } tm->next = p->next; p->next = tm; break; } } } M_EXIT_CRITICAL(); return 0; } //刪除定時器 int8_t m_timeout_delete(m_tm_tcb *tm) { m_tm_tcb *prev, *t; M_ENTER_CRITICAL(); for(t=ptm_list_header, prev=NULL; t!=NULL; prev=t, t=t->next) { if(t == tm) { if(t->next) t->next->time += tm->time; if(prev == NULL) { ptm_list_header = t->next; } else { prev->next = t->next; } M_EXIT_CRITICAL(); return 0; } } M_EXIT_CRITICAL(); return -1; } //定時器處理函數 void m_timeout_process(void) { m_tm_tcb *tmptm = ptm_list_header; uint32_t diff = tm_get_now() - m_timeouts_last_time; while(tmptm && (diff >= tmptm->time)) { diff -= tmptm->time; M_ENTER_CRITICAL(); m_timeouts_last_time += tmptm->time; ptm_list_header = tmptm->next; M_EXIT_CRITICAL(); if(tmptm->period) { tmptm->time = tmptm->period; m_timeout_add(tmptm); } if(tmptm->phandler) tmptm->phandler(tmptm->pdata); tmptm = ptm_list_header; } }