基于链表的软件定时器实现(转)

Posted citroen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于链表的软件定时器实现(转)相关的知识,希望对你有一定的参考价值。

软件定时器在实际应用比较重要,本文旨在实现一种便于移植,易扩展功能,效率高的软件定时器。本定时器是基于排序链表,将最近将触发的定时器置于链表头,后续新增定时器将计算出其合适位置插入。

主要数据结构及数据

 


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;

static m_tm_tcb *ptm_list_header;

static uint32_t m_timeouts_last_time; //上次触发的时间

 

例一:我们将依次新增5个定时器,定时周期为3,5,8,8,12;当前时间为n;插入第1个时,如果链表空,则设置m_timeouts_last_time = n;设置第一个元素time=3; next=null;插入第2个,设置time2=5-3 = 2(相对与第一个时间),next=NULL;插入第3个,设置time = 8 - 3 - 2 = 3(相对与第二个时间);同理,第4个,time = 0; 第5个 time = 4。

上述是按照排好序的方式添加,如果打乱呢?

例二:依次添加周期为3,5,12,8的定时器。前3个定时器的添加与上个例子相同,插入完成后time1=3,time2=2,time3=7;  插入第4个,time4-time1>0? 成立,time4-=time1=5;与链表第2个元素时间对比,time4-time2>0?,成立,time4-=time2 = 3;在与链表第3个元素时间对比时,time4-time3>0?不成立,此定时器应该插入在2、3两个元素之间;但是原来第3个元素的触发时间就增加了time4的时间,因此将time3 -= time4 = 4。

最后,上述添加是同时添加5个定时器,实际运用时不可能全部同时添加。

例三:添加第1个定时器,间隔5;过了4个时间片后,新增第2个定时器,间隔3;显然第2个定时器应该在第1个触发后2个时间片再触发;虽然time2-time1>0不成立;这里就新增1个变量记录上次触发时间:m_timeouts_last_time;添加定时器时将计算出现在距离上次触发时间diff = now-m_timeouts_last_time;time2+=diff;这个例子中time2=3+(4-0)=7;然后按照例2进行添加。

定时器主处理函数可在中断中调用,也可在普通任务中(无OS时就是主循环)调用,判断now-m_timeouts_last_time>ptm_list_header->time?成立,则代表有定时器触发,需要进行处理;处理后表头直针后移,看新元素时间是否为0(相对前一个)如果为0,则进行处理,直到不为0;如果时周期性定时器,则可在添加是将定时器结构体内period设置为周期时间,在定时器回调执行完成后重新添加定时器即可。

该定时器使用过程中的注意事项,如果定时器主处理函数如果放在中断中,则代表超时回调函数也在中断中处理,此时不应将回调函数写的太长,例如:擦、写flash;串口用阻塞的方式发送太长的数据(115200大约在10kB/S,10个字节对应1ms,如果,回调执行超过1ms,明显会使定时器不准确);如果在普通任务调用则无此问题。比较好的方法其实是使用前后台处理机制,将回调函数必要参数传回普通任务中,在任务中去执行,这样想写多长也不会影响实时性。后续的另一篇文章中,我将实现一种通用的无OS的前后台调度器,降低中断平面、任务平面的耦合性。

/**
 * @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
--------------------- 
/**
 * @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;
    }
}
 

 






















以上是关于基于链表的软件定时器实现(转)的主要内容,如果未能解决你的问题,请参考以下文章

数据结构22:数组和广义表

数据结构 - 基于链表的队列

聊聊高并发(三十二)实现一个基于链表的无锁Set集合

线性表-链表的实现--基于学生信息管理

基于单向链表的队列的实现

基于单向链表的队列的实现