Manejo de los periféricos de audio de la reTerminal D1001

Introducción
Esta guía presenta cómo manejar el periférico de audio I2S en la placa de desarrollo reTerminal D1001. La arquitectura del sistema incluye tres componentes principales:
- ESP32-P4: El procesador principal que gestiona el flujo de datos de audio y controla la configuración de los periféricos.
- ES8311: Un códec de audio mono de bajo consumo responsable de convertir los datos digitales I2S en señales de audio analógicas.
- PCA9535: Un expansor de E/S I2C usado para controlar el estado de habilitación del amplificador de potencia, proporcionando una expansión flexible de GPIO para el control de periféricos.
Diagrama de bloques de la arquitectura de audio
El sistema de audio utiliza una arquitectura de doble bus: el bus I2S está dedicado a la transmisión de datos de audio digital de alta velocidad, mientras que el bus I2C maneja los comandos de control de baja velocidad tanto para el códec como para el expansor de E/S.

Asignación de pines y principios
ESP32-P4 y ES8311 (datos y control de audio)
| Nombre de señal | Pin ESP32-P4 | Descripción de la función |
|---|---|---|
| I2C_SDA | GPIO20 | Datos en serie: Transporta comandos de configuración (volumen, frecuencia de muestreo) al ES8311. |
| I2C_SCL | GPIO21 | Reloj en serie: Sincroniza las transferencias de datos I2C. |
| I2S_MCK | GPIO33 | Reloj maestro: Reloj de referencia de alta frecuencia para los moduladores delta-sigma internos del códec. |
| I2S_BCK | GPIO32 | Reloj de bit: Sincroniza cada bit individual del flujo de datos de audio. |
| I2S_WS | GPIO31 | Selección de palabra: También conocido como LRCK, define el inicio de una nueva trama de audio y selecciona los canales Izquierdo/Derecho. |
| I2S_DO | GPIO30 | Salida de datos: Transmite los datos de audio PCM digitales desde el ESP32-P4 hacia el códec. |
| I2S_DI | GPIO11 | Entrada de datos: Reservado para una posible grabación de audio o loopback desde el códec. |
ESP32-P4 y PCA9535RGER (expansión de GPIO)
| Nombre de señal | Pin ESP32-P4 | Descripción de la función |
|---|---|---|
| I2C_SDA | GPIO20 | Bus de datos I2C compartido para controlar el expansor de E/S PCA9535. |
| I2C_SCL | GPIO21 | Bus de reloj I2C compartido. |
| EN_PA | EXP_GPO11 | Habilitación del PA: Se asigna al pin P13 en el PCA9535. Ajustar este pin a nivel ALTO habilita el amplificador de potencia externo. |
Flujo de software
Repositorio de ejemplo en GitHub
Descarga el repositorio oficial de reTerminal D1001 desde GitHub para obtener el código fuente y los controladores.
Por favor navega al directorio driver_examples/01_I2SCodec dentro del repositorio para encontrar el código fuente específico y los archivos de proyecto para este ejemplo de audio.
Secuencia de ejecución de desarrollo
Paso 1. Inicializar el expansor de E/S I2C (PCA9535RGER)
El amplificador de potencia externo (PA) se controla a través del expansor PCA9535. Habilitar el PA es crucial porque, sin él, ningún sonido audible llegará a los altavoces incluso si el códec está funcionando correctamente.
static esp_err_t pca9535_write_reg(uint8_t reg, uint8_t data)
{
uint8_t write_buf[2] = {reg, data};
return i2c_master_write_to_device(1, PCA9535_I2C_ADDR, write_buf, sizeof(write_buf), 1000 / portTICK_PERIOD_MS);
}
static void pca9535_init(void)
{
int i2c_master_port = 1; // Use I2C_NUM_1
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = MISC_I2C_SDA,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = MISC_I2C_SCL,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 100000,
};
i2c_param_config(i2c_master_port, &conf);
i2c_driver_install(i2c_master_port, conf.mode, 0, 0, 0);
// Configure Port 0 and Port 1 as output mode (0 = output, 1 = input)
pca9535_write_reg(0x06, 0x00);
pca9535_write_reg(0x07, 0x00);
// Set P13 to HIGH to enable the Power Amplifier
pca9535_write_reg(0x02, 0x00);
pca9535_write_reg(0x03, 0x08);
ESP_LOGI(TAG, "PCA9535 initialized, P13 set to HIGH");
}
Paso 2. Configurar el controlador I2S
I2S es un protocolo de comunicación serie síncrono utilizado específicamente para transmitir audio digital. Configuramos el ESP32-P4 como Maestro I2S, lo que significa que proporciona los relojes BCLK y WS al códec.
static esp_err_t i2s_driver_init(void)
{
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true; // Prevents playing stale data from the buffer
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(EXAMPLE_SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = I2S_MCK_IO,
.bclk = I2S_BCK_IO,
.ws = I2S_WS_IO,
.dout = I2S_DO_IO,
.din = I2S_DI_IO,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
std_cfg.clk_cfg.mclk_multiple = EXAMPLE_MCLK_MULTIPLE;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
return ESP_OK;
}
Paso 3. Inicializar el códec ES8311
El ES8311 debe configurarse para coincidir con los ajustes de I2S (frecuencia de muestreo, anchura de datos) definidos en el ESP32-P4. Esto se realiza a través del bus I2C. Antes de compilar el proyecto, puedes personalizar el comportamiento de audio modificando las macros en main/example_config.h:
| Macro | Descripción | Principios de configuración |
|---|---|---|
| EXAMPLE_SAMPLE_RATE | Frecuencia de muestreo de audio (Hz) | Define la frecuencia de las muestras de audio. Valores comunes son 16000 (voz) o 44100/48000 (música). |
| EXAMPLE_MCLK_MULTIPLE | Relación MCLK a LRCLK | El reloj maestro (MCLK) debe ser un múltiplo de la frecuencia de muestreo. 256 es estándar para 16 bits, pero 384 se usa a menudo para mayor precisión. |
| EXAMPLE_VOICE_VOLUME | Volumen de reproducción | Va de 0 a 100. Establece el nivel de salida inicial del códec ES8311. |
| EXAMPLE_MIC_GAIN | Ganancia del micrófono (dB) | Ajusta la sensibilidad de los micrófonos duales. Valores más altos aumentan el volumen pero pueden introducir ruido. |
| EXAMPLE_RECV_BUF_SIZE | Tamaño del búfer DMA | Controla el tamaño de los bloques de datos procesados por el DMA. Los búferes más grandes evitan cortes, pero aumentan la latencia de audio. |
static esp_err_t es8311_codec_init(void)
{
const i2c_config_t es_i2c_cfg = {
.sda_io_num = I2C_SDA_IO,
.scl_io_num = I2C_SCL_IO,
.mode = I2C_MODE_MASTER,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 100000,
};
i2c_param_config(I2C_NUM, &es_i2c_cfg);
i2c_driver_install(I2C_NUM, I2C_MODE_MASTER, 0, 0, 0);
es8311_handle_t es_handle = es8311_create(I2C_NUM, ES8311_ADDRRES_0);
const es8311_clock_config_t es_clk = {
.mclk_inverted = false,
.sclk_inverted = false,
.mclk_from_mclk_pin = true,
.mclk_frequency = EXAMPLE_MCLK_FREQ_HZ,
.sample_frequency = EXAMPLE_SAMPLE_RATE
};
es8311_init(es_handle, &es_clk, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16);
es8311_sample_frequency_config(es_handle, EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE, EXAMPLE_SAMPLE_RATE);
es8311_voice_volume_set(es_handle, EXAMPLE_VOICE_VOLUME, NULL);
es8311_microphone_config(es_handle, false);
return ESP_OK;
}
Paso 4. Punto de entrada principal y creación de tareas
La aplicación principal inicializa los periféricos y luego delega la lógica de reproducción a una tarea dedicada de FreeRTOS.
void app_main(void)
{
pca9535_init();
if (i2s_driver_init() != ESP_OK) {
ESP_LOGE(TAG, "i2s driver init failed");
abort();
}
if (es8311_codec_init() != ESP_OK) {
ESP_LOGE(TAG, "es8311 codec init failed");
abort();
}
xTaskCreate(i2s_music, "i2s_music", 4096, NULL, 5, NULL);
}
Paso 5. Precarga de DMA y reproducción de datos
El DMA (Acceso Directo a Memoria) permite que el periférico I2S obtenga datos directamente desde la memoria sin intervención de la CPU. La precarga del búfer DMA es una técnica crítica para evitar el ruido de “chasquidos” que se produce cuando el hardware I2S comienza con un búfer vacío, causando un cambio repentino del offset de CC.
static void i2s_music(void *args)
{
esp_err_t ret = ESP_OK;
size_t bytes_write = 0;
uint8_t *data_ptr = (uint8_t *)music_pcm_start;
// Preload data to avoid initial "pop" sound
ESP_ERROR_CHECK(i2s_channel_disable(tx_handle));
ESP_ERROR_CHECK(i2s_channel_preload_data(tx_handle, data_ptr, music_pcm_end - data_ptr, &bytes_write));
data_ptr += bytes_write;
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
while (1) {
ret = i2s_channel_write(tx_handle, data_ptr, music_pcm_end - data_ptr, &bytes_write, portMAX_DELAY);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "[music] i2s write failed");
abort();
}
data_ptr = (uint8_t *)music_pcm_start; // Loop playback
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
Solución de problemas
P1: No hay salida de sonido del altavoz
- Comprobar: Verifica si el amplificador de potencia (PA) está habilitado. La señal
EN_PAse controla mediante el expansor de E/S PCA9535 en el pin P13. Asegúrate de que se llame apca9535_init()y configure correctamente el registro de salida (Puerto 1, Bit 3). - Comprobar: Confirma las conexiones I2S y asegúrate de que se llame a la función
i2s_channel_enable()para el canal TX.
P2: El audio está distorsionado o contiene ruido crepitante
- Comprobar: Asegúrate de que la configuración del reloj I2S (MCLK, BCLK, WS) coincida con la frecuencia de muestreo de tu archivo de audio. Una discordancia en
EXAMPLE_SAMPLE_RATEpuede causar problemas de tono y velocidad. - Comprobar: Verifica que la precarga de DMA esté implementada como se muestra en el Paso 5. La precarga evita el sonido de "pop" causado por iniciar un canal con un búfer vacío.
P3: Fallo de comunicación I2C con ES8311 o PCA9535
- Comprobar: Verifica las conexiones I2C SDA (GPIO20) y SCL (GPIO21). Asegúrate de que ningún otro periférico esté en conflicto con estos pines.
- Comprobar: Confirma que las direcciones I2C sean correctas: 0x20 para PCA9535 y 0x18 para ES8311.