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);
        }    
    }
  • 相关阅读:
    Java实现 LeetCode 767 重构字符串(ASCII的转换)
    Java实现 LeetCode 767 重构字符串(ASCII的转换)
    Java实现 LeetCode 767 重构字符串(ASCII的转换)
    Java实现 LeetCode 766 托普利茨矩阵(暴力)
    Java实现 LeetCode 766 托普利茨矩阵(暴力)
    Java实现 LeetCode 766 托普利茨矩阵(暴力)
    Java实现 LeetCode 765 情侣牵手(并查集 || 暴力)
    219. Contains Duplicate II
    217. Contains Duplicate
    135. Candy
  • 原文地址:https://www.cnblogs.com/darren-pty/p/13295150.html
Copyright © 2011-2022 走看看