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
相关产品推荐
相关产品推荐

