项目概述

        本项目利用 零知派ESP32 开发板 控制 MCP4728 四通道 DAC 芯片,驱动红、黄、白、绿四个 LED灯珠模块,实现 6 种动态灯光效果(波浪、追逐、扫描、频闪、随机、心跳)。同时,通过一个 240×240 TFT 彩屏 实时显示当前模式、速度以及随灯光律动的动态边框。两个独立按钮分别切换模式和调节速度,所有设置支持断电记忆,重启后自动恢复上次状态。

项目亮点

  • 四通道独立 DAC 输出:MCP4728 提供真模拟电压,0~3.3V 平滑调节 LED 亮度,无 PWM 闪烁。

  • 真·呼吸效果:基于正弦函数生成亮度曲线,LED 呼吸如流水般自然。

  • 6 种酷炫效果:波浪、追逐、扫描、频闪、随机、心跳,一键切换。

  • 屏幕动态边框:屏幕四周的彩色边框会实时跟随 LED 亮度与颜色变化,慢速模式下灯光即使熄灭边框也保留微光,视觉连贯无抖动。

  • 断电记忆:自动保存当前模式和速度,重启即恢复。

项目难点及解决方案

  • 难点1:MCP4728 的 I2C 通信和库兼容性问题
    解决方案:使用 Adafruit MCP4728 库,并采用正确的通道枚举类型(MCP4728_channel_t),避免类型转换错误。

  • 难点2:屏幕边框闪烁和抖动
    解决方案:通过“智能绘制”策略,取消全局清屏,直接覆盖绘制,慢速模式下即使 LED 熄灭也保持极暗颜色,彻底消除闪烁感。

  • 难点3:按键抖动导致误触发
    解决方案:采用稳健的软件去抖算法,确保每次按下只响应一次。

一、硬件系统部分

1.1 硬件清单

序号 元件 数量 说明
1 零知派ESP32 开发板 1 主控制器
2 零知派ESP32扩展板 1 有屏幕专用接口,带按键
3 MCP4728 模块(四通道 DAC) 1 I2C 接口,输出模拟电压
4 ST7789 240×240 TFT 显示屏 1 SPI 接口,用于显示模式信息
5 零知派 LED灯珠模块(红、黄、白、绿) 各1 用不同颜色展示效果
6 面包板 + 杜邦线 若干 连接电路
7 USB 数据线 1 给 ESP32 供电及烧录程序

1.2 接线方案

MCP4728 零知派ESP32扩展板
VCC 3.3V
GND GND
SDA GPIO21
SCL GPIO22

LED灯珠模块连接

将每个 LED灯珠模块的IN引脚连接到 MCP4728 的对应输出通道,GND引脚阴极接 ESP32的GND。

  • VOUT_A → 红色 LED → GND

  • VOUT_B → 黄色 LED → GND

  • VOUT_C → 白色 LED → GND

  • VOUT_D → 绿色 LED → GND

1.3 硬件连接图

1.4 实物连接图

二、软件架构设计

2.1 系统初始化

  1. 串口初始化:用于输出调试信息。

  2. 加载设置:从 ESP32 的 NVS(非易失性存储)读取上次保存的模式和速度。

  3. 屏幕初始化:配置 TFT_eSPI 库,显示启动画面。

  4. I2C 总线初始化:检测 MCP4728 是否在线。

  5. DAC 通道归零:所有 LED 初始为熄灭状态。

  6. 按钮引脚配置:设置为输入上拉。

2.2 主循环逻辑

  1. 按键扫描:检测两个按钮,触发模式/速度切换并保存设置。

  2. 灯光效果更新:根据当前模式调用对应函数,计算四个 DAC 值并写入 MCP4728。

  3. 屏幕边框绘制:根据四个 LED 的亮度值,实时更新屏幕边框颜色。

  4. 信息区刷新:只在模式或速度变化时更新屏幕中央的文字和图标。

  5. 串口状态输出:每秒一次,方便监控。

三、代码拆分讲解

3.1 断电记忆的实现

使用 ESP32 的 Preferences 库,将 currentMode 和 globalSpeed 写入 NVS 分区。在 setup() 中调用 loadSettings() 恢复,每次按钮改变时调用 saveSettings() 保存。

void saveSettings() {
  prefs.begin("led_ctrl", false);
  prefs.putUChar("mode", currentMode);
  prefs.putFloat("speed", globalSpeed);
  prefs.end();
}

3.2 呼吸效果的核心——正弦波生成

以波浪模式为例,四个 LED 的亮度曲线为:

float val = (sin(2*PI*cycle + phase[ch]) + 1) / 2;

cycle 是周期内进度,phase 数组提供 0°、90°、180°、270° 的相位偏移,使得四个灯错开亮暗。

3.3 屏幕无闪烁边框绘制

void drawSideSmart(...) {
  if (brightness > 0) {
    // 直接绘制彩色
    tft.fillRect(x, y, w, h, color);
  } else {
    if (isSlow) {
      // 慢速模式绘制极暗颜色,避免全黑造成视觉抖动
      tft.fillRect(x, y, w, h, 极暗颜色);
    } else {
      tft.fillRect(x, y, w, h, TFT_BLACK);
    }
  }
}

此方法不再预先清除边框,而是直接覆盖,彻底消除闪烁。

四、操作过程及数据展示

4.1 操作步骤

  1. 硬件连接:正确连接所有模块。

  2. 编译与上传:在零知派中选择开发板“ESP32 ”,正确选择端口,编译并上传代码。

  3. 操作体验:

  • 上电后屏幕显示启动画面,2秒后进入主界面,默认为“Wave”模式,速度1.0x。
  • 短按扩展板GPIO12 按钮:模式依次循环(Wave→Chase→Scan→Strobe→Random→Heartbeat→Wave)。
  • 短按扩展板GPIO27 按钮:速度在 0.5x → 1.0x → 1.5x 之间循环。
  • 观察 LED 和屏幕边框同步变化,慢速模式边框平滑渐变,快速模式干脆利落。
  • 断电后重新上电,模式和速度保持上次的状态。

预期现象

  • 波浪模式:四路 LED 依次明暗,像流动的波浪,屏幕四边框颜色也跟随流动。

  • 追逐模式:一个高亮光点在四个 LED 间跳跃,其余灯有拖尾效果。

  • 频闪模式:红+白一组、黄+绿一组交替爆闪。

  • 心跳模式:所有 LED 同步连续两次快速脉冲后熄灭,模拟心跳。

4.2 视频演示

零知派ESP32--MCP4728 四通道炫彩呼吸灯控制器

五、技术原理

5.1 工作原理

  • MCP4728:内部集成四个 12 位 DAC,通过 I2C 接收数据,输出 0~VDD 的模拟电压。本项目中 VDD 接 3.3V,因此输出电压范围 0~3.3V,对应 DAC 值 0~4095。

  • LED 亮度控制:DAC 输出通过限流电阻直接驱动 LED,电压越高亮度越亮。12 位分辨率可提供 4096 级亮度,远超人眼识别极限,实现极致平滑。

  • 屏幕显示:TFT 屏通过 SPI 通信接收像素数据,我们利用 TFT_eSPI 库在屏幕上绘制文字、图形,并根据 ledBrightness 数组实时更新边框矩形颜色。

5.2 工作模式配置

六种模式的核心算法:

  • Wavesin(2π·t/T + φ) 产生正弦波。

  • Chase:亮点位置 pos = (t/T)*4,其余通道亮度按 decay^距离 衰减。

  • Scan:三角波 pos = 2t/T(上升段)和 2(1 - t/T)(下降段),两组 LED 反向变化。

  • Strobe:取模运算 (t/100ms) % 2 产生交替开关。

  • Random:定时器触发 random(0,4096) 产生随机亮度。

  • Heartbeat:自定义波形:sin(π·t/0.1T) 快速脉冲,二次稍弱。

六、使用MCP4728模块的好处

对比项 使用 MCP4728 ESP32 直接 PWM 输出
通道数 4 路,可同时输出 通常 16 路 LEDC,但需占用 4 个 GPIO
输出类型 真实模拟电压,0~3.3V 平滑连续 PWM 方波,需滤波才能变为平滑电压,或利用人眼暂留,但摄影时会看到频闪
LED 电流驱动 DAC 输出电流有限(约15mA),可直驱小功率 LED GPIO 输出电流可配置,但驱动能力类似,仍需限流电阻
分辨率 12 位(4096 级)真模拟 10~16 位 PWM,但有效位数受频率和滤波限制
亮度平滑度 极其平滑,无任何闪烁 低频 PWM 可能有闪烁,高频会降低分辨率,滤波复杂
接线复杂度 I2C 两根线 + 电源,GPIO 占用少 需要 4 个 GPIO,独立控制
扩展性 I2C 总线可挂多个 DAC,节省引脚 占用 GPIO 较多
库支持 Adafruit 库简洁易用 ESP32 LEDC 库需自行配置通道、频率、分辨率

总结使用MCP4728 的好处

  1. 真模拟输出:绝对平滑,肉眼和相机均无频闪,适合摄影、氛围灯等对画质要求高的场景。

  2. 芯片集成度高:四通道一片搞定,节省空间和 GPIO。

  3. 驱动简单:I2C 通信,无需考虑 PWM 频率、分辨率折衷,代码更简洁。

  4. 可靠性:DAC 输出为稳定直流,不受 CPU 负载影响,而软件 PWM 可能会因中断导致抖动。

七、常见问题指引

Q1:串口提示 "MCP4728 init failed!"

可能原因:

        I2C 接线错误或地址不对。

解决方法:

        检查 SDA→GPIO21, SCL→GPIO22, VCC→3.3V, GND→GND;确保模块 A0 引脚电平与代码默认地址 0x60 匹配。

Q2:LED 不亮或亮度极低

可能原因:

        LED 极性接反、蓝色/绿色 LED 正向电压过高。

解决方法:

        检查 LED 正负极;红色/黄色 LED 更易驱动。

Q3:断电后模式不记忆

可能原因:

        代码中未调用 saveSettings() 或 NVS 分区损坏。

解决方法:

        确保 saveSettings() 在每次按钮动作时被调用;可尝试擦除 ESP32 闪存后重新烧录。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐