关注公众号,后台回复“timer”,即可获得本次分享的源码哦


摘要

刚开始写代码的时候,可能更注重的是功能的实现,实现了功能之后,慢慢开始思考如何优雅的实现功能了,成为嵌入式开发的“高质量开发者”。今天小飞哥给大家伙介绍介绍如何优雅的使用定时器。当然,此方法不局限于定时器,重要的是掌握这种方法~

原理介绍

话不多说,来看看如何实现上面“扯的淡”

在嵌入式开发中加一点数据结构,是一种多么美妙的事情~没错,本次的分享主要使用到了单向循环链表

先来看看什么是单向循环链表:


如果把单链表的最后一个节点的指针指向链表头部,而不是指向NULL,那么就构成了一个单向循环链表,通俗讲就是把尾节点的下一跳指向头结点。

为什么要使用单向循环链表呢?

在单向链表中,头指针是相当重要的,因为单向链表的操作都需要头指针,所以如果头指针丢失或者破坏,那么整个链表都会遗失,并且浪费链表内存空间,因此我们引入了单向循环链表这种数据结构。如下图所示:

真香|定时器这么玩,我感觉我又行了_单向链表

具体的实现示意图如下图所示:

单向循环链表的插入:

真香|定时器这么玩,我感觉我又行了_链表_02

单向循环链表的插入与单向链表有所不同,因为单向循环链表首尾相连,所以没有从尾部插入的问题。

(1)从链表头部插入

将新插入的节点变为链表头部,next指向原来的第一个节点,在遍历整个链表找到链表末尾(即它的next指向的是head的那个节点),将这个节点的next指向新插入的节点,最后再将链表头指针指向新节点。

(2)从链表中间插入

此时插入的过程和单向链表的一样,找到要插入位置前驱节点,将前驱节点的next指向新节点的next,再将前驱节点的next指向新插入的节点。

单向循环链表的类图如下图所示:

单向循环链表的插入与单向链表有所不同,因为单向循环链表首尾相连,所以没有从尾部插入的问题。

(1)从链表头部插入

将新插入的节点变为链表头部,next指向原来的第一个节点,在遍历整个链表找到链表末尾(即它的next指向的是head的那个节点),将这个节点的next指向新插入的节点,最后再将链表头指针指向新节点。

(2)从链表中间插入

此时插入的过程和单向链表的一样,找到要插入位置前驱节点,将前驱节点的next指向新节点的next,再将前驱节点的next指向新插入的节点。单向循环链表的类图如下图所示:

真香|定时器这么玩,我感觉我又行了_单向链表_03

代码实现

原理介绍只能简单说说,没看明白的小伙伴可以再百度百度,深入了解一下

实现目标:

实现基于一个硬件定时器,多个软定时器的创建、删除,和硬件定时器的功能类似。

优点:

实现仅仅依赖依赖文件只有 stdilib.h 和 stdint.h文件,可移植性“极强”

接下来主要介绍几个核心函数,其他的就不一一介绍了,小伙伴们自行下载查看咯

创建timer并添加到链表

/*
* *@ author:lanxin
* *@ brief:创建新的timer,参数正确后添加到timer链表里面
* *@ note:
* *@ param:mode 工作模式
* *@ param:tim 定时周期
* *@ param:para 回调函数的参数
* *@ param:tim_cb 回调函数
* *@ retval:result != SOFT_TIMER_STATE_OK add faild
*/
static e_soft_timer_state_t fs_add_new_soft_timer(e_timer_work_mode_t mode, uint16_t tim, void *para, soft_timer_call_back tim_cb)
{
/* 先检查在开辟空间 */
TIMER_CHECK_WORKMODE(mode); //检查工作模式
TIMER_CHECK_NULL(tim_cb); //检查回调函数
TIMER_CHECK_NULL(soft_timer_manage); //检查是否为空
/* 创建新的timer */
static c_soft_timer_t *timer_dev_temp = 0x00;
timer_dev_temp = fs_malloc_new_soft_timer();
TIMER_CHECK_NULL(timer_dev_temp); //检查是否为空

/* 第一次添加timer */
if (soft_timer_manage->timer_total_num == 0)
{
soft_timer_manage->timer_head = timer_dev_temp; //第一个timer,作为头结点,以后删除timer的时候,通过这个遍历整个timer链表

timer_dev_temp->timer_next = timer_dev_temp; //目前就一个timer所有指向它本身
}
else
{
c_soft_timer_t *timer_head_temp = 0x00;
c_soft_timer_t *timer_add_temp = 0x00;

timer_head_temp = soft_timer_manage->timer_head; //找到第一个timer

timer_add_temp = timer_head_temp->timer_next; //找到未增加前第一个timer后面的timer

timer_head_temp->timer_next = timer_dev_temp; //添加新的timer 到第一个timer 后面

timer_dev_temp->timer_next = timer_add_temp; //将第一个timer原本的timer放在新增加的timer后面
}
soft_timer_manage->timer_total_num++; //timer总数加1
/* 拷贝信息 */
timer_dev_temp->mode = mode;
timer_dev_temp->timer_cb = tim_cb;
timer_dev_temp->cnt_aim = tim / SOFT_TIME_PERIOD; //实际的计数次数
timer_dev_temp->cnt_now = tim / SOFT_TIME_PERIOD;
timer_dev_temp->para = para;
timer_dev_temp->enable = SOFT_TIMER_ENABLE; //默认timer 使能状态
return SOFT_TIMER_STATE_OK;
}
/*
* *@ author:lanxin
* *@ brief:添加新的timer
* *@ note:如果之后要操作这个定时器,就得保存下来timer句柄,不操作就不用管。
* *@ param:mode 工作模式
* *@ param:tim 定时周期
* *@ param:para 回调函数的参数
* *@ param:tim_cb 回调函数
* *@ retval:新的timer 的句柄,
*/
static c_soft_timer_t *add_new_timer(e_timer_work_mode_t mode, uint16_t tim, void *para, soft_timer_call_back tim_cb)
{
if (fs_add_new_soft_timer(mode, tim, para, tim_cb) == SOFT_TIMER_STATE_OK)
{
return soft_timer_manage->timer_head->timer_next; //新添加的timer在timer 链表的第二个。
}
return 0x00;
}

删除timer

/*
* *@ author:lanxin
* *@ brief:删除一个定时器
* *@ note:立刻生效
* *@ param:timer 要删除的timer 句柄
* *@ retval:result != SOFT_TIMER_STATE_OK delete faild
*/
static e_soft_timer_state_t delete_timer(c_soft_timer_t *timer)
{
TIMER_CHECK_NULL(soft_timer_manage);
TIMER_CHECK_NULL(timer);

uint8_t find_cnt = 0;
if (soft_timer_manage->timer_total_num == 0)
{
return SOFT_TIMER_DELETE_NOT_FOUND;
}
/* 如果删除的是头结点,将头结点的下一个节点换为新的头结点 */
if (soft_timer_manage->timer_head == timer)
{
soft_timer_manage->timer_head = soft_timer_manage->timer_head->timer_next;
}

static c_soft_timer_t *timer_delete_temp = 0x00;
timer_delete_temp = soft_timer_manage->timer_head;
for (find_cnt = 0; find_cnt < soft_timer_manage->timer_total_num; find_cnt++)
{
if (timer_delete_temp->timer_next == timer)
{
break;
}
timer_delete_temp = timer_delete_temp->timer_next;
}
if (find_cnt == soft_timer_manage->timer_total_num)
{
return SOFT_TIMER_DELETE_NOT_FOUND; //要删除的timer不存在
}
timer_delete_temp->timer_next = timer->timer_next; //原本timer_delete_temp指向要删除的timer,现在指向要删除的timer的下一个timer
free(timer); //释放空间
soft_timer_manage->timer_total_num--; //timer总数减1
if (soft_timer_manage->timer_total_num == 0)
{
soft_timer_manage->timer_head = 0x00;
}
return SOFT_TIMER_STATE_OK;
}

timer心跳

/*
* *@ author:lanxin
* *@ brief:为soft timer 提供心跳
* *@ note:
* *@ param:NONE
* *@ retval:NONE
*/
static e_soft_timer_state_t fs_soft_timer_heart(void)
{
TIMER_CHECK_NULL(soft_timer_manage);
TIMER_CHECK_NULL(soft_timer_manage->timer_total_num);
if (soft_timer_manage->timer_module_enable == SOFT_TIMER_MODULE_STOP)
{
return SOFT_TIMER_MODULE_STOP;
}
static c_soft_timer_t *timer = 0x00;
timer = soft_timer_manage->timer_head;
/* 遍历所有的timer */
for (int cnt = 0; cnt < soft_timer_manage->timer_total_num; cnt++)
{
if (timer->enable == SOFT_TIMER_ENABLE)
{
timer->cnt_now--;
}
if (timer->cnt_now == 0)
{
timer->timeout = SOFT_TIMER_TIMEOUT;
if (timer->mode == SOFT_TIMER_WORK_PERIOD)
{
timer->cnt_now = timer->cnt_aim;
}
}
timer = timer->timer_next;
}
return SOFT_TIMER_STATE_OK;
}

定时器中断处理

/*
* *@ author:lanxin
* *@ brief:处理定时器的中断事件
* *@ note:放while(1)里面
* *@ param:NONE
* *@ retval:NONE
*/
static e_soft_timer_state_t fs_soft_timer_handle(void)
{
TIMER_CHECK_NULL(soft_timer_manage);
TIMER_CHECK_NULL(soft_timer_manage->timer_total_num);

if (soft_timer_manage->timer_module_enable == SOFT_TIMER_MODULE_STOP)
{
return SOFT_TIMER_MODULE_STOP;
}
static c_soft_timer_t *timer = 0x00;
timer = soft_timer_manage->timer_head;
for (int cnt = 0; cnt < soft_timer_manage->timer_total_num; cnt++)
{
if (timer->timeout == SOFT_TIMER_TIMEOUT)
{
timer->timer_cb(timer->para);
if (timer->mode == SOFT_TIMER_WORK_ONE_TIMER)
{
delete_timer(timer);
}
timer->timeout = 0;
}
timer = timer->timer_next;
}
return SOFT_TIMER_STATE_OK;
}

函数一览

主要包括定时器的周期、开始、结束、开启、关闭等常规操作

/* function declaration */
e_soft_timer_state_t fs_soft_timer_module_init(void);
static e_soft_timer_state_t fs_soft_timer_heart(void);
static e_soft_timer_state_t fs_soft_timer_handle(void);

static c_soft_timer_t *fs_malloc_new_soft_timer(void);

static e_soft_timer_state_t fs_add_new_soft_timer(e_timer_work_mode_t mode, uint16_t tim, void *para, soft_timer_call_back tim_cb);
static c_soft_timer_t *add_new_timer(e_timer_work_mode_t mode, uint16_t tim, void *para, soft_timer_call_back tim_cb);
static e_soft_timer_state_t delete_timer(c_soft_timer_t *timer);

static e_soft_timer_state_t fs_timer_set_period(c_soft_timer_t *timer, uint16_t period);
static e_soft_timer_state_t fs_timer_reload_cnt(c_soft_timer_t *timer, uint16_t tim_cnt);

static e_soft_timer_state_t fs_soft_timer_module_stop(void);
static e_soft_timer_state_t fs_soft_timer_module_start(void);

static e_soft_timer_state_t fs_soft_timer_enable(c_soft_timer_t *timer);
static e_soft_timer_state_t fs_soft_timer_disable(c_soft_timer_t *timer);

如何使用

想必大家比较关心的是,如何去使用这个驱动,哈哈,懂,安排~

代码中是有详细的使用说明的哟

真香|定时器这么玩,我感觉我又行了_单向链表_04

功能验证:

led 1000ms闪烁一次,五秒后熄灭

通过创建两个软件定时器,定时器1定时周期为1s,LED灯1S闪烁一次,另一个定时器周期5s删除第一个定时器,LED灯熄灭

代码中也是有很详细的说明的

真香|定时器这么玩,我感觉我又行了_循环链表_05

cubemx配置

配置也是非常的简单,配置下定时器6就行了,开中断,配置PA2,LED控制引脚即可

真香|定时器这么玩,我感觉我又行了_循环链表_06真香|定时器这么玩,我感觉我又行了_循环链表_07真香|定时器这么玩,我感觉我又行了_单向链表_08

定时器的心跳在硬件定时器中断中提供,类似于rtos等小系统心跳

真香|定时器这么玩,我感觉我又行了_循环链表_09

结果演示

硬件依然使我们前面开源的fs-board,非常的小巧好用


加入我们


欢迎添加小飞哥好友,进群聊一起探讨技术话题,期待优秀的你