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

ESP32 S3 I2S双工异常:MEMS麦可读但MAX98357仅输出杂音

问题描述

我尝试用MEMS数字麦克风和MAX98357音频codec放大器实现实时对讲功能,将麦克风的I2S输入引脚接ESP32 S3,ESP的I2S输出引脚接放大器。目前ESP能正常读取麦克风采集的声音,但向放大器输出时只有微弱杂音,无有效音频。已确认硬件无故障,推测问题出在代码,怀疑无法同时完成I2S读写操作,附上参考代码请求排查:

#include <stdint.h>
#include <stdlib.h>
#include "sdkconfig.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "driver/i2s_std.h"
#include "driver/gpio.h"
#include "esp_check.h"
#include "driver/i2s_tdm.h"


#define MICROPHONE_DATA_IN          GPIO_NUM_10 

#define I2S_BCLK                    GPIO_NUM_12     
#define I2S_LRCLK                   GPIO_NUM_13

#define AMPLIFIER_DATA_OUT          GPIO_NUM_11

#define I2S_MIC_LR                   GPIO_NUM_14

#define EXAMPLE_BUFF_SIZE               2048

static i2s_chan_handle_t                tx_chan;        
static i2s_chan_handle_t                rx_chan;        

static void i2s_audio_task(void *args)
{
    uint8_t *r_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
    assert(r_buf); 

    size_t r_bytes = 0;
    size_t w_bytes = 0;

    ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
    ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));

    esp_err_t ret = ESP_OK; 
    int count = 0;

    do {
        ret = i2s_channel_read(rx_chan, r_buf, EXAMPLE_BUFF_SIZE, &r_bytes, 100);
        if (ret == ESP_OK){
            printf("Read was successful...\n");
            printf("[0] %d [1] %d [2] %d [3] %d\n[4] %d [5] %d [6] %d [7] %d\n\n",
                   r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4], r_buf[5], r_buf[6], r_buf[7]);
        }
        ret = i2s_channel_write(tx_chan, r_buf, EXAMPLE_BUFF_SIZE, &w_bytes, 100);
        if(ret == ESP_OK){
            printf("Write was successful...\n");
            printf("[0] %d [1] %d [2] %d [3] %d\n[4] %d [5] %d [6] %d [7] %d\n\n",
                   r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4], r_buf[5], r_buf[6], r_buf[7]);
        } 

        count = count +1;
        vTaskDelay(100);
    }while (count <20);

    free(r_buf);
    vTaskDelete(NULL);
}

static void i2s_std_duplex(void)
{
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan));
    i2s_std_config_t std_cfg = {
        .clk_cfg  = I2S_STD_CLK_DEFAULT_CONFIG(44100),
        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED,    
            .bclk = I2S_BCLK,
            .ws   = I2S_LRCLK,
            .dout = AMPLIFIER_DATA_OUT,
            .din  = MICROPHONE_DATA_IN, 
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv   = false,
            },
        },
    };

    ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
    ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg));
}



void app_main(void)
{
    i2s_std_duplex();
    enter code here
    xTaskCreate(i2s_audio_task, "i2s_audio_task", 4096, NULL, 5, NULL);
}
问题排查与修复方案
  • 未使用麦克风LR引脚:代码中定义了I2S_MIC_LR但未配置,部分MEMS麦克风需通过该引脚指定采样声道,需补充引脚配置或按 datasheet 设置固定电平。
  • I2S双工配置冲突:复用同一配置初始化tx/rx通道可能导致冲突,需分别为接收(麦克风)和发送(放大器)通道配置独立的GPIO参数,避免无效引脚复用。
  • 数据格式不匹配:麦克风输出16位采样数据,代码用uint8_t缓冲区会拆分数据,导致字节序错误,需改用int16_t存储采样值。
  • 串行读写与延迟问题:vTaskDelay(100)会导致音频严重丢帧,实时场景需去掉额外延迟,用portMAX_DELAY保证读写数据完整。
  • 代码残留错误:app_main()中的enter code here是无效代码,需删除。
修改后的代码
#include <stdint.h>
#include <stdlib.h>
#include "sdkconfig.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "driver/i2s_std.h"
#include "driver/gpio.h"
#include "esp_check.h"

#define MICROPHONE_DATA_IN      GPIO_NUM_10 
#define I2S_BCLK                GPIO_NUM_12     
#define I2S_LRCLK               GPIO_NUM_13
#define AMPLIFIER_DATA_OUT      GPIO_NUM_11
#define I2S_MIC_LR              GPIO_NUM_14

#define EXAMPLE_BUFF_SIZE       2048  // 16位数据,对应1024个采样点

static i2s_chan_handle_t tx_chan;        
static i2s_chan_handle_t rx_chan;        

static void i2s_audio_task(void *args)
{
    // 用int16_t存储16位采样数据
    int16_t *r_buf = (int16_t *)calloc(1, EXAMPLE_BUFF_SIZE);
    assert(r_buf); 

    size_t r_bytes = 0;
    size_t w_bytes = 0;

    ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
    ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));

    esp_err_t ret = ESP_OK; 

    while (1) {
        // 读取麦克风数据,超时设为portMAX_DELAY保证数据完整
        ret = i2s_channel_read(rx_chan, r_buf, EXAMPLE_BUFF_SIZE, &r_bytes, portMAX_DELAY);
        if (ret == ESP_OK){
            printf("Read %d bytes, first sample: %d\n", r_bytes, r_buf[0]);
        }

        // 直接写入放大器,复用读取到的16位数据
        ret = i2s_channel_write(tx_chan, r_buf, r_bytes, &w_bytes, portMAX_DELAY);
        if(ret == ESP_OK){
            printf("Write %d bytes\n", w_bytes);
        } 
    }

    free(r_buf);
    vTaskDelete(NULL);
}

static void i2s_std_duplex(void)
{
    // 初始化双工通道,自动分配I2S端口
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan));

    // 麦克风接收通道配置
    i2s_std_config_t rx_std_cfg = {
        .clk_cfg  = I2S_STD_CLK_DEFAULT_CONFIG(44100),
        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED,    
            .bclk = I2S_BCLK,
            .ws   = I2S_LRCLK,
            .din  = MICROPHONE_DATA_IN, 
            .dout = I2S_GPIO_UNUSED,
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv   = false,  // 按麦克风datasheet调整极性
            },
        },
    };

    // 放大器发送通道配置
    i2s_std_config_t tx_std_cfg = {
        .clk_cfg  = I2S_STD_CLK_DEFAULT_CONFIG(44100),
        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED,    
            .bclk = I2S_BCLK,
            .ws   = I2S_LRCLK,
            .dout = AMPLIFIER_DATA_OUT,
            .din  = I2S_GPIO_UNUSED,
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv   = false,  // 按MAX98357 datasheet调整极性
            },
        },
    };

    // 分别初始化收发通道,避免配置冲突
    ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &rx_std_cfg));
    ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &tx_std_cfg));

    // 配置麦克风LR引脚,设置为高电平选择左声道(按需调整)
    gpio_config_t mic_lr_cfg = {
        .pin_bit_mask = (1ULL << I2S_MIC_LR),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE,
    };
    ESP_ERROR_CHECK(gpio_config(&mic_lr_cfg));
    ESP_ERROR_CHECK(gpio_set_level(I2S_MIC_LR, 1));
}

void app_main(void)
{
    i2s_std_duplex();
    // 增大栈大小适配16位缓冲区处理
    xTaskCreate(i2s_audio_task, "i2s_audio_task", 8192, NULL, 5, NULL);
}
额外注意事项
  • 核对MEMS麦克风和MAX98357的WS(LRCLK)极性,若杂音仍存在,可尝试修改ws_inv参数为true。
  • MAX98357默认工作在I2S模式,无需额外配置引脚,确保硬件连接正确(仅需BCLK、LRCLK、DATA三根线)。
  • 若出现音频卡顿,可适当增大缓冲区大小或调整采样率至48000Hz。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 01:38:11