java定时器的使用(timer)_单片机高级编程之软定时器的实现

提到定时器,大家肯定都不陌生,每个学习单片机的人都需要熟练掌握其定时器的使用,因为它实在太常用了。通常的使用方法就是初始化一个硬件定时器,让其在固定的时间后产生一个定时器中断,然后在定时器中断服务程序(ISR)里面置位,累加或累减某个变量,在while(1)中判断这个变量,当其值满足想要的条件,即需要定时的时间,就执行相应的功能。当然如果功能执行的语句比较少,也可直接在ISR中执行。当程序中有多个地方需要定时,那就多申请几个变量,在while(1)中多几个判断。

那么问题来了,有没有一种方法能将上述的申请变量,判断变量,执行功能等细节封装成一种固定的接口,让工程师在应用程序开发时简单放心的使用呢?有,那就是软定时器。顾名思义,软定时器就是用软件实现的定时器功能,它基于某个硬件定时器。

84b7356af6c5b5864c00aad4588a0fe3.png

一,重点来了,现在手把手教你怎么实现:

1,在硬件定时器ISR中做一个1毫秒的tick累加

volatile u16 ms_tick=0;

static void TIMER0_ISR(void)

{

........

ms_tick=((ms_tick==0xffff)? 0 : (ms_tick+1));

........

}

2,定义软定时器结构体数组timer_buff[]

#define TIMER_MAX 30 // 定时器最大个数,根据实际需要定义

#define INVALID_ID 0xff // 无效的ID

typedef enum{

MODE_SINGLE , // 单次模式,执行一次后自动关闭

MODE_REPEAT // 重复模式,重复执行,直到手动关闭为止

}mode_t;

typedef struct{

u8 valid; // 定时器是否有效

u8 on; // 定时器的开关

mode_t mode; // 定时器的模式

u16 start_time; 定时器的起始tick

u16 delay; 定时时间,单位毫秒

FuncU32_t cb; 时间到时执行的回调函数

u32 para; 回调函数的参数

}timer_t;

static timer_t timer_buff[TIMER_MAX]={0};

3,定义初始化函数,在系统初始化时调用一次

void sw_timer_init(void)

{

memset(timer_buff,0,sizeof(timer_buff));

}

4,定义定时器启动函数

static u8 valid_timer_cnt=0;保存有效定时器的总个数

u8 timer_start(u8 *p_id, mode_t mode, u16 delay, FuncU32_t cb, u32 para)

{

u8 i;

if(!cb)return 0;

if(!delay)return 0;

if(*p_id == INVALID_ID){ 如果ID无效,就重新申请一个

if(valid_timer_cnt>=TIMER_MAX)return 0; 如果定时器个数满,返回申请失败

for(i=0;i

if(!timer_buff[i].valid){

*p_id=i;

timer_buff[i].valid=1; 让当前定时器有效

valid_timer_cnt++;

break;

}

}

}

timer_buff[*p_id].mode=mode;

timer_buff[*p_id].delay=delay;

timer_buff[*p_id].cb=cb;

timer_buff[*p_id].para=para;

timer_buff[*p_id].start_time=ms_tick; 保存起始tick

timer_buff[*p_id].on=1; 启动当前定时器

return 1; 返回成功

}

5,定义定时器停止函数

void timer_stop(u8 *p_id)

{

if(*p_id == INVALID_ID)return;

if(!timer_buff[*p_id].on)return;

timer_buff[*p_id].on=0;

}

6,定义定时器清除函数

void timer_delete(u8 *p_id)

{

if(*p_id == INVALID_ID)return;

timer_buff[*p_id].valid=0;

timer_buff[*p_id].on=0;

valid_timer_cnt--;

*p_id=INVALID_ID;

}

7,定义获取有效定时器的总个数

u8 timer_num_get(void)

{

return valid_timer_cnt;

}

8,定义定时器处理task,放在while(1)中,具体思路,研读代码即可明白

void timer_task(void)

{

static u8 i;

static u32 temp;

for(i=0;i

if(timer_buff[i].valid && timer_buff[i].on){

if(ms_tick >= timer_buff[i].start_time){

temp=timer_buff[i].start_time;

temp += timer_buff[i].delay;

if(ms_tick >= temp){

if(timer_buff[i].mode == MODE_SINGLE)timer_buff[i].on=0;

timer_buff[i].start_time=ms_tick;

if(timer_buff[i].cb)

timer_buff[i].cb(timer_buff[i].para);

}

}

else{

if((0xffff-timer_buff[i].start_time+ms_tick) >= timer_buff[i].delay){

if(timer_buff[i].mode == MODE_SINGLE)timer_buff[i].on=0;

timer_buff[i].start_time=ms_tick;

if(timer_buff[i].cb)

timer_buff[i].cb(timer_buff[i].para);

}

}

}

}

}

二,以上为整个软定时器功能实现的步骤,下面举例说明一下:

1,在系统初始化函数中调用sw_timer_init()

2, main中执行task

while(1){

timer_task();

}

2,用户程序中定义定时器ID变量 static u8 app_tID=INVALID_ID;需要多少个定时器就定义多少个变量,用户后续对该定时器的一切操作需要传入此变量,一个ID变量对应一个定时器。注意该变量一定要初始化成INVALID_ID,定义以后不得人工更改!

3,启动定时器

static void app_tCB(u32 para)

{

该用户函数每20毫秒被回调一次,参数para=100

}

timer_start(&app_tID,MODE_REPEAT,20,app_tCB,100);

4,停止定时器 timer_stop(&app_tID);

5,删除定时器 timer_delete(&app_tID);

细心的小伙伴可能会发现,该架构中所有的回调参数都是u32,最多只能容纳4个字节,实际上一般应用也足够,用起来简单方便。如果实际参数超过四个字节,可以将参数强转成空指针,即void *pPara,传递一个空类型的地址作为参数,在用户回调函数中按照实际类型再做一次强转即可实现多参数传递。

三,优缺点分析

优点:1,该架构只需占用一个硬件定时器,便可实现几十个甚至上百个定时器的功能,只要内存足够;

2,程序员可以随时灵活的启动,停止,删除定时器,逻辑上很容易控制,有助于实现较复杂的逻辑和DEBUG

3,用简单的几个api将硬件底层跟应用上层解耦,程序员可以更好,更简单的专注于业务逻辑的实现。程序模块化也更容易实现

缺点:1,由于在task中运行,实时性没有硬件定时器ISR高,对于实时性要求很高的场合,比如:信号测量,信号合成等场景,不太适用

2,理论上讲该定时器的最大误差有1个tick,如果tick是1毫秒,最大误差就是1毫秒

关于软定时器,今天就讲到这里,欢迎大家积极留言转发,您的参与是小编的最大动力!

喜欢的小伙伴可以关注我,后面持续有干货呈现哦~~~~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值