有时在一些应用中,我们需要检测系统是否掉电了,或者要在掉电的瞬间需要做一些处理。STM32就有这样的掉电检测机制——PVD(Programmable Voltage Detecter),即可编程电压检测器。
通过PVD我们可以设定一个基准电压,当芯片的供电电压高于或低于该基准电压时便产生PVD中断,我们可以在PVD中断里做一些处理。本文以STM32L051为例,来说明PVD的使用。
以下是STM32L051用户手册中对PVD的描述,根据这张图我们可以知道,PVD中断在内部是连接在中断线16的,软件上可以配置需要上升沿中断还是下降沿中断,也可以设置双边沿触发,这和GPIO中断有点类似。
若设置了双边沿触发,则上电当VDD超过PVD阈值时产生下降沿中断;掉电时VDD低于PVD阈值产生上升沿中断。图中的100mv是滞后,所以准确地说,在掉电时是要当VDD电压小于(PVD阈值-100mv)时才会触发PVD中断。
PVD总共可以设置7个等级,可以通过PWR_CR寄存器的PLS[2:0]来设置。下面是PLS的描述,其中最后一个等级是特殊的,它使用PB7引脚的电压和内部基准电压进行比较,使用这一等级时,PB7必须设置成模拟输入。
一般而言,我们用前6个等级就足够了,具体选择哪个等级需要根据自己板子的实际情况来定夺,例如当MCU是3.3V供电且电源非常稳定时,就可以选择将阈值设置成3.1V,这样在掉电时就可以更早的触发PVD中断做紧急处理。同时也要注意电路上储能电容是否够大,这会影响PVD中断能处理多少代码,因为系统很快就要完全断电了。
在软件编程上,PVD的使用非常的简单,下面是一份HAL库的例子。PVD的初始化只需要提供2个参数,一个是PVDLevel,也就是上文提到的7个等级;另一个参数是Mode,即中断的边沿选择或事件的边沿选择,一般用中断就足够了,事件没用过。当设置为双边沿中断时,可以通过PWR_CSR的PVDO位来判断是上升沿还是下降沿,HAL库已经封装好了相应的宏:
__HAL_PWR_GET_FLAG,通过__HAL_PWR_GET_FLAG( PWR_FLAG_PVDO )就可以获取PVDO位的状态,为0则是VDD高于阈值(上电的情况),为1则是VDD小于阈值(掉电的情况)。
实测在掉电时,MCU会多次进入PVD中断,这应该是因为掉电瞬间电压不稳定导致的。因此如果在掉电前要做一些紧急操作,要记得加个静态变量标记,使紧急操作只执行一次。
/* 初始化PVD */
void PVD_Init(void)
{
PWR_PVDTypeDef PvdStruct;
HAL_PWR_EnablePVD();
PvdStruct.PVDLevel = PWR_PVDLEVEL_6;
PvdStruct.Mode = PWR_PVD_MODE_IT_RISING;
HAL_PWR_ConfigPVD(&PvdStruct);
HAL_NVIC_SetPriority(PVD_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(PVD_IRQn);
}
/* PVD中断处理 /
void PVD_IRQHandler(void)
{
if(__HAL_PWR_GET_FLAG( PWR_FLAG_PVDO )) / 1为VDD小于PVD阈值,掉电情况 /
{
/ 掉电前的紧急处理 */
}
}
最后顺便说下哪些应用场合会用到PVD,以下是我工作中遇到过得一些场景。
1.记录设备掉电时间。这个很好理解,可能业务上就有这个需求,或者可以利用这一点来完成低功耗设备的待机时长测试。
2.通知其他处理离线。假如设备中有由干电池供电的MCU1和由锂电池供电的MCU2,MCU1的部分功能可能需要MCU2来完成,MCU1需要知道MCU2是否离线(因为锂电池可拔插,可能随时被拔)。这种情况就可以在MCU2上利用PVD来通知MCU1。通知的方式有很多,例如串口直接通知另一方自己将要断电了。
不过要注意低功耗下的使用场景,例如STM32进入STOP模式时,系统时钟是关闭的,此时串口发送的数据波特率可能不是期望的波特率,被通知方收到的数据可能是错的,因此建议将通知方的串口时钟源配置成HSI,并且将低功耗唤醒后的默认时钟配成HSI,这样一旦在低功耗状态下进入PVD,串口发送的数据也不会有问题。
原作者:实测在掉电时,MCU会多次进入PVD中断,这应该是因为掉电瞬间电压不稳定导致的。因此如果在掉电前要做一些紧急操作,要记得加个静态变量标记,使紧急操作只执行一次。
/* 初始化PVD */
void PVD_Init(void)
{
PWR_PVDTypeDef PvdStruct;
HAL_PWR_EnablePVD();
PvdStruct.PVDLevel = PWR_PVDLEVEL_6;
PvdStruct.Mode = PWR_PVD_MODE_IT_RISING;
HAL_PWR_ConfigPVD(&PvdStruct);
HAL_NVIC_SetPriority(PVD_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(PVD_IRQn);
}
/* PVD中断处理 /
void PVD_IRQHandler(void)
{
if(__HAL_PWR_GET_FLAG( PWR_FLAG_PVDO )) / 1为VDD小于PVD阈值,掉电情况 /
{
/ 掉电前的紧急处理 */
}
}
最后顺便说下哪些应用场合会用到PVD,以下是我工作中遇到过得一些场景。
1.记录设备掉电时间。这个很好理解,可能业务上就有这个需求,或者可以利用这一点来完成低功耗设备的待机时长测试。
2.通知其他处理离线。假如设备中有由干电池供电的MCU1和由锂电池供电的MCU2,MCU1的部分功能可能需要MCU2来完成,MCU1需要知道MCU2是否离线(因为锂电池可拔插,可能随时被拔)。这种情况就可以在MCU2上利用PVD来通知MCU1。通知的方式有很多,例如串口直接通知另一方自己将要断电了。
不过要注意低功耗下的使用场景,例如STM32进入STOP模式时,系统时钟是关闭的,此时串口发送的数据波特率可能不是期望的波特率,被通知方收到的数据可能是错的,因此建议将通知方的串口时钟源配置成HSI,并且将低功耗唤醒后的默认时钟配成HSI,这样一旦在低功耗状态下进入PVD,串口发送的数据也不会有问题。
原作者:Dokin丶