今天来讲一讲我开发SmartTimer的思路。在上一篇介绍SmartTimer的文章《SmartTImer——一个基于STM32的时钟管理器》中,我提到了要实现延迟XX毫秒执行XX函数的功能,比较好的方式是在定时器中断中设置溢出标志,而在程序主循环中检测这个标志,如果标志置位则运行回调函数。这样不仅可以实现异步的操作,最大化利用MCU的计算资源,而且可以最小化定时器的中断时间。只不过每设置一个定时器事件,就需要为这个事件设定全局标志位,并且为其单独计数、检测标志位。如果处理不好,将会使全局标志散落在程序的各个角落,不仅使程序结构臃肿,而且会留下bug隐患。

基于以上原因,我明确了SmartTimer的开发目的,就是统一管理各个定时器事件,将定时计数、判断定时溢出,执行回调函数都封装起来。使用者在SmartTimer外部不必关心具体的内部实现,只需要用一行语句即可完成异步延迟的操作,不必进行额外的操作。除了简便外另一个好处是,即便定时器部分出现了程序上的bug,也使得问题更加集中,便于定位bug而进行修改。

在对外功能上,提供runlater,runloop,delay这三个功能。

SmartTimer的初步构想

明确了开发目的,就开始构建SmartTImer了。我考虑的SmartTimer架构其实很简单,总体上分为三大块,如下图所示:

简要说明一下这个系统框图:

  1. 用户调用runlater、runloop、delay后,会从内存池中拿出一个event对象并初始化,push进event链表中;
  2. 定时器中断函数每隔Xms执行一次,遍历整个event链表,给每个event事件定时+1,并判断定时时间是否到达。如果到达则将该事件的溢出计数+1;如果定时循环次数不为0,则重新计数,否则将该事件对象从event链表中转移到recycle数组中。
  3. 在主while循环中,查询mark table,如某事件的溢出计数大于0则执行对应的回调函数,并将溢出计数-1;查询recycle数组,如果有需要回收的event事件,则释放资源到内存池中。

架构出来后,本着先完成再优化的原则,稍微考虑下优化方针,就可以动手实现了。由于SmartTimer没有抢占机制,所以属于非实时性工具。但理论上来说,如果以上描述的3大块程序都执行的非常快,那么就可以理解为接近“实时”了。所以为了提高实时性,需要提高程序的执行速度。在代码优化上,我主要采用了空间换时间的方法,牺牲了一部分内存来换取执行速度。主要用在event pool上,以及用数组映射来设置溢出标志和执行回调函数等地方。

SmartTimer的初步实现

首先抽象出定时器事件的定义:

    struct stim_event{uint16_t interval;   //定时事件的定时间隔 uint16_t now;        //定时事件的当前计数  uint16_t looptimes;  //loop次数 uint8_t addIndex;    //事件对象在内存池中的序号 uint8_t stat;        //定时事件的当前状态struct stim_event *next;struct stim_event *prev;};

从结构体定义上可以看出,我准备用双向链表来组织这些定时器事件。用链表而不是数组来构建事件list,是因为定时器事件的加入和移除是动态的,且有时会从list中间增减事件,所以用链表比数组更加合理。

在SmartTimer初始化时,会预先创建一个stim_event的数组作为event pool来使用。另外还会创建两个元素数量与event pool相同的数组,一个是MarkArray,用来存储event的溢出计数;一个是CallbackArray,用来存储回调函数的函数指针。event、溢出计数、回调函数用stim_event中的addIndex联系起来。

event->addIndex为该event在event pool数组中的序号(数组下标),相应的MarkArray和CallbackArray数组中,序号为event->addIndex的元素,存储着该event的溢出计数和回调函数指针。当某个event计时时间到,那么MarkList中第event->addIndex个元素溢出计数+1,将CallbackArray数组中第event->addIndex个元素存储的函数指针调用一次。

定时器中断函数,主要是遍历event链表,进行定时计数并判断是否定时溢出。

    void stim_tick (void){struct stim_event *tmp;if (event_list.count == 0)return;tmp = event_list.head;while(tmp != NULL){ //遍历event链表if(tmp->stat != STIM_EVENT_ACTIVE){tmp = tmp->next;continue;}if(tmp->now == tmp->interval){mark_list[tmp->addIndex] += 1;  //时间到,溢出计数+1if((tmp->looptimes != STIM_LOOP_FOREVER) && (--tmp->looptimes == 0)){   // 循环次数为0,移除事件链表tmp->stat = STIM_EVENT_RECYCLE;recycle_list[recycle_count] = tmp;recycle_count++;}tmp->now = 0;}tmp->now++;tmp = tmp->next;}}   

在主while循环中,主要是执行callback函数,以及回收event事件对象

    void stim_mainloop ( void ){uint8_t i;struct stim_event *tmp;for(i = 0; i < STIM_EVENT_MAX_SIZE; i++){  //检查溢出标志,并执行回调函数if((mark_list[i] != STIM_INVALID) && (mark_list[i] > 0)){if(callback_list[i] != NULL){callback_list[i]();}mark_list[i] -= 1;}}if(recycle_count > 0){for(i = 0; i < STIM_EVENT_MAX_SIZE; i++){  //如果有需要回收的event,则回收对象tmp = recycle_list[i];if(tmp != NULL && mark_list[tmp->addIndex] == 0){pop_event(tmp);recycle_list[i] = NULL;recycle_count--;break;}}}}   

以上是SmartTimer 1.0版本的核心代码。至此,SmartTimer已经可以运行起来了,它大致上完成了我的设计目的,但是并没有完成我的设计目标——它运行的效率并不高,这样以来,实时性就会大打折扣。但是SmartTimer的设计架构我认为还是合理的,只需在这个框架内对调度算法进一步优化,就可以更完善了。下一篇文章,我会介绍我的优化思路和实现方法,敬请期待!

SmartTimer的应用技巧

在《SmartTImer——一个基于STM32的时钟管理器》中,我已经详细介绍过SmartTimer的一般使用方法,不再赘述,下面来简单谈谈高级用法。

我们先来看看传统的单片机程序架构:

    void main (void){system_init();    //系统初始化peripheral_init();//外设初始化while(1){switch(step){  //主程序状态机case A: // do somethingbreak;case B: //do somethingbreak;……default: //do somethingbreak;}usart_driver();//串口驱动timer_event_handler();//定时器事件处理……} }

一般来说都是先进行系统各项初始化,然后就进入了主while循环中,在while循环中,主要执行系统状态机,用来完成当发生xx事后,执行xx函数的功能。在状态机之外,有一些辅助性的功能,例如系统Led呼吸灯驱动、串口驱动、定时事件回调函数等等。

我们单看主while循环内的部分,做一个最简模型。如果假定switch状态机部分的执行需要100ms,串口驱动执行需要10ms,定时器事件处理执行需要20ms,那么while内的程序每循环一次使用130ms。我们其实可以换个角度来看这段程序,即switch状态机每隔30ms执行一次,串口驱动每隔120ms执行一次,而定时事件处理函数每隔110ms执行一次。

是不是很神奇?如果基于时间的角度来考虑问题,那么本来交织在一起的程序被轻易的模块化了,模块化的好处显而易见,不仅程序架构简单清晰,而且便于维护。利用SmartTimer的runloop功能可以轻松实现基于时间的模块化程序架构。我们来看看将上面的例子进行基于时间的模块化修改:

    static void state_machine(void){switch(step){  //主程序状态机case A: // do somethingbreak;case B: //do somethingbreak;……default: //do somethingbreak;}}void main (void){system_init();    //系统初始化peripheral_init();//外设初始化stim_loop(30,state_machine,STIM_LOOP_FOREVER);     stim_loop(120,usart_driver,STIM_LOOP_FOREVER);   stim_loop(110,timer_event_handler,STIM_LOOP_FOREVER);……while(1){stim_mainloop();} }

架构是不是瞬间变简洁了?另外,照这个思路考虑问题,状态机部分的程序也可以分解成若干个模块,使程序更加简洁、优雅。

转载于:https://www.cnblogs.com/elecsun/p/5733676.html

SmartTimer的开发思路相关推荐

  1. halcon学习笔记——机器视觉工程应用的开发思路

    机器视觉工程应用主要可划分为硬件和软件两大部分. 硬件:工程应用的第一步就是硬件选型.硬件选型很关键,因为它是你后面工作的基础.主要是光源.工业相机和镜头选择. 软件:目前业内商业库主要有Halcon ...

  2. Halcon学习笔记——机器视觉应用工程开发思路及相机标定

    机器视觉应用工程开发思路 机器视觉应用工程主要可划分为两大部分,硬件部分和软件部分. 1.硬件部分,硬件的选型至关重要,决定了后续工作是否可以正常开展,其中关键硬件部分包括:光源,相机以及镜头. 2. ...

  3. php进阶面向对象及tp5,TP5实战技巧---开发思路 引路造桥

    ## TP5实战技巧实例---开发思路 > 子曰:"学而不思则罔,思而不学则殆." [TOC] ### 利用TP的MVC框架 做快速开发 >[success] 写在前面 ...

  4. STM32的学习记录--单个模块开发思路

    1. 前言 很久没写STM32的学习博客了,不是因为没在搞,而且因为STM32要学习的部分太多,而且代码量太大了,有些代码还涉及到版权问题,就一直没写,最近刚好在搞一个智能设备有关WiFi模块的开发, ...

  5. php制作的ios端 跳转url,ThinkPHP 简易开发思路 MVC和URL跳转

    本文作者:Twe1ve(贝塔安全实验室-核心成员) 这段时间在看PHP代码审计相关知识,国内有不少CMS都是基于ThinkPHP开发的,因此了解ThinkPHP的开发思路更容易理解这类CMS的代码 一 ...

  6. 二次开发 英文_Revit二次开发——异形柱翻模插件的开发思路

    BIM管线综合项目中,结构模型柱梁板搭建,属于最没有营养的 而结构模型的精确是后续开展的前提 结构主体自动化建模--人工校核梁尺寸标高.板厚等,是效率较高的工作流程 看市面上翻模插件的数量就知道需求有 ...

  7. Android-图像识别项目OpenCV(4):开发思路以及问题

    上一篇文章:Android-图像识别项目OpenCV(3):程序目标以及单独发布 六.开发思路 搭建好环境和做好各种准备功夫,接下来就开始我们的开发之路. 首先,我们先查看一下官方教程文档,看有没有我 ...

  8. 抖音矩阵号搭建及开发思路分享丨抖音矩阵源码丨抖音矩阵号运营

    矩阵就是一个内容在多个平台多个账号去做分发,以此去获得的更大的公域流量.通过单个账号带动其他账号分散形成流量,那矩阵通过多个单账号做内容分发,再引流到主账号才是矩阵号的核心思维. 比如我们有10个账号 ...

  9. 游戏脚本开发思路总结

    初级版 1 看见屏幕的某个位置有某个图片,点它 2 看见屏幕上某个位置有有些字,我要去点某个位置或图片 3 看见屏幕上某个位置进度满了(空了),我该点屏幕上某个图片或某个位置了 4 屏幕上某个位置一直 ...

最新文章

  1. 使用mac pro电脑当tomcat端口被占用怎么解决?
  2. (轉貼) 美電腦工程師改寫遊戲軟體向女友求婚成功 (News)
  3. 双绞线网线的连接方式
  4. html属性选择器怎么写,html – 具有“type”属性与make-up属性的CSS属性选择器和区分大小写...
  5. 实验8.1 指针与数组 6-6 数组循环右移
  6. 解决maven加载不了oracle jdbc驱动包的问题
  7. Maven3生命周期和插件
  8. python有趣小程序-python好玩的小程序
  9. 图像质量评价(Image Quality Assessment,IQA)
  10. WCF DataService调试时的一些问题
  11. 阿里云吴翰清:我对计算的理解
  12. 查看CentOS版本信息
  13. 概率神经网络的主要思想,神经网络随机数预测
  14. 2022金属非金属矿山井下电气考试题模拟考试题库及答案
  15. PayPal支付集成到自己Web网站
  16. Samsung/三星I9128 Galaxy Grand root教程_方法
  17. java 修饰符 详解,详解Java修饰符
  18. 基于肌肉骨骼模型的预测仿真
  19. SLAM-Visual Navigation学习之SIFT算法与代码详解
  20. UILabel行间距调整

热门文章

  1. 程序以html形式发送邮件注意问题
  2. 一文了解 CVPR 2022 Workshop 都要做什么
  3. ETH联合Meta和鲁汶大学 提出视频恢复算法VRT,在视频超分辨率、去模糊和去噪性能达到SOTA...
  4. 重读 CenterNet,一个在Github有5.2K星标的目标检测算法
  5. 保研生看过来!加入DUT Media Lab,科研没有不可能!
  6. 2d游戏地图编辑器_从零开始的unity(3)——2d背景的制作和使用
  7. java sqlexec_java 执行Sql文件
  8. 收藏 | 13则PyTorch使用的小窍门
  9. Python编辑统一缩进(Pycharm)
  10. html lineheight div,html – Chrome上的文本输入:line-height似乎有最小值