简易数字电压表C程序
- 格式:doc
- 大小:30.00 KB
- 文档页数:3
简易数字电压表设计报告姓名:***班级:自动化1202学号:****************:***2014年11月26日一.设计题目采用C8051F360单片机最小系统设计一个简易数字电压表,实现对0~3.3V 直流电压的测量。
二.设计原理模拟输入电压通过实验板PR3电位器产生,A/D转换器将模拟电压转换成数字量,并用十进制的形式在LCD上显示。
用一根杜邦实验线将J8口的0~3.3V输出插针与J7口的P2.0插针相连。
注意A/D转换器模拟输入电压的范围取决于其所选择的参考电压,如果A/D 转换器选择内部参考电压源,其模拟电压的范围0~2.4V,如果选择外部电源作为参考电压,则其模拟输入电压范围为0~3.3V。
原理框图如图1所示。
图1 简易数字电压表实验原理框图三.设计方案1.设计流程图如图2所示。
图2 简易数字电压表设计A/D转换和计时流程图2.实验板连接图如图3所示。
图3 简易数字电压表设计实验板接线图3.设计步骤(1)编写C8051F360和LCD初始化程序。
(2)AD转换方式选用逐次逼近型,A/D转换完成后得到10位数据的高低字节分别存放在寄存器ADCOH和ADC0L中,此处选择右对齐,转换时针为2MH Z。
(3)选择内部参考电压2.4V为基准电压(在实际单片机调试中改为3.311V),正端接P2.0,负端接地。
四、测试结果在0V~3.3V中取10组测试数据,每组间隔约为0.3V左右,实验数据如表1所示:显示电压(V)0.206 0.504 0.805 1.054 1.406实际电压(v)0.210 0.510 0.812 1.061 1.414相对误差(%) 1.905 1.176 0.862 0.659 0.565显示电压(V) 2.050 2.383 2.652 2.935 3.246实际电压(v) 2.061 2.391 2.660 2.943 3.253相对误差(%)0.421 0.334 0.301 0.272 0.215表1 简易数字电压表设计实验数据(注:其中显示电压指LCD显示值,实际电压指高精度电压表测量值)五.设计结论1.LCD显示模块的CPLD部分由FPGA充当,芯片本身自带程序,所以这个部分不用再通过quartus软件进行编程。
编者:PYH日期:功能:实现ADC0809采集0〜5V电压采集,通过按键可选择0~2,3个通道切换,要选择其他通道按同理写按键函数即可,采用1602显示.*/maiii.c文「卜#iiiclude<reg52.h>#iiiclude<stdio.h>#iiiclude n1602.ir#include"delay.h”frdefineumtunsignedmt#defineuchaiunsignedchar芦***ADC0809引脚定义********************/sbitCLK=P2Z6;sbitST=P2A3;sbitALE=P2A7;sbitOE=P2A5;sbitEOC=P2A4;sbitADDA=P3Z5;sbitADDB=P3A6;sbitADDC=P3A7;/*****按键定义*****/sbitKEY1=P3A2;sbitKEY2=P3A3;sbitKEY3=P3A4;芦****°函数声明*************/voidADC0809_uut();//ADC0809初始化函数unsignedcharRead_AD();//读取AD数据函数unsignedcharkeyscaii();〃通道选择函数voidmain()(uclwnum=0;unsignedchaitemp[7];〃显示区域临时存储数组floatVoltage;〃浮点变量ADC0809_uut();LCD_Imt();//LCD初始化DelayMs(20);LCD.ClearQ;〃清屏LCD_W【ite_Stnng(3,0,''PYH_DVM〃设置表头ADC0809^uut();//AD0809初始化ST=O;while(l)ST=1;〃启动ADC0809ST=O;keyscan();〃。
〜2num=Read_AD();〃读取AD0809数据Vbltage=(float)num*5/256;〃数据转换,float是强制转换符号,将结果转换为浮点型spimtf(temp,"VO:%3.2f",Voltage);〃格式输出电压值,%3.2表示浮点输出,小数点后两位LCD_Wiite_Stiing(2,l,temp);〃将数据写入1602显示))voidtimerO(void)intemipt1〃中断处理函数,产生约500KHZ时钟供0809工作( TH0=(65536-200)/256;TL0=(65536-200)%256;CLK-CLK;}voidADC0809_uut()//ADC0809初始化(TMOD=0x01;TH0=(65536-200)/256;TL0=(65536-200)%256;ET0=l;TR0=l;EA=1;DelayMs(2);}unsignedcharRead_AD()〃读ADC0809数据(uchartemp=0;//存放AD采集的数据wlnle(EOC=0);〃等待转换完成OE=1;temp=Pl;〃读取数据DelayMs(l);OE=0;returntemp;unsignedcharkeyscan()(if(KEYl==O)〃选择通道0{ALE=1;//允许写入地址ADDA=O;ADDB=O;ADDC=O;ALE=O;)elseif(KEY2==0)〃选择通道1{ALE=1;ADDA=1;ADDB=O;ADDC=O;ALE=O;}elseif(KEY3=0)〃选择通道2{ALE=1;ADDA=0;ADDB=1;ADDC=0;ALE=0;)/****其他通道同理写入地址即可********/)#ifhdef_DELAY_H_#define_DELAYHus延时函数,含参,t值范围0〜255*voidDelayUs2x(unsignedchart); ms延时函数,含参,t值范围O~255*voidDelayMs(unsignedchart);#endif#include"delay.h”voidDelayUs2x(unsignedchart)(\vliile(—t);}voidDelayMs(unsignedchart)(while。
基于51单片机数字电压表本模块采用ADC0832模数转换芯片,LCD1602液晶显示,测量范围0-5V,精度误差0.01V看了很多网上的课程设计或者毕业论文,得出以下几点:1.数字电压表的方案有很多种,有的采用ADC0809,或者ADC0808等,他们都是8温AD,并口传输数据,具有速率高的优点。
但是硬件复杂,与单片机电路繁琐,焊接起来比较麻烦。
所以本设计采用ADC0832,同样8位AD,特点是串口传输数据,硬件接口简单,且精度误差一致,速率也比较快,对于要求不高的系统非常适合。
2.显示电路,网上采用LED显示居多,本设计采用LCD1602液晶显示,具有硬件搭设简单,显示美观等优点3.本设计方便移植,只需将LCD1602三个控制端口,ADC0832 四个控制端口修改即可。
注意LCD1602数据传输接口是单片机的P0口,如下图,需要接上拉电阻4.程序采用C代码编写,亲测直接可以使用,若需仿真文件,请用E-mail联系邮系。
邮箱:gnsywb@5.网上很多设计数据转换程序有误,不够正确。
在转换过程中,中间变量需设置为int 类型,虽然8位AD输出最高位255,但是余数转换过程中会大于255。
若设计char型,会造成显示输出有误。
void convert(uchar a){ uint temp; //特别注意这里需定义int型(余数将大于255)dis[0]=a/51; //取个位temp=a%51;temp=temp*10; dis[1]=temp/51; //取小数点后第一位temp=temp%51;temp=temp*10; dis[2]=temp/51; //取小数点后第二位}具体电路图如下:1.利用电压表与测量显示电压对比附录:C程序/******************************************** 功能:单片机数字电压表ADC0832+LCD16021,测量范围0-5V2,2路输入电压,可自行设定3,测量精度误差0.01V,LCD液晶显示编写者:小子在西藏gnsywb@编写日期:2012-11-5*********************************************/ #include <reg52.h>#include <intrins.h>#define uint unsigned int#define uchar unsigned charsbit lcdrs=P2^4;sbit lcdrw=P2^5;sbit lcden=P2^6; //1602控制端口sbit DI=P3^4;sbit DO=P3^4; //DI和DO与单片机共接口sbit Clk=P3^3;sbit CS=P3^5;//ADC0832控制端口uchar dis[3]={0x00,0x00,0x00}; //显示缓冲区uchar date=0; //AD值uchar CH; //ADC0832通道值/*****************************************AD0832转换程序******************************************/uchar ADC0832(uchar CH){uchar i,dis0,dis1;Clk=0; //拉低时钟DI=1; //初始化_nop_();CS=0; //芯片选定_nop_();Clk=1; //拉高时钟_nop_();if(CH==0) //通道选择{Clk=0; //第一次拉低时钟DI=1; //通道0的第一位_nop_();Clk=1; //拉高时钟_nop_();Clk=0; //第二次拉低时钟,ADC0832 DI接受数据DI=0; //通道0的第二位_nop_();Clk=1;_nop_();}else{Clk=0;DI=1; //通道1的第一位_nop_();Clk=1;_nop_();Clk=0;DI=1; //通道1的第二位_nop_();Clk=1;_nop_();}Clk=0; //第三次拉低时钟,此前DI两次赋值决定通道DI=1; //DI开始失效,拉高电平,便于DO数据传输for(i= 0;i<8;i++) //读取前8位的值{_nop_();dis0<<= 1;Clk=1;_nop_();Clk=0;if (DO)dis0|=0x01;elsedis0|=0x00;}for (i=0;i<8;i++) //读取后8位的值{dis1>>= 1;if (DO)dis1|= 0x80;elsedis1|= 0x00;_nop_();Clk=1;_nop_();Clk=0;}if(dis0==dis1) //两次结束数据比较,若相等date=dis0; //则赋值给dat_nop_();CS=1; //释放ADC0832DO=1; //拉高输出端,方便下次通道选择DI端有效Clk=1; //拉高时钟return date;}/***********************************************数据转换程序功能:将0-255级换算成0.00-5.00的电压数***********************************************/void convert(uchar a){uint temp; //特别注意这里需定义int型(余数将大于255)dis[0]=a/51; //取个位temp=a%51;temp=temp*10;dis[1]=temp/51; //取小数点后第一位temp=temp%51;temp=temp*10;dis[2]=temp/51; //取小数点后第二位}/***************************************** LCD1602驱动程序******************************************/ void delay(uchar z){uint x,y;for(x=z;x>0;x--)for(y=122;y>0;y--);}void write_cmd(uchar cmd)//lcd1602写命令函数{lcdrs=0;lcdrw=0; //选择指令寄存器lcden=1;P0=cmd; //写数据delay(5);lcden=0; //使能拉低lcden=1;}void write_date(uchar date)//lcd1602写数据函数{lcdrs=1;lcdrw=0; //选择数据寄存器lcden=1;P0=date; //写数据delay(5);lcden=0; //使能拉低lcden=1;}void init_lcd1602()//lcd1602初始化{write_cmd(0x01); //清屏write_cmd(0x38); //功能设置write_cmd(0x0c); //显示设置write_cmd(0x06); //输入方式从左到右delay(1);}/***************************************** 显示函数*****************************************/ void display(void){uchar i;write_cmd(0x80);for(i=0;i<3;i++){if(i==1) write_date('.'); //第二位显示小数点write_date (0x30+dis[i]);delay(5);}write_date('V'); //最后一位后显示字符'V'}/************************************************ 主函数***************************************************/ void main(void){CH=0; //选择通道0或1init_lcd1602();//液晶1602显示初始化while(1) //主循环{date=ADC0832(CH);//启动ADC0832转换并接受数据delay(1);convert(date); //数据转换成BCD码display(); //显示数值}}。
基于单片机实现简易数字电压表的设计任务的功能要求:1. 可以选择8路0~5V的输入电压值;2. 可以轮流显示或是单路选择显示;3. 测量显示的最小分辨率为0.01V,测量误差正负0.02V;4. 具有电压过低、过高的声光报警功能。
在此我们选择的单片机为STC89C52芯片、ADC0809数模转换器(因仿真软件没有,故选用ADC0808替代,其实差不多的,就是输出管脚的排序问题)、LCD1602液晶屏(接线相对与数码管简单多了)。
在protel软件中的仿真电路图如下:P2.7的开关的选择是选择显示1,2,3,4路或是5,6,7,8路。
下面的单片机的C程序长是长了点,但确实运行有效,主要花费在8路的数据实现上了,另外其中的报警设置也是思路中有一路需要报警也即报警的。
关于各个芯片及液晶的资料自己去网上找就好了。
另外P0端口必须接上拉电阻!lcd1602的3脚管必须接可调电阻!单片机的C程序清单:#include <reg52.h>#include <intrins.h>#include<string.h>#define dat_port P0#define uchar unsigned char #define uint unsigned int#define delay4us() {_nop_();_nop_();_nop_();_nop_();}sbit AB1=P3^2;sbit AB2=P3^1;sbit AB3=P3^0;sbit AB5=P3^3;sbit AB4=P2^7;sbit RS=P2^0;//RS=1 数据 RS=0 命令sbit RW=P2^1;//RW=1 读取 RW=0 写入sbit E=P2^2;//E 使能信号sbit EOC=P2^4;sbit OE=P2^5;sbit START=P2^3;sbit CLOCK=P2^6;uchar X[4]={"0000"};void Refresh_show();void delay50us(uint m){uint n,k;for(n=m;n>0;n--)for(k=25;k>0;k--);}void Write_LCD_Command(uchar cmd);//向LCD写入命令void Write_LCD_Data(uchar dat); //向LCD写入一个字节的数据函数 void Initialize_LCD1602(); //液晶初始化函数void LCD_Display(uchar *str);//在LCD上显示字符串//---------------忙检查-------------------//uchar LCD_Busy_Check(){uchar LCD_Status;RS = 0;RW = 1;E = 1;delay50us(4);LCD_Status = P0;E = 0;return LCD_Status;}//--------------向LCD写入命令--------------------//void Write_LCD_Command(uchar cmd){while((LCD_Busy_Check()& 0x80)==0x80); //忙等待RS = 0;RW = 0;E = 0;P0 = cmd;delay4us();E = 1;delay4us();E = 0;}//-----------向LCD写入一个字节的数据函数-----------------*/void Write_LCD_Data(uchar dat){while((LCD_Busy_Check()&0x80)==0x80);RS = 1;RW = 0;E = 0;P0 = dat;delay4us();E = 1;delay4us();E = 0;}//-----------LCD初始化-----------------*/void Initialize_LCD1602() //液晶初始化函数{Write_LCD_Command(0x38);delay50us(10); //功能设置,数据长度为8位,双行显示,5×7点阵字体Write_LCD_Command(0x0C);delay50us(10); // 显示开,关光标Write_LCD_Command(0x06);delay50us(10); //字符进入模式:屏幕不动,字符后移Write_LCD_Command(0x01); delay50us(10);//清屏}//-----------在LCD上显示字符串-----------------*/void LCD_Display(uchar *str){uchar i;for(i=0;i<strlen(str);i++){Write_LCD_Data(str[i]);delay50us(100);}}unsigned long dat_adc0808;uchar display_buffer[][16]={{"DC1=0.00DC2=0.00"}, {"DC3=0.00DC4=0.00"}};uchar display1_buffer[][16]={{"DC5=0.00DC6=0.00"}, {"DC7=0.00DC8=0.00"}};uint adc0808_init() // AD初始化{START=0;OE=0;START=1;START=0;while(EOC==0);OE=1;dat_adc0808=P1;OE=0;return dat_adc0808;}void Refresh_show0() //刷新显示{uint t=dat_adc0808*500.0/255; //if(t>400||t<100)X[0]=1;elseX[0]=0;display_buffer[0][4] = t/100+'0'; //整数位display_buffer[0][6] = t/10%10+'0'; //两个小数位 display_buffer[0][7] = t%10+'0'; }void Refresh_show1() //刷新显示{uint t=dat_adc0808*500.0/255; //if(t>400||t<100)X[1]=1;elseX[1]=0;display_buffer[0][12] = t/100+'0'; //整数位display_buffer[0][14] = t/10%10+'0'; //两个小数位 display_buffer[0][15] = t%10+'0'; }void Refresh_show2() //刷新显示{uint t=dat_adc0808*500.0/255; //if(t>400||t<100)X[2]=1;elseX[2]=0;display_buffer[1][4] = t/100+'0'; //整数位display_buffer[1][6] = t/10%10+'0'; //两个小数位 display_buffer[1][7] = t%10+'0'; }void Refresh_show3() //刷新显示{uint t=dat_adc0808*500.0/255; //if(t>400||t<100)X[3]=1;elseX[3]=0;display_buffer[1][12] = t/100+'0'; //整数位display_buffer[1][14] = t/10%10+'0'; //两个小数位 display_buffer[1][15] = t%10+'0';}void Refresh1_show0() //刷新显示{uint t=dat_adc0808*500.0/255; //if(t>400||t<100)X[0]=1;elseX[0]=0;display1_buffer[0][4] = t/100+'0'; //整数位display1_buffer[0][6] = t/10%10+'0'; //两个小数位 display1_buffer[0][7] = t%10+'0';}void Refresh1_show1() //刷新显示{uint t=dat_adc0808*500.0/255; //if(t>400||t<100)X[1]=1;elseX[1]=0;display1_buffer[0][12] = t/100+'0'; //整数位display1_buffer[0][14] = t/10%10+'0'; //两个小数位 display1_buffer[0][15] = t%10+'0'; }void Refresh1_show2() //刷新显示{uint t=dat_adc0808*500.0/255; //if(t>400||t<100)X[2]=1;elseX[2]=0;display1_buffer[1][4] = t/100+'0'; //整数位display1_buffer[1][6] = t/10%10+'0'; //两个小数位 display1_buffer[1][7] = t%10+'0';}void Refresh1_show3() //刷新显示{uint t=dat_adc0808*500.0/255; //if(t>400||t<100)X[2]=1;elseX[2]=0;display1_buffer[1][12] = t/100+'0'; //整数位display1_buffer[1][14] = t/10%10+'0'; //两个小数位 display1_buffer[1][15] = t%10+'0'; }void main(){TMOD=0x02;TH0=0x14;TL0=0x00;IE=0x82;TR0=1;Initialize_LCD1602();delay50us(10);while(1){if(AB4==1){AB1=0;AB2=0;AB3=0;adc0808_init();Refresh_show3();Write_LCD_Command(0x80);//设置显示的初始位置LCD_Display(display_buffer[0]); //显示测得的数据AB1=0;AB2=0;AB3=1;adc0808_init();Refresh_show0();Write_LCD_Command(0x80);//设置显示的初始位置LCD_Display(display_buffer[0]); //显示测得的数据 AB1=0;AB2=1;AB3=0;adc0808_init();Refresh_show1();Write_LCD_Command(0xC0);//设置显示的初始位置LCD_Display(display_buffer[1]); //显示测得的数据 AB1=0;AB2=1;AB3=1;adc0808_init();Refresh_show2();Write_LCD_Command(0xC0);//设置显示的初始位置LCD_Display(display_buffer[1]); //显示测得的数据 if(X[0]||X[1]||X[1]||X[2]==1) AB5=1;elseAB5=0;}else{AB1=1;AB2=0;AB3=0;adc0808_init();Refresh1_show3();Write_LCD_Command(0x80);//设置显示的初始位置LCD_Display(display1_buffer[0]); //显示测得的数据 AB1=1;AB2=0;AB3=1; adc0808_init();Refresh1_show0();Write_LCD_Command(0x80);//设置显示的初始位置LCD_Display(display1_buffer[0]); //显示测得的数据 AB1=1;AB2=1;AB3=0; adc0808_init();Refresh1_show1();Write_LCD_Command(0xC0);//设置显示的初始位置LCD_Display(display1_buffer[1]); //显示测得的数据 AB1=1;AB2=1;AB3=1; adc0808_init();Refresh1_show2();Write_LCD_Command(0xC0);//设置显示的初始位置LCD_Display(display1_buffer[1]); //显示测得的数据 if(X[0]||X[1]||X[1]||X[2]==1) AB5=1;elseAB5=0;}}}void Timer0_INT() interrupt 1 {CLOCK=!CLOCK; }。
/************************************************************************ADC0809数据电压表,采用数码管显示,显示000~255* 硬件连接:START,ALE接P2.0,EOC-P2.1 OE-P2.2 CLK -P2.3,CH0接模拟电压输入TEST0*ADDA、ADDB、ADDC接P2.4, P2.5,P2.6,*数据D0--D7依此对应连接P1.0-P1.7*数码管段码接P0口*数码管位码接P3口******************************************************************************* */#include <reg52.H>#define uint unsigned int#define uchar unsigned char#define DIGI P0 //宏定义,将P0口定义为数码管#define SELECT P3 //宏定义,将P3定义为数码管选择口sbit START = P2^0;sbit EOC = P2^1;sbit OE = P2^2;sbit CLK = P2^3;sbit ADDA = P2^4; // ADDA接P2.4sbit ADDB = P2^5; // ADDB接P2.5sbit ADDC = P2^6; // ADDC接P2.6uchar getdata; //定义变量接受AD转换的8位二进制void delay() //延迟函数{ uint ii=200; //若发现数码管闪烁,调节这里即可while(ii--);}uchar disbuffer[8]={0,1,2,3,4,5,6,7}; //定义显示缓冲区void display( ){ uchar i=0,temp,i1;uchar codevalue[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//0~F的字段码表uchar select[]={0x01,0x02,0x04,0x00,0x00,0x00,0x00,0x00};//位选码for(i=0;i<3;i++) //8个数码管轮流显示{ temp= disbuffer[i];i1 = codevalue[temp];DIGI = i1; //选择第i个数码管SELECT = select[i]; //显示idelay();} }/************************************************///定时器初始化void init(){TMOD= 0x12;// 定时器0工作方式2,定时器1工作方式1TH0=246;TL0=246;TH1=(65536-20000)/256;TL1=(65536-20000)%256;EA=1;ET0=1;ET1=1;TR1=1;TR0=1;}/***********************************************///软件延时产生ADC0809的时钟void clk() interrupt 1{CLK=(~CLK);}void displayint(void) interrupt 3 //定时/计数器1中断,数码管显示{TH1=(65536-20000)/256;TL1=(65536-20000)%256;display( );}/************************************************///AD转换函数unsigned char ADC(){char value;ADDA =0;ADDB =0;ADDC =0;START=0;START=1; //地址锁存START=0; //开始转换while(EOC==0);//等待转换结束OE=1;value=P1;OE=0;return value;}/************************************************/void main(){uint temp1;init();while(1){getdata = ADC();//输出转换得到的数据temp1=getdata; //暂存转换结果disbuffer[2]=getdata/100; /*将转换结果转换为10进制数放显示缓冲区*/temp1=temp1- disbuffer[2]*100;disbuffer[1]=temp1/10;temp1=temp1- disbuffer[1]*10;disbuffer[0]=temp1;}}1.在它的基础上,显示用LCD1602或12864;2.在它的基础上,加按键选择实现多路数字电压表。
5 程序源代码本设计的汇编、C语言原程序清单如下:(1)汇编程序;*************************************;测量电压最大为5V,显示最大值为5.00;使用AT89C52单片机,12MHz晶振,P0读入A/D值,P2口为A/D转换控制口;数码管为共阳极连接,P1口为字段码口,P3口为位选口;70H~77H存放采样的8个数据;78H~7BH存放显示数据,依次为个位、十位、百位、当前通道标志值;P3.5作单路显示/循环显示转换按键用,P3.6作单路显示时选择通道按键用;00H位为单路/循环显示控制位,当为0时循环显示,为1时单路显示;*************************************;*************************************;主程序和中断程序人口;*************************************ORG 0000HLJMP STARTORG 0003HRETIORG 000BHRETIORG 0013HRETIORG 001BHRETIORG 0023HRETIORG 002BHRETI;************************************;初始化程序中的各变量;************************************CLEARMEMIO:CLR AMOV P2,AMOV R0,#70HMOV R2,#0DHLOOPMEM:MOV @R0,AINC R0DJNZ R2 , LOOPMEMMOV 20H,#00HMOV A,#0FFHMOV P0,AMOV P1,AMOV P3,A_RET;***********************************;主程序;***********************************START:LCALL CLEARMEMIO ;初始化MAIN:LCALL TEST ;测量一次LCALL DISPLAY ;显示数据一次AJMP MAINNOP ;PC值出错处理NOPNOPLJMP START;******************************;显示控制程序;******************************DISPLAY:JB 00H,DISP11 ;标志位为1,则转单路显示控制子程序MOV R3,#08H ;8路信号循环显示控制子程序MOV R0,#70H ;显示数器初址70 H~77HMOV 7BH,#00H ;显示通过路数初值DISLOOP1:LCALL TUNBCD ;显示数据转为3位BCD码存入7AH、79H、78H MOV R2,#0FFH ;每路显示时间控制在4 ms×255,约1s DISLOOP2:LCALL DISP ;调4位显示程序LCALL KEYWORK1 ;按键检测DJNZ R2,DISLOOP2INC R0 ;显示下一路INC 7BH ;通道显示数加1DJNZ R3,DISLOOP1RETDISP11:MOV A,7BH ;单路显示控制子程序SUBB A,#01HMOV 7BH,AADD A,#70HMOV R0,ADISLOOP11:LCALL TUNBCD ;显示数据转为3位BCD码存入7AH、79H、78H MOV R2,#0FFH ;每路显示时间控制在4msX255DISLOOP22:LCALL DISP ;调4位显示程序LCALL KEYWORK2 ;按键检测DJNZ R2,DISLOOP22INC 7BH ;通道显示数加1RET;********************************;显示数据转为3位BCD码子程序;********************************;显示数据转为3位BCD码存入7AH、79H、78H(最大值5.00V)TUNBCD:MOV A,@R0 ;255/51=5.00 V运算MOV B,#51DIV ABMOV 7AH,A ;个位数放入7AHMOV A,B ;余数大于19H,F0为1,乘法溢出,结果加5CLR F0SUBB A,#1AHMOV F0,CMOV A,#10MUL ABMOV B,#51DIV ABJB F0,LOOP2ADD A,#5LOOP2:MOV 79H,A ;小数后第1位放入79HMOV A,BCLR F0SUBB A,#1AHMOV F0,CMOV A,#10MUL ABMOV B,#51DIV ABJB F0,LOOP3ADD A,#5LOOP3:MOV 78H,A ;小数后第2位放入78HRET;**********************;显示子程序;**********************;共阳显示子程序,显示内容在78H~7BHDISP:MOV R1,#78H ;共阳显示子程序,显示内容在78H~7BH MOV R5,0FEH ;数据在P1输出,列扫描在P3.0~P3.3 PLAY:MOV P1,#0FFHMOV A,R5ANL P3,AMOV A,@R1MOV DPTR,#TABMOVC A,@A+DPTRMOV P1,AJB P3.2,PLAY1 ;小数点处理CLR P1.7 ;小数点显示(显示格式为XX.XX)PLAY1:LCALL DL1MSINC R1MOV A,P3JNB ACC.3,ENDOUTRL AMOV R5,AMOV P3,#0FFHAJMP PLAYENDOUT:MOV P3,0FFHMOV P1,0FFHRET .TAB:DB C0H, F9H,A4H, 0B0H, 99H, 92H, 82H, 0F8H, 80H, 90H, FFH;段码表;***********************;延时程序;***********************DL10MS:MOV R6,#0D0H ;10ms延时子程序DL1:MOV R7,#19HDL2:DJNZ R7,DL2DJNZ R6、DL1RETDL1MS:MOV R4,#0FFH ;513+513≈1msLOOP11:DJNZ R4,LOOP11MOV R4,#0FFHLOOP22:DJNZ R4,LOOP22RET;******************************;电压测量(A/D)子程序;******************************i一次测量数据8个,依次放入70H~77H单元中TEST:CLR A ;模/数转换子程序MOV P2,AMOV R0,#70H ;转换值存放首址MOV R7,#08H ;转换8次控制LCALL TESTART ;启动测试WAIT:JB P3.7,MOVD ;等A/D转换结束信号AJMP W AITTESTART:SETB P2.3 ;测试启动NOPNOPCLR P2.3SETB P2.4NOPNOPCLR P2.4NOPNOPNOPNOPRETMOVD:SETB P2.5 ;取A/D转换数据MOV A,P0MOV @R0,ACLR P2.5INC R0MOV A,P2 ;通道地址加1INC AMOV P2,ACJNE A,#08H,TESTEND ;等8路A/D转换结束TESTEND:JC TESTCONCLR A ;结束恢复端口MOV P2,AMOV A,#0FFHMOV P0,AMOV P1,AMOV P3,ARETTESTCON:LCALL TESTARTLJMP W AIT;**************************;按键检测子程序;***************************KEYWORK1:JNB P3.5,KEY1KEYOUT:RETKEY1 :LCA LL DISP ;延时去抖动JB P3.5,KEYOUTWAIT11:JNB P3.5,W AIT12CPL 00HMOV R2,#01HMOV R3,#01HRETWAIT12:LCALL DISP ;键释放等待时显示用AJMP WAIT11KEYWORK2:JNB P3.5,KEY1JNB P3.6,KEY2RETKEY2:LCALL DISP ;延时去抖动JB P3.6,KEYOUTWAIT22:JNB P3.6,WAIT21INC 7BHMOV A,7BHCJNE A,#08H,KEYOUT11KEYOUT11:JC KEYOUT1MOV 7BH,#00HKEYOUT1:RETWAIT21:LCALL DISP ' ;键释放等待时显示用AJMP WAIT22END(2)C语言//KEY1(P3.5)为单路/循环显示转换按键//KEY2(P3.6)为单路显示时当前通道选择按键//FLAG为单路/循环显示控制位,当为0时循环显示,为1时单路显示#include “reg52.h”#include “intrins.h” //调用_nop_( )延时函数#define ad_con P2 //ADC0809的控制口#define addata P0 //ADC0809的数据口#define disdata P1 //数码管的字段码输出口#define uchar unsigned char#define uint unsigned intuchar number=0x00; //存放单通道显示时的当前通道数sbit ALE=P2^3; //ADC0809的地址锁存信号sbit START=P2^4; //ADC0809的启动信号sbit OE=P2^5; //ADC0809的允许信号sbit EOC=P3^7; //ADC0809的转换结束信号sbit KEY1=P3^5; //循环或单路显示选择按键sbit KEY2=P3^6; //通道选择按键sbit DISX=disdata^7; //小数点位sbit FLAG=PSW^0; //循环或单路显示标志位uchar code dis_7[11]={0xC0,0xF9,0xC0,0xF9,0xC0,0xF9,0xC0,0xF9};//LED的七段数码管的字段码(0-9,灭)uchar code scan_con[4]={0xfe0,0xfd,0xfb,0xf7}; //LED数码管的位选码uchar data ad_data[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};//ADC0809的8个通道转换数据缓冲区uint data dis[5]={0x00,0x00,0x00,0x00,0x00};//前4个为LED数码管的显示缓冲区,最后一个为暂存单元//1ms延时子函数delay1ms(unit t){ uint i,j;for(i=0;i<t;i++)for(j=0;j<120;j++);}//检测按键子函数keytest( ){ if(KEY1==0) //检测循环或单路选择按键是否按下?{FLAG=!FLAG; //标志位取反,循环、单路显示间切换while(KEY1==0);}if(FLAG==1) //单路显示方式,检测通道选择按键是否按下?{if(KEY2==0){ number++; //通道数+1if(number==8) {number=0;}while(KEY2==0) ;}}}//显示扫描子函数scan( ){ uchar k,n;int h;if(FLAG==0) //循环显示子程序{ dis[3]=0x00; //通道值清0for(n=0;n<8;n++) //8路通道{ dis[2]=ad_data[n]/51;//当前通道数据转换为BCD码存入显示缓冲区dis[4]=ad_data[n]%51; //余数(电压小数位)送暂存单元dis[4]=dis[4]*10; //余数×10dis[1]= dis[4]/51; //再除以51,结果取整送十位dis[4]= dis[4]%51; //结果取余送暂存单元dis[4]= dis[4]*10; //余数×10dis[0]= dis[4]/51; //再除以51,结果取整送个位for(h=0;h<500;h++) //每个通道显示1s{for(k=0;k<4;k++) //4位LED扫描显示{disdata=dis_7[dis[k]];if(k==2) {DISX=0;} //点亮小数点P3=scan_con[k];delay1ms(1);P3=0xff;}}dis[3]++; //通道值加1keytest( ); //按键检测}}if(FLAG==1) //单路显示子程序{ dis[3]=number; //当前通道数送通道显示for(k=0;k<4;k++){ disdata=dis_7[dis[k]];if(k==2) {DISX=0;}P3=scan_con[k];delay1ms(1);P3=0xff;}}keytest( ); //检测按键}//ADC0809转换子函数test( ){ uchar m;uchar s=0x00; //初始通道为0ad_con=s; //第一通道地址送ADC0809控制口for(m=0;m<8;m++){ALE=1;_nop_();_nop_(); ALE=0; //锁存通道地址START=1;_nop_();_nop_(); START=0; //启动转换_nop_();_nop_() ;_nop_();_nop_();while(EOC==0); //等待转换结束OE=1;ad_data[m]=addata;OE=0; //读取当前通道转换0数据s++;ad_con=s; //改变通道地址}ad_con=0x00; //通道地址恢复初值}//主函数main( ){ P0=0xff; //初始化端口P2=0x00; P1=0xff; //初始化为0通道P3=0xff;while(1){test( ); //测量转换数据scan( ); //显示数据}}。
#include<reg52.h>#include<intrins.h>sbit st=P2^4;sbit eoc=P3^7;sbit oe=P2^5;sbit ale=P2^3;sbit key1=P3^6;sbit key2=P3^5;unsigned charseg_code[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; unsigned char seg_ale[]={0xf8,0xf9,0xfa,0xfb};unsigned char num[4];/************延时函数**********************/void delay_us(unsigned int delayus){while(delayus--);}void delay_ms(unsigned int delaytime){unsigned int i;unsigned char j;for(i=0;i<delaytime;i++)for(j=0;j<110;j++);}/********************************************//************ADC转换函数*********************/void adc_convert(void){double temp1;unsigned int temp2,temp3;st=0;_nop_();st=1;_nop_();st=0;oe=0;while(eoc==0);oe=1;_nop_();temp1=P0;_nop_();temp1*=492; //基准电压 4.9 Vtemp1/=255;temp2=(unsigned int)temp1%1000;num[2]=(unsigned char)(temp2/100);temp3=(unsigned int)temp2%100;num[1]=(unsigned char)(temp3/10);num[0]=temp3%10;delay_us(1);oe=0;}/*******************************************//**************显示函数*********************/ void dis(void){unsigned char i,b;b=0x01;for(i=0;i<4;i++){if(i==2)P1=seg_code[num[i]]&0x7f;else if(i==3)P1=seg_code[num[i]]&0x7f;elseP1=seg_code[num[i]];P3=~b;b<<=1;delay_ms(5);}}/*******************************************//****************主函数*********************/void main(void){unsigned char i,automatic;unsigned char keynum;int j;for(i=0;i<4;i++)num[i]=0;keynum=0;for(;;){/**************按键扫描******************************/ if(key1==0){delay_ms(1);dis();delay_ms(1);dis();delay_ms(1);dis();delay_ms(1);dis();delay_ms(1);dis();delay_ms(1);dis();delay_ms(1);dis();if(key1==0){if(keynum<3)keynum++;elsekeynum=0;}}if(key2==0) //自动加按键{delay_ms(1);dis();delay_ms(1);dis();delay_ms(1);dis();delay_ms(1);dis();delay_ms(1);dis();if(key2==0){if(automatic==0)automatic=1;elseautomatic=0;}}/***********************************************//****************开启四通道自动切换*************/ if(automatic==1){if(keynum<3){ale=1;_nop_();P2=seg_ale[keynum];num[3]=keynum;_nop_();ale=0;adc_convert();delay_ms(1);for(j=0;j<50;j++){dis();delay_ms(10);if(key2==0){delay_ms(1);dis();delay_ms(1);dis();delay_ms(1);dis();delay_ms(1);dis();delay_ms(1);dis();if(key2==0)break;}}keynum++;}else{ale=1;_nop_();P2=seg_ale[keynum];num[3]=keynum;_nop_();ale=0;adc_convert();delay_ms(1);for(j=0;j<50;j++){dis();delay_ms(10);}keynum=0;}}/****************手动通道切换***********************/ ale=1;_nop_();P2=seg_ale[keynum];num[3]=keynum;_nop_();ale=0;adc_convert();delay_ms(1);dis();delay_ms(6);}}。
/*********************************************************************/
// 八路电压表C程序
// 使用keil C51 ver7.09
// 2004.10.11
/*********************************************************************/
/*使用AT89C52单片机,12MHZ晶振,P0口读入AD值,P2口作AD控制,用共阳LED 数码管
P1口输出段码,P3口扫描,最高位指示通道(0-7)。
*/
#include "reg52.h" //52系列单片机定义文件
#include "intrins.h" //调用_nop_();延时函数用
#define ad_con P2 //AD控制口
#define addata P0 //AD数据计入读入口
#define Disdata P1 //显示数据段码输出口
#define uchar unsigned char//无符号字符(8位)
#define uint unsigned int //无符号整数(16位)
sbit ALE=P2^3; //锁存地址控制位
sbit START=P2^4; //启动一次转换位
sbit OE=P2^5; //0809输出数据控制位
sbit EOC=P3^7; //转换结束标志位
sbit DISX=Disdata^7; //LED小数点
//
//
uchar code dis_7[11]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0xff};
/* 共阳七段LED段码表"0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "不亮" */ uchar code scan_con[4]={0xfe,0xfd,0xfb,0xf7}; //四位列扫描控制字
uchar data ad_data[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};//定义8个数据内存单元uint data dis[5]={0x00,0x00,0x00,0x00,0x00}; //定义4个显示数据单元、1个数据暂存单元
//
//
/********1毫秒延时子函数**********/
delay1ms(uint t)
{
uint i,j;
for(i=0;i<t;i++)
for(j=0;j<120;j++)
;
}
//
//
/***********显示扫描子函数**********/
scan()
{
uchar k,n;
int h;
dis[3]=0x00; //通道初值为0
for(n=0;n<8;n++) //每次显示8个数据
{
dis[2]=ad_data[n]/51; //测得值转换为三位BCD码,最大为5.00V dis[4]=ad_data[n]%51; // 余数暂存
dis[4]=dis[4]*10; //计算小数第一位
dis[1]=dis[4]/51; //
dis[4]=dis[4]%51; //
dis[4]=dis[4]*10; //计算小数第二位
dis[0]=dis[4]/51; //
for(h=0;h<500;h++) //每个通道值显示时间控制(约1秒){
for(k=0;k<4;k++) //四位LED扫描控制
{
Disdata=dis_7[dis[k]];
if(k==2){DISX=0;}
P3=scan_con[k];delay1ms(1);P3=0xff;
}
}
dis[3]++; //通道值加1
}
}
//
//
/*******0809AD转换子函数***********/
test()
{
uchar m;
uchar s=0x00;
ad_con=s;
for(m=0;m<8;m++)
{
ALE=1;_nop_();_nop_();ALE=0; //转换通道地址锁存
START=1;_nop_();_nop_();START=0; //开始转换命令
_nop_();_nop_();_nop_();_nop_(); //延时4微秒
while(EOC==0); //等待转换结束
OE=1;ad_data[m]=addata;OE=0;s++;ad_con=s;//取AD值,地址加1
}
ad_con=0x00; //控制复位
}
//
//
/**************主函数****************/
main()
{
P0=0xff; //初始化端口
P2=0x00;
P1=0xff;
P3=0xff;
while(1)
{
scan(); //依次显示8个通道值一次
test(); //测量转换一次
}
}
//
//
//*********************结束**************************//。