FreeRTOS-软件定时器
·
软件定时器
软件定时器简介
FreeRTOS中的软件定时器是一种轻量级的时间管理工具,用于在任务中创建和管理定时器,软件定时器是基于FreeRTOS内核提供的时间管理功能实现的,允许开发者创建、启动、停止、删除和管理定时器,从而实现在任务中对事件的灵活控制
- 硬件定时器:由芯片或微控制器提供,独立于CPU,可以在后台运行,不受任务调度器的影响,具有更高的精度和分辨率,具有更高的精度和分辨率,占用硬件资源,不会增加CPU的负载
- 软件定时器:FreeRTOS提供的功能来模拟定时器,依赖系统的任务调度器来进行计时和任务调度,FreeRTOS内部基于系统节拍tick实现的一种定时事件机制,精度和分辨率可能受到任务调度的影响,不需要额外的硬件资源,但可能会增加系统的负载A(软件定时器不是独立硬件,而是依赖于系统节拍中断,FreeRTOS调度器,一个内部的定时器任务和一个内部的命令队列)
当定时器的周期到期时,定时器的回调函数会被执行,定时器回调函数在定时器服务任务的上下文中执行,在定时器回调函数中不能调用导致阻塞的API函数
软件定时器服务任务是任务调度器中一个特殊任务,专门用于管理和维护软件定时器的正常运行,如果configUSE_TIMERS 设置为1,在开启任务调度器的时候,会自动创建软件定时器服务的任务,它主要负责软件定时器超时逻辑判断、调用超时软件定时器的超时回调函数、处理软件定时器命令队列
软件定时器的状态
FreeRTOS中的软件定时器有两种类型:一次性定时器和周期性定时器
FreeRTOS中的软件定时器有三种状态:
- 未创建:软件定时器被创建之前的状态,在这个状态下,定时器的数据结构已经被定义,但尚未通过xTimerCreate()函数创建
- 已创建:软件定时器已被成功创建,但尚未启动,在这个状态下,可对定时器
- 已运行:软件定时器已经被启动,正在运行中,在这个状态下定时器会按照预定的周期定时触超时事件,执行注册的回调函数
软件定时器相关API
| 函数 | 描述 |
|---|---|
| xTimerCreate() | 动态方式创建软件定时器 |
| xTimerCreateStatic() | 静态方式创建软件定时器 |
| xTimerStart() | 开启软件定时器定时 |
| xTimerStartFromISR() | 在中断中开启软件定时器定时 |
| xTimerStop() | 停止软件定时器定时 |
| xTimerStopFromISR() | 在中断中停止软件定时器定时 |
| xTimerReset() | 复位软件定时器定时 |
| xTimerResetFromISR() | 在中断中复位软件定时器定时 |
| xTimerChangPeriod() | 更改软件定时器的定时超时时间 |
| xTimerChangPeriodFromISR() | 在中断中更改定时超时时间 |
软件定时器历程
首先是要在配置文件进行以下的显式定义
/* 软件定时器相关定义 */
#define configUSE_TIMERS 1 /* 1: 使能软件定时器, 默认: 0。使能后需指定下面3个 */
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) /* 定义软件定时器任务的优先级 */
#define configTIMER_QUEUE_LENGTH 5 /* 定义软件定时器命令队列的长度*/
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE * 2) /* 定义软件定时器任务的栈空间大小*/
- configUSE_TIMERS:表示是否启用软件定时器
- configTIMER_TASK_PRIORITY:定时器服务任务优先级,决定回调执行的及时性,一般不要太低否则高优先级任务一直跑时,定时器回调可能延后
- configTIMER_QUEUE_LENGTH:定时器命令队列长度,调用xTimerStart()、xTimerStop()、xTimerReset()等本质上都是往这里发命令,如果太小命令可能发送失败
- configTIMER_TASK_STACK_DEPTH:定时器服务任务栈大小,回调里做的事情太多栈太小可能会溢出
软件定时器的整个工作流程通常是先调用xTimerCreate()创建一个软件定时器,然后调用xTimerStart(),这个API本质不是立刻启动硬件,而是向定时器命令队列发送一个启动命令,定时器服务任务取出命令,开始管理这个定时器,到了设定tick数后,定时器服务任务执行自定义的回调函数,如果是自动重装载定时器则会继续下一轮,如果是单次定时器,执行一次就停止
- 注意:软件定时器的回调函数不是在中断中执行的,是在FreeRTOS的定时器服务任务上下文中执行,所以在回调中适合设置标志位、发消息到队列、发信号量、通知任务、做很短小的状态切换,不适合长事件堵塞、vTaskDelay()、大量的printf、执行复杂计算、访问可能长时间等待的资源,因为一个回调执行太久会拖慢其他软件定时器,所有软件定时器本质上都是靠同一个定时器服务任务调度,所以最好的是在回调函数中唤醒另一个任务在任务中做复杂的任务
#include "FreeRTOS_demo.h"
#include "FreeRTOS.h"//这两个头文件是必须要引入的
#include "task.h"
#include "LED.h"
#include "Key.h"
#include "stdio.h"
#include "timers.h"
//启动任务的配置
#define START_TASK_STACK 128
#define START_TASK_PRIORITY 1
TaskHandle_t start_task_handle;
void start_task(void * pvParameters);//启动任务
//任务1的配置
#define TASK1_STACK 128
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
void task1(void * pvParameters);//启动任务
//定时器1的回调函数(单次)
void timer1_callback(TimerHandle_t xTimer);
//软件定时器的句柄
TimerHandle_t timer1_handle;
//定时器2的回调函数(周期)
void timer2_callback(TimerHandle_t xTimer);
//软件定时器的句柄
TimerHandle_t timer2_handle;
void freertos_start(void)//启动FreeRTOS
{
/*创建一次性软件定时器*/
timer1_handle = xTimerCreate(
(char*)"timer1",//软件定时器的名称
(TickType_t)500,//超时的时间等于500个FreeRTOS时钟节拍
(BaseType_t)pdFALSE,//是否自动重载,pdFALSE一次性
(void*)1,//定时器的唯一ID
(TimerCallbackFunction_t)timer1_callback//定义好的回调函数
);
if(timer1_handle != NULL)
{
printf("timer1创建成功");
}
/*创建周期性软件定时器*/
timer2_handle = xTimerCreate(
(char*)"timer2",//软件定时器的名称
(TickType_t)500,//超时的时间等于500个FreeRTOS时钟节拍
(BaseType_t)pdTRUE,//是否自动重载,pdFALSE一次性
(void*)2,//定时器的唯一ID
(TimerCallbackFunction_t)timer2_callback//定义好的回调函数
);
if(timer2_handle != NULL)
{
printf("timer2创建成功");
}
//1.创建一个启动任务
xTaskCreate((TaskFunction_t) start_task, //任务函数地址,前面是类型强转
(char*) "start_task", //取任务名
(configSTACK_DEPTH_TYPE) START_TASK_STACK, //任务栈大小,最小是128字节
(void *) NULL, //不需要形参就给一个NULL
(UBaseType_t) START_TASK_PRIORITY, //任务优先级,这里的优先级默认为5,有5个优先级
(TaskHandle_t *) &start_task_handle); //任务句柄
//任务的相关信息会保存到一个tcb地址,然后这个地址会赋值给这个任务句柄,所以这只用创建一个任务句柄即可
//2.启动调度器
vTaskStartScheduler();//这是必须要启动的,会自动创建空闲函数
}
void start_task(void * pvParameters)//启动任务,用来创建其他task
{
//进入临界区,临界区里面的代码不会被打断
taskENTER_CRITICAL();
//创建3个任务
xTaskCreate((TaskFunction_t) task1,
(char*) "task1",
(configSTACK_DEPTH_TYPE) TASK1_STACK,
(void *) NULL,
(UBaseType_t) TASK1_PRIORITY,
(TaskHandle_t *) &task1_handle);
//退出临界区
taskEXIT_CRITICAL();
//启动任务只需要执行一次,用完就删除自己
vTaskDelete(NULL);
}
void task1(void * pvParameters)//任务1:用于按键扫描,并对软件定时器进行开启、停止操作
{
uint8_t key = 0;
BaseType_t res = 0;
while(1)
{
key = Key_Detect();
if(key==KEY1_PRESS)
{
/*开启软件定时器*/
//往任务队列发送启动任务命令,然后定时器任务识别到这个命令才会启动,所以有一个等待时间
res = xTimerstart(timer1_handle,portMAX_DELAY);//timer句柄,等待时间
if(res != pdFAIL)
{
printf("timer1启动成功");//时间到了就执行回调函数
}
res = xTimerstart(timer2_handle,portMAX_DELAY);//timer句柄,等待时间
if(res != pdFAIL)
{
printf("timer2启动成功");
}
}
else if(key==KEY2_PRESS)
{
/*停止软件定时器*/
res = xTimerstop(timer1_handle,portMAX_DELAY);//timer句柄,等待时间
if(res != pdFAIL)
{
printf("timer1停止成功");
}
res = xTimerstop(timer2_handle,portMAX_DELAY);//timer句柄,等待时间
if(res != pdFAIL)
{
printf("timer2停止成功");
}
}
vTaskDelay(500);
}
}
void timer1_callback(TimerHandle_t xTimer)
{
static uint16_t timer1_count = 0;
printf("timer1超时回调了%d次",++timer1_count);
}
void timer2_callback(TimerHandle_t xTimer)
{
static uint16_t timer2_count = 0;
printf("timer1超时回调了%d次",++timer2_count);
}更多推荐
所有评论(0)