1. 任务需求

  • 量程:直流电压0~20V
  • 三档:0~200mV,200mV~2V,2V~20V
  • 精度:0.01,显示稳定,无闪烁
  • 误差:0.2V挡位≤10%,2V和20V挡≤1%

2. 需求分析

  • 直流电压表(0~20V):利用数码管,通过数字方式显示测定直流电压值,其范围为0~20V
  • 自动量程转换:0~20V直流电压分为三档:0~0.2V,0.2V~2V,2V~20V。且能根据0~20V测试电压的具体值来自动切换量程

3. 设计方案

3.1. 总概述

首先,我们了解到测试电压为0~20V的直流电压。现我们使用的ADC0809芯片只能处理0~5V电压。故,我们先需要将0~20V电压5分压至0~4V,即满足了ADC0809芯片处理范围。然后STC89C52接收到数字量后,在程序中判断当前量程是否合适。若是,则将电压值输出到数码管上显示。否,则反馈调节数据选择器的输出口去调节挡位,直到量程合适。总体设计图如3-1

图3-1

3.2. 电压放大部分

这一部分属于信号预处理部分,实际上电压表的误差主要出现在这里。首先,我了解到ADC0809为八位A/D转换芯片,量化模拟电压值范围为0~5V。我们测定电压范围为0~20V,故我首先要将其5分压至0~4V以便满足其需求。而为了精确测定,我需要将5分压后较小的两个量程0~0.2、0.2~2分别放大100倍、10倍。此处,我借助同相比例放大器。三个挡位增益由小到大根据图3-1分别为1、1+R10/R9、1+R11/R9。同时,为了满足自动转换这一需求,我借助74HC4051芯片——八选一数据选择器。将其数据地址口A、B分别与单片机P1.4、P1.5相连。在单片机内部程序中,根据ADC0809送入数字量判断当前量程是否合适,若不合适改变数据选择器的地址输入,即可完成自动量程转换这一需求。

图3-2

3.3. 模数转换部分

主要元器件为ADC0809与74LS74。这一部分属于整个电路中最最为关键的部分,但是实际上设计与操作来说,并不是特别复杂。因为ADC0809芯片集成度很高,我只需要将其启动转换端口,转换完成标志端口等将其与单片机STC89C52相连接,并且在程序内编写相关语句就可以完成对ADC0809芯片的控制,即可完成A/D转换的处理。而74LS74芯片是一个双D触发器。由于ADC0809内部并没有晶振电路,所以,其需要时钟信号,并且要求范围在0~640Khz之间。这次我使用的STC89C52芯片晶振频率为6Mhz,其ALE口输出为1Mhz,故使用D触发器进行二分频可得到500Khz时钟信号,即完成ADC0809芯片的正常运转。

 

图3-3

3.4. 数码管显示部分

 在位选部分我选择了P0口,值得注意的一点是:P0口在作为通用I/O使用时,需要接入上拉电阻并且手动先置高电平。并且,我使用了共阴极七段数码管,所以在位选个个端口要接入非门。在段选部分,我使用P2.0~3,使用了4511译码器,即可完成段选数据的输入。小数点则单独使用了P2.7口,在单片机程序内判断是否点亮它。

图3-4

4. 仿真

2~20V:测试用量5V

 

0.2~2V:测试用量1.5V

 

0~0.2:测试用量0.1V

5. 调试与测试

5.1. 几个问题

问题一:如何降低同相比例放大器处的误差?(此处误差为电压表误差最主要之处)

问题二:如何保证数码管处焊接无错误(此处为电路中最难焊接之处)

问题三:如何确保ADC0809芯片处于正常工作状态

5.2. 问题分析

问题一:由于定值电阻并不是完全准确,所以在同相比例放大器处放大倍数并不是在仿真中那样完全理想。故会产生较大的误差。

问题二:数码管处在段选部分,我们使用单个I/O控制,故此我们需要将每一个数码管对应的引脚并连。线路并不复杂,但极易出现短路、短路、链接错误等情况。

问题三:ADC0809正常工作有比较多的需求。首先,需要有0~640kHZ的时钟信号,其次,单片机内部程序要对其正确的操作。

5.3. 解决方法

问题一:采用电位器,在整个电路焊接好之后,调节电位器来减小误差。

问题二:编写测试文件,即单片机程序送出一个固定的数字,若数码管显示数字及小数点符合预期要求,即数码管处连接正确。反之,则有错误。

问题三:实际上来说这个问题并不能直接调试,因为假定写入完整的程序,数码管是固定的数字,并无法保证是其出错。我的方法是:写入完整的程序后,给定1V测试电压。在预期方案中1V属于0.2~2V量程,则此刻4051选择通道2,则此时其数据地址A为高,B为低。若是如此,则ADC0809完成了正常的数据采集操作,因为A之所以为高,B之所以为低是单片机程序作用的结果,而单片机只有接收到了正确的值才会做这个操作。故反推ADC0809是正常工作的。

6. 参考文献

汪文、陈林. 单片机原理及应用[M]. 湖北:华中科技大学出版社.

7. 附录1(测量数据)

8. 附录2(源码)

  1 #include<reg52.h>
  2 sbit start = P1^0; //启动A/D转换
  3 sbit eoc = P1^1;    //转换结束信号输出端
  4 sbit oe = P1^2;        //输出允许
  5 sbit ale = P1^3;    //地址锁存允许
  6
  7 //自动挡位选择输入
  8 sbit a = P1^4;
  9 sbit b = P1^5;
 10
 11 sbit point = P2^7;
 12
 13
 14 unsigned int W_temp_data[4] = {0x08, 0x04, 0x02, 0x01};//位选数据
 15 unsigned int D_temp_data[8] = {0,0,0,0,0,0,0,0}; //段选数据
 16
 17 unsigned long int temp_data = 0; //ADC0809输出数字量
 18
 19 /*********************
 20  *       软件延时     *
 21  *********************/
 22 void deley(unsigned int x)
 23 {
 24     unsigned int i,j;
 25     for(i=0; i<x; i++)
 26     {
 27         for(j=0; j<100; j++)
 28         {
 29             ;
 30         }
 31     }
 32 }
 33
 34 /*********************
 35  *       显示函数        *
 36  *********************/
 37 void display(unsigned int x)
 38 {
 39     unsigned int i, j;
 40     for(j = 0; temp_data ; j ++)
 41     //将数字量每一位取出放入D_temp_data数组中
 42     {
 43         D_temp_data[j] = temp_data % 10;
 44         temp_data = temp_data / 10;
 45     }
 46     for(i = 0; i < 4; i ++)
 47     {
 48
 49         //前导零消除
 50         if(x == 0 && i == 3 && D_temp_data[i] == 0)
 51         {
 52             break;
 53         }
 54         if(x == 2)
 55         {
 56             if(D_temp_data[i] == 0 && i == 2 && D_temp_data[3] == 0)
 57             {
 58                 break;
 59             }
 60             if(D_temp_data[i] == 0 && i == 3)
 61             {
 62                 break;
 63             }
 64         }
 65         P0 = W_temp_data[i];
 66         P2 = D_temp_data[i];
 67         //小数点显示
 68         if(x == 0)
 69         {
 70             if(i == 2)
 71             {
 72                 point = 1;
 73             }
 74         }
 75         else if(x == 1)
 76         {
 77             if(i == 3)
 78             {
 79                 point = 1;
 80             }
 81         }
 82         else if(x == 2)
 83         {
 84             if(i == 1)
 85             {
 86                 point = 1;
 87             }
 88         }
 89         deley(1);
 90     }
 91 }
 92
 93 /*********************
 94  *       挡位选择       *
 95  *    x: 0 *1挡        *
 96  *       1 *10挡        *
 97  *       2 *100挡        *
 98  *********************/
 99 void choose(unsigned int x)
100 {
101     switch(x)
102     {
103         case 0:
104             a = 0;
105             b = 0;
106             break;
107         case 1:
108             a = 1;
109             b = 0;
110             break;
111         case 2:
112             a = 0;
113             b = 1;
114             break;
115         default:
116             ;
117     }
118 }
119
120 void main()
121 {
122     unsigned int x = 0; //量程控制
123     while(1)
124     {
125         ale = 1;
126         deley(1);
127         ale = 0;
128         choose(x);
129         start = 1;
130         deley(1);
131         start = 0; //下降沿启动A/D转换
132         while(eoc == 0)
133         //转换结束
134         {
135             ;
136         }
137         oe = 1; //输出允许
138         temp_data = P3;
139         oe = 0; //输出阻塞
140         if(x == 0 && temp_data < 21)
141         //量程过高,切换至0.2-2V
142         {
143             x = 1;
144             continue;
145         }
146         if(x == 1 && temp_data < 21)
147         {
148         //量程过高,切换至0-0.2V
149             x = 2;
150             continue;
151         }
152         if(x ==1 && temp_data > 204)
153         //量程过低,切换至2-20V
154         {
155             x = 0;
156             continue;
157         }
158         if(x == 2 && temp_data > 204)
159         //量程过低,切换至0.2-2V
160         {
161             x = 1;
162             continue;
163         }
164         temp_data = temp_data * 100 * 5 /51;
165         display(x);
166     }
167 }

 

 

 

 

 

01-11 11:46