参考资料:野火电子F103文档
参考资料:STM32F1XX数据手册(官方)
1. 休眠模式
- 休眠指令
/** brief 等待中断
等待中断 是一个暂停执行指令
暂停至任意中断产生后被唤醒
*/
#define __WFI __wfi
__WFI();//中断唤醒
/** brief 等待事件
等待事件 是一个暂停执行指令
暂停至任意事件产生后被唤醒
*/
#define __WFE __wfe
__WFE();//事件唤醒
- 停止模式
/**
* @brief 进入停止模式
* @note 在停止模式下所有I/O都会保持在停止前的状态
* @note 从停止模式唤醒后,会使用HSI作为时钟源
* @note 调压器若工作在低功耗模式,可减少功耗,但唤醒时会增加延迟
* @param Regulator: 设置停止模式时调压器的工作模式
* @arg PWR_MAINREGULATOR_ON: 调压器正常运行
* @arg PWR_LOWPOWERREGULATOR_ON: 调压器低功耗运行
* @param STOPEntry: 设置使用WFI还是WFE进入停止模式
* @arg PWR_STOPENTRY_WFI: WFI进入停止模式
* @arg PWR_STOPENTRY_WFE: WFE进入停止模式
* @retval None
*/
void HAL_PWR_EnterSTOPMode(uint32_t Regulator, uint8_t STOPEntry)
要注意的是进入停止模式后,STM32的所有I/O都保持在停止前的状态,而当它被唤醒时,STM32使用HSI作为系统时钟(8MHz)运行,由于系统时钟会影响很多外设的工作状态,所以一般我们在唤醒后会重新开启HSE,把系统时钟设置回原来的状态
3. 待机模式
/**
* @brief 进入待机模式
* @note 待机模式时,除了以下引脚,其余引脚都在高阻态:
* - 复位引脚
* - RTC_AF1 引脚 (PC13)(需要使能侵入检测、时间戳事件或RTC闹钟事件)
* - RTC_AF2 引脚 (PI8) (需要使能侵入检测或时间戳事件)
* - WKUP 引脚 (PA0) (需要使能WKUP唤醒功能)
* @retval None
*/
void HAL_PWR_EnterSTANDBYMode(void)
在进入待机模式后,除了被使能了的用于唤醒的I/O,其余I/O都进入高阻态,而从待机模式唤醒后,相当于复位STM32芯片,程序重新从头开始执行
当系统处于停止模式低功耗状态时(包括睡眠模式及待机模式),使用DAP下载器是无法给芯片下载程序的,所以下载程序时要先把系统唤醒
2. 电源电压监测
STM32芯片主要通过引脚VDD从外部获取电源,在它的内部具有电源监控器用于检测VDD的电压,以实现复位功能及掉电紧急处理功能,保证系统可靠地运行
可编程电压检测器PVD
电压阈值与外部供电电压VDD比较,当低于工作阈值时,会直接进入复位状态,这可防止电压不足导致的误操作
STM32还提供了可编程电压检测器PVD,它也是实时检测VDD的电压,当检测到电压低于编程的VPVD阈值时,会向内核产生一个PVD中断(EXTI16线中断)以使内核在复位前进行紧急处理
3. STM32电源系统组成
- ADC独立电源接口
- 调压器供电称为1.8V电源域,包括内核,RAM等
正常运行模式:1.8V 全功率
停止模式:1.8V 时钟关闭,外设停止,保留寄存器和RAM
待机模式: 1.8V 断电,寄存器,RAM丢失 - 备份电源区
给LSE和RTC供电
4.低功耗模式


5. 睡眠模式
内核时钟关闭,内核停止
外设正常运行
6. 停止模式
所有时钟都停止
所有外设都停止
内核寄存器,RAM保存
7. 待机模式
所有时钟关闭
RAM清除
唤醒后重启
8. 停止模式代码备份
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "rtc.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint32_t main_cnt =0;
extern void board_sleep_init(void);
extern void board_enter_sleep(void);
extern void board_exit_sleep(void);
/* USER CODE END 0 */
/**
* @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_RTC_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
//获取重新配置后的时钟状态
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;
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("SYSCLK_Frequency=%d\r\n", SYSCLK_Frequency);
printf("HCLK_Frequency=%d\r\n", HCLK_Frequency);
printf("PCLK1_Frequency=%d\r\n", PCLK1_Frequency);
printf("PCLK2_Frequency=%d\r\n", PCLK2_Frequency);
printf("SYSCLK_Source=%d\r\n", SYSCLK_Source);
HAL_Delay(5000);
board_sleep_init();
//关闭板载指示灯
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_PIN_RESET);
HAL_Delay(2000);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_PIN_SET);
HAL_Delay(2000);
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
printf("sleep main_cnt= %d\r\n", main_cnt);
HAL_Delay(1000);
board_enter_sleep();
HAL_Delay(1000);
printf("wakeup\r\n");
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_4;
RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_RTC;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
main_cnt++;
}
void board_sleep_init(void)
{
// 使能电源接口时钟
__HAL_RCC_PWR_CLK_ENABLE();
// 允许备份区域访问
HAL_PWR_EnableBkUpAccess();
// 禁用可编程电压检测器 (PVD)
HAL_PWR_DisablePVD();
}
void board_enter_sleep(void)
{
//禁用串口
HAL_UART_DeInit(&huart1);
// 禁用 GPIOA 端口时钟
__HAL_RCC_GPIOA_CLK_DISABLE();
// 禁用 GPIOB 端口时钟
__HAL_RCC_GPIOB_CLK_DISABLE();
// 禁用 GPIOC 端口时钟
__HAL_RCC_GPIOC_CLK_DISABLE();
// 禁用 GPIOH 端口时钟
__HAL_RCC_GPIOH_CLK_DISABLE();
// 使能超低功耗模式
HAL_PWREx_EnableUltraLowPower();
// 使能快速唤醒功能
HAL_PWREx_EnableFastWakeUp();
// 禁用调试接口在 STOP 模式下的调试功能
HAL_DBGMCU_DisableDBGStopMode();
// 禁用调试接口在睡眠、停止和待机模式下的低功耗配置
HAL_DBGMCU_DBG_DisableLowPowerConfig(DBGMCU_SLEEP | DBGMCU_STOP | DBGMCU_STANDBY);
// 清除唤醒标志
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
// 注释掉的代码:禁用 RTC 唤醒定时器
// HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
// 注释掉的代码:设置 RTC 唤醒定时器中断,周期为 60 个 RTC 时钟周期
// HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 60, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
// 配置 STOP 模式下的唤醒时钟为 HSI
__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI);
// 进入 STOP 模式,使用低功耗稳压器,并通过等待中断 (WFI) 指令进入 STOP 模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
board_exit_sleep();
}
void board_exit_sleep(void)
{
// 检查唤醒标志是否被设置
if (__HAL_PWR_GET_FLAG(PWR_FLAG_WU) != RESET)
{
// 如果唤醒标志被设置,则清除唤醒标志
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
}
// 检查待机标志是否被设置
if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET)
{
// 如果待机标志被设置,则清除待机标志
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
}
// 配置系统时钟
SystemClock_Config();
// 使能 GPIOA 端口时钟
__GPIOA_CLK_ENABLE();
// 使能 GPIOB 端口时钟
__GPIOB_CLK_ENABLE();
// 使能 GPIOC 端口时钟
__GPIOC_CLK_ENABLE();
// 使能 GPIOH 端口时钟
__GPIOH_CLK_ENABLE();
// 初始化 USART1 UART 接口
MX_USART1_UART_Init();
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
### STM32L051低功耗调试笔记
## 第一步
* 使用的是STM32L051
* 使用的是停止模式
* 系统时钟+RTC中断唤醒, 休眠的时候是1uA的功耗
* 所有IO都设置为模拟输入
* GPIO始终有一个输入通道,可以是数字或模拟通道。
如果不需要读取GPIO数据,则优先配置为模拟输入。
这节省了输入施密特触发器的消耗。
在STM32CubeMX配置中都有这么一个选项:
将不用引脚配置为模拟状态
* 测试RTC唤醒工作的话功耗为6MA
## 第二步
* 唤醒后初始化串口,打印输出日志,每次输出值自增
* 可以看到RAM保持,数据没有被清楚掉
* 功耗为1uA
* 休眠失能串口,唤醒使能串口
## 第三步
测试功耗的时候注意使用3.3 V电源供电,不然功耗
9. PVD电源监控备份
void PVD_Config(void)
{
PWR_PVDTypeDef sConfigPVD;
/*使能 PWR 时钟 */
__PWR_CLK_ENABLE();
/* 配置 PVD 中断 */
/*中断设置,抢占优先级0,子优先级为0*/
HAL_NVIC_SetPriority(PVD_IRQn, 0 ,0);
HAL_NVIC_EnableIRQ(PVD_IRQn);
/* 配置PVD级别5 (PVD检测电压的阈值为2.8V,
VDD电压低于2.8V时产生PVD中断,具体数据
可查询数据手册获知) 具体级别根据自己的
实际应用要求配置*/
sConfigPVD.PVDLevel = PWR_PVDLEVEL_5;
sConfigPVD.Mode = PWR_PVD_MODE_IT_RISING_FALLING;
HAL_PWR_ConfigPVD(&sConfigPVD);
/* 使能PVD输出 */
HAL_PWR_EnablePVD();
}
void PVD_IRQHandler(void)
{
HAL_PWR_PVD_IRQHandler();
}
/**
* @brief PWR PVD interrupt callback
* @param None
* @retval None
*/
void HAL_PWR_PVDCallback(void)
{
/* 亮红灯,实际应用中应进入紧急状态处理 */
LED_RED;
}
注意这个中断服务函数的名是PVD_IRQHandler而不是EXTI16_IRQHandler(STM32没有这样的中断函数名),示例中我们仅点亮了LED红灯,不同的应用中要根据需求进行相应的紧急处理。
974

被折叠的 条评论
为什么被折叠?



