在工作過程中,遇到這樣一個產品,它基於 Cortex-M7 內核的 STM32F769 芯片,同時使用了 FreeRTOS 實時操作系統。
由於該產品使用電池供電,因此有着低功耗的需求。
接下來,我將簡單描述一下 STM32 與 FreeRTOS 各自的低功耗特性,以及在配合使用時如何去實現產品的低功耗。
一、STM32F769 芯片的三種低功耗模式[1]
STM32F769 支持三種低功耗模式,它們分別是:SLEEP、STOP和STANDBY,其省電能力依次增強。
-
SLEEP
在 SLEEP 模式下,只有 Cortex-M7 內核停止了工作,而外設仍然在運行。
在進入 SLEEP 模式后,所有中斷均可喚醒 MCU,從而退出 SLEEP 模式。
- STOP
在 STOP 模式下,內核停止工作,並且所有的時鍾(如 HCLK, PCLK1, PCLK2 等)也停止工作,即所有外設停止工作,這里有一點要特別注意,此時 SYSTICK 也會被停掉。當然,我們產品中的 RTC 還在繼續運行,因為它的時鍾源為外部的 32.768K 晶振。
在進入 STOP 模式后,只有外部中斷(EXTI)才能喚醒 MCU(由於 RTC 中斷掛在外部中斷線上,所以 RTC 中斷也能喚醒 MCU)。
- STANDBY
在 STANDBY 模式下,內核、所有的時鍾、以及后備 1.2V 電源全部停止工作。
從 STANDBY 模式中喚醒后,系統相當於執行了一次復位操作,程序會從頭來過。
綜上所述,很明顯地,在STM32 提供的這三種低功耗模式中,我們只能使用其中的 SLEEP 和 STOP 這兩種,STANDBY 不適用。
關於 STM32769 更詳細的低功耗內容介紹,請查看 Reference Manual 的4.3節 – Low-power modes.
二、FreeRTOS 的低功耗實現[2]
在啟動任務調度器時,FreeRTOS 會創建一個 IDLE 任務,其任務優先級最低,當且僅當所有其它任務均被阻塞時,IDLE 任務才會獲得 CPU 使用權。
因此,可以很容易想到在 IDLE 任務里去實現進入與退出 STM32F769 的低功耗模式,即在切入 IDLE 任務后,讓 STM32 也進入低功耗模式,而在即將切換出 IDLE 任務之前,去喚醒 STM32。
另外,較新版本的 FreeRTOS 中,增加了 Tickless mode,更詳細的介紹請查看參考文獻[2].
三、整個產品的低功耗實現
那么在 IDLE 任務里,要如何去確定當前 STM32 應該是進入 SLEEP 還是 STOP 模式呢?
考慮到 SLEEP 和 STOP 兩者之間的差異,即 SLEEP 下任何中斷均可喚醒 STM32,而在 STOP 下,只能通過外部中斷去喚醒,所以,我們的產品采用了如下的機制:
在可確定的將來的一段時間內,如果程序員知道這期間會發生一個非外部中斷,這時,就不能讓 STM32 進入 STOP 模式。因為,一旦進入了 STOP,STM32 就只能響應外部中斷,而不能對非外部中斷(如串口、I2C 等外設中斷)作出響應。
舉個例子會更便於理解。假設這樣一個場景 —— 通過中斷去讀取 I2C 數據。在程序員配置好 I2C 讀取數據中斷后,系統就處於等待 I2C 中斷的狀態。之后如果產生了 I2C 中斷,就代表數據已經讀取完畢,程序員接下來就可以去處理數據了。
接上面的,在配置好 I2C 讀取數據中斷后,如果此時 IDLE 任務得到執行,那么,這種情況下就不能讓 STM32 進入 STOP 模式,而只能進入 SLEEP 模式。一旦產生了 I2C 中斷,則 STM32 就會從 SLEEP 中被喚醒。而如果之前 STM32 進入了 STOP 模式,那么這個 I2C 中斷就會被略掉了。
所以,在這個產品中,我們提供了兩個接口,disable_enter_stop_mode 和 enable_enter_stop_mode,分別用來告知,當前不能進入 STOP 模式和當前可以進入 STOP 模式了。
整理一下,我們可以得到如下的流程圖:
如果當前可以進入 STOP 模式,在真正進入 STOP 之前,還有一件事要做——配置 RTC 喚醒定時器,讓其在某一時刻來喚醒 STM32。具體能在 STOP 模式下睡多長時間,由 FreeRTOS 中的 prvGetExpectedIdleTime 接口計算得出。
RTC 喚醒定時器配置完成后,即可通過調用 HAL_PWR_EnterSTOPMode 讓 STM32 進入 STOP 模式了。如果此時沒有任何中斷處於 PENDING 狀態,則 STM32 會立即進入 STOP 模式,如果此時有中斷處於 PENDING 狀態,則 STM32 不會進入 STOP 模式,代碼會繼續往下執行。
在 STM32 處於 STOP 模式期間,如果產生了任何外部中斷(EXTI 中斷),則 STM32 會被立馬喚醒,不管 RTC 喚醒定時器有沒有超時。如果期間一直沒有外部中斷,那么 STM32 會一直處於 STOP 模式,直到 RTC 喚醒定時器超時,從而將 STM32 喚醒。
另外,由於在 STOP 下,為 FreeRTOS 提供心跳時鍾的 SYSTICK 也停止了工作,所以,在被喚醒之后,還需要將在 STOP 下流逝的時間告訴 FreeRTOS。
總之,降低整個產品功耗的基本思想,就是讓 FreeRTOS 僅可能多的時間處於 IDLE 任務,讓 STM32 盡可能多的時間處於 STOP 模式,最終達到盡可能多的降低功耗的目的。
過段時間有空了,再把相關的代碼整理一下貼出來,以供參考。
參考文獻: