FreeRTOS 基础系列文章
基本对象
FreeRTOS——任务
FreeRTOS——队列
FreeRTOS——信号量
FreeRTOS——互斥量
FreeRTOS——任务通知
FreeRTOS——流和消息缓冲区
FreeRTOS——软件定时器
FreeRTOS——事件组
内存管理
FreeRTOS——静态与动态内存分配
FreeRTOS——堆内存管理
FreeRTOS——栈溢出保护
代码组织
FreeRTOS——源代码组织
FreeRTOS——创建新的项目
FreeRTOS——配置文件
FreeRTOS——软件计时器
软件计时器
软件计时器概述
软件计时器(或只是“计时器”)允许在将来的设定时间执行函数。计时器执行的函数称为计时器的回调函数。计时器启动和执行其回调函数之间的时间称为计时器的周期。简单来说,计时器的计时时间到了,就会执行计时器的回调函数。
请注意,必须先明确创建软件计时器,然后才能使用它。
软件计时器实现中的效率考虑
软件定时器功能易于实现,但难以有效实现。FreeRTOS 实现不会在中断上下文执行计时器回调函数,不会消耗任何处理时间除非计时器确实已计时到期,不会向滴答中断添加任何处理开销,并且在中断被禁用时不会遍历任何链表结构。
计时器服务任务(主要)利用现有的 FreeRTOS 功能,允许将计时器功能添加到应用程序的同时对应用程序可执行二进制文件的大小影响最小。
关于编写计时器回调函数的重要信息
计时器回调函数在计时器服务任务的上下文中执行。因此,计时器回调函数永远不要试图阻塞是很重要的。例如,计时器回调函数在访问队列或信号量时不得调用 vTaskDelay()、vTaskDelayUntil() 或指定非零阻塞时间。
计时器服务(或守护进程)任务
计时器功能是可选的,不是核心 FreeRTOS 内核的一部分。它由计时器服务(或守护进程)任务提供。
FreeRTOS 提供了一组与计时器相关的 API 函数。其中许多函数使用标准的 FreeRTOS 队列向计时器服务任务发送命令。用于此目的的队列称为“计时器命令队列”。“计时器命令队列”是 FreeRTOS 计时器实现私有的,不能直接访问。
下图演示了这种情况。左侧的代码代表了用户应用程序中的一个函数,并且它在某个任务中被调用。右边的代码代表了计时器服务任务的实现。计时器命令队列是应用程序任务和计时器服务任务之间的纽带。在这个演示案例中,xTimerReset()
API 函数是从应用程序代码中调用的。这导致将重置命令发送到计时器命令队列以供计时器服务任务处理。应用程序代码仅调用 xTimerReset()
API 函数 —— 它不会(也不能)直接访问计时器命令队列。
计时器守护进程配置
要在应用程序中使用FreeRTOS 软件计时器 API,只需:
-
将
FreeRTOS/Source/timers.c
源文件添加到你的项目中,然后 -
在应用程序
FreeRTOSConfig.h
头文件中定义下表中详述的常量。
常量 | 描述 |
---|---|
configUSE_TIMERS | 设置为 1 以包含计时器功能。当 configUSE_TIMERS 设置为 1 时,将在 RTOS 调度程序启动时自动创建计时器服务任务。 |
configTIMER_TASK_PRIORITY | 设置计时器服务任务的优先级。与所有任务一样,计时器服务任务可以以 0 至 ( configMAX_PRIORITIES - 1 ) 之间的任何优先级运行。 需要仔细选择该值以满足应用程序的需求。例如,如果计时器服务任务被设置为系统中最高优先级的任务,那么发送到计时器服务任务的命令(当调用计时器 API 函数时)和到期的计时器都会立即得到处理。相反,如果计时器服务任务被赋予低优先级,则发送到计时器服务任务的命令和到期的定时器将不会被处理,直到计时器服务任务是能够运行的最高优先级任务。然而,这里值得注意的是,计时器到期时间是相对于发送命令的时间计算的,而不是相对于处理命令的时间。 |
configTIMER_QUEUE_LENGTH | 这将设置计时器命令队列在任一时间可以容纳的未处理命令的最大数量。 计时器命令队列可能填满的原因包括: 1、在启动RTOS调度器之前以及在创建计时器服务任务之前调用多个计时器API函数。 2、从中断服务程序 (ISR) 进行多个(中断安全)计时器 API 函数调用。 3、从优先级高于计时器服务任务的任务调用多个计时器 API 函数。 |
configTIMER_TASK_STACK_DEPTH | 设置分配给计时器服务任务的堆栈大小(以字为单位,而不是字节)。 计时器回调函数在计时器服务任务的上下文中执行。因此,计时器服务任务的堆栈要求取决于计时器回调函数的堆栈要求。 |
单次与自动重载
有两种类型的计时器,单次计时器和自动重载计时器。一旦启动,单次计时器将只执行一次其回调函数。可以手动重启,但不会自动重启。相反,一旦启动,自动重载计时器将在每次执行其回调函数后自动重新启动,从而导致周期性回调执行。
下图中的时间线演示了单次计时器和自动重载计时器之间的行为差异。在此图中,计时器 1 是一个周期等于 100 的单次计时器,而计时器 2 是一个周期等于 200 的自动重载计时器。
重置软件计时器
可以重置已经开始运行的计时器。重置计时器会导致计时器重新计算其到期时间,因此到期时间与重置计时器的时间有关,而不是相对于计时器最初启动的时间。下图中演示了此行为,其中计时器 1 是一个单次计时器,其周期等于 5 秒。
在所描述的示例中,假设应用程序在按下某个键时打开 LCD 背光,并且背光保持亮起直到 5 秒后没有按下任何键。计时器 1 用于在 5 秒过后关闭 LCD 背光。