zoukankan      html  css  js  c++  java
  • MCU 51-8 AD/DA

    模拟量:自然界连续变化的物理量。所谓连续,包含两个方面的含义; 一方面从时间上来说,它是随时间连续变化的; 另一方面从数值上来说,它的数值也是连续变化的。这种连续变化的物理量通常称为模拟量。

    数字量:计算机中处理的是不连续变化的量,离散性的数字量。 当计算机用于数据采集和过程控制时,采集的对象往往是连续变化的物理量(模拟信号)如温度、压力、摄像头采集图像、照度等,但计算机处理的是离散的数字量,因此需要对连续变化的物理量进行A/D转换为不连续的数字量交给计算机处理,保存等。计算机输出的数字量有时需要通过D/A转换为模拟量去控制某些执行元件。A/D转换器完成模拟量至数字量的转换,D/A转换器完成数字量至模拟量的转换。

    PCF8591

    单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入、一个输出和一个串行I2C 总线接口。3 个地址引脚A0、A1 和A2 用于编程硬件地址,允许将最多8 个器件连接至I2C总线而不需要额外硬件。PCF8591由于其使用的简单方便和集成度高,在单片机应用系统中得到了广泛的应用。

    特点:

    1 单电源供电

    2 工作电压:2.5 V ~ 6 V

    3 I2C 总线串行输入/输出

    4 通过 3 个硬件地址引脚编址

    5 采样速率取决于 I2C 总线传输速率决定

    6 4 个模拟输入可编程为单端或差分输入

    7 自动增量通道选择 8

    8 位逐次比较型A/D 转换

     AOUT是模拟输出,通过数字量,改变模拟量输出,控制LED10的亮度

    8 位逐次比较型A/D 转换

    逐次比较型A/D转换器的工作原理可用天平秤重过程作比喻来说明。若四个砝码共重15克,每个重量分别为8、4、2、1克。设待秤重量Wx = 13克,可以用下表步骤来秤量:

    顺序  砝码重    比较判断     暂时结果

    1          8g         8g<13g       保留 8g

    2         8+4g      12g<13g     保留 12g

    3         8+4+2g  14g>13g     撤消 12g

    4         8+4+1g  13g=13g     保留 13g

    AD转换器的主要技术指标概述:

    分辨率

    分辩率指数字量变化一个最小量时模拟信号的变化量,定义为满刻度与2^n的比值(n为AD器件的位数)。对于5V的满刻度,采用8位的AD时,分辨率为5V/256≈0.01953V;当采用12位的AD时,分辨率则为5V/4096≈0.00122V。显然,位数越多分辨率就越高。

    转换速率

    转换速率是指完成一次从模拟转换到数字的AD转换所需的时间的倒数。积分型AD的转换时间是毫秒级属低速AD,逐次比较型AD是微秒级属中速AD,全并行/串并行型AD可达到纳秒级。采样时间则是另外一个概念,是指两次转换的间隔。为了保证转换的正确完成,采样速率必须小于或等于转换速率。

    量化误差

    由于AD的有限分辩率而引起的误差,即有限分辩率AD的阶梯状转换特性曲线与无限分辩率AD(理想AD)的转换特性曲线(直线)之间的最大偏差。通常是1 个或半个最小数字量的模拟变化量,表示为1LSB、1/2LSB。(尺子量出来的1CM不一定是1CM

    零值误差

    满刻度误差

    线性误差

    绝对精度

    在一个转换器中,任何数码所对应的实际模拟量输入与理论模拟输入之差的最大值,称为绝对精度。

    AD的差分输入与单端输入:

    单端输入,输入信号均以共同的地线为基准.这种输入方法主要应用于输入信号电压较高(高于1 V),信号源到模拟输入硬件的导线较短,且所有的输入信号共用一个基准地线.如果信号达不到这些标准,此时应该用差分输入.单端输入时, 是判断信号与 GND 的电压差. 

    差分输入,每一个输入信号都有自有的基准地线;由于共模噪声可以被导线所消除,从而减小了噪声误差. 差分输入时, 是判断两个信号线的电压差. 信号受干扰时, 差分的两线会同时受影响, 但电压差变化不大. (抗干扰性较佳) 而单端输入的一线变化时, GND 不变, 所以电压差变化较大. (抗干扰性较差) 

     

    PCF8591地址:

    I2C 总线系统中的每一片PCF8591 通过发送有效地址到该器件来激活。该地址包括固定部分和可编程部分。可编程部分必须根据地址引脚A0、A1 和A2 来设置,因此I2C系统中最多可接 =8个PCF8591。在I2C 总线协议中地址必须是起始条件后作为第一个字节发送。地址字节的最后一位是用于设置以后数据传输方向的读/写位1为读操作,0为写操作。

    PCF8591控制字节:

    发送到 PCF8591 的第二个字节将被存储在控制寄存器,用于控制器件功能。控制寄存器的高半字节用于允许模拟输出,和将模拟输入编程为单端或差分输入。低半字节选择一个由高半字节定义的模拟输入通道。如果自动增量(auto-increment)标志置1,每次A/D 转换后通道号将自动增加。

                 

    #include <reg52.h>
    #include <intrins.h>
    #define MAIN_Fosc        11059200UL    //宏定义主时钟HZ
    #define PCF8591_ADDR    0x90        //PCF8591地址
    #define DACOUT_EN        0x40        //DAC输出使能
    
    sbit BEEP = P2^3;     //蜂鸣器
    
    /*====================================
     自定义类型名
    ====================================*/
    typedef unsigned char INT8U;
    typedef unsigned char uchar;
    
    typedef unsigned int INT16U;
    typedef unsigned int uint;
    
    /*====================================
     硬件接口位声明
    ====================================*/
    sbit SDA = P2^0;   //I2C串行数据
    sbit SCL = P2^1;   //I2C串行时钟
    sbit DU  = P2^6;   //数码管段选
    sbit WE  = P2^7;   //数码管位选
    sbit LED1= P1^0;   //ADC读取失败指示灯(亮成功,灭失败)
    sbit LED2= P1^1;   //DAC转换失败指示灯(亮成功,灭失败)
    /*====================================
    共阴极数码管段选码
    ====================================*/
    uchar code table[]={ 
    //0        1     2     3     4     5     6     7     8
    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
    //9     A     B       C     D       E     F        -     .      关显示
    0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00
                       };
    
    /*====================================
    数码管位选码
    ====================================*/
                      //第1位    2位      3位     4位   5位    6位      7位    8位
    uchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码
    
    /*====================================
    函数:void Delay_Ms(INT16U ms)
    参数:ms,毫秒延时形参
    描述:12T 51单片机自适应主时钟毫秒级延时函数
    ====================================*/
    void Delay_Ms(INT16U ms)
    {
         INT16U i;
         do{
              i = MAIN_Fosc / 96000; 
              while(--i);   //96T per loop
         }while(--ms);
    }
    
    /*====================================
    函数:void Delay5us()
    描述:12T 51单片机5微秒延时函数自适应时钟(11.0592M,12M,22.1184M)
    ====================================*/
    void Delay5us()
    {
        #if MAIN_Fosc == 11059200
            _nop_();
        #elif MAIN_Fosc == 12000000
            _nop_()
        #elif MAIN_Fosc == 22118400
            _nop_(); _nop_(); _nop_();
        #endif
    }
    
    /*====================================
    函数:void Display(INT8U Value)
    参数:Value,显示值 取值0-255
    描述:共阴极数码管显示函数可显示一个字节的数
    ====================================*/
    void Display(INT8U Value)
    {
    //------------------------------
        DU = 1;
        P0 = table[Value/100];    //管显示百位
        DU = 0;
    
        P0 = 0xff;                  //清除断码
    
        WE = 1;
        P0 = T_COM[0];              //第一位数码管
        WE = 0;
        Delay_Ms(5);
    //-------------------------------
        DU = 1;
        P0 = table[Value%100/10]; //显示十位
        DU = 0;
    
        P0 = 0xff;                  //清除断码
    
        WE = 1;
        P0 = T_COM[1];              //第二位数码管
        WE = 0;
        Delay_Ms(5);
    //-------------------------------
        DU = 1;
        P0 = table[Value%10];        //显示个位
        DU = 0;
                                    
        P0 = 0xff;                    //清除断码
    
        WE = 1;
        P0 = T_COM[2];                //第三位数码管
        WE = 0;
        Delay_Ms(5);
    }
    
    /*====================================
    函数:I2C_init()
    描述:I2C总线初始化
    ====================================*/
    void I2C_init()
    {
        SDA = 1;   //数据总线高
        _nop_();
        SCL = 1;   //时钟总线高
        _nop_();
    }
    
    /*====================================
    函数:I2C_Start()
    描述:I2C起始信号
    ====================================*/
    void I2C_Start()  
    {
        SCL = 1;
        _nop_();
        SDA = 1;
        Delay5us();
        SDA = 0;
        Delay5us();
    }
    
    /*====================================
    函数:I2C_Stop()
    描述:I2C停止信号
    ====================================*/
    void I2C_Stop()
    {
        SDA = 0;
        _nop_();
        SCL = 1;
        Delay5us();
        SDA = 1;
        Delay5us();
    }
    
    /*====================================
    函数:Master_ACK(bit i)
    参数:i 为0时发送非应答 为1时发送应答
    描述:I2C主机发送应答
    ====================================*/
    void Master_ACK(bit i)    
    {
        SCL = 0; // 拉低时钟总线允许SDA数据总线上的数据变化
        _nop_(); // 让总线稳定
        if (i)     //如果i = 1 那么拉低数据总线 表示主机应答
        {
            SDA = 0;
        }
        else     
        {
            SDA = 1;     //发送非应答
        }
        _nop_();//让总线稳定
        SCL = 1;//拉高时钟总线 让从机从SDA线上读走 主机的应答信号
        _nop_();
        SCL = 0;//拉低时钟总线, 占用总线继续通信
        _nop_();
        SDA = 1;//释放SDA数据总线。
        _nop_();
    }
    
    /*====================================
    函数:Test_ACK()
    返回:0为不应答 1为应答
    描述:I2C检测从机应答
    ====================================*/
    bit Test_ACK()     // 检测从机应答
    {
        SCL = 1;//时钟总线为高电平期间可以读取从机应答信号
        Delay5us();
        if (SDA)
        {
            SCL = 0;
            I2C_Stop();
            return(0);
        }
        else
        {
            SCL = 0;
            return(1);
        }
    }
    
    /*====================================
    函数:I2C_send_byte(uchar byte)
    参数:byte 要发送的字节
    描述:I2C发送一个字节
    ====================================*/
    void I2C_send_byte(uchar byte)
    {
        uchar i;
        for(i = 0 ; i < 8 ; i++)
        {
            SCL = 0;
            _nop_();
            if (byte & 0x80)    //
            {
                SDA = 1;
                _nop_();
            }
            else
            {
                SDA = 0;
                _nop_();
            }
            SCL = 1;
            _nop_();
            byte <<= 1;
        }
        SCL = 0;
        _nop_();
        SDA = 1;
        _nop_();    
    }
    
    /*====================================
    函数:I2C_read_byte()
    返回:读取的字节
    描述:I2C读一个字节
    ====================================*/
    uchar I2C_read_byte()
    {
        uchar i, dat;
        SCL = 0 ;
        _nop_();
        SDA = 1;
        _nop_();
        for(i = 0 ; i < 8 ; i++)
        {
            SCL = 1;
            _nop_();
            dat <<= 1;      
            if (SDA)
            {
                dat |= 0x01;  
            }
            _nop_();
            SCL = 0;
            _nop_();
        }
        return(dat);
    }
    
    /*====================================
    函数:DAC_OUT(uchar DAT)
    参数:DAT,发送给PCF8591转换的数字量
    返回:返回1执行成功,0失败
    描述:主机发送数字量交由PCF8591转换为模拟量
    ====================================*/
    bit DAC_OUT(uchar DAT)
    {
        I2C_Start();                  //I2C总线起始
        I2C_send_byte(PCF8591_ADDR+0);//发送PCF8591地址加读写方向位0(写)
        if (!Test_ACK())              //检测是否发送成功(应答)
        {
            return(0);
        }
        I2C_send_byte(DACOUT_EN);      //发送控制字节DAC输出使能 
        if (!Test_ACK())              //检测是否发送成功(应答)
        {
            return(0);
        }
        I2C_send_byte(DAT);              //发送数字量交由PCF8591转为模拟量AOUT脚输出
        if (!Test_ACK())              //检测是否发送成功(应答)
        {
            return(0);
        }
        I2C_Stop();                      //I2C停止信号
        return(1);    
    }
    
    /*====================================
    函数:bit ADC_Read(uchar CON, uchar *DAT)
    参数:CON,ADC控制字节 *DAT指针变量,用于存放带入形参变量的地址
    返回:返回1执行成功,0失败
    描述:读取PCF8591的转换回的值
    ====================================*/
    bit ADC_Read(uchar CON, uchar *DAT)
    {
        I2C_Start();                     //I2C起始信号
        I2C_send_byte(PCF8591_ADDR+0);    //发送PCF8591地址加读写方向位0(写)
        if (!Test_ACK())                //检测是否发送成功(应答)
        {
            return(0);
        }
        I2C_send_byte(CON);                //发送控制字节
        Master_ACK(0);                    //发送非应答
        I2C_Start();                    //重发起始信号
        I2C_send_byte(PCF8591_ADDR+1);    //改变读写方向(读)
        if (!Test_ACK())                //检测是否发送成功(应答)
        {
            return(0);
        }
        *DAT = I2C_read_byte();            //把读取的值赋给形参
        Master_ACK(0);                    //主机发送非应答
        I2C_Stop();                        //I2C停止信号
        return(1);                        //成功返回1
    }
    
    /*====================================
    函数:void main()
    描述:主函数
    ====================================*/
    void main()
    {
        INT8U ADC_Value; //存放ADC的值
        I2C_init();
        while(1)
        {
            //读取通道2转换的值,单端输入。
            if(!ADC_Read(0x02, &ADC_Value))    LED1 = 1;    else;    LED1 = 0;
    
            //把AD转换得的数量在送给PCF8591转为模拟量控制模拟量输出口的小灯
            if(!DAC_OUT(ADC_Value))    LED2 = 1;    else    LED2 = 0;
            
            //显示读出的模拟量
            Display(ADC_Value);
    
            if (ADC_Value > 150)    BEEP = 0;    else    BEEP = 1;
            Delay_Ms(5);
        }    
    }
  • 相关阅读:
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    软件工程实践总结
  • 原文地址:https://www.cnblogs.com/darren-pty/p/13295150.html
Copyright © 2011-2022 走看看