From d024df6b065c125f07c929371c94a48b058eff22 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 12 Nov 2020 14:04:50 -0600 Subject: esp32s2: Use better optimizer flags in debug builds (note that the before and after files both lack trailing newlines; this is how the esp-idf do) OPTIMIZATION_DEFAULT is -Og, which enables optimizations that do not interfere with the debugger: ``` elseif(CONFIG_COMPILER_OPTIMIZATION_DEFAULT) list(APPEND compile_options "-Og") ``` --- ports/esp32s2/esp-idf-config/sdkconfig-debug.defaults | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ports/esp32s2/esp-idf-config/sdkconfig-debug.defaults b/ports/esp32s2/esp-idf-config/sdkconfig-debug.defaults index f45f6ae9f..b108532aa 100644 --- a/ports/esp32s2/esp-idf-config/sdkconfig-debug.defaults +++ b/ports/esp32s2/esp-idf-config/sdkconfig-debug.defaults @@ -10,4 +10,7 @@ CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y # CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT is not set # CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set -# CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set \ No newline at end of file +# CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y \ No newline at end of file -- cgit v1.2.3 From 05ba1431c387db97dbd66c07d3494a4dae7fc570 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 20 Nov 2020 14:53:13 -0600 Subject: esp32s2: espidf: Add IDFError this is an exception class for "generic" IDF errors that don't have their own exception type. --- ports/esp32s2/bindings/espidf/__init__.c | 27 +++++++++++++++++++++++---- ports/esp32s2/bindings/espidf/__init__.h | 1 + 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/ports/esp32s2/bindings/espidf/__init__.c b/ports/esp32s2/bindings/espidf/__init__.c index 554bfaa39..5d3950f04 100644 --- a/ports/esp32s2/bindings/espidf/__init__.c +++ b/ports/esp32s2/bindings/espidf/__init__.c @@ -65,12 +65,12 @@ STATIC mp_obj_t espidf_heap_caps_get_largest_free_block(void) { } MP_DEFINE_CONST_FUN_OBJ_0(espidf_heap_caps_get_largest_free_block_obj, espidf_heap_caps_get_largest_free_block); -//| class MemoryError(MemoryError): -//| """Raised when an ESP IDF memory allocation fails.""" +//| class IDFError(OSError): +//| """Raised for certain generic ESP IDF errors.""" //| ... //| -NORETURN void mp_raise_espidf_MemoryError(void) { - nlr_raise(mp_obj_new_exception(&mp_type_espidf_MemoryError)); +NORETURN void mp_raise_espidf_IDFError(void) { + nlr_raise(mp_obj_new_exception(&mp_type_espidf_IDFError)); } void espidf_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { @@ -83,6 +83,24 @@ void espidf_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kin mp_obj_exception_print(print, o_in, kind); } +const mp_obj_type_t mp_type_espidf_IDFError = { + { &mp_type_type }, + .name = MP_QSTR_IDFError, + .print = espidf_exception_print, + .make_new = mp_obj_exception_make_new, + .attr = mp_obj_exception_attr, + .parent = &mp_type_OSError, +}; + + +//| class MemoryError(MemoryError): +//| """Raised when an ESP IDF memory allocation fails.""" +//| ... +//| +NORETURN void mp_raise_espidf_MemoryError(void) { + nlr_raise(mp_obj_new_exception(&mp_type_espidf_MemoryError)); +} + const mp_obj_type_t mp_type_espidf_MemoryError = { { &mp_type_type }, .name = MP_QSTR_MemoryError, @@ -99,6 +117,7 @@ STATIC const mp_rom_map_elem_t espidf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_heap_caps_get_free_size), MP_ROM_PTR(&espidf_heap_caps_get_free_size_obj)}, { MP_ROM_QSTR(MP_QSTR_heap_caps_get_largest_free_block), MP_ROM_PTR(&espidf_heap_caps_get_largest_free_block_obj)}, + { MP_ROM_QSTR(MP_QSTR_IDFError), MP_ROM_PTR(&mp_type_espidf_IDFError) }, { MP_ROM_QSTR(MP_QSTR_MemoryError), MP_ROM_PTR(&mp_type_espidf_MemoryError) }, }; diff --git a/ports/esp32s2/bindings/espidf/__init__.h b/ports/esp32s2/bindings/espidf/__init__.h index 356c1c814..e35ec24b8 100644 --- a/ports/esp32s2/bindings/espidf/__init__.h +++ b/ports/esp32s2/bindings/espidf/__init__.h @@ -27,6 +27,7 @@ #ifndef MICROPY_INCLUDED_ESP32S2_BINDINGS_ESPIDF___INIT___H #define MICROPY_INCLUDED_ESP32S2_BINDINGS_ESPIDF___INIT___H +extern const mp_obj_type_t mp_type_espidf_IDFError; extern const mp_obj_type_t mp_type_espidf_MemoryError; NORETURN void mp_raise_espidf_MemoryError(void); -- cgit v1.2.3 From b40a579d180103d9f8a67390495d02e47554e7d7 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 30 Oct 2020 10:05:16 -0500 Subject: esp32s2: espidf: Consistent error checking of esp-idf calls By surrounding most ESP-IDF API calls with this new macro, their failure will result in a CircuitPython exception. --- ports/esp32s2/bindings/espidf/__init__.c | 61 +++++++++++++++++++++ ports/esp32s2/bindings/espidf/__init__.h | 7 +++ ports/esp32s2/esp_error.c | 91 ++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 ports/esp32s2/esp_error.c diff --git a/ports/esp32s2/bindings/espidf/__init__.c b/ports/esp32s2/bindings/espidf/__init__.c index 5d3950f04..708c30c39 100644 --- a/ports/esp32s2/bindings/espidf/__init__.c +++ b/ports/esp32s2/bindings/espidf/__init__.c @@ -127,3 +127,64 @@ const mp_obj_module_t espidf_module = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&espidf_module_globals, }; + +void raise_esp_error(esp_err_t err) { + const compressed_string_t *msg = NULL; + const mp_obj_type_t * exception_type = &mp_type_espidf_IDFError; + switch(err) { + case ESP_FAIL: + msg = translate("Generic Failure"); + break; + case ESP_ERR_NO_MEM: + exception_type = &mp_type_espidf_MemoryError; + msg = translate("Out of memory"); + break; + case ESP_ERR_INVALID_ARG: + msg = translate("Invalid argument"); + break; + case ESP_ERR_INVALID_STATE: + msg = translate("Invalid state"); + break; + case ESP_ERR_INVALID_SIZE: + msg = translate("Invalid size"); + break; + case ESP_ERR_NOT_FOUND: + msg = translate("Requested resource not found"); + break; + case ESP_ERR_NOT_SUPPORTED: + msg = translate("Operation or feature not supported"); + break; + case ESP_ERR_TIMEOUT: + msg = translate("Operation timed out"); + break; + case ESP_ERR_INVALID_RESPONSE: + msg = translate("Received response was invalid"); + break; + case ESP_ERR_INVALID_CRC: + msg = translate("CRC or checksum was invalid"); + break; + case ESP_ERR_INVALID_VERSION: + msg = translate("Version was invalid"); + break; + case ESP_ERR_INVALID_MAC: + msg = translate("MAC address was invalid"); + break; + } + if (msg) { + mp_raise_msg(exception_type, msg); + } + + const char *group = "ESP-IDF"; + + // tests must be in descending order + MP_STATIC_ASSERT( ESP_ERR_FLASH_BASE > ESP_ERR_MESH_BASE ); + MP_STATIC_ASSERT( ESP_ERR_MESH_BASE > ESP_ERR_WIFI_BASE ); + if(err >= ESP_ERR_FLASH_BASE) { + group = "Flash"; + } else if (err >= ESP_ERR_MESH_BASE) { + group = "Mesh"; + } else if (err >= ESP_ERR_WIFI_BASE) { + group = "WiFi"; + } + mp_raise_msg_varg(exception_type, translate("%s error 0x%x"), group, err); +} diff --git a/ports/esp32s2/bindings/espidf/__init__.h b/ports/esp32s2/bindings/espidf/__init__.h index e35ec24b8..5ff49970d 100644 --- a/ports/esp32s2/bindings/espidf/__init__.h +++ b/ports/esp32s2/bindings/espidf/__init__.h @@ -27,9 +27,16 @@ #ifndef MICROPY_INCLUDED_ESP32S2_BINDINGS_ESPIDF___INIT___H #define MICROPY_INCLUDED_ESP32S2_BINDINGS_ESPIDF___INIT___H +#include "esp_err.h" +#include "py/mpconfig.h" +#include "py/obj.h" + extern const mp_obj_type_t mp_type_espidf_IDFError; extern const mp_obj_type_t mp_type_espidf_MemoryError; NORETURN void mp_raise_espidf_MemoryError(void); +void raise_esp_error(esp_err_t err) NORETURN; +#define ESP_CALL_RAISE(x) do { int res = (x); if(res != ESP_OK) raise_esp_error(res); } while(0) + #endif // MICROPY_INCLUDED_ESP32S2_BINDINGS_ESPIDF___INIT___H diff --git a/ports/esp32s2/esp_error.c b/ports/esp32s2/esp_error.c new file mode 100644 index 000000000..57bc51528 --- /dev/null +++ b/ports/esp32s2/esp_error.c @@ -0,0 +1,91 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "esp_error.h" +#include "py/runtime.h" + +#include "bindings/espidf/__init__.h" + +void raise_esp_error(esp_err_t err) { + const compressed_string_t *msg = NULL; + const mp_obj_type_t * exception_type = &mp_type_espidf_IDFError; + switch(err) { + case ESP_FAIL: + msg = translate("Generic Failure"); + break; + case ESP_ERR_NO_MEM: + exception_type = &mp_type_espidf_MemoryError; + msg = translate("Out of memory"); + break; + case ESP_ERR_INVALID_ARG: + msg = translate("Invalid argument"); + break; + case ESP_ERR_INVALID_STATE: + msg = translate("Invalid state"); + break; + case ESP_ERR_INVALID_SIZE: + msg = translate("Invalid size"); + break; + case ESP_ERR_NOT_FOUND: + msg = translate("Requested resource not found"); + break; + case ESP_ERR_NOT_SUPPORTED: + msg = translate("Operation or feature not supported"); + break; + case ESP_ERR_TIMEOUT: + msg = translate("Operation timed out"); + break; + case ESP_ERR_INVALID_RESPONSE: + msg = translate("Received response was invalid"); + break; + case ESP_ERR_INVALID_CRC: + msg = translate("CRC or checksum was invalid"); + break; + case ESP_ERR_INVALID_VERSION: + msg = translate("Version was invalid"); + break; + case ESP_ERR_INVALID_MAC: + msg = translate("MAC address was invalid"); + break; + } + if (msg) { + mp_raise_msg(exception_type, msg); + } + + const char *group = "ESP-IDF"; + + // tests must be in descending order + MP_STATIC_ASSERT( ESP_ERR_FLASH_BASE > ESP_ERR_MESH_BASE ); + MP_STATIC_ASSERT( ESP_ERR_MESH_BASE > ESP_ERR_WIFI_BASE ); + if(err >= ESP_ERR_FLASH_BASE) { + group = "Flash"; + } else if (err >= ESP_ERR_MESH_BASE) { + group = "Mesh"; + } else if (err >= ESP_ERR_WIFI_BASE) { + group = "WiFi"; + } + mp_raise_msg_varg(exception_type, translate("%s error 0x%x"), group, err); +} -- cgit v1.2.3 From 537a4daabd79407a8c2c5cd4adead6019406d992 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 28 Oct 2020 10:20:36 -0500 Subject: mark script as executable --- tools/analyze_mpy.py | 1 + 1 file changed, 1 insertion(+) mode change 100644 => 100755 tools/analyze_mpy.py diff --git a/tools/analyze_mpy.py b/tools/analyze_mpy.py old mode 100644 new mode 100755 index a2f541d0f..99c06f62a --- a/tools/analyze_mpy.py +++ b/tools/analyze_mpy.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 # SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors) # # SPDX-License-Identifier: MIT -- cgit v1.2.3 From 2cd377f1a7a91a46d154f4da9dbf50a747da9b41 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 28 Oct 2020 13:27:48 -0500 Subject: audiobusio: Make PDMIn optional --- py/circuitpy_mpconfig.mk | 4 ++++ shared-bindings/audiobusio/PDMIn.c | 8 ++++++++ shared-bindings/audiobusio/PDMIn.h | 2 ++ 3 files changed, 14 insertions(+) diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index e588c41e0..9a07f62a3 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -55,6 +55,10 @@ CFLAGS += -DCIRCUITPY_AUDIOBUSIO=$(CIRCUITPY_AUDIOBUSIO) CIRCUITPY_AUDIOBUSIO_I2SOUT ?= $(CIRCUITPY_AUDIOBUSIO) CFLAGS += -DCIRCUITPY_AUDIOBUSIO_I2SOUT=$(CIRCUITPY_AUDIOBUSIO_I2SOUT) +# Likewise, some boards have I2SOut but do not implement PDMIn. +CIRCUITPY_AUDIOBUSIO_PDMIN ?= $(CIRCUITPY_AUDIOBUSIO) +CFLAGS += -DCIRCUITPY_AUDIOBUSIO_PDMIN=$(CIRCUITPY_AUDIOBUSIO_PDMIN) + CIRCUITPY_AUDIOIO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_AUDIOIO=$(CIRCUITPY_AUDIOIO) diff --git a/shared-bindings/audiobusio/PDMIn.c b/shared-bindings/audiobusio/PDMIn.c index 6c5fa7939..a3cc8b07e 100644 --- a/shared-bindings/audiobusio/PDMIn.c +++ b/shared-bindings/audiobusio/PDMIn.c @@ -84,6 +84,9 @@ //| ... //| STATIC mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +#if !CIRCUITPY_AUDIOBUSIO_PDMIN + mp_raise_NotImplementedError(translate("PDMIn not available")); +#else enum { ARG_clock_pin, ARG_data_pin, ARG_sample_rate, ARG_bit_depth, ARG_mono, ARG_oversample, ARG_startup_delay }; static const mp_arg_t allowed_args[] = { { MP_QSTR_clock_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -132,8 +135,10 @@ STATIC mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_ar mp_hal_delay_ms(startup_delay * 1000); return MP_OBJ_FROM_PTR(self); +#endif } +#if CIRCUITPY_AUDIOBUSIO_PDMIN //| def deinit(self) -> None: //| """Deinitialises the PDMIn and releases any hardware resources for reuse.""" //| ... @@ -237,10 +242,13 @@ STATIC const mp_rom_map_elem_t audiobusio_pdmin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&audiobusio_pdmin_sample_rate_obj) } }; STATIC MP_DEFINE_CONST_DICT(audiobusio_pdmin_locals_dict, audiobusio_pdmin_locals_dict_table); +#endif const mp_obj_type_t audiobusio_pdmin_type = { { &mp_type_type }, .name = MP_QSTR_PDMIn, .make_new = audiobusio_pdmin_make_new, +#if CIRCUITPY_AUDIOBUSIO_PDMIN .locals_dict = (mp_obj_dict_t*)&audiobusio_pdmin_locals_dict, +#endif }; diff --git a/shared-bindings/audiobusio/PDMIn.h b/shared-bindings/audiobusio/PDMIn.h index c2a8bab2f..e89fc7e23 100644 --- a/shared-bindings/audiobusio/PDMIn.h +++ b/shared-bindings/audiobusio/PDMIn.h @@ -33,6 +33,7 @@ extern const mp_obj_type_t audiobusio_pdmin_type; +#if CIRCUITPY_AUDIOBUSIO_PDMIN void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t* self, const mcu_pin_obj_t* clock_pin, const mcu_pin_obj_t* data_pin, uint32_t sample_rate, uint8_t bit_depth, bool mono, uint8_t oversample); @@ -43,5 +44,6 @@ uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* se uint8_t common_hal_audiobusio_pdmin_get_bit_depth(audiobusio_pdmin_obj_t* self); uint32_t common_hal_audiobusio_pdmin_get_sample_rate(audiobusio_pdmin_obj_t* self); // TODO(tannewt): Add record to file +#endif #endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOBUSIO_AUDIOOUT_H -- cgit v1.2.3 From a7542598a0ce1fbd7722312dd863b1d40b0c2837 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 30 Oct 2020 10:05:16 -0500 Subject: esp32s2: add I2SOut --- ports/esp32s2/Makefile | 1 + ports/esp32s2/common-hal/audiobusio/I2SOut.c | 126 ++++++++++++++ ports/esp32s2/common-hal/audiobusio/I2SOut.h | 45 +++++ ports/esp32s2/common-hal/audiobusio/PDMIn.c | 0 ports/esp32s2/common-hal/audiobusio/PDMIn.h | 0 ports/esp32s2/common-hal/audiobusio/__init__.c | 0 ports/esp32s2/common-hal/audiobusio/__init__.h | 0 ports/esp32s2/i2s_common.c | 231 +++++++++++++++++++++++++ ports/esp32s2/i2s_common.h | 61 +++++++ ports/esp32s2/mpconfigport.mk | 6 +- ports/esp32s2/supervisor/port.c | 8 + shared-module/audiocore/__init__.c | 60 +++++++ shared-module/audiocore/__init__.h | 8 + 13 files changed, 545 insertions(+), 1 deletion(-) create mode 100644 ports/esp32s2/common-hal/audiobusio/I2SOut.c create mode 100644 ports/esp32s2/common-hal/audiobusio/I2SOut.h create mode 100644 ports/esp32s2/common-hal/audiobusio/PDMIn.c create mode 100644 ports/esp32s2/common-hal/audiobusio/PDMIn.h create mode 100644 ports/esp32s2/common-hal/audiobusio/__init__.c create mode 100644 ports/esp32s2/common-hal/audiobusio/__init__.h create mode 100644 ports/esp32s2/i2s_common.c create mode 100644 ports/esp32s2/i2s_common.h diff --git a/ports/esp32s2/Makefile b/ports/esp32s2/Makefile index e08b8a792..c1e20d17a 100644 --- a/ports/esp32s2/Makefile +++ b/ports/esp32s2/Makefile @@ -175,6 +175,7 @@ CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32S2 -DCFG_TUSB_OS=OPT_OS_FREERTOS -DCFG_TUD SRC_C += \ background.c \ fatfs_port.c \ + i2s_common.c \ mphalport.c \ bindings/espidf/__init__.c \ boards/$(BOARD)/board.c \ diff --git a/ports/esp32s2/common-hal/audiobusio/I2SOut.c b/ports/esp32s2/common-hal/audiobusio/I2SOut.c new file mode 100644 index 000000000..be5b04fa9 --- /dev/null +++ b/ports/esp32s2/common-hal/audiobusio/I2SOut.c @@ -0,0 +1,126 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "mpconfigport.h" + +#include "bindings/espidf/__init__.h" + +// Some boards don't implement I2SOut, so suppress any routines from here. +#if CIRCUITPY_AUDIOBUSIO_I2SOUT + +#include "extmod/vfs_fat.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "common-hal/audiobusio/I2SOut.h" +#include "shared-bindings/audiobusio/I2SOut.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "supervisor/shared/translate.h" + +#include "driver/i2s.h" + +// 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, bool left_justified) { + + port_i2s_allocate_init(&self->peripheral, left_justified); + + i2s_pin_config_t i2s_pin_config = { + .bck_io_num = bit_clock->number, + .ws_io_num = word_select->number, + .data_out_num = data->number, + .data_in_num = I2S_PIN_NO_CHANGE, + }; + ESP_CALL_RAISE(i2s_set_pin(self->peripheral.instance, &i2s_pin_config)); + self->bit_clock = bit_clock; + self->word_select = word_select; + self->data = data; +} + +bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t* self) { + return self->peripheral.instance == -1; +} + +void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t* self) { + if (common_hal_audiobusio_i2sout_deinited(self)) { + return; + } + + if (self->bit_clock) { + reset_pin_number(self->bit_clock->number); + } + self->bit_clock = NULL; + + if (self->word_select) { + reset_pin_number(self->word_select->number); + } + self->word_select = NULL; + + if (self->data) { + reset_pin_number(self->data->number); + } + 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, + mp_obj_t sample, bool loop) { + if (common_hal_audiobusio_i2sout_get_playing(self)) { + common_hal_audiobusio_i2sout_stop(self); + } + port_i2s_play(&self->peripheral, sample, loop); +} + +void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t* self) { + port_i2s_pause(&self->peripheral); +} + +void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t* self) { + port_i2s_resume(&self->peripheral); +} + +bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t* self) { + return port_i2s_paused(&self->peripheral); +} + +void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t* self) { + port_i2s_stop(&self->peripheral); +} + +bool common_hal_audiobusio_i2sout_get_playing(audiobusio_i2sout_obj_t* self) { + return port_i2s_playing(&self->peripheral); +} + +#endif // CIRCUITPY_AUDIOBUSIO_I2SOUT diff --git a/ports/esp32s2/common-hal/audiobusio/I2SOut.h b/ports/esp32s2/common-hal/audiobusio/I2SOut.h new file mode 100644 index 000000000..90266342c --- /dev/null +++ b/ports/esp32s2/common-hal/audiobusio/I2SOut.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "supervisor/background_callback.h" +#include "common-hal/microcontroller/Pin.h" + +#include "i2s_common.h" + +// Some boards don't implement I2SOut, so suppress any routines from here. +#if CIRCUITPY_AUDIOBUSIO_I2SOUT + +typedef struct { + mp_obj_base_t base; + i2s_t peripheral; + const mcu_pin_obj_t *bit_clock; + const mcu_pin_obj_t *word_select; + const mcu_pin_obj_t *data; +} audiobusio_i2sout_obj_t; + +#endif diff --git a/ports/esp32s2/common-hal/audiobusio/PDMIn.c b/ports/esp32s2/common-hal/audiobusio/PDMIn.c new file mode 100644 index 000000000..e69de29bb diff --git a/ports/esp32s2/common-hal/audiobusio/PDMIn.h b/ports/esp32s2/common-hal/audiobusio/PDMIn.h new file mode 100644 index 000000000..e69de29bb diff --git a/ports/esp32s2/common-hal/audiobusio/__init__.c b/ports/esp32s2/common-hal/audiobusio/__init__.c new file mode 100644 index 000000000..e69de29bb diff --git a/ports/esp32s2/common-hal/audiobusio/__init__.h b/ports/esp32s2/common-hal/audiobusio/__init__.h new file mode 100644 index 000000000..e69de29bb diff --git a/ports/esp32s2/i2s_common.c b/ports/esp32s2/i2s_common.c new file mode 100644 index 000000000..1933665a3 --- /dev/null +++ b/ports/esp32s2/i2s_common.c @@ -0,0 +1,231 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" + +#include "i2s_common.h" +#include "bindings/espidf/__init__.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "shared-module/audiocore/__init__.h" + +#define I2S_QUEUE_SIZE (3) + +static i2s_t *i2s_instance[I2S_NUM_MAX]; +static QueueHandle_t i2s_queues[I2S_NUM_MAX]; +static TaskHandle_t i2s_tasks[I2S_NUM_MAX]; + +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_MAX); + 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_MAX; i++) { + port_i2s_reset_instance(i); + } +} + +static void i2s_fill_buffer(i2s_t *self) { + if (self->instance < 0 || self->instance >= I2S_NUM_MAX) { + return; + } + if (self->paused || !self->sample) { + i2s_zero_dma_buffer(self->instance); + return; + } + while (!self->stopping) { + if (self->sample_data == self->sample_end) { + uint32_t sample_buffer_length; + audioio_get_buffer_result_t get_buffer_result = + audiosample_get_buffer(self->sample, false, 0, + &self->sample_data, &sample_buffer_length); + self->sample_end = self->sample_data + sample_buffer_length; + if (get_buffer_result == GET_BUFFER_DONE) { + if (self->loop) { + audiosample_reset_buffer(self->sample, false, 0); + } else { + self->stopping = true; + break; + } + } + if (get_buffer_result == GET_BUFFER_ERROR || sample_buffer_length == 0) { + self->stopping = true; + break; + } + } + size_t bytes_written = 0; + size_t bytecount = self->sample_end - self->sample_data; + if (self->samples_signed && self->channel_count == 2) { + if (self->bytes_per_sample == 2) { + ESP_CALL_RAISE(i2s_write(self->instance, self->sample_data, bytecount, &bytes_written, 0)); + } else { + ESP_CALL_RAISE(i2s_write_expand(self->instance, self->sample_data, bytecount, 8, 16, &bytes_written, 0)); + } + } else { +#define STACK_BUFFER_SIZE (64) + size_t bytes_per_frame = self->channel_count * self->bytes_per_sample; + size_t framecount = MIN(STACK_BUFFER_SIZE / bytes_per_frame, bytecount); + int16_t signed_samples[STACK_BUFFER_SIZE / sizeof(int16_t)]; + if (self->samples_signed) { + assert(self->channel_count == 1); + if (self->bytes_per_sample == 1) { + audiosample_convert_s8m_s16s(signed_samples, (int8_t*)(void*)self->sample_data, framecount); + } else { + audiosample_convert_s16m_s16s(signed_samples, (int16_t*)(void*)self->sample_data, framecount); + } + } else { + if (self->channel_count == 1) { + if (self->bytes_per_sample == 1) { + audiosample_convert_u8m_s16s(signed_samples, (uint8_t*)(void*)self->sample_data, framecount); + } else { + audiosample_convert_u16m_s16s(signed_samples, (uint16_t*)(void*)self->sample_data, framecount); + } + } else { + if (self->bytes_per_sample == 1) { + audiosample_convert_u8s_s16s(signed_samples, (uint8_t*)(void*)self->sample_data, framecount); + } else { + audiosample_convert_u16s_s16s(signed_samples, (uint16_t*)(void*)self->sample_data, framecount); + } + } + } + size_t expanded_bytes_written = 0; + ESP_CALL_RAISE(i2s_write(self->instance, signed_samples, 4*framecount, &expanded_bytes_written, 0)); + assert(expanded_bytes_written % 4 == 0); + bytes_written = expanded_bytes_written / 4 * bytes_per_frame; + } + self->sample_data += bytes_written; + // We have filled the DMA buffer + if (!bytes_written) { + break; + } + } +} + +static void i2s_callback_fun(void *self_in) { + i2s_t *self = self_in; + i2s_fill_buffer(self); +} + +static void i2s_event_task(void *self_in) { + i2s_t *self = self_in; + while(true) { + i2s_event_type_t event; + BaseType_t result = xQueueReceive(i2s_queues[self->instance], &event, portMAX_DELAY); + if (result && event == I2S_EVENT_TX_DONE) { + background_callback_add(&self->callback, i2s_callback_fun, self_in); + } + } +} + +void port_i2s_allocate_init(i2s_t *self, bool left_justified) { + self->instance = port_i2s_allocate(); + + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, + .sample_rate = 44100, + .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 = 2, + .dma_buf_len = 128, // in _frames_, so 128 is 512 bytes per dma buf + .use_apll = false, + }; + ESP_CALL_RAISE(i2s_driver_install(self->instance, &i2s_config, I2S_QUEUE_SIZE, &i2s_queues[self->instance])); + + if (!xTaskCreate(i2s_event_task, "I2S_task", 3 * configMINIMAL_STACK_SIZE, self, CONFIG_PTHREAD_TASK_PRIO_DEFAULT, &i2s_tasks[self->instance])) { + mp_raise_OSError_msg(translate("xTaskCreate failed")); + } + i2s_instance[self->instance] = self; + +} + + +void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) { + self->sample = sample; + self->bytes_per_sample = audiosample_bits_per_sample(sample) / 8; + self->channel_count = audiosample_channel_count(sample); + bool single_buffer; + bool samples_signed; + uint32_t max_buffer_length; + uint8_t spacing; + audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed, + &max_buffer_length, &spacing); + self->samples_signed = samples_signed; + self->loop = loop; + self->playing = true; + self->paused = false; + self->stopping = false; + // We always output stereo so output twice as many bits. + // uint16_t bits_per_sample_output = bits_per_sample * 2; + ESP_CALL_RAISE(i2s_set_sample_rates(self->instance, audiosample_sample_rate(sample))); + i2s_fill_buffer(self); +} + +bool port_i2s_playing(i2s_t *self) { + return self->playing && !self->stopping; +} + +bool port_i2s_paused(i2s_t *self) { + return self->paused; +} + +void port_i2s_stop(i2s_t *self) { + self->sample = NULL; + self->paused = false; + self->playing = false; + self->stopping = false; +} + +void port_i2s_pause(i2s_t *self) { + if (!self->paused) { + self->paused = true; + ESP_CALL_RAISE(i2s_stop(self->instance)); + } +} + +void port_i2s_resume(i2s_t *self) { + if (self->paused) { + self->paused = false; + ESP_CALL_RAISE(i2s_start(self->instance)); + } +} diff --git a/ports/esp32s2/i2s_common.h b/ports/esp32s2/i2s_common.h new file mode 100644 index 000000000..7709735da --- /dev/null +++ b/ports/esp32s2/i2s_common.h @@ -0,0 +1,61 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "py/obj.h" + +#include "supervisor/background_callback.h" + +#include "driver/i2s.h" + +typedef struct { + mp_obj_t *sample; + bool left_justified; + bool loop; + bool paused; + bool playing; + bool stopping; + bool samples_signed; + int8_t bytes_per_sample; + int8_t channel_count; + int8_t instance; + uint16_t buffer_length; + uint8_t *sample_data, *sample_end; + i2s_config_t i2s_config; + background_callback_t callback; +} i2s_t; + + +void port_i2s_allocate_init(i2s_t *self, bool left_justified); +void port_i2s_reset_instance(int i); +void i2s_reset(void); +void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop); +void port_i2s_stop(i2s_t *self); +bool port_i2s_playing(i2s_t *self); +bool port_i2s_paused(i2s_t *self); +void port_i2s_pause(i2s_t *self); +void port_i2s_resume(i2s_t *self); diff --git a/ports/esp32s2/mpconfigport.mk b/ports/esp32s2/mpconfigport.mk index 2ad8afb51..562c60998 100644 --- a/ports/esp32s2/mpconfigport.mk +++ b/ports/esp32s2/mpconfigport.mk @@ -15,7 +15,11 @@ LONGINT_IMPL = MPZ # These modules are implemented in ports//common-hal: CIRCUITPY_FULL_BUILD = 1 CIRCUITPY_ALARM = 1 -CIRCUITPY_AUDIOBUSIO = 0 +CIRCUITPY_AUDIOCORE = 1 +CIRCUITPY_AUDIOMP3 = 0 +CIRCUITPY_AUDIOBUSIO = 1 +CIRCUITPY_AUDIOBUSIO_PDMIN = 0 +CIRCUITPY_AUDIOBUSIO_I2SOUT = 1 CIRCUITPY_AUDIOIO = 0 CIRCUITPY_CANIO = 1 CIRCUITPY_COUNTIO = 1 diff --git a/ports/esp32s2/supervisor/port.c b/ports/esp32s2/supervisor/port.c index ff35845f7..75492492e 100644 --- a/ports/esp32s2/supervisor/port.c +++ b/ports/esp32s2/supervisor/port.c @@ -61,6 +61,10 @@ #include "components/soc/soc/esp32s2/include/soc/cache_memory.h" #include "components/soc/soc/esp32s2/include/soc/rtc_cntl_reg.h" +#if CIRCUITPY_AUDIOBUSIO +#include "i2s_common.h" +#endif + #define HEAP_SIZE (48 * 1024) uint32_t* heap; @@ -153,6 +157,10 @@ void reset_port(void) { ps2_reset(); #endif +#if CIRCUITPY_AUDIOBUSIO + i2s_reset(); +#endif + #if CIRCUITPY_PULSEIO esp32s2_peripherals_rmt_reset(); pulsein_reset(); diff --git a/shared-module/audiocore/__init__.c b/shared-module/audiocore/__init__.c index ac9d41b60..7bdfe460c 100644 --- a/shared-module/audiocore/__init__.c +++ b/shared-module/audiocore/__init__.c @@ -70,3 +70,63 @@ void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel, proto->get_buffer_structure(MP_OBJ_TO_PTR(sample_obj), single_channel, single_buffer, samples_signed, max_buffer_length, spacing); } + +void audiosample_convert_u8m_s16s(int16_t *buffer_out, const uint8_t *buffer_in, size_t nframes) { + for(;nframes--;) { + int16_t sample = (*buffer_in++ - 0x80) << 8; + *buffer_out++ = sample; + *buffer_out++ = sample; + } +} + + +void audiosample_convert_u8s_s16s(int16_t *buffer_out, const uint8_t *buffer_in, size_t nframes) { + size_t nsamples = 2*nframes; + for(;nsamples--;) { + int16_t sample = (*buffer_in++ - 0x80) << 8; + *buffer_out++ = sample; + } +} + +void audiosample_convert_s8m_s16s(int16_t *buffer_out, const int8_t *buffer_in, size_t nframes) { + for(;nframes--;) { + int16_t sample = (*buffer_in++) << 8; + *buffer_out++ = sample; + *buffer_out++ = sample; + } +} + + +void audiosample_convert_s8s_s16s(int16_t *buffer_out, const int8_t *buffer_in, size_t nframes) { + size_t nsamples = 2*nframes; + for(;nsamples--;) { + int16_t sample = (*buffer_in++) << 8; + *buffer_out++ = sample; + } +} + + +void audiosample_convert_u16m_s16s(int16_t *buffer_out, const uint16_t *buffer_in, size_t nframes) { + for(;nframes--;) { + int16_t sample = *buffer_in++ - 0x8000; + *buffer_out++ = sample; + *buffer_out++ = sample; + } +} + + +void audiosample_convert_u16s_s16s(int16_t *buffer_out, const uint16_t *buffer_in, size_t nframes) { + size_t nsamples = 2*nframes; + for(;nsamples--;) { + int16_t sample = *buffer_in++ - 0x8000; + *buffer_out++ = sample; + } +} + +void audiosample_convert_s16m_s16s(int16_t *buffer_out, const int16_t *buffer_in, size_t nframes) { + for(;nframes--;) { + int16_t sample = *buffer_in++; + *buffer_out++ = sample; + *buffer_out++ = sample; + } +} diff --git a/shared-module/audiocore/__init__.h b/shared-module/audiocore/__init__.h index 7a56454b8..ec2cf1cfa 100644 --- a/shared-module/audiocore/__init__.h +++ b/shared-module/audiocore/__init__.h @@ -74,4 +74,12 @@ void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel, bool* single_buffer, bool* samples_signed, uint32_t* max_buffer_length, uint8_t* spacing); +void audiosample_convert_u8m_s16s(int16_t *buffer_out, const uint8_t *buffer_in, size_t nframes); +void audiosample_convert_u8s_s16s(int16_t *buffer_out, const uint8_t *buffer_in, size_t nframes); +void audiosample_convert_s8m_s16s(int16_t *buffer_out, const int8_t *buffer_in, size_t nframes); +void audiosample_convert_s8s_s16s(int16_t *buffer_out, const int8_t *buffer_in, size_t nframes); +void audiosample_convert_u16m_s16s(int16_t *buffer_out, const uint16_t *buffer_in, size_t nframes); +void audiosample_convert_u16s_s16s(int16_t *buffer_out, const uint16_t *buffer_in, size_t nframes); +void audiosample_convert_s16m_s16s(int16_t *buffer_out, const int16_t *buffer_in, size_t nframes); + #endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOCORE__INIT__H -- cgit v1.2.3 From 42cad23dea7c4b6fdfb7a8a294cfeb8048f74cac Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 29 Dec 2020 16:31:29 -0600 Subject: make translate --- locale/circuitpython.pot | 64 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 3edadd57c..9016a38c9 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-12-23 23:04-0500\n" +"POT-Creation-Date: 2020-12-29 16:31-0600\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -114,6 +114,11 @@ msgstr "" msgid "%q() takes %d positional arguments but %d were given" msgstr "" +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c +#, c-format +msgid "%s error 0x%x" +msgstr "" + #: py/argcheck.c msgid "'%q' argument required" msgstr "" @@ -536,6 +541,10 @@ msgstr "" msgid "CBC blocks must be multiples of 16 bytes" msgstr "" +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c +msgid "CRC or checksum was invalid" +msgstr "" + #: py/objtype.c msgid "Call super().__init__() before accessing native object." msgstr "" @@ -984,6 +993,10 @@ msgstr "" msgid "Function requires lock" msgstr "" +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c +msgid "Generic Failure" +msgstr "" + #: shared-bindings/displayio/Display.c #: shared-bindings/displayio/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -1110,6 +1123,7 @@ msgstr "" msgid "Invalid Pin" msgstr "" +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c #: py/moduerrno.c shared-module/rgbmatrix/RGBMatrix.c msgid "Invalid argument" msgstr "" @@ -1218,6 +1232,14 @@ msgstr "" msgid "Invalid security_mode" msgstr "" +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c +msgid "Invalid size" +msgstr "" + +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c +msgid "Invalid state" +msgstr "" + #: shared-bindings/audiomixer/Mixer.c msgid "Invalid voice" msgstr "" @@ -1258,6 +1280,10 @@ msgstr "" msgid "Length must be non-negative" msgstr "" +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c +msgid "MAC address was invalid" +msgstr "" + #: shared-module/bitbangio/SPI.c msgid "MISO pin init failed." msgstr "" @@ -1496,6 +1522,18 @@ msgstr "" msgid "Only raw int supported for ip" msgstr "" +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c +msgid "Operation or feature not supported" +msgstr "" + +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c +msgid "Operation timed out" +msgstr "" + +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c +msgid "Out of memory" +msgstr "" + #: ports/esp32s2/common-hal/socketpool/SocketPool.c msgid "Out of sockets" msgstr "" @@ -1504,6 +1542,10 @@ msgstr "" msgid "Oversample must be multiple of 8." msgstr "" +#: shared-bindings/audiobusio/PDMIn.c +msgid "PDMIn not available" +msgstr "" + #: shared-bindings/pwmio/PWMOut.c msgid "" "PWM duty_cycle must be between 0 and 65535 inclusive (16 bit resolution)" @@ -1520,6 +1562,10 @@ msgstr "" msgid "ParallelBus not yet supported" msgstr "" +#: ports/esp32s2/i2s_common.c +msgid "Peripheral in use" +msgstr "" + #: py/moduerrno.c msgid "Permission denied" msgstr "" @@ -1637,6 +1683,10 @@ msgstr "" msgid "Read-only object" msgstr "" +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c +msgid "Received response was invalid" +msgstr "" + #: shared-bindings/displayio/EPaperDisplay.c msgid "Refresh too soon" msgstr "" @@ -1649,6 +1699,10 @@ msgstr "" msgid "Requested AES mode is unsupported" msgstr "" +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c +msgid "Requested resource not found" +msgstr "" + #: ports/atmel-samd/common-hal/audioio/AudioOut.c msgid "Right channel unsupported" msgstr "" @@ -2019,6 +2073,10 @@ msgstr "" msgid "Value length > max_length" msgstr "" +#: ports/esp32s2/bindings/espidf/__init__.c ports/esp32s2/esp_error.c +msgid "Version was invalid" +msgstr "" + #: py/emitnative.c msgid "Viper functions don't currently support more than 4 arguments" msgstr "" @@ -3833,6 +3891,10 @@ msgstr "" msgid "x value out of bounds" msgstr "" +#: ports/esp32s2/i2s_common.c +msgid "xTaskCreate failed" +msgstr "" + #: shared-bindings/displayio/Shape.c msgid "y should be an int" msgstr "" -- cgit v1.2.3 From 7bb196b9d2133477bf967d6b1d5dafe0971f765f Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 7 Jan 2021 15:01:33 -0600 Subject: esp32s2: 'make flash': Allow customizing the esptool flags This can be useful so that e.g., on a Kaluga when programming via the FTDI chip, you can override the variable to specify "--after=hard_reset" to automatically return to running CircuitPython, choose a different baud rate (921600 is about 2s faster than 460800), etc: make BOARD=espressif_kaluga_1 ESPTOOL_FLAGS="-b 921600 --before=default_reset --after=hard_reset" --- ports/esp32s2/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ports/esp32s2/Makefile b/ports/esp32s2/Makefile index 4a334a2eb..88bc8ce63 100644 --- a/ports/esp32s2/Makefile +++ b/ports/esp32s2/Makefile @@ -300,6 +300,8 @@ ESP_AUTOGEN_LD = $(BUILD)/esp-idf/esp-idf/esp32s2/esp32s2_out.ld $(BUILD)/esp-id FLASH_FLAGS = --flash_mode $(CIRCUITPY_ESP_FLASH_MODE) --flash_freq $(CIRCUITPY_ESP_FLASH_FREQ) --flash_size $(CIRCUITPY_ESP_FLASH_SIZE) +ESPTOOL_FLAGS ?= -b 460800 --before=default_reset --after=no_reset write_flash + all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2 .PHONY: esp-idf-stamp @@ -338,10 +340,10 @@ $(BUILD)/firmware.uf2: $(BUILD)/circuitpython-firmware.bin $(Q)$(PYTHON3) $(TOP)/tools/uf2/utils/uf2conv.py -f 0xbfdd4eee -b 0x0000 -c -o $@ $^ flash: $(BUILD)/firmware.bin - esptool.py --chip esp32s2 -p $(PORT) -b 460800 --before=default_reset --after=no_reset write_flash $(FLASH_FLAGS) 0x0000 $^ + esptool.py --chip esp32s2 -p $(PORT) $(ESPTOOL_FLAGS) write_flash $(FLASH_FLAGS) 0x0000 $^ flash-circuitpython-only: $(BUILD)/circuitpython-firmware.bin - esptool.py --chip esp32s2 -p $(PORT) -b 460800 --before=default_reset --after=no_reset write_flash $(FLASH_FLAGS) 0x10000 $^ + esptool.py --chip esp32s2 -p $(PORT) $(ESPTOOL_FLAGS) write_flash $(FLASH_FLAGS) 0x10000 $^ include $(TOP)/py/mkrules.mk -- cgit v1.2.3 From 010a4e7b0fefbee12f8139f7c5c13436545bb1eb Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 7 Jan 2021 16:23:21 -0600 Subject: esp32s2: port: Ensure JTAG pins are available for debugging --- ports/esp32s2/supervisor/port.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ports/esp32s2/supervisor/port.c b/ports/esp32s2/supervisor/port.c index ead12003d..a6b6a82aa 100644 --- a/ports/esp32s2/supervisor/port.c +++ b/ports/esp32s2/supervisor/port.c @@ -111,6 +111,14 @@ safe_mode_t port_init(void) { heap = NULL; never_reset_module_internal_pins(); + #if defined(DEBUG) || defined(ENABLE_JTAG) + // JTAG + common_hal_never_reset_pin(&pin_GPIO39); + common_hal_never_reset_pin(&pin_GPIO40); + common_hal_never_reset_pin(&pin_GPIO41); + common_hal_never_reset_pin(&pin_GPIO42); + #endif + #ifdef CONFIG_SPIRAM heap = (uint32_t*) (DRAM0_CACHE_ADDRESS_HIGH - CONFIG_SPIRAM_SIZE); heap_size = CONFIG_SPIRAM_SIZE / sizeof(uint32_t); -- cgit v1.2.3 From 352226402c6b897ec9dc776a61cff3330b8a40df Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 7 Jan 2021 16:28:33 -0600 Subject: esp32s2: i2s: fix accounting for "stretched" frames --- ports/esp32s2/i2s_common.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ports/esp32s2/i2s_common.c b/ports/esp32s2/i2s_common.c index 1933665a3..24ded295f 100644 --- a/ports/esp32s2/i2s_common.c +++ b/ports/esp32s2/i2s_common.c @@ -103,9 +103,9 @@ static void i2s_fill_buffer(i2s_t *self) { } } else { #define STACK_BUFFER_SIZE (64) - size_t bytes_per_frame = self->channel_count * self->bytes_per_sample; - size_t framecount = MIN(STACK_BUFFER_SIZE / bytes_per_frame, bytecount); - int16_t signed_samples[STACK_BUFFER_SIZE / sizeof(int16_t)]; + 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) { assert(self->channel_count == 1); if (self->bytes_per_sample == 1) { @@ -129,9 +129,9 @@ static void i2s_fill_buffer(i2s_t *self) { } } size_t expanded_bytes_written = 0; - ESP_CALL_RAISE(i2s_write(self->instance, signed_samples, 4*framecount, &expanded_bytes_written, 0)); + ESP_CALL_RAISE(i2s_write(self->instance, signed_samples, bytes_per_output_frame*framecount, &expanded_bytes_written, 0)); assert(expanded_bytes_written % 4 == 0); - bytes_written = expanded_bytes_written / 4 * bytes_per_frame; + bytes_written = expanded_bytes_written / bytes_per_output_frame * bytes_per_input_frame; } self->sample_data += bytes_written; // We have filled the DMA buffer @@ -181,6 +181,10 @@ void port_i2s_allocate_init(i2s_t *self, bool left_justified) { void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) { + if (common_hal_audiobusio_i2sout_get_playing(self)) { + common_hal_audiobusio_i2sout_stop(self); + } + self->sample = sample; self->bytes_per_sample = audiosample_bits_per_sample(sample) / 8; self->channel_count = audiosample_channel_count(sample); -- cgit v1.2.3 From d3afda61d8172ad6ec8706782c397d9cd52c3edc Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 7 Jan 2021 16:29:08 -0600 Subject: esp32s2: i2s: must reset buffer, otherwise wave samples don't start .. and other housekeeping when starting to play a sample --- ports/esp32s2/i2s_common.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/esp32s2/i2s_common.c b/ports/esp32s2/i2s_common.c index 24ded295f..d5c0facaa 100644 --- a/ports/esp32s2/i2s_common.c +++ b/ports/esp32s2/i2s_common.c @@ -186,6 +186,7 @@ void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) { } self->sample = sample; + self->loop = loop; self->bytes_per_sample = audiosample_bits_per_sample(sample) / 8; self->channel_count = audiosample_channel_count(sample); bool single_buffer; @@ -195,12 +196,15 @@ void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) { audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed, &max_buffer_length, &spacing); self->samples_signed = samples_signed; - self->loop = loop; self->playing = true; self->paused = false; self->stopping = false; + self->sample_data = self->sample_end = NULL; // We always output stereo so output twice as many bits. // uint16_t bits_per_sample_output = bits_per_sample * 2; + + audiosample_reset_buffer(self->sample, false, 0); + ESP_CALL_RAISE(i2s_set_sample_rates(self->instance, audiosample_sample_rate(sample))); i2s_fill_buffer(self); } -- cgit v1.2.3 From cfd8288dfa0a27bd727e3877659d9bbb990dd1ce Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 7 Jan 2021 16:29:52 -0600 Subject: esp2s2: enlarge stack-buffer, use it instead of i2s_zero_dma_buffer .. it's not clear that there was a problem with i2s_zero_dma_buffer, but just in case. --- ports/esp32s2/i2s_common.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ports/esp32s2/i2s_common.c b/ports/esp32s2/i2s_common.c index d5c0facaa..ca2a3b93e 100644 --- a/ports/esp32s2/i2s_common.c +++ b/ports/esp32s2/i2s_common.c @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#include + #include "py/runtime.h" #include "i2s_common.h" @@ -69,8 +71,17 @@ static void i2s_fill_buffer(i2s_t *self) { if (self->instance < 0 || self->instance >= I2S_NUM_MAX) { return; } - if (self->paused || !self->sample) { - i2s_zero_dma_buffer(self->instance); +#define STACK_BUFFER_SIZE (512) + int16_t signed_samples[STACK_BUFFER_SIZE / sizeof(int16_t)]; +mp_printf(&mp_plat_print, "playing=%d paused=%d stopping=%d sample@%p sample_data=%p..%p\n", self->playing, self->paused, self->stopping, self->sample, self->sample_data, self->sample_end); + + if (!self->playing || self->paused || !self->sample) { + memset(signed_samples, 0, sizeof(signed_samples)); + + size_t bytes_written = 0; + do { + ESP_CALL_RAISE(i2s_write(self->instance, signed_samples, sizeof(signed_samples), &bytes_written, 0)); + } while (bytes_written != 0); return; } while (!self->stopping) { @@ -102,7 +113,6 @@ static void i2s_fill_buffer(i2s_t *self) { ESP_CALL_RAISE(i2s_write_expand(self->instance, self->sample_data, bytecount, 8, 16, &bytes_written, 0)); } } else { -#define STACK_BUFFER_SIZE (64) 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); -- cgit v1.2.3 From 873a300d02316ca8b6d227574613385acc3df6b9 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 8 Jan 2021 08:46:41 -0600 Subject: i2s_fill_buffer: Need to fill with zeros when stopping --- ports/esp32s2/i2s_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32s2/i2s_common.c b/ports/esp32s2/i2s_common.c index ca2a3b93e..5316c8088 100644 --- a/ports/esp32s2/i2s_common.c +++ b/ports/esp32s2/i2s_common.c @@ -75,7 +75,7 @@ static void i2s_fill_buffer(i2s_t *self) { int16_t signed_samples[STACK_BUFFER_SIZE / sizeof(int16_t)]; mp_printf(&mp_plat_print, "playing=%d paused=%d stopping=%d sample@%p sample_data=%p..%p\n", self->playing, self->paused, self->stopping, self->sample, self->sample_data, self->sample_end); - if (!self->playing || self->paused || !self->sample) { + if (!self->playing || self->paused || !self->sample || self->stopping) { memset(signed_samples, 0, sizeof(signed_samples)); size_t bytes_written = 0; -- cgit v1.2.3 From 0b7a4c4b2b7e6e56e40100ab1e85d79b8a93ef33 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 8 Jan 2021 08:46:56 -0600 Subject: i2s_fill_buffer: remove debug print --- ports/esp32s2/i2s_common.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/esp32s2/i2s_common.c b/ports/esp32s2/i2s_common.c index 5316c8088..a58bdc23b 100644 --- a/ports/esp32s2/i2s_common.c +++ b/ports/esp32s2/i2s_common.c @@ -73,7 +73,6 @@ static void i2s_fill_buffer(i2s_t *self) { } #define STACK_BUFFER_SIZE (512) int16_t signed_samples[STACK_BUFFER_SIZE / sizeof(int16_t)]; -mp_printf(&mp_plat_print, "playing=%d paused=%d stopping=%d sample@%p sample_data=%p..%p\n", self->playing, self->paused, self->stopping, self->sample, self->sample_data, self->sample_end); if (!self->playing || self->paused || !self->sample || self->stopping) { memset(signed_samples, 0, sizeof(signed_samples)); -- cgit v1.2.3 From 12264cca34179c2e3bafad49145c9af3f827fa0f Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 8 Jan 2021 08:47:13 -0600 Subject: port_i2s_play: remove build error --- ports/esp32s2/i2s_common.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ports/esp32s2/i2s_common.c b/ports/esp32s2/i2s_common.c index a58bdc23b..a948ee0f5 100644 --- a/ports/esp32s2/i2s_common.c +++ b/ports/esp32s2/i2s_common.c @@ -190,10 +190,6 @@ void port_i2s_allocate_init(i2s_t *self, bool left_justified) { void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) { - if (common_hal_audiobusio_i2sout_get_playing(self)) { - common_hal_audiobusio_i2sout_stop(self); - } - self->sample = sample; self->loop = loop; self->bytes_per_sample = audiosample_bits_per_sample(sample) / 8; -- cgit v1.2.3 From 430bcdb59d47fc89c7788aeb300027bcf4e00bf2 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 8 Jan 2021 08:49:25 -0600 Subject: port_i2s_play: fill the initial buffer via background callback There were _possibly_ problems where this routine was being entered by direct call AND by background callback. Schedule the work here, and it will be done almost immediately, without worry about interference. I don't know if this is strictly necessary, but it doesn't hurt. Since the I2S clock is being run all the time, we have to enter the background task to fill the FIFO with zeros constantly anyway. --- ports/esp32s2/i2s_common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/esp32s2/i2s_common.c b/ports/esp32s2/i2s_common.c index a948ee0f5..552fe7424 100644 --- a/ports/esp32s2/i2s_common.c +++ b/ports/esp32s2/i2s_common.c @@ -211,7 +211,8 @@ void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) { audiosample_reset_buffer(self->sample, false, 0); ESP_CALL_RAISE(i2s_set_sample_rates(self->instance, audiosample_sample_rate(sample))); - i2s_fill_buffer(self); + + background_callback_add(&self->callback, i2s_callback_fun, self); } bool port_i2s_playing(i2s_t *self) { -- cgit v1.2.3 From 5f0e41ad601664b37c109a5d84b2a6031a4a740f Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 8 Jan 2021 09:01:34 -0600 Subject: I2SOut: Enable ticks during audio playback .. otherwise, the background callback to load the I2S fifos does not get run. (I'm not sure this is _correct_ behavior of sleep + background tasks, but it is the current behavior) --- ports/esp32s2/common-hal/audiobusio/I2SOut.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/esp32s2/common-hal/audiobusio/I2SOut.c b/ports/esp32s2/common-hal/audiobusio/I2SOut.c index be5b04fa9..694cab49d 100644 --- a/ports/esp32s2/common-hal/audiobusio/I2SOut.c +++ b/ports/esp32s2/common-hal/audiobusio/I2SOut.c @@ -63,6 +63,8 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t* self, self->bit_clock = bit_clock; self->word_select = word_select; self->data = data; + + supervisor_enable_tick(); } bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t* self) { @@ -93,6 +95,8 @@ void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t* self) { port_i2s_reset_instance(self->peripheral.instance); } self->peripheral.instance = -1; + + supervisor_disable_tick(); } void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t* self, -- cgit v1.2.3 From 10861b403881c96cc7da6d1b8c6e80b40332fee7 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 9 Jan 2021 13:41:44 -0600 Subject: esp32s2: Rename ESP_CALL_RAISE to CHECK_ESP_RESULT Suggested by @tannewt, thanks! --- ports/esp32s2/bindings/espidf/__init__.h | 2 +- ports/esp32s2/common-hal/audiobusio/I2SOut.c | 2 +- ports/esp32s2/i2s_common.c | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ports/esp32s2/bindings/espidf/__init__.h b/ports/esp32s2/bindings/espidf/__init__.h index 5ff49970d..9248b01ac 100644 --- a/ports/esp32s2/bindings/espidf/__init__.h +++ b/ports/esp32s2/bindings/espidf/__init__.h @@ -37,6 +37,6 @@ extern const mp_obj_type_t mp_type_espidf_MemoryError; NORETURN void mp_raise_espidf_MemoryError(void); void raise_esp_error(esp_err_t err) NORETURN; -#define ESP_CALL_RAISE(x) do { int res = (x); if(res != ESP_OK) raise_esp_error(res); } while(0) +#define CHECK_ESP_RESULT(x) do { int res = (x); if(res != ESP_OK) raise_esp_error(res); } while(0) #endif // MICROPY_INCLUDED_ESP32S2_BINDINGS_ESPIDF___INIT___H diff --git a/ports/esp32s2/common-hal/audiobusio/I2SOut.c b/ports/esp32s2/common-hal/audiobusio/I2SOut.c index 694cab49d..ee16c6ca8 100644 --- a/ports/esp32s2/common-hal/audiobusio/I2SOut.c +++ b/ports/esp32s2/common-hal/audiobusio/I2SOut.c @@ -59,7 +59,7 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t* self, .data_out_num = data->number, .data_in_num = I2S_PIN_NO_CHANGE, }; - ESP_CALL_RAISE(i2s_set_pin(self->peripheral.instance, &i2s_pin_config)); + CHECK_ESP_RESULT(i2s_set_pin(self->peripheral.instance, &i2s_pin_config)); self->bit_clock = bit_clock; self->word_select = word_select; self->data = data; diff --git a/ports/esp32s2/i2s_common.c b/ports/esp32s2/i2s_common.c index 552fe7424..f967e9a09 100644 --- a/ports/esp32s2/i2s_common.c +++ b/ports/esp32s2/i2s_common.c @@ -79,7 +79,7 @@ static void i2s_fill_buffer(i2s_t *self) { size_t bytes_written = 0; do { - ESP_CALL_RAISE(i2s_write(self->instance, signed_samples, sizeof(signed_samples), &bytes_written, 0)); + CHECK_ESP_RESULT(i2s_write(self->instance, signed_samples, sizeof(signed_samples), &bytes_written, 0)); } while (bytes_written != 0); return; } @@ -107,9 +107,9 @@ static void i2s_fill_buffer(i2s_t *self) { size_t bytecount = self->sample_end - self->sample_data; if (self->samples_signed && self->channel_count == 2) { if (self->bytes_per_sample == 2) { - ESP_CALL_RAISE(i2s_write(self->instance, self->sample_data, bytecount, &bytes_written, 0)); + CHECK_ESP_RESULT(i2s_write(self->instance, self->sample_data, bytecount, &bytes_written, 0)); } else { - ESP_CALL_RAISE(i2s_write_expand(self->instance, self->sample_data, bytecount, 8, 16, &bytes_written, 0)); + CHECK_ESP_RESULT(i2s_write_expand(self->instance, self->sample_data, bytecount, 8, 16, &bytes_written, 0)); } } else { const size_t bytes_per_output_frame = 4; @@ -138,7 +138,7 @@ static void i2s_fill_buffer(i2s_t *self) { } } size_t expanded_bytes_written = 0; - ESP_CALL_RAISE(i2s_write(self->instance, signed_samples, bytes_per_output_frame*framecount, &expanded_bytes_written, 0)); + CHECK_ESP_RESULT(i2s_write(self->instance, signed_samples, bytes_per_output_frame*framecount, &expanded_bytes_written, 0)); assert(expanded_bytes_written % 4 == 0); bytes_written = expanded_bytes_written / bytes_per_output_frame * bytes_per_input_frame; } @@ -179,7 +179,7 @@ void port_i2s_allocate_init(i2s_t *self, bool left_justified) { .dma_buf_len = 128, // in _frames_, so 128 is 512 bytes per dma buf .use_apll = false, }; - ESP_CALL_RAISE(i2s_driver_install(self->instance, &i2s_config, I2S_QUEUE_SIZE, &i2s_queues[self->instance])); + CHECK_ESP_RESULT(i2s_driver_install(self->instance, &i2s_config, I2S_QUEUE_SIZE, &i2s_queues[self->instance])); if (!xTaskCreate(i2s_event_task, "I2S_task", 3 * configMINIMAL_STACK_SIZE, self, CONFIG_PTHREAD_TASK_PRIO_DEFAULT, &i2s_tasks[self->instance])) { mp_raise_OSError_msg(translate("xTaskCreate failed")); @@ -210,7 +210,7 @@ void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) { audiosample_reset_buffer(self->sample, false, 0); - ESP_CALL_RAISE(i2s_set_sample_rates(self->instance, audiosample_sample_rate(sample))); + CHECK_ESP_RESULT(i2s_set_sample_rates(self->instance, audiosample_sample_rate(sample))); background_callback_add(&self->callback, i2s_callback_fun, self); } @@ -233,13 +233,13 @@ void port_i2s_stop(i2s_t *self) { void port_i2s_pause(i2s_t *self) { if (!self->paused) { self->paused = true; - ESP_CALL_RAISE(i2s_stop(self->instance)); + CHECK_ESP_RESULT(i2s_stop(self->instance)); } } void port_i2s_resume(i2s_t *self) { if (self->paused) { self->paused = false; - ESP_CALL_RAISE(i2s_start(self->instance)); + CHECK_ESP_RESULT(i2s_start(self->instance)); } } -- cgit v1.2.3 From 4735cf4747764f0694e1285085e99f06a7a11a81 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 9 Jan 2021 13:48:36 -0600 Subject: esp32s2: audiobusio: move i2s_common inside Originally, I believed the implementation might be shared with AudioOut, as on the ESP32 (non-S2) the I2S peripheral was also used to drive the DAC. However, this is not the case on ESP32-S2 and appears it will not be the case with the ESP32-S3 or -C3, to the extent that there's skeletal support for either of them in esp-idf master branch. However, it could still be shared by I2SIn or PDMIn (the latter being hypothetically implemented as I2SIn + digital postprocessing like we did in the atmel-sam port, to my understanding), so I moved it to the common-hal folder. --- ports/esp32s2/Makefile | 1 - ports/esp32s2/common-hal/audiobusio/I2SOut.h | 2 +- ports/esp32s2/common-hal/audiobusio/__init__.c | 245 +++++++++++++++++++++++++ ports/esp32s2/common-hal/audiobusio/__init__.h | 61 ++++++ ports/esp32s2/i2s_common.c | 245 ------------------------- ports/esp32s2/i2s_common.h | 61 ------ ports/esp32s2/supervisor/port.c | 2 +- 7 files changed, 308 insertions(+), 309 deletions(-) delete mode 100644 ports/esp32s2/i2s_common.c delete mode 100644 ports/esp32s2/i2s_common.h diff --git a/ports/esp32s2/Makefile b/ports/esp32s2/Makefile index 88bc8ce63..9827b555c 100644 --- a/ports/esp32s2/Makefile +++ b/ports/esp32s2/Makefile @@ -175,7 +175,6 @@ CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32S2 -DCFG_TUSB_OS=OPT_OS_FREERTOS -DCFG_TUD SRC_C += \ background.c \ fatfs_port.c \ - i2s_common.c \ mphalport.c \ bindings/espidf/__init__.c \ boards/$(BOARD)/board.c \ diff --git a/ports/esp32s2/common-hal/audiobusio/I2SOut.h b/ports/esp32s2/common-hal/audiobusio/I2SOut.h index 90266342c..891e9af67 100644 --- a/ports/esp32s2/common-hal/audiobusio/I2SOut.h +++ b/ports/esp32s2/common-hal/audiobusio/I2SOut.h @@ -29,7 +29,7 @@ #include "supervisor/background_callback.h" #include "common-hal/microcontroller/Pin.h" -#include "i2s_common.h" +#include "common-hal/audiobusio/__init__.h" // Some boards don't implement I2SOut, so suppress any routines from here. #if CIRCUITPY_AUDIOBUSIO_I2SOUT diff --git a/ports/esp32s2/common-hal/audiobusio/__init__.c b/ports/esp32s2/common-hal/audiobusio/__init__.c index e69de29bb..01c77c531 100644 --- a/ports/esp32s2/common-hal/audiobusio/__init__.c +++ b/ports/esp32s2/common-hal/audiobusio/__init__.c @@ -0,0 +1,245 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/runtime.h" + +#include "common-hal/audiobusio/__init__.h" +#include "bindings/espidf/__init__.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "shared-module/audiocore/__init__.h" + +#define I2S_QUEUE_SIZE (3) + +static i2s_t *i2s_instance[I2S_NUM_MAX]; +static QueueHandle_t i2s_queues[I2S_NUM_MAX]; +static TaskHandle_t i2s_tasks[I2S_NUM_MAX]; + +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_MAX); + 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_MAX; i++) { + port_i2s_reset_instance(i); + } +} + +static void i2s_fill_buffer(i2s_t *self) { + if (self->instance < 0 || self->instance >= I2S_NUM_MAX) { + return; + } +#define STACK_BUFFER_SIZE (512) + int16_t signed_samples[STACK_BUFFER_SIZE / sizeof(int16_t)]; + + if (!self->playing || self->paused || !self->sample || self->stopping) { + memset(signed_samples, 0, sizeof(signed_samples)); + + size_t bytes_written = 0; + do { + CHECK_ESP_RESULT(i2s_write(self->instance, signed_samples, sizeof(signed_samples), &bytes_written, 0)); + } while (bytes_written != 0); + return; + } + while (!self->stopping) { + if (self->sample_data == self->sample_end) { + uint32_t sample_buffer_length; + audioio_get_buffer_result_t get_buffer_result = + audiosample_get_buffer(self->sample, false, 0, + &self->sample_data, &sample_buffer_length); + self->sample_end = self->sample_data + sample_buffer_length; + if (get_buffer_result == GET_BUFFER_DONE) { + if (self->loop) { + audiosample_reset_buffer(self->sample, false, 0); + } else { + self->stopping = true; + break; + } + } + if (get_buffer_result == GET_BUFFER_ERROR || sample_buffer_length == 0) { + self->stopping = true; + break; + } + } + size_t bytes_written = 0; + size_t bytecount = self->sample_end - self->sample_data; + if (self->samples_signed && self->channel_count == 2) { + if (self->bytes_per_sample == 2) { + CHECK_ESP_RESULT(i2s_write(self->instance, self->sample_data, bytecount, &bytes_written, 0)); + } else { + CHECK_ESP_RESULT(i2s_write_expand(self->instance, self->sample_data, bytecount, 8, 16, &bytes_written, 0)); + } + } 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) { + assert(self->channel_count == 1); + if (self->bytes_per_sample == 1) { + audiosample_convert_s8m_s16s(signed_samples, (int8_t*)(void*)self->sample_data, framecount); + } else { + audiosample_convert_s16m_s16s(signed_samples, (int16_t*)(void*)self->sample_data, framecount); + } + } else { + if (self->channel_count == 1) { + if (self->bytes_per_sample == 1) { + audiosample_convert_u8m_s16s(signed_samples, (uint8_t*)(void*)self->sample_data, framecount); + } else { + audiosample_convert_u16m_s16s(signed_samples, (uint16_t*)(void*)self->sample_data, framecount); + } + } else { + if (self->bytes_per_sample == 1) { + audiosample_convert_u8s_s16s(signed_samples, (uint8_t*)(void*)self->sample_data, framecount); + } else { + audiosample_convert_u16s_s16s(signed_samples, (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, 0)); + 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; + } + } +} + +static void i2s_callback_fun(void *self_in) { + i2s_t *self = self_in; + i2s_fill_buffer(self); +} + +static void i2s_event_task(void *self_in) { + i2s_t *self = self_in; + while(true) { + i2s_event_type_t event; + BaseType_t result = xQueueReceive(i2s_queues[self->instance], &event, portMAX_DELAY); + if (result && event == I2S_EVENT_TX_DONE) { + background_callback_add(&self->callback, i2s_callback_fun, self_in); + } + } +} + +void port_i2s_allocate_init(i2s_t *self, bool left_justified) { + self->instance = port_i2s_allocate(); + + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, + .sample_rate = 44100, + .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 = 2, + .dma_buf_len = 128, // in _frames_, so 128 is 512 bytes per dma buf + .use_apll = false, + }; + CHECK_ESP_RESULT(i2s_driver_install(self->instance, &i2s_config, I2S_QUEUE_SIZE, &i2s_queues[self->instance])); + + if (!xTaskCreate(i2s_event_task, "I2S_task", 3 * configMINIMAL_STACK_SIZE, self, CONFIG_PTHREAD_TASK_PRIO_DEFAULT, &i2s_tasks[self->instance])) { + mp_raise_OSError_msg(translate("xTaskCreate failed")); + } + i2s_instance[self->instance] = self; + +} + + +void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) { + self->sample = sample; + self->loop = loop; + self->bytes_per_sample = audiosample_bits_per_sample(sample) / 8; + self->channel_count = audiosample_channel_count(sample); + bool single_buffer; + bool samples_signed; + uint32_t max_buffer_length; + uint8_t spacing; + audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed, + &max_buffer_length, &spacing); + self->samples_signed = samples_signed; + self->playing = true; + self->paused = false; + self->stopping = false; + self->sample_data = self->sample_end = NULL; + // We always output stereo so output twice as many bits. + // uint16_t bits_per_sample_output = bits_per_sample * 2; + + audiosample_reset_buffer(self->sample, false, 0); + + CHECK_ESP_RESULT(i2s_set_sample_rates(self->instance, audiosample_sample_rate(sample))); + + background_callback_add(&self->callback, i2s_callback_fun, self); +} + +bool port_i2s_playing(i2s_t *self) { + return self->playing && !self->stopping; +} + +bool port_i2s_paused(i2s_t *self) { + return self->paused; +} + +void port_i2s_stop(i2s_t *self) { + self->sample = NULL; + self->paused = false; + self->playing = false; + self->stopping = false; +} + +void port_i2s_pause(i2s_t *self) { + if (!self->paused) { + self->paused = true; + CHECK_ESP_RESULT(i2s_stop(self->instance)); + } +} + +void port_i2s_resume(i2s_t *self) { + if (self->paused) { + self->paused = false; + CHECK_ESP_RESULT(i2s_start(self->instance)); + } +} diff --git a/ports/esp32s2/common-hal/audiobusio/__init__.h b/ports/esp32s2/common-hal/audiobusio/__init__.h index e69de29bb..7709735da 100644 --- a/ports/esp32s2/common-hal/audiobusio/__init__.h +++ b/ports/esp32s2/common-hal/audiobusio/__init__.h @@ -0,0 +1,61 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "py/obj.h" + +#include "supervisor/background_callback.h" + +#include "driver/i2s.h" + +typedef struct { + mp_obj_t *sample; + bool left_justified; + bool loop; + bool paused; + bool playing; + bool stopping; + bool samples_signed; + int8_t bytes_per_sample; + int8_t channel_count; + int8_t instance; + uint16_t buffer_length; + uint8_t *sample_data, *sample_end; + i2s_config_t i2s_config; + background_callback_t callback; +} i2s_t; + + +void port_i2s_allocate_init(i2s_t *self, bool left_justified); +void port_i2s_reset_instance(int i); +void i2s_reset(void); +void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop); +void port_i2s_stop(i2s_t *self); +bool port_i2s_playing(i2s_t *self); +bool port_i2s_paused(i2s_t *self); +void port_i2s_pause(i2s_t *self); +void port_i2s_resume(i2s_t *self); diff --git a/ports/esp32s2/i2s_common.c b/ports/esp32s2/i2s_common.c deleted file mode 100644 index f967e9a09..000000000 --- a/ports/esp32s2/i2s_common.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2020 Jeff Epler for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include - -#include "py/runtime.h" - -#include "i2s_common.h" -#include "bindings/espidf/__init__.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" - -#include "shared-module/audiocore/__init__.h" - -#define I2S_QUEUE_SIZE (3) - -static i2s_t *i2s_instance[I2S_NUM_MAX]; -static QueueHandle_t i2s_queues[I2S_NUM_MAX]; -static TaskHandle_t i2s_tasks[I2S_NUM_MAX]; - -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_MAX); - 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_MAX; i++) { - port_i2s_reset_instance(i); - } -} - -static void i2s_fill_buffer(i2s_t *self) { - if (self->instance < 0 || self->instance >= I2S_NUM_MAX) { - return; - } -#define STACK_BUFFER_SIZE (512) - int16_t signed_samples[STACK_BUFFER_SIZE / sizeof(int16_t)]; - - if (!self->playing || self->paused || !self->sample || self->stopping) { - memset(signed_samples, 0, sizeof(signed_samples)); - - size_t bytes_written = 0; - do { - CHECK_ESP_RESULT(i2s_write(self->instance, signed_samples, sizeof(signed_samples), &bytes_written, 0)); - } while (bytes_written != 0); - return; - } - while (!self->stopping) { - if (self->sample_data == self->sample_end) { - uint32_t sample_buffer_length; - audioio_get_buffer_result_t get_buffer_result = - audiosample_get_buffer(self->sample, false, 0, - &self->sample_data, &sample_buffer_length); - self->sample_end = self->sample_data + sample_buffer_length; - if (get_buffer_result == GET_BUFFER_DONE) { - if (self->loop) { - audiosample_reset_buffer(self->sample, false, 0); - } else { - self->stopping = true; - break; - } - } - if (get_buffer_result == GET_BUFFER_ERROR || sample_buffer_length == 0) { - self->stopping = true; - break; - } - } - size_t bytes_written = 0; - size_t bytecount = self->sample_end - self->sample_data; - if (self->samples_signed && self->channel_count == 2) { - if (self->bytes_per_sample == 2) { - CHECK_ESP_RESULT(i2s_write(self->instance, self->sample_data, bytecount, &bytes_written, 0)); - } else { - CHECK_ESP_RESULT(i2s_write_expand(self->instance, self->sample_data, bytecount, 8, 16, &bytes_written, 0)); - } - } 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) { - assert(self->channel_count == 1); - if (self->bytes_per_sample == 1) { - audiosample_convert_s8m_s16s(signed_samples, (int8_t*)(void*)self->sample_data, framecount); - } else { - audiosample_convert_s16m_s16s(signed_samples, (int16_t*)(void*)self->sample_data, framecount); - } - } else { - if (self->channel_count == 1) { - if (self->bytes_per_sample == 1) { - audiosample_convert_u8m_s16s(signed_samples, (uint8_t*)(void*)self->sample_data, framecount); - } else { - audiosample_convert_u16m_s16s(signed_samples, (uint16_t*)(void*)self->sample_data, framecount); - } - } else { - if (self->bytes_per_sample == 1) { - audiosample_convert_u8s_s16s(signed_samples, (uint8_t*)(void*)self->sample_data, framecount); - } else { - audiosample_convert_u16s_s16s(signed_samples, (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, 0)); - 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; - } - } -} - -static void i2s_callback_fun(void *self_in) { - i2s_t *self = self_in; - i2s_fill_buffer(self); -} - -static void i2s_event_task(void *self_in) { - i2s_t *self = self_in; - while(true) { - i2s_event_type_t event; - BaseType_t result = xQueueReceive(i2s_queues[self->instance], &event, portMAX_DELAY); - if (result && event == I2S_EVENT_TX_DONE) { - background_callback_add(&self->callback, i2s_callback_fun, self_in); - } - } -} - -void port_i2s_allocate_init(i2s_t *self, bool left_justified) { - self->instance = port_i2s_allocate(); - - i2s_config_t i2s_config = { - .mode = I2S_MODE_MASTER | I2S_MODE_TX, - .sample_rate = 44100, - .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 = 2, - .dma_buf_len = 128, // in _frames_, so 128 is 512 bytes per dma buf - .use_apll = false, - }; - CHECK_ESP_RESULT(i2s_driver_install(self->instance, &i2s_config, I2S_QUEUE_SIZE, &i2s_queues[self->instance])); - - if (!xTaskCreate(i2s_event_task, "I2S_task", 3 * configMINIMAL_STACK_SIZE, self, CONFIG_PTHREAD_TASK_PRIO_DEFAULT, &i2s_tasks[self->instance])) { - mp_raise_OSError_msg(translate("xTaskCreate failed")); - } - i2s_instance[self->instance] = self; - -} - - -void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop) { - self->sample = sample; - self->loop = loop; - self->bytes_per_sample = audiosample_bits_per_sample(sample) / 8; - self->channel_count = audiosample_channel_count(sample); - bool single_buffer; - bool samples_signed; - uint32_t max_buffer_length; - uint8_t spacing; - audiosample_get_buffer_structure(sample, false, &single_buffer, &samples_signed, - &max_buffer_length, &spacing); - self->samples_signed = samples_signed; - self->playing = true; - self->paused = false; - self->stopping = false; - self->sample_data = self->sample_end = NULL; - // We always output stereo so output twice as many bits. - // uint16_t bits_per_sample_output = bits_per_sample * 2; - - audiosample_reset_buffer(self->sample, false, 0); - - CHECK_ESP_RESULT(i2s_set_sample_rates(self->instance, audiosample_sample_rate(sample))); - - background_callback_add(&self->callback, i2s_callback_fun, self); -} - -bool port_i2s_playing(i2s_t *self) { - return self->playing && !self->stopping; -} - -bool port_i2s_paused(i2s_t *self) { - return self->paused; -} - -void port_i2s_stop(i2s_t *self) { - self->sample = NULL; - self->paused = false; - self->playing = false; - self->stopping = false; -} - -void port_i2s_pause(i2s_t *self) { - if (!self->paused) { - self->paused = true; - CHECK_ESP_RESULT(i2s_stop(self->instance)); - } -} - -void port_i2s_resume(i2s_t *self) { - if (self->paused) { - self->paused = false; - CHECK_ESP_RESULT(i2s_start(self->instance)); - } -} diff --git a/ports/esp32s2/i2s_common.h b/ports/esp32s2/i2s_common.h deleted file mode 100644 index 7709735da..000000000 --- a/ports/esp32s2/i2s_common.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2020 Jeff Epler for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#pragma once - -#include "py/obj.h" - -#include "supervisor/background_callback.h" - -#include "driver/i2s.h" - -typedef struct { - mp_obj_t *sample; - bool left_justified; - bool loop; - bool paused; - bool playing; - bool stopping; - bool samples_signed; - int8_t bytes_per_sample; - int8_t channel_count; - int8_t instance; - uint16_t buffer_length; - uint8_t *sample_data, *sample_end; - i2s_config_t i2s_config; - background_callback_t callback; -} i2s_t; - - -void port_i2s_allocate_init(i2s_t *self, bool left_justified); -void port_i2s_reset_instance(int i); -void i2s_reset(void); -void port_i2s_play(i2s_t *self, mp_obj_t sample, bool loop); -void port_i2s_stop(i2s_t *self); -bool port_i2s_playing(i2s_t *self); -bool port_i2s_paused(i2s_t *self); -void port_i2s_pause(i2s_t *self); -void port_i2s_resume(i2s_t *self); diff --git a/ports/esp32s2/supervisor/port.c b/ports/esp32s2/supervisor/port.c index a6b6a82aa..a5215fdef 100644 --- a/ports/esp32s2/supervisor/port.c +++ b/ports/esp32s2/supervisor/port.c @@ -62,7 +62,7 @@ #include "components/soc/soc/esp32s2/include/soc/rtc_cntl_reg.h" #if CIRCUITPY_AUDIOBUSIO -#include "i2s_common.h" +#include "common-hal/audiobusio/__init__.h" #endif #define HEAP_SIZE (48 * 1024) -- cgit v1.2.3 From e20c65d8f044cb9522cd1a7f4a3030efefa77cbb Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 9 Jan 2021 13:59:54 -0600 Subject: background tasks: Add, use port_wake_main_task Some ports need an extra operation to ensure that the main task is awoken so that a queued background task will execute during an ongoing light sleep. This removes the need to enable supervisor ticks while I2SOut is operating. Closes: #3952 --- ports/esp32s2/common-hal/audiobusio/I2SOut.c | 4 ---- ports/esp32s2/supervisor/port.c | 12 +++++++++++- supervisor/port.h | 4 ++++ supervisor/shared/background_callback.c | 4 ++++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/ports/esp32s2/common-hal/audiobusio/I2SOut.c b/ports/esp32s2/common-hal/audiobusio/I2SOut.c index ee16c6ca8..86322be86 100644 --- a/ports/esp32s2/common-hal/audiobusio/I2SOut.c +++ b/ports/esp32s2/common-hal/audiobusio/I2SOut.c @@ -63,8 +63,6 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t* self, self->bit_clock = bit_clock; self->word_select = word_select; self->data = data; - - supervisor_enable_tick(); } bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t* self) { @@ -95,8 +93,6 @@ void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t* self) { port_i2s_reset_instance(self->peripheral.instance); } self->peripheral.instance = -1; - - supervisor_disable_tick(); } void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t* self, diff --git a/ports/esp32s2/supervisor/port.c b/ports/esp32s2/supervisor/port.c index a5215fdef..6491e7430 100644 --- a/ports/esp32s2/supervisor/port.c +++ b/ports/esp32s2/supervisor/port.c @@ -111,6 +111,12 @@ safe_mode_t port_init(void) { heap = NULL; never_reset_module_internal_pins(); + #if defined(DEBUG) + // debug UART + common_hal_never_reset_pin(&pin_GPIO43); + common_hal_never_reset_pin(&pin_GPIO44); + #endif + #if defined(DEBUG) || defined(ENABLE_JTAG) // JTAG common_hal_never_reset_pin(&pin_GPIO39); @@ -291,10 +297,14 @@ void port_disable_tick(void) { esp_timer_stop(_tick_timer); } -void sleep_timer_cb(void* arg) { +void port_wake_main_task() { xTaskNotifyGive(circuitpython_task); } +void sleep_timer_cb(void* arg) { + port_wake_main_task(); +} + void port_interrupt_after_ticks(uint32_t ticks) { uint64_t timeout_us = ticks * 1000000ull / 1024; if (esp_timer_start_once(_sleep_timer, timeout_us) != ESP_OK) { diff --git a/supervisor/port.h b/supervisor/port.h index 862400986..812cf715b 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -99,4 +99,8 @@ void port_background_task(void); void port_start_background_task(void); void port_finish_background_task(void); +// Some ports need special handling to wake the main task from an interrupt +// context or other task. The port must implement the necessary code in this +// function. A default weak implementation is provided that does nothing. +void port_wake_main_task(void); #endif // MICROPY_INCLUDED_SUPERVISOR_PORT_H diff --git a/supervisor/shared/background_callback.c b/supervisor/shared/background_callback.c index ef686cbab..288c9a4df 100644 --- a/supervisor/shared/background_callback.c +++ b/supervisor/shared/background_callback.c @@ -38,6 +38,8 @@ STATIC volatile background_callback_t *callback_head, *callback_tail; #define CALLBACK_CRITICAL_BEGIN (common_hal_mcu_disable_interrupts()) #define CALLBACK_CRITICAL_END (common_hal_mcu_enable_interrupts()) +MP_WEAK void port_wake_main_task(void) {} + void background_callback_add_core(background_callback_t *cb) { CALLBACK_CRITICAL_BEGIN; if (cb->prev || callback_head == cb) { @@ -55,6 +57,8 @@ void background_callback_add_core(background_callback_t *cb) { } callback_tail = cb; CALLBACK_CRITICAL_END; + + port_wake_main_task(); } void background_callback_add(background_callback_t *cb, background_callback_fun fun, void *data) { -- cgit v1.2.3