Skip to content

Commit 201c58e

Browse files
author
gychang
committed
Use PDM audio on esp32-c3-lyra board
The I2S data pin is hooked up directly to the amplifier on this board, so while it does generate sound, it sounds terrible since it's not actually handling the data signal. The sample implementation in the esp-adf uses the I2S hardware with PDM encoding to drive the amp. https://github.com/espressif/esp-adf/blob/ad4ac707c5dffb450dbabfdcaa7443165ee4e852/examples/player/pipeline_spiffs_mp3/main/play_spiffs_mp3_example.c#L76 This change hardcodes the PDM implementation for this board.
1 parent 77b6137 commit 201c58e

File tree

5 files changed

+48
-4
lines changed

5 files changed

+48
-4
lines changed

ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@ CIRCUITPY_ESP_FLASH_SIZE = 4MB
1010
CIRCUITPY_ESP_USB_SERIAL_JTAG = 0
1111

1212
ESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD = 1
13+
ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT = 1
1314

1415
CFLAGS += -DESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD=$(ESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD)
16+
CFLAGS += -DESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT=$(ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT)

ports/espressif/common-hal/audiobusio/I2SOut.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,29 @@
2525

2626
#include "driver/i2s_std.h"
2727

28+
#if ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT
29+
#include "driver/i2s_pdm.h"
30+
#endif
31+
2832
// Caller validates that pins are free.
2933
void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self,
3034
const mcu_pin_obj_t *bit_clock, const mcu_pin_obj_t *word_select,
3135
const mcu_pin_obj_t *data, const mcu_pin_obj_t *main_clock, bool left_justified) {
3236
port_i2s_allocate_init(&self->i2s, left_justified);
3337

38+
#if ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT
39+
i2s_pdm_tx_config_t pdm_tx_cfg = {
40+
.clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(16000),
41+
.slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
42+
.gpio_cfg = {
43+
.dout = data->number,
44+
.invert_flags = {
45+
.clk_inv = false,
46+
},
47+
},
48+
};
49+
CHECK_ESP_RESULT(i2s_channel_init_pdm_tx_mode(self->i2s.handle, &pdm_tx_cfg));
50+
#else
3451
i2s_std_config_t i2s_config = {
3552
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
3653
.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,
4360
}
4461
};
4562
CHECK_ESP_RESULT(i2s_channel_init_std_mode(self->i2s.handle, &i2s_config));
63+
#endif
4664
self->bit_clock = bit_clock;
4765
self->word_select = word_select;
4866
self->mclk = main_clock;

ports/espressif/common-hal/audiobusio/__init__.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,18 @@
1919
#define I2S_DMA_BUFFER_MAX_SIZE 4092
2020
// The number of DMA buffers to allocate
2121
#define CIRCUITPY_BUFFER_COUNT (3)
22+
23+
#if ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT
24+
// The maximum DMA buffer size in frames (at mono 16-bit)
25+
#define CIRCUITPY_BUFFER_SIZE (I2S_DMA_BUFFER_MAX_SIZE / 8)
26+
// The number of output channels is fixed at 1
27+
#define CIRCUITPY_OUTPUT_SLOTS (1)
28+
#else
2229
// The maximum DMA buffer size in frames (at stereo 16-bit)
2330
#define CIRCUITPY_BUFFER_SIZE (I2S_DMA_BUFFER_MAX_SIZE / 4)
2431
// The number of output channels is fixed at 2
2532
#define CIRCUITPY_OUTPUT_SLOTS (2)
33+
#endif
2634

2735
static void i2s_fill_buffer(i2s_t *self) {
2836
if (self->next_buffer_size == 0) {
@@ -31,7 +39,7 @@ static void i2s_fill_buffer(i2s_t *self) {
3139
}
3240
int16_t *output_buffer = (int16_t *)self->next_buffer;
3341
size_t output_buffer_size = self->next_buffer_size;
34-
const size_t bytes_per_output_frame = 4;
42+
const size_t bytes_per_output_frame = CIRCUITPY_OUTPUT_SLOTS * 2;
3543
size_t bytes_per_input_frame = self->channel_count * self->bytes_per_sample;
3644
if (!self->playing || self->paused || !self->sample || self->stopping) {
3745
memset(output_buffer, 0, self->next_buffer_size);
@@ -62,7 +70,8 @@ static void i2s_fill_buffer(i2s_t *self) {
6270
size_t sample_bytecount = self->sample_end - self->sample_data;
6371
// The framecount is the minimum of space left in the output buffer or left in the incoming sample.
6472
size_t framecount = MIN(output_buffer_size / bytes_per_output_frame, sample_bytecount / bytes_per_input_frame);
65-
if (self->samples_signed && self->channel_count == 2) {
73+
74+
if (self->samples_signed && self->channel_count == CIRCUITPY_OUTPUT_SLOTS) {
6675
if (self->bytes_per_sample == 2) {
6776
memcpy(output_buffer, self->sample_data, framecount * bytes_per_output_frame);
6877
} else {
@@ -165,8 +174,16 @@ void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) {
165174
audiosample_reset_buffer(self->sample, false, 0);
166175

167176
uint32_t sample_rate = audiosample_get_sample_rate(sample);
177+
178+
#if ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT
179+
i2s_pdm_tx_clk_config_t clk_config = I2S_PDM_TX_CLK_DEFAULT_CONFIG(sample_rate);
180+
CHECK_ESP_RESULT(i2s_channel_reconfig_pdm_tx_clock(self->handle, &clk_config));
181+
size_t frame_size = sizeof(uint16_t);
182+
#else
168183
i2s_std_clk_config_t clk_config = I2S_STD_CLK_DEFAULT_CONFIG(sample_rate);
169184
CHECK_ESP_RESULT(i2s_channel_reconfig_std_clock(self->handle, &clk_config));
185+
size_t frame_size = sizeof(uint32_t);
186+
#endif
170187

171188
// preload the data
172189
self->playing = true;
@@ -183,12 +200,12 @@ void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) {
183200
self->next_buffer = &starting_frame;
184201
self->next_buffer_size = sizeof(starting_frame);
185202
i2s_fill_buffer(self);
186-
i2s_channel_preload_data(self->handle, &starting_frame, sizeof(uint32_t), &bytes_loaded);
203+
i2s_channel_preload_data(self->handle, &starting_frame, frame_size, &bytes_loaded);
187204
preloaded += bytes_loaded;
188205
}
189206

190207
// enable the channel
191-
i2s_channel_enable(self->handle);
208+
CHECK_ESP_RESULT(i2s_channel_enable(self->handle));
192209

193210
// The IDF will call us back when there is a free DMA buffer.
194211
}

ports/espressif/common-hal/audiobusio/__init__.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212

1313
#include "driver/i2s_std.h"
1414

15+
#if ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT
16+
#include "driver/i2s_pdm.h"
17+
#endif
18+
1519
typedef struct {
1620
mp_obj_t *sample;
1721
bool left_justified;

ports/espressif/mpconfigport.mk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,3 +349,6 @@ USB_NUM_IN_ENDPOINTS = 5
349349

350350
# Usually lots of flash space available
351351
CIRCUITPY_MESSAGE_COMPRESSION_LEVEL ?= 1
352+
353+
ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT ?= 0
354+
CFLAGS += -DESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT=$(ESPRESSIF_ESP32C3_LYRA_AUDIOBUSIO_PDMOUT)

0 commit comments

Comments
 (0)