STM32F030 电压监测实现
STM32发生掉电时,可以通过检测掉电,记录掉电发生或保存少量数据,在快速恢复电压或者重启后,进行状态恢复。
实现电压监测的方式有两种,第一种时利用PVD的方式,PVD 是Programmable Votage Detector 可编程电压监测器,作用是监视供电电压,在供电电压下降到给定的阀值以下时,可以产生一个中断,通知软件做紧急处理。在给出表格的上半部分就是可编程的监视阀值数据。当供电电压又恢复到给定的阀值以上时,也可以产生一个中断,通知软件供电恢复。供电下降的阀值与供电上升的PVD阀值有一个固定的差值,引入这个差值的目的是为了防止电压在阀值上下小幅抖动,而频繁地产生中断。
通过电源控制寄存器(PWR_CR)中的PLS[2:0]位来设定监控的电压值。 PLS[2:0]位用于选择PVD监控电源的电压阀值:
000:2.2V
001:2.3V
010:2.4V
011:2.5V
100:2.6V
101:2.7V
110:2.8V
111:2.9V
在电源控制/状态寄存器(PWR_CSR)中的PVDO标志用来表明VDD是高于还是低于PVD设定的电压阀值(PVDO=0: VDD高于阀值;PVDO=1,VDD低于阀值)。该事件连接到外部中断的第16线,如果该中断在外部中断寄存器中被使能的,该事件就会产生中断。当VDD下降到PVD阀值以下和(或)当VDD上升到PVD阀值之上时,根据外部中断第16线的上升/下降边沿触发设置,就会产生PVD中断。因此,对电压跌落或者电压回升的识别,可以通过查询PVDO标志或者配置中断线的方式。需要留意的是此功能需要开启特定的时钟。如(Keil参考代码):
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
然后设置监测电压的阀值并启动监测:
PWR_PVDLevelConfig(PWR_PVDLevel_4);//2.6V for power voltage monitoring
PWR_PVDCmd(ENABLE);
如果需要调整监测的阀值,则可通过如下代码:
PWR_PVDCmd(DISABLE);
PWR_PVDLevelConfig(PWR_PVDLevel_7);//2.9V for power voltage monitoring
PWR_PVDCmd(ENABLE);
然后就可以通过PWR_GetFlagStatus(PWR_FLAG_PVDO)读取PVDO的状态,等效于读取(PWR->CSR)&PWR_CSR_PVDO。采用中断识别的配置外部中断和中断相应代码即可。如:
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line16;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //EXTI_Trigger_Rising, EXTI_Trigger_Falling or EXTI_Trigger_Rising_Falling
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel =PVD_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
然后配置中断响应服务函数即可:
void PVD_IRQHandler(void)
{
if(PWR_GetFlagStatus(PWR_FLAG_PVDO))
{
//Do something
}
EXTI_ClearITPendingBit(EXTI_Line16);
但是,但是,部分STM32F030系列的芯片,内部没有PVD电路,所以无法使用上述功能。具体见下图:
所以,如果用到譬如STM32F030K6T6或一些型号,则需要采用另外一种电压检测方式,也就是对Vrefint进行监测,通过ADC-DMA方式,连续进行Vrefint电压采样,并在主循环里,持续进行检测,发现电压下降或者上升到特定阀值时,进行处理。(原理可参见https://blog.csdn.net/hwytree/article/details/103333769),对于STM32F030K6T6的实现代码如下。
以下是ADC-DMA的adc1.h部分:
#ifndef __ADC1_H
#define __ADC1_H
#include "stm32f0xx.h"
#include "stm32f0xx_adc.h"
#define ADC1_DR_Address 0x40012440
extern uint16_t STM32_Vrefint;
void ADC1_DMA_Init(void);
#endif
以下是ADC-DMA的adc1.c部分:
#include "ADC1.h"
void ADC1_DMA_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 , ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);
ADC_DeInit(ADC1);
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)(&STM32_Vrefint);
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular);
ADC_DMACmd(ADC1, ENABLE);
ADC_StructInit(&ADC_InitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Backward;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_ChannelConfig(ADC1, ADC_Channel_Vrefint ,ADC_SampleTime_239_5Cycles);
ADC_VrefintCmd(ENABLE);
ADC_GetCalibrationFactor(ADC1);
ADC_Cmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN));
ADC_StartOfConversion(ADC1);
}
然后在主程序里进入循环程序前启动ADC-DMA:
uint16_t STM32_Vrefint;
ADC1_DMA_Init();
然后STM32_Vrefint就是被连续的ADC-DMA更新的Vrefint采样值。在3.3V供电时,约等于1.2/3.3*4096。STM公司在芯片出厂前将STM32F030在3.3V的Vrefint测试校准值记录在特定16位寄存器里,可以通过下述访问方式获得:
(*((uint16_t *)(0x1FFFF7BA)))
因此,对应供电电压为2.9V时,对应的校准阀值为
(*((uint16_t *)(0x1FFFF7BA)))*3.3/2.9
在供电电压升高时,STM32_Vrefint会降低,在供电电压降低时,STM32_Vrefint会升高。如果STM32_Vrefint的值低于上面这个阀值,则可认为电压(如原始供电电压为3.3V)已从3.3V低过了2.9V。
上述就是STM32F030供电电压监测的方式,因为PVD方式不可用,所以ADC-DMA方式是最好的选择,如不采用DMA方式,主程序持续去控制读取ADC,则会引入不良延时。因为电路中的电容效应,供电电压变化非高速级别,通过ADC-DMA方式,可以满足电压适时变化的跟踪要求,并在跌落或上升到特定阀值时,做出相应的处理。
-End-