软件定时器允许设置一段时间,当设置的时间达到后就执行指定的功能函数,被软件定时器调用的功能函数叫做定时器的回调函数。软件定时器的回调函数是在定时器服务任务中执行的,所以一定不能在回调函数中调用任何阻塞任务的API函数,比如VTaskDelay。 软件定时器分为两种,单次定时器和周期定时器,单次定时器就执行一次,然后停止运行,不能自动重启,但我们可以手动重新启动,周期定时器就是执行完回调函数以后自动的重新启动,这样回调函数就会周期性的执行。
0x01 定时器相关配置
定时器的配置在FreeRTOSConfig.h中
-
configUSE_TIMES 如果要使用软件定时器,configUSE_TIMES要设置为1。 -
configTIMER_TASK_PRIORITY 设置软件定时器服务任务的任务优先级 -
configTIMER_QUEUE_LENGTH 设置定时器命令队列的队列长度 -
configTIMER_TASK_STACK_DEPTH 设置定时器任务的任务堆栈大小,单位为字,对于STM32来说一个字是4字节
0x02 创建定时器
使用软件定时器首先要创建,创建定时器分为动态和静态两种创建方法。
- xTimerCreate():使用动态方法创建软件定时器
- xTimerCreateStatic():使用静态方法创建软件定时器
TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction )
- const char * const pcTimerName:软件定时器名字
- const TickType_t xTimerPeriodInTicks:软件定时器的定时周期,单位是时钟节拍数,可以借助portTICK_PERIOD_MS将ms单位转换为时钟节拍数,比如定时器的周期为100个时钟节拍的话,那么xTimerPeriodInTicks=100,当定时器周期为500ms的时候,xTimerPeriodInTicks =(500/portTICK_PERIOD_MS)
- const UBaseType_t uxAutoReload:设置定时器模式,但是定时器还是周期定时器,当uxAutoReload=pdTRUE的时候创建的是周期定时器,uxAutoReload=pdFALSE是单次定时器。
- void * const pvTimerID:定时器ID号。
- TimerCallbackFunction_t pxCallbackFunction :定时器回调函数,当定时器周期到了以后就会调用这个函数。
TimerHandle_t xTimerCreateStatic( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction,
StaticTimer_t *pxTimerBuffer )
- const char * const pcTimerName:软件定时器名字
- const TickType_t xTimerPeriodInTicks:软件定时器的定时周期,单位是时钟节拍数,可以借助portTICK_PERIOD_MS将ms单位转换为时钟节拍数,比如定时器的周期为100个时钟节拍的话,那么xTimerPeriodInTicks=100,当定时器周期为500ms的时候,xTimerPeriodInTicks =(500/portTICK_PERIOD_MS)
- const UBaseType_t uxAutoReload:设置定时器模式,但是定时器还是周期定时器,当uxAutoReload=pdTRUE的时候创建的是周期定时器,uxAutoReload=pdFALSE是单次定时器。
- void * const pvTimerID:定时器ID号。
- TimerCallbackFunction_t pxCallbackFunction :定时器回调函数,当定时器周期到了以后就会调用这个函数。
- StaticTimer_t *pxTimerBuffer:参数指向一个StaticTimer_t类型的变量,用来保存定时器结构体
0x03 开启软件定时器
如果软件定时器停止运行的话可以使用FreeRTOS提供的两个开启函数来重新启动软件定时器
- xTimerStart():开启软件定时器,用于任务中
- xTimerStartFromISR():开启软件定时器,用于中断中
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait )
- TimerHandle_t xTimer:要开启的软件定时器的句柄
- TickType_t xTicksToWait :设置阻塞时间
BaseType_t xTimerStartFromISR( TimerHandle_t xTimer,
BaseType_t *pxHigherPriorityTaskWoken );
- TimerHandle_t xTimer:开启的软件定时器的句柄
- BaseType_t *pxHigherPriorityTaskWoken :标记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,当此值为pdTURE的时候,在退出中断服务函数之前一定要进行一次任务切换。
0x04 复位软件定时器
复位一个软件定时器会重新计算定时周期达到的时间点,这个新的时间点是相对于复位定时器的那个时刻开始的,比如说,我们有个timer1单次定时器,定时周期是5s,从0s到5s,如果没有干预的话,timer1在5s会停止运行,如果我们在3s时刻进行复位,则timer1会从3s开始,中间如果没有干预的话,会到8s停止运行,有复位的话,会从复位时刻继续运行5s。复位相关函数如下:
- xTimerReset:复位软件定时器,用在任务中
- xTimerResetFromISR:复位软件定时器,用在中断服务函数中
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait )
- TimerHandle_t xTimer:要复位的软件定时器句柄
- TickType_t xTicksToWait:设置阻塞时间
BaseType_t xTimerResetFromISR( TimerHandle_t xTimer,
BaseType_t *pxHigherPriorityTaskWoken );
- TimerHandle_t xTimer:要复位的软件定时器句柄
- BaseType_t *pxHigherPriorityTaskWoken:设置退出此函数是否进行任务切换
0x05 停止软件定时器
停止软件定时器相关中断如下:
- xTimerStop():停止软件定时器,用于任务中
- xTimerStopFromISR():停止软件定时器,用在中断服务函数中
BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait )
- TimerHandle_t xTimer:要停止的软件定时器句柄
- TickType_t xTicksToWait:设置阻塞时间
BaseType_t xTimerStopFromISR( TimerHandle_t xTimer,
BaseType_t *pxHigherPriorityTaskWoken )
- TimerHandle_t xTimer:要停止的软件定时器句柄
- BaseType_t *pxHigherPriorityTaskWoken :标志退出此函数是否要进行任务切换
实验
设计两个任务:
- start_task:用来创建timercontrol_task和两个软件定时器,这两个定时器分别为周期定时器和单次定时器,定时周期一个为1s,另一个为2s
- timercontrol_task:控制两个软件定时器的开启和停止
main函数中创建start_task任务
int main(void)
{
HAL_Init();
Stm32_Clock_Init(360,25,2,8);
delay_init(180);
uart_init(115200);
LED_Init();
KEY_Init();
BEEP_Init();
SDRAM_Init();
LCD_Init();
my_mem_init(SRAMIN);
POINT_COLOR = RED;
LCD_ShowString(30,10,200,16,16,"Apollo STM32F4/F7");
LCD_ShowString(30,30,200,16,16,"FreeRTOS Examp 15-1");
LCD_ShowString(30,50,200,16,16,"KEY_UP:Start Tmr1");
LCD_ShowString(30,70,200,16,16,"KEY0:Start Tmr2");
LCD_ShowString(30,90,200,16,16,"KEY1:Stop Tmr1 and Tmr2");
LCD_DrawLine(0,108,239,108);
LCD_DrawLine(119,108,119,319);
POINT_COLOR = BLACK;
LCD_DrawRectangle(5,110,115,314);
LCD_DrawLine(5,130,115,130);
LCD_DrawRectangle(125,110,234,314);
LCD_DrawLine(125,130,234,130);
POINT_COLOR = BLUE;
LCD_ShowString(6,111,110,16,16, "AutoTim:000");
LCD_ShowString(126,111,110,16,16,"OneTim: 000");
xTaskCreate(start_task,"start_task",256,NULL,1,&StartTask_Handler);
vTaskStartScheduler();
}
start_task来创建timercontrol_task和两个软件定时器
void start_task(void *pvParameters)
{
taskENTER_CRITICAL();
AutoReloadTimer_Handler=xTimerCreate("AutoReloadTimer",1000,pdTRUE,(void *)1,AutoReloadCallback);
OneShotTimer_Handler = xTimerCreate("OneShotTimer",2000,pdFALSE,(void*)2,OneShotCallback);
xTaskCreate(timercontrol_task,"timercontrol_task",256,NULL,2,&TimerControlTask_Handler);
taskEXIT_CRITICAL();
vTaskDelete(StartTask_Handler);
}
AutoReloadCallback和OneShotCallback是两个定时器的回调函数,内容为:
void AutoReloadCallback(TimerHandle_t xTYimer)
{
static u8 tmr1_num=0;
tmr1_num++;
LCD_ShowxNum(70,111,tmr1_num,3,16,0x80);
LCD_Fill(6,131,114,313,lcd_discolor[tmr1_num%14]);
}
void OneShotCallback(TimerHandle_t xTYimer)
{
static u8 tmr2_num =0;
tmr2_num++;
LCD_ShowxNum(190,111,tmr2_num,3,16,0x80);
LCD_Fill(126,131,233,313,lcd_discolor[tmr2_num%14]);
LED1 = !LED1;
printf("定时器2运行结束\r\n");
}
timercontrol_task内容
void timercontrol_task(void *pvParameter)
{
u8 key,num;
while(1)
{
if((AutoReloadTimer_Handler!=NULL)&&(OneShotTimer_Handler!=NULL))
{
key = KEY_Scan(0);
switch(key)
{
case WKUP_PRES:
xTimerStart(AutoReloadTimer_Handler,0);
printf("开启定时器1\r\n");
break;
case KEY0_PRES:
xTimerStart(OneShotTimer_Handler,0);
printf("开始定时器2\r\n");
break;
case KEY1_PRES:
xTimerStop(AutoReloadTimer_Handler,0);
xTimerStop(OneShotTimer_Handler,0);
printf("关闭定时器1和2\r\n");
break;
}
}
num++;
if(num==50)
{
num =0;
LED0 = !LED0;
}
vTaskDelay(10);
}
}
start_task只是创建了定时器,并没有开启,在timercontrol_task中,如果按下KEY_UP按键,就会开启AutoReloadTimer_Handler定时器,按下KEY0就会启动OneShotTimer_Handler定时器,按下KEY1停止两个定时器
串口输出内容
|