基于51单片机的差分双路电压检测(基于ADC0832)


前言

博主终于又空出时间啦,积攒了一大波的项目,今天分享一个51单片机利用ADC0832电压检测芯片检测双路差分电压信号的代码+电路图,实测有效!


一、本文任务

根据实际应用情况,可将差分电压衰减N倍以后送入单片机内部进行检测。通过ADC0832芯片将通道0的电压转换成0-255等级,分别对应0-5V,从而实现差分电压的测量。在此过程中,还通过数码管对电压进行显示。

二、材料

1、OPA2188(用作前端电压跟随,此处LM358实测效果不太行,建议更换这个芯片)

2、OPA2188(用作同相放大电路运算芯片,与前者一样,都是一样的芯片)

3、51单片机(AT,STC都可)

4、ADC0832(由于51单片机内部没有ADC芯片,因此需要用到外置的ADC芯片,这个芯片有两个电压检测脚,在代码里面通过设定通道从而检测对应通道的电压)

5、共阴数码管(此处是三位共阴数码管,通过G1-G4进行数码管的片选,别看图里面就一个数码管,其实有三个)

三、电路图

代码如下(示例):
在这里插入图片描述
电路图无需赘述,就是51单片机最小系统板加上一系列的外设,注意的是两个LM358芯片都要用OPA2188芯片来代替,贵是贵了点,架不住效果好哇哈哈哈!

四、代码解读

1.引脚定义及参数

#include <reg51.h>
#include <intrins.h>
/*********************************端口定义**********************************/
sbit CS   = P3^2;           //芯片CS口
sbit Clk = P3^0;            //芯片脉冲输出口
sbit DATI = P3^1;           //data数据通用口
sbit DATO = P3^1;
sbit led=P1^0;	            //定义P20口是输出口
sbit DIAN = P0^5;        		//小数点
/*******************************定义全局变量********************************/
unsigned char dat = 0x00;      //AD值
unsigned char count = 0x00;      //定时器计数
unsigned char CH;       //通道变量
unsigned char dis[4];       //显示数值
unsigned int sum=0;
typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;

CS/CLK/DATI/DAT0都是ADC0832的输出引脚,要注意的是P31引脚不仅仅用作输出,而且还用作输入,是一个双向的引脚。

led这里解释一下,这个是后面外加的一个功能,主要就是每经过5min检测一次当前差分电位,如果大于某个特定的电压值,就为高电平,小于某个特定的电压值,就为低电平,去掉不影响

DIAN是小数点为,人为设置的

其它均有注释

2.定时器中断与延时开启

/*******************************************************************************
* 函 数 名         : delay
* 函数功能		   : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay_ms(u16 i)
{
	while(i--);	
}
/*******************************************************************************
* 函 数 名         : Timer0Init
* 函数功能		   : 定时器0初始化
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer0Init()
{
	TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。

	TH0=0X3C;	//给定时器赋初值,定时50ms
	TL0=0XB0;	
	ET0=1;//打开定时器0中断允许
	EA=1;//打开总中断
	TR0=1;//打开定时器			
}

作用为调用ms级别的延时以及开启定时器0,定时器0的定时时长为50ms,主要是配合LED进行定时的判断输出高低电平。

3.数码管显示定义及ADC0832函数初始化

/*******************************共阴LED段码表*******************************/

unsigned char code tab[]={0x5F,0x44,0x9D,0xD5,0xC6,0xD3,0xDB,0x47,0xDF,0xD7};
/****************************************************************************
函数功能:AD转换子程序
入口参数:CH
出口参数:dat
****************************************************************************/
unsigned char adc0832(unsigned char CH)
{
	unsigned char i,test,adval;
	adval = 0x00;
	test = 0x00;
	Clk = 0;       //初始化
	DATI = 1;
	_nop_();
	CS = 0;
	_nop_();
	Clk = 1;
	_nop_();
	
	if ( CH == 0x00 )      //通道选择
	{
		Clk = 0;
		DATI = 1;      //通道0的第一位
		_nop_();
		Clk = 1;
		_nop_();
		Clk = 0;
		DATI = 0;      //通道0的第二位
		_nop_();
		Clk = 1;
		_nop_();
	} 
	else
	{
		Clk = 0;
		DATI = 1;      //通道1的第一位
		_nop_();
		Clk = 1;
		_nop_();
		Clk = 0;
		DATI = 1;      //通道1的第二位
		_nop_();
		Clk = 1;
		_nop_();
	}
	
	Clk = 0;
	DATI = 1;
	for( i = 0;i < 8;i++ )      //读取前8位的值
	{
		_nop_();
		adval <<= 1;
		Clk = 1;
		_nop_();
		Clk = 0;
		if (DATO)
		adval |= 0x01;
		else
		adval |= 0x00;
	}
	for (i = 0; i < 8; i++)      //读取后8位的值
	{
		test >>= 1;
		if (DATO)
		test |= 0x80;
		else 
		test |= 0x00;
		_nop_();
		Clk = 1;
		_nop_();
		Clk = 0;
	}
	if (adval == test)      //比较前8位与后8位的值,如果不相同舍去。若一直出现显示为零,请将该行去掉
	dat = test;
	nop_();
	CS = 1;        //释放ADC0832
	DATO = 1;
	Clk = 1;
	return dat;
}

会用就行,具体就是通过调用这个函数可以返回0-255的数值,分别对应0-5V,相当于一个8位的数据格式。需要设置的就是CH通道,有两种,一个是0通道,一个是1通道。分别对应ADC0832上面的CH0和CH1,根据自己的需求选择不同通道即可。

4.数据转换及数码管显示

/****************************************************************************
函数功能:延时子程序
入口参数:
出口参数:
****************************************************************************/
void delay(void)
{
    int k;
    for(k=10;k<5000;k++);
}
/****************************************************************************
函数功能:将0-255级换算成0.00-5.00的电压数值
入口参数:i
出口参数:
****************************************************************************/
void convdata(unsigned int i)
{
	i=i*1.96;	 
    dis[0] = i/100;         //个位
    dis[1] = i%100/10;      //小数点后第一位	 
    dis[2] = i%100%10;      //小数点后第二位
//	dis[3] = i%1000%100%10;     
}
/****************************************************************************
函数功能:数码管显示子程序
入口参数:
出口参数:
****************************************************************************/
void display(void)
{  
    P0=tab[dis[0]];      //显示个位和小数点11011111
	DIAN=1;
    P2=0xdf;                  //11011111
    delay_ms(100);    
    P2=0xff;

    P0=tab[dis[1]];       //显示小数点后第一位
    P2=0xbf;                //10111111
    delay_ms(100);
    P2=0xff;

    P0=tab[dis[2]];       //显示小数点后第二位
    P2=0x7f;              //01111111  
    delay_ms(100);
    P2=0xff;
}

convdat函数的逻辑就是将0-255的数值转换成0-500的数值,有个对应关系就是乘以1.96。做这个的主要目的就是为了把从ADC0832上面检测到的8位值转换成0-500的电压表示,500就代表5V,0就代表0V。并且将电压值表现出来。

display函数主要目的就是为了把数据显示到数码管上。逻辑也很简单,P0是控制数码管的显示的,P2口是控制选择哪个数码管的。根据逻辑指令。第一个数据要加上小数点,所以定义了DIAN=1;同时P0口显示当前的数字,这个是直接调用的tab数组,在最开始的参数定义里面已经定义好了共阴数码管所对应的数字数组。同时P2口设置为0xdf,我也在注释那里用二进制表现出来了,其实就是对应的数码管。

5.主函数及定时器函数

/****************************************************************************
函数功能:主程序
入口参数:
出口参数:
****************************************************************************/
void main(void)
{
	unsigned char m;   
	CH = 0x00;       //在这里选择通道 0x00或0x01
	Timer0Init();    //定时器0初始化
	while(1)         //主循环		  
	{
		for(m=0;m<50;m++) //检测50次取平均提高检测精度
		{
			dat = adc0832(CH);
			sum=sum+dat;
		}
		dat=sum/50;
		sum=0; 
		convdata(dat);  //数据转换
		display();      //显示数值
	}
}

/*******************************************************************************
* 函 数 名         : void Timer0() interrupt 1
* 函数功能		   : 定时器0中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer0() interrupt 1
{
	static u16 i;
	TH0=0X3C;	//给定时器赋初值,定时50ms
	TL0=0XB0;
	i++;
	if(i==6000)//如果过了5分钟,检测一次输出高低电位
	{
		if(dat>=153)
		led=0;
    else
    led=1;			
	}	
	if(i==6200)//再过10s,输出强制为0
	{
		i=0;
		led=0;
	}
}

主函数里面设置了通道0的检测。同时开启了定时器0的初始化。在while循环里面,对通道0的电压进行了50次的检测,并且将50次的检测出来的数据累加最后取50次的平均,平均后的数据再进行格式的转换以及数码管的显示。在定时器里面每隔5min进行一次电压的判断,前文提到过。

6.所有代码(就一个c文件)

#include <reg51.h>
#include <intrins.h>
/*********************************端口定义**********************************/
sbit CS   = P3^2;           //芯片CS口
sbit Clk = P3^0;            //芯片脉冲输出口
sbit DATI = P3^1;           //data数据通用口
sbit DATO = P3^1;
sbit led=P1^0;	            //定义P20口是输出口
sbit DIAN = P0^5;        		//小数点
/*******************************定义全局变量********************************/
unsigned char dat = 0x00;      //AD值
unsigned char count = 0x00;      //定时器计数
unsigned char CH;       //通道变量
unsigned char dis[4];       //显示数值
unsigned int sum=0;
typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;
/*******************************************************************************
* 函 数 名         : delay
* 函数功能		   : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay_ms(u16 i)
{
	while(i--);	
}
/*******************************************************************************
* 函 数 名         : Timer0Init
* 函数功能		   : 定时器0初始化
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer0Init()
{
	TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。

	TH0=0X3C;	//给定时器赋初值,定时50ms
	TL0=0XB0;	
	ET0=1;//打开定时器0中断允许
	EA=1;//打开总中断
	TR0=1;//打开定时器			
}
/*******************************共阴LED段码表*******************************/

unsigned char code tab[]={0x5F,0x44,0x9D,0xD5,0xC6,0xD3,0xDB,0x47,0xDF,0xD7};
/****************************************************************************
函数功能:AD转换子程序
入口参数:CH
出口参数:dat
****************************************************************************/
unsigned char adc0832(unsigned char CH)
{
	unsigned char i,test,adval;
	adval = 0x00;
	test = 0x00;
	Clk = 0;       //初始化
	DATI = 1;
	_nop_();
	CS = 0;
	_nop_();
	Clk = 1;
	_nop_();
	
	if ( CH == 0x00 )      //通道选择
	{
		Clk = 0;
		DATI = 1;      //通道0的第一位
		_nop_();
		Clk = 1;
		_nop_();
		Clk = 0;
		DATI = 0;      //通道0的第二位
		_nop_();
		Clk = 1;
		_nop_();
	} 
	else
	{
		Clk = 0;
		DATI = 1;      //通道1的第一位
		_nop_();
		Clk = 1;
		_nop_();
		Clk = 0;
		DATI = 1;      //通道1的第二位
		_nop_();
		Clk = 1;
		_nop_();
	}
	
	Clk = 0;
	DATI = 1;
	for( i = 0;i < 8;i++ )      //读取前8位的值
	{
		_nop_();
		adval <<= 1;
		Clk = 1;
		_nop_();
		Clk = 0;
		if (DATO)
		adval |= 0x01;
		else
		adval |= 0x00;
	}
	for (i = 0; i < 8; i++)      //读取后8位的值
	{
		test >>= 1;
		if (DATO)
		test |= 0x80;
		else 
		test |= 0x00;
		_nop_();
		Clk = 1;
		_nop_();
		Clk = 0;
	}
	if (adval == test)      //比较前8位与后8位的值,如果不相同舍去。若一直出现显示为零,请将该行去掉
	dat = test;
	nop_();
	CS = 1;        //释放ADC0832
	DATO = 1;
	Clk = 1;
	return dat;
}
/****************************************************************************
函数功能:延时子程序
入口参数:
出口参数:
****************************************************************************/
void delay(void)
{
    int k;
    for(k=10;k<5000;k++);
}
/****************************************************************************
函数功能:将0-255级换算成0.00-5.00的电压数值
入口参数:i
出口参数:
****************************************************************************/
void convdata(unsigned int i)
{
	i=i*1.96;	 
    dis[0] = i/100;         //个位
    dis[1] = i%100/10;      //小数点后第一位	 
    dis[2] = i%100%10;      //小数点后第二位
//	dis[3] = i%1000%100%10;     
}
/****************************************************************************
函数功能:数码管显示子程序
入口参数:
出口参数:
****************************************************************************/
void display(void)
{  
    P0=tab[dis[0]];      //显示个位和小数点11011111
	DIAN=1;
    P2=0xdf;                  //11011111
    delay_ms(100);    
    P2=0xff;

    P0=tab[dis[1]];       //显示小数点后第一位
    P2=0xbf;                //10111111
    delay_ms(100);
    P2=0xff;

    P0=tab[dis[2]];       //显示小数点后第二位
    P2=0x7f;              //01111111  
    delay_ms(100);
    P2=0xff;
}
/****************************************************************************
函数功能:主程序
入口参数:
出口参数:
****************************************************************************/
void main(void)
{
	unsigned char m;   
	CH = 0x00;       //在这里选择通道 0x00或0x01
	Timer0Init();    //定时器0初始化
	while(1)         //主循环		  
	{
		for(m=0;m<50;m++) //检测50次取平均提高检测精度
		{
			dat = adc0832(CH);
			sum=sum+dat;
		}
		dat=sum/50;
		sum=0; 
		convdata(dat);  //数据转换
		display();      //显示数值
	}
}

/*******************************************************************************
* 函 数 名         : void Timer0() interrupt 1
* 函数功能		   : 定时器0中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer0() interrupt 1
{
	static u16 i;
	TH0=0X3C;	//给定时器赋初值,定时50ms
	TL0=0XB0;
	i++;
	if(i==6000)//如果过了5分钟,检测一次输出高低电位
	{
		if(dat>=153)
		led=0;
    else
    led=1;			
	}	
	if(i==6200)//再过10s,输出强制为0
	{
		i=0;
		led=0;
	}
}

总结

点赞收藏评论三连哦,你们的支持就是我最大的动力哈哈哈,坚持分享!

  • 8
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值