蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片

上期我们学习了DS1302实时时钟的基本使用,现在我们来学习PCF8591A/D&D/A转换芯片的相关内容

蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片

    • PCF8591A/D&D/A转换芯片
      • 引脚图
      • 原理图
      • PCF8591的读写地址
      • 控制指令
      • AD转换(模拟量->数字量)
      • DA转换
    • IIC协议
      • 起始时序&结束时序
      • 等待应答&发送应答
      • 写时序
      • 读时序
    • PCF8591A/D&D/A的基本控制方法&代码
    • 实践部分
      • 1.任务要求
      • 2.实现思路
      • 3.代码展示
        • 3.1.main.c
        • 3.2.数码管显示函数
        • 3.3.官方提供的iic文件
    • 总结

PCF8591A/D&D/A转换芯片

PCF8591是具有I2C 总线接口的8 位A/D 及D/A 转换器。具有以下特点:
【1】 单电源供电
【2】 工作电压2.5~6.0V
【3】 低待机电流(低功耗)
【4】 使用IIC通信接口
【5】 具有三个可编程地址引脚
【6】 4个模拟输入通道可控制为单端输入或差分输入
【6】 可控制自动递增通道选择
【7】 模拟输出电压范围VSS~VDD
【8】 八位逐次逼近AD转换
【9】 一路模拟输出DA转换

引脚图

蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第1张图片
【1】 AIN0~3 :四路模拟输入,用于AD转换
【2】 A0~A2:可编程地址
【3】 VSS/VDD:负/正电压,采用双电源供电,模拟输出电压范围VSS~VDD
【4】 SDA:IIC接口数据线
【5】 SCL:IIC接口时钟线
【6】 OSC:时钟输入/输出引脚,外部时钟输入,内部时钟输出。
【7】 EXT:时钟输入输出开关,使用内部时钟时接地(为低电平)
【8】AGND:模拟信号的地
【9】VREF:基准电压,原理图上直接接的是VCC(5V)
【10】VOUT:模拟输出引脚

原理图

蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第2张图片

以上是PCF8591的硬件原理图,下面是4个模拟输入和模拟输出的产生方式。

1.AIN0/AOUT:直接接到排针J3上面,可以提供直接提供电压模拟输入&直接测量电压
蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第3张图片

2.AIN1:通过光敏电阻分压模拟输入
蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第4张图片
3.AIN2:先做保留,下次介绍
蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第5张图片
4.AIN3:通过一个继电器分压输入。
蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第6张图片

PCF8591的读写地址

PCF8591使用的是IIC通信协议,IIC可用于多机通信,因此在通信之前需要寻址,以下是PCF8591的读写地址。
蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第7张图片

  • 高四位在出厂之前就已经确定,属于不可修改部分
  • A0~A2是可编程部分,通过电路将对应的引脚拉高/拉低来确定。
  • 第八位是读写控制位,为1时表示通信方向为,PCF写数据,单片机读数据。为0时则相反。

控制指令

在通信寻址成功之后,我们需要对PCF8591写入控制指令,设置芯片的工作模式。
蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第8张图片
其中:

  • 第4位和第八位固定为0,不可更改。
  • 第7位为模拟输出使能位,为1时芯片工作为DA转换模式,通过IIC接口写入0~255之间的值,可以输出对应的模拟电压值。(具体计算公式在后面介绍)
  • 第5、6位可以选择模拟输入的方式,单端输入、双端输入、单端+双端输入
  • 第3位为自动增量标志,为1时,每完成一次AD转换,输入通道自动切换到下一个通道(0->1->2->3->0往复循环),为0时不会自增
  • 第1、2位共同控制AD转换的通道,具体对应关系如上图。

AD转换(模拟量->数字量)

AD转换是将时间连续和幅值连续的模拟量转换为时间离散、幅值也离散的数字量。使输出的数字量与输入的模拟量成正比。模拟量与输出量的关系如下图

1.单端输入
蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第9张图片
其中:

  • V-REF为基准电压,电路图上连接到VCC,理论值为5V,
  • V-AGND为模拟地,电路图上连接到GND,电压值为0V,
  • V-LSB为微分非线性dnl,即芯片可以区分最小模拟电压值,

举个栗子:
芯片使用通道0进行AD转换,在通道0输入一个1.0V的电压,那么对应的数字量 = 256*(V-AIN - V-AGND)/ (V-REF - V-AGND) = 256 * 1.0/5.0 = 51 = 0x33
特别的:当输入电压>(V-REF - V-AGND)以上时,输出为255不变

2.差分输入
蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第10张图片
原理和单端输入相似,不做介绍。

DA转换

DA转换则是将数字量转换为模拟量进行输出,与AD转换相反。
蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第11张图片
计算公式如图,特别的,当V-AGND = 0V 时,V-AOUT= 256*数字量/V-REF
注意:输入数字量的取值为0~255, 输出模拟量的范围是V-AGND~V-REF。

IIC协议

I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。
规定:在传输过程在,在SCL为低电平时,SDA允许改变,在SCL为高电平时读取SDA的电平,SDA不允许改变。

起始时序&结束时序

1.起始信号:SCL为高电平时,SDA产生下降沿,为开始信号
2.结束信号,SCL为高电平时,SDA产生上升沿,为结束信号
蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第12张图片

官方提供代码如下:

//总线启动条件
void IIC_Start(void)
{
    SDA = 1;        
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 0;        //SCL为高电平时,SDA产生下降沿,为开始信号
    IIC_Delay(DELAY_TIME);
    SCL = 0;	    
}

//总线停止条件
void IIC_Stop(void)
{
    SDA = 0;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 1;        //SCL为高电平时,SDA产生上升沿,为结束信号
    IIC_Delay(DELAY_TIME);
}

等待应答&发送应答

在IIC协议中,主机发送一个字节数据后,从机需要产生应答位(Ack),为0时表示应答成功,此时主机可以选择继续发送或结束本次通信;为1时表示应答失败,此时从机将不再介绍来自主机的数据。
当通信方向为单片机->PCF8591时,单片机每发送一个字节数据,需要等待PCF8591应答,反之则需要单片机发送应答。
应答的规则如下:
蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第13张图片
【1】 一个字节发送完毕,主机释放数据线(将SDA置为1)
【2】 从机应答,将SDA置为0
【3】 等待SCL为高电平读取应答位,
【4】 读取完毕,从机释放总线
【5】 主机的到应答,选择继续发送数据或结束通信

官方提供代码如下:

//发送应答
void IIC_SendAck(bit ackbit)
{
    SCL = 0;            //将SCL拉低,写入应答位
    SDA = ackbit;  					// 0:应答,1:非应答
    IIC_Delay(DELAY_TIME);
    SCL = 1;                        //将SCL拉高,从机读取应答位  
    IIC_Delay(DELAY_TIME);
    SCL = 0; 
    SDA = 1;                //主机释放SDA,结束应答
    IIC_Delay(DELAY_TIME);
}

//等待应答
bit IIC_WaitAck(void)
{
    bit ackbit;
	
    SCL  = 1;   //主机释放SCL,等待从机拉低SCL,发送应答     
    IIC_Delay(DELAY_TIME);
    ackbit = SDA;       //从机发送应答后,释放SCL   
    SCL = 0;            //主机再次拉低SCL,为下一次发送数据或者结束通信做准备
    IIC_Delay(DELAY_TIME);
    return ackbit;
}

写时序

蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第14张图片
官方提供代码如下:

//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
    unsigned char i;

    for(i=0; i<8; i++)  
    {
        SCL  = 0;       //SCL拉低
        IIC_Delay(DELAY_TIME);
        if(byt & 0x80) SDA  = 1;    //主机SDA写入数据位1/0
        else SDA  = 0;
        IIC_Delay(DELAY_TIME);
        SCL = 1;                    //SCL拉高,从机读取SDA电平
        byt <<= 1;                  
        IIC_Delay(DELAY_TIME);
    }               //重复八次,完成一个字节的发送
    SCL  = 0;       //将SCL拉低
}

读时序

蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第15张图片
官方提供代码如下:

//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
    unsigned char i, da;
    for(i=0; i<8; i++)      //
    {   
    SCL = 1;            //将SCL拉高,
	IIC_Delay(DELAY_TIME);
	da <<= 1;           
	if(SDA) da |= 1;    //主机读取SDA的状态
	SCL = 0;            //将SDA拉低,从机写入SDA状态
	IIC_Delay(DELAY_TIME);
    }               //循环八次,完成一个字节数据的读取
    return da;      //返回读取的字节
}

PCF8591A/D&D/A的基本控制方法&代码

1.AD转换的具体流程
【1】 单片机发送开始通信信号
【2】 单片机寻址,与PCF8591通信(方向:单片机-> PCF8591)
【3】 等待PCF8591应答,
【4】 应答成功,发送控制指令(AD转换,单端输入,通道为channel)
【5】 等待PCF8591应答,
【6】 应答成功,单片机结束通信
【7】 单片机发送开始通信信号
【8】 单片机寻址,与PCF8591通信(方向: PCF8591->单片机)
【9】 等待PCF8591应答,
【10】 PCF8591发送AD转换后的数字量
【11】 单片机读取数据,并且不应答
【12】 单片机结束通信
【13】 单片机对读取的数据进行处理,

代码如下:

//PCF8591AD转换函数,channel为模拟输入的通道
unsigned char  PCF8591_AD_Conversion(unsigned char channel )
{
    unsigned char dat = 0;
    IIC_Start();        //iic通信开始
    IIC_SendByte(PCF8591_Write_Addr);   //写入从机PCF8591的写地址,
    if(IIC_WaitAck())           //等待从机应答,为1表示不应答,为0表示应答
    {   
        IIC_Stop();         //如果不应答,则结束通信
        return 0;
    }
    IIC_SendByte(0x00 | channel);   //写入控制指令,单端输入,AD转换,选择通道channel
    IIC_WaitAck();      //这一步不能少,否则读出来一直是255
    IIC_Stop();         //结束本次通信,改变收发方向
    
    IIC_Start();        //iic通信开始
    IIC_SendByte(PCF8591_Read_Addr);    //写入从机PCF8591的读地址,
    if(IIC_WaitAck())           //等待应答
    {
        IIC_Stop();          //如果不应答,则结束通信
        return 0;
    }
    dat = IIC_RecByte();    //读取AD转换出的数字量 
    IIC_SendAck(1);         //不应答

    IIC_Stop();             //结束通信
    SEG_Arr[0] = dat/100%10;    //计算数字量的百位
    SEG_Arr[1] = dat/10%10;     //计算数字量的十位
    SEG_Arr[2] = dat%10;        //计算数字量的各位
    
    return dat;
}

2.DA转换的具体流程
【1】 单片机发送开始通信信号
【2】 单片机寻址,与PCF8591通信(方向:单片机-> PCF8591)
【3】 等待PCF8591应答,
【4】 应答成功,发送控制指令(DA转换)
【5】 等待PCF8591应答,
【6】 应答成功,单片机继续发送DA转换的数字量
【7】 等待PCF8591应答,
【8】 应答成功,PCF8591进行DA转换
【9】 单片机结束通信

代码如下:

//PCF8591DA转换函数,dat为输入的数字量
void PCF8591_DA_Conversion(unsigned  char dat)
{
    IIC_Start();        //iic通信开始
    IIC_SendByte(PCF8591_Write_Addr);   //写入PCF8591的写地址
    if(IIC_WaitAck())           //等待应答
    {
        IIC_Stop();             //如果不应答,就结束通信
    }
    else
    {
        IIC_SendByte(0x40);     //写入控制指令,DA转换
        IIC_WaitAck();      //等待应答
        IIC_SendByte(dat);      //写入数字量
        IIC_WaitAck();      //等待应答
        IIC_Stop();         //结束通信
    }
}

实践部分

1.任务要求

通过官方提供的iic代码,完成与PCF8591之间的通信,具体要求如下:

  • 使用通道0和通道3模拟输入,进行AD转换,并将输出的数字量通过数码管显示出来
  • 通过按键S5控制模拟输入的通道,每次按下按键,通道在0和3之间跳变,并通过数码管显示当前模拟输入的通道数
  • 将输出的数字量进行DA转换,模拟输出
  • 测量OUT引脚的电压,判断模拟输出是否正确
    数码管显示格式如下 - 通道 - [空] [空] 数字量

2.实现思路

蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第16张图片

3.代码展示

3.1.main.c
#include 
#include "LS138.h"
#include "iic.h"
#define  PCF8591_Write_Addr   0x90  //PCF8591写地址
#define  PCF8591_Read_Addr    0x91  //PCF8591读地址
unsigned char SEG_Arr[3];       //数码管显示数字
unsigned char dat = 0;          //存放PCF8591读取的内容
unsigned int INT0_Count = 0;
unsigned char channel = 3;
void IT0_Init()
{
    IT0= 1;
    EX0 =1;
    EA =1;
}
//PCF8591AD转换函数,channel为模拟输入的通道
unsigned char  PCF8591_AD_Conversion(unsigned char channel )
{
    unsigned char dat = 0;
    IIC_Start();        //iic通信开始
    IIC_SendByte(PCF8591_Write_Addr);   //写入从机PCF8591的写地址,
    if(IIC_WaitAck())           //等待从机应答,为1表示不应答,为0表示应答
    {   
        IIC_Stop();         //如果不应答,则结束通信
        return 0;
    }
    IIC_SendByte(0x00 | channel);   //写入控制指令,单端输入,AD转换,选择通道channel
    IIC_WaitAck();      //这一步不能少,否则读出来一直是255
    IIC_Stop();         //结束本次通信,改变收发方向
    
    IIC_Start();        //iic通信开始
    IIC_SendByte(PCF8591_Read_Addr);    //写入从机PCF8591的读地址,
    if(IIC_WaitAck())           //等待应答
    {
        IIC_Stop();          //如果不应答,则结束通信
        return 0;
    }
    dat = IIC_RecByte();    //读取AD转换出的数字量 
    IIC_SendAck(1);         //不应答

    IIC_Stop();             //结束通信
    SEG_Arr[0] = dat/100%10;    //计算数字量的百位
    SEG_Arr[1] = dat/10%10;     //计算数字量的十位
    SEG_Arr[2] = dat%10;        //计算数字量的各位
    
    return dat;
}
//PCF8591DA转换函数,dat为输入的数字量
void PCF8591_DA_Conversion(unsigned  char dat)
{
    IIC_Start();        //iic通信开始
    IIC_SendByte(PCF8591_Write_Addr);   //写入PCF8591的写地址
    if(IIC_WaitAck())           //等待应答
    {
        IIC_Stop();             //如果不应答,就结束通信
    }
    else
    {
        IIC_SendByte(0x40);     //写入控制指令,DA转换
        IIC_WaitAck();      //等待应答
        IIC_SendByte(dat);      //写入数字量
        IIC_WaitAck();      //等待应答
        IIC_Stop();         //结束通信
    }
}
void SEG_Show()
{
        SEG_Write(0,10);
        SEG_Write(1,channel);
        SEG_Write(2,10);
        SEG_Write(5,SEG_Arr[0]);
        SEG_Write(6,SEG_Arr[1]);
        SEG_Write(7,SEG_Arr[2]);
}
void main()     
{
    LS138_Init();       //LS138初始化
    IT0_Init();
	while(1)
	{
         dat = PCF8591_AD_Conversion(channel);       //选择通道3输入,通过电阻分压,模拟输入
         PCF8591_DA_Conversion(dat);                 //将读取的数字量通过DA转换,输出
         SEG_Show();

	}
}

void External_Hander0() interrupt 0
{
    INT0_Count++;
    if(INT0_Count % 2)
    {
        channel =3;
    }
    else
    {
       channel =1;
    }
}
3.2.数码管显示函数
                                   /*0    1   2     3    4    5    6   7     8    9 */
unsigned char code SEG_index[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
                                   0xBF  }; //写0点亮


void SEG_Write(unsigned char pos, unsigned char dat)
{
    LS138_Clear();
    P0 = 0x00;
    LS138_Set(7);
    P0 = SEG_index[dat];
    LS138_Clear();
    P0 = 0x00;
    LS138_Set(6);
    P0 = 0x01 << pos;
    LS138_Clear();
    Delayxms(1);

}

void SEG_Show()
{
        SEG_Write(0,10);
        SEG_Write(1,channel);
        SEG_Write(2,10);
        SEG_Write(5,SEG_Arr[0]);
        SEG_Write(6,SEG_Arr[1]);
        SEG_Write(7,SEG_Arr[2]);
}

3.3.官方提供的iic文件
/*
  程序说明: IIC总线驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台 8051,12MHz
  日    期: 2011-8-9
*/


#include "iic.h"
#define DELAY_TIME 5

#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1

//总线引脚定义
sbit SDA = P2^1;  /* 数据线 */
sbit SCL = P2^0;  /* 时钟线 */

void IIC_Delay(unsigned char i)
{
    do{_nop_();}
    while(i--);        
}
//总线启动条件
void IIC_Start(void)
{
    SDA = 1;        
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 0;        //SCL为高电平时,SDA产生下降沿,为开始信号
    IIC_Delay(DELAY_TIME);
    SCL = 0;	    
}

//总线停止条件
void IIC_Stop(void)
{
    SDA = 0;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 1;        //SCL为高电平时,SDA产生上升沿,为结束信号
    IIC_Delay(DELAY_TIME);
}

//发送应答
void IIC_SendAck(bit ackbit)
{
    SCL = 0;            //将SCL拉低,写入应答位
    SDA = ackbit;  					// 0:应答,1:非应答
    IIC_Delay(DELAY_TIME);
    SCL = 1;                        //将SCL拉高,从机读取应答位  
    IIC_Delay(DELAY_TIME);
    SCL = 0; 
    SDA = 1;                //主机释放SDA,结束应答
    IIC_Delay(DELAY_TIME);
}

//等待应答
bit IIC_WaitAck(void)
{
    bit ackbit;
	
    SCL  = 1;   //主机释放SCL,等待从机拉低SCL,发送应答     
    IIC_Delay(DELAY_TIME);
    ackbit = SDA;       //从机发送应答后,释放SCL   
    SCL = 0;            //主机再次拉低SCL,为下一次发送数据或者结束通信做准备
    IIC_Delay(DELAY_TIME);
    return ackbit;
}

//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
    unsigned char i;

    for(i=0; i<8; i++)  
    {
        SCL  = 0;       //SCL拉低
        IIC_Delay(DELAY_TIME);
        if(byt & 0x80) SDA  = 1;    //主机SDA写入数据位1/0
        else SDA  = 0;
        IIC_Delay(DELAY_TIME);
        SCL = 1;                    //SCL拉高,从机读取SDA电平
        byt <<= 1;                  
        IIC_Delay(DELAY_TIME);
    }               //重复八次,完成一个字节的发送
    SCL  = 0;       //将SCL拉低
}

//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
    unsigned char i, da;
    for(i=0; i<8; i++)      //
    {   
    SCL = 1;            //将SCL拉高,
	IIC_Delay(DELAY_TIME);
	da <<= 1;           
	if(SDA) da |= 1;    //主机读取SDA的状态
	SCL = 0;            //将SDA拉低,从机写入SDA状态
	IIC_Delay(DELAY_TIME);
    }               //循环八次,完成一个字节数据的读取
    return da;      //返回读取的字节
}

总结

在蓝桥杯单片机的板子上,PCF8591的基准电压为VCC,理论上是5V,但实际上会与5V存在差距,在本次实验中,我测得的VCC电压为4.53V,那么,如果假设输入数字量为100,进行DA转换之后的电压,就不是5100/255 = 1.96V 而因该是 4.53100/255 = 1.77V这一点需要注意。

如图:

蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第17张图片

蓝桥杯单片机学习11——PCF8591A/D&D/A转换芯片_第18张图片

你可能感兴趣的:(蓝桥杯单片机学习,单片机,蓝桥杯,学习,51单片机,c语言)