diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk index 4b23a76ba6a15..92bd13e9980c4 100644 --- a/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk @@ -8,3 +8,7 @@ CIRCUITPY_ESP_FLASH_FREQ = 80m CIRCUITPY_ESP_FLASH_SIZE = 4MB CIRCUITPY_ESP_USB_SERIAL_JTAG = 0 + +ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT = 1 + +CFLAGS += -DESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT=$(ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT) diff --git a/ports/espressif/common-hal/audiobusio/I2SOut.c b/ports/espressif/common-hal/audiobusio/I2SOut.c index adfc081389a13..50a9cb3b11275 100644 --- a/ports/espressif/common-hal/audiobusio/I2SOut.c +++ b/ports/espressif/common-hal/audiobusio/I2SOut.c @@ -25,12 +25,29 @@ #include "driver/i2s_std.h" +#if ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT +#include "driver/i2s_pdm.h" +#endif + // Caller validates that pins are free. void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self, const mcu_pin_obj_t *bit_clock, const mcu_pin_obj_t *word_select, const mcu_pin_obj_t *data, const mcu_pin_obj_t *main_clock, bool left_justified) { port_i2s_allocate_init(&self->i2s, left_justified); + #if ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT + i2s_pdm_tx_config_t pdm_tx_cfg = { + .clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(16000), + .slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), + .gpio_cfg = { + .dout = data->number, + .invert_flags = { + .clk_inv = false, + }, + }, + }; + CHECK_ESP_RESULT(i2s_channel_init_pdm_tx_mode(self->i2s.handle, &pdm_tx_cfg)); + #else i2s_std_config_t i2s_config = { .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000), .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO), @@ -43,6 +60,7 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self, } }; CHECK_ESP_RESULT(i2s_channel_init_std_mode(self->i2s.handle, &i2s_config)); + #endif self->bit_clock = bit_clock; self->word_select = word_select; self->mclk = main_clock; diff --git a/ports/espressif/common-hal/audiobusio/__init__.c b/ports/espressif/common-hal/audiobusio/__init__.c index d07a0b521ba9b..321542ebafe87 100644 --- a/ports/espressif/common-hal/audiobusio/__init__.c +++ b/ports/espressif/common-hal/audiobusio/__init__.c @@ -19,10 +19,18 @@ #define I2S_DMA_BUFFER_MAX_SIZE 4092 // The number of DMA buffers to allocate #define CIRCUITPY_BUFFER_COUNT (3) + +#if ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT +// The maximum DMA buffer size in frames (at mono 16-bit) +#define CIRCUITPY_BUFFER_SIZE (I2S_DMA_BUFFER_MAX_SIZE / 8) +// The number of output channels is fixed at 1 +#define CIRCUITPY_OUTPUT_SLOTS (1) +#else // The maximum DMA buffer size in frames (at stereo 16-bit) #define CIRCUITPY_BUFFER_SIZE (I2S_DMA_BUFFER_MAX_SIZE / 4) // The number of output channels is fixed at 2 #define CIRCUITPY_OUTPUT_SLOTS (2) +#endif static void i2s_fill_buffer(i2s_t *self) { if (self->next_buffer_size == 0) { @@ -31,7 +39,7 @@ static void i2s_fill_buffer(i2s_t *self) { } int16_t *output_buffer = (int16_t *)self->next_buffer; size_t output_buffer_size = self->next_buffer_size; - const size_t bytes_per_output_frame = 4; + const size_t bytes_per_output_frame = CIRCUITPY_OUTPUT_SLOTS * 2; size_t bytes_per_input_frame = self->channel_count * self->bytes_per_sample; if (!self->playing || self->paused || !self->sample || self->stopping) { memset(output_buffer, 0, self->next_buffer_size); @@ -62,7 +70,8 @@ static void i2s_fill_buffer(i2s_t *self) { size_t sample_bytecount = self->sample_end - self->sample_data; // The framecount is the minimum of space left in the output buffer or left in the incoming sample. size_t framecount = MIN(output_buffer_size / bytes_per_output_frame, sample_bytecount / bytes_per_input_frame); - if (self->samples_signed && self->channel_count == 2) { + + if (self->samples_signed && self->channel_count == CIRCUITPY_OUTPUT_SLOTS) { if (self->bytes_per_sample == 2) { memcpy(output_buffer, self->sample_data, framecount * bytes_per_output_frame); } else { @@ -165,8 +174,16 @@ void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) { audiosample_reset_buffer(self->sample, false, 0); uint32_t sample_rate = audiosample_get_sample_rate(sample); + + #if ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT + i2s_pdm_tx_clk_config_t clk_config = I2S_PDM_TX_CLK_DEFAULT_CONFIG(sample_rate); + CHECK_ESP_RESULT(i2s_channel_reconfig_pdm_tx_clock(self->handle, &clk_config)); + size_t frame_size = sizeof(uint16_t); + #else i2s_std_clk_config_t clk_config = I2S_STD_CLK_DEFAULT_CONFIG(sample_rate); CHECK_ESP_RESULT(i2s_channel_reconfig_std_clock(self->handle, &clk_config)); + size_t frame_size = sizeof(uint32_t); + #endif // preload the data self->playing = true; @@ -183,12 +200,12 @@ void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) { self->next_buffer = &starting_frame; self->next_buffer_size = sizeof(starting_frame); i2s_fill_buffer(self); - i2s_channel_preload_data(self->handle, &starting_frame, sizeof(uint32_t), &bytes_loaded); + i2s_channel_preload_data(self->handle, &starting_frame, frame_size, &bytes_loaded); preloaded += bytes_loaded; } // enable the channel - i2s_channel_enable(self->handle); + CHECK_ESP_RESULT(i2s_channel_enable(self->handle)); // The IDF will call us back when there is a free DMA buffer. } diff --git a/ports/espressif/common-hal/audiobusio/__init__.h b/ports/espressif/common-hal/audiobusio/__init__.h index 0088cb87d5f91..7954fd17e5a8a 100644 --- a/ports/espressif/common-hal/audiobusio/__init__.h +++ b/ports/espressif/common-hal/audiobusio/__init__.h @@ -12,6 +12,10 @@ #include "driver/i2s_std.h" +#if ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT +#include "driver/i2s_pdm.h" +#endif + typedef struct { mp_obj_t *sample; bool left_justified; diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index 42bf3418f9639..7e68411705010 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -349,3 +349,6 @@ USB_NUM_IN_ENDPOINTS = 5 # Usually lots of flash space available CIRCUITPY_MESSAGE_COMPRESSION_LEVEL ?= 1 + +ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT ?= 0 +CFLAGS += -DESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT=$(ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT)