zoukankan      html  css  js  c++  java
  • 自适应量程数字电压表设计总结

    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-2分别为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 }
  • 相关阅读:
    14.18 InnoDB Backup and Recovery 备份和恢复:
    14.18 InnoDB Backup and Recovery 备份和恢复:
    php使用 _before_index() 来实现访问页面前,判断登录
    php使用 _before_index() 来实现访问页面前,判断登录
    查询方式实例演示
    查询方式实例演示
    haproxy timeout server 46000 后台超时时间
    haproxy timeout server 46000 后台超时时间
    14.10.5 Reclaiming Disk Space with TRUNCATE TABLE 回收空间使用TRUNCATE TABLE
    14.10.5 Reclaiming Disk Space with TRUNCATE TABLE 回收空间使用TRUNCATE TABLE
  • 原文地址:https://www.cnblogs.com/iwuqing/p/11737379.html
Copyright © 2011-2022 走看看