软件定时器允许设置一段时间,当设置的时间达到后就执行指定的功能函数,被软件定时器调用的功能函数叫做定时器的回调函数。软件定时器的回调函数是在定时器服务任务中执行的,所以一定不能在回调函数中调用任何阻塞任务的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(); //初始化HAL库 Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhzdelay_init(180); //初始化延时函数uart_init(115200); //初始化串口LED_Init(); //初始化LED KEY_Init(); //初始化按键BEEP_Init(); //初始化蜂鸣器SDRAM_Init(); //初始化SDRAMLCD_Init(); //初始化LCDmy_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();//创建软件周期定时器,周期定时器,周期为1s(1000个时钟节拍)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);//延时10ms}
}
start_task只是创建了定时器,并没有开启,在timercontrol_task中,如果按下KEY_UP按键,就会开启AutoReloadTimer_Handler定时器,按下KEY0就会启动OneShotTimer_Handler定时器,按下KEY1停止两个定时器
串口输出内容