Skip to content

Commit 04a5441

Browse files
author
gychang
committed
Initial support for ESP32-C3-Lyra-V2 dev board
See https://docs.espressif.com/projects/esp-adf/en/latest/design-guide/dev-boards/user-guide-esp32-c3-lyra.html This commit adds support for audio out and the status LED. The audio out on this board is in PDM over the I2S encoder hardware. It's not 100% clear to me why the default neopixel_write implementation doesn't work, but it seems that deleting the RMT channel triggers some sort of glitch signal that clears the WS2812C LED.
1 parent 361dbc0 commit 04a5441

File tree

9 files changed

+158
-4
lines changed

9 files changed

+158
-4
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2021 microDev
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#include "supervisor/board.h"
8+
9+
// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2021 microDev
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#pragma once
8+
9+
// Board setup
10+
#define MICROPY_HW_BOARD_NAME "ESP32-C3-Lyra-V2"
11+
#define MICROPY_HW_MCU_NAME "ESP32-C3N4"
12+
13+
// Status LED
14+
#define MICROPY_HW_NEOPIXEL (&pin_GPIO10)
15+
#define MICROPY_HW_NEOPIXEL_COUNT (1)
16+
17+
// Default bus pins
18+
#define DEFAULT_UART_BUS_RX (&pin_GPIO20)
19+
#define DEFAULT_UART_BUS_TX (&pin_GPIO21)
20+
21+
// Serial over UART
22+
#define CIRCUITPY_CONSOLE_UART_RX DEFAULT_UART_BUS_RX
23+
#define CIRCUITPY_CONSOLE_UART_TX DEFAULT_UART_BUS_TX
24+
25+
// Audio out as PDM over IS2S
26+
#define CIRCUITPY_AUDIOBUSIO_PDMOUT (1)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CIRCUITPY_CREATOR_ID = 0x000C303A
2+
CIRCUITPY_CREATION_ID = 0x00C3A000
3+
4+
IDF_TARGET = esp32c3
5+
6+
CIRCUITPY_ESP_FLASH_MODE = qio
7+
CIRCUITPY_ESP_FLASH_FREQ = 80m
8+
CIRCUITPY_ESP_FLASH_SIZE = 4MB
9+
10+
CIRCUITPY_LEGACY_4MB_FLASH_LAYOUT = 1
11+
12+
CIRCUITPY_ESP_USB_SERIAL_JTAG = 0
13+
14+
CIRCUITPY_AUDIOBUSIO_PDMOUT = 1
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2021 microDev
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#include "shared-bindings/board/__init__.h"
8+
9+
static const mp_rom_map_elem_t board_module_globals_table[] = {
10+
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
11+
12+
{ MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) },
13+
{ MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) },
14+
{ MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) },
15+
{ MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) },
16+
{ MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) },
17+
{ MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) },
18+
{ MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) },
19+
{ MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) },
20+
{ MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) },
21+
{ MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) },
22+
{ MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) },
23+
{ MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) },
24+
{ MP_ROM_QSTR(MP_QSTR_IO19), MP_ROM_PTR(&pin_GPIO19) },
25+
{ MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) },
26+
{ MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) },
27+
28+
{ MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO20) },
29+
{ MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO21) },
30+
31+
{ MP_ROM_QSTR(MP_QSTR_MTMS), MP_ROM_PTR(&pin_GPIO4) },
32+
{ MP_ROM_QSTR(MP_QSTR_MTDI), MP_ROM_PTR(&pin_GPIO5) },
33+
{ MP_ROM_QSTR(MP_QSTR_MTCK), MP_ROM_PTR(&pin_GPIO6) },
34+
{ MP_ROM_QSTR(MP_QSTR_MTDO), MP_ROM_PTR(&pin_GPIO7) },
35+
36+
{ MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO9) },
37+
{ MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO10) },
38+
39+
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) },
40+
};
41+
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#
2+
# Espressif IoT Development Framework Configuration
3+
#
4+
#
5+
# Component config
6+
#
7+
#
8+
# LWIP
9+
#
10+
# end of LWIP
11+
12+
# end of Component config
13+
14+
# end of Espressif IoT Development Framework Configuration

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+
#ifdef CIRCUITPY_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+
#ifdef CIRCUITPY_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+
#ifdef CIRCUITPY_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+
#ifdef CIRCUITPY_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+
#ifdef CIRCUITPY_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/common-hal/neopixel_write/__init__.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,22 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout,
126126
// Update the next start to +2 ticks. It ensures that we've gone 300+ us.
127127
next_start_raw_ticks = port_get_raw_ticks(NULL) + 2;
128128

129+
#if defined(CONFIG_IDF_TARGET_ESP32C3)
130+
// Hold the pin, deleting the channel seems to glitch pixels into turning off.
131+
gpio_hold_en(digitalinout->pin->number);
132+
#endif
133+
129134
// Free channel again
130135
rmt_del_encoder(encoder);
131136
rmt_disable(channel);
132137
rmt_del_channel(channel);
133138
CHECK_ESP_RESULT(result);
139+
134140
// Swap pin back to GPIO mode
135141
gpio_set_direction(digitalinout->pin->number, GPIO_MODE_OUTPUT);
142+
143+
#if defined(CONFIG_IDF_TARGET_ESP32C3)
144+
// Release hold
145+
gpio_hold_dis(digitalinout->pin->number);
146+
#endif
136147
}

0 commit comments

Comments
 (0)