FreeRTOS 软定时器实现

[复制链接]
 楼主| keer_zu 发表于 2021-11-16 15:12 | 显示全部楼层 |阅读模式
本帖最后由 keer_zu 于 2021-11-16 15:15 编辑

简述考虑平台硬件定时器个数限制的, FreeRTOS 通过一个 Daemon 任务(启动调度器时自动创建)管理软定时器, 满足用户定时需求. Daemon 任务会在其执行期间检查用户启动的时间周期溢出的定时器,并调用其回调函数。
对于硬件定时器的中断服务程序, 我们知道不应该在里面执行复杂,可能导致阻塞的工作,相应的, 虽然软定时器实际是在定时Daemon 任务中执行,但是阻塞的话会导致其他定时器调用被延时, 所以实际使用也应该避免。
软定时器是通过一个任务来辅助实现,该功能是可裁剪的 , 只有设置 FreeRTOSConfig.h 中configUSE_TIMERS == 1 将相关代码编译进来, 才能正常使用相关功能。
分析的源码版本是 v9.0.0





 楼主| keer_zu 发表于 2021-11-16 15:15 | 显示全部楼层
使用定时器开始先介绍下如何在自己的工程中使用 FreeRTOS 的软件定时器。
配置定时器服务任务程序中需要使用到软件定时器, 需要先在 FreeRTOSConfig.h 中正确配置如下宏 :
  • configUSE_TIMERS
    是否编译定时器相关代码, 如需要使用定时器, 设置为 1
  • configTIMER_TASK_PRIORITY
    设置定时器Daemon 任务优先级, 如果优先级太低, 可能导致定时器无法及时执行
  • configTIMER_QUEUE_LENGTH
    设置定时器Daemon 任务的命令队列深度, 设置定时器都是通过发送消息到该队列实现的。
  • configTIMER_TASK_STACK_DEPTH
    设置定时器Daemon 任务的栈大小





 楼主| keer_zu 发表于 2021-11-16 15:16 | 显示全部楼层
创建 启动 停止定时器
如下示例代码所示

  1. TimerHandle_t xTimerUser; // 定义句柄

  2. // 定时器回调函数格式
  3. void vTimerCallback( TimerHandle_t xTimer )
  4. {
  5.     // do something no block
  6.     // 获取溢出次数
  7.     static uin32_t ulCount = ( uint32_t ) pvTimerGetTimerID( xTimer );
  8.     // 累积溢出次数
  9.     ++ulCount;
  10.     // 更新溢出次数
  11.     vTimerSetTimerID( xTimer, ( void * ) ulCount );
  12.    
  13.     if (ulCount == 10) {
  14.         // 停止定时器
  15.         xTimerStop( xTimer, 0 );
  16.     }
  17. }

  18. void fun()
  19. {
  20.     // 申请定时器, 配置
  21.     xTimerUser = xTimerCreate
  22.                    /*调试用, 系统不用*/
  23.                    ("Timer's name",
  24.                    /*定时溢出周期, 单位是任务节拍数*/
  25.                    100,   
  26.                    /*是否自动重载, 此处设置周期性执行*/
  27.                    pdTRUE,
  28.                    /*记录定时器溢出次数, 初始化零, 用户自己设置*/
  29.                   ( void * ) 0,
  30.                    /*回调函数*/
  31.                   vTimerCallback);
  32.                   
  33.      if( xTimerUser != NULL ) {
  34.         // 启动定时器, 0 表示不阻塞
  35.         xTimerStart( xTimerUser, 0 );
  36.     }
  37. }


 楼主| keer_zu 发表于 2021-11-16 15:17 | 显示全部楼层
如上所示, 调用函数 xTimerCreate申请,配置定时器, 通过 xTimerStart 启动定时器, 当定时器计数溢出时, 系统回调注册的函数。
定时器可以设置为一次性 One-shot 或者自动重载 Auto-reload 两种, 第一种溢出后停止定时器, 第二种溢出后会再次启动定时器。


3780161935b0c62ce5.png

 楼主| keer_zu 发表于 2021-11-16 15:18 | 显示全部楼层
修改定时器
在申请定时器的时候设置的定时器周期, 可以通过函数 xTimerChangePeriod 修改, 如下示例 :

  1. void vAFunction_2( TimerHandle_t xTimer )
  2. {
  3.      // 判断定时器是否处于运行状态
  4.      if( xTimerIsTimerActive( xTimer ) != pdFALSE )
  5.      {
  6.          /* xTimer is active, do something. */
  7.      }
  8.      else
  9.      {
  10.          // 处于这个状态的定时器, 可能由于 :
  11.          // 1 定时器 create 后没有start
  12.          // 2 一次性定时器执行溢出后
  13.          
  14.          // 修改定时器周期
  15.          if( xTimerChangePeriod( xTimer,
  16.                 /*修改定时周期*/
  17.                 500 / portTICK_PERIOD_MS,
  18.             /*允许阻塞最大时间 100 ticks*/
  19.             100 ) == pdPASS )
  20.          {
  21.              // update fail
  22.              // 阻塞 100 tick 仍然无法发送命令
  23.             
  24.              // 删除定时器 释放对应内存!
  25.              xTimerDelete( xTimer );
  26.          }
  27.          else
  28.          {
  29.              // 定时器配置更新成功, 并已经启动 !!
  30.          }
  31.     }
  32. }
如上, 该函数会修改定时器并使定时器 开始运行!!!

 楼主| keer_zu 发表于 2021-11-16 15:22 | 显示全部楼层
另外, 可以通过函数 xTimerReset 重启定时器, 如果已经启动计数, 重新开始计数; 如果没有启动,启动定时器。
定时器使用系统提供 API,涉及 Queue 操作, 如果是在中断程序中调用,需要调用对应带 FromISR的接口。


 楼主| keer_zu 发表于 2021-11-16 15:23 | 显示全部楼层
获取定时器状态
其他获取定时器信息的函数
  1. // 获取名称 , 申请是设置的字符串
  2. pcTimerGetName()
  3. // 定时器溢出周期
  4. xTimerGetPeriod()
  5. // 返回定时器溢出的时间点 (--> xTaskGetTickCount())
  6. xTimerGetExpiryTime()


 楼主| keer_zu 发表于 2021-11-16 15:24 | 显示全部楼层
定时器的实现参考:
定时器
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群:49734243 Email:zukeqiang@gmail.com

1478

主题

12917

帖子

55

粉丝
快速回复 在线客服 返回列表 返回顶部