前言:
今天弄了一天,直到现在的半夜,终于弄出了没有杂音(需要靠近INMP441麦克风说话)的音频采集+播放demo。一定有人苦于没有参考代码而抓狂,就像今天的我,故将代码录而记之,以飨后来者。
板子:esp32-wroom-32、INMP441、MAX98357A、喇叭
开发环境:esp-idf v5.2
连线:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s_std.h"
#include "driver/gpio.h"
//INMP引脚,根据自己连线修改
#define INMP_SD GPIO_NUM_13
#define INMP_SCK GPIO_NUM_14
#define INMP_WS GPIO_NUM_27
//MAX98357A引脚,根据自己连线修改
#define MAX_DIN GPIO_NUM_15
#define MAX_BCLK GPIO_NUM_16
#define MAX_LRC GPIO_NUM_17
//配置rx对INMP441的采样率为44.1kHz,这是常用的人声采样率
#define SAMPLE_RATE 44100
//buf size计算方法:根据esp32官方文档,buf size = dma frame num * 声道数 * 数据位宽 / 8
#define BUF_SIZE (1023 * 1 * 32 / 8)
//音频buffer
uint8_t buf[BUF_SIZE];
i2s_chan_handle_t rx_handle;
i2s_chan_handle_t tx_handle;
//初始化i2s rx,用于从INMP441接收数据
static void i2s_rx_init(void)
{
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
//dma frame num使用最大值,增大dma一次搬运的数据量,能够提高效率,减小杂音,使用1023可以做到没有一丝杂音
chan_cfg.dma_frame_num = 1023;
i2s_new_channel(&chan_cfg, NULL, &rx_handle);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
//虽然inmp441采集数据为24bit,但是仍可使用32bit来接收,中间存储过程不需考虑,只要让声音怎么进来就怎么出去即可
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.dout = I2S_GPIO_UNUSED,
.bclk = INMP_SCK,
.ws = INMP_WS,
.din = INMP_SD,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
i2s_channel_init_std_mode(rx_handle, &std_cfg);
i2s_channel_enable(rx_handle);
}
//初始化tx,用于向MAX98357A写数据
static void i2s_tx_init(void)
{
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_MASTER);
chan_cfg.dma_frame_num = 1023;
i2s_new_channel(&chan_cfg, &tx_handle, NULL);
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.din = I2S_GPIO_UNUSED,
.bclk = MAX_BCLK,
.ws = MAX_LRC,
.dout = MAX_DIN,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
i2s_channel_init_std_mode(tx_handle, &std_cfg);
i2s_channel_enable(tx_handle);
}
static void i2s_read_task(void *args)
{
size_t bytes = 0;
//一次性读取buf_size数量的音频,即dma最大搬运一次的数据量,读成功后,写入tx,即可通过MAX98357A播放
while (1) {
if (i2s_channel_read(rx_handle, buf, BUF_SIZE, &bytes, 1000) == ESP_OK)
{
i2s_channel_write(tx_handle, buf, BUF_SIZE, &bytes, 1000);
}
vTaskDelay(pdMS_TO_TICKS(5));
}
vTaskDelete(NULL);
}
void app_main(void){
i2s_tx_init();
i2s_rx_init();
xTaskCreate(i2s_read_task, "i2s_read_task", 4096 * 2, NULL, tskIDLE_PRIORITY, NULL);
}