ESP32-C5圆屏音频共存的引脚分配如何验

2026-10-294
AI 快速预览详细 收起
本文介绍了如何为带圆屏、扬声器、麦克风和电池的桌面设备分配GPIO,并提供了一套可复用的核验方法。首先,从物理连接反推引脚表,确保QSPI显示链路和ES8311音频配置无冲突。然后,检查QSPI显示链路,确保DMA传输大小适中。最后,验证ES8311音频配置,确保I2C和I2S接口正确连接。通过这些步骤,可以避免常见的黑屏、底噪等问题,确保各模块协同工作。

ESP32-C5圆屏音频共存的引脚分配如何验

给一块带圆屏、扬声器、麦克风和电池的桌面设备分配 GPIO,最容易掉进一个陷阱:每个模块单独看都能工作,合在一起却出现黑屏、底噪、按键失灵,甚至刷写后无法启动。Moji 2.0 的硬件把这些接口集中到一块很小的双层板上,正好适合用来演示一套可复用的核验方法。

本文围绕同一版本的 ESP32-C5 工程,做三件事:从嘉立创 EDA 工程确认屏幕和主板的物理连接,再用固件配置反查 GPIO 是否冲突,最后把 QSPI、ES8311 音频和省电逻辑拆成可以单独检查的代码片段。重点是核对过程,文章没有把“网页上写着支持”当成实测结论。

Moji 2.0 实物总览

图 1 项目实物总览,作为外观和器件布局参考

先把版本锁住

今天核对的工程是 Moji 2.0 小智AI桌面机器人,开源广场页面显示硬件许可为 CC BY-SA 4.0,更新时间为 2026-01-28。对应的嘉立创 EDA 工程在 专业版编辑器 中打开,工程树同时包含 Schematic_Moji2 和 PCB_Moji2。

这一步看似简单,却能避免一个常见错误:拿 1.0 版的 ESP32-S3 接口表去解释 2.0 版的 ESP32-C5。项目页面也明确写了 2.0 的屏幕改成 360×360、QSPI 插接,主控换成 ESP32-C5,外壳和 1.0 不通用。

装配结构与排线位置

图 2 装配结构图,先确认 FPC 插座、主板和屏幕板的相对位置

从物理连接反推引脚表

Moji 2.0 把显示、音频、按键、背光和指示灯放在同一颗主控上。先看引脚,再看初始化代码,顺序不能反过来。下面是从工程配置中整理出的最小核对表。

功能 引脚 代码中的名字 核对要点
QSPI 时钟 GPIO0 DISPLAY_QSPI_SCLK_PIN 与复位脚、片选脚分开
QSPI 复位 GPIO1 DISPLAY_QSPI_RESET_PIN 上电时序必须先于面板初始化
QSPI 数据 GPIO6~9 D3/D2/D1/D0 四根数据线不能与音频 I2S 复用
QSPI 片选 GPIO3 DISPLAY_QSPI_CS_PIN 只由屏幕总线使用
背光 PWM GPIO2 DISPLAY_BACKLIGHT_PIN 不要误当作屏幕数据线
ES8311 I2C GPIO26/27 SDA/SCL 配置中打开内部上拉,仍需核对板上电阻
ES8311 I2S GPIO11/12/23/24/25 BCLK/DIN/DOUT/WS/MCLK MCLK、BCLK、WS、数据方向要一一对应
按键 GPIO28 BOOT_BUTTON_GPIO 启动阶段和对话阶段含义不同

主板 PCB 资料图

图 3 主板 PCB 资料图。文章将其与 EDA 工程的 PCB 图层一起核对,不能把资料图当成新版改板的实测照片

下面这段代码保留了项目中的命名方式,但只摘出与核对有关的定义。它的价值不在于复制粘贴,而在于让表格和后续初始化拥有同一份“事实源”。

#define AUDIO_I2S_GPIO_MCLK         GPIO_NUM_25
#define AUDIO_I2S_GPIO_WS           GPIO_NUM_24
#define AUDIO_I2S_GPIO_BCLK         GPIO_NUM_11
#define AUDIO_I2S_GPIO_DIN          GPIO_NUM_12
#define AUDIO_I2S_GPIO_DOUT         GPIO_NUM_23

#define AUDIO_CODEC_PA_PIN          GPIO_NUM_5
#define AUDIO_CODEC_I2C_SDA_PIN     GPIO_NUM_26
#define AUDIO_CODEC_I2C_SCL_PIN     GPIO_NUM_27
#define BOOT_BUTTON_GPIO            GPIO_NUM_28

#define DISPLAY_BACKLIGHT_PIN       GPIO_NUM_2
#define DISPLAY_QSPI_SCLK_PIN       GPIO_NUM_0
#define DISPLAY_QSPI_RESET_PIN      GPIO_NUM_1
#define DISPLAY_QSPI_D0_PIN         GPIO_NUM_9
#define DISPLAY_QSPI_D1_PIN         GPIO_NUM_8
#define DISPLAY_QSPI_D2_PIN         GPIO_NUM_7
#define DISPLAY_QSPI_D3_PIN         GPIO_NUM_6
#define DISPLAY_QSPI_CS_PIN         GPIO_NUM_3

QSPI 显示链路要检查什么

页面介绍中把屏幕写成“360×360、QSPI、ST77916”。真正落到 ESP-IDF 时,至少要检查三个边界:SPI 总线初始化使用的 host 是否一致,四根数据线是否按 D0~D3 传入,单次 DMA 传输大小是否覆盖一行像素。

项目代码中用 DISPLAY_QSPI_H_RES * 80 * sizeof(uint16_t) 作为 max_transfer_sz。按 360 像素宽、16 位颜色计算,一次缓冲上限是 360×80×2 = 57,600 字节,能够覆盖 80 行的 RGB565 数据。这个数值不是“越大越好”:盲目放大会增加 DMA 内存压力,过小又会让刷新被切碎。

嘉立创 EDA 工程预览

图 4 同版本 EDA 工程预览。原理图和 PCB 顶底层均在嘉立创 EDA 专业版工程中重新核对

对应的初始化骨架如下:

static void InitializeSpi() {
    const spi_bus_config_t bus_config =
        MOJI2_ST77916_PANEL_BUS_QSPI_CONFIG(
            DISPLAY_QSPI_SCLK_PIN, DISPLAY_QSPI_D0_PIN,
            DISPLAY_QSPI_D1_PIN, DISPLAY_QSPI_D2_PIN,
            DISPLAY_QSPI_D3_PIN,
            DISPLAY_QSPI_H_RES * 80 * sizeof(uint16_t));
    ESP_ERROR_CHECK(spi_bus_initialize(
        DISPLAY_QSPI_HOST, &bus_config, SPI_DMA_CH_AUTO));
}

static void InitializeSt77916Display() {
    esp_lcd_panel_io_handle_t panel_io = nullptr;
    esp_lcd_panel_handle_t panel = nullptr;
    esp_lcd_panel_io_spi_config_t io_config =
        ST77916_PANEL_IO_QSPI_CONFIG(DISPLAY_QSPI_CS_PIN, NULL, NULL);
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(
        (esp_lcd_spi_bus_handle_t)DISPLAY_QSPI_HOST,
        &io_config, &panel_io));
}

这里有一个容易被忽略的细节:代码把 io_config.pclk_hz 留在默认值,而不是直接套用 40 MHz 宏。文章只记录源码事实,不把宏名当作实测时钟;如果要提高刷新率,应在目标板上用逻辑分析仪确认时序,再决定是否启用该配置。

ES8311 音频和显示为什么要分开验

音频链路有两套接口:I2C 用于寄存器配置,I2S 用于音频采样。显示屏走 QSPI,理论上互不占线,但它们共享电源、地和高频边沿。排查“有画面但有底噪”时,先把问题分成两层:

  1. GPIO 和外设驱动是否接对;
  2. PCB 回流、功放使能和供电去耦是否满足。

只改软件初始化不能替代第二层验证。项目的 EDA 图中可以看到主板和屏幕板的分工,屏幕板只承担显示连接,音频器件和主控集中在主板。

屏幕板 PCB 资料图

图 5 屏幕板 PCB 资料图,用于核对 FPC 和 QSPI 相关连接

省电逻辑的正确边界

项目固件把省电定时器和充电状态绑定:未充电时允许计时进入息屏,充电时关闭省电计时。这个策略比简单地“无操作就关屏”更安全,因为充电过程需要持续观察状态。

void InitializePowerSaveTimer() {
    power_save_timer_ = new PowerSaveTimer(240, -1, -1);
    power_save_timer_->OnEnterSleepMode([this]() {
        GetDisplay()->SetPowerSaveMode(true);
    });
    power_save_timer_->OnExitSleepMode([this]() {
        GetDisplay()->SetPowerSaveMode(false);
    });
    power_save_timer_->SetEnabled(true);
}

void InitializeBatteryMonitor() {
    adc_battery_monitor_ =
        new AdcBatteryMonitor(ADC_UNIT_1, ADC_CHANNEL_3,
                              5100000, 5100000, GPIO_NUM_NC);
    adc_battery_monitor_->OnChargingStatusChanged(
        [this](bool is_charging) {
            if (power_save_timer_ != nullptr)
                power_save_timer_->SetEnabled(!is_charging);
        });
}

这段逻辑可以迁移到其他电池设备,但不能直接推导出续航时间。续航还取决于射频占空比、屏幕亮度、音频功放和电池内阻;本次没有目标板实测,文章不做续航承诺。

实物正面与 PCB 顶层

图 6 实物正面和 PCB 顶层的对应关系,便于装配后检查器件方向

PCB 顶层

图 7 PCB 顶层资料图

PCB 底层

图 8 PCB 底层资料图。顶底层必须和同一 EDA 版本对应,不能混用旧版截图

我做了哪些可复验检查

这次没有把目标板“已经能跑”写成结论,而是把能在当前环境复验的部分单独列出:

  • 从 EDA 工程树确认存在 Schematic_Moji2 和 PCB_Moji2,并在编辑器中查看工程预览、主板和屏幕板资料。
  • 从 config.h 逐项解析 GPIO 宏,检查 QSPI、I2C、I2S、按键和背光的编号没有重复。
  • 从 README 和 config.json 核对构建目标为 ESP32-C5、ESP-IDF v5.5、16 MB Flash 和 80 MHz PSRAM 配置。
  • 用主机脚本验证 57,600 字节 DMA 行缓存计算和省电逻辑的状态转换。

没有完成的部分也必须说清:当前环境没有 Moji 2.0 实物、逻辑分析仪和声学测试设备,因此没有宣称 QSPI 波形、音频底噪、温升、续航或整机语音效果已经实测。需要复刻时,建议先按引脚表做 ERC/DRC,再用低亮度、无功放输出的顺序逐步上电。

复刻时的检查顺序

  1. 先在 EDA 中确认工程版本和 PCB 厚度,核对 FPC 插座方向。
  2. 不接电池,先用限流电源确认 3.3 V、背光和 QSPI 复位脚没有短路。
  3. 只烧录最小显示测试,确认 QSPI 四线和屏幕初始化。
  4. 再接 ES8311,先验证 I2C 应答,再打开 I2S 录放音。
  5. 最后启用省电和联网功能,观察充电状态切换时是否误入息屏。

资料与许可

本文基于 OSHWHub Moji 2.0 工程 和对应 嘉立创 EDA 工程 独立核验编写。硬件页面标注 CC BY-SA 4.0;固件片段来自 MoveCall/xiaozhi-esp32,仓库许可证为 MIT,引用代码保留原版权与许可要求。本文图片仅用于技术说明,图注已区分项目资料图与 EDA 工程预览;本次改动、代码整理和验证范围以文中说明为准。

技术交流,欢迎关注微信公众号:美男子玩编程。

创作许可协议

本项目采用 CC BY(署名) 进行许可。

评论(0)
- 没有更多了 -