OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 8413|回复: 17

基于51单片机的直流数字电压表(0-500V)自动切换量程第三版

[复制链接]

6

主题

36

帖子

0

精华

初级会员

Rank: 2

积分
160
金钱
160
注册时间
2016-4-2
在线时间
33 小时
发表于 2016-5-2 15:11:42 | 显示全部楼层 |阅读模式
实现功能:基于51单片机的0-500V自动切换量程数字电压表,测量精度<1%.
电路组成:100:1的电阻分压网络,TLC2543(12位)A/D转换芯片进行模拟信号采样,LM339电压比较器电路实现量程分档切换,51单片机进行数据处理,1602液晶显示电压及A/D转换数据值。
不足之处:电阻分压网络估计精度不是很高,上网查了下,99K,9K电阻误差一般1%左右,不像proteus一样一点误差都没有;还有个问题是,当在显示中测量100V以上的电压时,电压太大,对电路有危险,所以应当加保护电路,可我不知道怎样加最合适,希望网友能给点建议,最好是有曾经做过实物的建议。

电路原理图

电路原理图
QQ截图20160502145855.png
QQ截图20160502145935.png
QQ截图20160502150012.png
QQ截图20160502150054.png

基于51单片机的交直流数字电压表第三版.zip

1.19 MB, 下载次数: 1276

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

36

主题

249

帖子

0

精华

高级会员

Rank: 4

积分
680
金钱
680
注册时间
2015-12-13
在线时间
243 小时
发表于 2016-5-2 18:50:01 | 显示全部楼层
量程切换用可以软件来解决,检测某一路ADC的值大于设定值就切换下一路……如果仅仅是单一电压检测功能,模拟开关也可以省略…… 输入用运放接成差分模式,几个运放的输入并联在一起,设定不同的放大倍数,输出分别接单片机的各路AD输入就可以了!
回复 支持 1 反对 0

使用道具 举报

6

主题

36

帖子

0

精华

初级会员

Rank: 2

积分
160
金钱
160
注册时间
2016-4-2
在线时间
33 小时
 楼主| 发表于 2016-5-2 15:13:19 | 显示全部楼层
电路原理图放错了,这个才是对的
QQ截图20160502151219.png
回复 支持 反对

使用道具 举报

6

主题

36

帖子

0

精华

初级会员

Rank: 2

积分
160
金钱
160
注册时间
2016-4-2
在线时间
33 小时
 楼主| 发表于 2016-5-2 15:30:24 | 显示全部楼层
#include<reg52.h>
#include<intrins.h>
#define uint unsigned int
#define uchar unsigned char
uint LCD[6];   //电压数据转换成LCD1602液晶字符显示
uint Volt;
//***********TLC549管脚配置*********//
//sbit DO  = P1^0;        //时钟
//sbit CS  = P1^1;        //片选
//sbit CLK = P1^2;        //数据输出
//*********************************//
sbit RS = P2^0;                        //1602液晶数据/命令选择端
sbit RW = P2^1;                        //1602液晶读写端
sbit E  = P2^2;                        //1602液晶使能端
sbit X1 = P2^3;
sbit X2 = P2^4;
sbit X3 = P2^5;
sbit CS=P1^2;        //片选引脚
sbit CLK=P1^3;       //时钟脉冲引脚
sbit SDO=P1^0;       //数据输出引脚
sbit SDI=P1^1;       //数据输入引脚
sbit EOC=P1^4;                 //转换结束引脚
//*********ADC0809管脚配置************//
//sbit ALE = P1^3;        //地址锁存允许信号
//sbit CLK = P1^4;        //时钟脉冲输入端
//sbit ST = P1^5;                //A/D转换启动信号
//sbit EOC = P1^6;        //A/D转换结束信号
//sbit OE = P1^7;                //数据输出允许信号
//sbit ADD_A = P1^0;        //地址选择输入端
//sbit ADD_B = P1^1;
//sbit ADD_C = P1^2;
//************************************//
// Function Prototypes
void check_busy(void);
void write_command(uchar com);
void write_date(uchar date);
void LCD_init(void);
void string(uchar ad ,uchar *s);
//void lcd_test(void);
void delay(uint);
void Volt_To_LCD1602(void);
void delay_ms(uint xms);
void Data_Conversion(void);
//uchar TLC549_ADC(void);
/*******************************************
    LCD1602 Driver mapped as IO peripheral
*******************************************/  
// Delay
// Delay
void delay(uint j)
{
        uchar m = 60;
  for(; j>0; j--)
  {
                while(--m);
    m = 59;
    while(--m);
    m = 60;
  }
}

// Test the Busy bit
void check_busy(void)
{
        do
  {
                P0 = 0xff;
    E = 0;
    RS = 0;
    RW = 1;
    E = 1;
    _nop_();
  } while(P0 & 0x80);
  E = 0;
}

// Write a command
void write_command(uchar com)
{
        check_busy();
  E = 0;
  RS = 0;
  RW = 0;
  P0 = com;
  E = 1;
  _nop_();
  E = 0;
  delay(1);
}

// Write Data
void write_date(uchar date)
{
        check_busy();
  E = 0;
  RS = 1;
  RW = 0;
  P0 = date;
  E = 1;
  _nop_();
  E = 0;
  delay(1);   
}

// Initialize LCD controller
void LCD_init(void)
{
        write_command(0x38); // 8-bits, 2 lines, 7x5 dots
  write_command(0x0C); // no cursor, no blink, enable display
  write_command(0x06); // auto-increment on
  write_command(0x01); // clear screen
  delay(1);
}

// Display a string
void string(uchar ad, uchar *s)
{
        write_command(ad);
  while(*s>0)
  {
                 write_date(*s++);
     delay(100);
  }
}
//****************************************************//
//函数名:delay_ms(uint xms)
//函数功能:ms级延时函数
//***************************************************//

void delay_ms(uint xms)
{
        uint x,y;
        for(x=xms;x>0;x--)
                for(y=110;y>0;y--);
       
}
/*
void ADC0808_Init()
{
  ST=0;
  OE=0;
}
*/
//***************************************************//
//函数名称:ADC0808_ADC()
//函数功能:读取一次A/D转换的数据
//***************************************************//
/*
void ADC0808_ADC()
{
  ST=0;
  ST=1;
        ST=0;
  //OE=0;
  while(EOC==0);          //自动发送
  OE=1;

}
*/
//***************************************************//
//函数名称:TLC549_ADC()
//函数功能:读取上一次A/D转换的数据,启动下一次A/D转换
//**************************************************//
/*
uchar TLC549_ADC(void)
{
        uchar n, tmp;

        CS  = 1;                     //CS置高,片选无效
        CLK = 0;

        CS  = 0;                     //CS置低,片选有效,同时DO输出高位
        _nop_();
        _nop_();                     //适当延迟时间1.4us Setup Time

        for(n = 0; n < 8; n++) //串行数据移位输入
        {
                tmp <<= 1;
                tmp |=  DO;
  
                CLK = 1;        //0.4us
                _nop_();          //CLK transition time Max 0.1us
                CLK = 0;        //0.4us
        }
        CS = 1;            //CS置高,片选无效
        for(n = 17; n != 0; n--) _nop_();  //Next Coversion需要延迟17us
        return (tmp);
}
*/
//****************************************//
//函数功能:将模拟信号转换为数字信号
//函数名称:TLC2543_ADC()
//输入参数:uchar CH_i
//输出参数:uint类型
//****************************************//
unsigned int TLC2543_ADC(unsigned int CH_i)           //CH_i,通道值
{
        unsigned int AD_Val = 0;
        unsigned char i;
        CS = 1;                                                //一个转换周期开始
        EOC = 0;
        CLK = 0;                                        //为第一个脉冲做准备
        _nop_();
        _nop_();
        CS = 0;                                                //CS置0片选有效
        EOC = 1;                                        //EOC开始应设置为高电平
  CH_i<<=4;  //将通道值(D7,D6,D5,D4)移入高四位,转换通道设置
  CH_i|=0x02; //D3,D2,D1,D0=0,0,1,0 ,输出数据为12位,先输出低位
        for(i=0;i<8;i++)         //将A/D转换方式控制字写入TLC2543,并读取低8位转换结果
        {
               
          AD_Val>>=1;   //将读取结果逐位右移(先输出的是低位)   
          CLK=0;
          _nop_();
    if((CH_i&0x80)==0x80)       
            SDI=1;               
          else
            SDI=0;
            CH_i<<=1;        //在脉冲上升沿,从高位至低位依次将控制字写入TLC2543
      CLK=1;       
      _nop_();
          if(SDO==1)                    //在脉冲下降沿,TLC2543输出数据,写入AD_Val的第12位
          {
            AD_Val|=0x800;         
          }
          else
          {
            AD_Val|=0x000;
          }
        }
  SDI=0;        //8个数据流输入后,SDI端必须保持在一个固定的电平上,指引EOC变高
  for(i=8;i<12;i++)          //读取转换值的第8至第11位
  {
    AD_Val>>=1;
          CLK=0;                      
          _nop_();
    CLK=1;      
          _nop_();
          if(SDO==1)  
          {
            AD_Val|=0x800;        //在脉冲下降沿,TLC2543输出数据,写入AD_Val的第12位
          }
          else
          {
            AD_Val|=0x000;         //第12位写 ‘0’
          }
        }
  CLK=0;         //在第12个时钟下降沿来临时,EOC开始变低,开始对本次采样的模拟量进行A/D转换,
  _nop_();         //给硬件一点转换时间
  _nop_();
  _nop_();
  _nop_();
  _nop_();
  _nop_();
  CS=1;      // 停止转换,高电平无效
  EOC=0;  
  return AD_Val;
}       

//****************************************//
//函数名称ata_Conversion()
//函数功能:电压换算
//*****************************************//
void Data_Conversion(void)
{
        uint ad_val;
  unsigned char o,p,k,l;
       
        ad_val = TLC2543_ADC(4);
        o=ad_val/1000;                   //取千位数字
        p=(ad_val%1000)/100;           //取百位数字
        k=(ad_val%100)/10;               //取十位数字
        l=ad_val%10;                           //取个位数字
        write_command(0x80+0x40);
        write_date(0x30+o);
        write_date(0x30+p);
        write_date(0x30+k);
        write_date(0x30+l);
        //delay_ms(1000);
        if((X3==0)&&(X2==0))        //0-5V
        {
                //ADD_A = 0;
                //ADD_B = 1;
                //ADD_C = 0;
                Volt          = 5.0 / 4096 * ad_val*1000;                //(100-500)
                LCD[0] = Volt/1000;         //千位
               
                LCD[1] = Volt/100%10;  //百位
                LCD[2] = Volt/10%10;    //十位
                LCD[3] = '.';                                //小数点
                LCD[4] = Volt%10;           //个位
                LCD[5] = 'V';                              //字符V
                                                                                                                               
                write_command(0x80+8);
                write_date(0x30+LCD[0]);
               
                write_date(0x30+LCD[1]);
                write_date(0x30+LCD[2]);
                write_date(LCD[3]);
                write_date(0x30+LCD[4]);
                write_date(LCD[5]);
        }
        if((X3==1)&&(X2==0))        //5-50V
        {
                //ADD_A = 1;
                //ADD_B = 0;
                //ADD_C = 0;
                Volt = 5.0/4096*ad_val*1000;        //(50-500)
                LCD[0] = Volt/1000;         //千位
               
                LCD[1] = Volt/100%10;  //百位
               
                LCD[2] = Volt/10%10;    //十位
                LCD[3] = '.';                                //小数点
                LCD[4] = Volt%10;           //个位
                LCD[5] = 'V';                              //字符V
                                                                                                                               
                write_command(0x80+8);
                write_date(0x30+LCD[0]);
               
                write_date(0x30+LCD[1]);
               
                write_date(0x30+LCD[2]);
                write_date(LCD[3]);
                write_date(0x30+LCD[4]);
                write_date(LCD[5]);
        }
        if((X3==1)&&(X2==1))        //50V-500V
        {
                //ADD_A = 0;
                //ADD_B = 0;
                //ADD_C = 0;
                Volt   = 5.0/4096*ad_val*1000;        //(500-5000)
                LCD[0] = Volt/1000;         //千位
               
                LCD[1] = Volt/100%10;  //百位
               
                LCD[2] = Volt/10%10;    //十位
                LCD[3] = '.';                                //小数点
                LCD[4] = Volt%10;           //个位
                LCD[5] = 'V';                              //字符V
                                                                                                                               
                write_command(0x80+8);
                write_date(0x30+LCD[0]);
               
                write_date(0x30+LCD[1]);
               
                write_date(0x30+LCD[2]);
                write_date(LCD[3]);
                write_date(0x30+LCD[4]);
                write_date(LCD[5]);
        }
}

void Volt_To_LCD1602(void)
{
        Data_Conversion();                     //电压换算并放大1000倍
}
       
int main()
{
        //uint m;
        LCD_init();
        //ADC0808_Init();
        //ADC0808_ADC();
  delay_ms(100);
  write_command(0x01);
        write_command(0x80);
        string(0x80,"Voltage:");
        //delay_ms(2000);

        while(1)
        {

                delay_ms(50);
                Volt_To_LCD1602();
                //delay_ms(4000);
               
        }
}
回复 支持 反对

使用道具 举报

120

主题

7877

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12010
金钱
12010
注册时间
2013-9-10
在线时间
427 小时
发表于 2016-5-2 18:42:33 | 显示全部楼层
楼主牛逼,谢谢分享!
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复 支持 反对

使用道具 举报

头像被屏蔽

74

主题

75

帖子

0

精华

禁止访问

积分
2063
金钱
2063
注册时间
2015-9-11
在线时间
137 小时
发表于 2016-5-2 19:20:12 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

6

主题

36

帖子

0

精华

初级会员

Rank: 2

积分
160
金钱
160
注册时间
2016-4-2
在线时间
33 小时
 楼主| 发表于 2016-5-2 21:32:20 | 显示全部楼层
HER107 发表于 2016-5-2 18:50
量程切换用可以软件来解决,检测某一路ADC的值大于设定值就切换下一路……如果仅仅是单一电压检测功能,模 ...

用运放放大直流电压,但你考虑过直流放大后会达到你想要的增益吗?增益不准的话,相应单片机程序的数据处理就没法确定了。
回复 支持 反对

使用道具 举报

36

主题

249

帖子

0

精华

高级会员

Rank: 4

积分
680
金钱
680
注册时间
2015-12-13
在线时间
243 小时
发表于 2016-5-3 09:28:26 | 显示全部楼层
叶致习 发表于 2016-5-2 21:32
用运放放大直流电压,但你考虑过直流放大后会达到你想要的增益吗?增益不准的话,相应单片机程序的数据处 ...

呵呵~~实物我做的多了,本来我还想发个图上来的,既然你这么认为,上面的话就当我没说过!!!
回复 支持 反对

使用道具 举报

6

主题

36

帖子

0

精华

初级会员

Rank: 2

积分
160
金钱
160
注册时间
2016-4-2
在线时间
33 小时
 楼主| 发表于 2016-5-3 23:42:06 | 显示全部楼层
HER107 发表于 2016-5-3 09:28
呵呵~~实物我做的多了,本来我还想发个图上来的,既然你这么认为,上面的话就当我没说过!!!

你经过了实物验证,那肯定就是我错了的。不过,你的方法我不是很懂耶,你能不能帮我讲解下啊,顺便也为论坛的网友分享下你的设计思路和资料可以吗
回复 支持 反对

使用道具 举报

36

主题

249

帖子

0

精华

高级会员

Rank: 4

积分
680
金钱
680
注册时间
2015-12-13
在线时间
243 小时
发表于 2016-5-5 11:02:17 | 显示全部楼层
元件选定以后 误差值是固定的,并且是可以计算或者测量出来的,至于什么温漂,外部影响就不说了, 在采集回来的数据进行处理的时候根据元件的误差进行计算就可以得到准确的数据,……再说了,你用电阻分压难道没有误差?……
还有就是 ,像你这样直接分压接入AD和单片机,有机会你去测量一下那些稍微带点干扰的电源试试,肯定是乱码一堆,内部数字地和外部信号源的地连在一起,遇到干扰,单片机死机是很正常的事情…………
不过这都是实际应用需要考虑的问题,,
做个小玩具没必要顾及这些!
回复 支持 反对

使用道具 举报

6

主题

36

帖子

0

精华

初级会员

Rank: 2

积分
160
金钱
160
注册时间
2016-4-2
在线时间
33 小时
 楼主| 发表于 2016-5-5 13:32:27 | 显示全部楼层
HER107 发表于 2016-5-5 11:02
元件选定以后 误差值是固定的,并且是可以计算或者测量出来的,至于什么温漂,外部影响就不说了, 在采集回 ...

分压后不能直接接AD,那中间该接些什么了,能不能指导下呢?就以我的电路为例该如何改进呢
回复 支持 反对

使用道具 举报

10

主题

48

帖子

0

精华

初级会员

Rank: 2

积分
117
金钱
117
注册时间
2016-7-14
在线时间
28 小时
发表于 2016-9-14 00:29:12 | 显示全部楼层
请问 交流的怎么做 自动切换 两成范围0.28—28分三档,请教各位 大神
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
7
金钱
7
注册时间
2019-5-16
在线时间
2 小时
发表于 2019-5-16 12:08:44 | 显示全部楼层
兄弟可以给我讲讲你的这个设计的原理不,我在做设计
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
7
金钱
7
注册时间
2019-5-16
在线时间
2 小时
发表于 2019-5-16 12:39:56 | 显示全部楼层
如何把电压测量范围扩展至0-1000v,然后实现自动换挡
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
4
金钱
4
注册时间
2020-4-8
在线时间
1 小时
发表于 2020-4-8 11:50:28 来自手机 | 显示全部楼层
天幕 发表于 2019-5-16 12:39
如何把电压测量范围扩展至0-1000v,然后实现自动换挡

0-1000V,那么对AD转换芯片精度要求非常高,并且若采用分压电阻分压,分压比若太大,误差将会非常大。建议量程别弄那么大,没有实际意义。
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
4
金钱
4
注册时间
2020-4-8
在线时间
1 小时
发表于 2020-4-8 11:52:38 来自手机 | 显示全部楼层
请问楼主问题最后解决了吗
回复 支持 反对

使用道具 举报

0

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
80
金钱
80
注册时间
2020-5-9
在线时间
18 小时
发表于 2020-5-11 00:45:57 | 显示全部楼层

A.gif

关于输入电路,可以参考上面的电路。基本上功能如下:
1)高阻抗分压器:用于衰减输入电压,使其限制在输入的允许范围 (0v-5v)。
2)抗干扰和补偿:在滤除一些高频干扰的同时也对分压器分布电感进行补偿。
3)低阻抗分压器:补偿运放的偏压,使得对正负输入电压都不过载。
4)过压钳位保护:以确保输入电压不超过运放或ADC的输入范围。
5)单位增益运放:当输入电压相对高的时候,软件可以控制由该运放输出测量电压。
6)10倍增益运放:当输入电压相对低的时候,软件可以控制由该运放输出测量电压。

回复 支持 反对

使用道具 举报

0

主题

81

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
208
金钱
208
注册时间
2014-9-18
在线时间
21 小时
发表于 2020-5-16 12:02:06 | 显示全部楼层
不错,楼主威武哈
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-23 09:00

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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