这是什么
PCF8575 是恩智浦(NXP)出的一颗 16 位准双向 I2C IO 扩展芯片——单片机 I2C 总线上挂一颗,就能多出 16 个可读可写的数字 IO(P00~P07、P10~P17),常用在按键矩阵、LED 阵列、继电器板这类需要大量数字 IO、又不想占用单片机自身引脚的场合。
这份资料记录的不是"怎么用 PCF8575 驱动什么",而是拿到一批 PCF8575 模块后怎么快速筛出坏板: 跑在 ESP32-2432S028(CYD,2.8寸黄屏)上的一个专用固件,插上模块自动判断 0x20 地址在不在, PASS 后按一次板载 BOOT 键,16 路 IO 整体在 LOW(全拉低)和 HIGH(全释放)之间切换, 配合万用表挨个量每路的电压,一片板子几十秒就能测完。
CN1 排针(GND-IO22-IO27-3V3,IO22当SCL、IO27当SDA)
和本站 ESP32黄屏 万能IIC测试仪 用的是同一组脚。
本次测试还额外用那个万能测试仪对同一块 PCF8575 做了一次独立扫描,交叉确认了 0x20 地址(见第5节)。
硬件与接线
主控板关键参数
| 项目 | 说明 |
|---|---|
| 主控 | ESP32-2432S028R (CYD),ESP32-D0WD-V3 双核 |
| 屏幕 | 2.8寸 ST7789 驱动 TFT,240×320,本项目位带(bit-bang)直接操作GPIO寄存器手写驱动,不依赖 TFT_eSPI 库 |
| 按键 | 板载 BOOT 键(GPIO0),按下接地,本项目用来手动切换 16 路 LOW/HIGH |
CN1 扩展口接 PCF8575
| PCF8575 模块 | ESP32黄屏 CN1 |
|---|---|
| VCC | 3V3 |
| GND | GND |
| SDA | IO27 |
| SCL | IO22 |
| INT | 本测试不接 |
这块板子的 IO21 是背光控制脚(LCD_BLK),
跟 I2C 无关。接线时对照丝印用 IO27,不要凭编号相近误接到 21。
PCF8575 模块参考图(资料参考,非本次实拍模块)
常见同类 PCF8575 扩展板尺寸约 32.2×21mm,芯片丝印 PCF8575TS(NXP),
左侧一列 GND/VCC/SDA/SCL/INT,上下两排分别是 P17~P10 和 P00~P07
共 16 路 IO。具体引脚顺序、丝印以到手模块实物为准,不同批次/供应商的板型可能不同。
地址选择与 I2C 上拉(资料参考)
PCF8575 支持 3 位硬件地址选择(A0/A1/A2),这 3 位由模块上对应焊盘/跳线
接 VCC 或 GND 决定,理论上同一条 I2C 总线上最多可以并联 8 颗 PCF8575(地址 0x20~0x27)
互不冲突。本次测试用的模块实拍图分辨率不够看清楚 A0/A1/A2 是否有独立可改焊盘,
多数同类通用扩展板会直接固定接地(对应本页实测的 0x20)。如果你需要改地址,
请对照到手模块背面丝印确认,不要照抄这里的说法。
I2C 总线的 SDA/SCL 需要合适的上拉电阻才能正常通信。本模块是否已经
板载上拉、阻值多少,本项目没有专门测量确认过——如果你在自己的板子上接线后总线无
响应、地址识别不到,除了检查接线,也可以排查一下 SDA/SCL 是否缺上拉(常见做法是外加
4.7kΩ~10kΩ 到 VCC)。
本项目相关引脚定义(源码 src/main.cpp 第20-40行)
// ---- 屏幕 ST7789 位带 SPI 引脚(与"万能IIC测试仪"一致)----
#define LCD_W 240
#define LCD_H 320
#define LCD_SCK 14 // 屏幕 SPI 时钟
#define LCD_MOSI 13 // 屏幕 SPI 数据(不是 I2C 的 SDA)
#define LCD_CS 15
#define LCD_DC 2
#define LCD_BLK 21 // 背光
// ---- I2C 引脚(扫描到 0x20 用的就是这两根)----
#define I2C_SDA 27
#define I2C_SCL 22
#define PCF8575_ADDR 0x20
// 板载 BOOT 按钮 = GPIO0,按下接地(低电平)
#define BOOT_BTN 0
16路怎么测:灌电流电路原理
资料参考 —— 下面讲的是 PCF8575 IO 的通用电气特性,帮助理解测试电路为什么这么接,不是本项目独立验证的结论。
PCF8575 是准双向(quasi-bidirectional)IO:芯片内部只有弱上拉、没有强推挽输出—— 拉高能力弱、拉低(灌电流/sink)能力强。所以驱动 LED 这类负载时常见接法不是让 IO 自己往外推电流,而是:
| 环节 | 接法 |
|---|---|
| 电源 | 3.3V/5V |
| 限流 | 电阻 |
| 负载端 | PCF8575 IO |
即 VCC 经限流电阻接到 IO,IO 输出 LOW 时芯片把电流灌下去(负载导通/点亮), IO 输出 HIGH(释放)时电阻把节点拉回 VCC 附近。这套 16×10kΩ 上拉正是同一个电路的测试版本—— 用电阻代替 LED,用万用表代替"点不点亮",量的是同一个节点电压:
- 固件写
0x0000(16路全 LOW):PCF8575 灌电流拉低,节点应接近 0V。 - 固件写
0xFFFF(16路全释放/HIGH):芯片释放,10kΩ 电阻把节点拉回 VCC 附近。 - 16 路各自带一颗 10kΩ 上拉,固件让 16 路同步切换,操作者拿万用表逐路碰测试点,任何一路电压不对就是那一路虚焊或损坏。
实测结果
0x20 PASS 与 HIGH/LOW 状态。实测电压
| 状态 | 实测电压 | 判断 |
|---|---|---|
| LOW | 0.0068 V | PASS |
| HIGH | ≈ 3.0 V | PASS |
判断标准
| 状态 | 阈值 | 判断 |
|---|---|---|
| LOW | < 0.2 V | PASS |
| HIGH | > 2.8 V | PASS |
| LOW | > 0.5 V | 异常 |
| HIGH | < 2.5 V | 异常 |
LOW 0.0068 V、HIGH ≈3.0 V,均在 PASS 区间—— 这片 PCF8575 判定正常。批量测片时,LOW 看 < 0.2 V、HIGH 看 > 2.8 V 即可。本次是整体验证(16路同步切换后抽测), 没有逐路单独记录 16 个数值——如果要做更严格的产线级判定,建议改成逐路记录。
独立验证:万能IIC测试仪扫描
为交叉确认这片 PCF8575 的地址判断没错,同一块模块又接到本站
ESP32黄屏 万能IIC测试仪(同样走 CN1 接口)
跑了一次独立的 QUICK 地址扫描——两套完全不同的固件、两次独立测试,都确认到同一个地址 0x20。
I2C QUICK / OK / ADDR 0x20。固件源码关键段
下面三段摘自实际烧录测试通过的 src/main.cpp。
① 写 16 位 IO:PCF8575 准双向输出(第236-244行)
PCF8575 一次 I2C 写操作传 2 字节,先低 8 位(P00~P07)、再高 8 位(P10~P17), 写 1 = 释放为高(弱上拉),写 0 = 主动拉低(强灌电流)。
// ---- PCF8575 写 16 位 ----
// 先低 8 位(P00~P07),再高 8 位(P10~P17),共 16 路全部控制。
// PCF8575 是准双向(quasi-bidirectional)IO:写 1 = 释放为高,写 0 = 拉低。
// 返回值是 Wire.endTransmission() 的错误码:0 = 成功。
static byte pcfWrite(uint16_t value) {
Wire.beginTransmission(PCF8575_ADDR);
Wire.write(value & 0xFF); // P00~P07 低8位
Wire.write((value >> 8) & 0xFF); // P10~P17 高8位
return Wire.endTransmission();
}
② 启动自检:0x20 地址探测(第299-324行)
上电先用一次空写探测 0x20 有没有 ACK,PASS 才进入测试状态,FAIL 直接停机等重新排查接线。
// 检测 0x20
Wire.beginTransmission(PCF8575_ADDR);
byte err = Wire.endTransmission();
if (err == 0) {
Serial.println("Address: 0x20 PASS");
drawStartup(true);
delay(1500); // 给用户看清 PASS
drawTestHeader();
// 开机默认 16 路全部 LOW 并保持,等 BOOT 键切换
byte w = pcfWrite(0x0000);
if (w != 0) {
showError(w);
Serial.print("I2C ERROR code=");
Serial.println(w);
return; // running 保持 false,屏幕停在 FAIL
}
drawState("LOW", RED);
Serial.println("16 IO = LOW");
running = true;
} else {
Serial.print("Address: 0x20 FAIL code=");
Serial.println(err);
drawStartup(false);
// running 保持 false,loop() 里不动作,屏幕停在 FAIL
}
③ BOOT 键切换 16 路 LOW/HIGH(第327-354行)
按一次 BOOT 键(下降沿 + 200ms 消抖),16 路整体在 0x0000 / 0xFFFF
间切换并保持,不是自动循环——切一次、稳定住,方便万用表逐路测量。
void loop() {
if (!running) {
delay(100);
return;
}
// BOOT 键(GPIO0)按下为低电平,抓下降沿,按一次切换一次并保持
bool pressed = (digitalRead(BOOT_BTN) == LOW);
uint32_t now = millis();
if (pressed && !btnWasPressed && (now - lastBtnMs) > 200) {
lastBtnMs = now;
highState = !highState;
uint16_t value = highState ? 0xFFFF : 0x0000;
byte err = pcfWrite(value);
if (err != 0) {
showError(err);
Serial.print("I2C ERROR code=");
Serial.println(err);
running = false;
return;
}
drawState(highState ? "HIGH" : "LOW", highState ? GREEN : RED);
Serial.println(highState ? "16 IO = HIGH" : "16 IO = LOW");
}
btnWasPressed = pressed;
}
下载与编译
⬇ 下载 main.cpp ⬇ 下载源码包(.ini + main.cpp + README)
| 编译参数 | 取值 |
|---|---|
| 开发环境 | PlatformIO |
| 开发板 | esp32dev |
| Framework | arduino |
| Arduino-ESP32 核心 | 固定 2.0.17(3.x/ESP-IDF5 核心在本机会报
undefined reference to esp_bt_controller_mem_release,是工具链BT库版本不匹配的已知问题,
固定 2.0.17 绕开) |
| 依赖库 | 无——屏幕位带直驱、I2C 用核心自带 Wire.h,不需要 TFT_eSPI/LVGL 等第三方库 |
下载包已去掉本机的 upload_port/monitor_port
(原为 COM20),PlatformIO 会自动探测串口;如需手动指定,按自己电脑实际识别到的
COM 口在 platformio.ini 里加一行 upload_port = COMx,不要照抄我们的。
常见问题
| 现象 | 说明 / 排查方向 |
|---|---|
开机显示 0x20 FAIL(红) | 认不到 PCF8575,检查 SDA/SCL 有没有接反、VCC 是否 3.3V、GND 是否共地 |
测试中显示 I2C ERROR + 错误码 | 切换 LOW/HIGH 过程中总线通信失败,多是接触不良,检查排针插接是否牢固 |
| 某一路电压量不出变化 | 该路对应的上拉电阻没接好,或该路 IO 焊盘虚焊,判该模块这一路异常 |
| 为什么用 IO22/IO27 而不是常见的 I2C 默认脚 | ESP32 的 I2C 引脚是软件可配的通用 GPIO 复用,这里沿用了本站"万能IIC测试仪"CN1接口的既有定义,两篇资料共用同一组接口 |
| 16路是同时测的还是分开测的 | 固件是 16 路同步整体切换(一次 I2C 写 2 字节全控制),本次实测也是整体验证;要逐路单独记录,需要人工逐路量并记录数值 |