Update ADC and I2S APIs for 5.1

This fixes ESP32 because the BufferedIn used the old ADC API and
I2S did too indirectly.

Fixes #8429
This commit is contained in:
Scott Shawcroft 2023-09-26 11:53:01 -07:00
parent 1c0155c99b
commit 3fdfb9bd32
No known key found for this signature in database
GPG Key ID: 0DFD512649C052DA
23 changed files with 183 additions and 291 deletions

3
.gitmodules vendored
View File

@ -147,9 +147,6 @@
[submodule "ports/espressif/esp-protocols"] [submodule "ports/espressif/esp-protocols"]
path = ports/espressif/esp-protocols path = ports/espressif/esp-protocols
url = https://github.com/espressif/esp-protocols.git url = https://github.com/espressif/esp-protocols.git
[submodule "ports/espressif/esp-iot-solution"]
path = ports/espressif/esp-iot-solution
url = https://github.com/espressif/esp-iot-solution.git
[submodule "ports/espressif/esp-camera"] [submodule "ports/espressif/esp-camera"]
path = ports/espressif/esp-camera path = ports/espressif/esp-camera
url = https://github.com/adafruit/esp32-camera.git url = https://github.com/adafruit/esp32-camera.git

View File

@ -447,10 +447,6 @@ msgstr ""
msgid "A hardware interrupt channel is already in use" msgid "A hardware interrupt channel is already in use"
msgstr "" msgstr ""
#: ports/espressif/common-hal/analogio/AnalogIn.c
msgid "ADC2 is being used by WiFi"
msgstr ""
#: ports/raspberrypi/common-hal/wifi/Radio.c #: ports/raspberrypi/common-hal/wifi/Radio.c
msgid "AP could not be started" msgid "AP could not be started"
msgstr "" msgstr ""
@ -2156,11 +2152,6 @@ msgstr ""
msgid "Unable to allocate the heap." msgid "Unable to allocate the heap."
msgstr "" msgstr ""
#: ports/espressif/common-hal/analogbufio/BufferedIn.c
#, c-format
msgid "Unable to configure ADC DMA controller, ErrorCode:%d"
msgstr ""
#: ports/espressif/common-hal/busio/I2C.c #: ports/espressif/common-hal/busio/I2C.c
msgid "Unable to create lock" msgid "Unable to create lock"
msgstr "" msgstr ""
@ -2179,20 +2170,10 @@ msgstr ""
msgid "Unable to init parser" msgid "Unable to init parser"
msgstr "" msgstr ""
#: ports/espressif/common-hal/analogbufio/BufferedIn.c
#, c-format
msgid "Unable to initialize ADC DMA controller, ErrorCode:%d"
msgstr ""
#: shared-module/displayio/OnDiskBitmap.c #: shared-module/displayio/OnDiskBitmap.c
msgid "Unable to read color palette data" msgid "Unable to read color palette data"
msgstr "" msgstr ""
#: ports/espressif/common-hal/analogbufio/BufferedIn.c
#, c-format
msgid "Unable to start ADC DMA controller, ErrorCode:%d"
msgstr ""
#: ports/espressif/common-hal/mdns/Server.c #: ports/espressif/common-hal/mdns/Server.c
#: ports/raspberrypi/common-hal/mdns/Server.c #: ports/raspberrypi/common-hal/mdns/Server.c
msgid "Unable to start mDNS query" msgid "Unable to start mDNS query"
@ -4207,7 +4188,8 @@ msgstr ""
msgid "unexpected keyword argument" msgid "unexpected keyword argument"
msgstr "" msgstr ""
#: py/bc.c py/objnamedtuple.c shared-bindings/traceback/__init__.c #: py/argcheck.c py/bc.c py/objnamedtuple.c
#: shared-bindings/traceback/__init__.c
msgid "unexpected keyword argument '%q'" msgid "unexpected keyword argument '%q'"
msgstr "" msgstr ""
@ -4364,10 +4346,6 @@ msgstr ""
msgid "wrong output type" msgid "wrong output type"
msgstr "" msgstr ""
#: ports/espressif/common-hal/audiobusio/__init__.c
msgid "xTaskCreate failed"
msgstr ""
#: extmod/ulab/code/scipy/signal/signal.c #: extmod/ulab/code/scipy/signal/signal.c
msgid "zi must be an ndarray" msgid "zi must be an ndarray"
msgstr "" msgstr ""

2
main.c
View File

@ -1190,7 +1190,9 @@ void NORETURN nlr_jump_fail(void *val) {
#ifndef NDEBUG #ifndef NDEBUG
static void NORETURN __fatal_error(const char *msg) { static void NORETURN __fatal_error(const char *msg) {
#if CIRCUITPY_DEBUG == 0
reset_into_safe_mode(SAFE_MODE_HARD_FAULT); reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
#endif
while (true) { while (true) {
} }
} }

View File

@ -304,17 +304,6 @@ ifneq ($(CIRCUITPY_BLEIO),0)
SRC_C += common-hal/_bleio/ble_events.c SRC_C += common-hal/_bleio/ble_events.c
endif endif
ifneq ($(CIRCUITPY_PARALLELDISPLAY),0)
ifeq ($(IDF_TARGET),esp32s3)
LCD_SRC = 8080_lcd_$(IDF_TARGET)
else
LCD_SRC = i2s_lcd_$(IDF_TARGET)_driver
endif
SRC_C += esp-iot-solution/components/bus/$(LCD_SRC).c
$(BUILD)/esp-iot-solution/components/bus/$(LCD_SRC).o: CFLAGS += -Wno-sign-compare
CFLAGS += -isystem esp-iot-solution/components/bus/include
endif
ifneq ($(CIRCUITPY_DOTCLOCKFRAMEBUFFER),0) ifneq ($(CIRCUITPY_DOTCLOCKFRAMEBUFFER),0)
CFLAGS += -isystem esp-idf/components/esp_lcd/include CFLAGS += -isystem esp-idf/components/esp_lcd/include
CFLAGS += -isystem esp-idf/components/esp_lcd/interface CFLAGS += -isystem esp-idf/components/esp_lcd/interface

View File

@ -4,9 +4,9 @@ CIRCUITPY_CREATION_ID = 0x00320003
IDF_TARGET = esp32 IDF_TARGET = esp32
CIRCUITPY_ESP_FLASH_SIZE = 8MB CIRCUITPY_ESP_FLASH_SIZE = 8MB
CIRCUITPY_ESP_FLASH_MODE = dio CIRCUITPY_ESP_FLASH_MODE = qio
CIRCUITPY_ESP_FLASH_FREQ = 40m CIRCUITPY_ESP_FLASH_FREQ = 80m
CIRCUITPY_ESP_PSRAM_SIZE = 2MB CIRCUITPY_ESP_PSRAM_SIZE = 2MB
CIRCUITPY_ESP_PSRAM_MODE = qio CIRCUITPY_ESP_PSRAM_MODE = qio
CIRCUITPY_ESP_PSRAM_FREQ = 40m CIRCUITPY_ESP_PSRAM_FREQ = 80m

View File

@ -28,6 +28,7 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include "bindings/espidf/__init__.h"
#include "common-hal/analogbufio/BufferedIn.h" #include "common-hal/analogbufio/BufferedIn.h"
#include "shared-bindings/analogbufio/BufferedIn.h" #include "shared-bindings/analogbufio/BufferedIn.h"
#include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/Pin.h"
@ -39,7 +40,6 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "driver/adc.h"
// #define DEBUG_ANALOGBUFIO // #define DEBUG_ANALOGBUFIO
@ -72,35 +72,10 @@ static void stop_dma(analogbufio_bufferedin_obj_t *self);
void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *self, const mcu_pin_obj_t *pin, uint32_t sample_rate) { void common_hal_analogbufio_bufferedin_construct(analogbufio_bufferedin_obj_t *self, const mcu_pin_obj_t *pin, uint32_t sample_rate) {
self->pin = pin; self->pin = pin;
self->sample_rate = sample_rate; self->sample_rate = sample_rate;
}
static void start_dma(analogbufio_bufferedin_obj_t *self, adc_digi_convert_mode_t *convert_mode, adc_digi_output_format_t *output_format) {
uint16_t adc1_chan_mask = 0;
uint16_t adc2_chan_mask = 0;
const mcu_pin_obj_t *pin = self->pin;
uint32_t sample_rate = self->sample_rate;
*output_format = ADC_DIGI_OUTPUT_FORMAT_TYPE1;
if (pin->adc_index == ADC_UNIT_1) {
*convert_mode = ADC_CONV_SINGLE_UNIT_1;
} else {
*convert_mode = ADC_CONV_SINGLE_UNIT_2;
}
if (pin->adc_index == NO_ADC || pin->adc_channel == NO_ADC_CHANNEL) { if (pin->adc_index == NO_ADC || pin->adc_channel == NO_ADC_CHANNEL) {
raise_ValueError_invalid_pin(); raise_ValueError_invalid_pin();
} }
/*
* Chip version Conversion Mode Output Format Type
* ESP32 1 TYPE1
* ESP32S2 1,2,BOTH,ALTER TYPE1, TYPE2
* ESP32C3 ALTER TYPE2
* ESP32S3 1,2,BOTH,ALTER TYPE2
* ESP32H3 1,2,BOTH,ALTER TYPE2
*/
#if defined(CONFIG_IDF_TARGET_ESP32) #if defined(CONFIG_IDF_TARGET_ESP32)
if (pin->adc_index != ADC_UNIT_1) { if (pin->adc_index != ADC_UNIT_1) {
/* /*
@ -112,6 +87,36 @@ static void start_dma(analogbufio_bufferedin_obj_t *self, adc_digi_convert_mode_
} }
#endif #endif
// C3 and S3 have errata related to ADC2 and continuous mode.
#if (defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)) && !defined(CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3)
if (pin->adc_index != ADC_UNIT_1) {
raise_ValueError_invalid_pin();
}
#endif
common_hal_mcu_pin_claim(pin);
}
static void start_dma(analogbufio_bufferedin_obj_t *self, adc_digi_convert_mode_t *convert_mode, adc_digi_output_format_t *output_format) {
const mcu_pin_obj_t *pin = self->pin;
uint32_t sample_rate = self->sample_rate;
*output_format = ADC_DIGI_OUTPUT_FORMAT_TYPE1;
if (pin->adc_index == ADC_UNIT_1) {
*convert_mode = ADC_CONV_SINGLE_UNIT_1;
} else {
*convert_mode = ADC_CONV_SINGLE_UNIT_2;
}
/*
* Chip version Conversion Mode Output Format Type
* ESP32 1 TYPE1
* ESP32S2 1,2,BOTH,ALTER TYPE1, TYPE2
* ESP32C3 ALTER TYPE2
* ESP32S3 1,2,BOTH,ALTER TYPE2
* ESP32H3 1,2,BOTH,ALTER TYPE2
*/
#if defined(CONFIG_IDF_TARGET_ESP32C3) #if defined(CONFIG_IDF_TARGET_ESP32C3)
/* ESP32C3 only supports alter mode */ /* ESP32C3 only supports alter mode */
*convert_mode = ADC_CONV_ALTER_UNIT; *convert_mode = ADC_CONV_ALTER_UNIT;
@ -121,34 +126,21 @@ static void start_dma(analogbufio_bufferedin_obj_t *self, adc_digi_convert_mode_
*output_format = ADC_DIGI_OUTPUT_FORMAT_TYPE2; *output_format = ADC_DIGI_OUTPUT_FORMAT_TYPE2;
#endif #endif
common_hal_mcu_pin_claim(pin); adc_continuous_handle_cfg_t adc_dma_config = {
if (pin->adc_index == ADC_UNIT_1) {
adc1_chan_mask = 1 << pin->adc_channel;
} else {
adc2_chan_mask = 1 << pin->adc_channel;
}
adc_digi_init_config_t adc_dma_config = {
.max_store_buf_size = DMA_BUFFER_SIZE, .max_store_buf_size = DMA_BUFFER_SIZE,
.conv_num_each_intr = NUM_SAMPLES_PER_INTERRUPT, .conv_frame_size = NUM_SAMPLES_PER_INTERRUPT * SOC_ADC_DIGI_DATA_BYTES_PER_CONV
.adc1_chan_mask = adc1_chan_mask,
.adc2_chan_mask = adc2_chan_mask,
}; };
#if defined(DEBUG_ANALOGBUFIO) #if defined(DEBUG_ANALOGBUFIO)
mp_printf(&mp_plat_print, "pin:%d, ADC channel:%d, ADC index:%d, adc1_chan_mask:0x%x, adc2_chan_mask:0x%x\n", pin->number, pin->adc_channel, pin->adc_index, adc1_chan_mask, adc2_chan_mask); mp_printf(&mp_plat_print, "pin:%d, ADC channel:%d, ADC index:%d, adc1_chan_mask:0x%x, adc2_chan_mask:0x%x\n", pin->number, pin->adc_channel, pin->adc_index, adc1_chan_mask, adc2_chan_mask);
#endif // DEBUG_ANALOGBUFIO #endif // DEBUG_ANALOGBUFIO
esp_err_t err = adc_digi_initialize(&adc_dma_config); esp_err_t err = adc_continuous_new_handle(&adc_dma_config, &self->handle);
if (ESP_OK != err) { if (ESP_OK != err) {
stop_dma(self); stop_dma(self);
common_hal_analogbufio_bufferedin_deinit(self); CHECK_ESP_RESULT(err);
mp_raise_ValueError_varg(translate("Unable to initialize ADC DMA controller, ErrorCode:%d"), err);
} }
adc_digi_configuration_t dig_cfg = { adc_continuous_config_t dig_cfg = {
.conv_limit_en = ADC_CONV_LIMIT_EN,
.conv_limit_num = 250,
.pattern_num = NUM_ADC_CHANNELS, .pattern_num = NUM_ADC_CHANNELS,
.sample_freq_hz = sample_rate, .sample_freq_hz = sample_rate,
.conv_mode = *convert_mode, .conv_mode = *convert_mode,
@ -174,25 +166,28 @@ static void start_dma(analogbufio_bufferedin_obj_t *self, adc_digi_convert_mode_
mp_printf(&mp_plat_print, "adc_pattern[0].channel:%d, adc_pattern[0].unit:%d, adc_pattern[0].atten:%d\n", adc_pattern[0].channel, adc_pattern[0].unit, adc_pattern[0].atten); mp_printf(&mp_plat_print, "adc_pattern[0].channel:%d, adc_pattern[0].unit:%d, adc_pattern[0].atten:%d\n", adc_pattern[0].channel, adc_pattern[0].unit, adc_pattern[0].atten);
#endif // DEBUG_ANALOGBUFIO #endif // DEBUG_ANALOGBUFIO
err = adc_digi_controller_configure(&dig_cfg); err = adc_continuous_config(self->handle, &dig_cfg);
if (ESP_OK != err) { if (ESP_OK != err) {
stop_dma(self); stop_dma(self);
common_hal_analogbufio_bufferedin_deinit(self); CHECK_ESP_RESULT(err);
mp_raise_ValueError_varg(translate("Unable to configure ADC DMA controller, ErrorCode:%d"), err);
} }
err = adc_digi_start(); err = adc_continuous_start(self->handle);
if (ESP_OK != err) { if (ESP_OK != err) {
stop_dma(self); stop_dma(self);
common_hal_analogbufio_bufferedin_deinit(self); CHECK_ESP_RESULT(err);
mp_raise_ValueError_varg(translate("Unable to start ADC DMA controller, ErrorCode:%d"), err);
} }
self->started = true;
} }
static void stop_dma(analogbufio_bufferedin_obj_t *self) { static void stop_dma(analogbufio_bufferedin_obj_t *self) {
adc_digi_stop(); if (self->started) {
adc_digi_deinitialize(); adc_continuous_stop(self->handle);
// Release ADC Pin self->started = false;
reset_pin_number(self->pin->number); }
if (self->handle != NULL) {
adc_continuous_deinit(self->handle);
self->handle = NULL;
}
} }
bool common_hal_analogbufio_bufferedin_deinited(analogbufio_bufferedin_obj_t *self) { bool common_hal_analogbufio_bufferedin_deinited(analogbufio_bufferedin_obj_t *self) {
@ -203,6 +198,8 @@ void common_hal_analogbufio_bufferedin_deinit(analogbufio_bufferedin_obj_t *self
if (common_hal_analogbufio_bufferedin_deinited(self)) { if (common_hal_analogbufio_bufferedin_deinited(self)) {
return; return;
} }
// Release ADC Pin
reset_pin_number(self->pin->number);
self->pin = NULL; self->pin = NULL;
} }
@ -242,7 +239,7 @@ static bool check_valid_data(const adc_digi_output_data_t *data, const mcu_pin_o
} }
uint32_t common_hal_analogbufio_bufferedin_readinto(analogbufio_bufferedin_obj_t *self, uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample) { uint32_t common_hal_analogbufio_bufferedin_readinto(analogbufio_bufferedin_obj_t *self, uint8_t *buffer, uint32_t len, uint8_t bytes_per_sample) {
uint8_t result[NUM_SAMPLES_PER_INTERRUPT] __attribute__ ((aligned(4))) = {0}; uint8_t result[NUM_SAMPLES_PER_INTERRUPT * SOC_ADC_DIGI_DATA_BYTES_PER_CONV] __attribute__ ((aligned(4))) = {0};
uint32_t captured_samples = 0; uint32_t captured_samples = 0;
uint32_t captured_bytes = 0; uint32_t captured_bytes = 0;
esp_err_t ret; esp_err_t ret;
@ -263,7 +260,7 @@ uint32_t common_hal_analogbufio_bufferedin_readinto(analogbufio_bufferedin_obj_t
while (captured_bytes < len) { while (captured_bytes < len) {
ret_num = 0; ret_num = 0;
ret = adc_digi_read_bytes(result, NUM_SAMPLES_PER_INTERRUPT, &ret_num, ADC_READ_TIMEOUT_MS); ret = adc_continuous_read(self->handle, result, NUM_SAMPLES_PER_INTERRUPT * SOC_ADC_DIGI_DATA_BYTES_PER_CONV, &ret_num, ADC_READ_TIMEOUT_MS);
if (ret == ESP_OK) { if (ret == ESP_OK) {
for (uint32_t i = 0; i < ret_num; i += ADC_RESULT_BYTE) { for (uint32_t i = 0; i < ret_num; i += ADC_RESULT_BYTE) {

View File

@ -32,11 +32,15 @@
#include "common-hal/microcontroller/Pin.h" #include "common-hal/microcontroller/Pin.h"
#include "py/obj.h" #include "py/obj.h"
#include "components/esp_adc/include/esp_adc/adc_continuous.h"
// This is the analogbufio object // This is the analogbufio object
typedef struct { typedef struct {
mp_obj_base_t base; mp_obj_base_t base;
const mcu_pin_obj_t *pin; const mcu_pin_obj_t *pin;
uint32_t sample_rate; uint32_t sample_rate;
adc_continuous_handle_t handle;
bool started;
} analogbufio_bufferedin_obj_t; } analogbufio_bufferedin_obj_t;
#endif // MICROPY_INCLUDED_ESP32_COMMON_HAL_ANALOGBUFIO_BUFFEREDIN_H #endif // MICROPY_INCLUDED_ESP32_COMMON_HAL_ANALOGBUFIO_BUFFEREDIN_H

View File

@ -96,7 +96,9 @@ void common_hal_analogio_analogout_set_value(analogio_analogout_obj_t *self,
void analogout_reset(void) { void analogout_reset(void) {
#ifdef SOC_DAC_SUPPORTED #ifdef SOC_DAC_SUPPORTED
for (uint8_t c = 0; c < SOC_DAC_CHAN_NUM; c++) { for (uint8_t c = 0; c < SOC_DAC_CHAN_NUM; c++) {
dac_oneshot_del_channel(_active_handles[c]); if (_active_handles[c] != NULL) {
dac_oneshot_del_channel(_active_handles[c]);
}
_active_handles[c] = NULL; _active_handles[c] = NULL;
} }
#endif #endif

View File

@ -44,7 +44,7 @@
#include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/Pin.h"
#include "supervisor/shared/translate/translate.h" #include "supervisor/shared/translate/translate.h"
#include "driver/i2s.h" #include "driver/i2s_std.h"
// Caller validates that pins are free. // Caller validates that pins are free.
void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self, void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self,
@ -53,15 +53,20 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self,
if (main_clock != NULL) { if (main_clock != NULL) {
mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_main_clock); mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_main_clock);
} }
port_i2s_allocate_init(&self->peripheral, left_justified); port_i2s_allocate_init(&self->i2s, left_justified);
i2s_pin_config_t i2s_pin_config = { i2s_std_config_t i2s_config = {
.bck_io_num = bit_clock->number, .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
.ws_io_num = word_select->number, .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.data_out_num = data->number, .gpio_cfg = {
.data_in_num = I2S_PIN_NO_CHANGE, .mclk = I2S_GPIO_UNUSED,
.bclk = bit_clock->number,
.ws = word_select->number,
.dout = data->number,
.din = I2S_GPIO_UNUSED,
}
}; };
CHECK_ESP_RESULT(i2s_set_pin(self->peripheral.instance, &i2s_pin_config)); CHECK_ESP_RESULT(i2s_channel_init_std_mode(self->i2s.handle, &i2s_config));
self->bit_clock = bit_clock; self->bit_clock = bit_clock;
self->word_select = word_select; self->word_select = word_select;
self->data = data; self->data = data;
@ -71,7 +76,7 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self,
} }
bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t *self) { bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t *self) {
return self->peripheral.instance == -1; return self->data == NULL;
} }
void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t *self) { void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t *self) {
@ -79,6 +84,10 @@ void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t *self) {
return; return;
} }
common_hal_audiobusio_i2sout_stop(self);
port_i2s_deinit(&self->i2s);
if (self->bit_clock) { if (self->bit_clock) {
reset_pin_number(self->bit_clock->number); reset_pin_number(self->bit_clock->number);
} }
@ -94,10 +103,6 @@ void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t *self) {
} }
self->data = NULL; self->data = NULL;
if (self->peripheral.instance >= 0) {
port_i2s_reset_instance(self->peripheral.instance);
}
self->peripheral.instance = -1;
} }
void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t *self, void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t *self,
@ -105,27 +110,27 @@ void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t *self,
if (common_hal_audiobusio_i2sout_get_playing(self)) { if (common_hal_audiobusio_i2sout_get_playing(self)) {
common_hal_audiobusio_i2sout_stop(self); common_hal_audiobusio_i2sout_stop(self);
} }
port_i2s_play(&self->peripheral, sample, loop); port_i2s_play(&self->i2s, sample, loop);
} }
void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t *self) { void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t *self) {
port_i2s_pause(&self->peripheral); port_i2s_pause(&self->i2s);
} }
void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t *self) { void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t *self) {
port_i2s_resume(&self->peripheral); port_i2s_resume(&self->i2s);
} }
bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t *self) { bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t *self) {
return port_i2s_paused(&self->peripheral); return port_i2s_paused(&self->i2s);
} }
void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t *self) { void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t *self) {
port_i2s_stop(&self->peripheral); port_i2s_stop(&self->i2s);
} }
bool common_hal_audiobusio_i2sout_get_playing(audiobusio_i2sout_obj_t *self) { bool common_hal_audiobusio_i2sout_get_playing(audiobusio_i2sout_obj_t *self) {
return port_i2s_playing(&self->peripheral); return port_i2s_playing(&self->i2s);
} }
#endif // CIRCUITPY_AUDIOBUSIO_I2SOUT #endif // CIRCUITPY_AUDIOBUSIO_I2SOUT

View File

@ -36,7 +36,7 @@
typedef struct { typedef struct {
mp_obj_base_t base; mp_obj_base_t base;
i2s_t peripheral; i2s_t i2s;
const mcu_pin_obj_t *bit_clock; const mcu_pin_obj_t *bit_clock;
const mcu_pin_obj_t *word_select; const mcu_pin_obj_t *word_select;
const mcu_pin_obj_t *data; const mcu_pin_obj_t *data;

View File

@ -35,70 +35,26 @@
#include "shared-module/audiocore/__init__.h" #include "shared-module/audiocore/__init__.h"
#define I2S_QUEUE_SIZE (3) #define CIRCUITPY_BUFFER_COUNT 3
#define CIRCUITPY_BUFFER_SIZE 1023
static i2s_t *i2s_instance[I2S_NUM_AUTO]; #define CIRCUITPY_OUTPUT_SLOTS 2
static QueueHandle_t i2s_queues[I2S_NUM_AUTO];
static TaskHandle_t i2s_tasks[I2S_NUM_AUTO];
void port_i2s_allocate_i2s0(void) {
if (!i2s_instance[0]) {
i2s_instance[0] = (void *)~(intptr_t)0;
return;
}
mp_raise_RuntimeError(translate("Peripheral in use"));
}
static int8_t port_i2s_allocate(void) {
#if defined(I2S_NUM_1)
if (!i2s_instance[1]) {
return 1;
}
#endif
if (!i2s_instance[0]) {
return 0;
}
mp_raise_RuntimeError(translate("Peripheral in use"));
}
void port_i2s_reset_instance(int i) {
assert(i >= 0 && i < I2S_NUM_AUTO);
if (i2s_tasks[i]) {
vTaskDelete(i2s_tasks[i]);
}
i2s_tasks[i] = NULL;
(void)i2s_driver_uninstall(i);
i2s_instance[i] = NULL;
}
void i2s_reset(void) {
for (int i = 0; i < I2S_NUM_AUTO; i++) {
port_i2s_reset_instance(i);
}
}
#define I2S_WRITE_DELAY pdMS_TO_TICKS(1)
static void i2s_fill_buffer(i2s_t *self) { static void i2s_fill_buffer(i2s_t *self) {
if (self->instance < 0 || self->instance >= I2S_NUM_AUTO) { if (self->next_buffer_size == 0) {
// Error, no new buffer queued.
return; return;
} }
#define STACK_BUFFER_SIZE (4096) int16_t *output_buffer = (int16_t *)self->next_buffer;
int16_t signed_samples[STACK_BUFFER_SIZE / sizeof(int16_t)]; size_t output_buffer_size = self->next_buffer_size;
const size_t bytes_per_output_frame = 4;
size_t bytes_per_input_frame = self->channel_count * self->bytes_per_sample;
if (!self->playing || self->paused || !self->sample || self->stopping) { if (!self->playing || self->paused || !self->sample || self->stopping) {
memset(signed_samples, 0, sizeof(signed_samples)); memset(output_buffer, 0, self->next_buffer_size);
self->next_buffer = NULL;
size_t bytes_written = 0; self->next_buffer_size = 0;
do {
CHECK_ESP_RESULT(i2s_write(self->instance, signed_samples, sizeof(signed_samples), &bytes_written, I2S_WRITE_DELAY));
} while (bytes_written != 0);
return; return;
} }
while (!self->stopping) { while (!self->stopping && output_buffer_size > 0) {
if (self->sample_data == self->sample_end) { if (self->sample_data == self->sample_end) {
uint32_t sample_buffer_length; uint32_t sample_buffer_length;
audioio_get_buffer_result_t get_buffer_result = audioio_get_buffer_result_t get_buffer_result =
@ -118,51 +74,45 @@ static void i2s_fill_buffer(i2s_t *self) {
break; break;
} }
} }
size_t bytes_written = 0; size_t sample_bytecount = self->sample_end - self->sample_data;
size_t 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 == 2) {
if (self->bytes_per_sample == 2) { if (self->bytes_per_sample == 2) {
CHECK_ESP_RESULT(i2s_write(self->instance, self->sample_data, bytecount, &bytes_written, I2S_WRITE_DELAY)); memcpy(output_buffer, self->sample_data, framecount * bytes_per_output_frame);
} else { } else {
CHECK_ESP_RESULT(i2s_write_expand(self->instance, self->sample_data, bytecount, 8, 16, &bytes_written, I2S_WRITE_DELAY)); audiosample_convert_s8s_s16s(output_buffer, ((int8_t *)self->sample_data), framecount);
} }
} else { } else {
const size_t bytes_per_output_frame = 4;
size_t bytes_per_input_frame = self->channel_count * self->bytes_per_sample;
size_t framecount = MIN(STACK_BUFFER_SIZE / bytes_per_output_frame, bytecount / bytes_per_input_frame);
if (self->samples_signed) { if (self->samples_signed) {
assert(self->channel_count == 1); assert(self->channel_count == 1);
if (self->bytes_per_sample == 1) { if (self->bytes_per_sample == 1) {
audiosample_convert_s8m_s16s(signed_samples, (int8_t *)(void *)self->sample_data, framecount); audiosample_convert_s8m_s16s(output_buffer, (int8_t *)(void *)self->sample_data, framecount);
} else { } else {
audiosample_convert_s16m_s16s(signed_samples, (int16_t *)(void *)self->sample_data, framecount); audiosample_convert_s16m_s16s(output_buffer, (int16_t *)(void *)self->sample_data, framecount);
} }
} else { } else {
if (self->channel_count == 1) { if (self->channel_count == 1) {
if (self->bytes_per_sample == 1) { if (self->bytes_per_sample == 1) {
audiosample_convert_u8m_s16s(signed_samples, (uint8_t *)(void *)self->sample_data, framecount); audiosample_convert_u8m_s16s(output_buffer, (uint8_t *)(void *)self->sample_data, framecount);
} else { } else {
audiosample_convert_u16m_s16s(signed_samples, (uint16_t *)(void *)self->sample_data, framecount); audiosample_convert_u16m_s16s(output_buffer, (uint16_t *)(void *)self->sample_data, framecount);
} }
} else { } else {
if (self->bytes_per_sample == 1) { if (self->bytes_per_sample == 1) {
audiosample_convert_u8s_s16s(signed_samples, (uint8_t *)(void *)self->sample_data, framecount); audiosample_convert_u8s_s16s(output_buffer, (uint8_t *)(void *)self->sample_data, framecount);
} else { } else {
audiosample_convert_u16s_s16s(signed_samples, (uint16_t *)(void *)self->sample_data, framecount); audiosample_convert_u16s_s16s(output_buffer, (uint16_t *)(void *)self->sample_data, framecount);
} }
} }
} }
size_t expanded_bytes_written = 0;
CHECK_ESP_RESULT(i2s_write(self->instance, signed_samples, bytes_per_output_frame * framecount, &expanded_bytes_written, I2S_WRITE_DELAY));
assert(expanded_bytes_written % 4 == 0);
bytes_written = expanded_bytes_written / bytes_per_output_frame * bytes_per_input_frame;
}
self->sample_data += bytes_written;
// We have filled the DMA buffer
if (!bytes_written) {
break;
} }
self->sample_data += framecount * bytes_per_input_frame;
output_buffer += framecount * CIRCUITPY_OUTPUT_SLOTS;
output_buffer_size -= framecount * bytes_per_output_frame;
} }
self->next_buffer = NULL;
self->next_buffer_size = 0;
} }
static void i2s_callback_fun(void *self_in) { static void i2s_callback_fun(void *self_in) {
@ -170,39 +120,41 @@ static void i2s_callback_fun(void *self_in) {
i2s_fill_buffer(self); i2s_fill_buffer(self);
} }
static void i2s_event_task(void *self_in) { static bool i2s_event_interrupt(i2s_chan_handle_t handle, i2s_event_data_t *event, void *self_in) {
i2s_t *self = self_in; i2s_t *self = self_in;
while (true) { self->underrun = self->underrun || self->next_buffer != NULL;
i2s_event_type_t event; self->next_buffer = *(int16_t **)event->data;
BaseType_t result = xQueueReceive(i2s_queues[self->instance], &event, portMAX_DELAY); self->next_buffer_size = event->size;
if (result && event == I2S_EVENT_TX_DONE) { background_callback_add(&self->callback, i2s_callback_fun, self_in);
background_callback_add(&self->callback, i2s_callback_fun, self_in); return false;
}
}
} }
void port_i2s_allocate_init(i2s_t *self, bool left_justified) { void port_i2s_allocate_init(i2s_t *self, bool left_justified) {
self->instance = port_i2s_allocate(); i2s_chan_config_t chan_config = {
.id = I2S_NUM_AUTO,
i2s_config_t i2s_config = { .role = I2S_ROLE_MASTER,
.mode = I2S_MODE_MASTER | I2S_MODE_TX, .dma_desc_num = CIRCUITPY_BUFFER_COUNT,
.sample_rate = 44100, .dma_frame_num = CIRCUITPY_BUFFER_SIZE, // in _frames_, so 1023 is 4092 bytes per dma buf which is the maximum
.bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = left_justified ? I2S_COMM_FORMAT_STAND_I2S : I2S_COMM_FORMAT_STAND_I2S,
.dma_buf_count = 3,
.dma_buf_len = 1024, // in _frames_, so 1024 is 4096 bytes per dma buf
.use_apll = false,
}; };
CHECK_ESP_RESULT(i2s_driver_install(self->instance, &i2s_config, I2S_QUEUE_SIZE, &i2s_queues[self->instance])); esp_err_t err = i2s_new_channel(&chan_config, &self->handle, NULL);
if (err == ESP_ERR_NOT_FOUND) {
if (!xTaskCreate(i2s_event_task, "I2S_task", 3 * configMINIMAL_STACK_SIZE, self, CONFIG_PTHREAD_TASK_PRIO_DEFAULT, &i2s_tasks[self->instance])) { mp_raise_RuntimeError(translate("Peripheral in use"));
mp_raise_OSError_msg(translate("xTaskCreate failed"));
} }
i2s_instance[self->instance] = self;
i2s_event_callbacks_t callbacks = {
.on_recv = NULL,
.on_recv_q_ovf = NULL,
.on_sent = i2s_event_interrupt,
.on_send_q_ovf = NULL,
};
i2s_channel_register_event_callback(self->handle, &callbacks, self);
} }
void port_i2s_deinit(i2s_t *self) {
port_i2s_stop(self);
i2s_del_channel(self->handle);
self->handle = NULL;
}
void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) { void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) {
self->sample = sample; self->sample = sample;
@ -216,9 +168,6 @@ void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) {
audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed, audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed,
&max_buffer_length, &spacing); &max_buffer_length, &spacing);
self->samples_signed = samples_signed; self->samples_signed = samples_signed;
self->playing = true;
self->paused = false;
self->stopping = false;
self->sample_data = self->sample_end = NULL; self->sample_data = self->sample_end = NULL;
// We always output stereo so output twice as many bits. // We always output stereo so output twice as many bits.
// uint16_t bits_per_sample_output = bits_per_sample * 2; // uint16_t bits_per_sample_output = bits_per_sample * 2;
@ -226,12 +175,32 @@ void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) {
audiosample_reset_buffer(self->sample, false, 0); audiosample_reset_buffer(self->sample, false, 0);
uint32_t sample_rate = audiosample_sample_rate(sample); uint32_t sample_rate = audiosample_sample_rate(sample);
if (sample_rate != self->i2s_config.sample_rate) { i2s_std_clk_config_t clk_config = I2S_STD_CLK_DEFAULT_CONFIG(sample_rate);
CHECK_ESP_RESULT(i2s_set_sample_rates(self->instance, audiosample_sample_rate(sample))); CHECK_ESP_RESULT(i2s_channel_reconfig_std_clock(self->handle, &clk_config));
self->i2s_config.sample_rate = sample_rate;
// preload the data
self->playing = true;
self->paused = false;
self->stopping = false;
// This will be slow but we can't rewind the underlying sample. So, we will
// preload one frame at a time and drop the last sample that can't fit.
// We cap ourselves at the max DMA set to prevent a sample drop if starting
// fresh.
uint32_t starting_frame;
size_t bytes_loaded = 4;
size_t preloaded = 0;
while (bytes_loaded > 0 && preloaded < CIRCUITPY_BUFFER_SIZE * CIRCUITPY_BUFFER_COUNT) {
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);
preloaded += 1;
} }
background_callback_add(&self->callback, i2s_callback_fun, self); // enable the channel
i2s_channel_enable(self->handle);
// The IDF will call us back when there is a free DMA buffer.
} }
bool port_i2s_playing(i2s_t *self) { bool port_i2s_playing(i2s_t *self) {
@ -243,8 +212,8 @@ bool port_i2s_paused(i2s_t *self) {
} }
void port_i2s_stop(i2s_t *self) { void port_i2s_stop(i2s_t *self) {
port_i2s_pause(self);
self->sample = NULL; self->sample = NULL;
self->paused = false;
self->playing = false; self->playing = false;
self->stopping = false; self->stopping = false;
} }
@ -252,13 +221,13 @@ void port_i2s_stop(i2s_t *self) {
void port_i2s_pause(i2s_t *self) { void port_i2s_pause(i2s_t *self) {
if (!self->paused) { if (!self->paused) {
self->paused = true; self->paused = true;
CHECK_ESP_RESULT(i2s_stop(self->instance)); CHECK_ESP_RESULT(i2s_channel_disable(self->handle));
} }
} }
void port_i2s_resume(i2s_t *self) { void port_i2s_resume(i2s_t *self) {
if (self->paused) { if (self->paused) {
self->paused = false; self->paused = false;
CHECK_ESP_RESULT(i2s_start(self->instance)); CHECK_ESP_RESULT(i2s_channel_enable(self->handle));
} }
} }

View File

@ -30,7 +30,7 @@
#include "supervisor/background_callback.h" #include "supervisor/background_callback.h"
#include "driver/i2s.h" #include "driver/i2s_std.h"
typedef struct { typedef struct {
mp_obj_t *sample; mp_obj_t *sample;
@ -42,23 +42,22 @@ typedef struct {
bool samples_signed; bool samples_signed;
int8_t bytes_per_sample; int8_t bytes_per_sample;
int8_t channel_count; int8_t channel_count;
int8_t instance;
uint16_t buffer_length; uint16_t buffer_length;
uint8_t *sample_data, *sample_end; uint8_t *sample_data, *sample_end;
i2s_config_t i2s_config; void *next_buffer;
size_t next_buffer_size;
i2s_chan_handle_t handle;
background_callback_t callback; background_callback_t callback;
bool underrun;
} i2s_t; } i2s_t;
void port_i2s_allocate_init(i2s_t *self, bool left_justified); void port_i2s_allocate_init(i2s_t *self, bool left_justified);
void port_i2s_reset_instance(int i); void port_i2s_deinit(i2s_t *self);
void i2s_reset(void);
void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop); void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop);
void port_i2s_stop(i2s_t *self); void port_i2s_stop(i2s_t *self);
bool port_i2s_playing(i2s_t *self); bool port_i2s_playing(i2s_t *self);
bool port_i2s_paused(i2s_t *self); bool port_i2s_paused(i2s_t *self);
void port_i2s_pause(i2s_t *self); void port_i2s_pause(i2s_t *self);
void port_i2s_resume(i2s_t *self); void port_i2s_resume(i2s_t *self);
// some uses (imagecapture) can only operate on i2s0 and need their own init code
void port_i2s_allocate_i2s0(void);

View File

@ -28,12 +28,6 @@ CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN=y
CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
# end of RMT Configuration # end of RMT Configuration
#
# I2S Configuration
#
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
# end of I2S Configuration
# end of Driver Configurations # end of Driver Configurations
# #

View File

@ -41,12 +41,6 @@ CONFIG_BT_NIMBLE_EXT_ADV=y
CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
# end of RMT Configuration # end of RMT Configuration
#
# I2S Configuration
#
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
# end of I2S Configuration
# end of Driver Configurations # end of Driver Configurations
# #

View File

@ -41,12 +41,6 @@ CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN=y
CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
# end of RMT Configuration # end of RMT Configuration
#
# I2S Configuration
#
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
# end of I2S Configuration
# end of Driver Configurations # end of Driver Configurations
# #

View File

@ -41,12 +41,6 @@ CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN=y
CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
# end of RMT Configuration # end of RMT Configuration
#
# I2S Configuration
#
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
# end of I2S Configuration
# end of Driver Configurations # end of Driver Configurations
# #

View File

@ -19,12 +19,6 @@ CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN=y
CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
# end of RMT Configuration # end of RMT Configuration
#
# I2S Configuration
#
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
# end of I2S Configuration
# end of Driver Configurations # end of Driver Configurations
# #

View File

@ -49,12 +49,6 @@ CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN=y
CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
# end of RMT Configuration # end of RMT Configuration
#
# I2S Configuration
#
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
# end of I2S Configuration
# end of Driver Configurations # end of Driver Configurations
# #

View File

@ -13,12 +13,6 @@ CONFIG_PARTITION_TABLE_CUSTOM=y
# #
# Driver Configurations # Driver Configurations
# #
#
# Legacy ADC Configuration
#
CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y
# end of Legacy ADC Configuration
# #
# GPTimer Configuration # GPTimer Configuration
# #

@ -1 +0,0 @@
Subproject commit d07eb2733ca5d3379578f3952de04b4a4d188f36

View File

@ -36,6 +36,7 @@ CIRCUITPY_I2CTARGET ?= 1
CIRCUITPY_IMAGECAPTURE = 0 CIRCUITPY_IMAGECAPTURE = 0
CIRCUITPY_MEMORYMAP ?= 1 CIRCUITPY_MEMORYMAP ?= 1
CIRCUITPY_NVM ?= 1 CIRCUITPY_NVM ?= 1
CIRCUITPY_PARALLELDISPLAY ?= 0 # Turn off because it uses the old I2S driver which conflicts with the new ADC driver.
CIRCUITPY_PS2IO ?= 1 CIRCUITPY_PS2IO ?= 1
# Disabled temporarily # Disabled temporarily
CIRCUITPY_RGBMATRIX ?= 0 CIRCUITPY_RGBMATRIX ?= 0

View File

@ -49,7 +49,7 @@ extern const mp_obj_type_t mcu_pin_type;
#define NO_PIN (GPIO_NUM_NC) #define NO_PIN (GPIO_NUM_NC)
#define NO_ADC 0 #define NO_ADC SOC_ADC_PERIPH_NUM
#define NO_ADC_CHANNEL SOC_ADC_MAX_CHANNEL_NUM #define NO_ADC_CHANNEL SOC_ADC_MAX_CHANNEL_NUM
#define NO_TOUCH_CHANNEL TOUCH_PAD_MAX #define NO_TOUCH_CHANNEL TOUCH_PAD_MAX

View File

@ -74,10 +74,6 @@
#include "peripherals/touch.h" #include "peripherals/touch.h"
#endif #endif
#if CIRCUITPY_AUDIOBUSIO
#include "common-hal/audiobusio/__init__.h"
#endif
#if CIRCUITPY_BLEIO #if CIRCUITPY_BLEIO
#include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/_bleio/__init__.h"
#endif #endif
@ -388,10 +384,6 @@ void reset_port(void) {
analogout_reset(); analogout_reset();
#endif #endif
#if CIRCUITPY_AUDIOBUSIO
i2s_reset();
#endif
#if CIRCUITPY_BUSIO #if CIRCUITPY_BUSIO
i2c_reset(); i2c_reset();
spi_reset(); spi_reset();