STM32duino delay power saving.
如果使用STM32duino平台寫code,又需要使用low power的功能做省電時,若使用delay()語法做延遲,MCU會用全速的速度等待delay()設定的時間
例如
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(5000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(5000);
然後,delay這個function內部長這個樣子
void delay( uint32_t ms )
{
if (ms == 0)
return;
uint32_t start = GetCurrentMilli();
do {
yield();
} while (GetCurrentMilli() - start < ms);
}
所以當我們從外部呼叫delay(5000)時,MCU會以全速在do-while之間,繞圈不做任何事。
以STM32L051@32Mhz來說,核心的消耗電流為
140uA * 32 = 4.48 mA,這五秒間就白白把這些電消耗掉不做任何事。
如果需要優化,則需要在do-while之間增加省電機制,讓MCU掉入睡眠模式。
讓我們看看,在do-while迴圈間的yield()在做什麼事情
/**
* Empty yield() hook.
*
* This function is intended to be used by library writers to build
* libraries or sketches that supports cooperative threads.
*
* Its defined as a weak symbol and it can be redefined to implement a
* real cooperative scheduler.
*/
static void __empty() {
// Empty
}
void yield(void) __attribute__ ((weak, alias("__empty")));
這function讓使用者根據函式庫或者開發環境來自定義,因為有使用__attribute__ 前綴,所以只要在code內寫另一yield的函式,GCC會取代現有的yield函式(也就是目前無任何功能的"空"函式)。
所以,只要將yield()的函式設計成,讓系統進入sleep mode即可。
參考AN4445,STM32L0xx ultra-low power features overview
Sleep mode,只有將CPU進入Sleep,其餘皆正常運作,不用擔心外部中斷,或者正在通訊的UART會有收不到資料的情況。
但是,即使這樣,CPU停止後仍需要中斷,將CPU重新啟動,若是沒有外部中斷,或者UART能喚醒怎麼辦?
這時,就得了解STM32duino,其系統預設會使用SYSTICK做為系統時鐘,該時鐘會1ms中斷一次,所以即使進入睡眠,系統也會在1ms後醒來。
/**
* @brief This function configures the source of the time base.
* The time source is configured to have 1ms time base with a dedicated
* Tick interrupt priority.
* @note This function is called automatically at the beginning of program after
* reset by HAL_Init() or at any time when clock is reconfigured by HAL_RCC_ClockConfig().
* @note In the default implementation, SysTick timer is the source of time base.
* It is used to generate interrupts at regular time intervals.
* Care must be taken if HAL_Delay() is called from a peripheral ISR process,
* The the SysTick interrupt must have higher priority (numerically lower)
* than the peripheral interrupt. Otherwise the caller ISR process will be blocked.
* The function is declared as __Weak to be overwritten in case of other
* implementation in user file.
* @param TickPriority Tick interrupt priority.
* @retval HAL status
*/
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
/*Configure the SysTick to have interrupt in 1ms time basis*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000U);
/*Configure the SysTick IRQ priority */
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0U);
/* Return function status */
return HAL_OK;
}
這樣的設定,搭配sleep mode,MCU會在delay()迴圈時這樣跑
sleep() => SysTick ISR => do-while loop check => sleep()…
理論上,99%的時間都在sleep()睡覺,這樣就可以達到省電的目的。
那到底要怎樣實做呢?其實非常簡單,只要在STM32duino的Sketch內,內新增
void yield(void){
HAL_PWR_EnterSLEEPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI);
}
並於setup()內,Initial LowPower_init函式
LowPower_init();
這樣就能於delay時啟動省電機制了,簡單吧!