一、低功耗模式簡介
系統提供了多個低功耗模式,可在 CPU 不需要運作時(例如等待外部事件時)節省功耗。由使用者根據應用選擇具體的低功耗模式,以在低功耗、短啟動時間和可用喚醒源之間尋求最佳平衡。
睡眠模式、停止模式及待機模式中,若備份域電源正常供電,備份域内的 RTC 都可以正常運作,備份域内的寄存器的資料會被儲存,不受功耗模式影響。
從表中可以看到,這三種低功耗模式層層遞進,運作的時鐘或晶片功能越來越少,因而功耗越來越低。
模式名稱 | 說明 | 進入方式 | 喚醒方式 | 對1.8V區域時鐘的影響 | 對VDD區域時鐘的影響 | 調壓器 |
---|---|---|---|---|---|---|
睡眠模式 | 核心停止,所有外設包括M3核心的外設,如NVIC、系統時鐘(SysTick)等仍在運作 | 調用 指令 | 任意中斷 | 核心時鐘關,對其他時鐘和ADC時鐘無影響 | 無 | 開 |
睡眠模式 | 核心停止,所有外設包括M3核心的外設,如NVIC、系統時鐘(SysTick)等仍在運作 | 調用 指令 | 喚醒事件 | 核心時鐘關,對其他時鐘和ADC時鐘無影響 | 無 | 開 |
停止模式 | 所有的時鐘都已停止 | 配置PWR_CR寄存器的 + 位+ 位+ 或 指令 | 任意外部中斷 (在外部中斷寄存器中設定) | 關閉所有1.8V區域的時鐘 | HSI和HSE的振蕩器關閉 | 開啟或處于低功耗模式(依據電源控制寄存器的設定) |
待機模式 | 1.8V電源關閉 | 配置PWR_CR寄存器的 + 位+ 或 指令 | WKUP上升沿、引腳的RTC鬧鐘事件、NRST引腳上的外部複位、IWDG複位 | 關閉所有1.8V區域的時鐘 | HSI和HSE的振蕩器關閉 | 關 |
1.1 睡眠模式
在睡眠模式中,僅關閉了核心時鐘,核心停止運作,但其片上外設,CM3 核心的外設全都還照常運作。有兩種方式進入睡眠模式,它的進入方式決定了從睡眠喚醒的方式,分别是 WFI(wait for interrupt) 和 WFE(wait for event),即由等待“中斷”喚醒和由“事件”喚醒。
特性和說明:
- 立即睡眠: 在執行
或
WFI
指令時立即進入睡眠模式。
WFE
- 退出時睡眠: 在退出優先級最低的中斷服務程式後才進入睡眠模式。
- 進入方式: 核心寄存器的
,然後調用
SLEEPDEEP=0
或
WFI
指令即可進入睡眠模式;
WFE
時,進入“退出時睡眠”模式。
SLEEPONEXIT=1
- 喚醒方式: 如果是使用
指令睡眠的,則可使用任意中斷喚醒;如果是使用
WFI
指令睡眠的,則由事件喚醒。
WFE
- 睡眠時: 關閉核心時鐘,核心停止,而外設正常運作,在軟體上表現為不再執行新的代碼。這個狀态會保留睡眠前的核心寄存器、記憶體的資料。
- 喚醒延遲: 無延遲。
- 喚醒後: 若由中斷喚醒,先進入中斷,退出中斷服務程式後,接着執行
指令後的程式;若由事件喚醒,直接接着執行
WFI
後的程式。
WFE
1.2 停止模式
在停止模式中,進一步關閉了其它所有的時鐘,于是所有的外設都停止了工作,但由于其 1.8V 區域的部分電源沒有關閉,還保留了核心的寄存器、記憶體的資訊,是以從停止模式喚醒,并重新開啟時鐘後,還可以從上次停止處繼續執行代碼。停止模式可以由任意一個外部中斷(EXTI)喚醒,在停止模式中可以選擇電壓調節器為開模式或低功耗模式。
特性和說明:
- 調壓器低功耗模式: 在停止模式下調壓器可工作在正常模式或低功耗模式,可進一步降低功耗。
- 進入方式: 核心寄存器的
,PWR_CR 寄存器中的
SLEEPDEEP=1
,然後調用
PDDS=0
或
WFI
指令即可進入停止模式;PWR_CR 寄存器的
WFE
時,調壓器工作在正常模式,
LPDS=0
時工作在低功耗模式。
LPDS=1
- 喚醒方式: 如果是使用
指令睡眠的,可使用任意 EXTI 線的中斷喚醒;如果是使用
WFI
指令睡眠的,可使用任意配置為事件模式的 EXTI 線事件喚醒。
WFE
- 停止時: 核心停止,片上外設也停止。這個狀态會保留停止前的核心寄存器、記憶體的資料。
- 喚醒延遲: 基礎延遲為 HSI 振蕩器的啟動時間,若調壓器工作在低功耗模式,還需要加上調壓器從低功耗切換至正常模式下的時間。
- 喚醒後: 若由中斷喚醒,先進入中斷,退出中斷服務程式後,接着執行
指令後的程式;若由事件喚醒,直接接着執行
WFI
後的程式。喚醒後,STM32 會使用 HSI 作為系統時鐘。
WFE
1.3 待機模式
待機模式,它除了關閉所有的時鐘,還把 1.8V 區域的電源也完全關閉了,也就是說,從待機模式喚醒後,由于沒有之前代碼的運作記錄,隻能對晶片複位,重新檢測 boot 條件,從頭開始執行程式。它有四種喚醒方式,分别是 WKUP(PA0)引腳的上升沿,RTC 鬧鐘事件,NRST 引腳的複位和 IWDG(獨立看門狗)複位。
特性和說明:
- 進入方式: 核心寄存器的
,PWR_CR 寄存器中的
SLEEPDEEP=1
,PWR_CR 寄存器中的喚醒狀态位
PDDS=1
,然後調用
WUF=0
或
WFI
指令即可進入待機模式。
WFE
- 喚醒方式: 通過 WKUP 引腳的上升沿,RTC 鬧鐘、喚醒、入侵、時間戳事件或 NRST 引腳外部複位及 IWDG 複位喚醒。
- 待機時: 核心停止,片上外設也停止;核心寄存器、記憶體的資料會丢失;除複位引腳、RTC_AF1 引腳及 WKUP 引腳,其它 I/O 口均工作在高阻态。
- 喚醒延遲: 晶片複位的時間。
- 喚醒後: 相當于晶片複位,在程式表現為從頭開始執行代碼。
1.4 WFI與WFE指令
我們了解到進入各種低功耗模式時都需要調用
WFI
或
WFE
指令,它們實質上都是核心指令,在庫檔案
core_cm3.h
中把這些指令封裝成了函數。
/** brief 等待中斷
等待中斷 是一個暫停執行指令
暫停至任意中斷産生後被喚醒
*/
#define __WFI __wfi
/** brief 等待事件
等待事件 是一個暫停執行指令
暫停至任意事件産生後被喚醒
*/
#define __WFE __wfe
對于這兩個指令,我們應用時一般隻需要知道,調用它們都能進入低功耗模式,需要使用函數的格式“__WFI();”和“__WFE();”來調用(因為__wfi 及__wfe 是編譯器内置的函數,函數内部調用了相應的彙編指令)。
其中指令決定了它需要用中斷喚醒,而
WFI
則決定了它可用事件來喚醒。
WFE
二、建立工程
1. 打開 STM32CubeMX 軟體,點選“建立工程”
2. 選擇 MCU 和封裝
3. 配置時鐘
RCC 設定,選擇 HSE(外部高速時鐘) 為 Crystal/Ceramic Resonator(晶振/陶瓷諧振器)
開啟 LSE(外部低速時鐘) 為 Crystal/Ceramic Resonator(晶振/陶瓷諧振器)
選擇 Clock Configuration,配置系統時鐘 SYSCLK 為 72MHz
修改 HCLK 的值為 72 後,輸入回車,軟體會自動修改所有配置
4. 配置調試模式
非常重要的一步,否則會造成第一次燒錄程式後續無法識别調試器
SYS 設定,選擇 Debug 為 Serial Wire
三、停止模式
3.1 WFI按鍵外部中斷喚醒
3.1.1 流程圖
3.1.2 HAL庫與标準庫代碼比較
STM32CubeMX 使用 HAL 庫的代碼:
int main(void)
{
···
while(1)
{
···
// 暫停滴答時鐘,防止通過滴答時鐘中斷喚醒
HAL_SuspendTick();
/* 進入停止模式,設定電壓調節器為低功耗模式,等待中斷喚醒 */
HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON,PWR_STOPENTRY_WFI);
SYSCLKConfig_STOP();
// 被喚醒後,恢複滴答時鐘
HAL_ResumeTick();
···
}
}
static void SYSCLKConfig_STOP(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
uint32_t pFLatency = 0;
/* 啟用電源控制時鐘 */
__HAL_RCC_PWR_CLK_ENABLE();
/* 根據内部 RCC 寄存器擷取振蕩器配置 */
HAL_RCC_GetOscConfig(&RCC_OscInitStruct);
/* 從停止模式喚醒後重新配置系統時鐘: 啟用 HSE 和 PLL */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
while (1)
{;
}
}
/* 根據内部 RCC 寄存器擷取時鐘配置 */
HAL_RCC_GetClockConfig(&RCC_ClkInitStruct, &pFLatency);
/* 選擇 PLL 作為系統時鐘源, 并配置 HCLK、PCLK1 和 PCLK2 時鐘分頻系數 */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, pFLatency) != HAL_OK)
{
while (1)
{;
}
}
}
使用 STM32 标準庫的代碼:
int main(void)
{
···
while(1)
{
···
/* 進入停止模式,設定電壓調節器為低功耗模式,等待中斷喚醒 */
PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI);
// 從停止模式下被喚醒後使用的是 HSI 時鐘,此處重新開機 HSE 時鐘,使用 PLLCLK
SYSCLKConfig_STOP();
···
}
}
static void SYSCLKConfig_STOP(void)
{
/* 停機喚醒後配置系統時鐘 */
/* 使能 HSE */
RCC_HSEConfig(RCC_HSE_ON);
/* 等待 HSE 準備就緒 */
while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
/* 使能 PLL */
RCC_PLLCmd(ENABLE);
/* 等待 PLL 準備就緒 */
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
/* 選擇 PLL 作為系統時鐘源 */
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
/* 等待 PLL 被選擇為系統時鐘源 */
while (RCC_GetSYSCLKSource() != 0x08);
}
3.1.3 添加按鍵
初始化按鍵
PA0
中斷模式,以便當系統進入睡眠模式的時候可以通過按鍵來喚醒。
檢視 STM32CubeMX學習筆記(3)——EXTI(外部中斷)接口使用
3.1.4 添加LED燈
添加綠燈
PB0
表示運作狀态,紅燈
PB5
表示睡眠狀态,藍燈
PB1
表示剛從睡眠狀态中被喚醒。
檢視 STM32CubeMX學習筆記(2)——GPIO接口使用
3.1.5 添加序列槽列印
添加
USART1
用于列印資訊。
檢視 STM32CubeMX學習筆記(6)——USART序列槽使用
3.1.6 生成代碼
輸入項目名和項目路徑
選擇應用的 IDE 開發環境 MDK-ARM V5
每個外設生成獨立的
’.c/.h’
檔案
不勾:所有初始化代碼都生成在 main.c
勾選:初始化代碼生成在對應的外設檔案。 如 GPIO 初始化代碼生成在 gpio.c 中。
點選 GENERATE CODE 生成代碼
3.1.7 修改中斷回調函數
當系統進入停止狀态後,我們按下實驗闆上的 KEY1 按鍵,即可使系統回到正常運作的狀态,當執行完中斷服務函數後,會繼續執行 WFI
指令後的代碼。
打開
stm32f1xx_it.c
中斷服務函數檔案,找到 EXTI0 中斷的服務函數
EXTI0_IRQHandler()
中斷服務函數裡面就調用了 GPIO 外部中斷處理函數
HAL_GPIO_EXTI_IRQHandler()
打開
stm32f1xx_hal_gpio.c
檔案,找到外部中斷處理函數原型
HAL_GPIO_EXTI_IRQHandler()
,其主要作用就是判斷是幾号線中斷,清除中斷辨別位,然後調用中斷回調函數
HAL_GPIO_EXTI_Callback()
。
這個函數不應該被改變,如果需要使用回調函數,請重新在使用者檔案中實作該函數。
HAL_GPIO_EXTI_Callback()
按照官方提示我們應該再次定義該函數,
__weak
是一個弱化辨別,帶有這個的函數就是一個弱化函數,就是你可以在其他地方寫一個名稱和參數都一模一樣的函數,編譯器就會忽略這一個函數,而去執行你寫的那個函數;而
UNUSED(GPIO_Pin)
,這就是一個防報錯的定義,當傳進來的GPIO端口号沒有做任何處理的時候,編譯器也不會報出警告。其實我們在開發的時候已經不需要去理會中斷服務函數了,隻需要找到這個中斷回調函數并将其重寫即可而這個回調函數還有一點非常便利的地方這裡沒有展現出來,就是當同時有多個中斷使能的時候,STM32CubeMX會自動地将幾個中斷的服務函數規整到一起并調用一個回調函數,也就是無論幾個中斷,我們隻需要重寫一個回調函并判斷傳進來的端口号即可。
接下來我們就在
stm32f1xx_it.c
這個檔案的最下面添加
HAL_GPIO_EXTI_Callback()
/* USER CODE BEGIN 1 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
// 亮藍燈
HAL_GPIO_WritePin(GPIOB, LED_B_Pin, GPIO_PIN_RESET);
}
/* USER CODE END 1 */
3.1.8 修改main函數
初始化完成後使用 LED 及序列槽表示運作狀态,LED 燈為綠色時表示正常運作,紅燈時表示停止狀态,藍燈時表示剛從停止狀态中被喚醒。在停止模式下,I/O 口會保持停止前的狀态,是以 LED 彩燈在停止模式時也會保持亮紅燈。
程式執行一段時間後,調用庫函數
把調壓器設定在低功耗模式,進入停止狀态。由于
HAL_PWR_EnterSTOPMode
WFI
停止模式可以使用任意 EXTI 的中斷喚醒,是以我們可以使用按鍵中斷喚醒。
當系統進入停止狀态後,我們按下實驗闆上的 KEY1 按鍵,即可喚醒系統,當執行完中斷服務函數後,會繼續執行
函數後的代碼。
HAL_PWR_EnterSTOPMode
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint32_t SYSCLK_Frequency = 0;
uint32_t HCLK_Frequency = 0;
uint32_t PCLK1_Frequency = 0;
uint32_t PCLK2_Frequency = 0;
uint32_t SYSCLK_Source = 0;
/* 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("stop mode test\r\n");
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// 使用綠燈訓示,運作狀态
HAL_GPIO_WritePin(GPIOB, LED_G_Pin, GPIO_PIN_RESET);
HAL_Delay(2000);
HAL_GPIO_WritePin(GPIOB, LED_G_Pin, GPIO_PIN_SET);
// 任務執行完畢,進入睡眠降低功耗
// 使用紅燈訓示,進入停止狀态
HAL_GPIO_WritePin(GPIOB, LED_R_Pin, GPIO_PIN_RESET);
// 暫停滴答時鐘,防止通過滴答時鐘中斷喚醒
HAL_SuspendTick();
// 使能PWR時鐘
__HAL_RCC_PWR_CLK_ENABLE();
// 清除喚醒标記
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
// 進入停止模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
HAL_GPIO_WritePin(GPIOB, LED_R_Pin, GPIO_PIN_SET);
// 剛從STOP模式喚醒時鐘預設使用内部高速8M時鐘,是以需要重新配置時鐘
SystemClock_Config();
// 被喚醒後,恢複滴答時鐘
HAL_ResumeTick();
// 擷取重新配置後的時鐘狀态
SYSCLK_Frequency = HAL_RCC_GetSysClockFreq();
HCLK_Frequency = HAL_RCC_GetHCLKFreq();
PCLK1_Frequency = HAL_RCC_GetPCLK1Freq();
PCLK2_Frequency = HAL_RCC_GetPCLK2Freq();
SYSCLK_Source = __HAL_RCC_GET_SYSCLK_SOURCE();
// 重新配置時鐘源後始終狀态
printf("\r\n 重新配置後的時鐘狀态:\r\n");
printf(" SYSCLK 頻率:%d,\r\n HCLK 頻率:%d,\r\n PCLK1 頻率:%d,\r\n PCLK2 頻率:%d,\r\n 時鐘源:%d (0 表示 HSI,8 表示 PLLCLK)\n",SYSCLK_Frequency,HCLK_Frequency,PCLK1_Frequency,PCLK2_Frequency,SYSCLK_Source);
HAL_Delay(2000);
HAL_GPIO_WritePin(GPIOB, LED_B_Pin, GPIO_PIN_SET);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
3.2 RTC時鐘喚醒
3.2.1 添加RTC時鐘
檢視 STM32CubeMX學習筆記(14)——RTC實時時鐘使用
3.1.2 添加LED燈
添加綠燈
PB0
表示運作狀态,紅燈
PB5
表示睡眠狀态,藍燈
PB1
表示剛從睡眠狀态中被喚醒。
檢視 STM32CubeMX學習筆記(2)——GPIO接口使用
3.1.3 添加序列槽列印
添加
USART1
用于列印資訊。
檢視 STM32CubeMX學習筆記(6)——USART序列槽使用
3.2.4 使能RTC鬧鐘中斷
3.2.5 生成代碼
輸入項目名和項目路徑
選擇應用的 IDE 開發環境 MDK-ARM V5
每個外設生成獨立的
’.c/.h’
檔案
不勾:所有初始化代碼都生成在 main.c
勾選:初始化代碼生成在對應的外設檔案。 如 GPIO 初始化代碼生成在 gpio.c 中。
點選 GENERATE CODE 生成代碼
3.2.6 修改中斷回調函數
打開
stm32f1xx_it.c
中斷服務函數檔案,找到 RTC 鬧鐘中斷的服務函數
RTC_Alarm_IRQHandler()
中斷服務函數裡面就調用了 RTC 鬧鐘中斷處理函數
HAL_RTC_AlarmIRQHandler()
打開
stm32f1xx_hal_rtc.c
檔案,找到RTC鬧鐘中斷處理函數原型
HAL_RTC_AlarmIRQHandler()
,其主要作用就是判斷是否RTC中斷,清除中斷辨別位,然後調用中斷回調函數
HAL_RTC_AlarmAEventCallback()
。
這個函數不應該被改變,如果需要使用回調函數,請重新在使用者檔案中實作該函數。
HAL_RTC_AlarmAEventCallback()
按照官方提示我們應該再次定義該函數,
__weak
是一個弱化辨別,帶有這個的函數就是一個弱化函數,就是你可以在其他地方寫一個名稱和參數都一模一樣的函數,編譯器就會忽略這一個函數,而去執行你寫的那個函數;而
UNUSED(hrtc)
,這就是一個防報錯的定義,當傳進來的RTC号沒有做任何處理的時候,編譯器也不會報出警告。其實我們在開發的時候已經不需要去理會中斷服務函數了,隻需要找到這個中斷回調函數并将其重寫即可而這個回調函數還有一點非常便利的地方這裡沒有展現出來,就是當同時有多個中斷使能的時候,STM32CubeMX會自動地将幾個中斷的服務函數規整到一起并調用一個回調函數,也就是無論幾個中斷,我們隻需要重寫一個回調函并判斷傳進來的端口号即可。
接下來我們就在
stm32f1xx_it.c
這個檔案的最下面添加
HAL_RTC_AlarmAEventCallback()
/* USER CODE BEGIN 1 */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
SystemClock_Config(); // STOP模式喚醒後預設時鐘主頻為内部8M時鐘,是以要先初始化時鐘配置
// 亮藍燈
HAL_GPIO_WritePin(GPIOB, LED_B_Pin, GPIO_PIN_RESET);
printf("3s時間到,喚醒!\r\n");
}
/* USER CODE END 1 */
3.2.7 添加RTC鬧鐘中斷啟動函數
void RTC_AlarmStart(void)
{
RTC_AlarmTypeDef sAlarm = {0};
RTC_TimeTypeDef tim = {0};
// 擷取目前時間
HAL_RTC_GetTime(&hrtc, &tim, RTC_FORMAT_BIN);
sAlarm.AlarmTime.Hours = tim.Hours;
sAlarm.AlarmTime.Minutes = tim.Minutes;
sAlarm.AlarmTime.Seconds = tim.Seconds + 3; /* 設定下次鬧鐘提醒時間是目前時間的3s之後 */
sAlarm.Alarm = RTC_ALARM_A;
// 啟動鬧鐘中斷事件
HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);
}
3.2.8 修改main函數
初始化完成後使用 LED 及序列槽表示運作狀态,LED 燈為綠色時表示正常運作,紅燈時表示停止狀态,藍燈時表示剛從停止狀态中被喚醒。在停止模式下,I/O 口會保持停止前的狀态,是以 LED 彩燈在停止模式時也會保持亮紅燈。
程式執行一段時間後,調用庫函數 HAL_PWR_EnterSTOPMode 把調壓器設定在低功耗模式,進入停止狀态。由于 WFI 停止模式可以使用任意 EXTI 的中斷喚醒,是以我們可以使用RTC鬧鐘中斷喚醒。
當系統進入停止狀态後,隔 3 秒後鬧鐘喚醒系統,當執行完中斷服務函數後,會繼續執行 HAL_PWR_EnterSTOPMode 函數後的代碼。
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint32_t SYSCLK_Frequency = 0;
uint32_t HCLK_Frequency = 0;
uint32_t PCLK1_Frequency = 0;
uint32_t PCLK2_Frequency = 0;
uint32_t SYSCLK_Source = 0;
/* 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("stop mode test\r\n");
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// 使用綠燈訓示,運作狀态
HAL_GPIO_WritePin(GPIOB, LED_G_Pin, GPIO_PIN_RESET);
HAL_Delay(2000);
HAL_GPIO_WritePin(GPIOB, LED_G_Pin, GPIO_PIN_SET);
// 任務執行完畢,進入睡眠降低功耗
// 使用紅燈訓示,進入停止狀态
HAL_GPIO_WritePin(GPIOB, LED_R_Pin, GPIO_PIN_RESET);
// 暫停滴答時鐘,防止通過滴答時鐘中斷喚醒
HAL_SuspendTick();
// 配置下次喚醒的鬧鐘時間
RTC_AlarmStart();
// 使能PWR時鐘
__HAL_RCC_PWR_CLK_ENABLE();
// 清除喚醒标記
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
// 進入停止模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
HAL_GPIO_WritePin(GPIOB, LED_R_Pin, GPIO_PIN_SET);
// 被喚醒後,恢複滴答時鐘
HAL_ResumeTick();
// 擷取重新配置後的時鐘狀态
SYSCLK_Frequency = HAL_RCC_GetSysClockFreq();
HCLK_Frequency = HAL_RCC_GetHCLKFreq();
PCLK1_Frequency = HAL_RCC_GetPCLK1Freq();
PCLK2_Frequency = HAL_RCC_GetPCLK2Freq();
SYSCLK_Source = __HAL_RCC_GET_SYSCLK_SOURCE();
// 重新配置時鐘源後始終狀态
printf("\r\n 重新配置後的時鐘狀态:\r\n");
printf(" SYSCLK 頻率:%d,\r\n HCLK 頻率:%d,\r\n PCLK1 頻率:%d,\r\n PCLK2 頻率:%d,\r\n 時鐘源:%d (0 表示 HSI,8 表示 PLLCLK)\n",SYSCLK_Frequency,HCLK_Frequency,PCLK1_Frequency,PCLK2_Frequency,SYSCLK_Source);
HAL_Delay(2000);
HAL_GPIO_WritePin(GPIOB, LED_B_Pin, GPIO_PIN_SET);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
四、注意事項
使用者代碼要加在和
USER CODE BEGIN N
之間,否則下次使用 STM32CubeMX 重新生成代碼後,會被删除。
USER CODE END N
進入低功耗之前可以将引腳全部配置為浮空輸入或者Anglog模式,這樣最省電,如果你是用STM32CUBEMX,在這裡可以看到這麼一項配置就是将沒有用到的引腳配置為了Anglog模式:
當系統處于睡眠模式低功耗狀态時(包括後面講解的停止模式及待機模式),使用 DAP 下載下傳器是無法給晶片下載下傳程式的,是以下載下傳程式時要先把系統喚醒。或者使用如下方法:按着闆子的複位按鍵,使系統處于複位狀态,然後點選電腦端的下載下傳按鈕下載下傳程式,這時再釋放複位按鍵,就能正常給闆子下載下傳程式了。
• 由 Leung 寫于 2021 年 3 月 8 日
• 參考:STM32CubeMX系列教程14:電源控制器(PWR)
STM32MX電源管理低功耗模式
STM32F1系列使用HAL庫低功耗STOP和STANDBY模式喚醒(RTC時鐘喚醒+外部中斷喚醒示例)