51单片机AD/DA转换PCF8951·

51单片机第16章-- AD/DA实验

Hugh

Hugh

4745 0

16.1 模数转换(A/D)、数模转换(D/A)芯片 PCF8591 简介

PCF8591 是一款单电源供电的 8 位 CMOS 数据采集芯片,具有 4 个模拟信号输入通道、一个模拟信号输出通道以及一个 I2C 总线通信接口。与前面讲解的 I2C 芯片相同,通过管脚 A0、A1 和 A2 来配置芯片硬件地址,因此,在同一 I2C 总线上最多可同时连接 8 个 PCF8591 芯片。主机与该芯片的地址、控制和数据信息的通信都是通过 I2C 总线接口来实现的。芯片引脚定义及芯片封装如下图所示。该芯片有两种封装形式:一种双列直插式 DIP16、另一种贴片 SOP16 模式,两种封装的管脚顺序是一样的。

引脚 1-4 为四个数模(A/D)转换通道的模拟数据输入端,名称分别为 AIN0~AIN3。引脚 5-7 为芯片硬件地址配置引脚 A0-A2。引脚 16、8 分别为芯片电源正负极 VDD 和 VSS,供电电压范围为 2.5V-6V。引脚 9、10 分别为 I2C 总线数据、时钟引脚 SDA 和 SCL。管脚 11 为晶振输入/输出端口 OSC,管脚 12 为外部或内部晶振选择端口 EXT,EXT 接地时芯片选择内部时钟,EXT 连接 VDD 时,芯片选择 OSC 引脚的外部时钟。管脚 15、13 分别为数模(D/A)转换的模拟输出端 AOUT 和模拟地 AGND。管脚 14 为芯片转换的基准电压输入引脚 VREF。RY-51 开发板 PCF8591 原理图设计如下图所示: PCF8591 地址格式如下图所示: 8 位地址有固定值和可编程部分组成。高 4 位为固定值“1001”,紧接着 3 位为可配置位 A2-A0,最后一位为读写方向位,0 表示读,1 表示写。原理图中将 A2-A0 直接接地,因此,写操作时地址为 0x91,读操作时地址为 0x90。

PCF8591 控制寄存器如下图所示,bit1-bit0 为 A/D 通道选择为 00-11 分别表示通道 0-通道 3。Bit2 为通道自动累加选择位,为 1 时选择自动累加。Bit3 为系统功能预留位,常设为 0。Bit5-bit4 为单端或差分输入选择位,如下图所示。Bit6 为模拟输出选择位,为 1 时选择模拟输出。 PCF8591 数字量转模拟量(DAC)数据寄存器以及计算公式如下图所示: DAC 寄存器为 8 位,数字量取值范围为 0~255,模拟通道模拟量输出公式如上图所示。RY-51 开发板设计原理图中将 VAGND 接地,VREF 接 5V,因此,模拟通道最大输出为 255/256 x 5V,最小输出为 0。主机将相应的数字量通过 I2C 总线写入 DAC 数据寄存器后,PCF8951 通过相应的算法转换成模拟量输出。DAC转换通信协议如下:

如上图所示 DAC 转换步骤如下: 1. I2C 起始信号; 2. 主机发送器件地址,方向为“写”; 3. 主机发送控制字节数据; 4. 主机发送 DAC 字节数据 1~N; 5. 停止信号。

如上图所示,在发送 DAC 数据的同时,模拟通道 VAOUT 输出为前一次 DAC 数据寄存器的数值。DAC 函数如下所示:

//数字量转模拟量函数,val为待转换的数字量,模拟量从Vout引脚输出
void DAC(unsigned char val)
{
    Start_I2C();//启动I2C总线
    //寻址PCF8591,方向为”写”,如未响应,则停止并返回
    if(Wr_I2C(0x48<<1)) {Stop_I2C();return;}
    Wr_I2C(0x40);//写入控制字,使能模拟输出
    Wr_I2C(val); //发送DAC字节
    Stop_I2C();  //结束I2C总线
}

模拟量转数字量(ADC)工作原理如下:

将 0-5V 电压转换为 8 位的数字量,数字量范围为 0-255,将 5V 电压均分为 256 等份,当输入电压落在几等分处,则转换的数字量为几。当输入电压为 0-1/256 * 5V 时,转换的数字量为 1,当输入电压为 1/256 * 5V-2/256 * 5V 时,转换的数字量为 2,依次类推,当输入电压为 254/256 * 5V-255/256 * 5V 时,转换的数字量为 255。ADC 转换时序如下图所示,首先为寻址 PCF8951,方向为读,然后读取数据。加上 PCF8951 设置等相关语句,ADC 转换 I2C 通信步骤如下:

1. I2C 起始信号; 2. 主机发送器件地址,方向为“写”; 3. 主机发送控制字节数据,并设置相应通道; 4. 重新发送 I2C 起始信号; 5. 主机发送器件地址,方向为“读”; 6. 读取转换数据,并抛弃; 7. 再次读取数据,并返回; 8. 停止信号。

ADC 转换时序如下图所示:

上述步骤中 1-5 很好理解,6、7 的出现是什么原理,第六条语言为读取当前转换值,如上图所示,由于芯片的 AD 转换是需要时间的,当前返回值为上一次的转换值,当前的转换值会在下一次读数据时出现,因此,第 7 条语句读到的数据是写第 6 条语句时的转换值,因此将第 7 条语句的值返回。ADC 函数如下图所示:

//模拟量转数字量函数,将通道chn的值转换为数字量val,并返回val
unsigned char ADC(unsigned char chn)
{
    unsigned char val;//数字量存储变量

    Start_I2C();//启动I2C总线
    //寻址PCF8591,方向为”写”,如未响应,则停止并返回0
    if(Wr_I2C(0x48<<1)){Stop_I2C();return 0;} 
    Wr_I2C(0x40|chn);//写入控制字并选择转换通道chn的数据

    Start_I2C();//重新启动I2C总线
    Wr_I2C((0x48<<1)|0x01);//寻址PCF8591,方向为”读”
          RdACK_I2C();//读一次数据并抛弃,下一次数据才是转换的数据
    val = RdNAK_I2C();//读数据并返回

    return val;
}

按照惯例,驱动文件完整代码如下,"Drive_PCF8591.h"代码:

#ifndef __PCF8591_H__
#define __PCF8591_H__

//数字量转模拟量函数,val为待转换的数字量,模拟量从Vout引脚输出
extern unsigned char ADC(unsigned char chn);
//模拟量转数字量函数,将通道chn的值转换为数字量val,并返回val
extern void DAC(unsigned char val);

#endif

"Drive_PCF8591.c"代码:

#include<reg52.h>
#include"Drive_I2C.h"
//数字量转模拟量函数,val为待转换的数字量,模拟量从Vout引脚输出
void DAC(unsigned char val)
{
    Start_I2C();//启动I2C总线
    //寻址PCF8591,方向为”写”,如未响应,则停止并返回
    if(Wr_I2C(0x48<<1)) {Stop_I2C();return;}
    Wr_I2C(0x40);//写入控制子,使能模拟输出
    Wr_I2C(val); //发送DAC字节
    Stop_I2C();  //结束I2C总线
}
//模拟量转数字量函数,将通道chn的值转换为数字量val,并返回val
unsigned char ADC(unsigned char chn)
{
    unsigned char val;//数字量存储变量

    Start_I2C();//启动I2C总线
    //寻址PCF8591,方向为”写”,如未响应,则停止并返回0
    if(Wr_I2C(0x48<<1)){Stop_I2C();return 0;} 
    Wr_I2C(0x40|chn);//写入控制字并选择转换通道chn的数据

    Start_I2C();//重新启动I2C总线
    Wr_I2C((0x48<<1)|0x01);//寻址PCF8591,方向为”读”
          RdACK_I2C();//读一次数据并抛弃,下一次数据才是转换的数据
    val = RdNAK_I2C();//读数据并返回

    return val;
}

下面我们进行简单的应用,大家都知道,热敏电阻和光敏电阻这两种元器件。热敏电阻的特性为温度不一样是电阻值不一样,光敏电阻的特性为光照强度不一样则电阻不一样。因此,可以用ADC转换器去采集这两种元器件的电阻值便可以知道温度和光照强度是多少了。

16.2 热敏电阻

热敏电阻器是敏感元件的一类,按照温度系数不同分为正温度系数热敏电阻器(PTC)和负温度系数热敏电阻。热敏电阻器的典型特点是对温度敏感,不同的温度下表现出不同的电[阻值。正温度系数热敏电阻在温度越高时电阻值越大,负温度系数热敏电阻在温度越高时电阻值越低,它们同属于半导体器件。

16.3 光敏电阻

光敏电阻(photoresistor or light-dependent resistor,后者缩写为 ldr)或光导管,常用的制作材料为硫化镉,另外还有硒、硫化铝、硫化铅和硫化铋等材料。这些制作材料具有在特定波长的光照射下,其阻值迅速减小的特性。这是由于光照产生的载流子都参与导电,在外加电场的作用下作漂移运动,电子奔向电源的正极,空穴奔向电源的负极,从而使光敏电阻器的阻值迅速下降。光敏电阻是用硫化隔或硒化隔等半导体材料制成的特殊电阻器,其工作原理是基于内光电效应。光照愈强,阻值就愈低,随着光照强度的升高,电阻值迅速降低,亮电阻值可小至 1K Ω以下。光敏电阻对光线十分敏感,其在无光照时,呈高阻状态,暗电阻一般可达 1.5M Ω。光敏电阻的特殊性能,随着科技的发展将得到极其广泛应用。

16.4 综合试验

如电路原理图所示,我们分别在ADC器件的通道A0-A3连接可变电阻,固定电压,热敏电阻、光敏电阻,并将四个通道采集到的电压值显示在1602液晶显示器上,尝试改变电阻,温度以及光照强度,观察4通道的电压变化。主程序如下所示:

#include<reg52.h>
#include"Drive_1602.h"
#include"Drive_PCF8591.h"

#define uchar unsigned char
#define  uint unsigned int

#define FOSC 11059200 //单片机晶振频率
#define T_1ms (65536 - FOSC/12/1000)  //定时器初始值计算

sbit DU = P2^7;//数码管段选、位选引脚定义
sbit WE = P2^6; 

uchar str[10]=0;
uchar T_flag  = 0;
uchar val  = 0;
void ValueToString(unsigned char *str,unsigned char val);
void main()
{
    Init_1602();//1602初始化
    P0 = 0xff;//关闭所有数码管
    WE = 1;
    WE = 0; 
    TMOD = 0x01;     //定时器工作模式配置
    TL0  = T_1ms;   //装载初始值
    TH0  = T_1ms>>8;
    TR0  = 1;        //启动定时器
    ET0  = 1;        //允许定时器中断
    EA   = 1;        //开总中断


    Disp_1602_str(1,1,"A0  A1  A2  A3  ");//显示通道初始化
     while(1)
     {
        if(T_flag)
        {
            T_flag = 0;

            val = ADC(0);
            DAC(val);//将通道零采集到的数据DA输出
            ValueToString(str,val);
            Disp_1602_str(2,1,str);//显示通道0电压

            val = ADC(1);
            ValueToString(str,val);
            Disp_1602_str(2,5,str);//显示通道1电压

            val = ADC(2);
            ValueToString(str,val);
            Disp_1602_str(2,9,str);//显示通道2电压
    
            val = ADC(3);
            ValueToString(str,val);
            Disp_1602_str(2,13,str);//显示通道3电压
        }
     }
}
//将AD采样数值转化成字符串
void ValueToString(unsigned char *str,unsigned char val)
{
    //电压=采样值*5V/255
    val = (val*50)/255; //放大了10倍
    str[0] = (val/10) + '0';//整数位
    str[1] = '.';//小数点
    str[2] = (val%10) + '0';//小数位
}
//定时器0中断子程序,定时1ms
void timer0() interrupt 1
{
    static uint T_500ms = 0;

    TL0 = T_1ms;//重装初始值
    TH0 = T_1ms>>8; 

    T_500ms++;
    if(T_500ms>=500)//500ms,置位T_flag
    {
        T_500ms = 0;
        T_flag = 1; 
    }
}

16.5本章小结

本章详细介绍了AD/DA器件PCF8591的工作原理,通信时序,以及驱动程序的编写,并完成了简单的应用。

所属系列

从当前文章继续阅读它所在合集中的前后内容。

51单片机 第 16 / 19 篇
查看合集

本专栏C语言开发为基础,旨在深入解析51单片机技术开发,嵌入式C语言编程。 订阅了本专栏您将可以收获的技能: 1. 系统学习 51单片机开发 2. 掌握嵌入式C语言开发流程 3. 深度剖析51单片机工作原理 4. 了解更多嵌入式开发原理、流程 适宜人群: 1. 所有想了解 51单片机开发的人 2. 从事嵌入式开发的工程师 3. 想要了解C语言、单片机等技术之外的内容的人 作为订阅福利,我们建立了微信技术交流群,大家在这里可以认识更多做朋友。需要的朋友加微信:「RYMCU交流1群」。欢迎关注微信公众号「rymcucom」,订阅最新内容。

相关文章

优先推荐同专题、同标签和同作者内容,补足热门文章。

评论 0

登录 后参与评论

评论

成为第一个评论的人