找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 5335|回复: 1
打印 上一主题 下一主题
收起左侧

51单片机的ADC0808数字电压表设计文档下载

[复制链接]
跳转到指定楼层
楼主
ID:442669 发表于 2018-12-13 11:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

基于单片机的数字电压表的设计


摘要


近些年来,数字电压表逐渐进入人们的视线中,它主要采取数字化测量技术。与传统的指针式仪表相比,它的功能有了进一步的完善,并且精度也有了很大的提高。数字电压表主要采用单片机和模/数转换模块,这样不仅提高了测量速度,而且抗干扰能力强、使用便捷、可扩展性强、测量准确。

本文主要采用AT89C51单片机和ADC0808芯片制作的简易数字电压表,可以采集0~5V和5~10V的模拟直流电压进行测量,其测量结果在液晶LCD1602上显示。该设计硬件电路主要有三个模块组成:A/D转换模块、数据处理模块及输出显示模块。数据处理由单片机AT89C51来完成,它主要把ADC0808传送来的数值经一定的数据处理,然后送至显示模块进行显示,同时控制显示芯片1602的工作。程序设计上有各模块初始化操作、电压档位选择和LCD1602液晶显示程序等。



系统硬件设计总体方案

1.1    设计要求

以MCS-51单片机为关键部件,制作一个简易的数字电压表。

  • 使用双通道可选择直流电压输入,能够测量0-5V5-10V之间的直流电压。当电压值超过5V时,选择通道25-10V通道)采集电压。
  • 使用LCD1602来完成电压的液晶显示。
  • 使用较少的元器件,尽可能降低功率损耗,同时准确、快速完成测量。
  • 由于电压表允许过载,因此所测电压允许适当超过量程。
1.2    设计方案

总体设计电路有以下几部分组成:AT89C51单片机、A/D转换电路、液晶LCD显示电路、时钟电路、复位电路、被测电压输入电路及量程选择和报警电路。总体硬件设计框图如图1-1所示:

图1-1 硬件电路设计框图


1.3   2.      系统硬件电路设计2.1    A/D转换模块

日常生活中的物理量都是模拟量,为了能够方便的分析各个量,就需要把模拟量转换成数字量的器件。现在越来越多的设备都需要进行模数转换,把复杂的模拟信号转换成已明白的数字信号,因此A/D转换器也得到了更深一层的研究。按照不同的A/D转换芯片的转换原理可把其分为逐次逼近行、双积分型等。其中双积分式A/D转换器抗干扰能力强、转换精度高、价格便宜。但与双积分相比,逐次逼近式A/D转换的转换速度更快,而且精度更高,它们可以与单片机系统连接,将数字量送入单片机进行分析和显示。一个n位的逐次逼近型转换器只需要比较n次,这样大大节省了时间,而且逐次逼近型转换速度快,因而在实际中得到广泛的使用。

由于ADC0808芯片采用逐次逼近式A/D转换原理,因此本设计便运用其进行模/数转换,它可以实现8路模拟信号的选择采集,而且它的转换时间为100us。此次使用的是12MHz的晶振,因此它能够提供转换的时钟即满足设计的需要。

对于n位的A/D转换器,它的分辨率是满量程输入电压和2n之比。ADC0808的满量程为5V。则其分辨率为0.02V。

ADC0808内部结构主要有8路模拟通道选择开关、地址锁存与译码器和8位A/D转换器、三态输出锁存器等构成,其引脚及连接电路如图2-1所示:

图2-1 ADC0808引脚图

由图2-1所示,其中IN0~IN7为模拟量输入通道,其输入电压范围均为0~5V。此次设计分为两通道输入分别为IN0(0-5V)和IN1(5-10V),并且采用开关K1、K2来选择。A、B、C为模拟量输入通道的选择端,是与单片机的P1相连并通过软件的编程产生控制信号。ALE、START为地址锁存允许信号和转换启动信号,它们都有单片机P3口产生控制信号。转换器的CLK由单片机的中断程序产生,主要是完成输入数据的扫描。EOC是ADC0808转换结束信号端口,只有等到EOC变为高电平,数据转换才结束。实现这个过程,就需要使用程序设计来完成。OE是输出允许信号,只有OE为低电平时,才能输出转换得到的数据。程序中先让OE为0,然后为1,这样把数据送入单片机P2口。VREF(+)、VREF(—)是芯片的电源接口。

2.2    电压输入电路

ADC0808的基准电压为+5V,所以当要测量的电压值超过5V时则需要由开关选择衰减电路,经过衰减后输入转换采集器。输入电路如图2-4所示:

图2-4 输入电路图

当要测试的电压为0~5V时,选择IN0输入,在仿真中运用滑动变阻器分压的原理产生0-5V的电压来代表实际电压。而当超过5V时则选择左边的电路,由于实际的电压变允许适当的超量程,所以图中R5和R6的电阻值分别为6k、3k,这样就把电压衰减为原来电压值的1/3,同时由于RV5分压的原理可以得到5~10V的电压来模拟实际要测试的电压值。同时其最高测量电压允许适当超过10V。这样本电压表就有两个量程即0~5V和5V~10V。需要变换量程时,由选侧开关K1、K2相互切换。由于本此设计采用手动调节电压档位的方式,因此在测量电压时应该先对被测电压进行估算,同时先由较大量程进行测量,如果值过小,再调节档位。不然不容易超量程损坏仪器。直流电压输入时,由于尖峰的出现,也就需要对输入的电压进行滤波,电容C4、C5在次的作用就是进行滤波


2.3    接口电路

对于本设计,主要的接口电路有时钟电路、复位电路、电压量程选择和报警电路。

2.4.1时钟电路

单片机89C51芯片中有一个内部时钟,其中引脚18为输入端,19为输出端引脚,这两个引脚连接一个12MHz的晶振,同时再连接两个瓷片电容,这样便提供片内相移的条件,时钟电路如图2-5所示:

图2-5 时钟电路图

由图2-4可知,晶体的振荡频率通常取取12MHz,对于11.0592MHz一般在单片机串行通信时使用。这时单片机一个时钟周期为:

            (2-1)

图电容C1和C2,它们和晶振的主要作用是结合单片机内部振荡电路实现相位的180°移相,这样晶振才能够起振。同时对电路中所需的电容储电量要求不高,均为30pF。若过高或过低都会对振荡产生影响。

2.4.2 复位电路

AT89C51单片机的RST为复位引脚,复位信号高电平有效,并且其有效时间应该延续出现2个机器周期以上即可确保系统复位,复位操作完成后,RST端一直保持高电平,那么单片机就始终处于复位状态,当RST恢复低电平后单片机才能进入其他操作。单片机复位电路有几种类,本次设计主要采用手动复位电路,这样可以人为的操作,简单方便。电路如图2-6所示:

图2-6 复位电路

由上图2-6可知,只有RST端维持2个周期以上高电平才能完成复位操作。电容C3两端在单片机启动时持续充电为5V,由于按键未按下,电阻R1两端电压为0,此时RST处于低电平系统工作正常,当按键摁下时,电阻R2所在的支路导通,与C3形成一个回路,电容C3开始释放之前所充的电量,在很短的时间内,其电压值由5V变为1.0V,甚至更小些,与此同时,RST又收到高电平,这时系统自动复位。

2.4.3量程选择和报警电路

本设计由于采用双通道输入的方式,所以可以选择不同的量程,分别为0~5V和5~10V。这个操作由选择开关K1、K2来完成,但是如果K1、K2同时闭合,报警提示灯点亮。电路如图2-7所示:

图2-7 量程选择和报警电路图


2.4    LCD1602显示电路设计

本次设计中采用LCD1602作为显示器,与以前的LED数码管显示相比,其显示模块具有体积小、功耗低、显示内容丰富等优点,而且不需要外加驱动电路。同时可以满足不同的输入、移位要求,而且接口方式简单、可靠。LCD1602模块的引脚及连接电路如图2-8所示:

图2-8 LCD1602引脚

其中D0~D7数据接收端口与单片机P0口相连。E端为使能端,当它由0变为1,LCD1602才能进行读写操作,它和单片机P3.5相连,并由其输出控制信号。RS、RW是1602的读写控制端,它们分别与单片机的P3.6、P3.7相对应,这样使用LCD显示时可由单片机的程序完成控制。VDD、VSS为液晶屏的电源端口,VEE端电压信号的大小可以改变液晶屏的亮度。由于P0口作为输出口时,它没有高电平的状态所以仿真电路时需要加上拉电阻RP1,这样P0口就有高电平状态。


3.      系统程序设计

系统的主程序流程如图3-1所示:

图3-1 系统主程序流程图

本设计程序设计主要分为几个模块:初始化程序设计、A/D采样程序设计、测量参数数据处理程序设计、量程选择和报警程序设计、LCD1602显示程序设计。下面逐个介绍各个模块的程序设计。

3.1    初始化程序

所谓初始化,将利用到单片机内部各部件或扩展芯片进行初始工作状态设定,其主要负责设置定时器模式、初始设定、开中断和打开定时器等,对于液晶1602同样也要进行初始化,其中包括清除显示屏、显示开/关控制、功能设置、进入模式设置等。其中部分初始化程序如下:

w_comd(0x0c);                                          //开显示屏,关光标;

w_comd(0x06);                                          //字符进入模式:屏幕不动,字符后移;

ET0=1;                                                                      //开定时中断;

3.2    A/D转换程序

模/数转换流程图如图3-2所示。


图3-2 A/D转换流程图

由图可知,A/D转换程序首先定义启动信号、输出允许信号、输入地址锁存信号、A/D转换结束信号及CLK时钟信号的变量。然后利用AT89C51中定时器T0的工作方式2产生CLK信号,供A/D转换器使用,START信号的上升沿启动A/D转换,等待转换结束,即EOC从0变为1,同时OE是输出使能信号端,其信号从高到低电平,输出转换数据并将其进行数值转换分别求出百、十、个位,再送入LCD进行数据显示。

3.3    LCD1602显示程序3.3.1 LCD1602初始化

液晶LCD初始化主要就是在液晶显示器的每一个寄存器的初始设置,也就是向LCD中的各个寄存器写入要设定的数据。该设计的初始化过程为先上电,然后进行判忙操作,最后再进行各个功能的设置,其中包括显示状态的设置(行、位的起始位置)、输入方式的设置。初始化过程如图3-5所示:

图3-5 LCD初始化流程图

该设计主要使用了LCD1602的读忙操作、写数据操作、写命令操作和写字符操作。其中每个操作都需要使能端RW、RS的控制信号,当RS、RW均为0的情况下,可以进行读、写操作,而读忙只有RS=0、RW=1时,才能进行此操作。LCD1602如果要显示字符,首先要写入显示字符的首地址,此次使用的是从第一行第四个字符04地址开始显示,但是液晶写数据操作时地址最高位D7必须为高电平,因此写入数据的时候应该是00000100(04H)+10000000(80H)=10000100(84H)。


4.1    显示结果及误差分析4.2.1 显示结果
  • 当输入电压为4.55V时,显示结果如图4-1 所示,实际电压为4.54V

图4-1 输入电压为4.55V时,LCD显示结果


  • 当输入电压为9.97V时,显示结果如图4.2所示,实际电压为9.96V


4.2.2 误差分析

通过对输入不同的电压进行测试,得到了仿真数据。可得出两者的对比测试表,如下表4-1所示:

4-1 简易电压表与“标准”电压表对比测试表

标准电压/V

电压表测量值/V

绝对误差/V

1.50

3.50

4.50

5.00

7.45

8.71

9.97

1.50

3.49

4.49

5.00

7.44

8.70

9.96

0.00

0.01

0.01

0.00

0.01

0.01

0.01


从表4-1中的几组数据的分析,测试电压误差以0.01V的幅度变化。这主要是硬件本身的误差导致。由于单片机AT89C51、ADC0808数据传输端口为8,当输入电压为5.00V时,输出端口的数据为11111111(FFH),所以ADC0808的最高分辩率为0.0196V(5/255)。这就决定了电压表的最高分辩率只能到0.0196,因此测试电压通常以0.01的幅度变化。

该数字电压表所测得的电压值和标准的电压相比,大概有0-0.01V的偏差。由于硬件方面的原因,此误差只能通过硬件上的完善才能得以校正。因为该电压表设计时用的是5V的供电电源作为基准电压,所以电压可能出现误差。如果要测量大于5V的电压时,应当使用分压电路,程序中对计算结果进行调整就可以了。

通过多次的仿真和调整,此次设计的电压表的绝对误差为0~0.01V,因此本次毕业设计符合最初设计要求。

附录一 系统硬件设计总图


单片机源程序如下:


  1. #include<reg51.h>
  2. #define uchar unsigned char
  3. #define  uint unsigned int
  4. sbit OE=P3^2;                                                                                    //AD数据输出控制端口
  5. sbit EOC=P3^3;                                                                      //数据转换结束由0变1
  6. sbit START=P3^4;                                                                      //AD转换启动  ALE接口
  7. sbit CLK=P3^1;
  8. sbit  rw=P3^6;                                                                      //1602读写选择
  9. sbit  rs=P3^5;                                                                                    //1602寄存器选择端
  10. sbit  en=P3^7;                                                                      // //1602读写使能端
  11. sbit a=P1^0;                                                                                    //a,b,c为转换器通道选择地址
  12. sbit b=P1^1;
  13. sbit c=P1^2;
  14. sbit d=P1^5;                                                                                                  //报警信号端
  15. sbit K1=P1^6;                                                                                                  //量程选择开关
  16. sbit K2=P1^7;                                                                                   
  17. uchar dis[4]={0,0,0,0};                                                                      //缓存数据处理结果
  18. void delay(uchar i)                                                                      //延时程序
  19. { uchar n;
  20.   while(i--)
  21.     {
  22.   for(n=0;n<250;n++)
  23.      ;
  24.     }
  25. }

  26. bit lcd_busy_check()                                                        //1602状态检测是否忙碌
  27. { bit  result;
  28.   rs=0;
  29.   rw=1;
  30.   en=1;
  31.   delay(10);
  32.   result=(bit)(P0&0x80);
  33.   en=0;
  34.   return  result;
  35. }

  36. void w_comd(uchar command)                                            //写LCD命令
  37. {
  38.   while(lcd_busy_check());
  39.   rw=0;
  40.   rs=0;
  41.   en=0;
  42.   P0=command;
  43.   delay(10);
  44.   en=1;
  45.   delay(10);
  46.   en=0;
  47. }

  48. void  vWriteData(uchar dat)                                             //向1602发送数据
  49. {
  50.   while(lcd_busy_check());
  51.   rs=1;
  52.   rw=0;
  53.   en=0;
  54.   P0=dat;
  55.   delay(1);
  56.   en=1;
  57.   delay(1);
  58.   en=0;
  59. }


  60. lcd_init()                                                                                                     //LCD1602初始化
  61. {
  62.   w_comd(0x38);
  63.   delay(5);
  64.   w_comd(0x0c);                //开显示屏,关光标
  65.   delay(5);
  66.   w_comd(0x06);    //字符进入模式:屏幕不动,字符后移
  67.   delay(5);
  68.   w_comd(0x01);  //清屏

  69. }


  70. void show_lcd_char(uchar ucChar)                                                        //LCD1602字库表
  71. {
  72.               switch(ucChar)
  73.                             {
  74.                             case ' ': vWriteData(0x20);break;
  75.                             case '!': vWriteData(0x21);break;
  76.                             case '"': vWriteData(0x22);break;
  77.                             case '#': vWriteData(0x23);break;
  78.                             case '[/p]完整的Word格式文档51黑下载地址:
  79. 51单片机的数字电压表设计.rar (229.46 KB, 下载次数: 106)



  80. : vWriteData(0x24);break;
  81.                             case '%': vWriteData(0x25);break;
  82.                             case '&': vWriteData(0x26);break;
  83.                             case '>': vWriteData(0x27);break;
  84.                             case '(': vWriteData(0x28);break;
  85.                             case ')': vWriteData(0x29);break;
  86.                             case '.': vWriteData(0x2E);break;

  87.                             case '0': vWriteData(0x30);break;
  88.                             case '1': vWriteData(0x31);break;
  89.                             case '2': vWriteData(0x32);break;
  90.                             case '3': vWriteData(0x33);break;
  91.                             case '4': vWriteData(0x34);break;
  92.                             case '5': vWriteData(0x35);break;
  93.                             case '6': vWriteData(0x36);break;
  94.                             case '7': vWriteData(0x37);break;
  95.                             case '8': vWriteData(0x38);break;
  96.                             case '9': vWriteData(0x39);break;

  97.                            
  98.                             case 'V': vWriteData(0x56);break;

  99.                             case 'a': vWriteData(0x61);break;
  100.                             case 'd': vWriteData(0x64);break;
  101.                             case 'e': vWriteData(0x65);break;
  102.                             case 'f': vWriteData(0x66);break;
  103.                             case 'g': vWriteData(0x67);break;
  104.                             case 'h': vWriteData(0x68);break;
  105.                             case 'i': vWriteData(0x69);break;
  106.                             case 'j': vWriteData(0x6A);break;
  107.                             case 'k': vWriteData(0x6B);break;
  108.                             case 'l': vWriteData(0x6C);break;
  109.                             case 'm': vWriteData(0x6D);break;
  110.                             case 'n': vWriteData(0x6E);break;
  111.                             case 'o': vWriteData(0x6F);break;
  112.                             case 'p': vWriteData(0x70);break;
  113.                             case 'q': vWriteData(0x71);break;
  114.                             case 'r': vWriteData(0x72);break;
  115.                             case 's': vWriteData(0x73);break;
  116.                             case 't': vWriteData(0x74);break;
  117.                             case 'u': vWriteData(0x75);break;
  118.                             case 'v': vWriteData(0x76);break;
  119.                            

  120.                             default: break;
  121.                             }
  122. }

  123. void show_lcd(uchar xschar[])                                          //显示字符
  124. {
  125.   uchar i;
  126.               for(i=0;;i++)
  127.                             {
  128.                             show_lcd_char(xschar[i]);
  129.                             if(xschar[i+1]=='\0')
  130.                                           break;            
  131.         }                           
  132. }

  133. void v_display0(uint getdata)                                            //显示数据
  134. {
  135.    uint m,i;
  136.   m=getdata*1.0/255*500;
  137.   dis[0]=m/100;                                                                        //把计算的数据每个位存入数组
  138.   dis[1]=m/10%10;
  139.   dis[2]=m%10;
  140.   for(i=0;i<3;i++)                                             //数据逐一输出
  141.   {
  142.    show_lcd_char(dis[i]+48);

  143.    if(i==0)
  144.        show_lcd_char('.');
  145.   }
  146. }

  147. void v_display1(uint getdata)                                            //显示数据
  148. {
  149.    uint m,i;
  150.   m=getdata*1.0/255*500*3;
  151. if(m>=1000)
  152.   {
  153.      dis[0]=m/1000;                                                                        //把计算的数据每个位存入数组
  154.      dis[1]=m/100%10;
  155.      dis[2]=m/10%10;
  156.               dis[3]=m%10;
  157.      for(i=0;i<4;i++)                                             //数据逐一输出
  158.    {
  159.    show_lcd_char(dis[i]+48);

  160.    if(i==1)
  161.        show_lcd_char('.');}
  162.   }
  163.   else
  164.   {
  165.      dis[0]=m/100;                                                                        //把计算的数据每个位存入数组
  166.      dis[1]=m/10%10;
  167.      dis[2]=m%10;
  168.   for(i=0;i<3;i++)                                             //数据逐一输出
  169.    {
  170.    show_lcd_char(dis[i]+48);

  171.    if(i==0)
  172.        show_lcd_char('.');
  173.    }
  174.   }
  175. }


  176. void main()
  177. {
  178.   TMOD=0x01;                                            //定时器0,模式1
  179.   TH0=0xfa;
  180.   TL0=0x00;
  181.   EA=1;                                                                                    //开总中断
  182.   ET0=1;                                                        //开定时中断            
  183.   ET1=1;
  184.   TR0=1;                                                        //启动定时中断
  185.   d=0;

  186.   lcd_init();          //初始化

  187.   w_comd(0x84);                                                         //显示起始地址
  188.    delay(10);
  189.   show_lcd("Voltage:");

  190.   w_comd(0xc9);
  191.   delay(10);
  192.   show_lcd("(V)");

  193.   while(1)
  194.   {
  195.   w_comd(0xc4);
  196.   d=0;
  197.   if((K1==0)&&(K2!=0))                                          //测量0~5V电压
  198.   {a=0;
  199.   b=0;
  200.   c=0;
  201.   START=0;
  202.   OE=0;               
  203.   START=1;                                                        //启动AD转换

  204.   START=0;
  205.   while(EOC==0);                               //等待转换结束
  206.   OE=1;
  207.    v_display0(P2);
  208.   OE=0;
  209. ……………………

  210. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码

完整的Word格式文档51黑下载地址:




评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶2 踩
回复

使用道具 举报

沙发
ID:540911 发表于 2020-4-13 15:35 | 只看该作者
值得参考,很棒
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表