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电源控制寄存器

PWR电源控制寄存器

  位 [7:5]PLS 用于 设置 PVD 检测的电压阀值

  位 4 PVDE 位,用于使能或者禁止 PVD 检测,显然我们要使能 PVD 检测,该位置 1。

4.2、PWR电源控制状态寄存器

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);

  形参 sConfigPVDPWR_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;
}
posted @ 2024-01-29 19:02  星光映梦  阅读(75)  评论(0编辑  收藏  举报