目录
一、软件定时器简介
定时器是MCU的外设,硬件定时器不仅仅能用来定时,还能用作PWM输出、输入捕获等功能,其功能强大。但是硬件定时器是有限个数的,不同型号的MCU其定时器的个数也不一样。在FreeRTOS中提供了软件定时器,其功能较为简单,仅仅是将某个任务定时一段时间再执行而已。软件定时器是依据系统节拍来计时的,对于精度要求不高的任务可以使用软件定时器。软件定时器服务函数相当于硬件定时器中断服务函数。定时器服务函数中不能使用带有阻塞的函数,例如vTaskDelay()、vTaskDelayUnti()以及带有非0阻塞的访问队列或信号量的函数。
二、单次定时器与周期定时器
软件定时器有单次的和周期的。单次定时器即该定时器只计时一次(对应任务执行一次)就自动关闭了;周期定时器即定时器周期性计时(对应任务周期性执行),需要关闭则要手动关闭。
三、定时器相关的宏设置
文件"FreeRTOSConfig.h"中。
#define configUSE_TIMERS 1
// 1:使用软件定时器 0:不使用软件定时器
#define configTIMER_TASK_PRIORITY ( 2 )
//定时器服务任务的任务优先级(范围与任务优先级一致)
#define configTIMER_QUEUE_LENGTH 10
//设置定时器命令队列的队列长度
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 )
//服务任务的任务堆栈大小,单位字!
四、常用API函数
相关函数及定义在文件”timers.c”及”timers.h”中。
1、创建定时器
(1)动态方法
TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction )
/*参数:
pcTimerName: 定时器名字,字符串类型;
xTimerPeriodInTicks:定时周期,单位为系统时钟节拍数;
uxAutoReload: 定时器模式,pdTRUE为周期性,pdFALSE为单次定时器;
pvTimerID: 定时器ID,当多个定时器使用同一服务函数时,通过ID来处理不同的定时器;
pxCallbackFunction: 定时器服务函数;
返回值:创建成功返回定时器句柄,失败NULL;
*/
(1)静态方法
所需要的内存需要用户自行分配。
TimerHandle_t xTimerCreateStatic( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction,
StaticTimer_t * pxTimerBuffer )
/*参数:
pcTimerName: 定时器名字,字符串类型;
xTimerPeriodInTicks:定时周期,单位为系统时钟节拍数;
uxAutoReload: 定时器模式,pdTRUE为周期性,pdFALSE为单次定时器;
pvTimerID: 定时器ID,当多个定时器使用同一服务函数时,通过ID来处理不同的定时器;
pxCallbackFunction: 定时器服务函数;
pxTimerBuffer: 指向StaticTimer_t类型变量,用于保存定时器结构体;
返回值:创建成功返回定时器句柄,失败NULL;
*/
2、开启定时器
在软件定时器创建完成的时候并没有开始运行,需要调用开启函数来开启。
(1)任务级
/*函数原型*/
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait )
/*
参数:
xTimer: 定时器句柄;
xTicksToWait: 阻塞时间;
返回值:
pdPASS:开启成功
pdFAIL:开启失败,命令发送失败
*/
/*函数宏定义*/
#define xTimerStart( xTimer, xTicksToWait ) \
xTimerGenericCommand( ( xTimer ),
tmrCOMMAND_START,
( xTaskGetTickCount() ),
NULL,
( xTicksToWait ) )
(2)中断级
/*函数原型*/
BaseType_t xTimerStartFromISR( TimerHandle_t xTimer,
BaseType_t *pxHigherPriorityTaskWoken );
/*
参数:
xTimer: 定时器句柄;
pxHigherPriorityTaskWoken:标记退出此函数后是否进行任务切换,只需提供变量来保存,当该值为pdTRUE
时需要进行任务切换;
返回值:
pdPASS:开启成功
pdFAIL:开启失败,命令发送失败
*/
/*函数宏定义*/
#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) \
xTimerGenericCommand( ( xTimer ),
tmrCOMMAND_START_FROM_ISR,
( xTaskGetTickCountFromISR() ),
( pxHigherPriorityTaskWoken ),
0U )
3、复位定时器
复位定时器并不是将定时器计数值清零,而是将响应的时间在该时刻延后一个计时周期。在未开启定时器状态下使用复位函数也能开启定时器(与开启函数使用同一个函数)。
(1)任务级
/*函数原型*/
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait )
/*
参数:
xTimer: 定时器句柄;
xTicksToWait: 阻塞时间;
返回值:
pdPASS:复位成功,即命令发送成功
pdFAIL:复位失败,即命令发送失败
*/
/*函数宏定义*/
#define xTimerReset( xTimer, xTicksToWait ) \
xTimerGenericCommand( ( xTimer ),
tmrCOMMAND_RESET,
( xTaskGetTickCount() ),
NULL,
( xTicksToWait ) )
(2)中断级
/*函数原型*/
BaseType_t xTimerResetFromISR( TimerHandle_t xTimer,
BaseType_t *pxHigherPriorityTaskWoken );
/*
参数:
xTimer: 定时器句柄;
pxHigherPriorityTaskWoken:标记退出此函数后是否进行任务切换,只需提供变量来保存,当该值为pdTRUE
时需要进行任务切换;
返回值:
pdPASS: 复位成功,即命令发送成功
pdFAIL: 复位失败,即命令发送失败
*/
/*函数宏定义*/
#define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) \
xTimerGenericCommand( ( xTimer ),
tmrCOMMAND_RESET_FROM_ISR,
( xTaskGetTickCountFromISR() ),
( pxHigherPriorityTaskWoken ),
0U )
4、停止定时器
(1)任务级
/*函数原型*/
BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );
/*
参数:
xTimer: 定时器句柄;
xTicksToWait:阻塞时间;
返回值:
pdPASS: 停止成功,即命令发送成功
pdFAIL: 停止失败,即命令发送失败
*/
/*函数宏定义*/
#define xTimerStop( xTimer, xTicksToWait ) \
xTimerGenericCommand( ( xTimer ),
tmrCOMMAND_STOP,
0U,
NULL,
( xTicksToWait ) )
(2)中断级
/*函数原型*/
BaseType_t xTimerStopFromISR( TimerHandle_t xTimer,
BaseType_t *pxHigherPriorityTaskWoken );
/*
参数:
xTimer: 定时器句柄;
pxHigherPriorityTaskWoken:标记退出此函数后是否进行任务切换,只需提供变量来保存,当该值为pdTRUE
时需要进行任务切换;
返回值:
pdPASS: 停止成功,即命令发送成功
pdFAIL: 停止失败,即命令发送失败
*/
/*函数宏定义*/
#define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) \
xTimerGenericCommand( ( xTimer ),
tmrCOMMAND_STOP_FROM_ISR,
0,
( pxHigherPriorityTaskWoken ),
0U )
五、定时器使用案例
创建一个软件定时器任务控制LED灯周期性翻转,使用按键控制定时器的开启、关闭和复位。
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "core_cm4.h"
#include "timers.h"
TaskHandle_t start_taskHandle; //任务句柄
void start_task(void *pvParameters); //函数声明
TaskHandle_t key_taskHandle; //任务句柄
void key_task(void *pvParameters); //函数声明
//定时器服务函数
void TimerTask_1(void *pvParameters);
TimerHandle_t TimerTask_1Handle_t; //定时器任务句柄
int main(void)
{
SysTick_Config(168000);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
// delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口
LED_Init(); //初始化LED端口
KEY_Init();
xTaskCreate( (TaskFunction_t ) start_task, //任务函数
(const char * ) "start_task", //函数名称
(configSTACK_DEPTH_TYPE ) 128, //堆栈大小
(void * ) NULL, //传给任务函数的参数
(UBaseType_t ) 1, //优先级
(TaskHandle_t * ) &start_taskHandle);//任务句柄
vTaskStartScheduler(); //开启任务调度
}
//开始任务任务函数
void start_task(void *pvParameters)
{
//taskENTER_CRITICAL(); //进入临界区
//创建按键任务
xTaskCreate( (TaskFunction_t ) key_task,
(const char * ) "key_task",
(configSTACK_DEPTH_TYPE ) 128,
(void * ) NULL,
(UBaseType_t ) 3,
(TaskHandle_t * ) &key_taskHandle);
//创建定时器任务
TimerTask_1Handle_t = xTimerCreate( ( char * ) "TimerTask_1", //定时器名称
( TickType_t ) 1000, //延迟周期
( UBaseType_t) pdTRUE, //周期性
( void * ) 1, //ID
(TimerCallbackFunction_t) TimerTask_1);//服务函数
vTaskDelete(start_taskHandle); //删除该任务
//taskEXIT_CRITICAL(); //退出临界区
}
//定时器服务函数
void TimerTask_1(void *pvParameters)
{
LED0=~LED0;
LED1=~LED1;
}
//按键任务函数
void key_task(void *pvParameters)
{
int key_num=0;
while(1)
{
key_num = KEY_Scan(0);
if(key_num == KEY0_PRES)
{
xTimerStart( TimerTask_1Handle_t, 1 );
printf("开启定时器任务\r\n");
}
else if(key_num == KEY1_PRES)
{
xTimerStop( TimerTask_1Handle_t, 1 );
printf("关闭定时器任务\r\n");
}
else if(key_num == WKUP_PRES)
{
xTimerReset( TimerTask_1Handle_t, 1 );
printf("复位定时器任务\r\n");
}
else
vTaskDelay(20);
}
}