ESP32 + INMP441 + MAX98357A

INMP441 引脚 连接 ESP32 引脚 说明
VDD 3.3V 供电
GND GND 共地
SCK (BCLK) GPIO26 I²S BCLK
WS (LRCLK) GPIO25 I²S LRCLK
SD (DOUT) GPIO22 I²S 数据输出(麦到ESP32)
L/R GND 左声道(或接3.3V右声道)
CHIPEN 3.3V 常开
MAX98357A 引脚 连接 ESP32 引脚 说明
VIN 5V 功放电源(喇叭供电)
GND GND 共地
BCLK GPIO14 I²S BCLK
LRC GPIO12 I²S LRCLK
DIN GPIO27 I²S 数据输入(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();
  // }
}
posted @ 2025-08-12 14:14  huh&uh  阅读(235)  评论(0)    收藏  举报