一种基于有序链表的高效定时器的实现
软件定时器应用较广泛,在嵌入式开发时使用OS时,一般可使用OS提供的软件定时器,裸机开发时,可能会需要自己造轮子,本文设计了一种基于有序链表的软件定时器。
工作原理
维护1个定时器链表,表头为最早触发的定时器,后续每个定时器均包含距离上一个定时器的间隔时间;执行时只需判断表头定时器是否满足执行条件,不需要遍历所有定时器;添加定时器时,只需从表头向后遍历,将定时器插入合适的位置,同时将下1个定时器的间隔时间减去新增定时器间隔时间即可;删除定时器,则只需将定时器从链表移除,同时将下1个定时器间隔时间加上删除定时器间隔时间。为实现周期定时功能,需要新增一些指示定时器参数的成员变量。
例如:当定时器链表为空,顺序添加5个定时器,定时时间分别为定时器a:3、定时器b:5、c:12、d:8、e:8,则链表最终表现为 定时器n:间隔时间 a:3 -> b:2 -> d:3 -> e:0 -> c->4。 但是定时器任务不可能同时添加,可能会在任何时间添加或删除,因此需要维护一个参数记录最终触发时间,与当前时间求差即可获得已逝去时间,将这个差值加在新增定时器间隔时间即可。
定时器主处理函数,可放于中断中,或者直接放在主循环或者IDLE中即可。放于中断内,需要注意内部函数执行时间不能过长,否则影响定时精度。
代码
头文件: 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.c
/**
* @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;
}
}