在nodic-SDK的library中提供了一个好用的app_timer,通过创建timer并在定时时间到执行回调函数的方式简化了在裸机状态下的编程模型。
后参考了nordic官方和同事改版的app_timer随即想把它写的在使用过程中更简化一些。比如有些时候创建的timer只需要启动一次,我就可以省去创建ID并再start的步骤,并且能实现内存的动态管理,让只运行一次的timer不再占ram空间而可以启动更多的timer。
涉及到内存的动态管理,那就要用到链表。所以整体的思路就是当创建一个新的app timer的时候就分配一块儿内存加入链表中,当要把这个timer移除的时候就从列表里面删除。整体的思路就通过下面的图来大概分析一下:
通过宏定义,来配置最多可以创建的APP_TIMER数量,也就是内存区分配多大的空间,如下图所示,假如最多可以创建6-1个:(第一个作为链表的入口)

假如创建了一个timer应用,被分配到了2号的内存空间,整个的链表结构如下,entry指向2号,2号又指回了entry,这时候链表里面共有两个元素。

假设现在又创建一个app timer,被分配在了3号的内存中,链表结构如下:entry连接到2号,2号连接到3号,3号连接回entry,还是一个环状的结构。

再添加一个app timer

既然是动态管理,那么除了有添加就可能还存在删除,这个时候3号的定时器执行了一次就不再需要了,要删除它了。整个结构又变了,entyr->2->4->entry。可以看到3号内存空间被从链表里面踢出了。

整体思路清晰了,看代码就很轻松了,这个是头文件:
#ifndef __APP_TIMER_H
#define __APP_TIMER_H
#include "main.h"
#define MAX_TIMER 10
typedef void (*app_timer_handle)(void);
typedef struct app_timer_struct
{
uint32_t counter;
uint32_t delay;
uint8_t is_rt;
uint8_t is_repeat;
uint8_t pending;
app_timer_handle cb;
struct app_timer_struct* last;
struct app_timer_struct* next;
}app_timer_t;
typedef app_timer_t* APP_TIMER_ID;
void app_timer_init(void);
void app_timer_start(void);
void app_timer_stop(void);
app_timer_t* app_timer_oneshot(app_timer_handle cb,uint32_t delay,uint8_t is_rt);
app_timer_t* app_timer_repeat(app_timer_handle cb,uint32_t delay,uint8_t is_rt);
uint8_t app_timer_delete(APP_TIMER_ID p_timer);
void app_timer_process(void);
void app_timer_exec(void);
#endif
具体的实现可以看下面的c文件,可以解释下头文件的接口:
- 宏定义MAX_TIMER 定了了最大可以创建多少个APP_TIMER
- app_timer_init 用来初始化app_timer
- app_timer_start\ app_timer_stop 这两个接口函数用来启动和关闭app_timerde 运行,同时关闭所有的timer
- app_timer_oneshot 用来创建一个一次触发的timer,当timer的时间到回调函数执行完以后就会自动从管理链表里面删除,is_rt参数代表是否实时,实时的会在app_timer_process中执行,非实时的会被放在app_timer_exec执行
- app_timer_repeat和 app_timer_oneshot 功能类似,只是用来创建可以重复一直周期执行的timer
- app_timer_delete 删除一个timer,删除以后将从管理链表中移除
- app_timer_process 放在硬件定时器中断服务函数中,1ms周期性的调用
- app_timer_exec 放在main函数的while循环中周期性的调用
#include "app_timer.h"
#include "tim.h"
app_timer_t *p_timer_entry;
app_timer_t *p_timer_now;
app_timer_t app_timer[MAX_TIMER];
static uint8_t app_timer_run=0;
/**
* @brief initialize app timer
* @retval none
*/
void app_timer_init()
{
app_timer_run=0;
for(uint8_t i=0;i<MAX_TIMER;i++)
{
app_timer[i].counter=0;
app_timer[i].delay=0;
app_timer[i].is_rt=0;
app_timer[i].is_repeat=0;
app_timer[i].pending=0;
app_timer[i].cb=0;
app_timer[i].last=NULL;
app_timer[i].next=NULL;
}
p_timer_entry=&app_timer[0];
p_timer_entry->next=p_timer_entry;
p_timer_entry->last=p_timer_entry;
p_timer_now=p_timer_entry;
}
/**
* @brief start app timer
* @retval none
*/
void app_timer_start()
{
app_timer_run=1;
}
/**
* @brief stop app timer
* @retval none
*/
void app_timer_stop()
{
app_timer_run=0;
}
static app_timer_t* app_timer_create(app_timer_handle cb,uint32_t delay,uint8_t is_rt,uint8_t is_repeat)
{
for(uint8_t i=1;i<MAX_TIMER-2;i++)
{
if(app_timer[i].cb==NULL)
{
app_timer[i].counter=delay;
app_timer[i].delay=delay;
app_timer[i].is_rt=is_rt;
app_timer[i].is_repeat=is_repeat;
app_timer[i].pending=0;
app_timer[i].cb=cb;
app_timer[i].last=p_timer_now;
app_timer[i].next=p_timer_entry;
p_timer_now->next=&app_timer[i];
p_timer_now=&app_timer[i];
return p_timer_now;
}
}
return NULL;
}
/**
* @brief creat oneshot app timer
* @param callback function
* @param delay time
* @param is real time
@arg 0:called callback function not in the realtime way
@arg 1:called callback function in the realtime way
* @retval app timer id
*/
app_timer_t* app_timer_oneshot(app_timer_handle cb,uint32_t delay,uint8_t is_rt)
{
return app_timer_create(cb,delay,is_rt,0);
}
/**
* @brief creat repteat app timer
* @param callback function
* @param delay time
* @param is real time
@arg 0:called callback function not in the realtime way
@arg 1:called callback function in the realtime way
* @retval app timer id
*/
app_timer_t* app_timer_repeat(app_timer_handle cb,uint32_t delay,uint8_t is_rt)
{
return app_timer_create(cb,delay,is_rt,1);
}
/**
* @brief The application entry point.
* @param app timer id (app timer pointer)
* @retval int
*/
uint8_t app_timer_delete(APP_TIMER_ID p_timer)
{
if(p_timer==p_timer_now)
{
p_timer->last->next=p_timer_entry;
p_timer_now=p_timer->last;
}
else
{
p_timer->last->next=p_timer->next;
p_timer->next->last=p_timer->last;
}
p_timer->counter=0;
p_timer->delay=0;
p_timer->is_rt=0;
p_timer->cb=0;
return 1;
}
/**
* @brief called at 1ms intervals
* @retval none
*/
void app_timer_process()
{
app_timer_t *pt;
if(app_timer_run!=0)
{
for(pt=p_timer_entry->next;pt!=p_timer_entry;pt=pt->next)
{
pt->counter--;
if(pt->counter==0)
{
if(pt->is_rt==1)
{
pt->cb();
if(pt->is_repeat==0)app_timer_delete(pt); //oneshot timer need to delete
else pt->counter=pt->delay;
}
else pt->pending=1;
}
}
}
}
/**
* @brief called in main loop
* @retval none
*/
void app_timer_exec()
{
app_timer_t *pt;
if(app_timer_run!=0)
{
for(pt=p_timer_entry->next;pt!=p_timer_entry;pt=pt->next)
{
if(pt->pending!=0)
{
pt->cb();
pt->pending=0;
if(pt->is_repeat==0)app_timer_delete(pt);
else pt->counter=pt->delay;
}
}
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
app_timer_process();
}