電源管理
ESP-IDF中集成的電源管理算法可以根據應用程序組件的需求,調整外圍總線 (APB) 頻率、CPU 頻率,並使芯片進入 Light-sleep 模式,盡可能減少運行應用程序的功耗
應用程序組件可以通過創建和獲取電源管理鎖來控制功耗
編譯時可使用CONFIG_PM_ENABLE選項啟用電源管理功能
電源管理配置
(摘自官網)啟用電源管理功能將會增加中斷延遲。額外延遲與多個因素有關,例如CPU頻率、單/雙核模式、是否需要進行頻率切換等(CPU 頻率為 240 MHz 且未啟用頻率調節時,最小額外延遲為 0.2 us;如果啟用頻率調節,且在中斷入口將頻率由 40 MHz 調節至 80 MHz,則最大額外延遲為 40 us)
應用程序可以通過調用esp_pm_configure()函數啟用動態調頻(DFS)功能和自動light-sleep模式。
通過esp_pm_config_esp32_t結構體來設置相關參數,如下所示
struct esp_pm_config_esp32_t//pm代表power management
{
int max_freq_mhz;//最大CPU頻率,也就是獲取ESP_PM_CPU_FREQ_MAX鎖后使用的頻率,單位MHz
int min_freq_mhz;//最小CPU頻率,也就是獲取ESP_PM_APB_FREQ_MAX鎖后使用的頻率,單位MHz,可設置為晶振頻率值或晶振頻率除以一個整數,但是需要注意10MHz是生成1MHz的REF_TICK默認時鍾所需的最小頻率
bool light_sleep_enable;//當未獲得任何管理鎖時,決定系統是否需要自動進入light-sleep狀態
}
電源管理鎖與管理算法
應用程序可以通過獲取或釋放管理鎖來控制電源管理算法
ESP32 支持下表中所述的三種電源管理鎖。
電源管理鎖 | 描述 |
---|---|
ESP_PM_CPU_FREQ_MAX | 請求使用esp_pm_configure將CPU頻率設置為最大值。ESP32可以將該值設置為 80 MHz、160 MHz 或 240 MHz。 |
ESP_PM_APB_FREQ_MAX | 請求將APB頻率設置為最大值,ESP32支持的最大頻率為80MHz |
ESP_PM_NO_LIGHT_SLEEP | 禁止自動切換至Light-sleep模式 |
如果沒有獲取任何管理鎖,調用esp_pm_configure()將啟動Light-sleep模式
Light-sleep模式持續時間由以下因素決定:1.處於阻塞狀態的FreeRTOS任務書;2.高分辨率定時器API注冊的計數器數量
動態調頻和外設驅動
啟用動態調頻后,APB頻率可在一個RTOS滴答周期內多次更改。有些外設不受APB頻率變更的影響,但有些外設可能會出現問題
UART、LEDC、RMT不受APB頻率變更的影響
SPI master、I2C、I2S、SDMMC可以感知動態調頻並在調頻期間使用ESP_PM_APB_FREQ_MAX鎖
啟用SPI slave、以太網、wifi、藍牙、CAN時,將占用ESP_PM_APB_FREQ_MAX鎖
MCPWM、PCNT、Sigma-delta、Timer Group無法感知動態調頻,需要應用程序自行獲取、釋放管理鎖
ESP32 在內置Deep-sleep低功耗模式、RTC外設和ULP協處理器的支持下,可以滿足多種應用場景下的低功耗需求
(ULP協處理器見最后部分)
低功耗模式
ESP32可以進入light-sleep和deep-sleep模式,還能進入一個用於相對較低功耗運行的modem-sleep模式
注意:進入低功耗模式前,應用程序必須關閉wifi和藍牙設備,如果需要維持wifi連接,應當使用modem-sleep模式,在這個模式下當需要wifi驅動執行時系統會自動喚醒來維持wifi連接
light-sleep
CPU暫停運行,wifi/藍牙基帶和射頻關閉。RTC、ULP運行,任何喚醒事件都會喚醒芯片
在light-sleep模式下,數字外設、大部分內存和CPU都會被停用(停用時鍾),電源功耗也會降低,從light-sleep模式下喚醒后外設和CPU會接回時鍾源並繼續工作,他們的外部狀態會被保存
這個模式可以理解為電腦的掛起休眠
deep-sleep
CPU、大部分外設掉電,wifi/藍牙基帶和射頻關閉,進有RTC、ULP運行,wifi和藍牙連接數據被轉移到RTC內存中。僅有一部分中斷源會喚醒芯片
deep-sleep模式下,由APB_CLK時鍾提供是時鍾源的CPU、大部分內存和所有數字外設都會掉電;只有片上RTC控制器、RTC外設、ULP和RTC內存會被保留電源
這個模式可以理解為電腦的斷電休眠
Deep-sleep模式下支持從以下喚醒源觸發的設備喚醒
- 定時器
- touchpad
- Ext(0):RTC IO中某個指定GPIO滿足指定電平即喚醒
- Ext(1):RTC IO中某些指定GPIO同時滿足指定電平才能喚醒
- ULP協處理器
睡眠喚醒源可以在進入light-sleep或deep-sleep之前的任何時間設置
特別地,應用程序可以調用esp_sleep_pd_config()函數來讓RTC外設和RTC內存掉電
設置好中斷源后可以使用esp_light_sleep_start()和esp_deep_sleep_start()來進入睡眠模式
中斷源
使用esp_sleep_disable_wakeup_source()來停用某個已經設置的中斷源
設置中斷源的方法如下
定時器
RTC控制器自帶一個能夠在預定義時間后進行喚醒的定時器
這個換新模式不需要在睡眠期間為RTC外設或RTC內存上電
使用esp_sleep_enable_timer_wakeup()使能這個功能
觸摸檢測
RTC IO模塊包括了一套觸摸傳感器中斷觸發喚醒的邏輯,需要在MCU進入睡眠之前配置好觸摸中斷喚醒
只有在RTC外設沒有被強行上電的時候才能使用這個喚醒模式
使用esp_sleep_enable_touchpad_wakeup()函數來使能這個中斷源
特定外部引腳
RTC IO模塊包括了一套GPIO觸發喚醒的邏輯。如果這個中斷源被使能,RTC外設需要保持上電狀態。因為RTC IO模組在這個模式中被使能,中斷上拉或下拉電阻也會被使用到,它們需要通過rtc_gpio_pullup_en()和rtc_gpio_pulldown_en()函數設置
調用esp_sleep_enable_ext0_wakeup()函數來使能這個中斷源
此外,也可以使用多個GPIO同時觸發喚醒
配置API如下
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);//開啟特定GPIO喚醒
gpio_pullup_dis(gpio_num);//配置gpio_num為上拉
gpio_pulldown_en(gpio_num);//配置gpio_num為下拉
rtc_gpio_isolate(gpio_num);//配置gpio_num為高阻態
rtc_gpio_deinit(gpio_num);//使用這個函數來取消配置引腳
rtc_gpio_isolate()可以用於防止進入休眠后由GPIO產生的額外功耗
ULP協處理器
可以使用esp_sleep_enable_ulp_wakeup()來啟用ULP協處理器指令喚醒
GPIO
除了特定的外部引腳觸發喚醒,還可以在light-sleep下使用gpio_wakeup_enable()來設定任意GPIO的高/低電平觸發喚醒
在進入睡眠模式之前使用esp_sleep_enable_gio_wakeup()來使能該喚醒源
UART
在light-sleep下可以使用esp_sleep_enable_uart_wakeup()來啟用UART觸發喚醒
若開啟該觸發源,當睡眠狀態的ESP32收到來自外部設備的UART輸入的數個上升沿時,會自動喚醒,該上升沿數目可以用uart_set_wakeup_threshold()函數配置;在這個觸發信號被接收之前,所有信息不會被接收——這就意味着外部設備需要發送額外的字符給ESP32來讓它喚醒
modem-sleep
CPU運行、可配置時鍾,wifi/藍牙基帶和射頻關閉,但會維持wifi連接
RTC外設
RTC外設不僅包括RTC,還包括了片上溫度傳感器、ADC、RTC-GPIO和touchpad外設
ULP協處理器
ULP(Ultra Low Power超低功耗)協處理器是一種簡單的有限狀態機(FSM)。在主處理器處於Deep-sleep深度睡眠模式時,它可以使用ADC、溫度傳感器和外部IIC傳感器執行測量操作。ULP協處理器可以訪問RTC慢速內存區域(RTC_SLOW_MEM)及RTC_CNTL、RTC_IO、SARADC等外設寄存器。ULP協處理器使用32位固定寬度的指令、32位內存尋址,配備4個16位通用寄存器
ULP協處理器編程
ULP協處理器代碼是用匯編語言編寫的,並使用binutils-esp32ulp工具鏈進行編譯
開發環境被集成到ESP-IDF中
編譯方法如下:
-
ULP代碼必須導入到一個或多個.S擴展文件中,源文件必須放在組件目錄中一個獨立的目錄(如ulp/)
-
注冊后從組件CMakeLists.txt中調用ulp_embed_binary,示例如下
... idf_component_register() set(ulp_app_name ulp_${COMPONENT_NAME}) set(ulp_s_sources ulp/ulp_assembly_source_file.S) set(ulp_exp_dep_srcs "ulp_c_source_file.c") #二進制文件命名 ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}") # 導入二進制文件
-
使用常規方法編譯應用程序,ULP程序會被自動生成
構建系統的內部編譯步驟如下:
- 通過C預處理器運行每個.S文件,生成依賴文件和經過預處理的程序集文件
- 運行匯編器進行編譯
- 通過C預處理器運行鏈接器腳本模板(位於components/ulp/ld目錄)
- 將目標文件鏈接到.elf輸出文件
- 將elf中的內容轉儲為.bin二進制文件
- 使用esp32ulp-elf-nm工具在elf文件中生成全局符號列表
- 創建LD導出腳本和頭文件
- 將生成的二進制文件添加到要嵌入應用程序的二進制文件列表中
總體過程和在mcu中嵌入二進制格式的其他文件類似,只是多出了編譯的幾步
使用ULP程序
在ULP程序中定義的全局符號也可以在主程序中使用
如果要從主程序訪問ULP程序變量,應先使用include語句包含生成的頭文件,這樣就可以像訪問常規變量一樣訪問ulp程序變量
注意:ULP程序在RTC內存中只能使用32位字的低16位,因為寄存器是16位的並且不具備從字的高位加載的指令
主應用程序需要調用ulp_load_binary函數將ULP程序加載到RTC內存中,然后調用ulp_run函數啟動ULP程序。ULP協處理器由定時器啟動,而調用ulp_run則可啟動此定時器,定時器為RTC_SLOW_CLK的Tick事件計數(默認情況下,Tick由內部150 KHz晶振器生成)。一旦定時器為所選的SENS_ULP_CP_SLEEP_CYCx_REG寄存器的Tick事件計數,ULP協處理器就會啟動,並調用ulp_run的入口點開始運行程序。程序保持運行,直到遇到halt指令或非法指令。一旦程序停止,ULP協處理器電源關閉,定時器再次啟動。
使用 SENS_ULP_CP_SLEEP_CYCx_REG
寄存器 (x = 0..4) 設置 Tick 數值。第一次啟動 ULP 時,使用 SENS_ULP_CP_SLEEP_CYC0_REG
設置定時器 Tick 數值,之后,ULP 程序可以使用 sleep
指令來另外選擇 SENS_ULP_CP_SLEEP_CYCx_REG
寄存器
匯編指令集參考
ESP32與ESP32S2的匯編指令集並不相同,詳情參考官網即可
匯編大同小異,下面給出幾個常見的指令
指令 | 說明 |
---|---|
NOP | 空指令 |
SUB R1,R2,R3 | R1=R2-R3 |
AND R1,R2,R3 | R1=R2&R3 |
OR R1,R2,R3 | R1=R2|R3 |
LSH R1,R2,R3 | R1=R2<<R3 |
RSH R1,R2,R3 | R1=R2>>R3 |
MOVE R1,R2 | R1=R2 |
ST R1,R2,k | MEM[R2+k]=R1 |
LD R1,R2,k | R1=MEM[R2+k] |
JUMP R1 | 跳轉到R1所在地址 |
HALT | 協處理器停機 |
WAKE | 協處理器喚醒 |
SLEEP k | 協處理器睡眠k個時間單位 |
REG_RD Addr,HIGH,LOW | 讀外設寄存器地址為Addr從LOW到HIGH的內容 |
REG_WR Addr,HIGH,LOW,Data | 將Data寫入外設寄存器地址為Addr從LOW到HIGH的內容 |