軟件定時器允許設置一段時間,當設置的時間達到后就執行指定的功能函數,被軟件定時器調用的功能函數叫做定時器的回調函數。軟件定時器的回調函數是在定時器服務任務中執行的,所以一定不能在回調函數中調用任何阻塞任務的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停止兩個定時器
串口輸出內容