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

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

主要数据结构及数据

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;
    }
}
 

原文地址:https://www.cnblogs.com/citroen/p/9965888.html

时间: 2024-10-15 21:53:05

基于链表的软件定时器实现(转)的相关文章

OSAL之时间管理,软件定时器链表管理

读源码写作,尊重原创: 本博文根据蓝牙4.0, 协议族版本是1.3.2 OSAL的时钟实现在OSAL_CockBLE.c与OSAL_Clock.h两个文件中.OSAL支持完整的UTC(世界统一时间),以2000年1月1日00:00:00为时间起点,可以精确到年.月.日.时.分.秒的时间值. 背景知识 // number of seconds since 0 hrs, 0 minutes, 0 seconds, on the 1st of January 2000 UTC存储自2000年1月1日开

分享一个简单易用的软件定时器模块——基于keil+stm32f103zet+hal库(裸机实现)

公众号上看到一个比较好的一个github项目:https://github.com/0x1abin/MultiTimer 今天看了看,简单的,就移植了- 且看文档的说明, ================================= 分享一个简单易用的软件定时器模块——MultiTimer.源码地址: https://github.com/0x1abin/MultiTimer 简介 MultiTimer 是一个软件定时器扩展模块,可无限扩展你所需的定时器任务,取代传统的标志位判断方式, 更

软件定时器

软件定时器是 uC/OS 操作系统的一个内核对象,软件定时器是基于时钟节拍和系统管理创建的软件性定时器,理论上可以创建无限多个,但精准度肯定比硬件定时稍逊一筹.使用硬件定时器往往需要查阅芯片的相关数据手册,比较繁琐,而使用 uC/OS 的软件定时非常方便. 软件定时器启动之后是由软件定时器任务 OS_TmrTask() 统一管理,在创建软件定时器之前必须先使能软件定时器和配置软件定时器的相关参数. 软件定时器的使能位于"os_cfg.h": /* -------------------

【小梅哥FPGA进阶教程】第九章 基于串口猎人软件的串口示波器

九.基于串口猎人软件的串口示波器 1.实验介绍 本实验,为芯航线开发板的综合实验,该实验利用芯航线开发板上的ADC.独立按键.UART等外设,搭建了一个具备丰富功能的数据采集卡,芯航线开发板负责进行数据的采集并将数据通过串口发送到PC机上,PC端,利用强大的串口调试工具--串口猎人,来实现数据的接收分析,并将数据分别以波形.码表.柱状图的形式动态显示出来,以让使用者能够直观的看到ADC采集到的信号细节.同时,用户也可以使用串口猎人通过串口给下位机(FPGA)发送指令,下位机将对接收到的指令进行解

实现自己的软件定时器

为什么要实现软件定时器: 在芯片平台上,地址空间也是相当宝贵的,如果保留了更多的硬件定时器的话,就需要更多的地址空间,那么我们能不能作个折中方案呢?答案是肯定的,我们可以使用一个硬件定时器,来模拟实现一个软件定时器,可以满足更多的定时需求,需要注意的一点就是软件定时器精度可能会有稍微误差,因为会涉及到任务调度.锁中断等,在对定时精度要求不高的场景,可以考虑使用软件定时器.Linux内核中的timer_list精度为10ms,这里我们来实现一套精度为1ms的软件定时器(当然可以实现精度为微秒级的,

高效软件定时器的设计

软件定时器在协议栈等很多场景都有广泛的应用,有时候会有大量的定时器同时处于工作状态,它们的超时时间各异,要高效的保证每个定时器都能够较为准确的超时并执行到其回调函数并不是一件易事.本文分析嵌入式实时操作系统Nucleus的定时器方案,它巧妙的管理了一条按照相对时间来排序的双向链表,避免每次tick中断都要遍历链表检查超时和更新剩余时间,实现了一种相当高效的软件定时器. 结构体TM_TCB来表示动态创建的定时器,其定义如下 typedef struct TM_TCB_STRUCT { /*Nucl

uC/OS-III 软件定时器(三)

软件定时器是uC/OS 操作系统的一个内核对象,软件定时器是基于时钟节拍和系统管理创建的软件性定时器,理论上可以创建无限多个,操作简单,但精准度肯定比硬件定时稍逊一筹. 原理和实现过程 要用到的函数: OSTmrCreate () //创建定时器 OSTmrDel () //删除定时器 OSTmrStart () //开启定时器 OSTmrStop () //停止定时器 使用定时器前先使能,软件定时器的使能位于“os_cfg.h”. 其有关参数的配置位于“os_cpu_app.h”. OSTmr

【TencentOS tiny】深度源码分析(8)——软件定时器

软件定时器的基本概念 TencentOS tiny 的软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受硬件定时器资源限制的定时器服务,本质上软件定时器的使用相当于扩展了定时器的数量,允许创建更多的定时业务,它实现的功能与硬件定时器也是类似的. 硬件定时器是芯片本身提供的定时功能.一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断.硬件定时器的精度一般很高,可以达到纳秒级别,并且是中

C语言——软件定时器

都说程序设计 = 算法 + 数据结构.数据结构是挨踢必修课,不过好像学了数据结构之后也没用来做过啥.不知道做啥,就写个软件定时器. 软件定时器数据结构 typedef struct __software_timer{ u32 timeout; //初始化时间计数器 u32 repeat; //运行间隔:repeat > 0 :周期定时时间 repeat == 0 :只定时一次 void (*timeout_callback_handler)(void *para); //超时回调函数 struc