官方文档
Software Timer Management
[[FreeRTOS_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf#page=174&selection=7,0,7,25|FreeRTOS_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide, page 174]]
官方函数文档
5.3 xTimerCreate()
[[FreeRTOS_Reference_Manual_V10.0.0.pdf#page=259&selection=2,0,4,14|FreeRTOS_Reference_Manual_V10.0.0, page 259]]
什么是定时器:
- 定时器就像一个闹钟,在一定时间后会执行相应的程序。
软件定时器和硬件定时器的区别:
- 硬件定时器:
不同的芯片都会提供相应的硬件定时器,而硬件定时器的数量是有限的。 - 软件定时器:
FreeRTOS 的软件定时器基于Deamon (Time Service) Task
,通过定时器命令队列,对定时器任务发送命令,然后定时器任务调用相应的任务程序,执行软件定时器回调函数。
所以对于软件定时器来说:
- 它与硬件无关,与平台无关,在不同平台的 FreeRTOS 的操作系统当中,都是通过同样的函数来调用软件定时器;
- 硬件定时器会有相应的数量限制,但是软件定时器会根据(
configTIMER_TASK_STACK_DEPTH
)和软件的设置,可以有很多软件定时器。
xTimerCreate()
#include "FreeRTOS.h"
#include "timers.h"
TimerHandle_t xTimerCreate( const char *pcTimerName,
const TickType_t xTimerPeriod,
const UBaseType_t uxAutoReload,
void *const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction );
函数说明:
- 创建一个软件定时器,并返回一个句柄,用该句柄可以引用锁创建的软件定时器。
- 每个软件定时器都需要占用一点 RAM ,用来保持软件定时器的状态。
- 如果通过此函数创建了一个软件定时器,那么定时器所需内存将被自动分配在 FreeRTOS 的堆中。
- 如果通过
xTimerCreateStatic()
创建了软件定时器,那么软件定时器所用内存则有程序员所确定。
- 创建的软件定时器并不会自动运行,这些函数都可以用来启动软件定时器:
xTimerStart()
xTimerStartFromISR()
xTimerResetFromISR()
xTimerChangePeriod()
xTimerChangePeriodFromISR()
参数:
pcTimerName:
分配给定时器的纯文本名称,纯粹是为了调试。xTimerPeriod:
定时器周期。- 定时器以滴答周期的倍数指定。
pdMS_TO_TICKS()
宏可以把以毫秒为单位的时间转换为以刻度为单位的时间。
例如:- 如果一个时间定时器必须在 100 个刻度到期,
xNewPriod
可以直接设置为 100。 - 如果一个定时器必须在 500ms 后到期,
xNewPeriod
可以设置为pdMS_TO_TICKS(500)
,假设configTICK_RATE_HZ
小于等于 1000。
- 如果一个时间定时器必须在 100 个刻度到期,
- 定时器以滴答周期的倍数指定。
uxAutoReload:
- 设置为
pdTRUE
来定义一个自动重启的定时器。- 定时器一旦启动,自动重启的定时器将根据
xTimerPeriod
反复到期。
- 定时器一旦启动,自动重启的定时器将根据
- 设置为
pdFALSE
来定义一个不自动重启的定时器。- 定时器一旦启动,只会过期一次,只能通过手动的方式重启定时器。
- 设置为
pvTimerID:
分配给定时器的标识符。- 标识符可以在定时器创建后用
vTimerSetTimer()
函数更新。 - 如果同一个函数被多个定时器回调,定时器标识符可以在回调函数内部调查哪个定时器已到期。此外,定时器标识符可以用在和调用定时器的回调函数之间存储一个值。
- 标识符可以在定时器创建后用
pxCallbackFunction:
定时器过期后调用的回调函数。回调函数必须有被定义的原型。回调函数的返回值必须是void
,它的输入参数必须是一个TimerHandle
。
void vCallbackFunctionExample( TimerHandle_t xTimer );
返回值:
NULL:
软件定时器因为堆中没有足够的内存而无法被创建。Any other value:
软件定时器成功创建,并且返回一个可以引用此定时器的句柄。
xTimerStart()
#include "FreeRTOS.h"
#include "timers.h"
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );
函数说明:
- 启动软件定时器。
xTimerStartFromISR()
是一个等效函数,它可以在 中断 中启动定时器。 - 如果定时器还没有运行,定时器会计算相对于调用
xTimerStart()
时的到期时间。 - 如果定时器未停止、已删除、未重置,则计时器关联的回调函数将在调用
xTimerStart()
后的'n'
个刻度后被调用,其中'n'
是计时器的定义周期。
参数:
xTimer:
计时器将被重置(reset)、启动(started)或者重启(restarted)。xTicksToWait:
计时器函数并非由 FreeRTOS 的核心函数提供,而是由定时器服务( or deamon )提供。FreeRTOS API 发送命令到定时器任务的队列,如果队列已满,xTicksToWait
指定任务处于阻塞状态的最长时间,以等待计时器命令上的队列空间可用,设置为portMAX_DELAY
为等待无限多的时间。
返回值:
pdPASS:
成功启动定时器。pdFAIL:
因为定时器任务的消息队列已满,消息未能在超时时间之内发送到时间定时器的队列。
示例代码
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "freertos/queue.h"
#include "freertos/timers.h"
void vCallbackFunction(TimerHandle_t timer1)
{
printf("[TIMER_1 CALLBACK]: One shot timer.\n");
}
void app_main(void)
{
// 创建一个定时器,定时器名字"Timer 1",周期1s,周期结束之后重新启动,定时器标识符为0,定时器回调函数为 vCallbackFunction
TimerHandle_t xTimer1 = xTimerCreate("Timer 1", pdMS_TO_TICKS(1000), pdTRUE, 0, vCallbackFunction);
// 启动定时器,等待时间为无限
xTimerStart(xTimer1, portMAX_DELAY);
}
示例输出
根据输出可以观察到定时器(xTimer1)的回调函数在不断被调用。
xTimerStop()
#include "FreeRTOS.h"
#include "timers.h"
BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );
函数说明:
- 停止定时器的运行。
xTimerStopISR()
的一个等效函数,可以在中断中使用。
参数:
xTimer:
定时器句柄。xTicksToWait:
超时等待时长。
返回值:
pdPASS:
成功停止定时器。pdFAIL:
未能成功停止定时器,因为定时器守护任务的消息队列已满。
示例代码
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "freertos/queue.h"
#include "freertos/timers.h"
void vCallbackFunction(TimerHandle_t timer1)
{
printf("[TIMER_1 CALLBACK]: In timer.\n");
}
void app_main(void)
{
// 创建一个定时器,定时器名字"Timer 1",周期1s,周期结束之后重新启动,定时器标识符为0,定时器回调函数为 vCallbackFunction
TimerHandle_t xTimer1 = xTimerCreate("Timer 1", pdMS_TO_TICKS(1000), pdTRUE, 0, vCallbackFunction);
xTimerStart(xTimer1, portMAX_DELAY);
for (int delay = 5; delay > 0; delay--) {
printf("[MAIN]: The timer will stoped in %d second(s).\n", delay);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
xTimerStop(xTimer1, portMAX_DELAY);
}
示例输出
定时器在设定for
循环结束之后停止。
官方文档函数参考
271 5.1 pcTimerGetName()
[[FreeRTOS_Reference_Manual_V10.0.0.pdf#page=271&selection=0,3,4,16|FreeRTOS_Reference_Manual_V10.0.0, page 271]]
pcTimerGetName()
#include "FreeRTOS.h"
#include "timer.h"
const char *pcTimerGetName( TimerHandle_t xTimer );
函数说明:
- 返回人类可读的定时器名字。
参数:
xTimer:
将要取得定时器名字的句柄。
返回值:
- 返回一个指针,包含定时器的名字。
示例代码:
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "freertos/queue.h"
#include "freertos/timers.h"
void vCallbackFunction(TimerHandle_t timer1)
{
const char *timer_name = pcTimerGetName(timer1);
printf("[TIMER_1 CALLBACK]: In timer \"%s\".\n", timer_name);
}
void app_main(void)
{
// 创建一个定时器,定时器名字"Timer 1",周期1s,周期结束之后重新启动,定时器标识符为0,定时器回调函数为 vCallbackFunction
TimerHandle_t xTimer1 = xTimerCreate("Timer 1", pdMS_TO_TICKS(1000), pdTRUE, 0, vCallbackFunction);
xTimerStart(xTimer1, portMAX_DELAY);
for (int delay = 5; delay > 0; delay--) {
printf("[MAIN]: The timer will stoped in %d second(s).\n", delay);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
//
const char *timer_name = pcTimerGetName(xTimer1);
printf("[MAIN]: The timer \"%s\" will stop.\n", timer_name);
xTimerStop(xTimer1, portMAX_DELAY);
}
在实际编写程序时,可以根据定时器名字的不同,来让程序执行不同的代码。
pvGetTimerGetTimerID()
#include "FreeRTOS.h"
#include "timer.h"
void *pvTimerGetTimerID( TimerHandle_t xTimer );
函数说明:
- 获取定时器的ID。
参数:
- 定时器的句柄。
返回值:
(void *):
时间定时器的ID。
此处可以用
uint32_t
强制转换函数返回值,并用uint32_t
来接收返回值。
两个时间定时器使用同一个回调函数,获取Name
和ID
:
示例代码
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "freertos/queue.h"
#include "freertos/timers.h"
void callback_function(TimerHandle_t timer)
{
// 获取定时器名字
const char *timer_name = pcTimerGetName(timer);
// 获取定时器ID
uint32_t timer_id = (uint32_t)pvTimerGetTimerID(timer);
printf("[CALLBACK FUNCTION]: In timer \"%s\", ID: %ld\n", timer_name, timer_id);
}
void app_main(void)
{
TimerHandle_t timer1 = xTimerCreate("Timer 1", pdMS_TO_TICKS(1000), pdTRUE, (void *)0, callback_function);
TimerHandle_t timer2 = xTimerCreate("Timer 2", pdMS_TO_TICKS(1000), pdTRUE, (void *)1, callback_function);
const char *timer_name = NULL;
xTimerStart(timer1, portMAX_DELAY);
timer_name = pcTimerGetName(timer1);
for (int delay = 5; delay > 0; delay--) {
printf("[MAIN]: Timer \"%s\" will stop in %d second(s).\n", timer_name, delay);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
xTimerStop(timer1, portMAX_DELAY);
xTimerStart(timer2, portMAX_DELAY);
timer_name = pcTimerGetName(timer2);
for (int delay = 5; delay > 0; delay--) {
printf("[MAIN]: Timer \"%s\" will stop in %d second(s).\n", timer_name, delay);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
xTimerStop(timer2, portMAX_DELAY);
}
示例输出
![[14. 软件定时器(Software Timer)-20240615182443875.webp|1000]]
Timer 1 和 Timer 2 分别启动运行停止,因为时间定时器调用了相同的回调函数,所以回调函数打印出了不同的时间定时器名称和ID。
xTimerReset()
#include "FreeRTOS.h"
#include "timer.h"
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );
函数说明:
- 相当于看门狗的“喂狗”,表示让定时器重新启动。在中断过程中,存在等效函数
xTimerResetISR()
可以用。
参数:
xTimer:
时间定时器句柄。xTicksToWait:
超时等待时间。
返回值:
pdPASS:
重启时间定时器成功。pdFAIL:
重启时间定时器失败。
xTimerChangePeriod()
#include "FreeRTOS.h"
#include "timer.h"
BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, TickType_t xNewPeriod, TickType_t xTicksToWait );
函数说明:
- 改变定时器的周期,在中断下,存在等效函数
xTimerChangePeriodISR()
可以用。
参数:
xTimer:
需要改变定时器周期的定时器句柄。xNewPeriod:
新的周期。xTicksToWait:
超时等待时间。
返回值:
pdPASS:
定时器周期成功修改。pdFAIL:
定时器周期修改失败。
示例代码:
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "freertos/queue.h"
#include "freertos/timers.h"
void callback_function(TimerHandle_t timer)
{
const char *timer_name = pcTimerGetName(timer);
uint32_t *timer_id = (uint32_t *)pvTimerGetTimerID(timer);
printf("[CALLBACK FUNCTION]: In timer \"%s\", ID: %ld\n", timer_name, *timer_id);
}
void app_main(void)
{
static uint32_t timer1_id = 1;
TimerHandle_t timer1 = xTimerCreate("Timer 1", pdMS_TO_TICKS(1000), pdTRUE, (void *)&timer1_id, callback_function);
xTimerStart(timer1, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(5000)); // 让 timer1 运行 5s
xTimerChangePeriod(timer1, pdMS_TO_TICKS(500), portMAX_DELAY); // 改变定时器周期
vTaskDelay(pdMS_TO_TICKS(5000)); // 再让 timer1 运行 5s
xTimerStop(timer1, portMAX_DELAY);
}