基于51单片机的数字万用表设计

1、设计需求及目标

本次研究探讨了数字万用表的目的和意义,以STC89C52单片机作为主控制芯片,结合电压检测电路、电流检测电路、电阻检测电路、数码管驱动电路、复位电路、震荡电路、52单片机最小系统、显示部分、A/D转换和控制部分电路以及ADC0832数据转换芯片和数码显示装置,实现了数字万用表的设计。

2、设计思路及方案

本设计重点要解决的问题是对不同量程的各种测量的转换,还有就是各部分电路组合成一个完整的数字万用表,所设计的万用表能够测量电压值、电流值以及电阻值,并且以四位数码显示。而难点解决的问题就是程序的设计,要保证其可行性从而保证设计的正确性[7-8]。利用A/D模数转换器将连续的模拟信号、模拟量转换成离散的、不连续的数字量,通过程序利用相应的对应关系显示出简单易懂的数字量,从而完成数字万用表的功能。硬件电路:

3、部分代码

#include <reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#include "intrins.h"											   

#define     u8  			unsigned char
#define     u16   		unsigned int
#define     uchar  			unsigned char
#define     uint   		unsigned int

//按键                 
sbit Key_V=P3^0;				 //电压模式键
sbit Key_R=P1^4;				 //电阻模式键
sbit Key_I=P3^5;				 //电流模式键

#define KEY_V 		1		//电压模式
#define KEY_R			2		//电阻模式
#define KEY_I			3		//电流模式

/***********************************************************************************************************
数码管显示相关函数
***********************************************************************************************************/

#define   SMG_NUM   4
u8 code DisplayNum[16]={
0xc0,		//0
0xf9,		//1
0xa4,		//2
0xb0,		//3
0x99,		//4
0x92,		//5
0x82,		//6
0xf8,		//7
0x80,		//8
0x90,		//9
0x88,		//A
0x83,		//b
0xc6,		//C
0xa1,		//d
0x86,		//E
0x8e		//F
};
//
u8 code DisplayOther[]={
0xff, 		//0		空
0x7f, 		//1		"."
0xbf,			//2		"-"
0xa7			//3		c
};

void delay_ms(uint q)
{
	uint i,j;
	for(i=0;i<q;i++)
		for(j=0;j<110;j++);
}
//数码管位选定义
sbit smg_we1 = P2^0;	    //东西数码管2
sbit smg_we2 = P2^1;			//东西数码管1
sbit smg_we3 = P2^2;			//南北数码管2
sbit smg_we4 = P2^3;			//南北数码管1


void smg_we_switch(uchar i)
{
	switch(i)
	{
		case 0: smg_we1 = 0;  smg_we2 = 1; smg_we3 = 1;  smg_we4 = 1; break;
		case 1: smg_we1 = 1;  smg_we2 = 0; smg_we3 = 1;  smg_we4 = 1; break;
		case 2: smg_we1 = 1;  smg_we2 = 1; smg_we3 = 0;  smg_we4 = 1; break;
		case 3: smg_we1 = 1;  smg_we2 = 1; smg_we3 = 1;  smg_we4 = 0; break;
	}	
}


void delay_1ms(uint q)
{
	uint i,j;
	for(i=0;i<q;i++)
		for(j=0;j<110;j++);
}

#define LED_a		0	//数码管段选的a段接在段选IO口的第0位
#define LED_b		2
#define LED_c		6
#define LED_d		4
#define LED_e		3
#define LED_f		1
#define LED_g		7
#define LED_dp	5

u8 ChangeFor(u8 dat)
{
	u8 temp=0;
	if(dat&0x01)		//判断数据的第一位是否为1
		temp|=0x01<<LED_a;//如果为1,放到控制数码管a段的位置
	if(dat&0x02)
		temp|=0x01<<LED_b;
	if(dat&0x04)
		temp|=0x01<<LED_c;
	if(dat&0x08)
		temp|=0x01<<LED_d;
	if(dat&0x10)
		temp|=0x01<<LED_e;
	if(dat&0x20)
		temp|=0x01<<LED_f;
	if(dat&0x40)
		temp|=0x01<<LED_g;
	if(dat&0x80)
		temp|=0x01<<LED_dp;
	return temp;
}
uchar dis_smg[SMG_NUM];						//显示缓存数组

void DisplayScan()
{
	static uchar i;
	P0 = 0xff;	          //消隐 
		smg_we_switch(i);  	  			//位选
	P0 =	ChangeFor(dis_smg[i]);		  //段选	  
	i++;
	if(i>=SMG_NUM)
		i=0;
}


/***********************************************************************************************************
ADC0832相关函数
***********************************************************************************************************/
sbit ADCS 	=P1^2; //ADC0832 片选
sbit ADCLK  =P1^0; //ADC0832 时钟
sbit ADDI 	=P1^1; //ADC0832 数据输入		/*因为单片机的管脚是双向的,且ADC0832的数据输入输出不同时进行,
sbit ADDO 	=P1^1; //ADC0832 数据输出		/*为节省单片机引脚,简化电路所以输入输出连接在同一个引脚上

unsigned int Adc0832(unsigned char channel)
{
	uchar i=0;
	uchar j;
	uint dat=0;
	uchar ndat=0;
	uchar  Vot=0;

	if(channel==0)channel=2;
	if(channel==1)channel=3;
	ADDI=1;
	_nop_();
	_nop_();
	ADCS=0;//拉低CS端
	_nop_();
	_nop_();
	ADCLK=1;//拉高CLK端
	_nop_();
	_nop_();
	ADCLK=0;//拉低CLK端,形成下降沿1
	_nop_();
	_nop_();
	ADCLK=1;//拉高CLK端
	ADDI=channel&0x1;
	_nop_();
	_nop_();
	ADCLK=0;//拉低CLK端,形成下降沿2
	_nop_();
	_nop_();
	ADCLK=1;//拉高CLK端
	ADDI=(channel>>1)&0x1;
	_nop_();
	_nop_();
	ADCLK=0;//拉低CLK端,形成下降沿3
	ADDI=1;//控制命令结束
	_nop_();
	_nop_();
	dat=0;
	for(i=0;i<8;i++)
	{
		dat|=ADDO;//收数据
		ADCLK=1;
		_nop_();
		_nop_();
		ADCLK=0;//形成一次时钟脉冲
		_nop_();
		_nop_();
		dat<<=1;
		if(i==7)dat|=ADDO;
	}
	for(i=0;i<8;i++)
	{
		j=0;
		j=j|ADDO;//收数据
		ADCLK=1;
		_nop_();
		_nop_();
		ADCLK=0;//形成一次时钟脉冲
		_nop_();
		_nop_();
		j=j<<7;
		ndat=ndat|j;
		if(i<7)ndat>>=1;
	}
	ADCS=1;//拉低CS端
	ADCLK=0;//拉低CLK端
	ADDO=1;//拉高数据端,回到初始状态
	dat<<=8;
	dat|=ndat;

	return(dat);            //return ad data
}



/***********************************************************************************************************
主函数
***********************************************************************************************************/
void main (void)
{
	u8 Mode;
	uchar Read_AD;	//用于读取ADC数据
	uchar VIN;			//电压值变量
	u16 RIN;				//电阻值变量
	u16 IIN;				//电流值变量
	u16 i=0;;
	while (1)      				//主循环
	{		
		if(Key_V==0)				//电压按键按下
		{
			Key_V=1;					//清除按下标记
			if((Key_R==0)||(Key_I==0))//电阻电流按键也有按下
			{
				Key_I=1;
				Key_R=1;
				Key_V=1;
				Mode=4;					//标记为错误模式
			}
			else							//电阻电流键都没有按下
			Mode=1;						//标记为电压模式
		}
		if(Key_R==0)				//同电压键
		{
			Key_R=1;
			if((Key_V==0)||(Key_I==0))
			{
				Key_I=1;
				Key_R=1;
				Key_V=1;
				Mode=4;
			}
			else
			Mode=2;
		}
		if(Key_I==0)				//同电压键
		{
			Key_I=1;
			if((Key_V==0)||(Key_R==0))
			{
				Key_I=1;
				Key_R=1;
				Key_V=1;
				Mode=4;
			}
			else
			Mode=3;
		}
		if((Key_V==1)&&(Key_R==1)&&(Key_I==1))//都没有按下
		{
			Mode=0;			//标记为空闲模式
		}
		if(i==0)
		{
		Read_AD=Adc0832(0);				//读取AD值
		}
		i++;
		if(i>300)
			i=0;
			switch(Mode)
		{
			case 0:
						//空闲模式
								dis_smg[0]=DisplayOther[2];//关闭数码管显示
								dis_smg[1]=DisplayOther[2];
								dis_smg[2]=DisplayOther[2];
								dis_smg[3]=DisplayOther[2];
			break;
			case 1:
						//电压模式
								VIN=Read_AD*200/255;										//换算出电压值
								dis_smg[0]=DisplayNum[0xa];							//显示电压标志
								dis_smg[1]=DisplayNum[VIN/100%10];			//电压十位
								dis_smg[2]=DisplayNum[VIN/10%10]&0x7f;	//电压个位
								dis_smg[3]=DisplayNum[VIN%10];					//电压十分位
			break;
			case 2:
						//电阻模式		
								RIN=Read_AD*100/(255-Read_AD);				//换算出电阻值
								dis_smg[0]=DisplayNum[0xb];       		//显示电阻标志
								dis_smg[1]=DisplayNum[RIN/100%10];    //电阻百位
								dis_smg[2]=DisplayNum[RIN/10%10];     //电阻十位
								dis_smg[3]=DisplayNum[RIN%10];        //电阻个位
								if(RIN>=1000)													//超过或等于1000;
								{
									dis_smg[1]=DisplayOther[2];					//显示"-"
									dis_smg[2]=DisplayOther[2];					//显示"-"
									dis_smg[3]=DisplayOther[2];					//显示"-"
								}
			break;
			case 3:
						//电流模式		
								IIN=4*Read_AD;//单位mA								//换算出电流值
								dis_smg[0]=DisplayNum[0xc];           //显示电流标志
								if(IIN<=200)													//没有超过范围
								{
									dis_smg[1]=DisplayNum[IIN/100%10];	//电流百位
									dis_smg[2]=DisplayNum[IIN/10%10]; 	//电流十位
									dis_smg[3]=DisplayNum[IIN%10];    	//电流个位
								}
								else
								{
									dis_smg[1]=DisplayOther[2];					//显示"-"
									dis_smg[2]=DisplayOther[2];					//显示"-"
									dis_smg[3]=DisplayOther[2];					//显示"-"
								}
			break;
			case 4:
						//错误模式		
							dis_smg[0]=DisplayNum[0xe];					//显示"E"
							dis_smg[1]=DisplayNum[0xe];         //显示"E"
							dis_smg[2]=DisplayNum[0xe];         //显示"E"
							dis_smg[3]=DisplayNum[0xe];         //显示"E"
			break;
			default	:	
			break;
		}
		DisplayScan();		//数码管动态扫描

完整资料:
https://market.m.taobao.com/app/idleFish-F2e/widle-taobao-rax/page-detail?wh_weex=true&wx_navbar_transparent=true&id=614515414244&ut_sk=1.WUpxx7gpwUoDAMmnnrBIzAno_12431167_1585228024499.Copy.detail.614515414244.1828622527&forceFlush=1

发布了23 篇原创文章 · 获赞 7 · 访问量 319

猜你喜欢

转载自blog.csdn.net/weixin_41017942/article/details/105128811