You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32S3 I2C从机模式下音频播放功能异常问题求助

问题

我正在使用ESP32S3开发板进行IoT项目,通过I2C将两块板设置为主从关系:主ESP32连接传感器,负责将数据传输给从ESP32;从ESP32需通过PCM5102 DAC模块播放SD卡中的音频文件。代码逻辑较为简单,但在从机模式下音频播放功能无法正常工作。请问这是因为I2C同一时间只能单向传输导致的吗?有没有解决办法?

我使用的音频库为ESP32-audioI2S,开发环境为PlatformIO下的Arduino框架,以下是从机的代码:

#include "Arduino.h"
#include "Audio.h"
#include "SD.h"
#include "FS.h"

//SPI Pins
#define SD_CS D9
#define SPI_MOSI MOSI
#define SPI_MISO MISO
#define SPI_SCK SCK

// I2S Pins
#define I2S_DOUT SDA
#define I2S_BCLK SCL
#define I2S_LRC D6

Audio audio;

// Function to receive signal - Toggles volume based on data received
void receiveSignal(int numBytes)
{
    while(Wire.available())
    {
        bool c = Wire.read();
        if(c)
        {
            audio.setVolume(21);
        }
        else
        {
            audio.setVolume(0);
        }
    }
    Serial.println();
}


// Setup function - Initialises Serial, SPI, SD and Audio Pins
void setup()
{
    pinMode(SD_CS, OUTPUT);
    digitalWrite(SD_CS, HIGH);

    SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);

    Serial.begin(115200);
    delay(5000);

    SD.begin(SD_CS);

    audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
    audio.setVolume(21);

    audio.connecttoFS(SD, "/audio.wav");
    
    Wire.begin(8);
    Wire.onReceive(receiveSignal);
}

// Loop function - Loops audio
void loop()
{
    audio.loop();
}
解答

你的问题并非I2C单向传输特性导致,核心冲突出在引脚复用上:

  • 你把I2S的I2S_DOUT定义为SDA,I2S_BCLK定义为SCL——这两个引脚同时被I2C(Wire库)和I2S音频库占用。当从机通过Wire监听I2C数据时,I2S的音频输出会被中断;反之,音频播放时也会干扰I2C通信,直接导致音频功能失效。

解决办法分两种,按优先级推荐:

1. 更换引脚,彻底避免复用

ESP32S3支持多组I2C和I2S引脚,重新分配不重叠的硬件资源即可解决问题:

  • 给I2S分配独立引脚,比如将I2S_DOUT改为D10、I2S_BCLK改为D11,避开默认的SDA/SCL引脚;
  • 或者改用软件I2C,将I2C通信转移到其他GPIO引脚(ESP32S3几乎所有GPIO都支持软件I2C)。

修改后的引脚定义与初始化示例:

// I2S Pins(更换为非I2C引脚)
#define I2S_DOUT D10
#define I2S_BCLK D11
#define I2S_LRC D6

// I2C Pins(指定独立引脚)
#define I2C_SDA D2
#define I2C_SCL D3

// 在setup中初始化Wire时指定引脚
Wire.begin(8, I2C_SDA, I2C_SCL);

2. 优化中断处理,减少阻塞(不推荐,仅作应急方案)

如果因硬件限制必须复用引脚,需修改I2C中断回调逻辑,避免在中断上下文直接操作音频库:

  • I2C的onReceive回调运行在中断上下文,而音频库的setVolume涉及底层硬件操作,中断内的阻塞会打乱I2S的音频输出时序;
  • 可以将接收到的控制信号存入全局变量,在loop函数中延迟处理音量更新。

修改后的核心代码示例:

bool volumeToggle = true; // 全局变量存储控制信号

void receiveSignal(int numBytes)
{
    while(Wire.available())
    {
        volumeToggle = Wire.read(); // 仅在中断中存储数据,不执行其他操作
    }
}

void loop()
{
    audio.loop();
    // 在loop中处理音量更新,避免中断阻塞
    static bool lastToggle = volumeToggle;
    if(lastToggle != volumeToggle)
    {
        audio.setVolume(volumeToggle ? 21 : 0);
        lastToggle = volumeToggle;
    }
}

此方法仍可能存在信号干扰风险,优先推荐第一种引脚分离方案。

内容的提问来源于stack exchange,提问作者amatsuraikou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 06:24:55