LVR 与 LVD 简单介绍、使用、实际使用LVD侦测多种电压状态(九齐NY8为例)

本章内容:LVR 与 LVD 介绍,使用方式,代码案例,注意事项,源码解释,LVD侦测多种电压实战

本次使用九齐NY8系列芯片,示例代码使用的芯片为NY8A051H,其他NY8系列芯片的LVD使用相差无几,但还请确认不同芯片的芯片手册之间是否有所区别。

一、LVR

LVR:低电压复位,当供电低于设定的低电压值时,它会自动将芯片复位,以防止异常运行。

(一)设置LVR电压

请添加图片描述

(二)使能LVR

void system_init(void)
{
	//....
	//其他初始化操作
	
	PCON |= C_LVR_En;	//LVR使能

	//....
}

二、LVD

LVD:低电压检测,它用于检测芯片供电电压是否低于一定值,当电压低于这个值的时候,会将 LVDOUT 置 0

请添加图片描述

(一)代码案例

案例描述:芯片检测电压控制小灯,PB1接绿灯接地,PB2接红灯接地

​ 1.当电压大于4.1V时,绿灯亮,红灯灭

​ 2.当电压低于4.1V时,红灯亮,绿灯灭

#include <ny8.h>
#include "ny8_constant.h"
/*
*	案例代码
*/
void main()
{
    DISI();					//禁中断
    INTE = 0x00;
    
    IOSTB = C_PB_Output;	//设置全部PB引脚为输出模式
    PORTB = 0x00;			//全部PB脚输出低电平
	PCON = C_WDT_En;		//使能看门狗
    
    //*****低电压检测初始化设置*****
    CMPCR = 0x0A;			//电压选择控制寄存器
    PCONbits.LVDEN = 1;		//使能LVD
    PCON |= C_LVD_4P1V;		//设置LVD检测电压为4.1V
    //***************************
    
    while(1)
	{
        //如果PCON1 & C_LVDOUT的值为1,说明当前输入电压大于4.1V
        if(PCON1 & C_LVDOUT)
        {
            PORTBbits.PB1 = 1;	//绿灯亮
            PORTBbits.PB2 = 0;	//红灯灭
        }
        //否则,输入电压小于4.1V
        else
        {
            PORTBbits.PB1 = 0;	//绿灯灭
            PORTBbits.PB2 = 1;	//红灯亮
        }
	 	CLRWDT();				// 清理看门狗
    }
}

(二)注意事项

上述代码很接近官网示例,简单描述了 LVD 初始化以及使用,在第一次使用时,我便踩了不少坑,我先细细盘点我遇到的各种问题:

  1. LVD实际上就是一个比较器,不同芯片对其的封装便可能有区别,有的芯片在低于设定值时,读取LVDOUT会得到0;但是有的芯片在低于设定值时,读取LVDOUT会得到1。所以,确定LVDOUT的输出需要在官网查看官方文档的内部电路结构
  2. **LVD也可以侦测不同电压,每改变检测电压时,需要延时特定时间才能得到正确的状态值。**这个具体也会在芯片手册中的标题LVD下说明。请添加图片描述

(三)源码解释

//;------------------------------------------------------------	
//; PCON1 (0FH)	--------- 电源控制寄存器1
//;------------------------------------------------------------
//bit7:为全部中断使能
#define		C_All_INT_En				0x80			//启用所有未被屏蔽的中断
//bit6:低压检测器输出	低压为0,非低压为1
#define		C_LVDOUT					0x40			//【只读】
//bit[5:2] : 选择LVD电压	(注:必须设置CMPCR为0001010b,即0x0A)
#define		C_LVD_3P75V					0x3C			//电压3.75V	
#define		C_LVD_3P45V					0x38			//电压3.45V	
#define		C_LVD_3P2V					0x34			//电压3.2V	
#define		C_LVD_4P15V					0x30			//电压4.15V	
#define		C_LVD_2P6V					0x2C			//电压2.6V
#define		C_LVD_4P05V					0x28			//电压4.05V	
#define		C_LVD_1P95V					0x24			//电压1.95V	
#define		C_LVD_3P9V					0x20			//电压3.9V	
#define		C_LVD_3P6V					0x1C			//电压3.6V	
#define		C_LVD_3P3V					0x18			//电压3.3V	
#define		C_LVD_3P0V					0x14			//电压3.0V
#define		C_LVD_2P9V					0x10			//电压2.9V	
#define		C_LVD_2P8V					0x0C			//电压2.8V
#define		C_LVD_2P4V					0x08			//电压2.4V
#define		C_LVD_2P2V					0x04			//电压2.2V
#define		C_LVD_2P0V					0x00			//电压2.0V
//bit0 : 使能定时器0
#define		C_TMR0_En					0x01			// 使能定时器0
#define		C_TMR0_Dis					0x00			// 禁用定时器0

(四)LVD 侦测多种电压

在注意事项中,我提到LVD也可以侦测不同电压,但相对来说还挺复杂,便以一个实战来讲述这个设置方式。

注:NY8A051H 在修改PCON[5:2]时,需要延时50us才能得到正确的状态值,这是通过芯片手册得知,不同芯片可能会有差别

芯片封装:051H合封
在这里插入图片描述
原理图:
在这里插入图片描述
案例描述:
之前用过九齐051H与4054的合封芯片,PB4在芯片内部与4054的CHRG脚连接(充电时CHRG脚低电平,充满或不充时高阻塞),外部有四个引脚(PB0,PB1,PB2,PB3),分别连接了前灯,侧灯,红色LED指示灯,按键
需求说明:
一、充电部分
1.充电时:红灯常亮
2.充满或未充电时:红灯熄灭
3.电池电压低于3V时,红灯闪烁
二、功能要求
1.一档:LED1以780mA亮灯;0-3min:780mA保持;3-6min:电流缓慢下降,6min后,保持500mA。
2.二档:LED1爆闪模式,频率8 Hz。
3.三档:LED2以300mA亮灯;0-3min:300mA保持;3-6min,电流缓慢下降,6min后,保持;
4.四档:关掉所有灯。
三、待机时进入休眠模式
四、其他要求,在充满电后,如果不开灯或者插拔电源,红灯不能再重新亮起
五、低压关断:2.7V
头疼的问题:
充满电后,过了十几分钟4054会重新充电,导致红灯亮起,这不被允许,所以只好加LVD检测4.1V左右电压,以阻止此事发生。

user_config.h

#ifndef user_config_H
#define user_config_H

#include <ny8.h>
#include "ny8_constant.h"

#define u8 unsigned char
#define u16 unsigned int
#define u32 unsigned long

#define ENABLE	1
#define DISABLE	0

#define TRUE	1
#define FALSE	0

#define SET		1
#define RESET	0

#define INC		1
#define DEC		0

#define ON		0
#define OFF		1

#define BIT0	0x01
#define BIT1	0x02
#define BIT2	0x04
#define BIT3	0x08
#define BIT4	0x10
#define BIT5	0x20
#define BIT6	0x40
#define BIT7	0x80
#define BIT8	0x100
#define BIT9	0x200
#define BIT10	0x400
#define BIT11	0x800
#define BIT12	0x1000
#define BIT13	0x2000
#define BIT14	0x4000
#define BIT15	0x8000

#define IO_CHRG		PB4
#define IO_KEY		PB3
#define IO_COB1		PB2
#define IO_COB2		PB1
#define IO_LEDR		PB0

#define TMR0_T		170			//50us
#define TMR0_PS		C_PS0_Div2	//64分频

#define SYSTEM_5MS		BIT0
#define SYSTEM_100MS	BIT1

#define CHARGE_OFF			0	//未充电
#define CHARGE_ON			1	//充电中
#define CHARGE_FULL			2	//充满

#define UPDATE_REG(x)	__asm__("MOVR _" #x ",F")


#endif

main.c

#include "check.h"
#include "pub_config.h"

#define KEY_FILTER 	   		6	//按键抖动滤波次数
#define COB_MAX  			116
#define COB_MIN 			64	
#define COB_SEC_DELAY 		180
#define COB_CNT_DELAY 		200

extern u8 cnt;

u8 cob_cnt = 0;		//档位1毫秒计数(每5ms加1)
u8 cob_sec = 0;		//档位秒计数
u8 cob_step = 0;	//档位1状况,0-3 78亮,3-6 78->50,6 50

u8 key_scan = 0;			//按键状态
u8 key_last = 0;			//上一次按键状态
u8 key_filter = 0;			//按键抖动计数器
u8 cob_gear = 0;			//档位
u8 cob1_duty = 0;			//占空比
u8 cob2_duty = 0;			//占空比

volatile u8 sleep_delay = 0;
volatile u8 system_tick = 0;

void System_Init(void);
void LED_Show(void);
void LED_Task(void);
void Key_Task(void);
void Sleep_Task(void);

/*
*	每5ms定时器触发中断
*/
void isr() __interrupt(0)
{
	static u8 tick_5ms = 0;
	static u8 tick_100ms = 0;
	if(T0IF)//Timer0溢出中断标志
	{
		T0IF = 0;//清除中断标志
		TMR0 = TMR0_T;
		if(++tick_5ms > 100)
		{
			tick_5ms = 0;
			system_tick |= SYSTEM_5MS;
			if(++tick_100ms >= 20)
			{
				tick_100ms = 0;
				system_tick |= SYSTEM_100MS;
			}
		}
	}
	if(PBIF)
	{
		PBIF = 0;
		sleep_delay = 0;
	}
}

void main(void)
{
	System_Init();
	voltage_reset();
	CLRWDT();
    while(1)
    {
		LED_Show();
    	if(system_tick & SYSTEM_5MS)
    	{
    		CLRWDT();
    		system_tick &= ~SYSTEM_5MS;
    		Key_Task();//检查按键
			LED_Task();//检查灯
    	}
       	if(system_tick & SYSTEM_100MS)
		{
    		CLRWDT();
			system_tick &= ~SYSTEM_100MS;
			Check_CHAG_Task();		//检测是否充满电
			Sleep_Task();			//休眠
		}
    }
}

void System_Init(void)
{
	DISI();								//关中断
	
	//GPIO
	PORTB = 0x00; 					  	//全部输出低电平
	IOSTB = C_PB3_Input | C_PB4_Input;	//PB3 PB4 输入,其他输出
	BPHCON = (unsigned char)~(C_PB3_PHB | C_PB4_PHB);//PB3 PB4 上拉
	
	//时钟
	PCON1 = C_TMR0_Dis;							//关闭时钟
	TMR0 = TMR0_T;								//初始化Timer0寄存器
	T0MD = TMR0_PS;								//分频率64
	PCON =  C_WDT_En | C_LVR_En | C_LVD_En;		//使能看门狗
	CMPCR = 0x0A;
	PCONbits.LVDEN = 1;
	PCON1 = C_TMR0_En | C_LVD_2P9V;				//使能时钟 
	
	//充电和按键唤醒
	BWUCON	= C_PB3_Wakeup | C_PB4_Wakeup;//充电和按键唤醒
	INTE = C_INT_TMR0 | C_INT_PBKey;	//使能Timer0溢出中断和PB输入变化中断
	UPDATE_REG(PORTB);
	INTF = 0;							//清除所有中断标志

	ENI();								//开中断
}

//按键任务
void Key_Task(void)
{
	//当前高电平,却被按下,清除
   	if (IO_KEY)
   	{
   	   	if (key_scan)
   	   	   	key_filter = 0;
   	   	key_scan = 0;
   	}
   	//当前低电平,按键状态为0,设置为1,记为按下
   	else
   	{
   	   	if (!key_scan)
   	   		key_filter = 0;
   	   	key_scan = 1;
   	}
   	//按键抖动计数
   	if (key_filter < KEY_FILTER)
   	{
   	   	key_filter++;
   	   	sleep_delay = 0;
   	}
   	//按键有效
   	else if (key_last != key_scan)
   	{
   		key_last = key_scan;
   	   	if (key_scan == 0)
   	   		return;
   		if (cob_gear == 0)
   			cob_gear = 1;
   		else if (cob_gear == 1)
   	   		cob_gear = 2;
   	   	else if (cob_gear == 2)
   	   		cob_gear = 3;
   	   	else
   	   		cob_gear = 0;
   	}
}

void LED_Show(void)
{
	static u8 pwm_cnt = 0;
	
	if(cob_gear)
	{
		pwm_cnt++;
		IO_COB1 = pwm_cnt < cob1_duty ? 1 : 0;
		IO_COB2 = pwm_cnt < cob2_duty ? 1 : 0;
		if(pwm_cnt >= 200)
			pwm_cnt = 0;
	}
	else
	{
		IO_COB1 = 0;
		IO_COB2 = 0;
	}
}

void LED_Task(void)
{
	switch(cob_gear)
	{
		case 0:
			cob1_duty = 0;
			cob2_duty = 0;
			cob_cnt = 0;
			cob_sec = 0;
			cob_step = 0;
			break;
		case 1:
			cob2_duty = 0;
			cob_cnt++;
			if(cob_cnt >= COB_CNT_DELAY)
			{
				cob_cnt=0;
				cob_sec++;
			}
			//每3分钟进入下一步
			if(cob_sec >= COB_SEC_DELAY)
			{
				cob_sec = 0;
				if(++cob_step > 2)
					cob_step = 2;
			}
			//根据档位状况,调整占空比
			switch(cob_step)
			{
				case 0:
					cob1_duty = COB_MAX;
					break;
				case 1:
					//每6秒调低1%占空比
					if(++cob_sec % 6 == 0)
					{
						if(--cob1_duty <= COB_MIN)
							cob_step++;
					}
					break;
				default:
					cob1_duty = COB_MIN;
					break;
			}
			break;
		case 2:
			//清理档位1
			cob2_duty = 0;
			if(++cob_cnt <= 12)
				cob1_duty = COB_MAX;
			else if(++cob_cnt <= 25)
				cob1_duty = 0;
			else
				cob_cnt = 0;
			break;
		case 3:
			cob1_duty = 0;
			cob2_duty = 61;
			break;
		default:
			cob_gear = 0;
			cob1_duty = 0;
			cob2_duty = 0;
			break;
	}
}

//休眠
void Sleep_Task()
{
	//如果开着灯或在充电或低压,不休眠
	if(cob_gear || charge_state)
	{
		sleep_delay = 0;
		return;
	}
	//休眠延时达到,进入休眠
	if(++sleep_delay >= 10)
	{
		sleep_delay = 0;
		
		PCON &= ~C_WDT_En;
		PCON1 &= ~C_TMR0_En;
		NOP();
		
		cnt = 0;
		IO_COB1 = 0;
		IO_COB2 = 0;
		IO_LEDR = 0;
		
		NOP();
		SLEEP();
		NOP();
		NOP();
		
		PCON = C_WDT_En | C_LVR_En | C_LVD_En;
		PCON1 = C_TMR0_En | C_LVD_2P9V;
	}
}

check.h

#ifndef check_H
#define check_H

#include "user_config.h"

extern volatile u8 voltage_state;
extern volatile u8 charge_state;
extern volatile u8 charge_flag;

#define CHARGE_OFF		0	//未充电
#define CHARGE_ON		1	//充电中
#define CHARGE_FULL		2	//充满电

typedef enum
{
	VOLTAGE_OFF	= 0,
	VOLTAGE_LOW	,
	VOLTAGE_FULL,
}VOLTAGE_TypeDef;

void voltage_reset();
void Check_CHAG_Task(void);

#endif

check.c

#include "user_config.h"
#include "check.h"

#define LVD_CLEAR()		PCON1 &= ~(BIT2 | BIT3 | BIT4 | BIT5)
#define LVD_SET_OFF()	PCON1 |= C_LVD_2P8V		
#define LVD_SET_LOW()	PCON1 |= C_LVD_2P9V
#define LVD_SET_FULL()	PCON1 |= C_LVD_4P15V

#define CHECK_FILTER 	   	5	//充电抖动滤波次数

u8 cnt = 0;
volatile u8 voltage_state = 0;		//电压状态
volatile u8 charge_state = 0;		//充电状态
volatile u8 charge_flag = 0;
u8 charge_last_state = 0;	//上一次充电状态
u8 check_filter = 0;		//充电抖动计数

extern volatile u8 sleep_delay;
extern u8 cob_gear;			//档位

void check_set(u8 state)
{
   	LVD_CLEAR();
   	switch(state)
	{
		case VOLTAGE_OFF:	LVD_SET_OFF();	break;
		case VOLTAGE_LOW:	LVD_SET_LOW();	break;
		case VOLTAGE_FULL:	LVD_SET_FULL();	break;
		default:break;
	}
}

void voltage_reset()//1s
{
	static u8 cnt = 0;
   	u8 check = 0;
   	u8 i = 0;
   	u8 j = 0;
   	
   	voltage_state = VOLTAGE_FULL;
   	
   	while(1)
   	{
   	   	check_set(voltage_state);
   		//NOP 200 times = delay 100us
   	   	for(j = 0; j <= 120; j++)
   	   	{
   	   		NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP(); 
   	   	}
   	   	CLRWDT();
   	   	if (PCON1 & C_LVDOUT)
   	   	{
   	   	   	return;
   	   	}
   	   	if (--voltage_state == VOLTAGE_OFF)
   	   	   	return;
   	}
}


void Check_CHAG_Task(void)//100
{
	static u8 cnt = 0;
	static u8 low = 0;
	static u8 rise = 0;
	static u16 time = 0;
	if(PCON1 & C_LVDOUT)
	{
		low = 0;
		if(++rise >= 200)
		{
			rise = 0;
			voltage_reset();
		}
	}
	else
	{
		rise = 0;
		if(++low >= 20)
		{
			low = 0;
			voltage_reset();
		}
	}
	
	if(IO_CHRG)
    {
    	//chrg拉高后,如果电池电压为满电
    	if(voltage_state == VOLTAGE_FULL)
    	{
    		charge_state = CHARGE_FULL;	
    	}
    	else
    	{
    		charge_state = CHARGE_OFF;//如果电池未满电,则未充电
    	}
    	if(cob_gear)
    	{
    		charge_state = CHARGE_OFF;//如果开过灯,则状态为未充电
    	}
    }
    else
    {
    	if(charge_state != CHARGE_FULL)//如果不是充满电状态,则状态改为充电
    	{
			charge_state = CHARGE_ON;
		}
    }
    
    if(charge_state == CHARGE_ON)
    {
    	IO_LEDR = 1;
    }
    else if(voltage_state == VOLTAGE_OFF)
    {
		if(++cnt < 5)
		{
			IO_LEDR = 1;
		}
		else if(cnt < 10)
		{
			IO_LEDR = 0;
		}
		else
		{
			cnt = 0;
		}	
    }
    else
    {
    	IO_LEDR = 0;
    }
}

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值