【原創】STM32低功耗模式及中斷喚醒(基於BMI160及RTC)的研究


預研目標

       六軸靜止時,終端進入低功耗模式;六軸震動時,終端正常工作模式,從而極大減少非工作時的電流消耗。

解決方案

       機器靜止時,依據六軸算法,CPU進入休眠(停止)模式;機器工作時,觸發六軸中斷喚醒CPU,再配合系統空閑時進入CPU睡眠模式,從而極大降低機器非工作時的電流消耗和降低工作時底電流消耗。

關鍵技術

STM32功耗模式

按功耗由高到低排列,STM32具有運行、睡眠、停止和待機四種工作模式。上電復位后STM32處於運行狀態時,當內核不需要繼續運行,就可以選擇進入后面的三種低功耗模式降低功耗,這三種模式中,電源消耗不同、喚醒時間不同、喚醒源不同,用戶需要根據應用需求,選擇最佳的低功耗模式。三種低功耗的模式說明見表1。   

表 1  STM32的低功耗模式說明

模式

說明

進入方式

喚醒方式

對1.2V區域時鍾的影響

對VDD區域時鍾的影響

調壓器

睡眠

內核停止,所有外設包括M4核心的外設,如NVIC、系統時鍾(SysTick)等仍在運行

調用WFI命令

任一中斷

內核時鍾關,對其他時鍾和ADC時鍾無影響

調用WFE命令

喚醒事件

停止

所有的時鍾都已停止

配置PWR_CR寄存器的PDDS +LPDS 位+SLEEPDEEP位

+WFI或WFE命令

任一外部中斷( 在外部中斷寄存器中設置)

關閉所有1.2V區域的時鍾

HSI和HSE的振盪器關閉

開啟或處於低功耗模式( 依據電源控制寄存器的設定)

待機

1.2V 電源關閉

配置PWR_CR寄存器的PDDS +SLEEPDEEP位

+WFI或WFE命令

WKUP 引腳的上升沿、RTC鬧鍾事件、NRST 引腳上的外部復位、IWDG 復位

從表中可以看到,這三種低功耗模式層層遞進,運行的時鍾或芯片功能越來越少,因而功耗越來越低。

睡眠模式

在睡眠模式中,僅關閉了內核時鍾,內核停止運行,但其片上外設,CM4核心的外設全都還照常運行。有兩種方式進入睡眠模式,它的進入方式決定了從睡眠喚醒的方式,分別是WFI(wait for interrupt)和WFE(wait for event),即由等待"中斷"喚醒和由"事件"喚醒。睡眠模式的各種特性見表 2。

表 2  睡眠模式的各種特性

特性

說明

立即睡眠

在執行WFI 或WFE 指令時立即進入睡眠模式。

退出時睡眠

在退出優先級最低的中斷服務程序后才進入睡眠模式。

進入方式

內核寄存器的SLEEPDEEP = 0 ,然后調用WFI或WFE指令即可進入睡眠模式;

另外若內核寄存器的SLEEPONEXIT=0時,進入"立即睡眠"模式,SLEEPONEXIT=1時,進入"退出時睡眠"模式。

喚醒方式

如果是使用WFI指令睡眠的,則可使用任意中斷喚醒;

如果是使用WFE指令睡眠的,則由事件喚醒。

睡眠時

關閉內核時鍾,內核停止,而外設正常運行,在軟件上表現為不再執行新的代碼。這個狀態會保留睡眠前的內核寄存器、內存的數據。

喚醒延遲

無延遲。

喚醒后

若由中斷喚醒,先進入中斷,退出中斷服務程序后,接着執行WFI指令后的程序;若由事件喚醒,直接接着執行WFE后的程序。

停止模式

在停止模式中,進一步關閉了其它所有的時鍾,於是所有的外設都停止了工作,但由於其1.2V區域的部分電源沒有關閉,還保留了內核的寄存器、內存的信息,所以從停止模式喚醒,並重新開啟時鍾后,還可以從上次停止處繼續執行代碼。停止模式可以由任意一個外部中斷(EXTI)喚醒。在停止模式中可以選擇電壓調節器為開模式或低功耗模式,可選擇內部FLASH工作在正常模式或掉電模式。停止模式的各種特性見表3。

表 3 停止模式的各種特性

特性

說明

調壓器低功耗模式

在停止模式下調壓器可工作在正常模式或低功耗模式,可進一步降低功耗

FLASH掉電模式

在停止模式下FLASH可工作在正常模式或掉電模式,可進一步降低功耗

進入方式

內核寄存器的SLEEPDEEP =1,PWR_CR寄存器中的PDDS=0,然后調用WFI或WFE指令即可進入停止模式;

PWR_CR 寄存器的LPDS=0時,調壓器工作在正常模式,LPDS=1時工作在低功耗模式;

PWR_CR 寄存器的FPDS=0時,FLASH工作在正常模式,FPDS=1時進入掉電模式。

喚醒方式

如果是使用WFI指令睡眠的,可使用任意EXTI線的中斷喚醒;

如果是使用WFE指令睡眠的,可使用任意配置為事件模式的EXTI線事件喚醒。

停止時

內核停止,片上外設也停止。這個狀態會保留停止前的內核寄存器、內存的數據。

喚醒延遲

基礎延遲為HSI振盪器的啟動時間,若調壓器工作在低功耗模式,還需要加上調壓器從低功耗切換至正常模式下的時間,若FLASH工作在掉電模式,還需要加上FLASH從掉電模式喚醒的時間。

喚醒后

若由中斷喚醒,先進入中斷,退出中斷服務程序后,接着執行WFI指令后的程序;若由事件喚醒,直接接着執行WFE后的程序。喚醒后,STM32會使用HIS作為系統時鍾。

待機模式

待機模式,它除了關閉所有的時鍾,還把1.2V區域的電源也完全關閉了,也就是說,從待機模式喚醒后,由於沒有之前代碼的運行記錄,只能對芯片復位,重新檢測boot條件,從頭開始執行程序。它有四種喚醒方式,分別是WKUP(PA0)引腳的上升沿,RTC鬧鍾事件,NRST引腳的復位和IWDG(獨立看門狗)復位。

表 4 待機模式的各種特性

特性

說明

進入方式

內核寄存器的SLEEPDEEP =1,PWR_CR寄存器中的PDDS=1,PWR_CR寄存器中的喚醒狀態位WUF=0,然后調用WFI或WFE指令即可進入待機模式;

喚醒方式

通過WKUP引腳的上升沿,RTC鬧鍾、喚醒、入侵、時間戳事件或NRST引腳外部復位及IWDG復位喚醒。

待機時

內核停止,片上外設也停止;內核寄存器、內存的數據會丟失;除復位引腳、RTC_AF1引腳及WKUP引腳,其它I/O口均工作在高阻態。

喚醒延遲

芯片復位的時間

喚醒后

相當於芯片復位,在程序表現為從頭開始執行代碼。

在以上講解的睡眠模式、停止模式及待機模式中,若備份域電源正常供電,備份域內的RTC都可以正常運行、備份域內的寄存器及備份域內的SRAM數據會被保存,不受功耗模式影響。

 

功耗模式選擇及配置

功耗模式選擇

睡眠模式:RTOS空閑任務進入休眠,sysTick中斷喚醒;

停止模式:機器不工作(六軸靜止)時,進入停止模式;六軸動時中斷/RTC定時中斷觸發喚醒。

 

功耗模式配置

注:STOP模式下,喚醒后系統使用HSI作為系統時鍾,用戶可能需要未進入STOP模式前的系統時鍾配置。

 

RTC定時喚醒喂狗

       系統進入STOP模式后,需要RTC中斷定時喚醒喂狗防止系統復位。

RTC定時喚醒中斷使能:

HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0xA017, RTC_WAKEUPCLOCK_RTCCLK_DIV16);

RTC定時喚醒中斷失能:

HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);

RTC定時喚醒喂狗:

void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)

{

    feed_dog();

}

 

六軸BMI160

電源模式

在我們的產品場景中,Gyroscope(陀螺儀)未使用,默認配置為Suspend mode,Accelermoter(加速度)使用Normal mode(退出CPU休眠后)或Low power mode(進入休眠前配置);

 

中斷模式

 

模式選擇及配置

這里推薦使用BMI160官方驅動,源碼獲取參見參考資料第3項,關於驅動具體使用參照源碼README.md文件,下面

struct bmi160_dev sensor;

BMI160初始化

struct bmi160_dev sensor;

/* BMI160初始化 */
void AccelInit(void)
{

    /* You may assign a chip select identifier to be handled later */
    sensor.id = 0;
    sensor.interface = BMI160_SPI_INTF;
    sensor.read = user_spi_read;
    sensor.write = user_spi_write;
    sensor.delay_ms = user_delay_ms;

    int8_t rslt = BMI160_OK;
    rslt = bmi160_init(&sensor);
    printf("bmi160 chip id: %02x, accel power:%d, gyro power:%d\r\n", sensor.chip_id, sensor.accel_cfg.power, sensor.gyro_cfg.power);

    /* After the above function call, accel_cfg and gyro_cfg parameters in the device
    structure are set with default values, found in the datasheet of the sensor */
}

/* BMI160電源模式配置 */
void AccelConfig(void)
{
    int8_t rslt = BMI160_OK;
    struct bmi160_pmu_status pmuStatus;

    /* Select the Output data rate, range of accelerometer sensor */
    sensor.accel_cfg.odr = BMI160_ACCEL_ODR_400HZ;
    sensor.accel_cfg.range = BMI160_ACCEL_RANGE_4G;
    sensor.accel_cfg.bw = BMI160_ACCEL_BW_NORMAL_AVG4;
   
    /* Select the power mode of accelerometer sensor */
    sensor.accel_cfg.power = BMI160_ACCEL_NORMAL_MODE;

    /* Set the sensor configuration */
    rslt = bmi160_set_sens_conf(&sensor);

    bmi160_get_power_mode( &pmuStatus, &sensor);
    //printf("accel power:%d, gyro power:%d\r\n", pmuStatus.accel_pmu_status, pmuStatus.gyro_pmu_status);
}

/* BMI160 Any-motion中斷配置 */
void AnyMotionIntCfg(void)
{
    struct bmi160_int_settg int_config;

    /* Select the Interrupt channel/pin */
    int_config.int_channel = BMI160_INT_CHANNEL_BOTH;// Interrupt channel/pin 1

    /* Select the Interrupt type */
    int_config.int_type = BMI160_ACC_ANY_MOTION_INT;// Choosing Any motion interrupt

    /* Select the interrupt channel/pin settings */
    int_config.int_pin_settg.output_en = BMI160_ENABLE;// Enabling interrupt pins to act as output pin
    int_config.int_pin_settg.output_mode = BMI160_DISABLE;// Choosing push-pull mode for interrupt pin
    int_config.int_pin_settg.output_type = BMI160_DISABLE;// Choosing active low output
    int_config.int_pin_settg.edge_ctrl = BMI160_ENABLE;// Choosing edge triggered output
    int_config.int_pin_settg.input_en = BMI160_DISABLE;// Disabling interrupt pin to act as input
    int_config.int_pin_settg.latch_dur = BMI160_LATCH_DUR_NONE;// non-latched output

    /* Select the Any-motion interrupt parameters */
    int_config.int_type_cfg.acc_any_motion_int.anymotion_en = BMI160_ENABLE;// 1- Enable the any-motion, 0- disable any-motion
    int_config.int_type_cfg.acc_any_motion_int.anymotion_x = BMI160_ENABLE;// Enabling x-axis for any motion interrupt
    int_config.int_type_cfg.acc_any_motion_int.anymotion_y = BMI160_ENABLE;// Enabling y-axis for any motion interrupt
    int_config.int_type_cfg.acc_any_motion_int.anymotion_z = BMI160_ENABLE;// Enabling z-axis for any motion interrupt
    int_config.int_type_cfg.acc_any_motion_int.anymotion_dur = 0;// any-motion duration
    int_config.int_type_cfg.acc_any_motion_int.anymotion_thr = 20;// (2-g range) -> (slope_thr) * 3.91 mg, (4-g range) -> (slope_thr) * 7.81 mg, (8-g range) ->(slope_thr) * 15.63 mg, (16-g range) -> (slope_thr) * 31.25 mg

    /* Set the Any-motion interrupt */
    bmi160_set_int_config(&int_config, &sensor); /* sensor is an instance of the structure bmi160_dev  */
}

 

 

方案驗證

測試用例

typedef enum
{
  STOP_MODE_WAKE_FROM_NULL,
  STOP_MODE_WAKE_FROM_RTC,
  STOP_MODE_WAKE_FROM_ACCEL,
}StopModeWakeEnum;
 StopModeWakeEnum wakeReason = STOP_MODE_WAKE_FROM_NULL; void StopModeTest(void *pdata)
{ RCC_ClkInitTypeDef clkCfgPre; uint32_t flatencyPre
= 0; uint32_t freqPre = 0; GPIO_InitTypeDef GPIO_InitStruct; AccelInit(); AccelConfig(); AnyMotionIntCfg(); for(;;) { printf("\r\nSTM32 runing, system led off\r\n"); PrintSysClkInfo(); SysLedOff(); printf("\r\nSTM32 enter stop mode\r\n"); /*## Configure the Wake up timer ###########################################*/ /* RTC Wake-up Interrupt Generation: Wake-up Time Base = (RTC_WAKEUPCLOCK_RTCCLK_DIV /(LSI)) Wake-up Time = Wake-up Time Base * WakeUpCounter = (RTC_WAKEUPCLOCK_RTCCLK_DIV /(LSI)) * WakeUpCounter ==> WakeUpCounter = Wake-up Time / Wake-up Time Base To configure the wake up timer to 20s the WakeUpCounter is set to 0xA017: RTC_WAKEUPCLOCK_RTCCLK_DIV = RTCCLK_Div16 = 16 Wake-up Time Base = 16 /(~32.768KHz) = ~0,488 ms Wake-up Time = ~20s = 0,488ms * WakeUpCounter ==> WakeUpCounter = ~20s/0,488ms = 40983 = 0xA017 */ HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0xA017, RTC_WAKEUPCLOCK_RTCCLK_DIV16); /* FLASH Deep Power Down Mode enabled */ HAL_PWREx_EnableFlashPowerDown(); /* Enter Stop Mode */ HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); HAL_PWREx_DisableFlashPowerDown(); HAL_RCC_GetClockConfig( &clkCfgPre, &flatencyPre); freqPre = HAL_RCC_GetSysClockFreq (); SYSCLKConfig_STOP(); /* Disable Wake-up timer */ if(HAL_RTCEx_DeactivateWakeUpTimer(&hrtc) != HAL_OK) { /* Initialization Error */ Error_Handler(); } switch(wakeReason) { case STOP_MODE_WAKE_FROM_RTC: printf("\r\nSTM32 wake from stop mode from RTC, system led on\r\n"); break; case STOP_MODE_WAKE_FROM_ACCEL: printf("\r\nSTM32 wake from stop mode from ACCEL, system led on\r\n"); break; default: printf("\r\nSTM32 wake from stop mode from %d\r\n", wakeReason); break; } printf("SysFreq:%u, ClkSrc:%d(0:HSI, 1:HSE, 2:PLL, 3:PLLR)\r\n", freqPre, clkCfgPre.SYSCLKSource); printf("\r\nSTM32 recover system clk config\r\n"); PrintSysClkInfo(); OSTimeDlyHMSM( 0, 0, 5, 0); } } void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc) { SysLedOn(); feed_dog(); wakeReason = STOP_MODE_WAKE_FROM_RTC; } void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
  switch(GPIO_Pin) {     case WAKED_FROM_BT_Pin: if(GPIO_PIN_SET == HAL_GPIO_ReadPin(WAKED_FROM_BT_GPIO_Port, WAKED_FROM_BT_Pin)) {/*wake start*/ BleSpiRxRestart(); }break; case INT_ALARM_Pin: break; case GYRO_INT1_Pin: if(GPIO_PIN_SET == HAL_GPIO_ReadPin(GYRO_INT1_GPIO_Port, GYRO_INT1_Pin)) { } break; case GYRO_INT2_Pin: if(GPIO_PIN_SET == HAL_GPIO_ReadPin(GYRO_INT2_GPIO_Port, GYRO_INT2_Pin)) { SysLedOn(); wakeReason = STOP_MODE_WAKE_FROM_ACCEL; } break;
default:        break;
 }
}

 

實驗步驟

  1. 測試用例燒錄終端,連接打印串口、打開串口調試助手;
  2. 18:15:49前保持終端靜止、待進入CPU休眠后再次震動終端1次,重復操作2次;

測試log輸出:

實驗結論

CPU進入停止模式后,RTC定時中斷/六軸中斷可喚醒CPU,喚醒后CPU使用HSI作為系統時鍾源,CPU進入停止模式后功耗極大降低,可實現機器非工作時電流消耗的極大降低。

參考資料

1. 《STM32F401xB/C and STM32F401xD/E advanced Arm®-based 32-bit MCUs》

2.《Description of STM32F4 HAL and LL drivers》

3.https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMI160-DS000.pdf

4. https://github.com/BoschSensortec/BMI160_driver

 

作者:大毛孩 出處: https://www.cnblogs.com/damaohai/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。如果覺得還有幫助的話,可以點一下右下角的【推薦】。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM