写一个软件定时器

在公司做项目的时候,经常有一些需求会用到一些延时,比如说MCU有一个引脚需要延时100ms后拉高,保持200ms后再拉低。最简单的办法就是调用延时函数,delay_ms(t)来实现,但是这个函数实际就是一直在这里等待,啥事都不管了,这很浪费资源,所以我想通过某些方法来实现延时,而且在延时的时候MCU可以去处理其他一些事情,这样才不耽误事。

Cortex-M系列的单片机都有一个滴答定时器,也就是SysTick,一般用于提供系统节拍,很多单片机官方提供的库函数也都是把它配置为1ms中断一次,典型的例子就是stm32和GD32。它们的库函数中一般有以下这个中断服务函数:

void SysTick_Handler(void)
{
    delay_decrement();		
}

  

这个中断服务函数一般是1ms进入一次,所以想到了利用这个中断服务函数来做一个软件定时器的功能:①可以任意添加软件定时器(比如说LED1闪烁(频率100ms),LED2闪烁(频率200ms),LED3闪烁(频率400ms)……);②可以选择时单次计时,或者多次计时,或者连续不间断地计时;③计时时间到后可以根据实际情况执行不同的操作;④代码要简洁,要求把基础代码完善后,每次用到软件定时器时只需要调用少量的接口函数就能实现功能;

代码如下:

MyApplication.h


#ifndef _MYAPPLICATION_H_
#define _MYAPPLICATION_H_


#include "gd32e50x.h"
#include "systick.h"
#include "uart.h"
#include "key.h"
#include "led.h"


#define MAX_TIMER_NUM		10

#define LED_TIMER			0
typedef struct
{
	uint32_t Delay;
	uint32_t Cnt;
	uint8_t State;
	uint8_t Timeout;
	uint8_t Continue;
}SoftTimer;

typedef enum
{
	Disable = 0,
	Enble,
}STATE;

typedef enum
{
	FALSE = 0,
	TRUE,
}TrueOrFalse;

typedef enum
{
	OK = 0,
	Error,
}Result;

extern SoftTimer soft_timer[MAX_TIMER_NUM];

Result Soft_Timer_Add(uint8_t timer,uint32_t delay,uint8_t IsContinue);
void Soft_Timer_Run(void);
void Soft_Timer_Task(uint8_t timer,void(*Handle)(void));


#endif

  

MyApplication.c

#include "MyApplication.h"

SoftTimer soft_timer[MAX_TIMER_NUM];

Result Soft_Timer_Add(uint8_t timer,uint32_t delay,uint8_t IsContinue)
{
	if(timer >= MAX_TIMER_NUM)
	{
		return Error;
	}
	if(soft_timer[timer].State != ENABLE)
	{
		soft_timer[timer].State = ENABLE;
		soft_timer[timer].Delay = delay;
		soft_timer[timer].Cnt = 0;
		soft_timer[timer].Timeout = FALSE;
		soft_timer[timer].Continue = IsContinue;
	}
	return OK;
}


void Soft_Timer_Run(void)
{
	uint8_t i;
	for(i = 0; i < MAX_TIMER_NUM;i++)
	{
		if(soft_timer[i].State != DISABLE)
		{
			soft_timer[i].Cnt++;
			if(soft_timer[i].Cnt >= soft_timer[i].Delay)
			{
				soft_timer[i].Timeout = TRUE;
			}
			
		}
	}
}

void Soft_Timer_Task(uint8_t timer,void(*Handle)(void))
{
	if(soft_timer[timer].State != DISABLE)
	{
		if(soft_timer[timer].Timeout != FALSE)
		{
			Handle();
			if(soft_timer[timer].Continue != FALSE)
			{
				soft_timer[timer].Cnt = 0;
				soft_timer[timer].Timeout = FALSE;
			}
			else
			{
				soft_timer[timer].Cnt = 0;
				soft_timer[timer].Delay = 0;
				soft_timer[timer].Timeout = FALSE;
				soft_timer[timer].Continue = FALSE;
				soft_timer[timer].State = DISABLE;
			}
		}
	}
}

  

应用部分:
gd32e50x_it.c

  void SysTick_Handler(void)
    {
         delay_decrement();
         Soft_Timer_Run();
  }

main.c


void LED_Running(void)
{
	static uint8_t state = 0;

	state++;
	if(state == 5)
		state = 0;
	
	switch(state)
	{
		case 0 : LED1_OFF;LED2_OFF;LED3_OFF;LED4_OFF;
							break;
		
		case 1 : LED1_ON;LED2_OFF;LED3_OFF;LED4_OFF;
							break;
		
		case 2 : LED1_ON;LED2_ON;LED3_OFF;LED4_OFF;
							break;
		
		case 3 : LED1_ON;LED2_ON;LED3_ON;LED4_OFF;
							break;
		
		case 4 : LED1_ON;LED2_ON;LED3_ON;LED4_ON;
							break;
		
	}
}

int main(void)
{
    systick_config();

    led_init();
	
    usart_config();
	
    key_init();
	
    Soft_Timer_Add(LED_TIMER,500,TRUE);
    while(1)
{ Soft_Timer_Task(LED_TIMER,LED_Running); } }

  

这是一个跑马灯的程序,有4个LED灯,实现的功能就是按顺序逐个点亮LED灯,然后熄灭,再重新逐个点亮……频率是500ms,

① Result Soft_Timer_Add(uint8_t timer,uint32_t delay,uint8_t IsContinue)
该函数是添加软件定时器,第一个参数timer实际是一个软件定时器的编号,必须小于宏定义 MAX_TIMER_NUM 的值;第二个参数delay是该软件定时器需要延时的时间,第三个参数是是否连续计数,决定这个定时器计时时间到之后是停止计数或者继续重新计数;成功返回OK,失败返回Error; 
② void Soft_Timer_Run(void)
该函数是核心部分,这个函数在SysTick定时器的中断服务函数中调用,它会轮询各个软件定时器(根据软件定时器编号),看是否是ENABLE状态,如果是,再判断该定时器的计数值是否达到了延时的数值,如果是,即是计时时间到,否则计数值继续+1;如果是DISABLE状态,即是没启用这个定时器,那就不需要执行任何动作;
③ void Soft_Timer_Task(uint8_t timer,void(*Handle)(void));
该函数是判断某个定时器是否已经计时到了,并且执行相关的操作。第一个参数是软件定时器编号,第二个参数是一个函数指针,使用者具体要执行的操作就通过这个指针传入这个函数,在即使时间到之后执行操作。这个函数可以写在主循环中,也可以写在SysTick中断服务函数中,各有利弊,需要使用者自己衡量利弊,写在主循环中,你需要确保主循环中处理的其他事情不会占用很多时间,不然精度就会受到影响,大大降低。写在中断服务函数中,就会增加中断服务函数执行的时间,中断服务函数一般不允许占用太长时间,不然会出问题的,,当然如果你只使用到10个以内的软件定时器,应该不会有问题,这点代码量应该不算太多。

上面的代码还没有完全完善,暂时没有多次计数的功能,只有单次计数和连续不间断计数。有空我还会在完善一下。个人觉得这样会比单纯用delay延时函数好,可以减少占用资源,当然仁者见仁智者见智,也许有人也觉得累赘。

 

posted @ 2021-11-28 22:49  沙河淘金  阅读(181)  评论(0编辑  收藏  举报