赞
踩
LVD的功能是检测 单片机VDD / VDDA 引脚上的供电电压是否低于低电压检测阈值,该阈值由电源控制寄存器(PMU_CTL)中的LVDT[2:0]位进行配置。有以下几种阈值可选:
低压检测阈值选择LVDT[2:0]
LVD的检测结果就是以标志位LVDF的形式体现的,当芯片的VDD 和VDDA引脚输入的电压低于PMU_CTL寄存器中的LVDT[2:0]位进行配置的阈值电压时,LVDF标志硬件置1,否则LVDF硬件清0。可见LVDF标志只读,完全由硬件改写。
以下是简化后的LVD阈值波形图。手册上的图强调了LVD有100mV的迟滞电压,也就是说在掉电阶段,实际触发低压检测事件的阈值是比设置的阈值低100mV左右的。为了方便理解,这里忽略这个因素。
从下图可以发现,在上电阶段,电源引脚电压上升,LVDF会从1变为0,但是我们一般不检测这个阶段,所以可以忽略。在断电阶段,电源引脚电压下降,LVDF会从0变为1,触发低压事件。
LVD低压检测属于电源管理单元(PMU)中的一部分,所以要使用LVD,需要打开PMU的外设时钟。
rcu_periph_clock_enable(RCU_PMU); //使能电源管理单元时钟
LVD的寄存器很简单。RCU模块中,与LVD相关的只有3处寄存器位段:PMU_CTL.LVDT[2:0] 、 PMU_CTL.LVDEN 以及 PMU_CS.LVDF。
标准库中提供了一个函数用于配置LVD,如下:
- //文件:gd32f1x0_pmu.c
- //功能:设置低压检测阈值,并使能LVD功能
- void pmu_lvd_select(uint32_t lvdt_n)
- {
- PMU_CTL &= ~PMU_CTL_LVDEN; /* disable LVD */
- PMU_CTL &= ~PMU_CTL_LVDT; /* clear LVDT bits */
- PMU_CTL |= lvdt_n; //设置LVD检测阈值
- PMU_CTL |= PMU_CTL_LVDEN; //使能低压检测机制
- }
LVD事件连接至EXTI的第16线,用户可以通过配置EXTI的第16线产生相应的中断,即通过低压标志位LVDF的高和低的变化来触发EXTI Line16的中断。所以LVD中断必须借助EXTI去完成。EXTI Line16只用于服务LVD,没有其他的触发源可选。
由于我们通常只关注掉电阶段的低压检测事件,因此应该配置EXTI的检测沿为上升沿触发。
注意,虽然LVD中断是通过EXTI 来实现的,但是其中断在名义上却是LVD中断:要开启LVD中断使能,并编写LVD中断服务函数。
- nvic_irq_enable(LVD_IRQn,2,0); //使能LVD检测中断
-
- //LVD中断函数
- void LVD_IRQHandler(void)
- {
- //LVD紧急操作,例如写入重要数据到Flash做掉电存储
-
- }
在许多应用场景中,都有在单片机断电前及时将关键数据写入到非易失存储器中保存,以便下次开机再次恢复关键数据的需求。这种需求可以使用LVD中断来实现。当单片机电源断开时,必定导致其VDD/VDDA引脚上的电压缓慢下降,最终为0。尤其是当单片机电源加了大电容时,掉电过程会更加缓慢。那么单片机在触发LVD中断后,就会有更充足的时间去执行数据紧急存储操作。
使用LVD来做掉电存储的优点是无需外加复杂的断电检测电路,同时也避免反复写Flash或者EEPROM存储器,降低存储器寿命,因为只需要在掉电的时候写存储器就行了。
下面是一个例子,使用flag变量来决定LED闪烁的快慢,flag在每次掉电时,取反后存储到片上Flash。每次上电时从Flash读取到变量中。其结果是每断电上电一次,LED的闪烁频率就会改变一次。
- #include "gd32f1x0.h"
- #include <stdio.h>
-
- void delay_ms(uint32_t n)
- {
-
- uint32_t j;
- while(n--)
- {
- j=18888;
- while(j--);
- }
- }
-
- #define LED13_ON gpio_bit_set(GPIOC,GPIO_PIN_13)
- #define LED13_OFF gpio_bit_reset(GPIOC,GPIO_PIN_13)
- #define LED13_TOG \
- do{ \
- if(gpio_output_bit_get(GPIOC,GPIO_PIN_13)) \
- gpio_bit_reset(GPIOC,GPIO_PIN_13); \
- else \
- gpio_bit_set(GPIOC,GPIO_PIN_13); \
- }while(0)
-
-
- void RCU_config(void)
- {
- rcu_periph_clock_enable(RCU_GPIOC); //打开GPIOC外设时钟
- rcu_periph_clock_enable(RCU_PMU); //使能电源管理单元时钟
-
- }
-
- void NVIC_config(void)
- {
- nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
- nvic_irq_enable(LVD_IRQn,2,0); //使能LVD检测中断
- }
-
-
- void LVD_config(void)
- {
- while(RESET != pmu_flag_get(PMU_FLAG_LVD)) ; //越过上电阶段
- pmu_lvd_select(PMU_LVDT_7); //配置检测阈值,使能LVD功能
- }
-
-
- void EXTI_config(void)
- {
- //EXTI Line 16 for LVD
- exti_flag_clear(EXTI_16);
- exti_init(EXTI_16,EXTI_INTERRUPT,EXTI_TRIG_FALLING); //检测下降沿
- }
-
- void GPIO_config(void)
- {
- //配置PC13推挽输出
- gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_13);
- gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_13);
-
- }
-
-
- //掉电存储数据的起始Flash Page
- #define USER_FLASH_S_ADDR (0x08000000 + 32*1024)
-
- volatile uint32_t flag;
-
- int main(void)
- {
- RCU_config(); //外设时钟初始化
- NVIC_config(); //NVIC初始化
- LVD_config(); //LVD初始化
- EXTI_config(); //EXTI 初始化
- GPIO_config(); //GPIO初始化
-
- flag = REG32(USER_FLASH_S_ADDR); //从Flash加载数据到内存变量flag
-
- //擦除flag所在的page,为掉电存储做准备
- fmc_unlock();
- fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGERR);
- fmc_page_erase(USER_FLASH_S_ADDR);
- fmc_lock();
-
- while(1)
- {
- LED13_TOG;
- if(flag==0){
- delay_ms(100);
- }
- else{
- delay_ms(500);
- }
- }
- }
-
- //LVD中断函数
- void LVD_IRQHandler(void)
- {
- __disable_irq();
-
- //将flag取反然后写入到Flash掉电存储
- fmc_unlock();
- fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGERR);
- fmc_word_program(USER_FLASH_S_ADDR,!flag);
- fmc_lock();
-
- __enable_irq();
-
- exti_interrupt_flag_clear(EXTI_16); //清除EXTI Line中断标志
- }
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。