- 本节主要需要掌握以下内容:
1,软件定时器的简介(了解)
2,软件定时器的状态(熟悉)
3,单次定时器和周期定时器(熟悉)
4,软件定时器结构体成员介绍(熟悉)
5,FreeRTOS软件定时器相关API函数(熟悉)
6,FreeRTOS软件定时器实验(掌握)
7,课堂总结(掌握)
一、软件定时器的简介(了解)
- 定时器:
从指定的时刻开始,经过一个特定事件,然后触发一个超时事件,用户可以自定义定时器的周期。(类似于闹钟,响一次还是周期响)
- 硬件定时器:
芯片自带的定时器模块,硬件定时器一般精度很高,每次在定时时间到达之后就会自动触发一个中断,用户在中断服务函数中处理信息。
- 软件定时器:
是指具有定时功能的软件,可设置定时周期,当指定时间到达后要调用回调函数(也称超时函数),用户在回调函数中处理信息。
1.1 软件定时器的优缺点?
- 优点:
- 硬件定时器数量优先,而软件定时器理论上只需要足够内存,就可以创建多个;
- 使用简单、成本低
- 缺点:
- 软件定时器相对硬件定时器来说,精度没有那么高(因为它以系统时钟为基准,系统时钟中断优先级又是最低,容易被打断)。对于需要高精度要求的场合,不建议使用软件定时器。
1.2 FreeRTOS软件定时器特点
可裁剪:软件定时器是可裁剪可配置的功能,如果要使能软件定时器,需要将configUSE_TIMERS配置成1
单次和周期:软件定时器支持设置成单次定时器或周期定时器
注意:软件定时器的超时回调函数是由软件定时器服务任务调用的(这个任务去调超时回调函数),软件定时器的超时回调函数本身不是任务,因此不能再该回调函数中使用可能会导致阻塞的API函数。
软件定时器服务任务:在调用函数vTaskStartScheduler()开启任务调度的时候,会创建两个任务(一个空闲任务),一个用于管理软件定时器的任务,这个任务就叫软件定时器任务。
软件定时器服务任务作用
- 负责软件定时器超时的逻辑判断
- 调用超时软件定时器的超时回调函数
- 处理软件定时器命令队列
1.3 软件定时器的命令队列
FreeRTOS提供了许多软件定时器相关的API函数,这些API函数大多都是往定时器的队列中写入消息(发送命令),这个队列叫软件定时器命令队列,是提供给FreeRTOS中的软件定时器使用的,用户是不能直接访问的。
软件定时器的相关配置
当FreeRTOS的配置项configUSE_TIMERS设置为1,在启动任务调度器时,会自动创建软件定时器的服务/守护任务prvTimerTask();
软件定时器服务任务的优先级为:configTIMER_TASK_PRIORITY = 31;(配置为优先级最大,这样一有软件定时器超时,就立马调用超时回调函数,保证软件定时器的实时性)
定时器的命令队伍长度为:configTIMER_QUEUE_LENGTH = 5 ;
注意:软件定时器的超时回调函数是在软件定时器服务任务中被调用的,服务任务不是专为某个定时器服务的,它还要处理其他定时器。
所以:定时器的回调函数不要影响“他人”;
1、回调函数要尽快实行,不能进入阻塞状态,即不能调用那些会阻塞任务的API函数,如:vTaskDelay()
2、访问队列或信号量的非零阻塞时间的API函数也不能调用。
二、软件定时器的状态
软件定时器共有两种状态:
休眠态:软件定时器可以通过其句柄被引用,但因为没有运行,所以其超时回调函数不会被执行
运行态:运行态的定时器,当指定时间到达之后,它的超时回调函数会被调用
注意:新创建的软件定时器处于休眠状态,也就是未运行的!
问题:如何让软件定时器从休眠态转变为运行态?
发送命令队列,比如说发送开启软件定时器,这个时候软件定时器就开始工作了,从休眠态转变为运行态
三、单次定时器和周期定时器(熟悉)
FreeRTOS提供了两种软件定时器:
单次定时器:单次定时器的一旦定时超时,只会执行一次其软件定时器超时回调函数,不会自动重新开启定时时,不过可以被手动重新开启。
周期定时器:周期定时器的一旦被启动以后就可以在执行完回调函数以后自动的重新启动,从而周期的执行其软件定时器回调函数。
下图为单次定时器和周期定时器的示意图:
Timer1:周期定时器,定时超时时间为2个单位时间,开启后,一直以2个时间单位间隔重复执行;
Timer2:单次定时器,定时超时时间为1个单位时间,开启后,则在第一个超时后就不再执行了。
软件定时器的状态转换图:
首先创建一个定时器,创建完之后是休眠态的,通过发送命令队列(开启、复位、更改超时时间),都可以让其转换成运行态,调用停止命令就可以从运行态转换为休眠态,或者单次一旦超时(调用超时回调函数),就会转换为休眠态。
周期定时器状态转换图:
周期定时器 超时之后只要调用超时回调函数,就会自动计时,时间到了超时,又自动计时。
四、软件定时器结构体成员介绍(熟悉)
五、FreeRTOS软件定时器相关API函数(熟悉)
函数 | 描述 |
xTimerCreate() | 动态方式创建软件定时器 |
xTimerCreateStatic() | 静态方式创建软件定时器 |
xTimerStart() | 开启软件定时器定时 |
xTimerStartFromISR() | 在中断中开启软件定时器定时 |
xTimerStop() | 停止软件定时器定时 |
xTimerStopFromISR() | 在中断中停止软件定时器定时 |
xTimerReset() | 复位软件定时器定时 |
xTimerResetFromISR() | 在中断中复位软件定时器定时 |
xTimerChangePeriod() | 更改软件定时器的定时超时时间 |
xTimerChangePeriodFromISR() | 在中断中更改定时超时时间 |
5.1,创建软件定时器API函数
形参 | 描述 |
pcTimerName | 软件定时器名 |
xTimerPeriodInTicks | 定时超时时间,单位:系统时钟节拍 |
uxAutoReload | 定时器模式, pdTRUE:周期定时器, pdFALSE:单次定时器 |
pvTimerID | 软件定时器 ID,用于多个软件定时器公用一个超时回调函数 |
pxCallbackFunction | 软件定时器超时回调函数 |
返回值 | 描述 |
NULL | 软件定时器创建失败 |
其他值 | 软件定时器创建成功,返回其句柄 |
5.2,开启软件定时器API函数
形参 | 描述 |
xTimer | 待开启的软件定时器的句柄 |
xTickToWait | 发送命令到软件定时器命令队列的最大等待时间 |
返回值 | 描述 |
pdPASS | 软件定时器开启成功 |
pdFAIL | 软件定时器开启失败 |
5.3,停止软件定时器API函数
形参 | 描述 |
xTimer | 待停止的软件定时器的句柄 |
xTickToWait | 发送命令到软件定时器命令队列的最大等待时间 |
返回值 | 描述 |
pdPASS | 软件定时器停止成功 |
pdFAIL | 软件定时器停止失败 |
5.4,复位软件定时器API函数
该功能将使软件定时器的重新开启定时,复位后的软件定时器以复位时的时刻作为开启时刻重新定时
形参 | 描述 |
xTimer | 待复位的软件定时器的句柄 |
xTickToWait | 发送命令到软件定时器命令队列的最大等待时间 |
返回值 | 描述 |
pdPASS | 软件定时器复位成功 |
pdFAIL | 软件定时器复位失败 |
5.5,更改软件定时器超时时间API函数
形参 | 描述 |
xTimer | 待更新的软件定时器的句柄 |
xNewPeriod | 新的定时超时时间,单位:系统时钟节拍 |
xTickToWait | 发送命令到软件定时器命令队列的最大等待时间 |
返回值 | 描述 |
pdPASS | 软件定时器定时超时时间更改成功 |
pdFAIL | 软件定时器定时超时时间更改失败 |
六,FreeRTOS软件定时器实验(掌握)
6.1、实验目的:
学习 FreeRTOS 的软件定时器相关API函数的使用。
6.2、实验设计:
将设计三个任务:start_task、task1
三个任务的功能如下:
- start_task:用来创建task1任务,并创建两个定时器(单次和周期)
(发现全文把好多单次 打成了 单词,如有单词,则指单次)
- task1:用于按键扫描,并对软件定时器进行开启、停止操作
6.3 实验代码
****************************************************************************************************
*/
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1( void * pvParameters );
void timer1_callback( TimerHandle_t pxTimer );
void timer2_callback( TimerHandle_t pxTimer );
/******************************************************************************************************/
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
xTaskCreate((TaskFunction_t ) start_task,
(char * ) "start_task",
(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t * ) &start_task_handler );
vTaskStartScheduler();
}
TimerHandle_t timer1_handle = 0; /* 单次定时器 */
TimerHandle_t timer2_handle = 0; /* 周期定时器 */
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 单次定时器 */
timer1_handle = xTimerCreate( "timer1",
500,
pdFALSE,
(void *)1,
timer1_callback );
/* 周期定时器 */
timer2_handle = xTimerCreate( "timer2",
2000,
pdTRUE,
(void *)2,
timer2_callback );
xTaskCreate((TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handler );
vTaskDelete(NULL);
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/* 任务一,按键扫描并控制软件定时器 */
void task1( void * pvParameters )
{
uint8_t key = 0;
while(1)
{
key = key_scan(0);
if(key == KEY0_PRES)
{
xTimerStart(timer1_handle,portMAX_DELAY);
xTimerStart(timer2_handle,portMAX_DELAY);
}else if(key == KEY1_PRES)
{
xTimerStop(timer1_handle,portMAX_DELAY);
xTimerStop(timer2_handle,portMAX_DELAY);
}
vTaskDelay(10);
}
}
/* timer1的超时回调函数 */
void timer1_callback( TimerHandle_t pxTimer )
{
static uint32_t timer = 0; /* 不加static 每次打印都是1 不然每次进来都会把timer清零 */
printf("timer1的运行次数:%d\r\n",++timer);
}
/* timer2的超时回调函数 */
void timer2_callback( TimerHandle_t pxTimer )
{
static uint32_t timer = 0; /* 不加static 每次打印都是1 不然每次进来都会把timer清零 */
printf("timer2的运行次数:%d\r\n",++timer);
}