STM32CubeMX學(xué)習(xí)筆記(17)——電源管理(PWR)低功耗待機(jī)模式

一、低功耗模式簡介

系統(tǒng)提供了多個(gè)低功耗模式,可在 CPU 不需要運(yùn)行時(shí)(例如等待外部事件時(shí))節(jié)省功耗。由用戶根據(jù)應(yīng)用選擇具體的低功耗模式,以在低功耗、短啟動(dòng)時(shí)間和可用喚醒源之間尋求最佳平衡。

睡眠模式、停止模式及待機(jī)模式中,若備份域電源正常供電,備份域內(nèi)的 RTC 都可以正常運(yùn)行,備份域內(nèi)的寄存器的數(shù)據(jù)會(huì)被保存,不受功耗模式影響。

從表中可以看到,這三種低功耗模式層層遞進(jìn),運(yùn)行的時(shí)鐘或芯片功能越來越少,因而功耗越來越低。

模式名稱 說明 進(jìn)入方式 喚醒方式 對(duì)1.8V區(qū)域時(shí)鐘的影響 對(duì)VDD區(qū)域時(shí)鐘的影響 調(diào)壓器
睡眠模式 內(nèi)核停止,所有外設(shè)包括M3核心的外設(shè),如NVIC、系統(tǒng)時(shí)鐘(SysTick)等仍在運(yùn)行 調(diào)用WFI命令 任意中斷 內(nèi)核時(shí)鐘關(guān),對(duì)其他時(shí)鐘和ADC時(shí)鐘無影響
睡眠模式 內(nèi)核停止,所有外設(shè)包括M3核心的外設(shè),如NVIC、系統(tǒng)時(shí)鐘(SysTick)等仍在運(yùn)行 調(diào)用WFE命令 喚醒事件 內(nèi)核時(shí)鐘關(guān),對(duì)其他時(shí)鐘和ADC時(shí)鐘無影響
停止模式 所有的時(shí)鐘都已停止 配置PWR_CR寄存器的PDDS+LPDS位+SLEEPDEEP位+WFIWFE命令 任意外部中斷EXTI(在外部中斷寄存器中設(shè)置) 關(guān)閉所有1.8V區(qū)域的時(shí)鐘 HSI和HSE的振蕩器關(guān)閉 開啟或處于低功耗模式(依據(jù)電源控制寄存器的設(shè)定)
待機(jī)模式 1.8V電源關(guān)閉 配置PWR_CR寄存器的PDDS+SLEEPDEEP位+WFIWFE命令 WKUP上升沿、引腳的RTC鬧鐘事件、NRST引腳上的外部復(fù)位、IWDG復(fù)位 關(guān)閉所有1.8V區(qū)域的時(shí)鐘 HSI和HSE的振蕩器關(guān)閉 關(guān)

1.1 睡眠模式

在睡眠模式中,僅關(guān)閉了內(nèi)核時(shí)鐘,內(nèi)核停止運(yùn)行,但其片上外設(shè),CM3 核心的外設(shè)全都還照常運(yùn)行。有兩種方式進(jìn)入睡眠模式,它的進(jìn)入方式?jīng)Q定了從睡眠喚醒的方式,分別是 WFI(wait for interrupt)WFE(wait for event),即由等待“中斷”喚醒和由“事件”喚醒。

特性和說明:

  • 立即睡眠: 在執(zhí)行 WFIWFE 指令時(shí)立即進(jìn)入睡眠模式。
  • 退出時(shí)睡眠: 在退出優(yōu)先級(jí)最低的中斷服務(wù)程序后才進(jìn)入睡眠模式。
  • 進(jìn)入方式: 內(nèi)核寄存器的 SLEEPDEEP=0 ,然后調(diào)用 WFIWFE 指令即可進(jìn)入睡眠模式;SLEEPONEXIT=1 時(shí),進(jìn)入“退出時(shí)睡眠”模式。
  • 喚醒方式: 如果是使用 WFI 指令睡眠的,則可使用任意中斷喚醒;如果是使用 WFE 指令睡眠的,則由事件喚醒。
  • 睡眠時(shí): 關(guān)閉內(nèi)核時(shí)鐘,內(nèi)核停止,而外設(shè)正常運(yùn)行,在軟件上表現(xiàn)為不再執(zhí)行新的代碼。這個(gè)狀態(tài)會(huì)保留睡眠前的內(nèi)核寄存器、內(nèi)存的數(shù)據(jù)。
  • 喚醒延遲: 無延遲。
  • 喚醒后: 若由中斷喚醒,先進(jìn)入中斷,退出中斷服務(wù)程序后,接著執(zhí)行 WFI 指令后的程序;若由事件喚醒,直接接著執(zhí)行 WFE 后的程序。

1.2 停止模式

在停止模式中,進(jìn)一步關(guān)閉了其它所有的時(shí)鐘,于是所有的外設(shè)都停止了工作,但由于其 1.8V 區(qū)域的部分電源沒有關(guān)閉,還保留了內(nèi)核的寄存器、內(nèi)存的信息,所以從停止模式喚醒,并重新開啟時(shí)鐘后,還可以從上次停止處繼續(xù)執(zhí)行代碼。停止模式可以由任意一個(gè)外部中斷(EXTI)喚醒,在停止模式中可以選擇電壓調(diào)節(jié)器為開模式或低功耗模式。

特性和說明:

  • 調(diào)壓器低功耗模式: 在停止模式下調(diào)壓器可工作在正常模式或低功耗模式,可進(jìn)一步降低功耗。
  • 進(jìn)入方式: 內(nèi)核寄存器的 SLEEPDEEP=1,PWR_CR 寄存器中的 PDDS=0,然后調(diào)用 WFIWFE 指令即可進(jìn)入停止模式;PWR_CR 寄存器的 LPDS=0 時(shí),調(diào)壓器工作在正常模式,LPDS=1 時(shí)工作在低功耗模式。
  • 喚醒方式: 如果是使用 WFI 指令睡眠的,可使用任意 EXTI 線的中斷喚醒;如果是使用 WFE 指令睡眠的,可使用任意配置為事件模式的 EXTI 線事件喚醒。
  • 停止時(shí): 內(nèi)核停止,片上外設(shè)也停止。這個(gè)狀態(tài)會(huì)保留停止前的內(nèi)核寄存器、內(nèi)存的數(shù)據(jù)。
  • 喚醒延遲: 基礎(chǔ)延遲為 HSI 振蕩器的啟動(dòng)時(shí)間,若調(diào)壓器工作在低功耗模式,還需要加上調(diào)壓器從低功耗切換至正常模式下的時(shí)間。
  • 喚醒后: 若由中斷喚醒,先進(jìn)入中斷,退出中斷服務(wù)程序后,接著執(zhí)行 WFI 指令后的程序;若由事件喚醒,直接接著執(zhí)行 WFE 后的程序。喚醒后,STM32 會(huì)使用 HSI 作為系統(tǒng)時(shí)鐘。

1.3 待機(jī)模式

待機(jī)模式,它除了關(guān)閉所有的時(shí)鐘,還把 1.8V 區(qū)域的電源也完全關(guān)閉了,也就是說,從待機(jī)模式喚醒后,由于沒有之前代碼的運(yùn)行記錄,只能對(duì)芯片復(fù)位,重新檢測(cè) boot 條件,從頭開始執(zhí)行程序。它有四種喚醒方式,分別是 WKUP(PA0)引腳的上升沿,RTC 鬧鐘事件,NRST 引腳的復(fù)位和 IWDG(獨(dú)立看門狗)復(fù)位。

特性和說明:

  • 進(jìn)入方式: 內(nèi)核寄存器的 SLEEPDEEP=1,PWR_CR 寄存器中的 PDDS=1,PWR_CR 寄存器中的喚醒狀態(tài)位 WUF=0,然后調(diào)用 WFIWFE 指令即可進(jìn)入待機(jī)模式。
  • 喚醒方式: 通過 WKUP 引腳的上升沿,RTC 鬧鐘、喚醒、入侵、時(shí)間戳事件或 NRST 引腳外部復(fù)位及 IWDG 復(fù)位喚醒。
  • 待機(jī)時(shí): 內(nèi)核停止,片上外設(shè)也停止;內(nèi)核寄存器、內(nèi)存的數(shù)據(jù)會(huì)丟失;除復(fù)位引腳、RTC_AF1 引腳及 WKUP 引腳,其它 I/O 口均工作在高阻態(tài)。
  • 喚醒延遲: 芯片復(fù)位的時(shí)間。
  • 喚醒后: 相當(dāng)于芯片復(fù)位,在程序表現(xiàn)為從頭開始執(zhí)行代碼。

1.4 WFI與WFE命令

我們了解到進(jìn)入各種低功耗模式時(shí)都需要調(diào)用 WFIWFE 命令,它們實(shí)質(zhì)上都是內(nèi)核指令,在庫文件 core_cm3.h 中把這些指令封裝成了函數(shù)。

/** brief 等待中斷
  等待中斷 是一個(gè)暫停執(zhí)行指令
  暫停至任意中斷產(chǎn)生后被喚醒
*/
#define __WFI        __wfi 

/** brief 等待事件
  等待事件 是一個(gè)暫停執(zhí)行指令
  暫停至任意事件產(chǎn)生后被喚醒
*/
#define __WFE        __wfe

對(duì)于這兩個(gè)指令,我們應(yīng)用時(shí)一般只需要知道,調(diào)用它們都能進(jìn)入低功耗模式,需要使用函數(shù)的格式“__WFI();”和“__WFE();”來調(diào)用(因?yàn)開_wfi 及__wfe 是編譯器內(nèi)置的函數(shù),函數(shù)內(nèi)部調(diào)用了相應(yīng)的匯編指令)。

其中 WFI 指令決定了它需要用中斷喚醒,而 WFE 則決定了它可用事件來喚醒。

二、新建工程

1. 打開 STM32CubeMX 軟件,點(diǎn)擊“新建工程”

2. 選擇 MCU 和封裝

3. 配置時(shí)鐘
RCC 設(shè)置,選擇 HSE(外部高速時(shí)鐘) 為 Crystal/Ceramic Resonator(晶振/陶瓷諧振器)
開啟 LSE(外部低速時(shí)鐘) 為 Crystal/Ceramic Resonator(晶振/陶瓷諧振器)


選擇 Clock Configuration,配置系統(tǒng)時(shí)鐘 SYSCLK 為 72MHz
修改 HCLK 的值為 72 后,輸入回車,軟件會(huì)自動(dòng)修改所有配置

4. 配置調(diào)試模式
非常重要的一步,否則會(huì)造成第一次燒錄程序后續(xù)無法識(shí)別調(diào)試器
SYS 設(shè)置,選擇 Debug 為 Serial Wire

三、待機(jī)模式

3.1 WKUP按鍵喚醒

3.1.1 流程圖

3.1.2 HAL庫與標(biāo)準(zhǔn)庫代碼比較

STM32CubeMX 使用 HAL 庫的代碼:

int main(void)
{
    // 檢測(cè)復(fù)位來源
    if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB) == SET) 
    {
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
        printf("\r\n 待機(jī)喚醒復(fù)位 \r\n");
    }
    else 
    {
        printf("\r\n 非待機(jī)喚醒復(fù)位 \r\n");
    }
    ···
    while(1)
    {
        ···
        HAL_Delay(5000);
        /*清除 WU 狀態(tài)位*/
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
        /* 使能 WKUP 引腳的喚醒功能 ,使能 PA0*/
        HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
        /* 進(jìn)入待機(jī)模式 */
        HAL_PWR_EnterSTANDBYMode();
        ···
    }
}

使用 STM32 標(biāo)準(zhǔn)庫的代碼:

int main(void)
{
    /* 使能電源管理單元的時(shí)鐘,必須要使能時(shí)鐘才能進(jìn)入待機(jī)模式 */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    // 檢測(cè)復(fù)位來源
    if(PWR_GetFlagStatus(PWR_FLAG_WU) == SET) 
    {
        printf("\r\n 待機(jī)喚醒復(fù)位 \r\n");
    } 
    else 
    {
        printf("\r\n 非待機(jī)喚醒復(fù)位 \r\n");
    }
    ···
    while(1)
    {
        ···
        Delay(0xFFFF);
        /*清除 WU 狀態(tài)位*/
        PWR_ClearFlag(PWR_FLAG_WU);     
        /* 使能 WKUP 引腳的喚醒功能 ,使能 PA0*/
        PWR_WakeUpPinCmd(ENABLE); 
        /* 進(jìn)入待機(jī)模式 */
        PWR_EnterSTANDBYMode();
        ···
    }
}

3.1.3 添加WKUP按鍵

添加系統(tǒng)喚醒按鍵 PA0 ,以便當(dāng)系統(tǒng)進(jìn)入待機(jī)模式的時(shí)候可以通過按鍵來喚醒。


或者勾選 System Wake-Up

3.1.4 添加LED燈

添加綠燈 PB0 表示本次復(fù)位是上電或引腳復(fù)位,藍(lán)燈 PB1 表示本次是待機(jī)喚醒的復(fù)位。
查看 STM32CubeMX學(xué)習(xí)筆記(2)——GPIO接口使用

3.1.5 添加串口打印

添加 USART1 用于打印信息。
查看 STM32CubeMX學(xué)習(xí)筆記(6)——USART串口使用

3.1.6 生成代碼

輸入項(xiàng)目名和項(xiàng)目路徑


選擇應(yīng)用的 IDE 開發(fā)環(huán)境 MDK-ARM V5

每個(gè)外設(shè)生成獨(dú)立的 ’.c/.h’ 文件
不勾:所有初始化代碼都生成在 main.c
勾選:初始化代碼生成在對(duì)應(yīng)的外設(shè)文件。 如 GPIO 初始化代碼生成在 gpio.c 中。

點(diǎn)擊 GENERATE CODE 生成代碼

3.1.7 修改main函數(shù)

程序中首先初始化了系統(tǒng)時(shí)鐘、LED 燈及串口以便用于指示芯片的運(yùn)行狀態(tài),由于待機(jī)模式喚醒使用 WKUP 引腳并不需要特別的引腳初始化。

使用庫函數(shù) __HAL_PWR_GET_FLAG 檢測(cè) PWR_FLAG_SB 標(biāo)志位,當(dāng)這個(gè)標(biāo)志位為 SET 狀態(tài)的時(shí)候,表示本次系統(tǒng)是從待機(jī)模式喚醒的復(fù)位,否則可能是上電復(fù)位。
我們利用這個(gè)區(qū)分兩種復(fù)位形式,分別使用藍(lán)色 LED 燈或綠色 LED 燈來指示。

在使用庫函數(shù) HAL_PWR_EnableWakeUpPin 發(fā)送待機(jī)命令前,要先使用庫函數(shù) __HAL_PWR_CLEAR_FLAG 清除 PWR_FLAG_WU 標(biāo)志位,并且使用庫函數(shù) HAL_PWR_EnableWakeUpPin 使能 WKUP 喚醒功能,這樣進(jìn)入待機(jī)模式后才能使用 WKUP 喚醒。

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  printf("standby mode test\r\n");

  if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB) == SET) 
  {
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
    printf("\r\n standby reset \r\n");
    HAL_GPIO_WritePin(GPIOB, LED_B_Pin, GPIO_PIN_RESET);
  }
  else 
  {
    printf("\r\n normal reset \r\n");
    HAL_GPIO_WritePin(GPIOB, LED_G_Pin, GPIO_PIN_RESET);
  }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_Delay(5000);
    /*清除 WU 狀態(tài)位*/
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    /* 使能 WKUP 引腳的喚醒功能 ,使能 PA0*/
    HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
    /* 進(jìn)入待機(jī)模式 */
    HAL_PWR_EnterSTANDBYMode();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

3.2 RTC時(shí)鐘喚醒

3.2.1 添加RTC時(shí)鐘

查看 STM32CubeMX學(xué)習(xí)筆記(14)——RTC實(shí)時(shí)時(shí)鐘使用

3.1.2 添加LED燈

添加綠燈 PB0 表示本次復(fù)位是上電或引腳復(fù)位,藍(lán)燈 PB1 表示本次是待機(jī)喚醒的復(fù)位。
查看 STM32CubeMX學(xué)習(xí)筆記(2)——GPIO接口使用

3.1.3 添加串口打印

添加 USART1 用于打印信息。
查看 STM32CubeMX學(xué)習(xí)筆記(6)——USART串口使用

3.2.4 使能RTC鬧鐘中斷

3.2.5 生成代碼

輸入項(xiàng)目名和項(xiàng)目路徑


選擇應(yīng)用的 IDE 開發(fā)環(huán)境 MDK-ARM V5

每個(gè)外設(shè)生成獨(dú)立的 ’.c/.h’ 文件
不勾:所有初始化代碼都生成在 main.c
勾選:初始化代碼生成在對(duì)應(yīng)的外設(shè)文件。 如 GPIO 初始化代碼生成在 gpio.c 中。

點(diǎn)擊 GENERATE CODE 生成代碼

3.2.6 添加RTC鬧鐘中斷啟動(dòng)函數(shù)

void RTC_AlarmStart(void)
{
    RTC_AlarmTypeDef sAlarm = {0};
    RTC_TimeTypeDef tim = {0};

    // 獲取當(dāng)前時(shí)間
    HAL_RTC_GetTime(&hrtc, &tim, RTC_FORMAT_BIN);

    sAlarm.AlarmTime.Hours = tim.Hours;
    sAlarm.AlarmTime.Minutes = tim.Minutes;
    sAlarm.AlarmTime.Seconds = tim.Seconds + 3;  /* 設(shè)置下次鬧鐘提醒時(shí)間是當(dāng)前時(shí)間的3s之后 */
    sAlarm.Alarm = RTC_ALARM_A;

    // 啟動(dòng)鬧鐘中斷事件
    HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);
}

3.2.7 修改main函數(shù)

程序中首先初始化了系統(tǒng)時(shí)鐘、LED 燈及串口以便用于指示芯片的運(yùn)行狀態(tài),由于待機(jī)模式喚醒使用 WKUP 引腳并不需要特別的引腳初始化。

使用庫函數(shù) __HAL_PWR_GET_FLAG 檢測(cè) PWR_FLAG_SB 標(biāo)志位,當(dāng)這個(gè)標(biāo)志位為 SET 狀態(tài)的時(shí)候,表示本次系統(tǒng)是從待機(jī)模式喚醒的復(fù)位,否則可能是上電復(fù)位。
我們利用這個(gè)區(qū)分兩種復(fù)位形式,分別使用藍(lán)色 LED 燈或綠色 LED 燈來指示。

在使用庫函數(shù) HAL_PWR_EnableWakeUpPin 發(fā)送待機(jī)命令前,要先使用庫函數(shù) __HAL_PWR_CLEAR_FLAG 清除 PWR_FLAG_WU 標(biāo)志位,并且使用庫函數(shù) HAL_PWR_EnableWakeUpPin 使能 WKUP 喚醒功能,這樣進(jìn)入待機(jī)模式后才能使用 WKUP 喚醒。

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  printf("standby mode test\r\n");

  if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB) == SET) 
  {
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
    printf("\r\n standby reset \r\n");
    HAL_GPIO_WritePin(GPIOB, LED_B_Pin, GPIO_PIN_RESET);
  }
  else 
  {
    printf("\r\n normal reset \r\n");
    HAL_GPIO_WritePin(GPIOB, LED_G_Pin, GPIO_PIN_RESET);
  }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_Delay(5000);
    RTC_AlarmStart();
    /*清除 WU 狀態(tài)位*/
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    /* 使能 WKUP 引腳的喚醒功能 ,使能 PA0*/
    HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
    /* 進(jìn)入待機(jī)模式 */
    HAL_PWR_EnterSTANDBYMode();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

四、注意事項(xiàng)

用戶代碼要加在 USER CODE BEGIN NUSER CODE END N 之間,否則下次使用 STM32CubeMX 重新生成代碼后,會(huì)被刪除。

進(jìn)入低功耗之前可以將引腳全部配置為浮空輸入或者Anglog模式,這樣最省電,如果你是用STM32CUBEMX,在這里可以看到這么一項(xiàng)配置就是將沒有用到的引腳配置為了Anglog模式:

當(dāng)系統(tǒng)處于睡眠模式低功耗狀態(tài)時(shí)(包括后面講解的停止模式及待機(jī)模式),使用 DAP 下載器是無法給芯片下載程序的,所以下載程序時(shí)要先把系統(tǒng)喚醒。或者使用如下方法:按著板子的復(fù)位按鍵,使系統(tǒng)處于復(fù)位狀態(tài),然后點(diǎn)擊電腦端的下載按鈕下載程序,這時(shí)再釋放復(fù)位按鍵,就能正常給板子下載程序了。


? 由 Leung 寫于 2021 年 3 月 11 日

? 參考:STM32CubeMX系列教程14:電源控制器(PWR)
    STM32MX電源管理低功耗模式
    STM32F1系列使用HAL庫低功耗STOP和STANDBY模式喚醒(RTC時(shí)鐘喚醒+外部中斷喚醒示例)
    《嵌入式-STM32開發(fā)指南》第二部分 基礎(chǔ)篇 - 第10章 低功耗(HAL庫)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容