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

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条回答
istars2005
2019-12-19 01:41
本帖最后由 istars2005 于 2017-7-19 21:37 编辑

楼主的方式貌似有点繁琐


  1. vu32 *gTimOut[TM_OUT_CNT] = {0};   // 超时计数变量指针数组
  2. static u8  tm_Out_Cnt = 0;                   // 实际超时变量个数
复制代码

  1. /**
  2. * 函数功能: 配置超时指针
  3. * 输入参数: *ptout 需要传入的超时变量指针
  4. * 输出参数: 配置结果 0:成功  -1:索引溢出  1:超时指针重复(不影响正常结果)
  5. * 功能说明: 修改原来实现方式的bug,不再使用idx变量
  6.              当超过最大允许的超时变量个数时返回-1
  7. */
  8. s8 TimeOutSet(vu32 *ptout)
  9. {
  10.     if(tm_Out_Cnt >= TM_OUT_CNT)
  11.         return -1;
  12.     for(int i=0;i<tm_Out_Cnt;i++) {
  13.         if(ptout == gTimOut[i])
  14.             return 1;
  15.     }
  16.     gTimOut[tm_Out_Cnt] = ptout;
  17.     tm_Out_Cnt++;
  18.     return 0;
  19. }
复制代码

在系统滴答定时器中调用如下代码

  1.     int i=0;
  2.     while(i<tm_Out_Cnt) {                                                       // 扫描超时变量,并且把不为0的变量进行自减一
  3.         *gTimOut[i] ? (*gTimOut[i])-- : 0;
  4.         i++;
  5.     }
复制代码


使用方法
  1. vu32 _gTimeOut1 = 0;                          // 全局变量
  2. TimeOutSet(&_gTimeOut1 );                         // 超时变量注册,最好注册一次,重复注册也可以使用,但是稍浪费一点时间

  3. //正式使用
  4. if(_gTimeOut1  == 0) {
  5.        _gTimeOut1 = 50;
  6.       
  7.        // DoSomeThing();
  8. }
复制代码

一周热门 更多>