[代码分享]一个简单好用的轮询延时器模块

2019-12-16 22:38发布

本帖最后由 Jmhh247 于 2017-5-23 10:08 编辑

关键字:延时器;非阻塞自由延时


在一些按键或其它形式的命令响应中,要求严格的话通常会需要队列缓存命令,以防丢失。

但是我通常还会遇到一些要求非常不严格的场景,这些场景甚至要限制响应的次数或频率(这里指不能响应太快)。
比如(只是个例子),我的产品是一个根据接收到的串口命令来工作的。串口命令发送的速度是不确定的,有时候1秒
会发送10帧命令过来,有的时候1秒确能发送100帧,但是这个产品每秒只打算处理20帧命令。而且由于命令有多个,
对于有些命令码可能每秒响应40帧或30帧等,这种场景用多个延时器就很合适了。


延时器模块的接口函数一共3个,如下:
  1. /* Exported functions ------------------------------------------------------- */
  2. bool zl_poll_delay_set(uint8_t chUserId, uint16_t hwTime);
  3. bool zl_poll_delay_timeout(uint8_t chUserId);
  4. void zl_poll_delay_tick(void);
复制代码

看下接口函数的实现,很简单的
  1. /*   Z L _ P O L L _ D E L A Y _ S E T   */
  2. /*-------------------------------------------------------------------------
  3. 功能描述  : 设定延时时间,同时启动延时。
  4. 输入参数  : uint8_t chUserId  延时器ID
  5.              uint16_t hwTime   延时时间
  6. 输出参数  : 无
  7. 返 回 值  :

  8. 修改历史      
  9.   1.日    期   : 2017年2月6日
  10.     作    者   : zys
  11.     修改内容   : 新生成函数,OK。

  12. -------------------------------------------------------------------------*/
  13. bool zl_poll_delay_set(uint8_t chUserId, uint16_t hwTime)
  14. {
  15.     if (chUserId >= ZL_POLL_DELAY_NUM) {
  16.         return false;
  17.     }

  18.     if (0 == hwTime) {
  19.         return false;
  20.     }

  21.     s_tPollBuf[chUserId].Time = hwTime;

  22.     return true;

  23. }


  24. /*   Z L _ P O L L _ D E L A Y _ T I M E O U T   */
  25. /*-------------------------------------------------------------------------
  26. 功能描述  : 查询给定ID的延时器是否超时。
  27. 输入参数  : uint8_t chUserId  
  28. 输出参数  : 无
  29. 返 回 值  : true 超时;false 未超时。

  30. 修改历史      
  31.   1.日    期   : 2017年5月22日
  32.     作    者   : zys
  33.     修改内容   : 新生成函数。

  34. -------------------------------------------------------------------------*/
  35. bool zl_poll_delay_timeout(uint8_t chUserId)
  36. {
  37.     if (chUserId >= ZL_POLL_DELAY_NUM) {
  38.         return false;
  39.     }

  40.     if ( !s_tPollBuf[chUserId].Time) {
  41.         return true;
  42.     }

  43.     return false;
  44. }


  45. /*   Z L _ P O L L _ D E L A Y _ T I C K   */
  46. /*-------------------------------------------------------------------------
  47. 功能描述  : 延时模块的驱动任务,在心跳中断里运行。
  48. 输入参数  : void  
  49. 输出参数  : 无
  50. 返 回 值  :

  51. 修改历史      
  52.   1.日    期   : 2017年5月22日
  53.     作    者   : zys
  54.     修改内容   : 新生成函数。

  55. -------------------------------------------------------------------------*/
  56. void zl_poll_delay_tick(void)
  57. {
  58.     uint8_t i;


  59.     for ( i = 0 ; i < ZL_POLL_DELAY_NUM; i++ ) {
  60.         if (s_tPollBuf[i].Time) {
  61.             s_tPollBuf[i].Time--;
  62.         }
  63.     }
  64. }
复制代码

模块的使用
使用很简单,首先把模块源文件添加到工程中,然后把
  1. void zl_poll_delay_tick(void)
复制代码
放到心跳中断里,我一般用1ms的心跳中断。


现在就能使用
  1. bool zl_poll_delay_set(uint8_t chUserId, uint16_t hwTime)
复制代码
来设置延时器了,入参分别是延时器的ID和延时时间,延时时间单位和心跳中断有关,看自己需求了。。。


然后用
  1. bool zl_poll_delay_timeout(uint8_t chUserId)
复制代码
来查询延时器是否超时,入参是要查询的延时器ID号。


延时器的可用最大数量通过宏定义配置
  1. /* 定义可用的延时数量 */
  2. #define ZL_POLL_DELAY_NUM   5
复制代码



模拟一个应用场景演示代码


  1. /* 定义延时器信息,便于阅读修改 */
  2. #define DELAY_ID0           0
  3. #define DELAY_TIME0         50

  4. #define DELAY_ID1           1
  5. #define DELAY_TIME1         900



  6. /* 模拟一个应用场景 */
  7. void xxx_cmd_process(uint8_t chCmd)
  8. {
  9.     switch (chCmd) {
  10.     case CMD_LEFT:

  11.         /* 该命令执行间隔不能小于50ms */
  12.         if (zl_poll_delay_timeout(DELAY_ID0)) {
  13.             zl_poll_delay_set(DELAY_ID0, DELAY_TIME0);

  14.             cmd_do_left();
  15.         }
  16.         break;

  17.     case CMD_RIGHT:

  18.         /* 该命令执行间隔不能小于900ms */
  19.         if (zl_poll_delay_timeout(DELAY_ID1)) {
  20.             zl_poll_delay_set(DELAY_ID1, DELAY_TIME1);

  21.             cmd_do_left();
  22.         }
  23.         break;

  24.     case CMD_UP:

  25.         /* 该命令无限制,来就执行 */
  26.         cmd_do_up();
  27.         break;

  28.     case CMD_DOWN:
  29.         break;

  30.     default:
  31.         break;
  32.     }
  33. }


  34. /* 在1ms心跳(时基)中断里运行 */
  35. void st_systick_1ms_irq(void)
  36. {
  37.     zl_poll_delay_tick()
  38. }
复制代码


延时器的用处还有很多,可以灵活。。。

zlib poll delay模块源码.rar (2.02 KB, 下载次数: 108) 2017-5-23 10:07 上传 点击文件名下载附件

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
38条回答
Jmhh247
1楼-- · 2019-12-19 03:30
istars2005 发表于 2017-7-19 21:35
楼主的方式貌似有点繁琐

哈哈,你的方式很不错哈,简单说,你的是将数据与算法分离了

对于这种起辅助作用的功能模块,我现在的思维是尽可能的多做封装,
不去管理数据,而是管理 ID,让使用者更关注自己的代码实现。


现在很火的共享单车(辅助模块),对用户来说,有很多车摆在那里,根本
无需申请一辆车(声明数据),直接扫码(管理ID)骑走。


繁琐与否,就看个人理解吧。。。


我另外一个帖子,也是基于这样的考虑
点我 [代码分享]一个软件定时器模块,简单好用
Jmhh247
2楼-- · 2019-12-19 05:12
fm007 发表于 2017-7-19 16:30
感谢楼主分享,我目前也使用过这种思路的,但是用的是离散的结构体标志

结构体标志也不错
istars2005
3楼-- · 2019-12-19 10:21
Jmhh247 发表于 2017-7-20 09:05
哈哈,你的方式很不错哈,简单说,你的是将数据与算法分离了

对于这种起辅助作用的功能模块,我现在的思 ...


我也是考虑到封装问题,
把这部分功能放到了STM32的SysTick处理的模块当中,
只提供一个延时变量注册的接口.

这样使用时候只需注册一下自己模块内部的静态变量
后面的操作都是本地变量的赋值和判断.
自我感觉封装的还不错


编辑原因: 标题错误
Jmhh247
4楼-- · 2019-12-19 11:35
istars2005 发表于 2017-7-20 09:20
我也是考虑到封装问题,
把这部分功能放到了STM32的SysTick处理的模块当中,
只提供一个延时变量注册的接口 ...

你这么说我就看懂了,很有参考价值哈

用户处理好超时变量一次注册的问题就行了。
istars2005
5楼-- · 2019-12-19 12:07
 精彩回答 2  元偷偷看……
jiangyimfs1
6楼-- · 2019-12-19 12:24
楼主的非阻塞延时器很实用,在工程中使用了一下,出了点问题,请教一下楼主是不是哪个步骤使用错误了,贴上代码
  1. void Task_Thread(void)
  2. {   
  3.         switch(step)
  4.                   {       

  5.                            case 0: //两个LED全灭
  6.                           
  7. //                              
  8.                    if (zl_poll_delay_timeout(0))        //DELAY_ID1  0   标志位
  9.                                                                                  {
  10.                                                                                  
  11.                       zl_poll_delay_set(0, 4000);    //    延时4000ms,4s
  12.                                                                                           LED1=1;
  13.                         LED2=1;
  14.                                                                                         step=1;
  15.                                                                                  }
  16.                              
  17.                      break;
  18.                              
  19.                  
  20.                                       
  21.                      case 1:        // led1亮
  22.                   
  23.                                    
  24.                           LED1=0;
  25.                                if (zl_poll_delay_timeout(1))        //DELAY_ID1  1   标志位
  26.                                                                                  {
  27.                                                                                  
  28.                          zl_poll_delay_set(1, 4000);          //    延时4000ms,4s
  29.                                                                                            LED1=0;
  30.                                                                                            step=2;
  31.                                                                                  }
  32.                            break;
  33.                            
  34.                            
  35.                        case 2:        // led2亮
  36.                                                                                                
  37.                                                                                          
  38.                         if (zl_poll_delay_timeout(2))        //DELAY_ID1  0   标志位
  39.                                                                                  {
  40.                                                                                  
  41.                       zl_poll_delay_set(2, 4000);    //    延时4000ms,4s
  42.                                                                                           LED2=0;
  43.                                                                                           step=0;
  44.                                                                                  }

  45.                            break;
  46.                                                                                  
  47.                                                                                  
  48.                                                                                  default:
  49.                        break;

  50.                         }
  51. }
复制代码


//定时器3中断服务程序  1ms
void TIM3_IRQHandler(void)   //TIM3中断
{
         u8 Index;       

       
        zl_poll_delay_tick();
       

一周热门 更多>