目录
前言
在现代计算机系统和嵌入式系统中,定时器是一种常见且重要的工具。它们用于在指定的时间间隔或延迟后执行特定的任务或操作,对于任务调度、超时处理和事件触发等应用场景都有广泛的应用。本次学习将聚焦于FreeRTOS中定时器的使用,包括定时器的创建、启动、停止和删除等操作。通过深入学习和理解这些操作,我们将掌握如何利用FreeRTOS提供的软件定时器功能来实现精确和可靠的时间触发机制。
目标
- 理解定时器的概念
- 掌握FreeRTOS的Timer创建
- 掌握FreeRTOS的Timer启动
- 掌握FreeRTOS的Timer停止
- 掌握FreeRTOS的Timer删除
内容
定时器
定时器是计算机系统中的一种常用工具,用于在指定的时间间隔或延迟后执行特定的任务或操作。定时器可以用于各种应用场景,例如任务调度、超时处理、事件触发等。
在计算机系统中,定时器通常分为软件定时器和硬件定时器两种类型:
- 软件定时器。
软件定时器是通过软件实现的定时机制。它依赖于系统的时钟源和计时功能,通过不断地检查当前时间与预设的时间间隔来判断是否触发定时事件。软件定时器的精度和准确性受到系统时钟的影响,通常适用于对时间要求不太严格的应用场景。软件定时器一般由操作系统或应用程序提供,并通过任务调度器或中断服务例程来触发相应的任务或回调函数。
- 硬件定时器。
硬件定时器是由计算机系统的硬件部分实现的定时机制。它通常基于硬件的计时器或定时器模块,具有更高的精度和准确性。硬件定时器可以通过设置定时器的计数器、预分频器、中断等参数来实现特定的定时功能。硬件定时器常用于实时操作系统、嵌入式系统和实时应用中,能够提供可靠的定时功能和高精度的时间触发。
FreeRTOS中也具有定时器功能,属于软件定时器。
开发流程
Timer的创建
TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const BaseType_t xAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction );
函数参数说明:
const char * const pcTimerName
:定时器名称。const TickType_t xTimerPeriodInTicks
:定时器的周期const BaseType_t xAutoReload
:是否自动重载。void * const pvTimerID
:定时器标识IDTimerCallbackFunction_t pxCallbackFunction
:回调函数
返回值说明:
TimerHandle_t
:创建的定时器句柄。创建失败为NULL。
定时器周期
直译过来,就是表示多少次Tick运行一个周期。
稍微直白点说,多少次Tick执行一次回调函数。
如果需要再直白点描述,就需要把Tick概念了解清楚。在 FreeRTOS 中,Tick(时钟节拍)是指 FreeRTOS 内核使用的基本时间单位。它代表了内核中的时间流逝。在FreeRTOSConfig.h
中默认配置有这个Tick的频率,configTICK_RATE_HZ
默认值为1000,表示1秒钟有1000个Tick。Tick可以理解为数数,1秒钟数1000下。
回到定时器周期描述上来说,简单来说,操作系统数多少下,执行1次回调函数。
人的认知,可能更好的理解多长时间执行一次这个函数,这里有可以将时间转换为tick计数的方法:pdMS_TO_TICKS
.
pdMS_TO_TICKS(1000);//参数为毫秒值
自动重载
自动重载,取值有两种:pdFALSE
和pdTrue
pdFALSE
:非自动重载模式。
在非自动重载模式下,定时器只会触发一次,在到期时停止计时,不会自动重新启动。这意味着在定时器到期后,需要手动调用 xTimerStart() 函数重新启动定时器,才能触发下一次定时。
pdTRUE
:自动重载模式。
在自动重载模式下,定时器在每次到期后会自动重新启动,以便周期性地触发任务或回调函数的执行。定时器到期后,会重新开始计时,等待下一个到期时刻。
回调函数
// typedef void (* TimerCallbackFunction_t)( TimerHandle_t xTimer );
void callback(TimerHandle_t xTimer);
定时器标识
通常多个定时器公用一个回调时,可以通过标识来判断是哪个个timer触发的。
// 定时器回调函数
void timerCallback(TimerHandle_t xTimer)
{
// 获取定时器的标识符
BaseType_t timerID = pvTimerGetTimerID(xTimer);
// 根据标识符处理相应的逻辑
if (timerID == 1)
{
// 处理定时器1的逻辑
}
else if (timerID == 2)
{
// 处理定时器2的逻辑
}
// ...
}
......
// 创建定时器1,并传递标识符1
TimerHandle_t timer1 = xTimerCreate("Timer1", pdMS_TO_TICKS(1000), pdTRUE, (void *)1, timerCallback);
// 创建定时器2,并传递标识符2
TimerHandle_t timer2 = xTimerCreate("Timer2", pdMS_TO_TICKS(2000), pdTRUE, (void *)2, timerCallback);
Timer开启和停止
开启:
xTimerStart(xTimer, xTicksToWait);
- 第一个参数为timer的句柄
- 第二个参数表示,多少个tick后启动timer,0表示立刻启动
在中断中开启:
// 在中断中启动Timer,需要特殊的函数
xTimerStartFromISR(timer2_handle, pxHigherPriorityTaskWoken);
停止:
xTimerStop(xTimer, xTicksToWait);
- 第一个参数为timer的句柄
- 第二个参数表示,多少个tick后停止timer,0表示立刻停止
Timer的开启和启动可以重复调用。
Timer删除
xTimerDelete(xTimer, xTickToWait);
- 第一个参数为timer的句柄
- 第二个参数表示,多少个tick后删除timer,0表示立刻删除
开发案例
重复Timer和单次Timer
timer间隔固定时间执行逻辑
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "usart0.h"
TaskHandle_t start_handler;
TimerHandle_t timer1;
void Usart0_recv(uint8_t *data, uint32_t len) {
printf("recv: %s\r\n", data);
}
void timerCallback(TimerHandle_t xTimer) {
printf("timer\r\n");
}
static void GPIO_config() {
// 时钟初始化
rcu_periph_clock_enable(RCU_GPIOA);
// 配置GPIO模式
gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}
int main(void)
{
NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
GPIO_config();
Usart0_init();
timer1 = xTimerCreate("Timer1", pdMS_TO_TICKS(1000), pdTRUE, (void *)1, timerCallback);
xTimerStart(timer1, 0);
vTaskStartScheduler();
while(1) {}
}
- 创建timer时配置是否自动重载
Timer启停控制
通过按键启动和停止Timer
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "usart0.h"
TaskHandle_t start_handler;
TaskHandle_t task_key_handler;
TimerHandle_t timer1;
void task_key(void *pvParameters) {
uint32_t flag = 0;
FlagStatus pre_state = RESET;
BaseType_t result;
while(1) {
FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
if(SET == state && pre_state == RESET) {
// 当前高电平, 上一次为低电平,按下
pre_state = state;
if(flag == 1) {
printf("stop\r\n");
xTimerStop(timer1, 0);
} else {
printf("start\r\n");
xTimerStart(timer1, 0);
}
flag ++;
if(flag == 2) flag = 0;
} else if(RESET == state && pre_state == SET) {
// 当前高电平, 上一次为低电平,抬起
pre_state = state;
}
vTaskDelay(20);
}
}
void Usart0_recv(uint8_t *data, uint32_t len) {
printf("recv: %s\r\n", data);
}
void timerCallback(TimerHandle_t xTimer) {
printf("timer\r\n");
}
static void GPIO_config() {
// 时钟初始化
rcu_periph_clock_enable(RCU_GPIOA);
// 配置GPIO模式
gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}
void start_task(void *pvParameters) {
GPIO_config();
Usart0_init();
taskENTER_CRITICAL();
timer1 = xTimerCreate("Timer1", pdMS_TO_TICKS(1000), pdTRUE, (void *)1, timerCallback);
xTaskCreate(task_key, "task_key", 64, NULL, 2, &task_key_handler);
vTaskDelete(start_handler);
taskEXIT_CRITICAL();
}
int main(void)
{
NVIC_SetPriorityGrouping(NVIC_PRIGROUP_PRE4_SUB0);
xTaskCreate(start_task, "start_task", 128, NULL, 1, &start_handler);
vTaskStartScheduler();
while(1) {}
}
中断中启停Timer
在中断中启动和停止Timer
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "usart0.h"
TaskHandle_t start_handler;
TaskHandle_t task_key_handler;
TimerHandle_t timer1;
void task_key(void *pvParameters) {
uint32_t flag = 0;
FlagStatus pre_state = RESET;
BaseType_t result;
while(1) {
FlagStatus state = gpio_input_bit_get(GPIOA, GPIO_PIN_0);
if(SET == state && pre_state == RESET) {
// 当前高电平, 上一次为低电平,按下
pre_state = state;
if(flag == 1) {
printf("stop\r\n");
xTimerStop(timer1, 0);
} else {
printf("start\r\n");
xTimerStart(timer1, 0);
}
flag ++;
if(flag == 2) flag = 0;
} else if(RESET == state && pre_state == SET) {
// 当前高电平, 上一次为低电平,抬起
pre_state = state;
}
vTaskDelay(20);
}
}
void Usart0_recv(uint8_t *data, uint32_t len) {
printf("recv: %s\r\n", data);
if(data[0] == 0x00) {
printf("start \r\n");
xTimerStartFromISR(timer1, NULL);
} else if(data[0] == 0x01) {
printf("stop \r\n");
xTimerStopFromISR(timer1, NULL);
}
}
void timerCallback(TimerHandle_t xTimer) {
printf("timer\r\n");
}
static void GPIO_config() {
// 时钟初始化
rcu_periph_clock_enable(RCU_GPIOA);
// 配置GPIO模式
gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);
}
void start_task(void *pvParameters) {
GPIO_config();
Usart0_init();
taskENTER_CRITICAL();
timer1 = xTimerCreate("Timer1", pdMS_TO_TICKS(1000), pdTRUE, (void *)1, timerCallback);
xTaskCreate(task_key, "task_key", 64, NULL, 2, &task_key_handler);
vTaskDelete(start_handler);
taskEXIT_CRITICAL();
}
int main(void)
{
// 配置全局系统优先级分组
nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
xTaskCreate(start_task, "start_task", 128, NULL, 1, &start_handler);
vTaskStartScheduler();
while(1) {}
}
总结
本次学习中,我们深入探讨了FreeRTOS中软件定时器的基本概念和操作流程。首先,我们了解了定时器的概念及其在计算机系统中的重要性,包括软件定时器和硬件定时器的区别。在此基础上,我们详细介绍了FreeRTOS中定时器的创建、启动、停止和删除等操作方法,以及相关参数的含义和使用场景。特别是,我们重点讨论了定时器的周期设定和自动重载模式的区别,以及如何通过回调函数处理定时器到期时的任务执行。通过本次学习,相信您已经掌握了在FreeRTOS中使用定时器的基本技能,能够在实际应用中灵活运用定时器实现所需的时间控制功能。