精华内容
参与话题
问答
  • 软件定时器的实现

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

    1.什么是软件定时器
    软件定时器是用程序模拟出来的定时器,可以由一个硬件定时器模拟出成千上万个软件定时器,这样程序在需要使用较多定时器的时候就不会受限于硬件资源的不足,这是软件定时器的一个优点,即数量不受限制。但由于软件定时器是通过程序实现的,其运行和维护都需要耗费一定的CPU资源,同时精度也相对硬件定时器要差一些。


    2.软件定时器的实现原理
    在Linux,uC/OS,FreeRTOS等操作系统中,都带有软件定时器,原理大同小异。典型的实现方法是:通过一个硬件定时器产生固定的时钟节拍,每次硬件定时器中断到,就对一个全局的时间标记加一,每个软件定时器都保存着到期时间,程序需要定期扫描所有运行中的软件定时器,将各个到期时间与全局时钟标记做比较,以判断对应软件定时器是否到期,到期则执行相应的回调函数,并关闭该定时器。

    以上是单次定时器的实现,若要实现周期定时器,即到期后接着重新定时,只需要在执行完回调函数后,获取当前时间标记的值,加上延时时间作为下一次到期时间,继续运行软件定时器即可。


    3.基于STM32的软件定时器
    3.1 时钟节拍
    软件定时器需要一个硬件时钟源作为基准,这个时钟源有一个固定的节拍(可以理解为秒针的每次滴答),用一个32位的全局变量tickCnt来记录这个节拍的变化:

    static volatile uint32_t tickCnt = 0;       //软件定时器时钟节拍
    1
    每来一个节拍就对tickCnt加一(记录滴答了多少下):

    /* 需在定时器中断内执行 */
    void tickCnt_Update(void)
    {
        tickCnt++;
    }

    一旦开始运行,tickCnt将不停地加一,而每个软件定时器都记录着一个到期时间,只要tickCnt大于该到期时间,就代表定时器到期了。

    3.2 数据结构
    软件定时器的数据结构决定了其执行的性能和功能,一般可分为两种:数组结构和链表结构。什么意思呢?这是(多个)软件定时器在内存中的存储方式,可以用数组来存,也可以用链表来存。

    两者的优劣之分就是两种数据结构的特性之分:数组方式的定时器查找较快,但数量固定,无法动态变化,数组大了容易浪费内存,数组小了又可能不够用,适用于定时事件明确且固定的系统;链表方式的定时器数量可动态增减,易造成内存碎片(如果没有内存管理),查找的时间开销相对数组大,适用于通用性强的系统,Linux,uC/OS,FreeRTOS等操作系统用的都是链表式的软件定时器。

    本文使用数组结构:

    static softTimer timer[TIMER_NUM];        //软件定时器数组
    1
    数组和链表是软件定时器整体的数据结构,当具体到单个定时器时,就涉及软件定时器结构体的定义,软件定时器所具有的功能与其结构体定义密切相关,以下是本文中软件定时器的结构体定义:

    typedef struct softTimer {
        uint8_t state;           //状态
        uint8_t mode;            //模式
        uint32_t match;          //到期时间
        uint32_t period;         //定时周期
        callback *cb;            //回调函数指针
        void *argv;              //参数指针
        uint16_t argc;           //参数个数
    }softTimer;

    定时器的状态共有三种,默认是停止,启动后为运行,到期后为超时。

    typedef enum tmrState {
        SOFT_TIMER_STOPPED = 0,  //停止
        SOFT_TIMER_RUNNING,      //运行
        SOFT_TIMER_TIMEOUT       //超时
    }tmrState;

    模式有两种:到期后就停止的是单次模式,到期后重新定时的是周期模式。

    typedef enum tmrMode {
        MODE_ONE_SHOT = 0,       //单次模式
        MODE_PERIODIC,           //周期模式
    }tmrMode;

    不管哪种模式,定时器到期后,都将执行回调函数,以下是该函数的定义,参数指针argv为void指针类型,便于传入不同类型的参数。

    typedef void callback(void *argv, uint16_t argc);
    1
    上述结构体中的模式state和回调函数指针cb是可选的功能,如果系统不需要周期执行的定时器,或者不需要到期后自动执行某个函数,可删除此二者定义。

    3.3 定时器操作
    3.3.1 初始化
    首先是软件定时器的初始化,对每个定时器结构体的成员赋初值,虽说static变量的初值为0,但个人觉得还是有必要保持初始化变量的习惯,避免出现一些奇奇怪怪的BUG。

    void softTimer_Init(void)
    {
        uint16_t i;
        for(i=0; i<TIMER_NUM; i++) {
            timer[i].state = SOFT_TIMER_STOPPED;
            timer[i].mode = MODE_ONE_SHOT;
            timer[i].match = 0;
            timer[i].period = 0;
            timer[i].cb = NULL;
            timer[i].argv = NULL;
            timer[i].argc = 0;
        }
    }

    3.3.2 启动
    启动一个软件定时器不仅要改变其状态为运行状态,同时还要告诉定时器什么时候到期(当前tickCnt值加上延时时间即为到期时间),单次定时还是周期定时,到期后执行哪个函数,函数的参数是什么,交代好这些就可以开跑了。

    void softTimer_Start(uint16_t id, tmrMode mode, uint32_t delay, callback *cb, void *argv, uint16_t argc)
    {
        assert_param(id < TIMER_NUM);
        assert_param(mode == MODE_ONE_SHOT || mode == MODE_PERIODIC);
        
        timer[id].match = tickCnt_Get() + delay;
        timer[id].period = delay;
        timer[id].state = SOFT_TIMER_RUNNING;
        timer[id].mode = mode;
        timer[id].cb = cb;
        timer[id].argv = argv;
        timer[id].argc = argc;
    }

    上面函数中的assert_param()用于参数检查,类似于库函数assert()。

    3.3.3 更新
    本文中软件定时器有三种状态:停止,运行和超时,不同的状态做不同的事情。停止状态最简单,啥事都不做;运行状态需要不停地检查有没有到期,到期就执行回调函数并进入超时状态;超时状态判断定时器的模式,如果是周期模式就更新到期时间,继续运行,如果是单次模式就停止定时器。这些操作都由一个更新函数来实现:

    void softTimer_Update(void)
    {
        uint16_t i;
        
        for(i=0; i<TIMER_NUM; i++) {
          switch (timer[i].state) {
              case SOFT_TIMER_STOPPED:
                  break;
            
              case SOFT_TIMER_RUNNING:
                  if(timer[i].match <= tickCnt_Get()) {
                      timer[i].state = SOFT_TIMER_TIMEOUT;
                      timer[i].cb(timer[i].argv, timer[i].argc);       //执行回调函数
                  }
                  break;
                
              case SOFT_TIMER_TIMEOUT:
                  if(timer[i].mode == MODE_ONE_SHOT) {
                      timer[i].state = SOFT_TIMER_STOPPED;
                  } else {
                      timer[i].match = tickCnt_Get() + timer[i].period;
                      timer[i].state = SOFT_TIMER_RUNNING;
                  }
                  break;
            
              default:
                  printf("timer[%d] state error!\r\n", i);
                  break;
          }
      }
    }

    3.3.4 停止
    如果定时器跑到一半,想把它停掉,就需要一个停止函数,操作很简单,改变目标定时器的状态为停止即可:

    void softTimer_Stop(uint16_t id)
    {
        assert_param(id < TIMER_NUM);
        timer[id].state = SOFT_TIMER_STOPPED;
    }

    3.3.5 读状态
    又如果想知道一个定时器是在跑着呢还是已经停下来?也很简单,返回它的状态:

    uint8_t softTimer_GetState(uint16_t id)
    {
        return timer[id].state;
    }

    或许这看起来很怪,为什么要返回,而不是直接读?别忘了在前面3.2节中定义的定时器数组是个静态全局变量,该变量只能被当前源文件访问,当外部文件需要访问它的时候只能通过函数返回,这是一种简单的封装,保持程序的模块化。

    3.4 测试
    最后,当然是来验证一下我们的软件定时器有没达到预想的功能。定义三个定时器:定时器TMR_STRING_PRINT只执行一次,1s后在串口1打印一串字符;定时器TMR_TWINKLING为周期定时器,周期为0.5s,每次到期都将取反LED0的状态,实现LED0的闪烁;定时器TMR_DELAY_ON执行一次,3s后点亮LED1,跟第一个定时器不同的是,此定时器的回调函数是个空函数nop(),点亮LED1的操作通过主循环中判断定时器的状态来实现,这种方式在某些场合可能会用到。

    static uint8_t data[] = {1,2,3,4,5,6,7,8,9,0};

    int main(void)
    {
        USART1_Init(115200);
        TIM4_Init(TIME_BASE_MS);
        TIM4_NVIC_Config();
        LED_Init();
        
        printf("I just grabbed a spoon.\r\n");
        
        softTimer_Start(TMR_STRING_PRINT, MODE_ONE_SHOT, 1000, stringPrint, data, 5);
        softTimer_Start(TMR_TWINKLING, MODE_PERIODIC, 500, LED0_Twinkling, NULL, 0);
        softTimer_Start(TMR_DELAY_ON, MODE_ONE_SHOT, 3000, nop, NULL, 0);
        
        while(1) {
            softTimer_Update();
            if(softTimer_GetState(TMR_DELAY_ON) == SOFT_TIMER_TIMEOUT) {
                LED1_On();
            }
        }
    }
    --------------------- 
    作者:马可波罗包 
    来源:CSDN 
    原文:https://blog.csdn.net/qq_26904271/article/details/83833168 
    版权声明:本文为博主原创文章,转载请附上博文链接!

    展开全文
  • 通用软件定时器是通过一个硬件定时器产生固定的时钟节拍,每次硬件定时器中断时,就对链表中处于运行状态的软件定时器的计数值进行递减,并判断是否递减到0以判断是否超时,如超时则执行相应的回调函数。在执行完...

    目录

    一、软件定时器功能描述

    二、使用场景

    三、使用注意事项

    四、数据结构

    五、操作函数声明

    六、具体实现


    一、软件定时器功能描述

    通用软件定时器是通过一个硬件定时器产生固定的时钟节拍,每次硬件定时器中断时,就对链表中处于运行状态的软件定时器的计数值进行递减,并判断是否递减到0以判断是否超时,如超时则执行相应的回调函数。在执行完超时回调函数后,如该软件定时器模式为单次定时则将定时器状态置为完成状态;如该软件定时器为周期定时则将定时器状态置为运行状态,并重新装载软件定时器的计数值为周期时间,重新开始计数。

    二、使用场景

    在硬件定时器资源受限,但又有较多任务需要周期处理,对执行周期要求不是很严格的情况下,可以由一个硬件定时器产生固定的时钟节拍,模拟出多个软件定时器来单次或周期性的执行相应的任务,这样就不会受限于硬件资源的不足

    三、使用注意事项

    1、不要在软件定时器的超时回调函数中做一些耗时的处理,比如延时、多次循环等

    2、如果使用了RTOS,并在超时回调函数中调用了RTOS的API,需要注意是否有只能在中断中调用的限制,如FreeRTOS中就有专门在中断中使用的释放信号量、发送消息到消息队列的API

    3、需要根据硬件定时器周期重新设置软件定时器的时钟节拍

    四、数据结构

    /* It needs to be modified according to the user's own environment before use !!!!!!!*/
    #define SOFT_TIMER_TICK_MS      5
    #define SOFT_TIMER_MALLOC(size) pvPortMalloc(size)
    #define SOFT_TIMER_FREE(p)      vPortFree(p)
    
    /* soft timer unit */
    #define SOFT_TIMER_UNIT_MIN           1
    #define SOFT_TIMER_UNIT_S             2
    #define SOFT_TIMER_UNIT_MS            3
    
    /* soft timer mode */
    #define SOFT_TIMER_MODE_UNUSED        0
    #define SOFT_TIMER_MODE_SIGLE         1
    #define SOFT_TIMER_MODE_RERIOD        2
    
    /* soft timer status */
    #define SOFT_TIMER_STATUS_UNUSED      0
    #define SOFT_TIMER_STATUS_STOP        1
    #define SOFT_TIMER_STATUS_RUNNING     2
    #define SOFT_TIMER_STATUS_COMPLETED   3
    
    struct soft_timer
    {
    	struct soft_timer *next; /* timer list */
    	unsigned char mode;      /* timer mode */
    	unsigned char status;    /* timer status */
    	unsigned char unit;      /* set unit:min/s/ms */
    	unsigned long timeout;   /* soft timer period(ms) */
    	unsigned long count;     /* timeout decrement value */
        void *param;             /* timeout_callback param */
    	void (*timeout_cb)(void *param);/* timeout_callback */
    };
    

    五、操作函数声明

    extern struct soft_timer *creat_soft_timer(void);
    extern void soft_timer_del   (struct soft_timer *this_timer);
    extern void soft_timer_start (struct soft_timer *this_timer);
    extern void soft_timer_stop  (struct soft_timer *this_timer);
    extern void soft_timer_reload(struct soft_timer *this_timer);
    
    extern void soft_timer_config(struct soft_timer *this_timer, \
                                  unsigned long timeout, \
                                  unsigned char mode, \
                                  unsigned char unit, \
                                  void *param,\
                                  void (*timeout_cb)(void *param));
    
    extern void soft_timer_set_timeout(struct soft_timer *this_timer, unsigned long timeout);
    extern unsigned char get_soft_timer_status(struct soft_timer *this_timer);
    extern void soft_timer_exe(void);

    六、具体实现

    #include  <stdlib.h>
    #include  <stdio.h>
    #include "drv_soft_timer.h"
    
    static struct soft_timer *soft_timer_list_head = NULL;/*the head of the soft timer list*/
    
    /**
     * dynamically create a soft timer.
     * 
     * @param NONE
     * @return NULL:malloc fail
     *        !NULL:the soft timer
     */
    struct soft_timer *creat_soft_timer(void)
    {
    	struct soft_timer *new_timer = NULL;
    	struct soft_timer *p_timer = NULL;
    	
    	/* create a new soft timer */
    	new_timer = SOFT_TIMER_MALLOC(sizeof(struct soft_timer));
    	
    	/* does the new soft timer created success */
    	if (new_timer == NULL)
    	{
    		return NULL;
    	}
    	else
    	{
    		new_timer->timeout = 0;
    		new_timer->count = 0;
    		new_timer->mode = SOFT_TIMER_MODE_UNUSED;
    		new_timer->status = SOFT_TIMER_STATUS_UNUSED;
    	}
    	
    	/* does the soft timer list already exit */
    	if (soft_timer_list_head == NULL)
    	{
            new_timer->next = NULL;
    		soft_timer_list_head = new_timer;
    	}
    	else
    	{
    		/* add the new soft timer to the end of the soft timer list */
    		p_timer = soft_timer_list_head;
    		while(p_timer->next != NULL)
    		{
    			p_timer = p_timer->next;
    		}
            new_timer->next = NULL;
    		p_timer->next = new_timer;
    	}
    	
    	return new_timer;
    }
    
    /**
     * delete a soft timer from the timer list.
     * 
     * @param this_timer:soft timer to be deleted 
     * @return NONE
     */
    void soft_timer_del(struct soft_timer *this_timer)
    {
    	struct soft_timer *cur_timer = soft_timer_list_head;
    	struct soft_timer *pre_timer = NULL;
    	
    	/* does this soft timer is vaild */
    	if (this_timer == NULL)
    		return;
    	
    	/* find this timer delete it from timer list */
    	while (cur_timer != NULL)
    	{
    		if (cur_timer == this_timer)
    		{
    			/* head node is the find soft timmer */
    			if (cur_timer == soft_timer_list_head)
    				soft_timer_list_head = cur_timer->next;
    			else
    				pre_timer->next = cur_timer->next;
    			
    			cur_timer->next = NULL;
    			SOFT_TIMER_FREE(cur_timer);
    			cur_timer = NULL;
    			break;
    		}
    		pre_timer = cur_timer;
    		cur_timer = cur_timer->next;
    	}
    }
    
    /**
     * start the soft timer.
     * 
     * @param this_timer:soft timer to start
     * @return NONE
     */
    void soft_timer_start(struct soft_timer *this_timer)
    {
        if (this_timer->timeout == 0)
          return;
        
    	this_timer->status = SOFT_TIMER_STATUS_RUNNING;
        this_timer->count  = this_timer->timeout;
    }
    
    /**
     * stop the soft timer.
     * 
     * @param this_timer:soft timer to stop
     * @return NONE
     */
    void soft_timer_stop(struct soft_timer *this_timer)
    {
    	this_timer->status = SOFT_TIMER_STATUS_STOP;
    }
    
    /**
     * reload the soft timer.
     * 
     * @param this_timer:soft timer to reload
     * @return NONE
     */
    void soft_timer_reload(struct soft_timer *this_timer)
    {
        if (this_timer->timeout == 0)
          return;
        
    	this_timer->status = SOFT_TIMER_STATUS_RUNNING;
        this_timer->count  = this_timer->timeout;
    }
    
    /**
     * config the soft timer.
     * 
     * @param this_timer:soft timer to config
     * @param timeout:soft timer period
     * @param mode:SOFT_TIMER_MODE_SIGLE  SOFT_TIMER_MODE_RERIOD
     * @param unit:SOFT_TIMER_UNIT_MIN    SOFT_TIMER_UNIT_S    SOFT_TIMER_UNIT_MS
     * @param param:timeout callback param
     * @param timeout_cb:timeout callback
     *
     * @return NONE
     */
    void soft_timer_config(struct soft_timer *this_timer, \
                                  unsigned long timeout, \
                                  unsigned char mode, \
                                  unsigned char unit, \
                                  void *param,\
                                  void (*timeout_cb)(void *param))
    
    {
        this_timer->mode  = mode;
        this_timer->unit  = unit;
        this_timer->param = param;
        this_timer->timeout_cb = timeout_cb;
    	soft_timer_set_timeout(this_timer, timeout);
    }
    
    /**
     * set the soft timer timeout.
     * 
     * @param this_timer:soft timer to config
     * @param timeout:soft timer period
     *
     * @return NONE
     */
    void soft_timer_set_timeout(struct soft_timer *this_timer, unsigned long timeout)
    {
    	/* since the increments are ms, different units of timing need to be converted to ms */
    	if (this_timer->unit == SOFT_TIMER_UNIT_MS)
    		this_timer->timeout = timeout;
    	else if (this_timer->unit == SOFT_TIMER_UNIT_S)
    		this_timer->timeout = timeout*1000;
    	else if (this_timer->unit == SOFT_TIMER_UNIT_MIN)
    		this_timer->timeout = timeout*60*1000;
    }
    
    /**
     * get the soft timer status.
     * 
     * @param this_timer:soft timer to get
     *
     * @return soft timer status
     */
    unsigned char get_soft_timer_status(struct soft_timer *this_timer)
    {
    	return this_timer->status;
    }
    
    /**
     * soft timer execute.
     * 
     * @param NONE
     *
     * @return NONE
     */
    void soft_timer_exe(void)
    {
        struct soft_timer *p_timer = soft_timer_list_head;
    	
    	/* effective timer */
    	while (p_timer != NULL)
    	{
    		if (p_timer->status == SOFT_TIMER_STATUS_RUNNING)
    		{
    			/* Counting time decrement */
    			if (p_timer->count < SOFT_TIMER_TICK_MS)
    				p_timer->count = 0;
    			else
    				p_timer->count -= SOFT_TIMER_TICK_MS;
    			
    			/* time out */
    			if (p_timer->count == 0)
    			{
    				if (p_timer->mode == SOFT_TIMER_MODE_SIGLE)
    				{
    					p_timer->status = SOFT_TIMER_STATUS_COMPLETED;
    					if (p_timer->timeout_cb != NULL)
    						p_timer->timeout_cb(p_timer->param);
    				}
    				else if (p_timer->mode == SOFT_TIMER_MODE_RERIOD)
    				{
    					p_timer->status = SOFT_TIMER_STATUS_RUNNING;
    					p_timer->count = p_timer->timeout;
    					if (p_timer->timeout_cb != NULL)
    						p_timer->timeout_cb(p_timer->param);
    				}
    			}
    		}
    		
    		p_timer = p_timer->next;
    	}
    }

     

     

    展开全文
  • 讲到定时器,大家多多少少都会接触到硬件定时器,但是由于有时候资源的限制,又难免会出现使用软件定时器的情况,但是讲定时器需要从硬件定时器开始讲,软件定时器是在其基础之上延伸出来的。 硬件定时器 1.一般硬件...

    概括

    硬件定时器很精确,软件定时器无论如何都有延迟,主要用在不需要精确定时的地方,而且软件定时比较浪费单片机资源。
    梳理
    讲到定时器,大家多多少少都会接触到硬件定时器,但是由于有时候资源的限制,又难免会出现使用软件定时器的情况,但是讲定时器需要从硬件定时器开始讲,软件定时器是在其基础之上延伸出来的。

    硬件定时器

    1.一般硬件定时器集成在CPU的内部,有的可以使用外置的硬件定时器芯片,可以人为通过编程来设置硬件定时器的工作频率,硬件定时器一旦设定好了工作频率,只要上电,那么硬件定时器就会周期性的给CPU输出一个中断信号,称这个中断信号为时钟中断,linux内核已经实现好了时钟中断对应的服务程序,这个服务程序也称之为时钟中断服务函数,既然硬件定时器周期性的给CPU产生时钟中断,那么对应的中断服务程序就会被内核周期性的调用;

    2.硬件延时利用定时器/计数器芯片,或用微控制器内部的定时器/计数器,实际上,它就是对晶振的分频(分频系数可编程设置),得到一个精确的低频的周期信号,用这个周期信号(比如10ms)去触发中断,每10ms调用一次定时中断服务程序。在定时中断服务程序中加入计数变量,就可以得到任意的定时了。在10ms没有到时,微控制器可以运行其它程序,10ms到时再自动进去中断服务程序以处理定时任务,不会像软件延时阻塞了。

    软件定时器

    软件定时器是在硬件定时器基础之上出来的定时器,可以由一个硬件定时器模拟出成千上万个软件定时器,这样程序在需要使用较多定时器的时候就不会受限于硬件资源的不足,这是软件定时器的一个优点,即数量不受限制。但由于软件定时器是通过程序实现的,其运行和维护都需要耗费一定的CPU资源,同时精度也相对硬件定时器要差一些。
    实现方法
    典型的实现方法是:通过一个硬件定时器产生固定的时钟节拍,每次硬件定时器中断到,就对一个全局的时间标记加一,每个软件定时器都保存着到期时间,程序需要定期扫描所有运行中的软件定时器,将各个到期时间与全局时钟标记做比较,以判断对应软件定时器是否到期,到期则执行相应的回调函数,并关闭该定时器。
    注意
    因为是在内核态中进行的中断服务,我们定义全局变量需要使用volatile,前面的博客也有讲到他的用法,明确有三种情况,否则造成定时器失败。如:

    volatile uint32_t whiole_cnt;
    void tickCnt_Update(void)
    {
        whiole_cnt++;
    }
    

    定一个我们需要的回调函数:argv,argc为其参数:

    typedef void callback(void *argv ,uint16_t argc);
    

    一旦开始运行,whiole_cnt将不停地加一,而每个软件定时器都记录着一个到期时间,只要whiole_cnt大于该到期时间,就代表定时器到期了。
    上面的代码都能理解,这时如果我们需要一个定时,需要确定哪些东西呢?

    typedef struct time
    {
    	unsigned char state;//此软件定时的状态
    	unsigned char mode;//单次计时还是循环计时
    	uint32_t timer_cnt;//此软件计时值与whiole_cnt比较
    	uint32_t period;//循环计时时时间间隔
    	callback *cb;//回调函数指针
    	void *argv;//回调函数参数,根据实际情况可增可减
    	uint16_t argc;//回调函数参数,根据实际情况可增可减
    }_timer;
    

    这是单个软件定时器的基本情况,如果需要多个,可以这样:
    定义两个定时器数组

    #define NUM 2
    _timer TIME[NUM];
    

    如上只是一个框架,我们需要先对他初始化,以免影响到后期使用,
    初始化函数:

    void TIME_NUM_INIT(void)
    {
    	int i=0; 
    	for( i ;i<NUM;i++)//循环初始化结构体
    	{
    		TIME[i].timer_cnt=0;
    		TIME[i].period=0;
    		TIME[i].state=stop;
    		TIME[i].mode=once;
    		TIME[i].cb=NULL;
    		TIME[i].argv=NULL;
    		TIME[i].argc=0;
    	}
    }
    

    TIME[i].state有三种状态:

    typedef enum state
    {
    	stop=1,
    	runing,
    	time_out
    }timer_status;
    

    TIME[i].mode有两种状态:

    typedef enum mode
    {
    	once=1,
    	on_once
    }timer_mode;
    

    初始化完,实际上还没有真正的定时器出现,下一步做的就是,封装结构体,构造单独的定时器:

    void TIME_START(uint8_t id,uint32_t timer,uint32_t delay,unsigned char mode,void *cb,void *argv,uint16_t argc)
    {
    	TIME[id].timer_cnt=timer;
    	TIME[id].period=delay;
    	TIME[id].mode=mode;
    	TIME[id].cb=cb;
    	TIME[id].argv=argv;
    	TIME[id].argc=argc;
    	TIME[id].state=runing;
    }
    

    根据定义的NUM值,分别初始化,初始化后就可以跑了,如:想要定时一个500ms的软件定时器,且此时定时器中断的时间为10ms,则需要50个中断即可:

    TIME_START(0,50,0,once,Time ,"12345",15);
    

    Time是回调函数指针:
    argv,argc为其参数

    int *Time(void *argv,uint16_t argc)
    {
    	printf("运行状态:%d",time_state(0));
    	printf("%s,%d\r\n",argv,argc);
    	
    }
    

    初始化完成后,我们需要一直扫描软件定时器,主要的原因就是跟定时器中断的变量作比较,判断是否定时时间到!被扫描的函数是这样的:

    void check_time(void)
    {
    	uint16_t i=0;
    	  for(i;i<NUM;i++)
    			{
    				switch(TIME[i].state)
    				{
    					case stop:
    						break;
    						
    					case runing:	
    						if(TIME[i].timer_cnt<=whiole_cnt)
    						{
    							TIME[i].state=time_out;
    							TIME[i].cb(TIME[i].argv,TIME[i].argc);
    						}
    						break;
    						
    					case time_out:
    						if(TIME[i].mode==once)
    						{
    							TIME[i].state=stop;
    						}
    						else
    						{
    							TIME[i].timer_cnt=whiole_cnt+TIME[i].period;
    							TIME[i].state=runing;
    						}
    						break;
    					default:break;		
    				}
    			}
    }
    

    我们在主函数中定义一个while(1),一直扫描,方可达到效果。
    主函数main.c

    int main(void)
    {
    	
    	LED_Init();
    	delay_init();
    	timer_init();
    	usart_init(115200);
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
       
    	TIME_NUM_INIT();//初始化
    
    	TIME_START(0,2,2,on_once,Time ,"12345",15);//定时器1
    	TIME_START(1,4,2,once,Time ,"6789",30);//定时器2初始化
    	
    	 while(1)
    	 { 
    		 check_time();
    		 if(TIME[1].state==stop)//如果软件定时器1定时时间到,LED0反转
    		 {
    			  LED0=!LED0;
    		 }
    	 }
    }
    

    别忘记了回调函数!

    总结

    不管哪种模式,定时器到期后,都将执行回调函数,以下是该函数的定义,参数指针argv为void指针类型,便于传入不同类型的参数。

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

    2019-06-22 08:47:04
    在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。

     

    未完待续。。。

    展开全文
  • 软件定时器的使用

    2020-05-20 10:23:05
    void SoftTimerHandler(void) { TimerFuncPtr func; rt_tick_t tick; int i; if(g_TimerNbr==0) return; tick = rt_tick; /* Find one time slot */ for(i=0; i<MAX_TIMER_NUM;...=NU.
  • LiteOS 软件定时器

    千次阅读 2019-06-03 16:16:22
    软件定时器,是基于系统Tick时钟中断,且由软件来模拟的定时器,当经过设定的Tick时钟计数值后触发用户定义的回调函数(类似硬件的中断服务函数) 定时精度与系统Tick时钟的周期有关 定时器的定时周期,是两次触发回...
  • 软件定时器是FreeRTOS中的一个重要模块,使用软件定时器可以方便的实现一些与超时或周期性相关的功能,本篇从FreeRTOS的源码入手,来分析FreeRTOS软件定时器的运行机理。 1 基础知识 1.1 软件定时器与硬件定时器的...
  • 软件定时器3

    2019-07-20 09:23:23
    定时器应该是很常用的一个功能了,很多地方都需要。然而硬定时器就那么几个。所以面对一些对时间精度要求没那么高的地方,软定时器就很有用了。 简单描述 所谓软定时器,不过借助硬定时器产生一个累积计数值。然后...
  • 简单实用软件定时器

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

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

    2020-01-15 15:01:33
    都说程序设计 = 算法 + 数据结构。数据结构是挨踢必修课,不过好像学了数据...不知道做啥,就写个软件定时器软件定时器数据结构 typedef struct __software_timer{ u32 timeout; //初始化时间计数器 u32 rep...
  • 【UCOSIII】UCOSIII软件定时器

    千次阅读 2018-06-29 19:15:40
    在学习STM32的时候会使用定时器来做很多定时任务,这个定时器是单片机自带的,也就是硬件定时器,在UCOSIII中提供了软件定时器,我们可以使用这些软件定时器完成一些功能,本文我们就讲解一下UCOSIII软件定时器。...
  • UCOSII-软件定时器

    2019-11-19 11:00:29
    UCOSII系统支持的软件定时器采用回调函数的方式执行,简单好用!本文简单介绍了软件定时器的基本工作原理以及配置步骤,供大家参考,谢谢!
  • 软件定时器使用模板小结: //!!!全局变量!!! os_timer_t OS_Timer_1; //①:定义软件定时器(os_timer_t型结构体) void ICACHE_FLASH_ATTR OS_Timer_1_cb(void) // ②:定义回调函数 { ... //...
  • 软件定时器

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

    千次阅读 2015-08-13 16:44:39
    本质软件定时器本质上是一个周期性的任务或单次执行任务。A software timer (or just a 'timer') allows a function to be executed at a set time in the future. The function executed by the timer is called ...
  • FreeRTOS 的软件定时器工作模式有两种:单次触发 和 自动重载。 1. 主要功能函数介绍 官方API文档:FreeRTOS 软件定时器 API文档 最为常用的函数有: // 创建软件定时器,返回 TimerHandle_t句柄 TimerHandle...
  • 单片机软件定时器使用方法

    千次阅读 2016-05-30 22:45:39
    单片机软件定时器使用方法 本文讲述了一种单片机软件定时器使用方法
  • FreeRTOS学习笔记九【软件定时器

    千次阅读 2019-01-19 14:53:14
    目的软件定时器的属性与状态定时周期一次性定时器和固定频率定时器软件定时器的状态软件定时器的上下文软件定时器的回调函数RTOS守护程序(定时服务)任务定时器命令队列守护程序任务的调度软件定时器使用...
  • uCosIII之软件定时器使用

    千次阅读 2017-12-25 11:29:16
    采用uCosIII软件定时器实现调速设备速度平滑控制(采用无初始定时周期模式)和调速设备速度阶梯延时控制(采用有初始定时周期模式)
  • FreeRTOS软件定时器

    2019-12-18 17:44:44
    软件定时器用于在将来的设定时间或以固定频率定期计划功能的执行。软件定时器执行的功能称为软件定时器的回调函数。软件定时器由FreeRTOS内核实现并受其控制。它们不需要硬件支持,并且与硬件计时器或硬件计数器无关...
  • STM32软件定时器的设计

    千次阅读 2016-06-01 11:25:47
    定时器分为硬件定时器和软件定时器,几乎所有的微控制器上都配备了数量有限的硬件定时器,即控制器本身有专门实现定时的模块。几乎所有的硬件定时器的工作原理都是一样的:定时器在外部时钟提供的周期脉冲下进行计数...
  • 软件定时器实现源码

    千次阅读 2016-10-09 15:33:18
    原理:软件定时器的原理其实很简单,就是在启动定时器的时候获取当前系统的时间戳start_ts,然后用start_ts加上要定时的时间dly_ts(timeout= start_ts + dly_ts),在心跳定时器中断中查询当前时间计数值(时间戳)...
  • 绪论 软件定时器用于以后的固定时间,或以一个固定频率周期运行的...软件定时器函数是可选的,使用软件定时器需要以下操作: 1,编译源文件中的FreeRTOS/Source/timers.c 。 2,将FreeRTOSConfig.h中的 configUSE_T...
  • 本文介绍了软件定时器的特点和原理,并从时钟节拍,数据结构,定时器操作等角度分析,实现了基于STM32的软件定时器,该软件定时器具有两种模式:单次模式和周期模式,到期后将自动执行回调函数,文章的最后通过具体...
  • 软件定时器是由操作系统提供的一:类系统接口,它构建在硬件定时器基础之上(系统滴答定时器)。软件定时器使系统能够提供不受数目限制的定时器服务。 RT-Thread操作系统提供的软件定时器,以系统节拍(OSTick)的时间...
  • freertos软件定时器

    2020-04-28 21:30:59
    我们将会讲解一个利用任务延时实现的软件定时器. 1.创建 timer结构体和创建一目了然,看看初始化函数 是不是有点熟悉.timer的管理有点类似任务的管理. 如果是第一次创建则初始化timer列表和timer队列,...
  • 单片机软件定时器

    2018-06-22 19:28:00
    2019独角兽企业重金招聘Python工程师标准>>> ...
  • ucosIII 软件定时器

    千次阅读 2015-12-18 22:03:39
    软件定时器 定时器本质是递减计数器,当计数器减到零时可以触发某种动作的执行,这个动作通过回调函数来实现。当定时器计时完成时,定义的回调函数就会被立即调用,应用程序可以有任意数量的定时器,UCOSIII中定
  • STM32CubeMX FreeRTOS软件定时器实验

    千次阅读 2018-02-05 22:51:10
    软件: 1、SW4STM32 2、STM32CubeMX 关于这两个软件在linux的安装教程请看我另一篇博文 Linux/Windows配置stm32免费开发环境详细流程 sw4stm32比较难下载,在此提供百度网盘链接: 链接:...

空空如也

1 2 3 4 5 ... 20
收藏数 52,053
精华内容 20,821
关键字:

软件定时器