项目概述
本项目以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 P101.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。