l 軟體定時器的基本概念
定時器,是指從指定的時刻開始,經過一個指定時間,然後觸發一個逾時事件,使用者可以自定義定時器的周期與頻率。類似生活中的鬧鐘,我們可以設定鬧鐘每天什麼時候響,還能設定響的次數,是響一次還是每天都響。
使用硬體定時器時,每次在定時時間到達之後就會自動觸發一個中斷,使用者在中斷中處理資訊;而使用軟體定時器時,需要我們在建立軟體定時器時指定時間到達後要調用的函數(也稱逾時函數/回調函數),在回調函數中處理資訊。軟體定時器的回調函數類似硬體的中斷服務函數,是以回調函數也要快進快出,而且回調函數中不能有任何阻塞任務運作的情況(軟體定時器回調函數的上下文環境是任務)。
單次模式:當使用者建立了定時器并啟動了定時器後,定時時間到了,隻執行一次回調函數之後就将該定時器進入休眠狀态,不再重新執行。周期模式:這個定時器會按照設定的定時時間循環執行回調函數,直到使用者将定時器删除。
FreeRTOS 通過一個 prvTimerTask 任務(也叫守護任務 Daemon)管理軟定時器,它是在啟動排程器時自動建立的, 為了滿足使用者定時需求。prvTimerTask 任務會在其執行期間檢查使用者啟動的時間周期溢出的定時器,并調用其回調函數。
l 軟體定時器的運作機制
軟體定時器是可選的系統資源,在建立定時器的時候會配置設定一塊記憶體空間。當使用者建立并啟動一個軟體定時器時, FreeRTOS 會根據目前系統時間及使用者設定的定時确定該定時器喚醒時間,并将該定時器控制塊挂入軟體定時器清單, FreeRTOS 中采用兩個定時器清單維護軟體定時器, pxCurrentTimerList 與 pxOverflowTimerList 是清單指針, 在初始化的時候分别指向 xActiveTimerList1 與 xActiveTimerList2。
同時, FreeRTOS 的軟體定時器還有采用消息隊列進行通信, 利用“ 定時器指令隊列”向軟體定時器任務發送一些指令,任務在接收到指令就會去處理指令對應的程式,比如啟動定時器,停止定時器等。假如定時器任務處于阻塞狀态,我們又需要馬上再添加一個軟體定時器的話,就是采用這種消息隊列指令的方式進行添加,才能喚醒處于等待狀态的定時器任務,并且在任務中将新添加的軟體定時器添加到軟體定時器清單中,是以,在定時器啟動函數中, FreeRTOS 是采用隊列的方式發送一個消息給軟體定時器任務,任務被喚醒進而執行接收到的指令。
l 軟體定時器的回調函數中應快進快出,絕對不允許使用任何可能引軟體定時器起任務挂起或者阻塞的 API 接口,在回調函數中也絕對不允許出現死循環。
l 軟體定時器使用了系統的一個隊列和一個任務資源,軟體定時器任務的優先級預設為 configTIMER_TASK_PRIORITY,為了更好響應,該優先級應設定為所有任務中最高的優先級。
l 建立單次軟體定時器,該定時器逾時執行完回調函數後,系統會自動删除該軟體定時器,并回收資源。
l 定時器任務的堆棧大小預設為 configTIMER_TASK_STACK_DEPTH 個位元組。
l 軟體定時器控制塊
軟體定時器雖然不屬于核心資源,但是也是 FreeRTOS 核心組成部分,是一個可以裁剪的功能子產品,同樣在系統中由一個控制塊管理其相關資訊,軟體定時器的控制塊中包含沒用過建立的軟體定時器基本資訊,在使用定時器前我們需要通過xTimerCreate()/xTimerCreateStatic()函數建立一個軟體定時器,在函數中, FreeRTOS 将向系統管理的記憶體申請一塊軟體定時器控制塊大小的記憶體用于儲存定時器的資訊。
l 軟體定時器建立函數 xTimerCreate()
軟體定時器與 FreeRTOS 核心其他資源一樣,需要建立才允許使用的, FreeRTOS 為我們提供了兩種建立方式,一種是動态建立軟體定時器 xTimerCreate(), 另一種是靜态建立方式 xTimerCreateStatic(),并傳回一個句柄。
l 軟體定時器啟動函數
² xTimerStart():
在系統開始運作的時候,系統會幫我們自動建立一個軟體定時器任務( prvTimerTask),在這個任務中,如果暫時沒有運作中的定時器,任務會進入阻塞态等待指令, 而我們的啟動函數就是通過“定時器指令隊列” 向定時器任務發送一個啟動指令,定時器任務獲得指令就解除阻塞,然後執行啟動軟體定時器指令。
² xTimerStartFromISR()
除在任務啟動軟體定時器之外,還有在中斷中啟動軟體定時器的函數xTimerStartFromISR()。xTimerStartFromISR()是函數 xTimerStart()的中斷版本, 用于啟動一個先前由函數 xTimerCreate() / xTimerCreateStatic()建立的軟體定時器。
l 軟體定時器停止函數
² xTimerStop()
xTimerStop() 用于停止一個已經啟動的軟體定時器, 該函數的實作也是通過“定時器指令隊列”發送一個停止指令給軟體定時器任務,進而喚醒軟體定時器任務去将定時器停止。
² xTimerStopFromISR()
xTimerStopFromISR()是函數 xTimerStop()的中斷版本, 用于停止一個正在運作的軟體定時器, 讓其進入休眠态, 實作過程也是通過“定時器指令隊列”向軟體定時器任務發送停止指令。
l 軟體定時器任務
軟體定時器回調函數運作的上下文環境是任務,軟體定時器任務是在系統開始排程( vTaskStartScheduler()函數) 的時候就被建立的,前提是将宏定義 configUSE_TIMERS 開啟。
l 軟體定時器删除函數 xTimerDelete()
xTimerDelete()用于删除一個已經被建立成功的軟體定時器, 删除之後就無法使用該定時器, 并且定時器相應的資源也會被系統回收釋放。
l 軟體定時器實驗
軟體定時器實驗是在 FreeRTOS 中建立了兩個軟體定時器,其中一個軟體定時器是單次模式, 5000 個 tick 調用一次回調函數,另一個軟體定時器是周期模式, 1000 個 tick 調用一次回調函數,在回調函數中輸出相關資訊,
定時器1每秒執行一次,定時器2單次執行(5秒)
實驗效果
從結果上來看,雖然實作了功能,但是定時的精度差太遠了。1秒中都定時出了2秒多,,,這顯然是不可接受的。前後試過将心跳頻率提高/降低都無法提升時間的精度,這個時間的誤差都沒變化。
在滴答時鐘裡面每50ms中斷一次,時間基準就不對
也不知道這個空閑模式裡面到底幹了啥,驗證影響了程式的定時準确度
關掉空閑模式,這樣下來時間就很準了
原來這是低功耗配置,也就是每個tick不一定會進入。
Hankin
2020.10.05