单片机PCF8591信号传输详解
PCF8591 是一款结合了模拟到数字转换器(A/D)和数字到模拟转换器(D/A)的芯片,广泛应用于各种需要模拟与数字信号交互的系统。其具有多通道模拟输入、模拟输出以及灵活的控制字节配置功能,非常适合在自动化控制、传感器接口等领域中使用。本文将从多个方面介绍 PCF8591 的工作原理、功能以及应用。
视频讲解:
51单片机入门教程-2020版 程序全程纯手打 从零开始入门
PCF8591介绍
PCF8591是八位的A/D和D/A转换器,通过I2C总线串行输入/输出,通过3个硬件地址(A0、A1、A2)引脚编址,具有4个模拟输入(AIN0~AIN3)通道,1个输出通道(AOUR:D/A转换输出端)和1个串行I2C总线接口。
I²C 总线接口
PCF8591 通过 I²C 总线进行与外部设备的通信。I²C 是一种常见的串行通信协议,由两根信号线组成:SCL(时钟线)和 SDA(数据线)。除此之外,PCF8591 还通过 A0、A1、A2 引脚设置硬件地址,方便与其他 I²C 设备进行区分。
主要寄存器功能
PCF8591 中有多个重要寄存器来控制其功能:
电源和复位
PCF8591 由 VDD 和 VSS 提供电源,且芯片上电时会自动进行复位操作,以确保其在正确的状态下开始工作。
振荡器
PCF8591 内置了一个振荡器,OSC 引脚可以接入外部时钟信号,或者使用芯片内部的振荡器生成时钟信号,为芯片提供所需的工作时钟。
控制逻辑
PCF8591 内部有控制逻辑模块,根据状态寄存器的内容和控制字节的设置,控制芯片的工作模式和操作流程。
模拟多路复用器(Analog Multiplexer)
该芯片配备了一个模拟多路复用器,支持将四个模拟输入通道(AIN0 – AIN3)进行选择。用户可以根据需要将这些输入配置为单端或差分输入模式,适应不同的应用需求。
采样保持电路(Sample and Hold)
采样保持电路用于采样输入的模拟信号,并将其保持一段时间,以便进行 A/D 转换。这一过程确保了模拟信号在转换过程中的稳定性。
比较器(Comparator)
PCF8591 内部集成了一个比较器,用于将采样保持电路中的模拟信号与 DAC 输出的信号进行比较,支持逐次逼近算法的实现。这一过程是 A/D 转换的关键部分。
逐次逼近寄存器 / 逻辑(SAR Logic)
PCF8591 使用逐次逼近算法(Successive Approximation)进行 A/D 转换。通过逐步逼近的方法,模拟信号会被转化为相应的数字信号,并存储在 ADC 数据寄存器中。
D/A 转换器(DAC)
PCF8591 的 D/A 转换器将存储在 DAC 数据寄存器中的数字信号转化为模拟电压信号,并通过 AOUT 引脚输出。参考电压(VREF)和模拟地(AGND)为 D/A 转换提供了基准电压。
控制字节的结构
控制字节由 8 位组成,最高位(MSB)和次高位固定为 0,剩余的 6 位用于配置芯片的不同功能。控制字节中的具体位控制如下:
- 模拟输入编程(ANALOGUE INPUT PROGRAMMING):
-
A/D 通道号(A/D CHANNEL NUMBER)
由控制字节低四位中的低两位来决定,分别对应选择通道 0、通道 1、通道 2、通道 3。
通道1为光敏电阻,通道3为滑动电阻。
-
自动递增标志(AUTO-INCREMENT FLAG)
如果该位为 1,则激活自动递增功能,A/D 转换完成后,通道号会自动递增。 -
模拟输出使能标志(ANALOGUE OUTPUT ENABLE FLAG)
若该位为 1,则激活模拟输出功能。
一般来说,使用AD时的配置为光敏电阻:0x01 ,滑动电阻:0x03,
DA:0x41。
功能描述
在 I²C 总线系统中,每一个 PCF8591 设备都需通过向其发送有效地址来激活。该地址由固定部分和可编程部分构成。可编程部分必须依据地址引脚 A0、A1 和 A2 进行设置。在 I²C 总线协议里,地址总是要在起始条件之后作为第一个字节发送出去。地址字节的最后一位是读 / 写位,它决定了后续数据传输的方向
地址发送规范:I²C 总线有着严格的通信协议规范,在数据传输流程中,当主设备发起与 PCF8591 设备的通信时,首先会发送一个起始条件信号(SDA 线由高电平跳变为低电平,同时 SCL 线保持高电平)。在起始条件信号之后,主设备紧接着要发送的第一个字节就是目标 PCF8591 设备的地址,这种规范保证了总线上所有设备能准确识别主设备的通信意图。
读 / 写位功能:地址字节的最后一位起着关键作用,它被称为读 / 写位。当这个读 / 写位被设置为 0 时,就表示后续的数据传输方向是主设备向 PCF8591 设备写入数据;反之,若读 / 写位为 1,则意味着后续主设备要从 PCF8591 设备读取数据。例如,当主设备需要获取 PCF8591 的 A/D 转换结果时,发送的地址字节中的读 / 写位会被置为 1;而当主设备要配置 PCF8591 的控制字节时,读 / 写位则会被设置为 0。
代码实现
创建iic.c和iic.h
iic.h
#ifndef __iic_H__
#define __iic_H__
unsigned char AD_Read();
void unsigned char DA_Write(unsigned char data);
#endif
iic.c
导入官方给的iic底层通信协议代码,在最后添加函数unsigned char AD_Read()和
void unsigned char DA_Write(unsigned char data)。
#include <STC15F2K60S2.H>
#include <intrins.h>
/* # I2C代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#define DELAY_TIME 5
sbit scl = P2^0;
sbit sda = P2^1;
//
static void I2C_Delay(unsigned char n)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
}
while(n--);
}
//
void I2CStart(void)
{
sda = 1;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 0;
I2C_Delay(DELAY_TIME);
scl = 0;
}
//
void I2CStop(void)
{
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}
//
void I2CSendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++){
scl = 0;
I2C_Delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
I2C_Delay(DELAY_TIME);
scl = 1;
byt <<= 1;
I2C_Delay(DELAY_TIME);
}
scl = 0;
}
//
unsigned char I2CReceiveByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++){
scl = 1;
I2C_Delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
I2C_Delay(DELAY_TIME);
}
return da;
}
//
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
scl = 1;
I2C_Delay(DELAY_TIME);
ackbit = sda;
scl = 0;
I2C_Delay(DELAY_TIME);
return ackbit;
}
//
void I2CSendAck(unsigned char ackbit)
{
scl = 0;
sda = ackbit;
I2C_Delay(DELAY_TIME);
scl = 1;
I2C_Delay(DELAY_TIME);
scl = 0;
sda = 1;
I2C_Delay(DELAY_TIME);
}
unsigned char AD_Read()
{
unsigned char temp; // 声明一个变量来存储返回的值
I2CStart(); // 开始 I²C 通信
I2CSendByte(0x90); // 发送字节 0x90 (设备地址 + 写操作)
I2CWaitAck(); // 等待设备的 ACK 确认
I2CSendByte(0x01); // 选择通道1即光敏电阻
//I2CSendByte(0x03); // 选择通道3即滑动电阻
I2CWaitAck(); // 等待设备的 ACK 确认
I2CStart(); // 再次开始 I²C 通信
I2CSendByte(0x91); // 发送字节 0x91 (设备地址 + 读操作)
I2CWaitAck(); // 等待设备的 ACK 确认
temp = I2CReceiveByte(); // 接收一个字节数据
I2CSendAck(1); // 发送 ACK 信号
I2CStop(); // 结束 I²C 通信
return temp; // 返回读取的字节数据
}
void unsigned char DA_Write(unsigned char data)
{
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x41);//数字转模拟量
I2CWaitAck();
I2CSendByte(data);
I2CWaitAck();
I2CStop();
}
作者:星光始终闪耀