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 }