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(); //数码管动态扫描