1987的一个人 发表于 2012-11-4 11:03:05

请问大神一些AD采集数据误差的一些问题

多路电流采集
        方案:采用如下电路图,电源引用实验板电压。利用电阻分压,通过各电阻将电流转换成电压。然后通过STC12LE5608AD自带十位AD转换进行电压采集。 。
注:从左到右分别为一、二、三、四路。

多路同时采集值,10AD读回来的值(每隔2分钟左右记录一次)
一路        534        538        537        538        536        534        537        538        535
二路        535        539        538        540        538        536        539        540        536
三路        176        183        181        184        180        177        181        182        177
四路        532        536        535        537        534        533        535        536        533

一路        519        519        519        519        520        520        520        521        521
二路        523        523        523        523        523        523        523        524        524
三路        156        156        156        156        157        157        157        158        158
四路        519        520        520        520        519        520        520        521        521
如果是电压分一半的话,AD读回来应该是512,可是为何都是偏大。而且我把电压与地调换后,读回来的值还是偏大。是AD转换的自己的误差么。

一路电流采集值(其他三路断开):
一路        527        526        532        528        538        534        527        526        526
      526        524        532        528        522        535        534       
单路还是偏大。               

ks.albert 发表于 2012-11-4 12:16:40

能上传你的C代码吗?

电阻都有误差的,个人感觉应该用精密电阻

lijunmin1990 发表于 2012-11-4 12:34:44

如果单次转换能达到这种一致性对STC来说我觉得已经很不错了.
然后关于偏大的问题,话说来就比较长了,这里不存在参考源的问题,
简短的说,你能把 ADC的 GND~VCC的曲线描出来, 问题就解决了.(最大读到0的电压,和最小读到1024的电压,连起来就差不多了.这段直线的非线性我觉得已经没办法改善了.)
其中包括ADC的参数都是ADC的直流参数. INL DNL, offset err , gain err .(噪声可以取平均滤掉)
总之,ADC并不是一个理想的线性器件.

1987的一个人 发表于 2012-11-5 15:53:28

ks.albert 发表于 2012-11-4 12:16 static/image/common/back.gif
能上传你的C代码吗?

电阻都有误差的,个人感觉应该用精密电阻

电阻误差不大,误差大的也就1欧左右(万用表测量)
#include<reg52.h>
#include"LCD.h"
sfr P1M0 = 0X91;        //P1口模式配置寄存器 0
sfr P1M1 = 0X92;        //P1口模式配置寄存器 0
sfr ADC_CONTR= 0Xc5;        //ADC控制寄存器
sfr ADC_DATA = 0XC6;        //ADC结果高八位寄存器
sfr ADC_LOW = 0Xbe;        //ADC结果高八位寄存器
sfr AUXR = 0XA2;
#define ADC_POWER 0x80         //电源打开       
#define ADC_FLAG0x10       //模数转换标志位 完成后为1
#define ADC_START 0x08           //AD 转换启动控制位
#define ADC_SPEEDLL 0x00       //AD 转换时间 1080个时钟周期转换一次
#define ADC_SPEEDL 0x20       //同上 810
#define ADC_SPEEDH 0x40       //同上 540
#define ADC_SPEEDHH 0x60//同上 270
#define FOSC18432000
#define BAUD9600
uchar i,flag,tt;
voidADC_init()//AD 初始化
{
        P1M0 = 0xff;                //设置P1口为AD 转换模式
        P1M1 = 0xff;
        ADC_CONTR = ADC_POWER;
        delay_1(20);
        ADC_CONTR = ADC_POWER|ADC_START|ADC_SPEEDHH;
        //控制寄存器初始化
        ADC_DATA= 0;         //数据寄存器高八位清零
//        ADC_LOW2= 0;        //数据寄存器低二位清零
//        IE = 0Xff;                //中断开放
        //AUXR = 0Xff;        //允许AD 中断
        delay_1(20);
}
/*void senddat(uchar dat)
{
        while(!TI);
        TI = 0;
        SBUF = dat;       
} */
/*void adc_ist() interrupt 5 using 1
{
        ADC_CONTR&=!ADC_FLAG;//清除ADC 中断标志位
        //senddat(ch);
        //senddat(ADC_DATA);
        if(++ch>7)ch=0;
        ADC_CONTR = ADC_POWER|ADC_START|ADC_SPEEDHH|ch;
}*/
void inituart()
{
        SCON = 0x5a;
        TMOD = 0x21;                                        //定时器工作方式3
        TH1= -(FOSC/12/32/BAUD);          //设置通用异步收发机波特率
        TL1= -(FOSC/12/32/BAUD);
        TH0= 0x4c;          //设置通用异步收发机波特率
        TL0= 0x00;
        ET0= 1;
        TR1= 1;
        TR0= 1;               //定时器开始工作       
//        TF0= 0;
        EA   = 1;
}
/*输入:实际电压值,AD转换寄存器值
**功能:返回一个合适输出值
*/
uint actuall_ele_tran(uint ac_ele,uint temp)
{
        uint aa,bb,cc;
        ac_ele = ac_ele*100;
        aa = ac_ele/1024;
        cc= temp;
        temp *= aa;
        bb = (ac_ele%1024)*10/1024;
        cc = cc*bb/10;
        temp = temp + cc;
        return temp;               
}
void main()
{
        uint temp,temp2,temp1,ch,j;
        inituart();
        ADC_init();
        LCD_init();
        LCD_qingping(0);
        delay_1(10);
        AUXR|=0X10;
        IE = 0Xa2;
        while(1)
        {
                ADC_CONTR = ADC_POWER|ADC_START|ADC_SPEEDHH;
                delay_1(20);
                if(tt == 1)
                {
                        tt = 0;
                        flag = 0;
                        LCD_qingping(0);
                        for(ch = 0;ch<4; ch++)
                        {
                                ADC_CONTR = ADC_POWER|ADC_START|ADC_SPEEDHH|ch;
        //                        w_hanzi(0,0,0);        //电流
        //                        w_hanzi(1,0,2);
        //                        w_hanzi(0,2,0);//电压
        //                        w_hanzi(1,2,1);
                                delay_1(200);
                                temp = ADC_DATA;
                                temp <<= 2;
                                temp2 = ADC_LOW;
                                temp2 = temp2&0x0003;       //清楚高六位
                                temp= temp|temp2;
                                temp1 = temp;                       //保护原始数据
                                if(ch == 0 )
                                {
                                        j = 120;       
                                }
                                if(ch == 1)
                                {
                                        j = 120;
                                }
                                if(ch == 2)
                                {
                                        j = 99;       
                                }
                                if(ch == 3)
                                {
                                        j = 623;       
                                }
                                temp2 = actuall_ele_tran(329,temp);

                //                electric(temp2,ch*2,j);       //temp2是要显示的电流,ch为要显示的行数
                //                xs(temp2,ch*2,18);
                                temp = temp1;
                                xs(temp,ch*2,0);
                                for(i = 10;i>0;i--)
                                {
                                        temp1 = temp&0x01;
                                        w_shuzi(i+6,ch*2,temp1);
                                        temp = temp>>1;
                                }        
                                delay_1(20);
                        }
                }       
        }
}
void Timer() interrupt 1
{

                TH0= 0x4c;          //设置通用异步收发机波特率
                TL0= 0x00;
                TR0= 1;
                if(flag++ == 25)
                {
                        flag = 0;
                        tt = 1;
                }
}

以下是LCD和现实程序
#include<reg52.h>
#include"LCD.h"
uchar code shuzi[]=
{
/*--文字:0--*/
0xF8,0x04,0x04,0x04,0xF8,0x00,0x01,0x02,0x02,0x02,0x01,0x00,

/*--文字:1--*/
0x00,0x08,0xFC,0x00,0x00,0x00,0x00,0x02,0x03,0x02,0x00,0x00,

/*--文字:2--*/
0x18,0x84,0x44,0x24,0x18,0x00,0x03,0x02,0x02,0x02,0x02,0x00,

/*--文字:3--*/
0x08,0x04,0x24,0x24,0xD8,0x00,0x01,0x02,0x02,0x02,0x01,0x00,

/*--文字:4--*/
0x40,0xB0,0x88,0xFC,0x80,0x00,0x00,0x00,0x00,0x03,0x02,0x00,

/*--文字:5--*/
0x3C,0x24,0x24,0x24,0xC4,0x00,0x01,0x02,0x02,0x02,0x01,0x00,

/*--文字:6--*/
0xF8,0x24,0x24,0x2C,0xC0,0x00,0x01,0x02,0x02,0x02,0x01,0x00,

/*--文字:7--*/
0x0C,0x04,0xE4,0x1C,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,

/*--文字:8--*/
0xD8,0x24,0x24,0x24,0xD8,0x00,0x01,0x02,0x02,0x02,0x01,0x00,

/*--文字:9--*/
0x38,0x44,0x44,0x44,0xF8,0x00,0x00,0x03,0x02,0x02,0x01,0x00,
/*--文字:---*/
/*--宋体9;此字体下对应的点阵为:宽x高=6x12   --*/
/*--高度不是8的倍数,现调整为:宽度x高度=6x16--*/
0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/*--文字:.--*/
/*--宋体9;此字体下对应的点阵为:宽x高=6x12   --*/
/*--高度不是8的倍数,现调整为:宽度x高度=6x16--*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,
};
uchar code hanzi[]=
{
/*--文字:电--*/
/*--宋体9;此字体下对应的点阵为:宽x高=12x12   --*/
/*--高度不是8的倍数,现调整为:宽度x高度=12x16--*/
0xFC,0x24,0x24,0x24,0xFF,0x24,0x24,0x24,0xFC,0x00,0x00,0x00,0x03,0x01,0x01,0x01,
0x07,0x09,0x09,0x09,0x09,0x08,0x0E,0x00,
/*--文字:流--*/
/*--宋体9;此字体下对应的点阵为:宽x高=12x12   --*/
/*--高度不是8的倍数,现调整为:宽度x高度=12x16--*/
0x22,0x44,0x00,0x24,0xB4,0x2C,0xA5,0x26,0xA4,0x34,0x64,0x00,0x04,0x02,0x08,0x04,
0x03,0x00,0x0F,0x00,0x07,0x08,0x0E,0x00,
/*--文字:压--*/
/*--宋体9;此字体下对应的点阵为:宽x高=12x12   --*/
/*--高度不是8的倍数,现调整为:宽度x高度=12x16--*/
0x00,0xFE,0x02,0x42,0x42,0x42,0xFA,0x42,0x42,0x42,0x02,0x00,0x08,0x07,0x08,0x08,
0x08,0x08,0x0F,0x08,0x09,0x0A,0x08,0x00,
/*--文字:阻--*/
/*--宋体9;此字体下对应的点阵为:宽x高=12x12   --*/
/*--高度不是8的倍数,现调整为:宽度x高度=12x16--*/
0xFE,0x02,0x32,0xCE,0x00,0xFE,0x92,0x92,0x92,0xFE,0x00,0x00,0x0F,0x02,0x02,0x01,
0x08,0x0F,0x08,0x08,0x08,0x0F,0x08,0x00,
};       
void LCD_write_dat(uchar dat) //写数据
{
        uchar i;
        CS = 0;
        A0 = 1;
        for(i = 0;i<8;i++)
        {
                if(0x80 == (dat&0x80))
                        l_sda = 1;
                else l_sda = 0;
                dat = dat<<1;
                l_scl = 0;
                l_scl = 1;
        }
        CS = 1;       
}
void LCD_write_com(uchar com) //写命令
{
        uchar i;
        CS = 0;
        A0 = 0;
        for(i = 0;i<8;i++)
        {
                if(0x80 == (com&0x80))
                        l_sda = 1;
                else l_sda = 0;
                com = com<<1;
                l_scl = 0;
                l_scl = 1;
        }
        CS = 1;       
}
void LCD_add(uchar x,y) //写命令
{
        LCD_write_com(0xb0|y);
        x = x+4;
        LCD_write_com(x>>4|0x10);           //要写两次地址
        LCD_write_com(x&0x0f);
}

void w_shuzi(uchar x,uchar y,uchar zz)
{
        uchar i,z;
        z = zz;
        LCD_add(x*6,y);
        for(i = 0;i<6;i++)
                LCD_write_dat(shuzi);
        LCD_add(x*6,y+1);
        for(i = 6;i<12;i++)
                LCD_write_dat(shuzi);               
}
void w_hanzi(uchar x,uchar y,uchar z) //x列数,y是行数,z是要显示的数
{
        uchari;
        LCD_add(x*12,y);
        for(i=0;i<12;i++)
                LCD_write_dat(hanzi);
        LCD_add(x*12,y+1);
        for(i=12;i<24;i++)
                LCD_write_dat(hanzi);
}
void LCD_qingping(uchar aa)
{
        uchar page,j;
       for(page = 0;page<8;page++)
               for(j = 0;j<128;j++)
                {
                        LCD_add(j,page);       
                        LCD_write_dat(aa);
                }
}
void delay_1(uint z)
{
        uint x,y;
        for(x=0;x<z;x++)
                for(y=0;y<148;y++);
}
void LCD_init()
{
//        CS=0;
//        res();   
        LCD_write_com(0xA2);    //LCD偏压设置,V3时选--LCD Bias selection(1/65 Duty,1/9Bias)   
        LCD_write_com(0xA1);    //Segment方向选择,正常--ADC selection(SEG0->SEG128)   
        LCD_write_com(0x80);    //设置显示起始行对应RAM--SHL selection(COM0->COM64)   
          
       delay_1(5);
       LCD_write_com(0x26);    //设置Rb/Ra=6--Regulator Resistor Selection   
       LCD_write_com(0x81);    //电量设置模式(显示亮度)--Electronic Volume
       LCD_write_com(0x08);   //Reference Register selectionVo=(1+Rb/Ra)(1+a)*2.1=10
       
       delay_1(10);
       LCD_write_com(0x2f);       //设置上电控制模式--Power Control(Vc=1;Vr=1;Vf=1)   
       delay_1(10);
       LCD_write_com(0xF8);    //升压比设置
       LCD_write_com(0x00);   
       delay_1(5);
        LCD_write_com(0xAF);    //LCD显示开--Display on
       delay_1(5);
        LCD_write_com(0xa4);
/*        LCD_write_com(0xaf); //开关指令       
        LCD_write_com(0x40); //读状态设置
        LCD_write_com(0xa1);//ADC选择
        LCD_write_com(0xa6); //正向显示
        LCD_write_com(0xa2); //偏压比设置
        LCD_write_com(0xa4); //全屏点亮设置
        LCD_write_com(0xe0); //读写操作
        LCD_write_com(0xc0); //COM口正常方向扫描
        LCD_write_com(0x2f); //上电控制
        LCD_write_com(0x23); //电阻控制
        LCD_write_com(0x3f); //电量控制
        LCD_write_com(0x81); //电量控制
        LCD_write_com(0xad); //静态指示器关        */
}
//作用:电压装换显示
void xs(uint dd,uchar i,uchar k)//dd 要显示的函数,k显示的列数位置,i显示行数位置
{
   uint temp4,temp5,shu;
   char j = 0;
   if(dd<10)
   {
                   w_shuzi(k,i,dd);
   }
   else
   {
/*        temp4 = dd/10000;
                if(temp4 != 0)
                {
                        w_shuzi(k,i,temp4);
                        w_shuzi(++k,i,10);
                        k++;
                        dd = dd%10000;       
                }*/
                else if(dd/1000 != 0)
                {
                        w_shuzi(k,i,0);
                        w_shuzi(++k,i,10);
                        k++;
                        dd = dd%1000;
                }
          while(dd)
               {
                        temp4 = dd/10;
                        temp5 = dd%10;
                        shu = temp5;       
                        dd = temp4;
               }
               for(--j;j>=0;j--)
               {
                        w_shuzi(k++,i,shu);
               }
        }
}
void elexs(uint dd,uchar i,uchar k)
{
        uchar temp,temp4,temp5,shu;
        char j = 0;
        temp = dd%10;
        dd = dd/10;
        if(dd<10)
           {
                   w_shuzi(k++,i,dd);
           }
           else
   {
                while(dd)
               {
                        temp4 = dd/10;
                        temp5 = dd%10;
                        shu = temp5;       
                        dd = temp4;
               }
               for(--j;j>=0;j--)
               {
                        w_shuzi(k++,i,shu);
               }
       }
/*       if(k == 1)
       while(1);       */
       w_shuzi(k++,i,10);
       w_shuzi(k,i,temp);
}
void electric(uint ele,uchar ch,uint j)
{
        uint temp;
        uchar i = 0;
        temp = ele/j;
        elexs(temp,ch,i);
        if(temp > 0)
                i = 0;
        if(temp > 10)
                i = 1;
        if(temp > 100)
                i = 2;
        temp =        ele%j;
        temp *= 100;       //精确到电流(ma)后三位
        xs(temp/j,ch,i+2);       
}

1987的一个人 发表于 2012-11-5 16:53:23

lijunmin1990 发表于 2012-11-4 12:34 static/image/common/back.gif
如果单次转换能达到这种一致性对STC来说我觉得已经很不错了.
然后关于偏大的问题,话说来就比较长了,这里 ...

呵呵,谢谢啊 不过有些地方我还不是很懂
“你能把 ADC的 GND~VCC的曲线描出来, 问题就解决了”,意思是说实验板上的输出电压引回去AD采集
(全是1,我测试过)但这时的电压已经是比AD(逐次比较型)的基准电压大了?是这样理解么?
还要追加一个问题就是为何每一次给单片机上电,但读回来的值却不一样!
请给予解答下?

lijunmin1990 发表于 2012-11-5 22:22:15

1987的一个人 发表于 2012-11-5 16:53 static/image/common/back.gif
呵呵,谢谢啊 不过有些地方我还不是很懂
“你能把 ADC的 GND~VCC的曲线描出来, 问题就解决了”,意思是说 ...

额,ADC还有输入阻抗的问题。把ADC的输入电阻先并到你的电路里。然后再计算。

真的要开始校准这些ADC参数,搞个好点的电压表,先测下VCC,(必须的)和 待测点(解决掉电阻的误差)。

ADC 的offset err 是指最大读到0的电压,    (比如0V 读到0,0.03V读到的还有可能是0)
ADC 的gain err是指最小读到全1的电压, (比如说 5V读到 1024, 但是 4.98V读到的也是1024)

把这个 “0.03V ~ 4.98V” 连起来是ADC的线性范围,线性范围内的误差(你测直流电压,按照DNL, INL计算)

1987的一个人 发表于 2012-11-8 10:04:58

lijunmin1990 发表于 2012-11-5 22:22 static/image/common/back.gif
额,ADC还有输入阻抗的问题。把ADC的输入电阻先并到你的电路里。然后再计算。

真的要开始校准这些ADC参 ...

呵呵谢谢啊
页: [1]
查看完整版本: 请问大神一些AD采集数据误差的一些问题