寒假第六讲(AD_DA)

PCF8591 AD/DA转换模块学习笔记

1. 模块基础介绍

1.1 什么是PCF8591?

PCF8591是一款单电源、低功耗的8位CMOS数据采集器件,具有:

  • 4路模拟输入(ADC) - 将模拟信号转换为数字信号
  • 1路模拟输出(DAC) - 将数字信号转换为模拟信号
  • 通过I2C总线 与单片机通信

1.2 硬件连接

#include <intrins.h>  // 必须包含,用于_nop_()延时

sbit scl = P2 ^ 0;  // I2C时钟线(连接到PCF8591的SCL引脚)
sbit sda = P2 ^ 1;  // I2C数据线(双向,连接到PCF8591的SDA引脚)

1.3 I2C总线基础函数(官方提供)

I2C_Delay()      // I2C延时函数
I2CStart()       // 发送起始信号
I2CStop()        // 发送停止信号
I2CSendByte()    // 发送一个字节
I2CReceiveByte() // 接收一个字节
I2CWaitAck()     // 等待应答信号
I2CSendAck()     // 发送应答信号

2.1 地址计算原理

PCF8591的I2C设备地址由7位固定地址+1位读写位组成:

设备 固定地址 R/W位 完整地址
PCF8591 0x48 写=0, 读=1 写:0x90, 读:0x91

计算公式:

  • 写地址 = (0x48 << 1) | 0 = 0x90
  • 读地址 = (0x48 << 1) | 1 = 0x91

3. 控制寄存器详解

3.1 寄存器格式

PCF8591的控制寄存器(8位)格式如下:

位7   位6   位5   位4   位3   位2   位1   位0
┌────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  0 │  0 │ AI1│ AI0│ CH1│ CH0│
└────┴────┴────┴────┴────┴────┴────┴────┘
  │    │    │    │    │    │    │    └── 通道选择位0
  │    │    │    │    │    │    └───── 通道选择位1
  │    │    │    │    │    └───────── 模拟输入模式位0
  │    │    │    │    └───────────── 模拟输入模式位1
  │    │    │    └────────────────── DA使能位(1=开启,0=关闭)
  │    │    └─────────────────────── 固定为0
  │    └──────────────────────────── 固定为1
  └───────────────────────────────── 固定为0

4. DA转换(数字→模拟)

4.1 工作原理

数字值(0-255) → DAC转换 → 模拟电压(0-5V)
     ↓                    ↓
 单片机写入         PCF8591转换输出

4.2 DA转换函数

/**
 * 功能: PCF8591 DA转换
 * 描述: 向PCF8591的DA输出设置模拟量
 *      设置范围: 0~255 (对应0~5V)
 * 参数: dat - DA转换值(0~255)
 */
void Da_Write(unsigned char dat)
{
    I2CStart();        // 发送起始信号
    I2CSendByte(0x90); // 发送设备地址+写操作(0x90)
    I2CWaitAck();      // 等待应答
    
    I2CSendByte(0x41); // 发送控制字(开启DA输出,通道1)
    I2CWaitAck();      // 等待应答
    
    I2CSendByte(dat);  // 发送DA值
    I2CWaitAck();      // 等待应答
    
    I2CStop();         // 发送停止信号
}

5. AD转换(模拟→数字)

5.1 工作原理

模拟电压(0-5V) → ADC转换 → 数字值(0-255)
     ↓                      ↓
 传感器输入           单片机读取

5.2 AD转换函数

/**
 * 功能: PCF8591 AD转换
 * 描述: 从PCF8591的指定通道读取AD转换值
 *      读取范围: 0~255 (对应0~5V)
 * 参数: addr - 控制字,指定通道和配置
 *       0x41 - 通道1,开启DA(光照传感器)
 *       0x43 - 通道3,开启DA(滑动变阻器)
 * 返回值: AD转换结果(0~255)
 */
unsigned char Ad_Read(unsigned char addr)
{
    unsigned char temp;
    
    // 写操作阶段:选择通道
    I2CStart();          // 发送起始信号
    I2CSendByte(0x90);   // 发送设备地址+写操作
    I2CWaitAck();        // 等待应答
    I2CSendByte(addr);   // 发送控制字(选择通道)
    I2CWaitAck();        // 等待应答
    
    // 读操作阶段:获取数据
    I2CStart();          // 重新发送起始信号
    I2CSendByte(0x91);   // 发送设备地址+读操作
    I2CWaitAck();        // 等待应答
    
    temp = I2CReceiveByte();  // 读取AD转换结果
    I2CSendAck(1);       // 发送非应答信号
    
    I2CStop();           // 发送停止信号
    return temp;         // 返回AD转换结果
}

6. 应用层示例

6.1 数值换算

  • 数字量 → 模拟电压电压 = 数字量 ÷ 51
  • 模拟电压 → 数字量数字量 = 电压 × 51

注:255对应5V,所以换算系数为 255÷5 = 51

6.2 完整应用示例

// 全局变量
unsigned char AD_Slow_Down = 0;    // 延时计数器
unsigned char AD_Light_Val_100x;   // 光照值(放大100倍)
unsigned char AD_RB2_Val_100x;     // 滑动变阻器值(放大100倍)

void AD_DA()
{
    // 检查延时是否到达(80-160ms读取一次)
    if (AD_Slow_Down < 80)
        return;
    AD_Slow_Down = 0;
    
    // 读取光照传感器(通道1)并转换为百分比
    AD_Light_Val_100x = Ad_Read(0x41) * 100 / 51;
    
    // 读取滑动变阻器(通道3)并转换为百分比
    AD_RB2_Val_100x = Ad_Read(0x43) * 100 / 51;
    
    // 设置DA输出2V电压(2×51=102)
    Da_Write(2 * 51);
}

7. 重要注意事项

7.1 读取延迟问题

问题现象 :连续读取时,下一次读取到的是上一次转换后的值。

解决方案 :使用"交换地址读取法"

// 交换地址读取(反读)
AD_Light_Val_100x = Ad_Read(0x43) * 100 / 51;  // 实际读取的是RB2通道
AD_RB2_Val_100x = Ad_Read(0x41) * 100 / 51;    // 实际读取的是光照传感器

7.2 常见问题排查

  1. 无响应 :检查I2C地址是否正确(0x90/0x91)
  2. 数据错误 :检查控制字(addr参数)是否正确

总结 :PCF8591是一款简单易用的AD/DA转换芯片,通过I2C接口与单片机通信,只需掌握地址计算、控制字设置和基本读写时序即可灵活使用。