简单实用软件定时器

软件定时器

在嵌入式开发中,定时器是及其常见的,但考虑到芯片外设资源有限,可以自己写一个软件定时器,应用于对计时不是太严格的场合,比如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)
    {
    }
}

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值