4.8 51单片机-PCF8591(ADC/DAC)转换芯片

Posted DS小龙哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4.8 51单片机-PCF8591(ADC/DAC)转换芯片相关的知识,希望对你有一定的参考价值。

4.8 PCF8591(ADC/DAC)转换芯片

4.8.1 原理图

当前实验板上没有PCF8591芯片,这里采用外接模块的形式使用。

 图4-8-1 PCF8591模块实物图

 

图4-8-2 原理图

通过原理图得到的重要信息: PCF8591芯片地址线全部接GND。也就是当前模块的地址固定为: 1001000

4.8.2 PCF8591模块功能介绍

在一个完整的单片机系统中,A/D转换芯片往往是必不可少的;PCF8591是一款具有I2C总线接口的A/D转换芯片。在与CPU的信息传输过程中仅靠时钟线SCL和数据线SDA就可以实现。

PCF8591具有8位AD/DA转换器,有4个模拟输入口、1个模拟输出口和1个串行I²C总线接口。PCF8591的3个地址引脚A0, A1和A2可用于硬件地址编程,飞利浦公司规定A/D转换芯片的内部器件地址为1001,这样就允许在同个I2C总线上接入8个PCF8591器件,而无需额外的硬件。在PCF8591器件上输入输出的地址、控制和数据信号都是通过双线双向I2C总线以串行的方式进行传输。

I2C总线是Philips(飞利浦)公司推出的串行总线,整个系统仅靠数据线(SDA)和时钟线(SCL)实现完善的全双工数据传输,即CPU与各个外围器件仅靠这两条线实现信息交换。I2C总线系统与传统的并行总线系统相比具有结构简单、可维护性好、易实现系统扩展、易实现模块化标准化设计、可靠性高等优点。

 图4-8-3 PCF8591内部框架图

图4-8-4 芯片引脚图

PCF8591的电源电压典型值为5V,所有输入信号的电压值都不能超过 VCC,即+5V,否则可能会损坏 ADC 芯片。

引脚功能介绍:

(1)AIN0~AIN3:模拟信号输入端。

(2)A0~A3:引脚地址端。

(3)VDD、VSS:电源端。(2.5~6V)

(4)SDA、SCL:I2C总线的数据线、时钟线。

(5)OSC:外部时钟输入端,内部时钟输出端。

(6)EXT:内部、外部时钟选择线,使用内部时钟时EXT接地。

(7)AGND:模拟信号地。

(8)AOUT:D/A转换输出端。

(9)VREF:基准电源端

PCF8591的ADC是逐次逼近型的,转换速率算是中速,它的速度瓶颈在 I2C 通信上。由于 I2C 通信速度较慢,所以最终的 PCF8591 的转换速度,直接取决于 I2C 的通信速率。由于 I2C 速度的限制,所以 PCF8591 算是个低速的 AD 和 DA 的集成,主要应用在一些转换速度要求不高,希望成本较低的场合,比如电池供电设备,测量电池的供电电压,电压低于某一个值,报警提示更换电池等类似场合。

4.8.3 PCF8591寄存器介绍

PCF8591遵循标准IIC协议,编程肯定符合这个协议的。

单片机对 PCF8591 进行初始化,一共发送三个字节即可;第一个字节和 EEPROM 类似,是器件地址字节,其中 7 位代表地址, 1 位代表读写方向。地址高 4位固定是 0b1001,低三位是 A2, A1, A0,这三位在模块电路上都接了 GND,因此也就是 0b000 (这里0b是二进制的前缀)。

(1). PCF8591的设备地址

图4-8-5 设备地址

其中:

高位是器件固定的地址:1001

低位A2 A1 A0 由硬件电路决定

R/W是读写位:1表示读  0表示写

本模块的硬件地址固定为: 0 0 0  ,得出PCF8591的地址:0x91(读方向)  0x90(写方向)

(2). PCF8591的控制字节

发送到 PCF8591 的第二个字节将被存储在控制寄存器,用于控制 PCF8591 的功能。

其中第 3 位和第 7 位是固定的 0,另外 6 位各自有各自的作用。

图4-8-6 控制字节

控制字节的第6位是DAC使能位,这一位置1表示 DAC 输出引脚使能,会产生模拟电压输出功能。

第 4位和第 5位可以实现把 PCF8591的 4路模拟输入配置成单端模式和差分模式,具体的配置可以看下面图片。

图4-8-7 模式配置

控制字节的第 2 位是自动增量控制位,如果此位为1,就使用自动增量;自动增量的意思就是,比如一共有 4 个通道,当4个通道全部使用的时候,读完了通道 0,下一次再读,会自动进入通道 1 进行读取,不需要指定下一个通道,由于 A/D 每次读到的数据,都是上一次的转换结果,所以在使用自动增量功能的时候,要特别注意,当前读到的是上一个通道的值。

控制字节的第 0 位和第 1 位是通道选择位, 00、 01、 10、 11 代表了从 0 到 3 的一共4 个通道选择。

图4-8-8 通道选择

如果使用通道0为单端输入,采集ADC转换的数据,那么控制字节就为:  0000 0000 -->0x00

如果使用通道1为单端输入,采集ADC转换的数据,那么控制字节就为:  0000 0001 -->0x01

如果使用通道2为单端输入,采集ADC转换的数据,那么控制字节就为:  0000 0010 -->0x02

如果使用通道3为单端输入,采集ADC转换的数据,那么控制字节就为:  0000 0011 -->0x03

如果使用DAC输出功能,那么控制字节就为:  0100 0000 -->0x40

4.8.4 读写时序介绍

(1) 设置PCF8591转换通道读取ADC转换数据的时序

图4-8-9

示例代码:

/*
函数功能: 设置ADC转换通道,并返回采集的数据值
ch的范围:0x00 0x01 0x02 0x03  分别代表通道0~3
*/
u8 PCF8591_GetADC_CHx(u8 ch)

    u8 dat;
    IIC_SendStart();//起始信号
    IIC_SendOneByte(PCF8591_WRITE_ADDR);//发送设备地址
    IIC_GetAck();//获取应答
    IIC_SendOneByte(ch); //发送控制字节
    IIC_GetAck();//获取应答
    
    IIC_SendStart();//起始信号
    IIC_SendOneByte(PCF8591_READ_ADDR);//发送设备地址
    IIC_GetAck();//获取应答
    dat=IIC_RecvOneByte();//读取数据
    IIC_SendAck(1); //发送非应答
    IIC_SendStop(); //停止信号
return dat;

(2). 设置DAC通道输出数据时序

图4-8-10

示例代码:

/*
函数功能:设置DAC通道输出的值
*/
void PCF8591_SetDAC_Data(u8 val)

    IIC_SendStart();//起始信号
    IIC_SendOneByte(PCF8591_WRITE_ADDR);//发送设备地址
    IIC_GetAck();//获取应答
    IIC_SendOneByte(0x40); //发送控制字节
    IIC_GetAck();//获取应答
    IIC_SendOneByte(val); //设置AD值
    IIC_GetAck();//获取应答
IIC_SendStop();//停止信号

4.8.9 PCF8591完整示例代码

PCF8591模块接在实验板P2.1(SCL)和P2.0(SDA)口上,在主函数里按照顺序读取4个通道ADC值,打印到串口终端,并且使用其中一个ADC通道检测的输入值,作为DAC通道的输出值,用以测试DAC通道功能。

图 4-8-11

(硬件平台说明:CPU是STC90C516RD 、晶振频率12MHZ 、工作在12T模式下、一个机器周期为1us时间)

示例代码:

#include <reg51.h>
/*
硬件连接: 
    SCL---P2.1
    SDA---P2.0
*/
//数据线
sbit IIC_SDA=P2^0;
//时钟线
sbit IIC_SCL=P2^1;

/*
函数功能: 发送起始信号
当时钟线为高电平的时候,数据线由高电平变为低电平的过程
*/
void IIC_SendStart(void)

    u8 i=0;
    IIC_SCL=1;
    IIC_SDA=1;
    IIC_SDA=0;
    IIC_SCL=0; //总线开始工作、开始读写数据

/*
函数功能: 停止信号
当时钟线为高电平的时候,数据线由低电平变为高电平的过程
*/
void IIC_SendStop(void)

    u8 i=0;
    IIC_SCL=0;
    IIC_SDA=0;
    IIC_SCL=1;
    IIC_SDA=1;

/*
函数功能: 获取应答信号
返 回 值: 0表示获取到应答 1表示没有获取到应答
*/
u8 IIC_GetAck(void)

    u8 i=0;
    IIC_SDA=1; //上拉
    IIC_SCL=0;
    IIC_SCL=1;
    while(IIC_SDA)
    
        i++;
        if(i>250)return 1; //获取不到应答
    
    IIC_SCL=0;
    return 0;

/*
函数功能: 发送应答信号
函数参数:0表示应答 1表示非应答
*/
void IIC_SendAck(u8 ack)

    u8 i=0;
    IIC_SCL=0;
    if(ack)IIC_SDA=1; //发送非应答
    else IIC_SDA=0; //发送应答
    IIC_SCL=1;
    IIC_SCL=0;

/*
函数功能: 发送一个字节数据
*/
void IIC_SendOneByte(u8 dat)

    u8 j=0,i=0;
    for(j=0;j<8;j++)
    
        IIC_SCL=0;
        if(dat&0x80)IIC_SDA=1;
        else IIC_SDA=0;
        IIC_SCL=1;
        dat<<=1;
    
    IIC_SCL=0;

/*
函数功能: 接收一个字节数据
*/
u8 IIC_RecvOneByte(void)

    u8 i=0,j=0;
    u8 dat=0;
    for(j=0;j<8;j++)
    
        IIC_SCL=0;
        IIC_SCL=1;
        dat<<=1; //表示默认收到0
        if(IIC_SDA)dat|=0x01;
    
    IIC_SCL=0;
    return dat;

/*
函数功能: 设置ADC转换通道,并返回采集的数据值
ch的范围:0x00 0x01 0x02 0x03  分别代表通道0~3
*/
u8 PCF8591_GetADC_CHx(u8 ch)

    u8 dat;
    IIC_SendStart();//起始信号
    IIC_SendOneByte(PCF8591_WRITE_ADDR);//发送设备地址
    IIC_GetAck();//获取应答
    IIC_SendOneByte(ch); //发送控制字节
    IIC_GetAck();//获取应答
    
    IIC_SendStart();//起始信号
    IIC_SendOneByte(PCF8591_READ_ADDR);//发送设备地址
    IIC_GetAck();//获取应答
    dat=IIC_RecvOneByte();//读取数据
    IIC_SendAck(1); //发送非应答
    IIC_SendStop(); //停止信号
    return dat;

/*
函数功能:设置DAC通道输出的值
*/
void PCF8591_SetDAC_Data(u8 val)

    IIC_SendStart();//起始信号
    IIC_SendOneByte(PCF8591_WRITE_ADDR);//发送设备地址
    IIC_GetAck();//获取应答
    IIC_SendOneByte(0x40); //发送控制字节
    IIC_GetAck();//获取应答
    IIC_SendOneByte(val); //设置AD值
    IIC_GetAck();//获取应答
    IIC_SendStop();//停止信号


int main()

    u8 ch0,ch1,ch2,ch3;      //存放ADC通道检测的值
    u8 dac_val;  //存放DAC输出的值
    UART_Init(); //初始化串口波特率为4800
    while(1)
    
        /*1. 转换并读取通道0的AD值: 模块上通道0默认接可调0-5v的可变电阻*/
        ch0=PCF8591_GetADC_CHx(0x00);//模块上标注是AIN3
        printf("(CH0)可变电阻=%d\\r\\n",(int)ch0);
        /*2. 转换并读取通道1的AD值: 模块上通道1默认接光敏电阻*/
        ch1=PCF8591_GetADC_CHx(0x01); //模块上标注是AIN0
        printf("(CH1)光敏电阻=%d\\r\\n",(int)ch1);
        /*3. 转换并读取通道2的AD值: 模块上通道2默认接热敏电阻*/
        ch2=PCF8591_GetADC_CHx(0x02);//模块上标注是AIN1
        printf("(CH2)热敏电阻=%d\\r\\n",(int)ch2);
        /*4. 转换并读取通道3的AD值: 模块上通道3默认悬空没有接检测点*/
        ch3=PCF8591_GetADC_CHx(0x03);//模块上标注是AIN2
        printf("(CH3)=%d\\r\\n",(int)ch3);
        printf("---------------------\\r\\n");
        /*5. 设置DAC输出值*/
        PCF8591_SetDAC_Data(ch0); //使用通道1测量的可变电阻值传给DAC输出
        DelayMs(1000); //延时一段时间
    

 

图4-8-12 

以上是关于4.8 51单片机-PCF8591(ADC/DAC)转换芯片的主要内容,如果未能解决你的问题,请参考以下文章

Linux驱动开发-编写PCF8591(ADC)芯片驱动

Linux驱动开发-编写PCF8591(ADC)芯片驱动

单片机引脚P2.0/A8是啥意思,还有P0.0/AD0?还有我想接一个pcf8591t和lm386芯片不知道该怎样接

基于51单片机怎么让pcf8591输出正弦波形

51单片机AD转换pcf8591+数码管显示+Proteus仿真二

51单片机 PCF8591模数与数模转换实例+Proteus仿真