fix(audio): resolve ES7210 microphone popping sound by correcting OSR and clock division configuration

This commit is contained in:
Adolfo Reyna
2026-06-18 22:38:47 -04:00
parent 9eca3549c3
commit d778391c5c
7 changed files with 212 additions and 77 deletions
+39 -34
View File
@@ -2,9 +2,8 @@ import time
import machine
from machine import Pin, I2S
import board_config
import audio_util
def play_raw_pcm(filename, channels=2, rate=16000, bits=16, volume=90):
def play_ram_pcm(audio_chunks, channels=2, rate=16000, bits=16, volume=90):
from audio_util import ES8311
# 1. Start MCLK PWM using board config parameters
@@ -38,16 +37,11 @@ def play_raw_pcm(filename, channels=2, rate=16000, bits=16, volume=90):
amp_pin = Pin(board_config.audio_amp_pin, Pin.OUT, value=on_val)
try:
print(f"Streaming raw audio to speaker from '{filename}'...")
with open(filename, 'rb') as f:
buf = bytearray(2048)
while True:
bytes_read = f.readinto(buf)
if bytes_read == 0:
break
i2s.write(buf[:bytes_read])
print(f"Streaming raw audio to speaker from RAM ({len(audio_chunks)} chunks)...")
for chunk in audio_chunks:
i2s.write(chunk)
except Exception as e:
print("Error during raw playback:", e)
print("Error during RAM playback:", e)
finally:
time.sleep_ms(100) # Let buffer play out
amp_pin.value(off_val) # Disable amp
@@ -58,10 +52,10 @@ def play_raw_pcm(filename, channels=2, rate=16000, bits=16, volume=90):
def main():
display = board_config.display_instance
print("=== Dynamic Audio Loopback Utility Script ===")
print("=== Dynamic RAM-based Audio Loopback Script ===")
print(f"Board Detected: {board_config.BOARD_TYPE}")
# Initialize buttons using polling Pins instead of BoardButtons class (to avoid edge-triggered interrupt storms)
# Initialize buttons using polling Pins instead of BoardButtons class
key_pin = Pin(18, Pin.IN, Pin.PULL_UP)
boot_pin = Pin(0, Pin.IN, Pin.PULL_UP)
@@ -71,8 +65,6 @@ def main():
return board_config.touch.is_touched()
return key_pin.value() == 0
filename = "local_audio_test.pcm"
while True:
if display:
display.clear(0)
@@ -81,13 +73,13 @@ def main():
display.line(10, 20, 390, 20, 1)
display.text("1. Press KEY button to record 10s", 15, 60, 1)
display.text("2. Playback will start automatically", 15, 80, 1)
display.text("Ready...", 15, 120, 1)
display.text("Ready (RAM-based)...", 15, 120, 1)
else:
display.text("Touch Audio Loopback Test", 10, 10, 1)
display.line(10, 20, 310, 20, 1)
display.text("1. Press screen/KEY to record 10s", 10, 50, 1)
display.text("2. Playback starts automatically", 10, 70, 1)
display.text("Ready...", 10, 100, 1)
display.text("Ready (RAM-based)...", 10, 100, 1)
display.show()
print("Ready: Press and hold key/screen to record...")
@@ -129,8 +121,6 @@ def main():
display.text("Speak now!", 10, 80, 1)
display.show()
# We record as long as the button is pressed (or up to 10 seconds max)
# 1. Start MCLK PWM using board config parameters
mclk_pwm = None
if board_config.audio_mclk_pin is not None:
@@ -139,26 +129,28 @@ def main():
mclk_pwm.freq(board_config.audio_mclk_freq)
mclk_pwm.duty_u16(32768)
# 2. Configure I2S RX (Stereo 16kHz)
# 2. Configure I2S RX (Stereo 16kHz) - ibuf set to 16000 for safety
i2s_rx = I2S(1,
sck=Pin(board_config.audio_i2s_sck),
ws=Pin(board_config.audio_i2s_ws),
sd=Pin(board_config.audio_i2s_rx_sd),
mode=I2S.RX,
ibuf=8000,
ibuf=16000,
rate=16000,
bits=16,
format=I2S.STEREO)
# 3. Wake up and configure the microphone chip (ES7210 vs ES8311)
init_ok = False
if board_config.audio_mic_codec == "ES7210":
from audio_util import ES7210
mic_adc = ES7210(board_config.i2c_bus)
mic_adc.init(sample_rate=16000, bit_width=16)
init_ok = mic_adc.init(sample_rate=16000, bit_width=16)
else:
from audio_util import ES8311
mic_adc = ES8311(board_config.i2c_bus)
if mic_adc.init(sample_rate=16000):
init_ok = True
mic_adc.set_volume(80)
try:
mic_adc._write(0x14, 0x1A) # Enable analog mic input & PGA
@@ -166,19 +158,32 @@ def main():
mic_adc._write(0x17, 0xC8) # Set ADC digital volume
except:
pass
if not init_ok:
print("Microphone codec initialization failed! Aborting recording.")
i2s_rx.deinit()
if mclk_pwm:
mclk_pwm.deinit()
if display:
display.clear(0)
display.text("Codec Init Failed!", 15, 80, 1)
display.show()
time.sleep(3)
continue
# Record loop
buffer = bytearray(1024)
# Record loop to RAM - using ticks_ms for safe timing
buffer = bytearray(2048)
audio_chunks = []
total_bytes = 0
start_rec_time = time.time()
start_rec_time = time.ticks_ms()
max_duration_ms = 10000 # 10 seconds
try:
with open(filename, 'wb') as f:
while (time.time() - start_rec_time) < 10:
bytes_read = i2s_rx.readinto(buffer)
if bytes_read > 0:
f.write(buffer[:bytes_read])
total_bytes += bytes_read
while time.ticks_diff(time.ticks_ms(), start_rec_time) < max_duration_ms:
bytes_read = i2s_rx.readinto(buffer)
if bytes_read > 0:
audio_chunks.append(bytes(buffer[:bytes_read]))
total_bytes += bytes_read
except Exception as e:
print("Recording failed:", e)
finally:
@@ -186,12 +191,12 @@ def main():
if mclk_pwm:
mclk_pwm.deinit()
print(f"Recorded {total_bytes} bytes to '{filename}'.")
print(f"Recorded {total_bytes} bytes in RAM ({len(audio_chunks)} chunks).")
# Wait for release of key/trigger to debounce
time.sleep_ms(200)
# 4. Playback
# 4. Playback from RAM
if display:
display.clear(0)
if board_config.BOARD_TYPE == 'WAVESHARE_RLCD':
@@ -206,7 +211,7 @@ def main():
display.text(f"Bytes: {total_bytes}", 10, 80, 1)
display.show()
play_raw_pcm(filename, channels=2, rate=16000, bits=16, volume=95)
play_ram_pcm(audio_chunks, channels=2, rate=16000, bits=16, volume=95)
if display:
display.clear(0)