ESP32 + INMP441 + MAX98357A
| INMP441 引腳 | 連接 ESP32 引腳 | 說明 |
|---|---|---|
| VDD | 3.3V | 供電 |
| GND | GND | 共地 |
| SCK (BCLK) | GPIO26 | I2S BCLK |
| WS (LRCLK) | GPIO25 | I2S LRCLK |
| SD (DOUT) | GPIO22 | I2S 數據輸出(麥到ESP32) |
| L/R | GND | 左聲道(或接3.3V右聲道) |
| CHIPEN | 3.3V | 常開 |
| MAX98357A 引腳 | 連接 ESP32 引腳 | 說明 |
|---|---|---|
| VIN | 5V | 功放電源(喇叭供電) |
| GND | GND | 共地 |
| BCLK | GPIO14 | I2S BCLK |
| LRC | GPIO12 | I2S LRCLK |
| DIN | GPIO27 | I2S 數據輸入(ESP32 到功放) |
| SD/EN | 3.3V | 常開(靜音腳,低電平關機) |
| GAIN | 懸空 | 默認增益 9dB |
platformio.ini
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
upload_speed = 921600
; 可選:更安靜的編譯輸出與更快的串口監視器
monitor_filters = time
; 如果遇到 I2S 兼容性問題,可嘗試固定核心版本,例如:
; platform = espressif32@6.11.0
main.cpp
#include <Arduino.h>
#include "driver/i2s.h"
/* -------- Pin Map -------- */
// INMP441 (I2S RX on I2S0)
#define I2S_RX_PORT I2S_NUM_0
#define RX_BCLK 26
#define RX_LRCLK 25
#define RX_DOUT 22
#define CH_FMT_RX I2S_CHANNEL_FMT_ONLY_LEFT // L/R=GND=左;若接3V3則改為 ONLY_RIGHT
// MAX98357A (I2S TX on I2S1)
#define I2S_TX_PORT I2S_NUM_1
#define TX_BCLK 14
#define TX_LRCLK 12
#define TX_DIN 27
// Amplifier SD/EN (mute control, HIGH=enable, LOW=shutdown)
#define AMP_SD_PIN 33
/* -------- Audio Params -------- */
#define SAMPLE_RATE 16000 // 可改 22050/32000/44100/48000(INMP441常用16k/48k)
#define RX_SAMPLES 512 // 每次搬運的“單聲道樣本數”
#define BITS_PER_SAMPLE I2S_BITS_PER_SAMPLE_32BIT // INMP441 24bit 裝在 32bit 框
#define SOFT_GAIN_DB 0.0f // 簡單軟件增益(dB),負值為衰減,如 -6.0f
#define LIMIT_ENABLE 1 // 開啟簡易限幅(避免溢出爆音)
#define HPF_ENABLE 1 // 開啟簡易高通(去直流),一階 IIR
#define HPF_ALPHA 0.999f // 0.99~0.999 間可調;越大越慢
/* -------- Helpers -------- */
// 計算線性增益系數
static float db_to_gain(float db) {
return powf(10.0f, db / 20.0f);
}
// 將 32bit 框中的 24bit(位于[31:8])取出并符號擴展為 32bit
static inline int32_t unpack24_from32(int32_t x32) {
int32_t s24 = x32 >> 8;
if (s24 & 0x00800000) s24 |= 0xFF000000;
return s24;
}
// 把 24bit 有效位放回 32bit 框(左對齊至[31:8])
static inline int32_t pack24_to32(int32_t s) {
// 限制到 24bit 范圍
if (s > 0x7FFFFF) s = 0x7FFFFF;
if (s < -0x800000) s = -0x800000;
return (s << 8);
}
/* -------- I2S Init -------- */
static void i2s_rx_init() {
i2s_config_t cfg = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = SAMPLE_RATE,
.bits_per_sample = BITS_PER_SAMPLE,
.channel_format = CH_FMT_RX, // 單聲道(左或右)
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = 0,
.dma_buf_count = 6,
.dma_buf_len = 256, // 每個 DMA 緩沖的樣本數
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = 0
};
i2s_pin_config_t pin = {
.bck_io_num = RX_BCLK,
.ws_io_num = RX_LRCLK,
.data_out_num = -1,
.data_in_num = RX_DOUT
};
i2s_driver_install(I2S_RX_PORT, &cfg, 0, nullptr);
i2s_set_pin(I2S_RX_PORT, &pin);
i2s_set_clk(I2S_RX_PORT, SAMPLE_RATE, BITS_PER_SAMPLE, I2S_CHANNEL_MONO);
}
static void i2s_tx_init() {
i2s_config_t cfg = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
.sample_rate = SAMPLE_RATE,
.bits_per_sample = BITS_PER_SAMPLE, // 給 MAX98357A 32bit frame(里頭放 24bit)
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 立體聲幀(我們 L=R)
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = 0,
.dma_buf_count = 6,
.dma_buf_len = 256,
.use_apll = false,
.tx_desc_auto_clear = true,
.fixed_mclk = 0
};
i2s_pin_config_t pin = {
.bck_io_num = TX_BCLK,
.ws_io_num = TX_LRCLK,
.data_out_num = TX_DIN,
.data_in_num = -1
};
i2s_driver_install(I2S_TX_PORT, &cfg, 0, nullptr);
i2s_set_pin(I2S_TX_PORT, &pin);
i2s_set_clk(I2S_TX_PORT, SAMPLE_RATE, BITS_PER_SAMPLE, I2S_CHANNEL_STEREO);
}
/* -------- Simple Audio Processing -------- */
// 直流阻斷(單通道),y[n] = x[n] - x[n-1] + alpha*y[n-1]
typedef struct {
float prev_x;
float prev_y;
} DCBlocker;
static DCBlocker dcblk = {0};
static inline float dc_block(float x, float alpha) {
float y = x - dcblk.prev_x + alpha * dcblk.prev_y;
dcblk.prev_x = x;
dcblk.prev_y = y;
return y;
}
void setup() {
Serial.begin(115200);
delay(300);
// SD/EN 控制腳
pinMode(AMP_SD_PIN, OUTPUT);
digitalWrite(AMP_SD_PIN, HIGH); // 先開機(如需開機靜音可先 LOW,準備好后再 HIGH)
i2s_rx_init();
i2s_tx_init();
Serial.println("ESP32 INMP441 -> MAX98357A passthrough ready.");
}
void loop() {
static int32_t rxBuf[RX_SAMPLES]; // I2S 讀回的 32bit 框
static int32_t txBuf[RX_SAMPLES * 2]; // 立體聲 L=R
size_t rxBytes = 0;
// 讀單聲道樣本
esp_err_t er = i2s_read(I2S_RX_PORT, (void*)rxBuf, sizeof(rxBuf), &rxBytes, portMAX_DELAY);
if (er != ESP_OK || rxBytes == 0) return;
const size_t n = rxBytes / sizeof(int32_t);
const float g = db_to_gain(SOFT_GAIN_DB);
// 處理并復制到雙聲道
size_t j = 0;
for (size_t i = 0; i < n; ++i) {
int32_t s = unpack24_from32(rxBuf[i]); // [-2^23, 2^23-1]
float xf = (float)s; // 轉浮點做處理
// 簡易高通去直流
if (HPF_ENABLE) xf = dc_block(xf, HPF_ALPHA);
// 軟件增益
xf *= g;
// 限幅至 24bit
if (LIMIT_ENABLE) {
if (xf > 8388607.0f) xf = 8388607.0f;
if (xf < -8388608.0f) xf = -8388608.0f;
}
int32_t s_proc = (int32_t)lrintf(xf);
int32_t s32 = pack24_to32(s_proc);
txBuf[j++] = s32; // Left
txBuf[j++] = s32; // Right
}
size_t txBytes = 0;
i2s_write(I2S_TX_PORT, (const void*)txBuf, j * sizeof(int32_t), &txBytes, portMAX_DELAY);
// (可選)打印一點信息
// static uint32_t t0 = millis();
// if (millis() - t0 > 1000) {
// Serial.printf("rx %u samp, tx %u bytes\n", (unsigned)n, (unsigned)txBytes);
// t0 = millis();
// }
}

浙公網安備 33010602011771號