打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
真香|定时器这么玩,我感觉我又行了

摘要

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

原理介绍

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

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

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

摘自: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,非常的小巧好用

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
linux 内核笔记之watchdog
DW的SD卡驱动分析(二)
一线触摸屏协议驱动
高手进阶:Linux系统下内核定时器的用法
Linux驱动之触摸屏程序编写
排序算法总结
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服