软件定时器应用较普遍,在嵌入式开发时使用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; } }