最简单的51单片机软件定时器的编写

为什么要使用软件定时器?

  • 定时器在我们单片机编程时必不可少,虽然现在已经有很多单片机的定时器数量功能数量十分的丰富。但是在一些环境下(例如一些系统),总不能一个任务调一个硬件定时器中断吧?那如果有几十个任务呢?
  • 所以就有了软件定时器存在的必要。软件定时器的优点在于,数量可以根据用户需求而定,不需要频繁的配置,维护性较高。缺点在于时间误差较大。
    但是,对于一些延时要求不是特别高的任务来说,软件定时器无疑是一个不错的选择。
  • 我最近学习了一下软件定时器的实现原理,自己在51单片机上面实现了,软件定时器的主要功能(已经可以使用)。
  • 初识者,只需阅读本文章,即可知道软件定时器的一个原理,紧接着可以尝试自己去编写一个软件定时器。(希望文章可以帮到你,大佬可以直接划过了,另外大家有什么意见可以在评论区下留言讨论,一个人的思维是有限的)

不说废话,上代码讲解

软件定时器实现基础流程

main.c

读者只需要关心add_soft_timer()以及soft_timer_process()函数

u16 led_cb(void)//led引脚翻转函数
{
	static u8 flag=0;
	
	flag=!flag;
	Write_GPIO_Value(GPIO2,PIN_5,flag);
	return 0;
}

void main(void)
{
	GPIO_Init();									//IO口初始化
	Timer_Init();									//定时器初始化
	Uart_Init();									//串口初始化1
	soft_timer_init();								//软件定时器初始化
	SET_ALL_IRQ(IRQ_ENABLE);						//打开总中断

	add_soft_timer(&led_cb,200,FOREVER);//添加一个200ms的软件定时器回调函数
	
	while(1)
	{
		soft_timer_process();//软件定时器轮询函数	
	}
}

soft_timer.h

#ifndef __SOFT_TIMER_H
#define __SOFT_TIMER_H

#include "user.h"
#include "timer.h"

#define SOFT_TIMER_NUM	10	//定时器最大个数
/*
1)TIMER_START:记录定时器的状态(无效态、运行态、溢出态);
2)TIMER_MODE:记录定时器时的模式(单次、循环);
3)TIMER_STR:定时器的所有参数都包含在该结构体内;
4)typedef u16 (*timer_cb)(void) :为一个返回值为u16,无形参的函数指针类型,定义新名;
*/

typedef u16 (*timer_cb)(void);

typedef enum {

	INVALID=0,//无效态,表示定时器不存在
	RUNING,		//运行态
	TIMEOUT,	//超时态

}TIMER_START;

typedef enum {
	
	ONCE = 0,	//单次
	FOREVER,	//循环
	
}TIMER_MODE;


typedef struct{
	u16 cycle;			//定时器周期		0~65535	units:100us
	u16 tick;			//当前计数值		0~65535	units:100us
	TIMER_START status;	//定时器状态		运行/停止/溢出
	TIMER_MODE	mode;	//定时器模式		1次/无限次
	timer_cb cb;		//回调函数
}TIMER_STR;

void soft_timer_init(void);
int add_soft_timer(timer_cb *cb,u16 cyc,TIMER_MODE mode);
void soft_timer_process(void);

#endif

soft_timer.c

#include "soft_timer.h"
#include "uart.h"
#include "gpio.h"

//我使用51单片机内存不够,其它芯片无需放在外部存储器
xdata TIMER_STR timer_real[SOFT_TIMER_NUM];//定义结构体数组,10个。用于存放每个定时器的参数
u16 base_timer_cnt;//计数变量

//中断服务,获取时间	:计数单位1ms
void Soft_Timer_Base_Function(void) interrupt 19
{	//1ms自加1,周而复始。溢出后会重0开始继续加
	base_timer_cnt++;
}

//初始化定时器时基设置
void soft_timer_init(void)
{
	memset(&timer_real,0,sizeof(timer_real));	//结构体清除,就是参数全部清0
	Timer3Init();								//初始化定时器
}

//获取base_timer_cnt计数器的值,该值在定时器中断内不断加1
u16 get_cur_tick(void)
{
	return base_timer_cnt;
}

//添加定时器
int add_soft_timer(timer_cb cb,u16 cyc,TIMER_MODE mode)	//回调、定时周期、定时模式
{
	u8 i;
	u16 cur_tick=get_cur_tick();//获取计数值
	
	//遍历数组,查找是否存在空位,可以被插
	for(i=0;i<SOFT_TIMER_NUM;i++)
	{
		if(INVALID==timer_real[i].status)
		{
			//寻找位置插空
			timer_real[i].cycle	=	cyc;
			timer_real[i].tick	=	cur_tick+cyc;
			timer_real[i].cb		= cb;		//添加回调函数
			timer_real[i].mode	=	mode;
			timer_real[i].status=	RUNING;//添加后进入运行态
			return 0;
		}
	}
	return -1;//没有空位置了
}

//轮询处理:核心函数
void soft_timer_process(void)
{
	u8 i;
	u16 get_tick=get_cur_tick();//获取当前计数值
	int diff;
	
	for(i=0;i<SOFT_TIMER_NUM;i++)//数组的每一项遍历
	{	
			if(INVALID==timer_real[i].status)continue;//此定时器不存在,则结束本次循环,开始新循环
			
			if(RUNING==timer_real[i].status)//如果,定时器i,处于运行态
			{
				/*
				此处,我定义了一个int类型的diff变量,表示差值
				没有直接比较base_timer_cnt与timer_real[i].tick值的原因在于
				base_timer_cnt变量存在溢出的情况(就是到达65535后,接着变为0)
				那么此时比较大小显然是不行的。
				而用差值原因在于,a-b实际上是被当做a+(-b)来处理的。
				那么即使是存在变量溢出,最终得到的差值也是理想差值。(具体读者可以去查补码的知识)
				*/
				diff=get_tick-timer_real[i].tick;//int类型
				if(diff > 0)//溢出了
				{
					timer_real[i].status=TIMEOUT;//标记溢出
				}
			}
			
			if(TIMEOUT==timer_real[i].status)//如果,定时器i被标记溢出,则进行如下处理
			{
				timer_real[i].cb();		//执行回调任务(执行我们的任务)
				
				if(ONCE==timer_real[i].mode)//单次模式
				{
					memset(&timer_real[i],0,sizeof(TIMER_STR));//清除定时器i的结构体
				}
				else if(FOREVER==timer_real[i].mode)//循环模式
				{
					timer_real[i].status=RUNING;//状态跳转会运行态
					timer_real[i].tick=base_timer_cnt+timer_real[i].cycle;//记录下一个周期值
				}
			}
			
	}
}

//定时器删除,不一定使用到
int delete_soft_timer(u8 idex)
{
	//判断是否存在
	if(INVALID==timer_real[idex].status)
	{
		memset(&timer_real[idex],0,sizeof(TIMER_STR));	//结构体清除
	}
	
	return -1;//定时器不存在,所以无法删除
}

说明:本程序是在学习的时候编写,适当参考网上代码,如涉及到版权问题,可以联系我。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值