一种基于有序链表的高效定时器的实现

一种基于有序链表的高效定时器的实现


软件定时器应用较普遍,在嵌入式开发时使用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;
    }
}
相关文章
相关标签/搜索