文章目录[隐藏]
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
版权声明:本文为CSDN博主「DS小龙哥」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiaolong1126626497/article/details/121031765
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
版权声明:本文为CSDN博主「DS小龙哥」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiaolong1126626497/article/details/121031765
暂无评论