1,软件定时器的简介(了解)
定时器:
从指定的时刻开始,经过一个指定时间,然后触发一个超时事件,用户可自定义定时器的周期
硬件定时器:
芯片本身自带的定时器模块,硬件定时器的精度一般很高,每次在定时时间到达之后就会自动触发一个中断,用户在中断服务函数中处理信息。
软件定时器:
是指具有定时功能的软件,可设置定时周期,当指定时间到达后要调用回调函数(也称超时函数),用户在回调函数中处理信息
软件定时器优缺点
优点:
硬件定时器数量有限,而软件定时器理论上只需有足够内存,就可以创建多个;
使用简单、成本低
缺点:
软件定时器相对硬件定时器来说,精度没有那么高(因为它以系统时钟为基准,系统时钟中断优先级又是最低,容易被打断)。 对于需要高精度要求的场合,不建议使用软件定时器。
FreeRTOS软件定时器特点
可裁剪: 软件定时器是可裁剪可配置的功能, 如果要使能软件定时器,需将configUSE_TIMERS 配置项配置成 1
单次和周期: 软件定时器支持设置成:单次定时器或周期定时器
注意:软件定时器的超时回调函数是由软件定时器服务任务调用的,软件定时器的超时回调函数本身不是任务,因此不能在该回调函数中使用可能会导致任务阻塞的 API 函数。
软件定时器服务任务作用:
1、负责软件定时器超时的逻辑判断
2、调用超时软件定时器的超时回调函数
3、处理软件定时器命令队列
软件定时器的命令队列
FreeRTOS 提供了许多软件定时器相关的 API 函数,这些 API 函数大多都是往定时器的队列中写入消息(发送命令),这个队列叫做软件定时器命令队列,是提供给 FreeRTOS 中的软件定时器使用的,用户是不能直接访问的。
软件定时器的相关配置
当FreeRTOS 的配置项 configUSE_TIMERS 设置为1,在启动任务调度器时,会自动创建软件定时器的服务/守护任务prvTimerTask( ) ;
软件定时器服务任务的优先级为 configTIMER_TASK_PRIORITY = 31;
定时器的命令队列长度为 configTIMER_QUEUE_LENGTH = 5 ;
注意:软件定时器的超时回调函数是在软件定时器服务任务中被调用的,服务任务不是专为某个定时器服务的,它还要处理其他定时器。
所以,定时器的回调函数不要影响其他“人”:
1、回调函数要尽快实行,不能进入阻塞状态,即不能调用那些会阻塞任务的 API 函数,如:vTaskDelay() ;
2、访问队列或者信号量的非零阻塞时间的 API 函数也不能调用。
2,软件定时器的状态(熟悉)
软件定时器共有两种状态:
休眠态: 软件定时器可以通过其句柄被引用,但因为没有运行,所以其定时超时回调函数不会被执行
运行态: 运行态的定时器,当指定时间到达之后,它的超时回调函数会被调用
注意:新创建的软件定时器处于休眠状态 ,也就是未运行的!
问题:如何让软件定时器从休眠态转变为运行态?
发送命令队列
FreeRTOS 提供了两种软件定时器:
单次定时器:单次定时器的一旦定时超时,只会执行一次其软件定时器超时回调函数,不会自动重新开启定时,不过可以被手动重新开启。
周期定时器:周期定时器的一旦启动以后就会在执行完回调函数以后自动的重新启动 ,从而周期地执行其软件定时器回调函数。
Timer1:周期定时器,定时超时时间为 2 个单位时间,开启后,一直以2个时间单位间隔重复执行;
Timer2:单次定时器,定时超时时间为 1 个单位时间,开启后,则在第一个超时后就不在执行了。
软件定时器的状态转换图(熟悉)
单次定时器状态转换图:
周期定时器状态转换图:
4,软件定时器结构体成员介绍(熟悉)
typedef struct{const char * pcTimerName /* 软件定时器名字 */ListItem_t xTimerListItem /* 软件定时器列表项 */TickType_t xTimerPeriodInTicks; /* 软件定时器的周期 */ void * pvTimerID /* 软件定时器的ID */TimerCallbackFunction_t pxCallbackFunction; /* 软件定时器的回调函数 */#if ( configUSE_TRACE_FACILITY == 1 )UBaseType_t uxTimerNumber /* 软件定时器的编号,调试用 */#endifuint8_t ucStatus; /* 软件定时器的状态 */} xTIMER;
5, FreeRTOS软件定时器相关API函数(熟悉)
5.1,创建软件定时器API函数
TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction );
5.2,开启软件定时器API函数
BaseType_t xTimerStart( TimerHandle_t xTimer, const TickType_t xTicksToWait );
5.3,停止软件定时器API函数
BaseType_t xTimerStop( TimerHandle_t xTimer, const TickType_t xTicksToWait);
5.4,复位软件定时器API函数
BaseType_t xTimerReset( TimerHandle_t xTimer, const TickType_t xTicksToWait);
该功能将使软件定时器的重新开启定时,复位后的软件定时器以复位时的时刻作为开启时刻重新定时
5.5,更改软件定时器超时时间API函数
BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, const TickType_t xNewPeriod, const TickType_t xTicksToWait);
6,FreeRTOS软件定时器实验(掌握)
1、实验目的:学习 FreeRTOS 的软件定时器相关API函数的使用 。
2、实验设计:将设计两个任务:start_task、task1
两个任务的功能如下:
start_task 用来创建task1任务,并创建两个定时器(单次和周期)
task1 :用于按键扫描,并对软件定时器进行开启、停止操作
#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 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );#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);TimerHandle_t timer1_handle=0;
TimerHandle_t timer2_handle=0;
/*** @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();}
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();}
/* 任务一,用于扫描按键,当KEY0按下,发送任务通知值 */
void task1( void * pvParameters )
{uint8_t key;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);}}
void timer1_callback(TimerHandle_t pxTimer)
{static uint32_t timer = 0;printf("timer1的运行次数:%d\r\n",++timer);}void timer2_callback(TimerHandle_t pxTimer)
{static uint32_t timer = 0;printf("timer2的运行次数:%d\r\n",++timer);}