fix(audio): resolve ES7210 microphone popping sound by correcting OSR and clock division configuration
This commit is contained in:
+88
-37
@@ -13,6 +13,12 @@ class ES7210:
|
||||
def __init__(self, i2c):
|
||||
self.i2c = i2c
|
||||
|
||||
def _write(self, reg, val):
|
||||
self.i2c.writeto_mem(self.ADDR, reg, bytes([val]))
|
||||
|
||||
def _read(self, reg):
|
||||
return self.i2c.readfrom_mem(self.ADDR, reg, 1)[0]
|
||||
|
||||
def init(self, sample_rate=16000, bit_width=16):
|
||||
"""Initializes the ES7210 registers for dual-microphone recording.
|
||||
|
||||
@@ -23,45 +29,85 @@ class ES7210:
|
||||
Returns:
|
||||
bool: True if initialization was successful, False otherwise.
|
||||
"""
|
||||
print("Initializing ES7210 Microphone ADC...")
|
||||
print("Initializing ES7210 Microphone ADC (ESPHome sequence)...")
|
||||
try:
|
||||
# 1. Reset the chip
|
||||
self._write(0x00, 0xFF) # Write all 1s to reset register
|
||||
time.sleep_ms(10)
|
||||
self._write(0x00, 0x00) # Release reset
|
||||
# 1. Software reset
|
||||
self._write(0x00, 0xFF)
|
||||
time.sleep_ms(20)
|
||||
self._write(0x00, 0x32)
|
||||
time.sleep_ms(20)
|
||||
self._write(0x01, 0x3F) # Clock off during config
|
||||
|
||||
# 2. Power management and system configuration
|
||||
self._write(0x01, 0x00) # Enable analog power, reference voltage
|
||||
self._write(0x11, 0x60) # Enable master clock PLL
|
||||
# 2. Timing control
|
||||
self._write(0x09, 0x30)
|
||||
self._write(0x0A, 0x30)
|
||||
|
||||
# 3. Configure Clock Dividers
|
||||
if sample_rate == 16000:
|
||||
self._write(0x02, 0x0C) # BCLK divider
|
||||
self._write(0x03, 0x10) # LRCK divider
|
||||
else: # 44100 / 48000 defaults
|
||||
self._write(0x02, 0x04)
|
||||
self._write(0x03, 0x08)
|
||||
# 3. High-pass filter
|
||||
self._write(0x23, 0x2A)
|
||||
self._write(0x22, 0x0A)
|
||||
self._write(0x20, 0x0A)
|
||||
self._write(0x21, 0x2A)
|
||||
|
||||
# 4. Mode config: clear bit 0 of Reg 0x08
|
||||
val08 = self._read(0x08)
|
||||
self._write(0x08, val08 & ~0x01)
|
||||
|
||||
# 5. Configure analog power
|
||||
self._write(0x40, 0xC3)
|
||||
|
||||
# 6. Mic bias voltage
|
||||
self._write(0x41, 0x70)
|
||||
self._write(0x42, 0x70)
|
||||
|
||||
# 7. Configure I2S format (16-bit, standard I2S, TDM disabled)
|
||||
self._write(0x11, 0x60)
|
||||
self._write(0x12, 0x00)
|
||||
|
||||
# 8. Configure sample rate (16kHz with 12.288MHz MCLK)
|
||||
# adc_div = 0x03, dll = 0x01, doubler = 0x01, osr = 0x20, lrck_h = 0x03, lrck_l = 0x00
|
||||
reg02_val = 0x03 | (1 << 6) | (1 << 7) # 0xC3
|
||||
self._write(0x02, reg02_val)
|
||||
self._write(0x07, 0x20)
|
||||
self._write(0x04, 0x03)
|
||||
self._write(0x05, 0x00)
|
||||
|
||||
# 9. Clear select bits for MIC gain registers
|
||||
for i in range(4):
|
||||
val_gain = self._read(0x43 + i)
|
||||
self._write(0x43 + i, val_gain & ~0x10)
|
||||
|
||||
# 4. Input Configuration (Enable Mics 1 and 2, power down Mics 3 and 4)
|
||||
self._write(0x47, 0x00) # Enable MIC1 / MIC2 analog front-ends
|
||||
self._write(0x48, 0xFF) # Power down MIC3 / MIC4 path
|
||||
self._write(0x49, 0x0A) # Power up PGA (Programmable Gain Amplifier) 1 and 2
|
||||
self._write(0x4A, 0x00) # Power down PGA 3 and 4
|
||||
# 10. Power down all MIC bias & PGA initially
|
||||
self._write(0x4B, 0xFF)
|
||||
self._write(0x4C, 0xFF)
|
||||
|
||||
# 5. Microphone Gain Settings (+24dB standard)
|
||||
# Gain range: 0x00 (0dB) to 0x0F (+45dB) in 3dB steps. 0x08 = +24dB.
|
||||
self._write(0x43, 0x08) # Set MIC1 Gain (+24dB)
|
||||
self._write(0x44, 0x08) # Set MIC2 Gain (+24dB)
|
||||
# 11. Configure MIC1 and MIC2 (gain = 30dB -> 0x0A, enable SELMIC)
|
||||
gain_reg_val = 0x0A
|
||||
# Enable ADC12 clocks
|
||||
val01 = self._read(0x01)
|
||||
self._write(0x01, val01 & ~0x0B)
|
||||
# Power on MIC1/2 bias, ADC, PGA
|
||||
self._write(0x4B, 0x00)
|
||||
# Select MIC1 and gain
|
||||
val43 = self._read(0x43)
|
||||
self._write(0x43, (val43 & ~0x0F) | 0x10 | gain_reg_val)
|
||||
# Select MIC2 and gain
|
||||
val44 = self._read(0x44)
|
||||
self._write(0x44, (val44 & ~0x0F) | 0x10 | gain_reg_val)
|
||||
|
||||
# 6. Set Digital Interface Format (I2S standard format)
|
||||
# Bit width: 0x00 = 24-bit, 0x01 = 16-bit, 0x02 = 8-bit, 0x03 = 32-bit
|
||||
fmt = 0x01 if bit_width == 16 else 0x00
|
||||
self._write(0x13, fmt) # Set serial output interface format
|
||||
self._write(0x14, 0x18) # Enable frame clock / bit clock output
|
||||
# 12. Power on mics low power registers
|
||||
self._write(0x47, 0x08)
|
||||
self._write(0x48, 0x08)
|
||||
self._write(0x49, 0x08)
|
||||
self._write(0x4A, 0x08)
|
||||
|
||||
# 7. Unmute ADCs and enable output
|
||||
self._write(0x12, 0x00) # Enable ADC digital filters (unmute)
|
||||
self._write(0x15, 0x30) # Enable output data pin (SDOUT) active
|
||||
# 13. Power down DLL
|
||||
self._write(0x06, 0x04)
|
||||
|
||||
# 14. Enable device state machine
|
||||
self._write(0x00, 0x71)
|
||||
time.sleep_ms(20)
|
||||
self._write(0x00, 0x41)
|
||||
time.sleep_ms(100)
|
||||
|
||||
print("ES7210 initialization complete.")
|
||||
return True
|
||||
@@ -69,9 +115,6 @@ class ES7210:
|
||||
print(f"Failed to initialize ES7210: {e}")
|
||||
return False
|
||||
|
||||
def _write(self, reg, val):
|
||||
self.i2c.writeto_mem(self.ADDR, reg, bytes([val]))
|
||||
|
||||
|
||||
def record_audio(duration_seconds=10, filename='recording.pcm'):
|
||||
"""Records raw stereo PCM data from the dual microphones to a file.
|
||||
@@ -120,6 +163,7 @@ def record_audio(duration_seconds=10, filename='recording.pcm'):
|
||||
|
||||
# Create reading buffer (reads 100ms chunks: 16000 samples/sec * 2 channels * 2 bytes/sample * 0.1s = 6400 bytes)
|
||||
buffer = bytearray(6400)
|
||||
mono_buf = bytearray(3200) # Half size for mono extraction
|
||||
|
||||
start_time = time.time()
|
||||
total_bytes = 0
|
||||
@@ -130,8 +174,15 @@ def record_audio(duration_seconds=10, filename='recording.pcm'):
|
||||
# Read raw stereo PCM data from I2S
|
||||
bytes_read = i2s.readinto(buffer)
|
||||
if bytes_read > 0:
|
||||
f.write(buffer[:bytes_read])
|
||||
total_bytes += bytes_read
|
||||
# Stereo-to-mono: extract left channel (every other 16-bit sample)
|
||||
mono_len = bytes_read // 2
|
||||
j = 0
|
||||
for i in range(0, bytes_read, 4):
|
||||
mono_buf[j] = buffer[i]
|
||||
mono_buf[j + 1] = buffer[i + 1]
|
||||
j += 2
|
||||
f.write(mono_buf[:mono_len])
|
||||
total_bytes += mono_len
|
||||
|
||||
print(f"Recording saved successfully to '{filename}' ({total_bytes} bytes).")
|
||||
return True
|
||||
|
||||
@@ -34,6 +34,26 @@ audio_amp_pin = None
|
||||
audio_amp_active_level = 0 # 0 = Active Low, 1 = Active High
|
||||
audio_mic_codec = "ES8311" # "ES7210" or "ES8311"
|
||||
|
||||
def _i2c_recovery(sda_pin, scl_pin):
|
||||
import time
|
||||
scl = Pin(scl_pin, Pin.OUT)
|
||||
sda = Pin(sda_pin, Pin.OUT)
|
||||
scl.value(1)
|
||||
sda.value(1)
|
||||
time.sleep_ms(1)
|
||||
for _ in range(9):
|
||||
scl.value(0)
|
||||
time.sleep_ms(1)
|
||||
scl.value(1)
|
||||
time.sleep_ms(1)
|
||||
scl.value(0)
|
||||
sda.value(0)
|
||||
time.sleep_ms(1)
|
||||
scl.value(1)
|
||||
time.sleep_ms(1)
|
||||
sda.value(1)
|
||||
time.sleep_ms(1)
|
||||
|
||||
def detect_board():
|
||||
global BOARD_TYPE, DISPLAY_TYPE, DISPLAY_WIDTH, DISPLAY_HEIGHT
|
||||
global spi_bus, i2c_bus, display_instance, touch
|
||||
@@ -60,6 +80,7 @@ def detect_board():
|
||||
|
||||
# Setup Touch: FT6336U on I2C(1)
|
||||
try:
|
||||
_i2c_recovery(2, 3)
|
||||
touch_i2c = I2C(1, sda=Pin(2), scl=Pin(3), freq=400000)
|
||||
from ft6336u import FT6336U
|
||||
touch = FT6336U(touch_i2c, rst_pin=28, int_pin=25)
|
||||
@@ -73,6 +94,7 @@ def detect_board():
|
||||
|
||||
# Try scanning SDA=16, SCL=15 (Hosyond pins)
|
||||
try:
|
||||
_i2c_recovery(16, 15)
|
||||
test_i2c = SoftI2C(sda=Pin(16), scl=Pin(15))
|
||||
devices = test_i2c.scan()
|
||||
if 0x38 in devices:
|
||||
@@ -121,6 +143,7 @@ def detect_board():
|
||||
|
||||
# 3. Try scanning SDA=13, SCL=14 (Waveshare RLCD pins)
|
||||
try:
|
||||
_i2c_recovery(13, 14)
|
||||
test_i2c = SoftI2C(sda=Pin(13), scl=Pin(14))
|
||||
devices = test_i2c.scan()
|
||||
if 0x70 in devices or 0x51 in devices:
|
||||
|
||||
Reference in New Issue
Block a user