真香|定时器这么玩,我感觉我又行了
摘要
刚开始写代码的时候,可能更注重的是功能的实现,实现了功能之后,慢慢开始思考如何优雅的实现功能了,成为嵌入式开发的“高质量开发者”。今天小飞哥给大家伙介绍介绍如何优雅的使用定时器。当然,此方法不局限于定时器,重要的是掌握这种方法~
原理介绍
话不多说,来看看如何实现上面“扯的淡”
在嵌入式开发中加一点数据结构,是一种多么美妙的事情~没错,本次的分享主要使用到了单向循环链表
先来看看什么是单向循环链表:
摘自:https://blog.csdn.net/yiquanlaoshi/article/details/101075016
如果把单链表的最后一个节点的指针指向链表头部,而不是指向NULL,那么就构成了一个单向循环链表,通俗讲就是把尾节点的下一跳指向头结点。
为什么要使用单向循环链表呢?
在单向链表中,头指针是相当重要的,因为单向链表的操作都需要头指针,所以如果头指针丢失或者破坏,那么整个链表都会遗失,并且浪费链表内存空间,因此我们引入了单向循环链表这种数据结构。如下图所示:
具体的实现示意图如下图所示:
单向循环链表的插入:
单向循环链表的插入与单向链表有所不同,因为单向循环链表首尾相连,所以没有从尾部插入的问题。
(1)从链表头部插入
将新插入的节点变为链表头部,next指向原来的第一个节点,在遍历整个链表找到链表末尾(即它的next指向的是head的那个节点),将这个节点的next指向新插入的节点,最后再将链表头指针指向新节点。
(2)从链表中间插入
此时插入的过程和单向链表的一样,找到要插入位置前驱节点,将前驱节点的next指向新节点的next,再将前驱节点的next指向新插入的节点。
单向循环链表的类图如下图所示:
单向循环链表的插入与单向链表有所不同,因为单向循环链表首尾相连,所以没有从尾部插入的问题。
(1)从链表头部插入
将新插入的节点变为链表头部,next指向原来的第一个节点,在遍历整个链表找到链表末尾(即它的next指向的是head的那个节点),将这个节点的next指向新插入的节点,最后再将链表头指针指向新节点。
(2)从链表中间插入
此时插入的过程和单向链表的一样,找到要插入位置前驱节点,将前驱节点的next指向新节点的next,再将前驱节点的next指向新插入的节点。单向循环链表的类图如下图所示:
代码实现
原理介绍只能简单说说,没看明白的小伙伴可以再百度百度,深入了解一下
实现目标:
实现基于一个硬件定时器,多个软定时器的创建、删除,和硬件定时器的功能类似。
优点:
实现仅仅依赖依赖文件只有 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);
如何使用
想必大家比较关心的是,如何去使用这个驱动,哈哈,懂,安排~
代码中是有详细的使用说明的哟
功能验证:
led 1000ms闪烁一次,五秒后熄灭
通过创建两个软件定时器,定时器1定时周期为1s,LED灯1S闪烁一次,另一个定时器周期5s删除第一个定时器,LED灯熄灭
代码中也是有很详细的说明的
cubemx配置
配置也是非常的简单,配置下定时器6就行了,开中断,配置PA2,LED控制引脚即可
定时器的心跳在硬件定时器中断中提供,类似于rtos等小系统心跳
结果演示
硬件依然使我们前面开源的fs-board,非常的小巧好用