STM32F030 电压监测实现

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-

猜你喜欢

转载自blog.csdn.net/hwytree/article/details/106694331