51. 电源监控
一、PVD电源监控器
PVD,即电源电压检测器,是 STM32 微控制器中的一项内置功能,用于监测供电电压是否低于预设的阈值。这一功能对于确保系统在电压不稳定或电源故障时的安全至关重要,尤其是在电池供电的设备中,它可以防止因电压过低而导致的数据损坏或硬件故障。
PVD 通过持续监控 VDD 电源轨的电压来工作。当检测到电源电压低于设定的阈值时,PVD 可以触发一个中断或标志,通知 MCU 采取适当的措施。这些措施可能包括:
- 储存关键数据至非易失性存储器(如 EEPROM)
- 执行安全关机程序
- 切换到备用电源(如果有)
二、上电复位和掉电复位
上电时,当 VDD 低于指定 VPOR 阈值时,系统无需外部复位电路便会保持复位模式。一旦 VDD 电源电压高于 VPOR 阈值,系统便会退出复位状态,芯片正常工作。掉电时,当 VDD 低于指定 VPDR 阈值时,系统就会保持复位模式。
三、欠压复位
上电期间,欠压复位(BOR)将使系统保持复位状态,直到 VDD 电源电压达到指定的 VBOR 阈值。VBOR 阈值通过系统选项字节(某些寄存器的 BOR_LEV 位)进行配置。默认情况下,BOR 关闭。可选择以下可编程 VBOR 阈值:
四、电源控制寄存器
4.1、PWR电源控制寄存器
位 [7:5]PLS 用于 设置 PVD 检测的电压阀值。
位 4 PVDE 位,用于使能或者禁止 PVD 检测,显然我们要使能 PVD 检测,该位置 1。
4.2、PWR电源控制状态寄存器
五、PVD电压监控配置步骤
5.1、使能电源管理时钟
#define __HAL_RCC_PWR_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\
UNUSED(tmpreg); \
} while(0U)
5.2、配置PVD参数
void HAL_PWR_ConfigPVD(PWR_PVDTypeDef *sConfigPVD);
形参 sConfigPVD 是 PWR_PVDTypeDef 结构体类型变量,它的定义如下:
typedef struct
{
uint32_t PVDLevel; /*!< PVDLevel: Specifies the PVD detection level.
This parameter can be a value of @ref PWR_PVD_detection_level */
uint32_t Mode; /*!< Mode: Specifies the operating mode for the selected pins.
This parameter can be a value of @ref PWR_PVD_Mode */
}PWR_PVDTypeDef;
PVDLevel:指向 PVD 检测级别,对应 PWR_CR 寄存器的 PLS 位的设置,取值范围 PWR_PVDLEVEL_0 到 PWR_PVDLEVEL_7,共八个级别。
/** @defgroup PWR_PVD_detection_level PWR PVD detection level
* @{
*/
#define PWR_PVDLEVEL_0 PWR_CR_PLS_LEV0
#define PWR_PVDLEVEL_1 PWR_CR_PLS_LEV1
#define PWR_PVDLEVEL_2 PWR_CR_PLS_LEV2
#define PWR_PVDLEVEL_3 PWR_CR_PLS_LEV3
#define PWR_PVDLEVEL_4 PWR_CR_PLS_LEV4
#define PWR_PVDLEVEL_5 PWR_CR_PLS_LEV5
#define PWR_PVDLEVEL_6 PWR_CR_PLS_LEV6
#define PWR_PVDLEVEL_7 PWR_CR_PLS_LEV7
Mode:指定 PVD 的 EXTI 边沿触发模式。
/** @defgroup PWR_PVD_Mode PWR PVD Mode
* @{
*/
#define PWR_PVD_MODE_NORMAL 0x00000000U /*!< basic mode is used */
#define PWR_PVD_MODE_IT_RISING 0x00010001U /*!< External Interrupt Mode with Rising edge trigger detection */
#define PWR_PVD_MODE_IT_FALLING 0x00010002U /*!< External Interrupt Mode with Falling edge trigger detection */
#define PWR_PVD_MODE_IT_RISING_FALLING 0x00010003U /*!< External Interrupt Mode with Rising/Falling edge trigger detection */
#define PWR_PVD_MODE_EVENT_RISING 0x00020001U /*!< Event Mode with Rising edge trigger detection */
#define PWR_PVD_MODE_EVENT_FALLING 0x00020002U /*!< Event Mode with Falling edge trigger detection */
#define PWR_PVD_MODE_EVENT_RISING_FALLING 0x00020003U /*!< Event Mode with Rising/Falling edge trigger detection */
5.3、使能PVD检测
void HAL_PWR_EnablePVD(void);
5.4、使能中断
5.4.1、设置中断优先级分组
HAL_NVIC_SetPriorityGrouping() 函数是设置中断优先级分组函数。其声明如下:
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup);
其中,参数 PriorityGroup 是 中断优先级分组号,可以选择范围如下:
#define NVIC_PRIORITYGROUP_0 0x00000007U /*!< 0 bits for pre-emption priority
4 bits for subpriority */
#define NVIC_PRIORITYGROUP_1 0x00000006U /*!< 1 bits for pre-emption priority
3 bits for subpriority */
#define NVIC_PRIORITYGROUP_2 0x00000005U /*!< 2 bits for pre-emption priority
2 bits for subpriority */
#define NVIC_PRIORITYGROUP_3 0x00000004U /*!< 3 bits for pre-emption priority
1 bits for subpriority */
#define NVIC_PRIORITYGROUP_4 0x00000003U /*!< 4 bits for pre-emption priority
0 bits for subpriority */
这个函数在一个工程里基本只调用一次,而且是在程序 HAL 库初始化函数里面已经被调用,后续就不会再调用了。因为当后续调用设置成不同的中断优先级分组时,有可能造成前面设置好的抢占优先级和响应优先级不匹配。如果调用了多次,则以最后一次为准。
5.4.2、设置中断优先级
HAL_NVIC_SetPriority() 函数是设置中断优先级函数。其声明如下:
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
其中,参数 IRQn 是 中断号,可以选择范围:IRQn_Type 定义的枚举类型,定义在 stm32f407xx.h。
typedef enum
{
PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */
} IRQn_Type;
参数 PreemptPriority 是 抢占优先级,可以选择范围:0 到 15,具体根据中断优先级分组决定。
参数 SubPriority 是 响应优先级,可以选择范围:0 到 15,具体根据中断优先级分组决定。
5.4.3、使能中断
HAL_NVIC_EnableIRQ() 函数是中断使能函数。其声明如下:
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
其中,参数 IRQn 是 中断号,可以选择范围:IRQn_Type 定义的枚举类型,定义在 stm32f407xx.h。
5.5、编写中断服务函数
每开启一个中断,就必须编写其对应的中断服务函数,否则将导致死机(CPU 将找不到中断服务函数)。中断服务函数接口厂家已经在 startup_stm32f407xx.s 中写好了。
void PVD_IRQHandler(void);
HAL 库为了用户使用方便,提供了一个中断通用入口函数 HAL_PWR_PVD_IRQHandler()
。
/**
* @brief This function handles the PWR PVD interrupt request.
* @note This API should be called under the PVD_IRQHandler().
* @retval None
*/
void HAL_PWR_PVD_IRQHandler(void)
{
/* Check PWR Exti flag */
if(__HAL_PWR_PVD_EXTI_GET_FLAG() != RESET)
{
/* PWR PVD interrupt user callback */
HAL_PWR_PVDCallback();
/* Clear PWR Exti pending bit */
__HAL_PWR_PVD_EXTI_CLEAR_FLAG();
}
}
该函数又会调用 HAL_PWR_PVDCallback()
回调函数,需要用户根据需要重写该回调函数。
void HAL_PWR_PVDCallback(void);
六、程序源码
初始化 PVD 电压监视器函数:
/**
* @brief 初始化PVD电压监视器函数
*
* @param level 电压等级,可选值: PWR_PVDLEVEL_x, x 可选范围:[0, 7],对应电压范围: 2.2V ~ 2.9V
*/
void PVD_Init(uint32_t level)
{
PWR_PVDTypeDef PWR_PVDInit = {0};
__HAL_RCC_PWR_CLK_ENABLE();
PWR_PVDInit.PVDLevel = level; // 电压监测等级
PWR_PVDInit.Mode = PWR_PVD_MODE_IT_RISING_FALLING; // 使用中断线的上升沿和下降沿双边缘触发
HAL_PWR_ConfigPVD(&PWR_PVDInit);
HAL_PWR_EnablePVD(); // 使能PVD
// 设置PVD中断优先级
HAL_NVIC_SetPriority(PVD_IRQn, 4, 0);
HAL_NVIC_EnableIRQ(PVD_IRQn);
}
PVD 中断服务函数:
/**
* @brief PVD中断服务函数
*
*/
void PVD_IRQHandler(void)
{
HAL_PWR_PVD_IRQHandler();
}
PVD 中断回调函数:
/**
* @brief PVD中断回调函数
*
*/
void HAL_PWR_PVDCallback(void)
{
if (__HAL_PWR_GET_FLAG(PWR_FLAG_PVDO)) // 电压比PLS设置的还低
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET);
}
}
main() 函数:
int main(void)
{
HAL_Init();
System_Clock_Init(8, 336, 2, 7);
Delay_Init(168);
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
PVD_Init(PWR_PVDLEVEL_7);
LED_Init();
while (1)
{
}
return 0;
}