文章目錄
一·、軟件定時(shí)器的基本概念
二、軟件定時(shí)器應(yīng)用場(chǎng)景
三、軟件定時(shí)器的精度
四、軟件定時(shí)器的運(yùn)作機(jī)制
五、軟件定時(shí)器函數(shù)接口講解
1.軟件定時(shí)器創(chuàng)建函數(shù) xTimerCreate()
2.軟件定時(shí)器啟動(dòng)函數(shù) xTimerStart()
?3.軟件定時(shí)器停止函數(shù)? xTimerStop()
?4.軟件定時(shí)器任務(wù)
5.軟件定時(shí)器刪除函數(shù) xTimerDelete()
五、軟件定時(shí)器實(shí)驗(yàn)
六、實(shí)驗(yàn)現(xiàn)象
一·、軟件定時(shí)器的基本概念
? ? ? 定時(shí)器,是指從指定的時(shí)刻開(kāi)始,經(jīng)過(guò)一個(gè)指定時(shí)間,然后觸發(fā)一個(gè)超時(shí)事件,用戶(hù) 可以自定義定時(shí)器的周期與頻率。類(lèi)似生活中的鬧鐘,我們可以設(shè)置鬧鐘每天什么時(shí)候響, 還能設(shè)置響的次數(shù),是響一次還是每天都響。
? ? ? 定時(shí)器有硬件定時(shí)器和軟件定時(shí)器之分:
? ? ? 硬件定時(shí)器是芯片本身提供的定時(shí)功能。一般是由外部晶振提供給芯片輸入時(shí)鐘,芯 片向軟件模塊提供一組配置寄存器,接受控制輸入,到達(dá)設(shè)定時(shí)間值后芯片中斷控制器產(chǎn) 生時(shí)鐘中斷。硬件定時(shí)器的精度一般很高,可以達(dá)到納秒級(jí)別,并且是中斷觸發(fā)方式。
? ? ? 軟件定時(shí)器,軟件定時(shí)器是由操作系統(tǒng)提供的一類(lèi)系統(tǒng)接口,它構(gòu)建在硬件定時(shí)器基 礎(chǔ)之上,使系統(tǒng)能夠提供不受硬件定時(shí)器資源限制的定時(shí)器服務(wù),它實(shí)現(xiàn)的功能與硬件定 時(shí)器也是類(lèi)似的。
? ? ? 使用硬件定時(shí)器時(shí),每次在定時(shí)時(shí)間到達(dá)之后就會(huì)自動(dòng)觸發(fā)一個(gè)中斷,用戶(hù)在中斷中 處理信息;而使用軟件定時(shí)器時(shí),需要我們?cè)趧?chuàng)建軟件定時(shí)器時(shí)指定時(shí)間到達(dá)后要調(diào)用的 函數(shù)(也稱(chēng)超時(shí)函數(shù)/回調(diào)函數(shù),為了統(tǒng)一,下文均用回調(diào)函數(shù)描述),在回調(diào)函數(shù)中處理 信息。
? ? ? 注意:軟件定時(shí)器回調(diào)函數(shù)的上下文是任務(wù),下文所說(shuō)的定時(shí)器均為軟件定時(shí)器。
? ? ? 軟件定時(shí)器在被創(chuàng)建之后,當(dāng)經(jīng)過(guò)設(shè)定的時(shí)鐘計(jì)數(shù)值后會(huì)觸發(fā)用戶(hù)定義的回調(diào)函數(shù)。 定時(shí)精度與系統(tǒng)時(shí)鐘的周期有關(guān)。一般系統(tǒng)利用 SysTick 作為軟件定時(shí)器的基礎(chǔ)時(shí)鐘,軟 件定時(shí)器的回調(diào)函數(shù)類(lèi)似硬件的中斷服務(wù)函數(shù),所以,回調(diào)函數(shù)也要快進(jìn)快出,而且回調(diào) 函數(shù)中不能有任何阻塞任務(wù)運(yùn)行的情況(軟件定時(shí)器回調(diào)函數(shù)的上下文環(huán)境是任務(wù)),比 如 vTaskDelay()以及 其它能阻 塞任務(wù)運(yùn) 行的函數(shù) ,兩次觸發(fā) 回調(diào)函數(shù) 的時(shí)間間 隔 xTimerPeriodInTicks 叫定時(shí)器的定時(shí)周期。
? ? ? FreeRTOS 操作系統(tǒng)提供軟件定時(shí)器功能,軟件定時(shí)器的使用相當(dāng)于擴(kuò)展了定時(shí)器的數(shù) 量,允許創(chuàng)建更多的定時(shí)業(yè)務(wù)。FreeRTOS 軟件定時(shí)器功能上支持:
?裁剪:能通過(guò)宏關(guān)閉軟件定時(shí)器功能。
?軟件定時(shí)器創(chuàng)建。
?軟件定時(shí)器啟動(dòng)。
?軟件定時(shí)器停止。
?軟件定時(shí)器復(fù)位。
?軟件定時(shí)器刪除。
? ? ? FreeRTOS 提供的軟件定時(shí)器支持單次模式和周期模式,單次模式和周期模式的定時(shí)時(shí) 間到之后都會(huì)調(diào)用軟件定時(shí)器的回調(diào)函數(shù),用戶(hù)可以在回調(diào)函數(shù)中加入要執(zhí)行的工程代碼。
? ? ?單次模式:當(dāng)用戶(hù)創(chuàng)建了定時(shí)器并啟動(dòng)了定時(shí)器后,定時(shí)時(shí)間到了,只執(zhí)行一次回調(diào) 函數(shù)之后就將該定時(shí)器進(jìn)入休眠狀態(tài),不再重新執(zhí)行。
? ? ?周期模式:這個(gè)定時(shí)器會(huì)按照設(shè)置的定時(shí)時(shí)間循環(huán)執(zhí)行回調(diào)函數(shù),直到用戶(hù)將定時(shí)器 刪除。具體看圖1
?圖1軟件定時(shí)器的單次模式與周期模式
? ? ? FreeRTOS 通過(guò)一個(gè) prvTimerTask 任務(wù)(也叫守護(hù)任務(wù) Daemon)管理軟定時(shí)器,它是 在啟動(dòng)調(diào)度器時(shí)自動(dòng)創(chuàng)建的,為了滿(mǎn)足用戶(hù)定時(shí)需求。prvTimerTask 任務(wù)會(huì)在其執(zhí)行期間 檢查用戶(hù)啟動(dòng)的時(shí)間周期溢出的定時(shí)器,并調(diào)用其回調(diào)函數(shù)。只有設(shè)置 FreeRTOSConfig.h 中的宏定義 configUSE_TIMERS 設(shè)置為 1 ,將相關(guān)代碼編譯進(jìn)來(lái),才能正常使用軟件定時(shí) 器相關(guān)功能。
二、軟件定時(shí)器應(yīng)用場(chǎng)景
? ? ? 在很多應(yīng)用中,我們需要一些定時(shí)器任務(wù),硬件定時(shí)器受硬件的限制,數(shù)量上不足以 滿(mǎn)足用戶(hù)的實(shí)際需求,無(wú)法提供更多的定時(shí)器,那么可以采用軟件定時(shí)器來(lái)完成,由軟件 定時(shí)器代替硬件定時(shí)器任務(wù)。但需要注意的是軟件定時(shí)器的精度是無(wú)法和硬件定時(shí)器相比 的,而且在軟件定時(shí)器的定時(shí)過(guò)程中是極有可能被其它中斷所打斷,因?yàn)檐浖〞r(shí)器的執(zhí) 行上下文環(huán)境是任務(wù)。所以,軟件定時(shí)器更適用于對(duì)時(shí)間精度要求不高的任務(wù),一些輔助 型的任務(wù)。
三、軟件定時(shí)器的精度
? ? ? 在操作系統(tǒng)中,通常軟件定時(shí)器以系統(tǒng)節(jié)拍周期為計(jì)時(shí)單位。系統(tǒng)節(jié)拍是系統(tǒng)的心跳 節(jié)拍,表示系統(tǒng)時(shí)鐘的頻率,就類(lèi)似人的心跳,1s 能跳動(dòng)多少下,系統(tǒng)節(jié)拍配置為 configTICK_RATE_HZ,該宏在 FreeRTOSConfig.h 中有定義,默認(rèn)是 1000。那么系統(tǒng)的時(shí) 鐘節(jié)拍周期就為 1ms(1s跳動(dòng) 1000 下,每一下就為 1ms)。軟件定時(shí)器的所定時(shí)數(shù)值必須 是這個(gè)節(jié)拍周期的整數(shù)倍,例如節(jié)拍周期是 10ms,那么上層軟件定時(shí)器定時(shí)數(shù)值只能是 10ms,20ms,100ms 等,而不能取值為 15ms。由于節(jié)拍定義了系統(tǒng)中定時(shí)器能夠分辨的 精確度,系統(tǒng)可以根據(jù)實(shí)際系統(tǒng) CPU 的處理能力和實(shí)時(shí)性需求設(shè)置合適的數(shù)值,系統(tǒng)節(jié)拍周期的值越小,精度越高,但是系統(tǒng)開(kāi)銷(xiāo)也將越大,因?yàn)檫@代表在 1 秒中系統(tǒng)進(jìn)入時(shí)鐘中 斷的次數(shù)也就越多。
四、軟件定時(shí)器的運(yùn)作機(jī)制
? ? ? 軟件定時(shí)器是可選的系統(tǒng)資源,在創(chuàng)建定時(shí)器的時(shí)候會(huì)分配一塊內(nèi)存空間。當(dāng)用戶(hù)創(chuàng) 建并啟動(dòng)一個(gè)軟件定時(shí)器時(shí), FreeRTOS 會(huì)根據(jù)當(dāng)前系統(tǒng)時(shí)間及用戶(hù)設(shè)置的定時(shí)確定該定 時(shí)器喚醒時(shí)間,并將該定時(shí)器控制塊掛入軟件定時(shí)器列表,F(xiàn)reeRTOS 中采用兩個(gè)定時(shí)器列 表維護(hù)軟件定時(shí)器,pxCurrentTimerList 與 pxOverflowTimerList 是列表指針,在初始化的時(shí) 候分別指向 xActiveTimerList1 與 xActiveTimerList2
軟件定時(shí)器用到的列表
PRIVILEGED_DATA static List_t xActiveTimerList1;
PRIVILEGED_DATA static List_t xActiveTimerList2;
PRIVILEGED_DATA static List_t *pxCurrentTimerList;
PRIVILEGED_DATA static List_t *pxOverflowTimerList;
? ? ? xCurrentTimerList:系統(tǒng)新創(chuàng)建并激活的定時(shí)器都會(huì)以超時(shí)時(shí)間升序的方式插入到 pxCurrentTimerList 列表中。系統(tǒng)在定時(shí)器任務(wù)中掃描 pxCurrentTimerList 中的第一個(gè)定時(shí) 器,看是否已超時(shí),若已經(jīng)超時(shí)了則調(diào)用軟件定時(shí)器回調(diào)函數(shù)。否則將定時(shí)器任務(wù)掛起, 因?yàn)槎〞r(shí)時(shí)間是升序插入軟件定時(shí)器列表的,列表中第一個(gè)定時(shí)器的定時(shí)時(shí)間都還沒(méi)到的 話(huà),那后面的定時(shí)器定時(shí)時(shí)間自然沒(méi)到。 pxOverflowTimerList 列表是在軟件定時(shí)器溢出的時(shí)候使用,作用與 pxCurrentTimerList 一致。 同時(shí),F(xiàn)reeRTOS 的軟件定時(shí)器還有采用消息隊(duì)列進(jìn)行通信,利用“定時(shí)器命令隊(duì)列” 向軟件定時(shí)器任務(wù)發(fā)送一些命令,任務(wù)在接收到命令就會(huì)去處理命令對(duì)應(yīng)的程序,比如啟 動(dòng)定時(shí)器,停止定時(shí)器等。假如定時(shí)器任務(wù)處于阻塞狀態(tài),我們又需要馬上再添加一個(gè)軟 件定時(shí)器的話(huà),就是采用這種消息隊(duì)列命令的方式進(jìn)行添加,才能喚醒處于等待狀態(tài)的定 時(shí)器任務(wù),并且在任務(wù)中將新添加的軟件定時(shí)器添加到軟件定時(shí)器列表中,所以,在定時(shí) 器啟動(dòng)函數(shù)中,F(xiàn)reeRTOS 是采用隊(duì)列的方式發(fā)送一個(gè)消息給軟件定時(shí)器任務(wù),任務(wù)被喚醒 從而執(zhí)行接收到的命令。
? ? ? 例如:系統(tǒng)當(dāng)前時(shí)間 xTimeNow 值為 0,注意:xTimeNow 其實(shí)是一個(gè)局部變量,是根 據(jù) xTaskGetTickCount()函數(shù)獲取的,實(shí)際它的值就是全局變量 xTickCount 的值,下文都采 用它表示當(dāng)前系統(tǒng)時(shí)間。在當(dāng)前系統(tǒng)中已經(jīng)創(chuàng)建并啟動(dòng)了 1 個(gè)定時(shí)器 Timer1;系統(tǒng)繼續(xù)運(yùn) 行,當(dāng)系統(tǒng)的時(shí)間 xTimeNow 為 20 的時(shí)候,用戶(hù)創(chuàng)建并且啟動(dòng)一個(gè)定時(shí)時(shí)間為 100 的定時(shí) 器 Timer2,此 時(shí) Timer2 的 溢出 時(shí)間 xTicksToWait 就 為定 時(shí)時(shí)間 +系統(tǒng) 當(dāng)前時(shí) 間 (100+20=120),然后將 Timer2 按 xTicksToWait 升序插入軟件定時(shí)器列表中;假設(shè)當(dāng)前 系統(tǒng)時(shí)間 xTimeNow 為 40 的時(shí)候,用戶(hù)創(chuàng)建并且啟動(dòng)了一個(gè)定時(shí)時(shí)間為 50 的定時(shí)器 Timer3 , 那 么 此 時(shí) Timer3 的 溢 出 時(shí) 間 xTicksToWait 就 為 40+50=90 , 同 樣 安 裝 xTicksToWait 的數(shù)值升序插入軟件定時(shí)器列表中,在定時(shí)器鏈表中插入過(guò)程具體見(jiàn)圖 2。 同理創(chuàng)建并且啟動(dòng)在已有的兩個(gè)定時(shí)器中間的定時(shí)器也是一樣的,具體見(jiàn)圖3。
圖2 定時(shí)器鏈表示意圖1
?
圖 3??定時(shí)器鏈表示意圖 2
? ? ? 那么系統(tǒng)如何處理軟件定時(shí)器列表?系統(tǒng)在不斷運(yùn)行,而 xTimeNow(xTickCount) 隨著 SysTick 的觸發(fā)一直在增長(zhǎng)(每一次硬件定時(shí)器中斷來(lái)臨,xTimeNow 變量會(huì)加 1), 在軟件定時(shí)器任務(wù)運(yùn)行的時(shí)候會(huì)獲取下一個(gè)要喚醒的定時(shí)器,比較當(dāng)前系統(tǒng)時(shí)間 xTimeNow 是否大于或等于下一個(gè)定時(shí)器喚醒時(shí)間 xTicksToWait,若大于則表示已經(jīng)超時(shí), 定時(shí)器任務(wù)將會(huì)調(diào)用對(duì)應(yīng)定時(shí)器的回調(diào)函數(shù),否則將軟件定時(shí)器任務(wù)掛起,直至下一個(gè)要 喚醒的軟件定時(shí)器時(shí)間到來(lái)或者接收到命令消息。
使用軟件定時(shí)器時(shí)候要注意以下幾點(diǎn):
? ? ?軟件定時(shí)器的回調(diào)函數(shù)中應(yīng)快進(jìn)快出,絕對(duì)不允許使用任何可能引軟件定時(shí)器起 任務(wù)掛起或者阻塞的 API 接口,在回調(diào)函數(shù)中也絕對(duì)不允許出現(xiàn)死循環(huán)。
? ? ?軟件定時(shí)器使用了系統(tǒng)的一個(gè)隊(duì)列和一個(gè)任務(wù)資源,軟件定時(shí)器任務(wù)的優(yōu)先級(jí)默 認(rèn)為 configTIMER_TASK_PRIORITY,為了更好響應(yīng),該優(yōu)先級(jí)應(yīng)設(shè)置為所有任 務(wù)中最高的優(yōu)先級(jí)。? ? ? ?創(chuàng)建單次軟件定時(shí)器,該定時(shí)器超時(shí)執(zhí)行完回調(diào)函數(shù)后,系統(tǒng)會(huì)自動(dòng)刪除該軟件 定時(shí)器,并回收資源。
? ? ? 定時(shí)器任務(wù)的堆棧大小默認(rèn)為 configTIMER_TASK_STACK_DEPTH 個(gè)字節(jié)。
五、軟件定時(shí)器函數(shù)接口講解
? ? ? 軟件定時(shí)器的功能是在定時(shí)器任務(wù)(或者叫定時(shí)器守護(hù)任務(wù))中實(shí)現(xiàn)的。軟件定時(shí)器 的很多 API 函數(shù)通過(guò)一個(gè)名字叫“定時(shí)器命令隊(duì)列”的隊(duì)列來(lái)給定時(shí)器守護(hù)任務(wù)發(fā)送命令。 該定時(shí)器命令隊(duì)列由 RTOS 內(nèi)核提供,且應(yīng)用程序不能夠直接訪問(wèn),其消息隊(duì)列的長(zhǎng)度由 宏 configTIMER_QUEUE_LENGTH 定義,下面就講解一些常用的軟件定時(shí)器函數(shù)接口。
1.軟件定時(shí)器創(chuàng)建函數(shù) xTimerCreate()
? ? ? 軟件定時(shí)器與 FreeRTOS 內(nèi)核其他資源一樣,需要?jiǎng)?chuàng)建才允許使用的,F(xiàn)reeRTOS 為我 們提供了兩種創(chuàng)建方式,一種是動(dòng)態(tài)創(chuàng)建軟件定時(shí)器 xTimerCreate(),另一種是靜態(tài)創(chuàng)建方 式 xTimerCreateStatic(),因?yàn)閯?chuàng)建過(guò)程基本差不多,所以在這里我們只講解動(dòng)態(tài)創(chuàng)建方式。
? ? ? xTimerCreate()用于創(chuàng)建一個(gè)軟件定時(shí)器,并返回一個(gè)句柄。要想使用該函數(shù)函數(shù)必須 在 頭 文 件 FreeRTOSConfig.h 中 把 宏 configUSE_TIMERS 和 configSUPPORT_DYNAMIC_ALLOCATION 均 定義為 1 (configSUPPORT_DYNAMIC_ALLOCATION 在 FreeRTOS.h 中默認(rèn)定義為 1),并且需 要把 FreeRTOS/source/times.c 這個(gè) C 文件添加到工程中。 每一個(gè)軟件定時(shí)器只需要 很少的 RAM 空間來(lái)保存其的狀態(tài)。如果使用函 數(shù) xTimeCreate()來(lái)創(chuàng)建一個(gè)軟件定時(shí)器,那么需要的 RAM 是動(dòng)態(tài)分配的。如果使用函數(shù) xTimeCreateStatic()來(lái)創(chuàng)建一個(gè)事件組,那么需要的 RAM 是靜態(tài)分配的 軟件定時(shí)器在創(chuàng)建成功后是處于休眠狀態(tài)的,可以使用 xTimerStart()、xTimerReset()、 xTimerStartFromISR() 、 xTimerResetFromISR() 、 xTimerChangePeriod() 和 xTimerChangePeriodFromISR()這些函數(shù)將其狀態(tài)轉(zhuǎn)換為活躍態(tài)。
xTimerCreate()使用實(shí)例
static TimerHandle_t Swtmr1_Handle =NULL; /* 軟件定時(shí)器句柄 */
static TimerHandle_t Swtmr2_Handle =NULL; /* 軟件定時(shí)器句柄 */
static void AppTaskCreate(void)
{
taskENTER_CRITICAL(); //進(jìn)入臨界區(qū)
/************************************************************************************
* 創(chuàng)建軟件周期定時(shí)器
* 函數(shù)原型
* TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction )
* @uxAutoReload : pdTRUE為周期模式,pdFALS為單次模式
* 單次定時(shí)器,周期(1000個(gè)時(shí)鐘節(jié)拍),周期模式
*************************************************************************************/
Swtmr1_Handle=xTimerCreate((const char* )"AutoReloadTimer",
(TickType_t )1000,/* 定時(shí)器周期 1000(tick) */
(UBaseType_t )pdTRUE,/* 周期模式 */
(void* )1,/* 為每個(gè)計(jì)時(shí)器分配一個(gè)索引的唯一ID */
(TimerCallbackFunction_t)Swtmr1_Callback);
if(Swtmr1_Handle != NULL)
{
/***********************************************************************************
* xTicksToWait:如果在調(diào)用xTimerStart()時(shí)隊(duì)列已滿(mǎn),則以tick為單位指定調(diào)用任務(wù)應(yīng)保持
* 在Blocked(阻塞)狀態(tài)以等待start命令成功發(fā)送到timer命令隊(duì)列的時(shí)間。
* 如果在啟動(dòng)調(diào)度程序之前調(diào)用xTimerStart(),則忽略xTicksToWait。在這里設(shè)置等待時(shí)間為0.
**********************************************************************************/
xTimerStart(Swtmr1_Handle,0); //開(kāi)啟周期定時(shí)器
}
/************************************************************************************
* 創(chuàng)建軟件周期定時(shí)器
* 函數(shù)原型
* TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction )
* @uxAutoReload : pdTRUE為周期模式,pdFALS為單次模式
* 單次定時(shí)器,周期(5000個(gè)時(shí)鐘節(jié)拍),單次模式
*************************************************************************************/
Swtmr2_Handle=xTimerCreate((const char* )"OneShotTimer",
(TickType_t )5000,/* 定時(shí)器周期 5000(tick) */
(UBaseType_t )pdFALSE,/* 單次模式 */
(void* )2,/* 為每個(gè)計(jì)時(shí)器分配一個(gè)索引的唯一ID */
(TimerCallbackFunction_t)Swtmr2_Callback);
if(Swtmr2_Handle != NULL)
{
/***********************************************************************************
* xTicksToWait:如果在調(diào)用xTimerStart()時(shí)隊(duì)列已滿(mǎn),則以tick為單位指定調(diào)用任務(wù)應(yīng)保持
* 在Blocked(阻塞)狀態(tài)以等待start命令成功發(fā)送到timer命令隊(duì)列的時(shí)間。
* 如果在啟動(dòng)調(diào)度程序之前調(diào)用xTimerStart(),則忽略xTicksToWait。在這里設(shè)置等待時(shí)間為0.
**********************************************************************************/
xTimerStart(Swtmr2_Handle,0); //開(kāi)啟周期定時(shí)器
}
vTaskDelete(AppTaskCreate_Handle); //刪除AppTaskCreate任務(wù)
taskEXIT_CRITICAL(); //退出臨界區(qū)
}
static void Swtmr1_Callback(void* parameter)
{
/* 軟件定時(shí)器的回調(diào)函數(shù),用戶(hù)自己實(shí)現(xiàn) */
}
static void Swtmr2_Callback(void* parameter)
{
/* 軟件定時(shí)器的回調(diào)函數(shù),用戶(hù)自己實(shí)現(xiàn) */
}
2.軟件定時(shí)器啟動(dòng)函數(shù) xTimerStart()
xTimerStart()
? ? ? 如果是認(rèn)真看上面 xTimerCreate()函數(shù)使用實(shí)例的同學(xué)應(yīng)該就發(fā)現(xiàn)了,這個(gè)軟件定時(shí)器 啟動(dòng)函數(shù) xTimerStart()在上面的實(shí)例中有用到過(guò),前一小節(jié)已經(jīng)說(shuō)明了,軟件定時(shí)器在創(chuàng) 建完成的時(shí)候是處于休眠狀態(tài)的,需要用 FreeRTOS 的相關(guān)函數(shù)將軟件定時(shí)器活動(dòng)起來(lái), 而 xTimerStart()函數(shù)就是可以讓處于休眠的定時(shí)器開(kāi)始工作。 我們知道,在系統(tǒng)開(kāi)始運(yùn)行的時(shí)候,系統(tǒng)會(huì)幫我們自動(dòng)創(chuàng)建一個(gè)軟件定時(shí)器任務(wù) (prvTimerTask),在這個(gè)任務(wù)中,如果暫時(shí)沒(méi)有運(yùn)行中的定時(shí)器,任務(wù)會(huì)進(jìn)入阻塞態(tài)等 待命令,而我們的啟動(dòng)函數(shù)就是通過(guò)“定時(shí)器命令隊(duì)列”向定時(shí)器任務(wù)發(fā)送一個(gè)啟動(dòng)命令, 定時(shí)器任務(wù)獲得命令就解除阻塞,然后執(zhí)行啟動(dòng)軟件定時(shí)器命令。
xTimerStartFromISR()
? ? ? 當(dāng) 然 除 在任 務(wù)啟 動(dòng) 軟件 定 時(shí)器 之外 , 還有 在 中斷 中啟 動(dòng) 軟件 定 時(shí)器 的函 數(shù) xTimerStartFromISR()。xTimerStartFromISR()是函數(shù) xTimerStart()的中斷版本,用于啟動(dòng)一 個(gè)先前由函數(shù) xTimerCreate() / xTimerCreateStatic()創(chuàng)建的軟件定時(shí)器。
xTimerStartFromISR()函數(shù)說(shuō)明
?3.軟件定時(shí)器停止函數(shù)? xTimerStop()
xTimerStop()
? ? ? xTimerStop() 用于停止一個(gè)已經(jīng)啟動(dòng)的軟件定時(shí)器,該函數(shù)的實(shí)現(xiàn)也是通過(guò)“定時(shí)器 命令隊(duì)列”發(fā)送一個(gè)停止命令給軟件定時(shí)器任務(wù),從而喚醒軟件定時(shí)器任務(wù)去將定時(shí)器停 止。要想使函數(shù) xTimerStop()必須在頭文件 FreeRTOSConfig.h 中把宏 configUSE_TIMERS 定義為 1
xTimerStop()函數(shù)說(shuō)明
static void AppTaskCreate(void)
{
taskENTER_CRITICAL(); //進(jìn)入臨界區(qū)
/************************************************************************************
* 創(chuàng)建軟件周期定時(shí)器
* 函數(shù)原型
* TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction )
* @uxAutoReload : pdTRUE為周期模式,pdFALS為單次模式
* 單次定時(shí)器,周期(1000個(gè)時(shí)鐘節(jié)拍),周期模式
*************************************************************************************/
Swtmr1_Handle=xTimerCreate((const char* )"AutoReloadTimer",
(TickType_t )1000,/* 定時(shí)器周期 1000(tick) */
(UBaseType_t )pdTRUE,/* 周期模式 */
(void* )1,/* 為每個(gè)計(jì)時(shí)器分配一個(gè)索引的唯一ID */
(TimerCallbackFunction_t)Swtmr1_Callback);
if(Swtmr1_Handle != NULL)
{
/***********************************************************************************
* xTicksToWait:如果在調(diào)用xTimerStart()時(shí)隊(duì)列已滿(mǎn),則以tick為單位指定調(diào)用任務(wù)應(yīng)保持
* 在Blocked(阻塞)狀態(tài)以等待start命令成功發(fā)送到timer命令隊(duì)列的時(shí)間。
* 如果在啟動(dòng)調(diào)度程序之前調(diào)用xTimerStart(),則忽略xTicksToWait。在這里設(shè)置等待時(shí)間為0.
**********************************************************************************/
xTimerStart(Swtmr1_Handle,0); //開(kāi)啟周期定時(shí)器
}
static void test_task(void* parameter)
{
while (1) {
/* 用戶(hù)自己實(shí)現(xiàn)任務(wù)代碼 */
xTimerStop(Swtmr1_Handle,0); //停止定時(shí)器
}
}
?xTimerStopFromISR()
? ? ? xTimerStopFromISR()是函數(shù) xTimerStop()的中斷版本,用于停止一個(gè)正在運(yùn)行的軟件 定時(shí)器,讓其進(jìn)入休眠態(tài),實(shí)現(xiàn)過(guò)程也是通過(guò)“定時(shí)器命令隊(duì)列”向軟件定時(shí)器任務(wù)發(fā)送 停止命令。xTimerStopFromISR()函數(shù)說(shuō)明
?xTimerStopFromISR()函數(shù)應(yīng)用舉例
void vAnExampleInterruptServiceRoutine( void )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (xTimerStopFromISR(xTimer,&xHigherPriorityTaskWoken)!=pdPASS ) {
/* 軟件定時(shí)器停止命令沒(méi)有成功執(zhí)行 */
}
if ( xHigherPriorityTaskWoken != pdFALSE ) {
/* 執(zhí)行上下文切換 */
}
}
?4.軟件定時(shí)器任務(wù)
? ? ? 我們知道,軟件定時(shí)器回調(diào)函數(shù)運(yùn)行的上下文環(huán)境是任務(wù),那么軟件定時(shí)器任務(wù)是在 干什么的呢?如何創(chuàng)建的呢?下面跟我一步步來(lái)分析軟件定時(shí)器的工作過(guò)程。 軟件定時(shí)器任務(wù)是在系統(tǒng)開(kāi)始調(diào)度(vTaskStartScheduler()函數(shù))的時(shí)候就被創(chuàng)建的, 前提 是將宏定 義 configUSE_TIMERS 開(kāi)啟, 具體見(jiàn)代 碼清單 21-12 加 粗部分, 在 xTimerCreateTimerTask()函數(shù)里面就是創(chuàng)建了一個(gè)軟件定時(shí)器任務(wù),就跟我們創(chuàng)建任務(wù)一 樣,支持動(dòng)態(tài)與靜態(tài)創(chuàng)建,我們暫時(shí)看動(dòng)態(tài)創(chuàng)建的即可,
5.軟件定時(shí)器刪除函數(shù) xTimerDelete()
? ? ? xTimerDelete()用于刪除一個(gè)已經(jīng)被創(chuàng)建成功的軟件定時(shí)器,刪除之后就無(wú)法使用該定 時(shí)器,并且定時(shí)器相應(yīng)的資源也會(huì)被系統(tǒng)回收釋放。要想使函數(shù) xTimerStop()必須在頭文 件 FreeRTOSConfig.h 中把宏 configUSE_TIMERS 定義為 1。
xTimerDelete()函數(shù)說(shuō)明
? ? ? 從軟件定時(shí)器刪除函數(shù) xTimerDelete()的原型可以看出,刪除一個(gè)軟件定時(shí)器也是在軟 件定時(shí)器任務(wù)中刪除,調(diào)用 xTimerDelete()將刪除軟件定時(shí)器的命令發(fā)送給軟件定時(shí)器任務(wù), 軟件定時(shí)器任務(wù)在接收到刪除的命令之后就進(jìn)行刪除操作,該函數(shù)的使用方法很簡(jiǎn)單。
xTimerDelete()使用實(shí)例
static void test_task(void* parameter)
{
while (1) {
/* 用戶(hù)自己實(shí)現(xiàn)任務(wù)代碼 */
xTimerDelete(Swtmr1_Handle,0); //刪除軟件定時(shí)器
}
}
五、軟件定時(shí)器實(shí)驗(yàn)
? ? ? 軟件定時(shí)器實(shí)驗(yàn)是在 FreeRTOS 中創(chuàng)建了兩個(gè)軟件定時(shí)器,其中一個(gè)軟件定時(shí)器是單 次模式,5000 個(gè) tick 調(diào)用一次回調(diào)函數(shù),另一個(gè)軟件定時(shí)器是周期模式,1000 個(gè) tick 調(diào)用 一次回調(diào)函數(shù),在回調(diào)函數(shù)中輸出相關(guān)信息。
/* FreeRTOS頭文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
/* 開(kāi)發(fā)板硬件bsp頭文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
/**************************** 任務(wù)句柄 ********************************/
/*
* 任務(wù)句柄是一個(gè)指針,用于指向一個(gè)任務(wù),當(dāng)任務(wù)創(chuàng)建好之后,它就具有了一個(gè)任務(wù)句柄
* 以后我們要想操作這個(gè)任務(wù)都需要通過(guò)這個(gè)任務(wù)句柄,如果是自身的任務(wù)操作自己,那么
* 這個(gè)句柄可以為NULL。
*/
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 創(chuàng)建任務(wù)句柄 */
/********************************** 內(nèi)核對(duì)象句柄 *********************************/
/*
* 信號(hào)量,消息隊(duì)列,事件標(biāo)志組,軟件定時(shí)器這些都屬于內(nèi)核的對(duì)象,要想使用這些內(nèi)核
* 對(duì)象,必須先創(chuàng)建,創(chuàng)建成功之后會(huì)返回一個(gè)相應(yīng)的句柄。實(shí)際上就是一個(gè)指針,后續(xù)我
* 們就可以通過(guò)這個(gè)句柄操作這些內(nèi)核對(duì)象。
*
* 內(nèi)核對(duì)象說(shuō)白了就是一種全局的數(shù)據(jù)結(jié)構(gòu),通過(guò)這些數(shù)據(jù)結(jié)構(gòu)我們可以實(shí)現(xiàn)任務(wù)間的通信,
* 任務(wù)間的事件同步等各種功能。至于這些功能的實(shí)現(xiàn)我們是通過(guò)調(diào)用這些內(nèi)核對(duì)象的函數(shù)
* 來(lái)完成的
*
*/
static TimerHandle_t Swtmr1_Handle =NULL; /* 軟件定時(shí)器句柄 */
static TimerHandle_t Swtmr2_Handle =NULL; /* 軟件定時(shí)器句柄 */
/******************************* 全局變量聲明 ************************************/
/*
* 當(dāng)我們?cè)趯?xiě)應(yīng)用程序的時(shí)候,可能需要用到一些全局變量。
*/
static uint32_t TmrCb_Count1 = 0; /* 記錄軟件定時(shí)器1回調(diào)函數(shù)執(zhí)行次數(shù) */
static uint32_t TmrCb_Count2 = 0; /* 記錄軟件定時(shí)器2回調(diào)函數(shù)執(zhí)行次數(shù) */
/******************************* 宏定義 ************************************/
/*
* 當(dāng)我們?cè)趯?xiě)應(yīng)用程序的時(shí)候,可能需要用到一些宏定義。
*/
/*
*************************************************************************
* 函數(shù)聲明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于創(chuàng)建任務(wù) */
static void Swtmr1_Callback(void* parameter);
static void Swtmr2_Callback(void* parameter);
static void BSP_Init(void);/* 用于初始化板載相關(guān)資源 */
/*****************************************************************
* @brief 主函數(shù)
* @param 無(wú)
* @retval 無(wú)
* @note 第一步:開(kāi)發(fā)板硬件初始化
第二步:創(chuàng)建APP應(yīng)用任務(wù)
第三步:?jiǎn)?dòng)FreeRTOS,開(kāi)始多任務(wù)調(diào)度
****************************************************************/
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定義一個(gè)創(chuàng)建信息返回值,默認(rèn)為pdPASS */
/* 開(kāi)發(fā)板硬件初始化 */
BSP_Init();
printf("這是一個(gè)FreeRTOS軟件定時(shí)器實(shí)驗(yàn)!\n");
/* 創(chuàng)建AppTaskCreate任務(wù) */
xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任務(wù)入口函數(shù) */
(const char* )"AppTaskCreate",/* 任務(wù)名字 */
(uint16_t )512, /* 任務(wù)棧大小 */
(void* )NULL,/* 任務(wù)入口函數(shù)參數(shù) */
(UBaseType_t )1, /* 任務(wù)的優(yōu)先級(jí) */
(TaskHandle_t* )&AppTaskCreate_Handle);/* 任務(wù)控制塊指針 */
/* 啟動(dòng)任務(wù)調(diào)度 */
if(pdPASS == xReturn)
vTaskStartScheduler(); /* 啟動(dòng)任務(wù),開(kāi)啟調(diào)度 */
else
return -1;
while(1); /* 正常不會(huì)執(zhí)行到這里 */
}
/***********************************************************************
* @ 函數(shù)名 : AppTaskCreate
* @ 功能說(shuō)明: 為了方便管理,所有的任務(wù)創(chuàng)建函數(shù)都放在這個(gè)函數(shù)里面
* @ 參數(shù) : 無(wú)
* @ 返回值 : 無(wú)
**********************************************************************/
static void AppTaskCreate(void)
{
taskENTER_CRITICAL(); //進(jìn)入臨界區(qū)
/************************************************************************************
* 創(chuàng)建軟件周期定時(shí)器
* 函數(shù)原型
* TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction )
* @uxAutoReload : pdTRUE為周期模式,pdFALS為單次模式
* 單次定時(shí)器,周期(1000個(gè)時(shí)鐘節(jié)拍),周期模式
*************************************************************************************/
Swtmr1_Handle=xTimerCreate((const char* )"AutoReloadTimer",
(TickType_t )1000,/* 定時(shí)器周期 1000(tick) */
(UBaseType_t )pdTRUE,/* 周期模式 */
(void* )1,/* 為每個(gè)計(jì)時(shí)器分配一個(gè)索引的唯一ID */
(TimerCallbackFunction_t)Swtmr1_Callback);
if(Swtmr1_Handle != NULL)
{
/***********************************************************************************
* xTicksToWait:如果在調(diào)用xTimerStart()時(shí)隊(duì)列已滿(mǎn),則以tick為單位指定調(diào)用任務(wù)應(yīng)保持
* 在Blocked(阻塞)狀態(tài)以等待start命令成功發(fā)送到timer命令隊(duì)列的時(shí)間。
* 如果在啟動(dòng)調(diào)度程序之前調(diào)用xTimerStart(),則忽略xTicksToWait。在這里設(shè)置等待時(shí)間為0.
**********************************************************************************/
xTimerStart(Swtmr1_Handle,0); //開(kāi)啟周期定時(shí)器
}
/************************************************************************************
* 創(chuàng)建軟件周期定時(shí)器
* 函數(shù)原型
* TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction )
* @uxAutoReload : pdTRUE為周期模式,pdFALS為單次模式
* 單次定時(shí)器,周期(5000個(gè)時(shí)鐘節(jié)拍),單次模式
*************************************************************************************/
Swtmr2_Handle=xTimerCreate((const char* )"OneShotTimer",
(TickType_t )5000,/* 定時(shí)器周期 5000(tick) */
(UBaseType_t )pdFALSE,/* 單次模式 */
(void* )2,/* 為每個(gè)計(jì)時(shí)器分配一個(gè)索引的唯一ID */
(TimerCallbackFunction_t)Swtmr2_Callback);
if(Swtmr2_Handle != NULL)
{
/***********************************************************************************
* xTicksToWait:如果在調(diào)用xTimerStart()時(shí)隊(duì)列已滿(mǎn),則以tick為單位指定調(diào)用任務(wù)應(yīng)保持
* 在Blocked(阻塞)狀態(tài)以等待start命令成功發(fā)送到timer命令隊(duì)列的時(shí)間。
* 如果在啟動(dòng)調(diào)度程序之前調(diào)用xTimerStart(),則忽略xTicksToWait。在這里設(shè)置等待時(shí)間為0.
**********************************************************************************/
xTimerStart(Swtmr2_Handle,0); //開(kāi)啟周期定時(shí)器
}
vTaskDelete(AppTaskCreate_Handle); //刪除AppTaskCreate任務(wù)
taskEXIT_CRITICAL(); //退出臨界區(qū)
}
/***********************************************************************
* @ 函數(shù)名 : Swtmr1_Callback
* @ 功能說(shuō)明: 軟件定時(shí)器1 回調(diào)函數(shù),打印回調(diào)函數(shù)信息&當(dāng)前系統(tǒng)時(shí)間
* 軟件定時(shí)器請(qǐng)不要調(diào)用阻塞函數(shù),也不要進(jìn)行死循環(huán),應(yīng)快進(jìn)快出
* @ 參數(shù) : 無(wú)
* @ 返回值 : 無(wú)
**********************************************************************/
static void Swtmr1_Callback(void* parameter)
{
TickType_t tick_num1;
TmrCb_Count1++; /* 每回調(diào)一次加一 */
tick_num1 = xTaskGetTickCount(); /* 獲取滴答定時(shí)器的計(jì)數(shù)值 */
LED1_TOGGLE;
printf("Swtmr1_Callback函數(shù)執(zhí)行 %d 次\n", TmrCb_Count1);
printf("滴答定時(shí)器數(shù)值=%d\n", tick_num1);
}
/***********************************************************************
* @ 函數(shù)名 : Swtmr2_Callback
* @ 功能說(shuō)明: 軟件定時(shí)器2 回調(diào)函數(shù),打印回調(diào)函數(shù)信息&當(dāng)前系統(tǒng)時(shí)間
* 軟件定時(shí)器請(qǐng)不要調(diào)用阻塞函數(shù),也不要進(jìn)行死循環(huán),應(yīng)快進(jìn)快出
* @ 參數(shù) : 無(wú)
* @ 返回值 : 無(wú)
**********************************************************************/
static void Swtmr2_Callback(void* parameter)
{
TickType_t tick_num2;
TmrCb_Count2++; /* 每回調(diào)一次加一 */
tick_num2 = xTaskGetTickCount(); /* 獲取滴答定時(shí)器的計(jì)數(shù)值 */
printf("Swtmr2_Callback函數(shù)執(zhí)行 %d 次\n", TmrCb_Count2);
printf("滴答定時(shí)器數(shù)值=%d\n", tick_num2);
}
/***********************************************************************
* @ 函數(shù)名 : BSP_Init
* @ 功能說(shuō)明: 板級(jí)外設(shè)初始化,所有板子上的初始化均可放在這個(gè)函數(shù)里面
* @ 參數(shù) :
* @ 返回值 : 無(wú)
*********************************************************************/
static void BSP_Init(void)
{
/*
* STM32中斷優(yōu)先級(jí)分組為4,即4bit都用來(lái)表示搶占優(yōu)先級(jí),范圍為:0~15
* 優(yōu)先級(jí)分組只需要分組一次即可,以后如果有其他的任務(wù)需要用到中斷,
* 都統(tǒng)一用這個(gè)優(yōu)先級(jí)分組,千萬(wàn)不要再分組,切忌。
*/
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* LED 初始化 */
LED_GPIO_Config();
/* 串口初始化 */
USART_Config();
/* 按鍵初始化 */
Key_GPIO_Config();
}
/********************************END OF FILE****************************/
六、實(shí)驗(yàn)現(xiàn)象
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-679658.html
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-679658.html
到了這里,關(guān)于FreeRTOS軟件定時(shí)器 基于STM32的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!