软件定时器

对某些精度要求不高的场合,可以不使用硬件定时器

定时器服务任务本身也是一个任务(系统自动创建的任务)

用户对寄存器操作的指令实际上是通过定时器指令队列传给这个任务,然后这个任务来执行操作

定时器到期后执行回调函数,回调函数无法阻塞,无法延时

(1)USE_TIMERS

  • 功能:启用或禁用整个软件定时器功能。
  • 默认值Enabled(启用)。
  • 关键特性:一旦启用,不可修改(在CubeMX中灰色显示,无法禁用)。启用后,FreeRTOS内核会自动创建定时器服务任务和指令队列。
  • 建议:始终保持启用,除非系统绝对不需要软件定时器功能。

(2)TIMER_TASK_PRIORITY

  • 功能:设置定时器服务任务的优先级。
  • 默认值2
  • 设置范围055(因为FreeRTOS优先级总数通常为56)。
  • 优先级上下文:比空闲任务(优先级0)高,确保定时器服务总能及时运行。但低于许多应用任务(典型应用任务优先级在3及以上)。
  • 配置建议提高优先级(如5以上):如果定时器回调需要快速响应(如实时控制)。降低优先级(如1):如果定时器操作不紧急,避免抢占关键任务。避免与硬件中断冲突:优先级不应高于系统关键中断处理任务。

(3)TIMER_QUEUE_LENGTH

  • 功能:设置定时器指令队列的长度(即队列可存储的消息数量)。
  • 默认值:未明确显示,但通常为10(根据FreeRTOS常见默认)。
  • 设置范围1255
  • 作用:该队列用于缓冲定时器操作命令(如启动、停止、复位定时器)。
  • 配置建议小型系统:设置5-10(少量定时器操作)。大型系统:设置20-50(频繁定时器操作,防止队列溢出)。监控队列使用:如果队列常满,增加长度以避免命令丢失。

(4)TIMER_TASK_STACK_DEPTH

  • 功能:设置定时器服务任务的栈空间大小**(以字(Word)为单位)。
  • 默认值256words(通常对应1KB栈空间,假设1 word = 4字节)。
  • 设置范围12832768words。
  • 重要性:栈空间不足会导致定时器服务任务崩溃,影响所有定时器。
  • 配置建议基础应用256-512words(简单回调函数)。复杂回调1024-2048words(回调函数使用较多局部变量或调用深层函数)。调试方法:使用FreeRTOS的栈溢出检查功能(如configCHECK_FOR_STACK_OVERFLOW)。

常见问题与解决方案

  • 问题1:定时器回调执行延迟或不执行。原因:定时器服务任务优先级过低,被高优先级任务阻塞。解决:提高TIMER_TASK_PRIORITY(如设为4或5)。
  • 问题2:系统不稳定或崩溃。原因:定时器服务任务栈溢出。解决:增加TIMER_TASK_STACK_DEPTH,并检查回调函数是否过于复杂。
  • 问题3:定时器操作(如启动)失败。原因:指令队列已满。解决:增加TIMER_QUEUE_LENGTH或减少并发定时器操作。

定时器服务任务如果比一般任务的优先级高,则先执行

否则仅仅是让定时器服务收到接受指令后仅仅进入就绪状态

仅仅是定时器服务任务处理定时器指令的时机是不同的,定时器的起始时间都是从发送“启动定时器”指令到队列才开始计算的

定时器相关函数

1
2
3
4
5
TimerHandle_t xTimerCreate(const char* const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void* const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction);

pcTimerName- 定时器名称

xTimerPeriodInTicks`- 定时周期

uxAutoReload- 定时器类型

  • pdTRUE周期定时器(自动重载),定时器到期自动重启
  • pdFALSE单次定时器,只执行一次后进入休眠状态

重要特性:定时器类型在创建后无法修改

pvTimerID- 定时器标识符

当多个定时器共享同一个回调函数时,用于区分不同定时器

pxCallbackFunction- 回调函数

  • 初始状态:新创建的定时器处于休眠状态(Dormant)
  • 启动定时器:必须调用 xTimerStart()xTimerReset()启动定时器

设置定时器周期

1
2
#define xTimerChangePeriod(xTimer, xNewPeriod, xTicksToWait) \
xTimerGenericCommand((xTimer), tmrCOMMAND_CHANGE_PERIOD, (xNewPeriod), NULL, (xTicksToWait))
  • xTimer:目标定时器的句柄(TimerHandle_t类型)
  • xNewPeriod:新的定时周期(以系统节拍数表示)可使用 pdMS_TO_TICKS()宏将毫秒转换为节拍数示例:pdMS_TO_TICKS(500)表示500毫秒
  • xTicksToWait:指令发送等待时间0:不等待,立即返回portMAX_DELAY:无限期等待直到指令队列有空间其他值:等待指定节拍数

返回值

  • pdTRUE:指令成功发送到定时器指令队列
  • pdFALSE:等待超时,指令未能发送到队列

底层通用函数 xTimerGenericCommand()

1
2
3
4
5
6
7
BaseType_t xTimerGenericCommand(
TimerHandle_t xTimer, // 定时器句柄
const BaseType_t xCommandID, // 命令ID(此处为tmrCOMMAND_CHANGE_PERIOD)一些宏定义
const TickType_t xOptionalValue, // 新周期值
BaseType_t * const pxHigherPriorityTaskWoken, // ISR任务唤醒标志(此处为NULL)
const TickType_t xTicksToWait // 等待时间
);

查询定时器周期

xTimerGetPeriod()

系统节拍数(Ticks) 表示的周期值

xTimerIsTimerActive() - 查询运行状态

  • pdTRUE:定时器正在运行(已启动且在计时)
  • pdFALSE:定时器处于休眠状态(未启动或已停止)

xTimerStart() / xTimerStartFromISR() - 启动定时器

注意启动时机,发送到队列里就开始执行了,而不是从定时器服务任务执行指令的时刻开始算

停止定时器

#define xTimerStop(xTimer, xTicksToWait) \ xTimerGenericCommand((xTimer), tmrCOMMAND_STOP, 0U, NULL, (xTicksToWait))

复位定时器

xTimerReset()

  • 休眠状态:等效于 xTimerStart()(启动定时器)
  • 运行状态:重置计时起始点为当前时刻(重新开始计时)

具体示例

osTimeonce是单次 ostimerperiodic是周期