低功耗管理(Low Power Management)也可以稱為功耗管理(Power Management),本?檔中會簡稱為PM。
Telink低功耗解惑
????????我查閱多連接SDK開發手冊時,低功耗管理章節看了兩三遍也沒太明白,有以下幾個問題一直沒得以解決,比較困惑,麻煩大神給點指
????????進入低功耗好像只有cpu_sleep_wakeup()這個API可以設置進入低功耗的時間,而且還只有200+秒鐘,而且只有timer和pad兩種喚醒方式;那么
- 等到有master發送連接請求了,再退出sleep,等上5秒之后,如果主機還沒發送需要的信號,slave又重新進入sleep,如果發送了就不進入sleep,又應該如何設置呢?
- 如果想設置更長的睡眠時間怎么設置呢?
- 如果用串口喚醒的話,怎么設置呢?
- 以及比如下面這情況,是通過cpu_sleep_wakeup()API設置的嗎?,通過這個API設置的tick和這里的sleep時間有關嗎?這情況和這個API有什么關系呢?
回答:
(1)(4) 在Telink BLE Multiple Connection SDK中,如果當前有BLE任務,廣播/掃描/連接,睡眠由BLE stack進行管控,用戶不需要介入,但是用戶可以在app_process_power_management()使用blc_pm_setSleepMask()配置當前是否暫時關閉sleep,比如當前正在按鍵或進行OTA。
(3) 如果用串口喚醒的話,建議配置UART_Rx_GPIO為低電平喚醒API cpu_set_gpio_wakeup(),在回調BLT_EV_FLAG_SLEEP_ENTER里將UART_Rx_GPIO配置為GPIO模式,在回調BLT_EV_FLAG_SUSPEND_EXIT里將UART_Rx_GPIO配置為UART模式。
當沒有BLE任務(廣播關閉/掃描關閉/連接斷開),BLE stack不進行睡眠管控,程序在main_loop()循環轉,這時用戶可以使用API cpu_sleep_wakeup()自行管理睡眠時間,
(2)當期望睡眠時間大于268秒可以使用API cpu_long_sleep_wakeup_32k_rc()自行管理睡眠時間。
如果只打開BLE_APP_PM_ENABLE,協議棧在任務空閑時只會進入suspend。如果同時打開BLE_APP_PM_ENABLE和PM_DEEPSLEEP_RETENTION_ENABLE,協議棧在任務空閑時會進入suspend或deepsleep retention,會根據任務空閑的時間來選擇進入哪個睡眠模式,空閑時間長進入deepsleep retention,空閑時間短進入suspend,空閑時間的門限是API blc_pm_setDeepsleepRetentionThreshold()設置的,默認配置為95ms。
注:
對于uart的接收GPIO作為串口喚醒;在回調BLT_EV_FLAG_SUSPEND_ENTER里將UART_Rx_GPIO配置為GPIO模式,在回調BLT_EV_FLAG_SUSPEND_EXIT里將UART_Rx_GPIO配置為UART模式。
bls_app_registerEventCallback(BLT_EV_FLAG_SUSPEND_EXIT, &task_suspend_exit);
bls_app_registerEventCallback(BLT_EV_FLAG_SUSPEND_ENTER, &task_suspend_enter);
在回調函數 task_suspend_exit中UART_Rx_GPIO配置為UART模式,task_suspend_enter中UART_Rx_GPIO配置為GPIO模式。
空閑時間的門限是API blc_pm_setDeepsleepRetentionThreshold()設置的,默認配置為95ms。
這個95ms的值,不是唯一固定的,你要看你的SDK中,power?management initialization中,是否有規定了adv和conn的門限值。
低功耗喚醒源
MCU 的suspend/deepsleep/deepsleep retention 在硬件上有2 個喚醒源:TIMER、GPIO PAD。
- 喚醒源PM_WAKEUP_TIMER 來?硬件32k timer(32k RC timer or 32k Crystal timer)。32k timer 在SDK 中已經被正確初始化,user 在使?時不需要任何配置,只需要在cpu_sleep_wakeup() 中設置該喚醒源即可。
- 喚醒源PM_WAKEUP_PAD 來?GPIO 模塊,除MSPI 4 個管腳外所有的GPIO(PAx/PBx/PCx/PDx)的?低電平都具有喚醒功能。
配置GPIO PAD 喚醒sleep mode 的API:
typedef enum{
Level_Low=0,
Level_High =1,
} GPIO_LevelTypeDef;
void cpu_set_gpio_wakeup (GPIO_PinTypeDef pin, GPIO_LevelTypeDef pol, int en);
- pin 為GPIO 定義。
- pol 為喚醒極性定義:Level_High 表??電平喚醒,Level_Low 表?低電平喚醒。
- en: 1 表?enable,0 表?disable。
舉例說明:
cpu_set_gpio_wakeup (GPIO_PC2, Level_High, 1); //GPIO_PC2 PAD 喚醒打開, ?電平喚醒
cpu_set_gpio_wakeup (GPIO_PC2, Level_High, 0); //GPIO_PC2 PAD 喚醒關閉
cpu_set_gpio_wakeup (GPIO_PB5, Level_Low, 1); //GPIO_PB5 PAD 喚醒打開, 低電平喚醒
cpu_set_gpio_wakeup (GPIO_PB5, Level_Low, 0); //GPIO_PB5 PAD 喚醒關閉
低功耗模式的進?和喚醒
設置MCU 進?睡眠和喚醒的API 為:
int cpu_sleep_wakeup (SleepMode_TypeDef sleep_mode, SleepWakeupSrc_TypeDef wakeup_src, unsigned int wakeup_tick);
- 第?個參數sleep_mode:設置sleep mode,有以下4 個選擇,分別表?suspend mode、deepsleep mode、deepsleep retention 16K Sram、deepsleep retention 32K Sram。
typedef enum {
SUSPEND_MODE = 0,
DEEPSLEEP_MODE = 0x80,
DEEPSLEEP_MODE_RET_SRAM_LOW16K = 0x43,
DEEPSLEEP_MODE_RET_SRAM_LOW32K = 0x07,
}SleepMode_TypeDef;
- 第?個參數wakeup_src:設置當前的suspend/deepsleep 的喚醒源,參數只能是PM_WAKEUP_PAD、PM_WAKEUP_TIMER 中的?個或者多個。如果wakeup_src 為0,那么進?低功耗sleep mode 后,?法被喚醒。
- 第三個參數“wakeup_tick”:當wakeup_src 中設置了PM_WAKEUP_TIMER 時,需要設置wakeup_tick來決定timer 在何時將MCU 喚醒。如果沒有設置PM_WAKEUP_TIMER 喚醒,該參數?意義。
wakeup_tick 的值是?個絕對值,按照本?檔前?介紹的System Timer tick 來設置,當System Timer tick 的值達到這個設定的wakeup_tick 后,sleep mode 被喚醒。wakeup_tick 的值需要根據當前的System Timer tick的值,加上由需要睡眠的時間換算成的絕對時間,才可以有效地控制睡眠時間。如果沒有考慮當前的SystemTimer tick,直接對wakeup_tick 進?設置,喚醒的時間點就?法控制。
由于wakeup_tick 是絕對時間,必須在32bit 的System Timer tick 能表?的范圍之內,所以這個API 能表?的最?睡眠時間是有限的。?前的設計是最?睡眠時間為32bit 能表?的最?System Timer tick 對應時間的7/8。System Timer tick 最?能表??概268s,那么最?sleep 時間時間為268*7/8=234 s,即下?delta_Tick不能超過234 s, 若需要更?的睡眠時間,user 可以調??睡眠函數,具體可參考4.2.7 章節。
cpu_sleep_wakeup(SUSPEND_MODE, PM_WAKEUP_TIMER, clock_time() + delta_tick);
cpu_sleep_wakeup (SUSPEND_MODE , PM_WAKEUP_PAD, 0);
cpu_sleep_wakeup (SUSPEND_MODE , PM_WAKEUP_PAD | PM_WAKEUP_TIMER,
clock_time() + 50* CLOCK_16M_SYS_TIMER_CLK_1MS);
deepsleep mode
cpu_sleep_wakeup (DEEPSLEEP_MODE, PM_WAKEUP_PAD, 0);
cpu_sleep_wakeup (DEEPSLEEP_MODE_RET_SRAM_LOW32K , PM_WAKEUP_TIMER, clock_time() + 8*
? CLOCK_16M_SYS_TIMER_CLK_1S);
cpu_sleep_wakeup (DEEPSLEEP_MODE_RET_SRAM_LOW32K , PM_WAKEUP_PAD | PM_WAKEUP_TIMER,clock_time()
? + 10* CLOCK_16M_SYS_TIMER_CLK_1S);
低功耗運行流程
- no sleep?
- 如果沒有sleep mode,MCU 的運?流程為在while(1) 中循環,反復執?“Operation Set A” -????????>“Operation Set B”。
- suspend? ?
- 如果調?cpu_sleep_wakeup 函數進?suspend mode,當suspend 被喚醒后,相當于cpu_sleep_wakeup 函數的正常退出,MCU 運?到“Operation Set B”。
- suspend 是最?凈的sleep mode,在suspend 期間所有的Sram 數據能保持不變,所有的數字/模擬寄存器狀態也保持不變(只有?個特殊的例外);suspend 喚醒后,程序接著原來的位置運?,?乎不需要考慮任何sram和寄存器狀態的恢復。suspend 的缺點是功耗偏?。
- deepsleep
- 如果調?cpu_sleep_wakeup 函數進?deepsleep mode,當deepsleep 被喚醒后,MCU 會重新回到Runhardware bootloader。
- 可以看出,deepsleep wake_up 跟Power on 的流程是?乎?致的,所有的軟硬件初始化都得重新做。MCU 進?deepsleep 后,所有的Sram 和數字/模擬寄存器(只有?個模擬寄存器例外)都會掉電,所以功耗很低,MCU 電流?于1uA。
- deepsleep retention
- 如果調?cpu_sleep_wakeup 函數進?deepsleep retention mode,當deepsleep retention 被喚醒后,MCU會重新回到Run software bootloader。
- deepsleep retention 是介于suspend 和deepsleep 之間的?種sleep mode。
- suspend 因為要保存所有的sram 和寄存器狀態?導致電流偏?;deepsleep retention 不需要保存寄存器狀態,Sram 只保留前16K(或32K)不掉電,所以功耗?suspend 低很多,只有2uA 左右。
- deepsleep wake_up 后需要把所有的流程重新運??遍,?deepsleep retention 可以跳過“Run hardware bootloader”這?步,這是因為Sram 的前16K(32K)上數據是不丟的,不需要再從flash 上重新拷??次。但由于Sram 上retention area 有限,“run software bootloader”?法跳過,必須得執?;由于deepsleep retention ?法保存寄存器狀態,所以system initilization 必須執?,寄存器的初始化需要重新設置。deepsleep retention wake_up 后的user initilization 可以做?些優化改進,和MCU power on/deepsleep wake_up 后的user initilization 做區分處理,參考本?檔后?的介紹。
API
int pm_is_MCU_deepRetentionWakeup(void);
return 值為1,表?deepsleep retention wake_up;return 值為0,表?power on 或deepsleep wake_up。
void bls_pm_setSuspendMask (u8 mask);
u8 bls_pm_getSuspendMask (void);
mask可以有多個選擇
#define SUSPEND_DISABLE 0
#define SUSPEND_ADV BIT(0)
#define SUSPEND_CONN BIT(1)
#define DEEPSLEEP_RETENTION_ADV BIT(2)
#define DEEPSLEEP_RETENTION_CONN BIT(3)
SUSPEND_DISABLE 表?sleep disable,不允許MCU 進?suspend 和deepsleep retention。
SUSPEND_ADV 和DEEPSLEEP_RETENTION_ADV 分別?于控制Advertising state 時MCU 進?suspend 和deepsleep retention。
SUSPEND_CONN 和DEEPSLEEP_RETENTION_CONN 分別?于控制Conn state Slave role 時MCU 進?suspend和deepsleep retention。
SDK 低功耗sleep mode 的設計上,deepsleep retention 是suspend 的替代模式,?的是降低sleep mode 的功耗。
以Conn state slave role 為例,SDK ?先得看到bltPm.suspend_mask 中SUSPEND_CONN 是否?效,才可以進?suspend。在可以進?suspend 的基礎上,根據實際情況再結合bltPm.suspend_mask 中DEEPSLEEP_RETENTION_CONN 是否?效,才能決定此時suspend mode 是否被切換為deepsleep retention mode。
所以如果user 希望MCU 進?suspend,打開SUSPEND_ADV/SUSPEND_CONN 即可;如果希望MCU 進?deepsleep retention mode,必須同時打開SUSPEND_CONN 和DEEPSLEEP_RETENTION_CONN。
該API 最常?的3 種情況如下:
bls_pm_setSuspendMask(SUSPEND_DISABLE);
MCU 不允許進?sleep mode。
bls_pm_setSuspendMask(SUSPEND_ADV | SUSPEND_CONN);
MCU 在Advertising state 和Conn state Slave role 只允許進?suspend,但是不允許進?deepsleep retention
bls_pm_setSuspendMask(SUSPEND_ADV | DEEPSLEEP_RETENTION_ADV
|SUSPEND_CONN | DEEPSLEEP_RETENTION_CONN);
MCU 在Advertising state 和Conn state Slave role 允許進?suspend 和deepsleep retention,具體進?哪種?sleep mode 由當前sleep 的時間?度決定。
閾值:
blc_pm_setDeepsleepRetentionThreshold(43, 43);
設置43ms
以?個10ms connection interval * (99 + 1) = 1s 的?連接為例進?說明:
在Conn state slave role 時,由于應?層的任務、?動latency 的設置等,會導致MCU suspend 時可能出現10ms、20ms、50ms、100ms、1s 等時間值。根據43ms 的閾值設置,MCU 會?動將50ms、100ms、1s 等suspend 切換為deepsleep retention,?10ms、20ms 等suspend 還是維持suspend,這樣的處理可以保證?個最優的功耗。
connection latency ?效時的Sleep 時序
if(conn_latency != 0)
{latency_use = bls_calculateLatency();T_wakeup = T_brx + (latency_use +1) * conn_interval;
}
else
{T_wakeup = T_brx + conn_interval;
}
當BLE slave 經過connection parameters update(連接參數更新)流程,conn_latency ?效后,sleep wake_up的時間為
T_wakeup = T_brx + (latency_use +1) * conn_interval;
下圖所?為?個conn_latency ?效時的sleep 時序,此時latency_use= 2。
conn_latency 沒有?效時,sleep 的時間最?不超過1 個connection interval (?般都?較?)。由于conn_latency的?效,sleep 的時間可能會出現?個?較?的值,如1s、2s 等,系統功耗可以變得?常低。?sleep 期間使?功耗更?的deepsleep retention mode 才變得有意義。
應?層定時喚醒
應?層定時喚醒API:
void bls_pm_setAppWakeupLowPower(u32 wakeup_tick, u8 enable);
- wakeup_tick 為定時喚醒的System Timer tick 值;
- enable 為1 時打開該喚醒功能,enable 為0 時關閉。
以Conn state Slave role 為例:
當user 使?bls_pm_setAppWakeupLowPower 設置了應?層定時喚醒的app_wakeup_tick,SDK 在進?sleep 前,會檢查app_wakeup_tick 是否在T_wakeup 之前。
- 如果app_wakeup_tick 在T_wakeup 之前,如下圖所?,就會在app_wakeup_tick 觸發sleep 提前喚醒;
- 如果app_wakeup_tick 在T_wakeup 之后,MCU 還是會在T_wakeup 喚醒。