MENU

28 AD模数转换

July 28, 2020 • 51单片机

--- 当前时间: ---

28 AD模数转换

28.1 A/D转换器的主要技术指标

注意:精(确)度分辨率不是同一个概念,要分开

28.1.1 分辨率

ADC(Analog to Digital Converter)的分辨率是指使输出数字量变化一个相邻数码所需输入模拟电压的变化量,常用二进制的位数表示

例如12位ADC的分辨率就是12位,或者说分辨率为满刻度的$\large\dfrac{1}{2^{12}}$

一个$10V$满刻度的12位ADC能分辨输入电压变化最小值是:

$$ \Large\tag{28.1}10V\times\dfrac{1}{2^{12}}=2.4mV $$

28.1.2 量化误差

ADC把模拟量变为数字量,用数字量近似表示模拟量,这个过程称为量化

量化误差是ADC的有限位数对模拟量进行量化而引起的误差

实际上,要准确表示模拟量,ADC的位数需<u>很大甚至无穷大</u>

一个分辨率有限的ADC的阶梯状转换特性曲线与具有无限分辨率的ADC转换特性曲线(直线)之间的最大偏差即是量化误差

28.1.3 偏移误差(零值误差)

偏移误差是指输入信号为零时,输出信号不为零的值,所以有时又称为零值误差

假定ADC没有非线性误差,则其转换特性曲线各阶梯中点的连线必定是直线,这条直线与横轴相交点所对应的输入电压值就是偏移误差

正偏移误差:

负偏移误差:

28.1.4 满刻度误差(增益误差)

满刻度误差又称为增益误差

ADC的满刻度误差是指满刻度输出数码所对应的实际输入电压与理想输入电压之差

正增益误差:

负增益误差:

28.1.5 线性度(误差)/非线性度(误差)

线性度(误差)有时又称为非线性度(误差),它是指转换器实际的转换特性与理想<u>直线</u>的最大偏差

线性度指标有两个:

  • INL(ILE):积分非线性,指的是ADC整体的非线性程度

  • DNL(DLE):微分非线性,指的是ADC局部(细节)的非线性程度

我们通常讲的“线性度”都是指“积分非线性”,积分非线性一般以百分比给出,或者以位数给出

28.1.6 绝对精度(总未调整误差)

在一个转换器中,任何数码所对应的实际模拟量输入与理论模拟输入之差的最大值,称为绝对精度(总未调整误差),记为TUE

对于ADC而言,可以在每一个阶梯的水平中点进行测量,它包括了所有的误差

28.1.7 转换速率

ADC的转换速率是能够重复进行数据转换的速度,即每秒转换的次数

完成一次A/D转换所需的时间(包括稳定时间),则是<u>转换速率的倒数</u>

A/D转换器的转换速度主要取决于转换电路的类型,不同类型A/D转换器的转换速度相差很大:

  1. <u>双积分型A/D转换器</u>的转换速度最慢,需几百毫秒左右
  2. <u>逐次逼近式A/D转换器</u>的转换速度较快,需几十微秒开发板使用此种
  3. <u>并行比较型A/D转换器</u>的转换速度最快,仅需几十纳秒时间

28.2 各种ADC的转换原理

28.2.1 逐次逼近式ADC的转换原理

逐次逼近式AD转换器与计数式A/D转换类似,只是数字量由“逐次逼近寄存器SAR(图中的‘N位寄存器’)”产生

SAR使用“对分搜索法”产生数字量——以8位数字量为例:

  1. SAR首先产生8位数字量的一半,即$1000\,\,0000\text B$,试探模拟量$V_{IN}$的大小

    • 若$V_N>V_{IN}$,清除最高位
    • 若$V_N<V_{IN}$,保留最高位
  2. 在最高位确定后,SAR又以对分搜索法确定次高位,即以低7位的一半$y100\,\,0000\text B$($y$为已确定位) 试探模拟量$V_{IN}$的大小
  3. 在bit6确定后,SAR以对分搜索法确定bit5位,即以低6位的一半$yy10\,\,0000\text B$($y$为已确定位) 试探模拟量的大小
  4. 重复这一过程,直到最低位bit0被确定,转换结束

28.2.2 *双积分式ADC的转换原理

双积分式ADC的基本电路如图所示,左侧的运放电路为积分器,右侧的运放电路作为比较器

电路先对未知的模拟输入电压$V_{IN}$进行固定时间$T$的积分,然后转为对标准电压进行反向积分,直到积分输出返回起始值

如上右图所示,输入电压越大,则反向积分时间越长

整个采样期间,积分电容上的充电电荷等于放电电荷,因为$V_{IN},T=Const$,因而反向积分时间与输入模拟电压$V_{IN}$成正比,此期间单片机的内部计数器计数值与信号电压的大小成正比,此计数值就是$V_{IN}$所对应的数字量

28.2.3 *并行比较型ADC的转换原理

参考文档

28.3 开发板模块电路

PCF8591芯片手册

  • AIN3 contact pin:可以检测外部模拟信号的预留通道

28.4 实例代码

i2c.h

#ifndef _I2C_H_
#define _I2C_H_

#include <reg52.h>

#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint
#define uint unsigned int
#endif

sbit SCL = P2^1;
sbit SDA = P2^0;

void  I2CStart();
void  I2CStop();
uchar I2CSendByte(uchar dat);
uchar I2CReadByte();

#endif

i2c.c

#include "i2c.h"

void Delay10us()
{
    uchar a, b;
    for(b = 1; b > 0; b--)
        for(a = 2; a > 0; a--);
}

void I2CStart()
{
    SDA = 1;
    Delay10us();
    SCL = 1;
    Delay10us();
    SDA = 0;
    Delay10us();
    SCL = 0;
    Delay10us();
}

void I2CStop()
{
    SDA = 0;
    Delay10us();
    SCL = 1;
    Delay10us();
    SDA = 1;
    Delay10us();
}

uchar I2CSendByte(uchar dat)
{
    uchar a = 0, b = 0;
    for(a = 0; a < 8; a++)
    {
        SDA = dat >> 7;
        dat <<= 1;
        Delay10us();
        
        SCL = 1;
        Delay10us();
        SCL = 0;
        Delay10us();
    }
    
    SDA = 1;
    Delay10us();
    SCL = 1;
    while(SDA)    // waiting
    {
        b++;
        if(b > 200)
        {
            SCL = 0;
            Delay10us();
            // error
            return 0;
        }
    }
    SCL = 0;
    Delay10us();
    return 1;    // success
}

uchar I2CReadByte()
{
    uchar a = 0, dat = 0;
    SDA = 1;
    Delay10us();
    for(a = 0; a < 8; a++)
    {
        SCL = 1;
        Delay10us();
        dat <<= 1;
        dat |= SDA;
        Delay10us();
        SCL = 0;
        Delay10us();
    }
    return dat;
}

main.c

#include <reg52.h>
#include "i2c.h"

// define the R/W address
#define R_ADDR 0x91                // read
#define W_ADDR 0x90                // write
// define channels
#define AD     0                    // potentiometer
#define NTC    1                    // temperature dependent resistor
#define RG    2                    // light dependent resistor
#define OTH    3                    // others~

// buttons to change channel
sbit k1  = P1^0;
sbit k2  = P1^1;
sbit k3  = P1^2;
sbit k4  = P1^3;
// 74LS138
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;

typedef unsigned char u8;        // 0 ~ 255
typedef unsigned int u16;        // 0 ~ 65535

u8 ch = AD;                        // Default is potentiometer
// 0 ~ 9
u8 code DIG_CODE[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
u8 DisplayData[4];

void delay(u16 i)
{
    while(i--);
}

void keyPros()
{
    if(k1 == 0)
    {
        delay(1000);            // 10 ms disappears shakes
        if(k1 == 0) ch = AD;
        while(!k1);                // The key loosen 
    }
    
    if(k2 == 0)
    {
        delay(1000);            // 10 ms disappears shakes
        if(k2 == 0) ch = NTC;
        while(!k2);                // The key loosen 
    }
    
    if(k3 == 0)
    {
        delay(1000);            // 10 ms disappears shakes
        if(k3 == 0) ch = RG;
        while(!k3);                // The key loosen 
    }
    
    if(k4 == 0)
    {
        delay(1000);            // 10 ms disappears shakes
        if(k4 == 0) ch = OTH;
        while(!k4);                // The key loosen 
    }
}

void DigDisplay()
{
    u8 i;
    for(i = 0; i < 4; i++)
    {
        // 8 - 4 = 4
        switch(i + 4)
        {
            case 4:
                LSA = 0; LSB = 0; LSC = 1; break;    // 1 0 0
            case 5:
                LSA = 1; LSB = 0; LSC = 1; break;    // 1 0 1
            case 6:
                LSA = 0; LSB = 1; LSC = 1; break;    // 1 1 0
            case 7:
                LSA = 1; LSB = 1; LSC = 1; break;    // 1 1 1
        }
        P0 = DisplayData[i];
        delay(100);
        P0 = 0x00;                                    // REMEMBER to clear!
    }        
}

void PCF8591SendByte(u8 channel)
{
    I2CStart();
    I2CSendByte(W_ADDR);
    I2CSendByte(0x40 | channel);
    I2CStop();
}

u8 PCF8591ReadByte()
{
    u8 dat;
    
    I2CStart();
    I2CSendByte(R_ADDR);
    dat = I2CReadByte();
    I2CStop();
    
    return dat;
}

void main()
{
    u16 adNum;
    float value;
    
    while(1)
    {
        keyPros();
        
        PCF8591SendByte(ch);
        // Waiting for delivery to complete prevents crossing lines or even starting DAC!!
        delay(1000);
        
        adNum = PCF8591ReadByte() * 2;
        // Every 1 read is 5/256 V (* 0.01953)
        value = adNum / 2 * 0.01953;
        // Keep two decimal places
        adNum = value * 100;
        
        DisplayData[0] = DIG_CODE[adNum / 1000];
        DisplayData[1] = DIG_CODE[adNum % 1000 / 100] | 0x80;
        DisplayData[2] = DIG_CODE[adNum % 100 / 10];
        DisplayData[3] = DIG_CODE[adNum % 10];
        
        DigDisplay();
    }
}
Last Modified: August 21, 2020
Archives QR Code
QR Code for this page
Tipping QR Code