软件定时器_博图软件定时器 - CSDN
精华内容
参与话题
  • 简单软件定时器

    2019-07-20 09:20:21
    软件定时器 在嵌入式开发中,定时器是及其常见的,但考虑到芯片外设资源有限,可以自己写一个软件定时器,应用于对计时不是太严格的场合,比如LED的闪烁,定时处理某一任务等等。该软件定时器的原理是基于滴答系统...

    软件定时器
    在嵌入式开发中,定时器是及其常见的,但考虑到芯片外设资源有限,可以自己写一个软件定时器,应用于对计时不是太严格的场合,比如LED的闪烁,定时处理某一任务等等。该软件定时器的原理是基于滴答系统时钟中断,在中断中获得时间基,该时间基可由用户自由设置。另外有两种方式可以实现软件定时处理功能,后面会讲到。

    软件定时器结构体元素
    首先说明一下该软件定时器的结构体元素:

    #define STIMER_EMPTY  0    
    #define STIMER_VALID  1
    #define STIMER_BASETIME 10   /* ms */
    typedef int (*stimerproc_t)(int arg);
    typedef struct {
     stimerproc_t     proc;
     uint32_t     arg;      /* 传入参数 */
     uint32_t      inv;      /* 时间间隔 */
     uint32_t    cnt;      /* 时间计数 */
     uint8_t       status; /* 定时器状态 */
     uint32_t     tflag;     /* 超时标志 */
     int            cycle;     /* 循环周期,-1为无限循环 */
    } stimer_t;


    stimer_t结构体中有一个指针函数proc,这是用户定时处理的任务,并且看到该指针函数有一个传入参数,这由用户添加定时器时传入。其中循环周期,视具体任务情况而定。
    在枚举中定义需要使用到的定时器序号,本文使用的是指定对象定时器的方式,另外的一种方式为定义一个大的定时器缓冲区,在添加定时器时检测到空的定时器即可添加,这两种方式各有取舍,看具体应用场景修改。

    typedef enum {
     LED_TIMER = 0,
     KEY_TIMER,
     MAX_TIMER
    }stimer_index_t;
    static stimer_t g_stimer_buf[MAX_TIMER]; /* 定时器缓冲区 */

    定时器核心代码
    在了解软件定时器结构体元素之后,再来详细看一下软件定时器核心代码:

    int add_stimer(uint8_t id, stimerproc_t proc, uint32_t inv, uint32_t arg, int cycle)
    {
        if (id >= MAX_TIMER) return -1;
        if (STIMER_EMPTY == g_stimer_buf[id].status) {
          g_stimer_buf[id].status = STIMER_VALID;
          g_stimer_buf[id].cnt = 0;
          g_stimer_buf[id].tflag = 0;
          g_stimer_buf[id].inv = inv;
          g_stimer_buf[id].proc = proc;
          g_stimer_buf[id].arg = arg;
          g_stimer_buf[id].cycle = cycle;
          return 1;
        }
        return -1;
    }

    void stop_stimer(uint8_t id)
    {
        if (id >= MAX_TIMER) return;
        if (STIMER_VALID == g_stimer_buf[id].status) {
            g_stimer_buf[id].status = STIMER_EMPTY;
          g_stimer_buf[id].cnt = 0;
          g_stimer_buf[id].cycle = 0;
          g_stimer_buf[id].tflag = 0;
          g_stimer_buf[id].inv = 0;
        }
    }

    void stimer_proc(void)
    {
        int i;
        for (i = 0; i < MAX_TIMER; i++) {
          if (STIMER_VALID == g_stimer_buf[i].status) {
               g_stimer_buf[i].cnt++;
                  if (g_stimer_buf[i].cnt >= g_stimer_buf[i].inv) {
                    g_stimer_buf[i].tflag = 1;
                    g_stimer_buf[i].cnt = 0;
               }
            }
        }
    }
    void stimer_task(void)
    {
        int i;
        stimerproc_t func;
        for (i = 0; i < MAX_TIMER; i++) {
          if (STIMER_VALID == g_stimer_buf[i].status) {
               if (g_stimer_buf[i].tflag) {
                    g_stimer_buf[i].tflag = 0;
                    func = g_stimer_buf[i].proc;
                    if ( func != NULL) 
                      (*func)(g_stimer_buf[i].arg);
                    if (0 == g_stimer_buf[i].cycle) 
                     stop_stimer(i);
                      if (g_stimer_buf[i].cycle > 0)
                     g_stimer_buf[i].cycle--;
               }
          }
        } 
    }

    __weak void system_tick_callback(void)
    {
        static int cnt = 0;
        cnt++;
        if (cnt >= STIMER_BASETIME) {
           cnt = 0;
           stimer_proc(); 
        }
    }
    /* 滴答中断1ms一次 */
    void SysTick_Handler(void)
    {
        HAL_IncTick();
        system_tick_callback();
    }
    void main(void)
    {
        while (1) 
        {
        stimer_task();
        }
    }

    利用滴答系统时钟产生的中断计时获得时间,这里的滴答时钟配置为1ms一次中断,当中断10次时即为一个软件定时器的时间基。
    当需要添加一个定时任务时,比如让LED灯500ms反转一次状态:

    int led_task_proc(int arg)
    {
        bsp_led_togglepin(LED1);
        return 0;
    }
    /* 添加软件定时器 */
    void main(void)
    {
        add_stimer(LED_TIMER, led_task_proc, 500 / STIMER_BASETIME, 0, -1);
        while (1) 
        {
            stimer_task();
        }
    }

    以上这种方式为在滴答中断中计时,在main函数中执行,另外还有一种方式针对无阻塞并对时间有要求的任务,即是把stimer_proc()与stimer_task()结合在一起实现,任务在滴答中断中执行,切记定时任务不能阻塞滴答中断。

    void stimer_proc(void)
    {
        int i;
        stimerproc_t func;
        for (i = 0; i < MAX_TIMER; i++) {
          if (STIMER_VALID == g_stimer_buf[i].status) {
            g_stimer_buf[i].cnt++;
                   if (g_stimer_buf[i].cnt >= g_stimer_buf[i].inv) {
                        g_stimer_buf[i].cnt = 0;
                        func = g_stimer_buf[i].proc;
                if ( func != NULL) 
                     (*func)(g_stimer_buf[i].arg);
                 if (0 == g_stimer_buf[i].cycle) 
                      stop_stimer(i);
                 if (g_stimer_buf[i].cycle > 0)
                      g_stimer_buf[i].cycle--;
             }
           }
        }
    }
    void main(void)
    {    
        while(1)
        {
        }
    }
    --------------------- 
    作者:我姓梁 
    来源:CSDN 
    原文:https://blog.csdn.net/LiaRonBob/article/details/85914399 
    版权声明:本文为博主原创文章,转载请附上博文链接!

    展开全文
  • 设计软件定时器

    2019-06-22 12:38:10
    在MCU芯片内部,往往硬件定时器的数量是非常有限的,而...这里我只介绍第二种方法,我们知道,硬件定时器是通过对系统时钟周期进行计数实现的,那么软件定时器也不例外,首先得要有时基,然后得计数以及清零或者重新...

    在MCU芯片内部,往往硬件定时器的数量是非常有限的,而实际工程中却需要大量的定时器来辅助完成具体的功能,如果一个函数占用一个定时器,那么显然不够用,怎么办?

    思路有2种:

    1、直接将开源嵌入式操作系统的软件定时器搬来使用

    2、自己设计软件定时器

    这里我只介绍第二种方法,我们知道,硬件定时器是通过对系统时钟周期进行计数实现的,那么软件定时器也不例外,首先得要有时基,然后得计数以及清零或者重新装载的动作。

    那么如何获得时基呢?硬件定时器是直接有系统时钟输入给它们,但是软件定时器则没有,我们必须通过其他的方法获得。有以下两种方法获得时基:

    1、直接利用硬件定时器中断作为时基,在硬件定时器中断服务函数中完成对软件定时器的计数。

    2、在主函数循环中监视硬件定时器的数值,如果数值达到预定值之后软件定时器就计数。

    上面第一种是比较精确的,毕竟中断服务函数总是如期到来,时基差不多是如下这种情况:

    但是第二种方法就没那么好了,比如受到任务运行的阻塞,也许硬件定时器早已到达预设数值,软件定时器还没有检测到,这种情况相当于输入到软件定时器的波形为占空比不断变化的时钟,而且硬件定时器的开关以及计数值装载还要通过软件去控制,显然这种方法设计的软件定时器只适合用在对精度要求非常低的情景下,比如按键扫描。

    基于上面两种获取时基方式利弊的考虑,我们采用硬件定时器中断的方式获取软件定时器的时基。于是基本思路就很清晰了,在中断服务函数中不断的对软件定时器进行计数,函数再利用软件定时器是否达到预设值来决定是运行还是阻塞。首先我们设计一个结构体,这个结构体包含了四个成员:

    typedef struct _SOFTTIMER
    {
        bool bIsActivate;
        uint16_t usCount;
        uint16_t usTimerValue;
        bool bIsTimeOut;
    }SOFTTIMER;

    bIsActivate,该成员用于描述该定时器是否被激活,激活就为TURE,没有激活则为FALSE。
    usCount,该成员用于在定时器中断服务函数中计数
    TimerValue,该成员用于表示软件定时器的计数最大值
    bIsTimeOut,该成员用于表示软件定时器是否到达预设计数值,以作为外部函数是否执行的依据

    下面定义若干个软件定时器:

    #define SOFTTIMER_MXANUM  5
    volatile SOFTTIMER stSOFTTIMERGROUP[SOFTTIMER_MXANUM];

    下面设计一个函数用于对软件定时器进行计数,该函数被定时器中断服务函数执行。如下所示:

    void SoftTimerRunning(void)
    {
        uint16_t i;
        for(i=0;i<SOFTTIMER_MXANUM;i++)
        {
            if(TRUE==stSOFTTIMERGROUP[i].bIsActivate)
            {
                stSOFTTIMERGROUP[i].usCount++;
                if(stSOFTTIMERGROUP[i].usCount>=stSOFTTIMERGROUP[i].usTimerValue)
                {
                    stSOFTTIMERGROUP[i].usCount=0;
                    stSOFTTIMERGROUP[i].bIsTimeOut=TRUE;
                    /*stSOFTTIMERGROUP[i].bIsActivate=FALSE;*/
                }
            }
        }
    }

    该函数很简单,基本思路就是首先判断某定时器是否被激活,如果被激活则对该定时器进行计数,计数值到预设值则清零并且设置时间到标志。

    而定时器的激活则是由外部函数完成的,当外部定时器需要使用某软件定时器的时候就将该软件定时器激活,并将预设计数值传给它,激活后软件定时器就开始计数,之后就检测该定时器是否到达预设值。代码如下:

    bool SetTimerOut(uint8_t *FunID,uint16_t PreValue)
    {
        uint8_t i;
        for(i=0;i<SOFTTIMER_MXANUM;i++)
        {
            if(TURE!=stSOFTTIMERGROUP[i].bIsActivate)
            {
                stSOFTTIMERGROUP[i].bIsActivate=TRUE;
                stSOFTTIMERGROUP[i].usTimerValue=PreValue;
                *FunID = i;
                return TRUE;
            }
        }
        return FALSE;
    }
    
    bool IsTimerOut(uint8_t *FunID)
    {
        uint8_t i;
        assert_param(IS_FUNID_RIGHT(*FunID));
        if(TRUE==stSOFTTIMERGROUP[*FunID].bIsTimeOut)return TRUE;
    }

    先判断某定时器是否被占用(即是否已经激活),如果被占用则查询下一个定时器,直到找到没有被占用的定时器并将预设值传给它,然后返回该定时器的ID号,如果没有找到可用定时器则返回FALSE。

     

    未完待续。。。

    展开全文
  • 前后台系统和多任务操作系统,在简单功能上差别不大,唯一不顺手的就是前后台系统没有合适的软件定时器。封装一层软件定时器接口,对后续功能开发事半功倍。 定义结构体数组,其基础成员如下: static uint32_t hw_...

    前后台系统和多任务操作系统,在简单功能上差别不大,唯一不顺手的就是前后台系统没有合适的软件定时器。封装一层软件定时器接口,对后续功能开发事半功倍。

    定义结构体数组,其基础成员如下:

    static uint32_t hw_interrupt_timer_ticks = 0;//timer ticks
    typedef void (*timer_callback)(void);
    //软件定时器最大个数
    #define SW_TIMER_MAX  10
    
    typedef struct
    {
        uint32 delay;                //延迟时间,实际是当前时间+期望延时的时间后的tick
        uint32 auto_repeat;			 //执行后自动重载时间,为0表示只执行一次
        timer_callback callback;     //回调函数
    } timer_struct;
    timer_struct my_timer[SW_TIMER_MAX] = {0};    //定时器参数数组,最大支持10个
    
    //启动定时器,index可按需求定为枚举类型,必须小于SW_TIMER_MAX
    void timer_start(uint8 index, uint32 delay, bool loop, timer_callback callback)void timer_stop(uint8 index)//停止定时器,delay清零,回调函数置为NULL
    bool timer_is_running(uint8 index)//当前定时器是否在计时运行中,判断delay是否非0
    

    对应用提供的启动定时器接口实现可以参考如下:

    static void timer_start(uint8 index, uint32 delay, bool loop, timer_callback callback)
    {
        if((index >= SW_TIMER_MAX) || (delay == 0) || (callback == NULL))
        {
            return;
        }
    
        my_timer[index].delay = hw_timer_ticks + delay;
        if(loop)//自动重载
        {
            my_timer[index].auto_repeat = delay;
        }
        else
        {
            my_timer[index].auto_repeat = 0;
        }
    
        my_timer[index].callback = callback;
    }
    

    使用硬件定时中断,例如1ms中断一次,每次中断hw_timer_ticks自增一次。主程序中查询hw_timer_ticks 大于delay表示当前定时器任务超时,执行callback。这种在8位单片机系统合适,如果硬件资源有限或者系统时钟太低,可以调整硬件定时中断的精度。这样多个定时器时事件的执行只需要一个硬件定时器。

    static void timer_loop(void)
    {
        uint8 i = 0, n = 0, min = 0;
        for(i = 0 ; i < SW_TIMER_MAX ; i++)
        {
            if(my_timer[i].delay > 0 && my_timer[i].callback != NULL)
            {
                if(hw_timer_ticks >= my_timer[i].delay)
                {
                    if(my_timer[i]->auto_repeat > 0)
                    {
                        my_timer[i].delay = hw_timer_ticks + my_timer[i]->auto_repeat;
                    }
                    else
                    {
                        my_timer[i].delay = 0; //stop
                    }
                    my_timer[i].callback();
                }
            }
        }
    }
    

    实现基础框架后,应用层只需要简单的操作就是实现,例如定时每60秒读取一次ADC结果,和延时30秒后关闭LED显示。

    static void timer_start(READ_ADC_TIMER_ID, 60*1000, TRUE, read_adc_callback);
    static void timer_start(LED_ON_TIMEROUT_TIMER_ID, 30*1000, FALSE, led_on_timeout_callback);
    

    这种方案的缺点是my_timer数组下标index与应用绑定,即使没有全部使用,主程序每次都要查询全部数组成员;11个定时任务即使明确最多只有9个同时在运行,该接口也无法满足需求。

    增加申请定时器资源的接口,在10个定时器池中获取没有被使用的,将index下标返回,再传入timer_statrt,同样的定义,可以满足10个定时任务同时运行,提高了定时器的利用率。

    因为主程序轮流查询定时器状态,加上前面的回调函数执行的时间,实际执行回调时间与期望的延时时间存在一定误差。在硬件资源充足的情况下,可以将数组改为链表,按delay从小到大排序,新启动的定时器按顺序插入到合适的位置,这样主程序查询速度会加快,在最近的软件定时器,也就是第一个软件定时器超时前可以休眠省电。或者分为多个数组映射不同的优先级和时间精度,这样提高回调函数执行的时间准确度。

    工欲善其事,必先利其器,基础件完美才能支撑复杂多变的应用层。

    在这里插入图片描述

    展开全文
  • 软件定时器

    2017-02-17 19:44:55
    ucosiii的软件定时器是在系统节拍的基础上分频得到的。 定时器的本质是递减计数器,当计数器减到0时可以触发某种动作执行,这个动作通过回调函数来实现。当定时器计数完成时,定义的回调函数会立即被调用,应用程序...

    ucosiii的软件定时器是在系统节拍的基础上分频得到的。

    定时器的本质是递减计数器,当计数器减到0时可以触发某种动作执行,这个动作通过回调函数来实现。当定时器计数完成时,定义的回调函数会立即被调用,应用程序可以有任意数量的定时器,ucosiii中定时器的时间分辨率由宏OS_CFG_TMR_TASK_RATE_HZ表示步长,比如一次10ms,那么100次就是1s,单位为Hz默认100Hz

    注意:一定要避免在回调函数中使用阻塞调用或可以阻塞或删除定时器任务的函数。阻塞函数是非常耗时的这样不利于系统的效率

    使能宏:通过OS_CFG_TMR_EN使能为1;

    回调函数:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方法直接调用,而是在特定的时间或条件发生时由另外的一方调用的,用于对事件进行响应。

    回调函数是自己编写的每次达到定时器的要求时都会运行一次回调函数。

    以下这些函数都不可在中断中调用

    OSTmrCreat()创建定时器并制定运行模式  在使用ucosiii的软件定时器必须先声明和创建软件定时器。

    OSTmrDel()删除定时器

    OSTmrRemainGet()获取定时器的剩余时间

    OSTmrStateGet()获取当前定时器状态

    OSTmrStop()停止计数器倒计时,软件定时器被停掉之后可也调用OSTmrStart()函数重启,但是重启之后定时器是从头计时,而不是接着上次停止的时刻继续计时。

    OSTmrStart()启动定时器计数其中涉及到两个函数OS_TmrUnlink()和OS_TmrLink()如果定时器处于运行状态则用这两个函数分别让定时器脱离定时器列表和插入定时器列表达到重启的效果。所有的的软件定时器都是通过时间轮来实现管理的,定时器轮与时钟节拍列表数组一样,就是有若干个定时器列表组成的数组。



    总结:软件定时器是ucosiii操作系统的一种软件性定时器,也就是通过代码实现的具有定时功能的内核机制。它与硬件定时器不同,精准度也无法和硬件定时器想媲美

    要使用软件定时器首先要声明一个OS_TMR对象,并通过OSTmrCreat()函数创建软件定时器。软件定时器分OS_OPT_TMR_ONE_SHOT和OS_OPT_TMR_PERIODIC两种类型。前者相当于硬件定时器的突发模式,执行的是一次性定时,定时完成后软件定时器就停止工作,需要再次启动才能再次工作。后者相当于硬件定时器的周期模式,具有周期重载值,可以不断周期工作,不需再次启动。

    软件定时器创建完成之后,还需要调用OSTmrStart()函数启动它

    OSTmrStop()函数可以停止一个软件定时器定时,但并没有删除,可以调用OSTmrStart()函数启动它,但重启是重新计时,不是紧接着停止的时候继续计时的。

    OSTmrDel()将删除定时器,删除之后定时器就不能继续使用了。



    使能软件定时器后 在OS_TmrInit()中会根据OSCfg_TmrTaskRate_Hz确定OSTmrUpdateCtr  该值在OSTimeTick()会随着每次的时钟节拍的来临而递减当减到0时,会给OS_TmrTask()发送一个信号量,OS_TmrTask检测到后 OSTmrTickCtr就会自加,并且  p_tmr->Remain--,如果  p_tmr->Remain==0则调用回调函数。



    软件定时器最最核心的一点是在底层的一个硬件定时器上进行软件分频,ucosiiiduiying的硬件定时器就是产生时钟节拍的那个定时器。

    展开全文
  • 5-软件定时器

    2019-11-10 11:01:29
    UCOSIII 软件定时器 软件定时器原理 软件定时器相关函数汇总 总结函数的参数和用法及注意事项 举个栗子 在不带操作系统的时候,如果想要定时一般会使用芯片上的硬件定时器,但是在UCOS中,我们可以使用...
  • 004_软件定时器

    2019-12-25 09:31:50
    (一) 将软件定时器打开 #define OS_CFG_TMR_EN 1u (一)定义软件定时器 OS_TMR tmr1; //定时器1 OS_TMR tmr2; //定时器2 (二)创建软件定时器 //创建定时器1 OSTmrCreate((OS_TMR )&tmr1, ...
  • 软件定时器的实现

    千次阅读 2019-07-20 09:18:01
    软件定时器是用程序模拟出来的定时器,可以由一个硬件定时器模拟出成千上万个软件定时器,这样程序在需要使用较多定时器的时候就不会受限于硬件资源的不足,这是软件定时器的一个优点,即数量不受限制。但由于软件...
  • 简单实用软件定时器

    千次阅读 2019-01-06 11:34:00
    在嵌入式开发中,定时器是及其常见的,但考虑到芯片外设资源有限,可以自己写一个软件定时器,应用于对计时不是太严格的场合,比如LED的闪烁,定时处理某一任务等等。该软件定时器的原理是基于滴答系统时钟中断,在...
  • 高效软件定时器的设计

    千次阅读 2016-07-14 19:05:02
    软件定时器在协议栈等很多场景都有广泛的应用,有时候会有大量的定时器同时处于工作状态,需要管理,它们的超时时间各异,要高效的保证每个定时器都能够较为准确的超时并执行到其回调函数并不是一件易事。...
  • 单片机软件定时器的使用方法

    千次阅读 2016-05-31 09:29:07
    单片机软件定时器的使用方法 本文讲述了一种单片机软件定时器的使用方法。
  • ucos_ii定时器详解

    万次阅读 2015-09-19 17:24:23
    UCOSII从V2.83版本以后,加入了软件定时器,这使得UCOSII的功能更加完善,在其上的应用程序开发与移植也更加方便。在实时操作系统中一个好的软件定时器实现要求有较高的精度、较小的处理器开销,且占用较少的存储器...
  • 定时器的实现依赖的是CPU时钟中断,时钟中断的精度就决定定时器精度的极限。一个时钟中断源如何实现多个定时器呢?对于内核,简单来说就是用特定的数据...软件实现动态定时器常用数据结构有:时间轮、最小堆和红黑树。
  • 介绍软件定时器使用基础:单次触发和自动重载定时器。 1、头文件声明 #include "FreeRTOS.h" #include "task.h" #include "timers.h" #include "supporting_functions.h"...
  • 在freertos中用到了软件定时器,但是发现启动某个任务后导致定时器不能正常启动,经排查发现是定时器任务的优先级太低,在另外一个任务中有一段for的死循环,导致定时器Daemon线程无法被调度所以定时器运行不起来 ...
  • STM32软件定时器的设计

    千次阅读 2016-06-01 13:29:49
    定时器分为硬件定时器和软件定时器,几乎所有的微控制器上都配备了数量有限的硬件定时器,即控制器本身有专门实现定时的模块。几乎所有的硬件定时器的工作原理都是一样的:定时器在外部时钟提供的周期脉冲下进行计数...
  • ——高级定时器、通用定时器、基本定时器的区别   TIM1和TIM8定时器的功能包括【增强型】: ● 16位向上、向下、向上/下自动装载计数器 ● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535...
  • STM32CubeMX FreeRTOS软件定时器实验

    千次阅读 2019-08-27 16:44:43
    软件: 1、SW4STM32 2、STM32CubeMX 关于这两个软件在linux的安装教程请看我另一篇博文 Linux/Windows配置stm32免费开发环境详细流程 sw4stm32比较难下载,在此提供百度网盘链接: 链接:...
  • ———*UCOSIII-软件定时器* 一、软件定时器简介 UC/OS-III提供了软件定时器服务,定时器的本质就是一个递减的计数器,当计数器减到0的时候可以触发某种动作的执行,这个动作通过回调函数来实现,回调函数
  • uCOS的软件定时器

    千次阅读 2016-04-27 17:28:35
    uCOS2.81后的版本中有软件定时器的概念,软件定时器其实跟硬件中断是相似的,定时时间到了,就执行一次回调函数,虽然好用,但是也会降低系统的实时性。  软件定时器也需要一个时钟节拍驱动,这个驱动也是由硬件...
  • 定时器和多线程的区别和联系

    千次阅读 2015-07-14 17:21:26
    向原作者致敬!!!  1 软件定时器  ...缺点1,速度:软件定时器的精度比较低,这是由Windows不实时的特性所决定的,在XP下,如果关闭所有能关闭的进程,MFC的软件定时器可以达到接近15ms的精度
1 2 3 4 5 ... 20
收藏数 50,234
精华内容 20,093
关键字:

软件定时器