零知派ESP32--通过I2C 接口扩展芯片PCF8575使用矩阵键盘控制LED
项目概述
本项目以 ESP32 为主控,通过 I2C 接口扩展芯片 PCF8575,将有限的主控引脚扩展为 16 路数字 I/O,并在其上挂载:
| 模块 | 作用 |
|---|---|
| 4×4 矩阵键盘 | 人机输入,控制灯效与工作模式 |
| 2 路高电平 LED | 状态指示 / 感应联动照明 |
| RGB 三色灯模块 | 多色彩显示与颜色轮播 |
| SR505 人体感应 | 有人自动开灯、无人自动灭灯 |
| ST7789 1.3 寸屏(240×240) | 实时显示模式、灯态、感应状态 |
系统上电后默认进入 AUTO(人体感应)模式:有人靠近时两路 LED 点亮,人离开后熄灭。同时可通过矩阵键盘切换到 MANUAL(手动)模式,独立控制 LED、RGB 颜色与轮播。
核心目标:在一块扩展芯片上同时演示 I2C 通信、数字输出驱动、数字输入采集、矩阵键盘扫描、传感器联动与图形界面刷新。
项目亮点
- 一芯多用:键盘扫描(输入)、LED/RGB 驱动(输出)、人体感应采集(输入)全部走 PCF8575,充分展示 I/O 扩展器的双向能力。
- AUTO / MANUAL 双模式:感应自动控灯与按键手动控灯可一键切换,互不抢占 RGB。
- 矩阵键盘完整消抖 + 边沿触发:按一下只触发一次,不会连发。
- RGB 独立轮播:按键
C控制颜色循环,与感应模式解耦,避免彩灯干扰 PIR。 - ST7789 卡片式 UI:240×240 方形屏上分区显示 MODE / LED / RGB / PIR,状态变化即时刷新、不整屏闪烁。
- 接线容错:键盘排线反向(P17→P10)时,仅改掩码数组即可适配,无需重插线。
项目难点及解决方案
| 难点 | 现象 | 解决方案 |
|---|---|---|
| PCF8575 高电平驱动能力弱 | LED 极暗甚至几乎不亮 | 输出引脚与 VCC 之间加 330Ω~1kΩ 上拉电阻,写 1 时由电阻供电流,写 0 时芯片灌电流关灯 |
| 矩阵键盘与 LED 同芯片 | 扫描键盘时误把 LED 熄灭 | 所有写端口统一走 writePort(),始终保留当前 ledState / rgbIndex |
| RGB 点亮干扰 SR505 | 开 RGB 后 PIR 一直判“有人” | 感应模式 只控制两路 LED,不驱动 RGB;RGB 由按键 C / 0~7 独立控制 |
| 按键连发 / 抖动 | 一次按下触发多次 | 20ms 软件消抖 + 仅在“按下边沿”调用 onKeyPressed() |
| 键盘排线方向反了 | 按键识别错位 | 用 ROW_MASKS / COL_MASKS 映射实际引脚,代码适配硬件 |
| 屏幕频繁全刷闪烁 | UI 闪、观感差 | 静态背景只画一次(drawStaticUI),状态区脏标记刷新(displayDirty) |
| SR505 供电不稳 | 亮灯后 OUT 粘高、关不掉 | 保证模块 VCC 接牢 5V,GND 共地;必要时电源脚加滤波电容 |
一、硬件系统部分
1.1 硬件清单
| 名称 | 规格 / 说明 | 数量 |
|---|---|---|
| 零知ESP32 开发板 | 带 USB 下载,I2C 默认 GPIO21/22 | 1 |
| 零知ESP32 扩展板 | 带屏幕接口,方便接线 | 1 |
| PCF8575 模块 | I2C 16 位 I/O 扩展,默认地址 0x20 | 1 |
| 4×4 薄膜矩阵键盘 | 8 线排针,无源 | 1 |
| 高电平触发 LED 模块 | 单路信号 + VCC/GND | 2 |
| RGB LED 模块 | R/G/B 三路独立,共阴或模块 GND | 1 |
| SR505 人体感应模块 | 工作电压约 4.5~20V,OUT 数字输出 | 1 |
| ST7789 彩屏 | 240×240,SPI,TFT_eSPI 驱动 | 1 |
| 杜邦线、面包板 | — | 若干 |
1.2 接线方案
(1)ESP32 ↔ PCF8575(I2C)
| ESP32 | PCF8575 |
|---|---|
| GPIO21(SDA) | SDA |
| GPIO22(SCL) | SCL |
| 5V | VCC |
| GND | GND |
(2)PCF8575 外设分配
| PCF8575 引脚 | 外设 | 方向 |
|---|---|---|
| P00 | LED1 信号 | 输出 |
| P01 | LED2 信号 | 输出 |
| P02 | RGB-R | 输出 |
| P03 | RGB-G | 输出 |
| P04 | RGB-B | 输出 |
| P05 | SR505 OUT | 输入 |
| P06、P07 | 未用 | — |
| P17~P14 | 键盘行 R1~R4 | 输出扫描 |
| P13~P10 | 键盘列 C1~C4 | 输入读取 |
(3)4×4 键盘排线对应关系(本工程实际接法)
键盘正面朝上、排线朝下,从左到右 8 根线:
排线左 → 右:R1 R2 R3 R4 C1 C2 C3 C4
接 PCF8575: P17 P16 P15 P14 P13 P12 P11 P10
1.3 硬件连接图

┌─────────────┐
│ ESP32 │
│ SDA=GPIO21 ├──┐
│ SCL=GPIO22 ├──┼── I2C 总线
│ SPI → TFT │ │
└──────┬──────┘ │
│ │
┌────────────┴───┐ │
│ ST7789 240² │ │
│ (TFT_eSPI) │ │
└────────────────┘ │
│
┌────────────────┴──────┐
│ PCF8575 0x20 │
│ │
P00 ──[上拉]── LED1 │
P01 ──[上拉]── LED2 │
P02 ──[上拉]── RGB R │
P03 ──[上拉]── RGB G │
P04 ──[上拉]── RGB B │
P05 ────────── SR505 OUT (+→5V) │
P17..P14 ───── 键盘行 R1..R4 │
P13..P10 ───── 键盘列 C1..C4 │
└───────────────────────┘
系统数据流示意:

1.4 实物连接图

二、软件架构设计
2.1 系统初始化(setup)
初始化顺序与代码一致:
- 打开串口
115200 tft.init()+setRotation(),调用drawStaticUI()画静态背景Wire.begin(21, 22),I2C 时钟 100kHz- 探测 PCF8575 地址
0x20是否应答 writePort(0xFF00):LED/RGB 灭,行列写 1,P05~P07 保持输入- 读取一次 PIR 电平,置
displayDirty,等待主循环刷新动态区
2.2 主循环逻辑(loop)
每个循环周期(约 5ms 间隔)依次执行:
A[scanMatrix 扫描键盘] --> B[消抖 + 边沿检测]
B -->|确认按下| C[onKeyPressed]
B --> D{autoMode?}
D -->|是| E[handleMotion 有人开灯/无人灭灯]
D -->|否| F[跳过感应]
E --> G[handleRgbCycle]
F --> G
G --> H[每 200ms 采样 PIR 电平]
H --> I{displayDirty?}
I -->|是| J[updateDisplay]
I -->|否| K[delay 5ms]
J --> K
设计要点:
- 键盘优先:先扫键再处理感应,保证按键响应及时
- 脏标记刷新:只有状态变化才重画动态卡片,降低 SPI 负担
- 感应与 RGB 解耦:
handleMotion只改ledState,不改rgbIndex
三、代码拆分讲解
3.1 底层读写 PCF8575
void writePCF8575(uint16_t value); // 先低字节 P00-P07,再高字节 P10-P17
uint16_t readPCF8575(); // requestFrom 2 字节拼成 16 位
writePort(rowColBits) 是关键写出口:高字节负责行列扫描,低字节始终合并:
ledBits():LED1/2 + RGB 三通道UNUSED_LOW:P05~P07 写 1,保证 PIR 可被外部驱动
3.2 矩阵键盘扫描 scanMatrix
- 逐行将对应行掩码拉低,其余行/列写 1
- 延时约 500µs 等待稳定
- 读端口,列位为 0 表示该键按下
- 扫完后恢复行列全 1,并保持 LED/RGB 状态不变
消抖在 loop 中完成:电平变化后稳定 ≥20ms,且仅在 按下瞬间 触发事件。
3.3 按键事件 onKeyPressed
| 按键 | 行为 |
|---|---|
0~7 |
直接设 RGB 颜色,关闭轮播 |
A / B |
切换 LED1 / LED2,并退出 AUTO |
C |
开关 RGB 轮播(开:从红色起每秒换色;关:RGB 熄灭) |
D |
切换 AUTO / MANUAL |
# |
LED 全亮 + RGB 白,退出 AUTO,停轮播 |
* |
全灭,退出 AUTO,停轮播 |
3.4 人体感应 handleMotion
- 读 P05 → 50ms 消抖得到
motionStable true:ledState = 0x03(两灯亮)false:ledState = 0x00(两灯灭)- 不修改 RGB
进入 AUTO 时,setAutoMode(true) 会故意翻转一次稳定态,强制按当前传感器电平立刻同步灯态。
3.5 RGB 轮播 handleRgbCycle
rgbCycle == true 时,每 1000ms:rgbIndex 在 1~7 循环(红→绿→黄→蓝→紫→青→白)。
3.6 屏幕显示
drawStaticUI():背景、顶栏、分区标签(只跑一次)updateDisplay():刷新 MODE / LED 圆点 / RGB 色点与名称 / PIR 状态与OUT:H/L
四、操作过程及数据展示
4.1 操作步骤
-
接线检查
- I2C:SDA/SCL、共地
- LED/RGB:IN→P00/P01/P02/P03/P04
- SR505:
+→5V,OUT→P05,-→GND - 键盘:左→右接 P17→P10
-
编译烧录
- 开发板选 零知ESP32,波特率 115200
- 打开串口监视器,应看到
PCF8575 OK.与Ready.
-
功能验证
| 步骤 | 操作 | 预期现象 |
|---|---|---|
| ① | 上电默认 AUTO | 屏显 MODE=AUTO;挥手 LED 亮,离开后灭;PIR 显示 MOTION/IDLE |
| ② | 按 A / B |
进入 MANUAL;对应 LED 翻转 |
| ③ | 按 1~7 |
RGB 变为对应颜色;屏上颜色名同步 |
| ④ | 按 C |
RGB 开始轮播,屏显 CYCLE;再按 C 停止并熄灭 |
| ⑤ | 按 D |
在 AUTO / MANUAL 间切换 |
| ⑥ | 按 # / * |
全开(白)/ 全关 |
- 屏幕与串口对照
- 屏右上角显示最近按键字符
- 串口打印:
Key: x LED1:.. LED2:.. RGB:.. Auto:ON/OFF - 感应动作打印:
[Auto] Motion -> LEDs ON/Idle -> LEDs OFF
按键速查表
[1] [2] [3] [A] 0~7 : RGB 定色(停轮播)
[4] [5] [6] [B] A/B : LED1 / LED2 切换(进 MANUAL)
[7] [8] [9] [C] C : RGB 轮播开关
[*] [0] [#] [D] D : AUTO / MANUAL
#/* : 全开 / 全关
4.2 演示视频
零知派ESP32--通过PCF8575使用矩阵键盘控制LED
五、技术原理
5.1 工作原理
(1)PCF8575 准双向口
- 写 1:引脚为弱上拉/高阻输入态,可被外部拉低,也可外加上拉电阻对外供电
- 写 0:强下拉,灌电流能力强(适合关断高电平 LED)
- 因此“高电平点亮 LED”必须外加电阻,不能指望芯片内部弱上拉直接点灯
(2)矩阵键盘扫描
行列交叉处按下时,被拉低的行会把对应列线拉低。MCU 逐行输出低电平并读列,即可定位键位,再经 KEYMAP 映射为 '0'`‘9’`、`‘A’`'D'、'*'、'#'。
(3)SR505 人体感应
热释电传感器检测人体红外变化。检测到运动时 OUT 输出高电平,并保持约数秒(硬件固定,SR505 一般不可调电位器)。本工程以该数字电平作为“有人/无人”依据。
(4)I2C 多设备与 SPI 屏
- PCF8575 独占 I2C(本工程地址 0x20)
- ST7789 走 SPI,与 I2C 总线分离,互不抢总线
5.2 工作模式配置
| 模式 | 进入方式 | LED | RGB | 说明 |
|---|---|---|---|---|
| AUTO | 上电默认 / 按 D 切入 |
由 SR505 自动控制 | 不联动;仍可用 C/0~7 手动控 |
屏显绿色 AUTO |
| MANUAL | 按 A/B/#/* 或 D 切出 |
按键手动 | 按键手动 | 屏显橙色 MANUAL |
相关宏配置(可按硬件微调):
#define PCF8575_ADDR 0x20 // I2C 地址,A0/A1/A2 接地时通常为 0x20
#define LED_ACTIVE_LOW 0 // 0=高电平亮(上拉接法);1=低电平亮(灌电流接法)
#define MOTION_ACTIVE_HIGH 1 // 1=OUT 高表示有人;模块相反则改 0
const uint32_t MOTION_DEBOUNCE_MS = 50;
const uint32_t DEBOUNCE_MS = 20;
键盘引脚映射(本工程反向排线):
ROW_MASKS = {P17, P16, P15, P14};
COL_MASKS = {P13, P12, P11, P10};
六、常见问题指引
Q1:串口提示 PCF8575 not found at 0x20
检查 SDA/SCL 是否接 GPIO21/22、模块供电与共地;用 I2C 扫描确认实际地址(若跳线改地址,同步改 PCF8575_ADDR)。
Q2:按键识别错位(按 1 变成别的键)
排线方向或行列对调。对照本文 1.2 节改线,或只改 ROW_MASKS / COL_MASKS / KEYMAP。
Q3:按一下键触发很多次
确认使用的是边沿触发消抖逻辑;机械抖动过大时可把 DEBOUNCE_MS 提到 30~50。
更多推荐



所有评论(0)