diff options
Diffstat (limited to 'ports')
246 files changed, 3790 insertions, 1881 deletions
diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 6870667436..d258b27b1d 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -21,6 +21,7 @@ Reset\n\ Exit ALIF_TOC_CONFIG = alif_cfg.json +ALIF_TOC_BIN = $(BUILD)/firmware.toc.bin ALIF_TOC_APPS = $(BUILD)/$(ALIF_TOC_CONFIG) ALIF_TOC_CFLAGS += -DTOC_CFG_FILE=$(ALIF_TOOLKIT_CFG_FILE) @@ -69,7 +70,7 @@ include $(TOP)/extmod/extmod.mk # Main targets .PHONY: all -all: $(BUILD)/firmware.toc.bin +all: $(BUILD)/firmware.zip # Force make commands to run the targets every time # regardless of whether firmware.toc.bin already exists @@ -81,24 +82,28 @@ $(BUILD): $(MKDIR) -p $@ $(BUILD)/M55_HP/firmware.bin: - make -f alif.mk MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0 + make -f alif.mk BUILD=$(BUILD)/M55_HP MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0 $(BUILD)/M55_HE/firmware.bin: - make -f alif.mk MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1 + make -f alif.mk BUILD=$(BUILD)/M55_HE MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1 $(BUILD)/$(ALIF_TOC_CONFIG): mcu/$(ALIF_TOC_CONFIG).in | $(BUILD) $(ECHO) "Preprocess toc config $@" $(Q)$(CPP) -P -E $(ALIF_TOC_CFLAGS) - < mcu/$(ALIF_TOC_CONFIG).in > $@ -$(BUILD)/firmware.toc.bin: $(ALIF_TOC_APPS) +$(ALIF_TOC_BIN): $(ALIF_TOC_APPS) $(Q)python $(ALIF_TOOLS)/app-gen-toc.py \ --filename $(abspath $(BUILD)/$(ALIF_TOC_CONFIG)) \ --output-dir $(BUILD) \ --firmware-dir $(BUILD) \ --output $@ +$(BUILD)/firmware.zip: $(ALIF_TOC_BIN) $(ALIF_TOC_APPS) + $(ECHO) "Create $@" + $(Q)$(ZIP) -q - $(BUILD)/application_package.ds $^ > $@ + .PHONY: deploy -deploy: $(BUILD)/firmware.toc.bin +deploy: $(ALIF_TOC_BIN) $(ECHO) "Writing $< to the board" $(Q)python $(ALIF_TOOLS)/app-write-mram.py \ --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ diff --git a/ports/alif/README.md b/ports/alif/README.md index 824a63da18..dcf327060f 100644 --- a/ports/alif/README.md +++ b/ports/alif/README.md @@ -19,3 +19,61 @@ The following more advanced features will follow later: - Ethernet support. - SDRAM support. - Other machine modules. + +Build instructions +------------------ + +Before building the firmware for a given board the MicroPython cross-compiler +must be built; it will be used to pre-compile some of the built-in scripts to +bytecode. The cross-compiler is built and run on the host machine, using: +```bash +$ make -C mpy-cross +``` + +This command should be executed from the root directory of this repository. +All other commands below should be executed from the ports/alif/ directory. + +An ARM compiler is required for the build, along with the associated binary +utilities. The recommended toolchain version to use with this port is +Arm GNU toolchain version 13.3.Rel1. The compiler can be changed using the +`CROSS_COMPILE` variable when invoking `make`. + +Next, the board to build must be selected. The default board is `ALIF_ENSEMBLE` +but any of the names of the subdirectories in the `boards/` directory is valid. +The board name must be passed as the argument to `BOARD=` when invoking `make`. + +All boards require certain submodules to be obtained before they can be built. +The correct set of submodules can be initialised using (with `ALIF_ENSEMBLE` +as an example of the selected board): +```bash +make BOARD=ALIF_ENSEMBLE submodules +``` + +Then to build the board's firmware run: +```bash +make BOARD=ALIF_ENSEMBLE +``` + +The above command should produce binary images in the `build-ALIF_ENSEMBLE/` +subdirectory (or the equivalent directory for the board specified). + +### Update the SE Firmware + +The SE firmware must be updated **before** flashing the main firmware to match +the version used by MicroPython. This step only needs to be performed once. +Connect the board to your PC via the **SE UART USB** port (on the Ensemble kit, +this is labeled **PRG USB**), then run: + +```bash +make update-system-package +``` + +**Note:** The board must be power-cycled after this step. + +### Deploy MicroPython + +To flash the firmware, run: + +```bash +make BOARD=ALIF_ENSEMBLE deploy +``` diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index eee27b3b7d..265418aa07 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -3,7 +3,6 @@ BOARD ?= ALIF_ENSEMBLE BOARD_DIR ?= boards/$(BOARD) -BUILD ?= build-$(BOARD)/$(MCU_CORE) ifeq ($(wildcard $(BOARD_DIR)/.),) $(error Invalid BOARD specified: $(BOARD_DIR)) @@ -103,10 +102,6 @@ CFLAGS += -Wl,-T$(BUILD)/ensemble.ld \ -Wl,--print-memory-usage \ -Wl,--no-warn-rwx-segment -ifeq ($(MCU_CORE),M55_HP) -CFLAGS += -Wl,--wrap=dcd_event_handler -endif - ################################################################################ # Source files and libraries diff --git a/ports/alif/boards/ALIF_ENSEMBLE/board.json b/ports/alif/boards/ALIF_ENSEMBLE/board.json new file mode 100644 index 0000000000..0c63494f7a --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/board.json @@ -0,0 +1,17 @@ +{ + "deploy": [ + "./deploy.md" + ], + "docs": "", + "features": [ + "Ethernet" + ], + "images": [ + "ensemble-devkit-gen-2.jpg" + ], + "mcu": "AE722F80F55D5XX", + "product": "Ensemble E7 DevKit", + "thumbnail": "", + "url": "https://alifsemi.com/support/kits/ensemble-devkit/", + "vendor": "Alif Semiconductor" +} diff --git a/ports/alif/boards/ALIF_ENSEMBLE/deploy.md b/ports/alif/boards/ALIF_ENSEMBLE/deploy.md new file mode 100644 index 0000000000..acbc85617e --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/deploy.md @@ -0,0 +1,37 @@ +### Alif Security Toolkit + +This board can be programmed via the SE UART interface using the Alif Security +Toolkit. MicroPython uses a custom version of the toolkit, which can be downloaded +from [here](https://github.com/micropython/alif-security-toolkit/) or cloned: + +```bash +git clone https://github.com/micropython/alif-security-toolkit/ +``` + +--- + +### Update the SE Firmware (Optional) + +If needed, update the SE firmware to match the version used by MicroPython. Ensure +you have the correct port, part number, and chip revision. + +```bash +python alif-security-toolkit/toolkit/updateSystemPackage.py --port /dev/ttyACM0 --cfg-part AE722F80F55D5LS --cfg-rev B4 +``` + +**Note:** The board must be power-cycled after this step. + +--- + +### Deploy MicroPython + +Download (or build) the firmware package, unzip it, then deploy it: + +```bash + +python alif-security-toolkit/toolkit/app-write-mram.py --port /dev/ttyACM0 --cfg-part AE722F80F55D5LS --cfg-rev B4 --pad --images file:build-ALIF_ENSEMBLE/application_package.ds +``` + +The MicroPython REPL is available on the second USB serial port, eg `/dev/ttyACM1`. + +--- diff --git a/ports/alif/cyw43_configport.h b/ports/alif/cyw43_configport.h index 4b5cce6713..bfd4364ab9 100644 --- a/ports/alif/cyw43_configport.h +++ b/ports/alif/cyw43_configport.h @@ -27,13 +27,8 @@ #define MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H // The board-level config will be included here, so it can set some CYW43 values. -#include "py/mpconfig.h" -#include "py/mperrno.h" -#include "py/mphal.h" -#include "py/runtime.h" -#include "extmod/modnetwork.h" #include "extmod/mpbthci.h" -#include "pendsv.h" +#include "extmod/cyw43_config_common.h" #ifndef static_assert #define static_assert(expr, msg) typedef int static_assert_##__LINE__[(expr) ? 1 : -1] @@ -53,51 +48,15 @@ #define CYW43_WARN(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) #endif -#define CYW43_IOCTL_TIMEOUT_US (1000000) -#define CYW43_SLEEP_MAX (50) -#define CYW43_NETUTILS (1) #define CYW43_CLEAR_SDIO_INT (1) -#define CYW43_EPERM MP_EPERM // Operation not permitted -#define CYW43_EIO MP_EIO // I/O error -#define CYW43_EINVAL MP_EINVAL // Invalid argument -#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out - -#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER -#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT -#define CYW43_THREAD_LOCK_CHECK - -#define CYW43_HOST_NAME mod_network_hostname_data - #define CYW43_SDPCM_SEND_COMMON_WAIT __WFE() #define CYW43_DO_IOCTL_WAIT // __WFE() -#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait() - -#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) - -#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT -#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT -#define CYW43_HAL_PIN_PULL_NONE 0 - -#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 -#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR - -#define cyw43_hal_ticks_us mp_hal_ticks_us -#define cyw43_hal_ticks_ms mp_hal_ticks_ms - -#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t -#define cyw43_hal_pin_read mp_hal_pin_read -#define cyw43_hal_pin_low mp_hal_pin_low -#define cyw43_hal_pin_high mp_hal_pin_high #define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate #define cyw43_hal_uart_write mp_bluetooth_hci_uart_write #define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar -#define cyw43_hal_get_mac mp_hal_get_mac -#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii -#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac - #define CYW43_PIN_WL_REG_ON pin_WL_REG_ON #define CYW43_PIN_WL_IRQ pin_WL_IRQ @@ -110,15 +69,7 @@ void cyw43_post_poll_hook(void); -static inline void cyw43_delay_us(uint32_t us) { - uint32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - } -} - -static inline void cyw43_delay_ms(uint32_t ms) { - mp_hal_delay_ms(ms); -} +#undef cyw43_hal_pin_config // mp_hal_pin_config on alif port is not API compatible static inline void cyw43_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uint32_t pull, uint32_t alt) { if (mode == MP_HAL_PIN_MODE_INPUT) { @@ -127,7 +78,6 @@ static inline void cyw43_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uin mp_hal_pin_output(pin); } } - static inline void cyw43_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool enable) { mp_hal_pin_config_irq_falling(pin, enable); } diff --git a/ports/alif/irq.h b/ports/alif/irq.h index 08b8ef8086..02df524a49 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -48,6 +48,7 @@ #define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 7, 0) #define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0) #define IRQ_PRI_GPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0) +#define IRQ_PRI_GPIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 50, 0) #define IRQ_PRI_RTC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 100, 0) #define IRQ_PRI_CYW43 NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 126, 0) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) diff --git a/ports/alif/machine_pin.c b/ports/alif/machine_pin.c index 02c5e482ad..b8773f103f 100644 --- a/ports/alif/machine_pin.c +++ b/ports/alif/machine_pin.c @@ -30,8 +30,76 @@ #include "extmod/virtpin.h" #include "shared/runtime/mpirq.h" +#ifndef MACHINE_PIN_NUM_VECTORS +#define MACHINE_PIN_NUM_PORT_IO (8) +#define MACHINE_PIN_NUM_VECTORS (16 * MACHINE_PIN_NUM_PORT_IO) +#endif + +typedef struct _machine_pin_irq_obj_t { + mp_irq_obj_t base; + uint32_t flags; + uint32_t trigger; + IRQn_Type irq_num; + bool reserved; // for use by other drivers +} machine_pin_irq_obj_t; + +#define MACHINE_PIN_IRQ_INDEX(port, pin) \ + ((port) * MACHINE_PIN_NUM_PORT_IO + (pin)) + +#define MACHINE_PIN_IRQ_OBJECT(port, pin) \ + (MP_STATE_PORT(machine_pin_irq_obj[MACHINE_PIN_IRQ_INDEX((port), (pin))])) + +// Defines a single GPIO IRQ handler +#define DEFINE_GPIO_IRQ_HANDLER(pname, port, pin) \ + void pname##_IRQ##pin##Handler(void) { \ + machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(port, pin); \ + machine_pin_obj_t *self = MP_OBJ_TO_PTR(irq->base.parent); \ + gpio_interrupt_eoi(self->gpio, pin); \ + irq->flags = irq->trigger; \ + mp_irq_handler(&irq->base); \ + } + +// Defines all 8 pin IRQ handlers for a port +#define DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(gpio, port) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 0) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 1) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 2) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 3) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 4) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 5) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 6) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 7) + +// Generate handlers for GPIO ports 0 to 14 + LPGPIO +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO0, 0) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO1, 1) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO2, 2) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO3, 3) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO4, 4) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO5, 5) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO6, 6) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO7, 7) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO8, 8) + +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 0) +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 1) +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 2) +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 3) +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 4) +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 5) +// DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 6) // Reserved for WiFi +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 7) + +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO10, 10) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO11, 11) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO12, 12) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO13, 13) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO14, 14) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(LPGPIO, 15) + extern const mp_obj_dict_t machine_pin_cpu_pins_locals_dict; extern const mp_obj_dict_t machine_pin_board_pins_locals_dict; +static const mp_irq_methods_t machine_pin_irq_methods; static const machine_pin_obj_t *machine_pin_find_named(const mp_obj_dict_t *named_pins, mp_obj_t name) { const mp_map_t *named_map = &named_pins->map; @@ -171,6 +239,7 @@ mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args); } + return MP_OBJ_FROM_PTR(self); } @@ -225,6 +294,129 @@ static mp_obj_t machine_pin_toggle(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle); +static mp_uint_t machine_pin_irq_trigger(mp_obj_t self_in, mp_uint_t trigger) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(self->port, self->pin); + + irq->flags = 0; + irq->trigger = trigger; + + // Disable IRQs. + gpio_disable_interrupt(self->gpio, self->pin); + gpio_mask_interrupt(self->gpio, self->pin); + + NVIC_ClearPendingIRQ(irq->irq_num); + NVIC_DisableIRQ(irq->irq_num); + + // Return if the trigger is disabled. + if (trigger == 0) { + return 0; + } + + // Clear and enable GPIO IRQ. + gpio_enable_interrupt(self->gpio, self->pin); + gpio_unmask_interrupt(self->gpio, self->pin); + + // Clear GPIO config. + self->gpio->GPIO_INT_BOTHEDGE &= ~(1 << self->pin); + self->gpio->GPIO_INT_POLARITY &= ~(1 << self->pin); + self->gpio->GPIO_INTTYPE_LEVEL &= ~(1 << self->pin); + + // Configure GPIO IRQ trigger + if (trigger == MP_HAL_PIN_TRIGGER_FALL) { + gpio_interrupt_set_edge_trigger(self->gpio, self->pin); + gpio_interrupt_set_polarity_low(self->gpio, self->pin); + } else if (trigger == MP_HAL_PIN_TRIGGER_RISE) { + gpio_interrupt_set_edge_trigger(self->gpio, self->pin); + gpio_interrupt_set_polarity_high(self->gpio, self->pin); + } else if (trigger == (MP_HAL_PIN_TRIGGER_FALL | MP_HAL_PIN_TRIGGER_RISE)) { + gpio_interrupt_set_both_edge_trigger(self->gpio, self->pin); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("Invalid IRQ trigger")); + } + + // Clear GPIO IRQ (must be done after configuring trigger) + gpio_interrupt_eoi(self->gpio, self->pin); + + // Clear and enable NVIC GPIO IRQ. + NVIC_ClearPendingIRQ(irq->irq_num); + NVIC_SetPriority(irq->irq_num, IRQ_PRI_GPIO); + NVIC_EnableIRQ(irq->irq_num); + + return 0; +} + +static mp_uint_t machine_pin_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(self->port, self->pin); + + if (info_type == MP_IRQ_INFO_FLAGS) { + return irq->flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return irq->trigger; + } + return 0; +} + +static const mp_irq_methods_t machine_pin_irq_methods = { + .trigger = machine_pin_irq_trigger, + .info = machine_pin_irq_info, +}; + +// pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING, hard=False) +static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_handler, ARG_trigger, ARG_hard }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_trigger, MP_ARG_INT, {.u_int = MP_HAL_PIN_TRIGGER_FALL | MP_HAL_PIN_TRIGGER_RISE} }, + { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} }, + }; + + machine_pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(self->port, self->pin); + + // Allocate a new IRQ object if it doesn't exist. + if (irq == NULL) { + irq = m_new_obj(machine_pin_irq_obj_t); + uint32_t idx = MACHINE_PIN_IRQ_INDEX(self->port, self->pin); + + irq->base.base.type = &mp_irq_type; + irq->base.methods = (mp_irq_methods_t *)&machine_pin_irq_methods; + irq->base.parent = MP_OBJ_FROM_PTR(self); + irq->base.handler = mp_const_none; + irq->base.ishard = false; + irq->reserved = false; + irq->irq_num = (self->port < 15) ? (GPIO0_IRQ0_IRQn + idx) : (LPGPIO_IRQ0_IRQn + self->pin); + MP_STATE_PORT(machine_pin_irq_obj[idx]) = irq; + } + + if (n_args > 1 || kw_args->used != 0) { + if (irq->reserved) { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Pin IRQ is reserved")); + } + irq->base.handler = args[ARG_handler].u_obj; + irq->base.ishard = args[ARG_hard].u_bool; + machine_pin_irq_trigger(self, args[ARG_trigger].u_int); + } + + return MP_OBJ_FROM_PTR(irq); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq); + +void machine_pin_irq_deinit(void) { + for (size_t i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(machine_pin_irq_obj)); i++) { + machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_obj[i]); + if (irq != NULL) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(irq->base.parent); + machine_pin_irq_trigger(self, 0); + MP_STATE_PORT(machine_pin_irq_obj[i]) = NULL; + } + } +} + static MP_DEFINE_CONST_OBJ_TYPE( pin_cpu_pins_obj_type, MP_QSTR_cpu, @@ -248,6 +440,7 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_low_obj) }, { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&machine_pin_high_obj) }, { MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) }, + { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_pin_irq_obj) }, // class attributes { MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&pin_board_pins_obj_type) }, @@ -259,6 +452,8 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(MP_HAL_PIN_MODE_OPEN_DRAIN) }, { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(MP_HAL_PIN_PULL_UP) }, { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(MP_HAL_PIN_PULL_DOWN) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(MP_HAL_PIN_TRIGGER_FALL) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(MP_HAL_PIN_TRIGGER_RISE) }, }; static MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table); @@ -296,3 +491,5 @@ MP_DEFINE_CONST_OBJ_TYPE( mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t obj) { return machine_pin_find(obj); } + +MP_REGISTER_ROOT_POINTER(void *machine_pin_irq_obj[MACHINE_PIN_NUM_VECTORS]); diff --git a/ports/alif/machine_spi.c b/ports/alif/machine_spi.c index abb60bc4e8..b2c14745cf 100644 --- a/ports/alif/machine_spi.c +++ b/ports/alif/machine_spi.c @@ -39,6 +39,7 @@ typedef struct _machine_spi_obj_t { uint8_t id; SPI_Type *inst; bool is_lp; + uint32_t bits; } machine_spi_obj_t; static machine_spi_obj_t machine_spi_obj[] = { @@ -246,6 +247,9 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n // Get static peripheral object. machine_spi_obj_t *self = &machine_spi_obj[spi_id]; + // Set args + self->bits = args[ARG_bits].u_int; + // here we would check the sck/mosi/miso pins and configure them, but it's not implemented if (args[ARG_sck].u_obj != MP_OBJ_NULL || args[ARG_mosi].u_obj != MP_OBJ_NULL || @@ -294,22 +298,50 @@ static void machine_spi_deinit(mp_obj_base_t *self_in) { } } +static void machine_spi_poll_flag(SPI_Type *spi, uint32_t flag, uint32_t timeout) { + mp_uint_t tick_start = mp_hal_ticks_ms(); + while (!(spi->SPI_SR & flag)) { + if (mp_hal_ticks_ms() - tick_start >= timeout) { + mp_raise_OSError(MP_ETIMEDOUT); + } + mp_event_handle_nowait(); + } +} + static void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; - spi_transfer_t spi_xfer = { - .tx_buff = src, - .tx_total_cnt = len, - .rx_buff = dest, - .rx_total_cnt = len, - .tx_default_val = 0xFF, - .tx_default_enable = true, - .mode = SPI_TMOD_TX_AND_RX, - }; - // TODO redo transfer_blocking to timeout and poll events. - if (!self->is_lp) { - spi_transfer_blocking(self->inst, &spi_xfer); - } else { - lpspi_transfer_blocking(self->inst, &spi_xfer); + volatile uint32_t *dr = self->inst->SPI_DR; + + spi_set_tmod(self->inst, SPI_TMOD_TX_AND_RX); + + for (size_t i = 0; i < len; i++) { + // Wait for space in the TX FIFO + machine_spi_poll_flag(self->inst, SPI_SR_TFNF, 100); + + // Send data + if (src == NULL) { + *dr = 0xFFFFFFFFU; + } else if (self->bits > 16) { + *dr = ((uint32_t *)src)[i]; + } else if (self->bits > 8) { + *dr = ((uint16_t *)src)[i]; + } else { + *dr = ((uint8_t *)src)[i]; + } + + // Wait for data in the RX FIFO + machine_spi_poll_flag(self->inst, SPI_SR_RFNE, 100); + + // Recv data + if (dest == NULL) { + (void)*dr; + } else if (self->bits > 16) { + ((uint32_t *)dest)[i] = *dr; + } else if (self->bits > 8) { + ((uint16_t *)dest)[i] = *dr; + } else { + ((uint8_t *)dest)[i] = *dr; + } } } diff --git a/ports/alif/main.c b/ports/alif/main.c index fd35604cbc..ab5e85d5b9 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -55,8 +55,9 @@ extern uint8_t __StackTop, __StackLimit; extern uint8_t __GcHeapStart, __GcHeapEnd; +extern void machine_pin_irq_deinit(void); -NORETURN void panic(const char *msg) { +MP_NORETURN void panic(const char *msg) { mp_hal_stdout_tx_strn("\nFATAL ERROR:\n", 14); mp_hal_stdout_tx_strn(msg, strlen(msg)); for (;;) { @@ -164,6 +165,7 @@ int main(void) { mp_bluetooth_deinit(); #endif soft_timer_deinit(); + machine_pin_irq_deinit(); gc_sweep_all(); mp_deinit(); } diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c index 9868abbeea..71b5be5193 100644 --- a/ports/alif/modmachine.c +++ b/ports/alif/modmachine.c @@ -47,11 +47,11 @@ static mp_obj_t mp_machine_unique_id(void) { return mp_obj_new_bytes(id, sizeof(id)); } -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { se_services_reset_soc(); } -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { __disable_irq(); MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); @@ -112,7 +112,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { #endif } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { #if MICROPY_HW_ENABLE_USBDEV mp_machine_enable_usb(false); #endif diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 6c9e6af635..ddfc551bf8 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -158,19 +158,11 @@ #define MICROPY_FATFS_MAX_SS (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES) #endif -#if MICROPY_PY_NETWORK_CYW43 -extern const struct _mp_obj_type_t mp_network_cyw43_type; -#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, -#else -#define MICROPY_HW_NIC_CYW43 -#endif - #ifndef MICROPY_BOARD_NETWORK_INTERFACES #define MICROPY_BOARD_NETWORK_INTERFACES #endif #define MICROPY_PORT_NETWORK_INTERFACES \ - MICROPY_HW_NIC_CYW43 \ MICROPY_BOARD_NETWORK_INTERFACES \ // Bluetooth code only runs in the scheduler, no locking/mutex required. diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 4f81439b54..f03dc7c1c1 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -90,6 +90,9 @@ extern ringbuf_t stdin_ringbuf; #define MP_HAL_PIN_SPEED_LOW (0) #define MP_HAL_PIN_SPEED_HIGH (PADCTRL_SLEW_RATE_FAST) +#define MP_HAL_PIN_TRIGGER_FALL (1) +#define MP_HAL_PIN_TRIGGER_RISE (2) + #define mp_hal_pin_obj_t const machine_pin_obj_t * #define MP_HAL_PIN_ALT(function, unit) (MP_HAL_PIN_ALT_MAKE((MP_HAL_PIN_ALT_##function), (unit))) diff --git a/ports/cc3200/misc/mperror.c b/ports/cc3200/misc/mperror.c index 6d6c0ff0ba..69f0a25371 100644 --- a/ports/cc3200/misc/mperror.c +++ b/ports/cc3200/misc/mperror.c @@ -159,7 +159,7 @@ void mperror_heartbeat_signal (void) { } } -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { #ifdef DEBUG if (msg != NULL) { // wait for 20ms diff --git a/ports/cc3200/misc/mperror.h b/ports/cc3200/misc/mperror.h index 1c3eb62697..30effbbf91 100644 --- a/ports/cc3200/misc/mperror.h +++ b/ports/cc3200/misc/mperror.h @@ -27,7 +27,7 @@ #ifndef MICROPY_INCLUDED_CC3200_MISC_MPERROR_H #define MICROPY_INCLUDED_CC3200_MISC_MPERROR_H -extern void NORETURN __fatal_error(const char *msg); +extern void MP_NORETURN __fatal_error(const char *msg); void mperror_init0 (void); void mperror_bootloader_check_reset_cause (void); diff --git a/ports/cc3200/mods/modmachine.c b/ports/cc3200/mods/modmachine.c index f7184df442..8986635ac6 100644 --- a/ports/cc3200/mods/modmachine.c +++ b/ports/cc3200/mods/modmachine.c @@ -93,7 +93,7 @@ extern OsiTaskHandle xSimpleLinkSpawnTaskHndl; /******************************************************************************/ // MicroPython bindings; -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { // disable wlan wlan_stop(SL_STOP_TIMEOUT_LONG); // reset the cpu and it's peripherals @@ -161,7 +161,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { pyb_sleep_sleep(); } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { pyb_sleep_deepsleep(); for (;;) { } diff --git a/ports/cc3200/mods/pybsleep.c b/ports/cc3200/mods/pybsleep.c index 9d04dd411d..291860de8d 100644 --- a/ports/cc3200/mods/pybsleep.c +++ b/ports/cc3200/mods/pybsleep.c @@ -136,7 +136,7 @@ static MP_DEFINE_CONST_OBJ_TYPE( ******************************************************************************/ static pyb_sleep_obj_t *pyb_sleep_find (mp_obj_t obj); static void pyb_sleep_flash_powerdown (void); -static NORETURN void pyb_sleep_suspend_enter (void); +static MP_NORETURN void pyb_sleep_suspend_enter (void); void pyb_sleep_suspend_exit (void); static void pyb_sleep_obj_wakeup (void); static void PRCMInterruptHandler (void); @@ -360,7 +360,7 @@ static void pyb_sleep_flash_powerdown (void) { MAP_SPICSDisable(SSPI_BASE); } -static NORETURN void pyb_sleep_suspend_enter (void) { +static MP_NORETURN void pyb_sleep_suspend_enter (void) { // enable full RAM retention MAP_PRCMSRAMRetentionEnable(PRCM_SRAM_COL_1 | PRCM_SRAM_COL_2 | PRCM_SRAM_COL_3 | PRCM_SRAM_COL_4, PRCM_SRAM_LPDS_RET); diff --git a/ports/esp32/README.md b/ports/esp32/README.md index d9115072a4..d8b55e45f3 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -31,7 +31,7 @@ manage the ESP32 microcontroller, as well as a way to manage the required build environment and toolchains needed to build the firmware. The ESP-IDF changes quickly and MicroPython only supports certain versions. -Currently MicroPython supports v5.2, v5.2.2, v5.3 and v5.4. +Currently MicroPython supports v5.2, v5.2.2, v5.3, v5.4 and v5.4.1. To install the ESP-IDF the full instructions can be found at the [Espressif Getting Started guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#installation-step-by-step). @@ -49,10 +49,10 @@ The steps to take are summarised below. To check out a copy of the IDF use git clone: ```bash -$ git clone -b v5.2.2 --recursive https://github.com/espressif/esp-idf.git +$ git clone -b v5.4.1 --recursive https://github.com/espressif/esp-idf.git ``` -You can replace `v5.2.2` with any other supported version. +You can replace `v5.4.1` with any other supported version. (You don't need a full recursive clone; see the `ci_esp32_setup` function in `tools/ci.sh` in this repository for more detailed set-up commands.) @@ -61,7 +61,7 @@ MicroPython and update the submodules using: ```bash $ cd esp-idf -$ git checkout v5.2.2 +$ git checkout v5.4.1 $ git submodule update --init --recursive ``` @@ -221,6 +221,30 @@ import machine antenna = machine.Pin(16, machine.Pin.OUT, value=0) ``` +Partition table and filesystem size +----------------------------------- + +ESP32 firmware contains a bootloader, partition table and main application +firmware, which are all stored in (external) SPI flash. The user filesystem +is also stored in the same SPI flash. By default, MicroPython does not have +a fixed entry in the ESP32 partition table for the filesystem. Instead it +will automatically determine the size of the SPI flash upon boot, and then -- +so long as there is no existing partition called "vfs" or "ffat" -- it will +create a partition for the filesystem called "vfs" which takes all of the +remaining flash between the end of the last defined partition up until the +end of flash. + +This means that firmware built for, say, a 4MiB flash will work on any +device that has at least 4MiB flash. The user "vfs" filesystem will then +take up as much space as possible. + +This auto-detection behaviour can be overridden: if the ESP32 partition +table does contain an entry called "vfs" or "ffat" then these are used for +the user filesystem and no automatic "vfs" partition is added. This is +useful in cases where only the MicroPython ESP32 application is flashed to +the device (and not the bootloader or partition table), eg when deploying +.uf2 files. + Defining a custom ESP32 board ----------------------------- diff --git a/ports/esp32/adc.c b/ports/esp32/adc.c index 83af6dce0b..d0e5e81330 100644 --- a/ports/esp32/adc.c +++ b/ports/esp32/adc.c @@ -26,83 +26,73 @@ */ #include "py/mphal.h" +#include "py/mperrno.h" #include "adc.h" -#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" #include "esp_adc/adc_cali_scheme.h" -#define DEFAULT_VREF 1100 +static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten); -void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits) { - if (bits < SOC_ADC_RTC_MIN_BITWIDTH && bits > SOC_ADC_RTC_MAX_BITWIDTH) { - // Invalid value for the current chip, raise exception in the switch below. - bits = -1; +void adc_is_init_guard(machine_adc_block_obj_t *self) { + if (!self->handle) { + mp_raise_OSError(MP_EPERM); } - switch (bits) { - case 9: - self->width = ADC_BITWIDTH_9; - break; - case 10: - self->width = ADC_BITWIDTH_10; - break; - case 11: - self->width = ADC_BITWIDTH_11; - break; - case 12: - self->width = ADC_BITWIDTH_12; - break; - case 13: - self->width = ADC_BITWIDTH_13; - break; - default: - mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); - } - self->bits = bits; +} - if (self->unit_id == ADC_UNIT_1) { - adc1_config_width(self->width); - } +esp_err_t apply_self_adc_channel_atten(const machine_adc_obj_t *self, uint8_t atten) { + adc_is_init_guard(self->block); + + adc_oneshot_chan_cfg_t config = { + .atten = atten, + .bitwidth = self->block->bitwidth, + }; + esp_err_t ret = adc_oneshot_config_channel(self->block->handle, self->channel_id, &config); + return ret; } mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id) { - int raw = 0; - if (self->unit_id == ADC_UNIT_1) { - raw = adc1_get_raw(channel_id); - } else { - #if (SOC_ADC_PERIPH_NUM >= 2) - check_esp_err(adc2_get_raw(channel_id, self->width, &raw)); - #endif - } - return raw; + adc_is_init_guard(self); + + int reading = 0; + adc_oneshot_read(self->handle, channel_id, &reading); + return reading; +} + +/* +During testing, it turned out that the function `adc_cali_raw_to_voltage` does not account for the lower resolution, +instead it expects the full resolution value as an argument, hence the scaling applied here +*/ +mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { + int raw = madcblock_read_helper(self, channel_id); + int uv = 0; + + check_esp_err(ensure_adc_calibration(self, atten)); + check_esp_err(adc_cali_raw_to_voltage(self->calib[atten], (raw << (ADC_WIDTH_MAX - self->bitwidth)), &uv)); + return (mp_int_t)uv * 1000; } static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten) { - if (self->handle[atten] != NULL) { + if (self->calib[atten] != NULL) { return ESP_OK; } + esp_err_t ret = ESP_OK; + #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED adc_cali_curve_fitting_config_t cali_config = { .unit_id = self->unit_id, .atten = atten, - .bitwidth = self->width, + .bitwidth = self->bitwidth, }; - return adc_cali_create_scheme_curve_fitting(&cali_config, &self->handle[atten]); + ret = adc_cali_create_scheme_curve_fitting(&cali_config, &self->calib[atten]); #else adc_cali_line_fitting_config_t cali_config = { .unit_id = self->unit_id, .atten = atten, - .bitwidth = self->width, + .bitwidth = self->bitwidth, }; - return adc_cali_create_scheme_line_fitting(&cali_config, &self->handle[atten]); + ret = adc_cali_create_scheme_line_fitting(&cali_config, &self->calib[atten]); #endif -} -mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { - int raw = madcblock_read_helper(self, channel_id); - int uv; - - check_esp_err(ensure_adc_calibration(self, atten)); - check_esp_err(adc_cali_raw_to_voltage(self->handle[atten], raw, &uv)); - - return (mp_int_t)uv * 1000; + return ret; } diff --git a/ports/esp32/adc.h b/ports/esp32/adc.h index 5688e0a29a..ebf7fcc21a 100644 --- a/ports/esp32/adc.h +++ b/ports/esp32/adc.h @@ -29,17 +29,45 @@ #define MICROPY_INCLUDED_ESP32_ADC_H #include "py/runtime.h" -#include "esp_adc_cal.h" +#include "esp_adc/adc_oneshot.h" #include "esp_adc/adc_cali_scheme.h" -#define ADC_ATTEN_MAX SOC_ADC_ATTEN_NUM +#define ADC_ATTEN_COUNT SOC_ADC_ATTEN_NUM +#define ADC_ATTEN_MIN ADC_ATTEN_DB_0 +#define ADC_ATTEN_MAX ADC_ATTEN_DB_11 + +/* +https://github.com/espressif/esp-idf/issues/13128 +https://github.com/espressif/esp-idf/blob/release/v5.2/components/soc/esp32s3/include/soc/soc_caps.h +https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32s2/03-errata-description/index.html +https://docs.espressif.com/projects/esp-idf/en/v4.2/esp32/api-reference/peripherals/adc.html + +Looks like only the original esp32 is capable of bitwidth adjustment, all others are stuck at 12 bits, +except the S2, which is locked at 13 bits, otherwise attenuation doesn't work. +*/ +#if CONFIG_IDF_TARGET_ESP32S2 + +#define ADC_WIDTH_MIN ADC_BITWIDTH_13 +#define ADC_WIDTH_MAX ADC_BITWIDTH_13 + +#elif CONFIG_IDF_TARGET_ESP32 + +#define ADC_WIDTH_MIN ADC_BITWIDTH_9 +#define ADC_WIDTH_MAX ADC_BITWIDTH_12 + +#else + +#define ADC_WIDTH_MIN ADC_BITWIDTH_12 +#define ADC_WIDTH_MAX ADC_BITWIDTH_12 + +#endif typedef struct _machine_adc_block_obj_t { mp_obj_base_t base; adc_unit_t unit_id; - mp_int_t bits; - adc_bits_width_t width; - adc_cali_handle_t handle[ADC_ATTEN_MAX]; + adc_oneshot_unit_handle_t handle; + adc_bitwidth_t bitwidth; + adc_cali_handle_t calib[ADC_ATTEN_COUNT]; } machine_adc_block_obj_t; typedef struct _machine_adc_obj_t { @@ -51,11 +79,18 @@ typedef struct _machine_adc_obj_t { extern machine_adc_block_obj_t madcblock_obj[]; -void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits); +esp_err_t apply_self_adc_channel_atten(const machine_adc_obj_t *self, uint8_t atten); + mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id); mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten); const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id); void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +mp_int_t mp_machine_adc_atten_get_helper(const machine_adc_obj_t *self); +void mp_machine_adc_atten_set_helper(const machine_adc_obj_t *self, mp_int_t atten); + +mp_int_t mp_machine_adc_width_get_helper(const machine_adc_obj_t *self); +void mp_machine_adc_block_width_set_helper(machine_adc_block_obj_t *self, mp_int_t width); + #endif // MICROPY_INCLUDED_ESP32_ADC_H diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json index 9d0016017f..936a498b5e 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json @@ -17,8 +17,8 @@ "ABX00092_01.iso_1000x750.jpg" ], "mcu": "esp32s3", - "product": "Arduino Nano ESP32", + "product": "Nano ESP32", "thumbnail": "", - "url": "https://store.arduino.cc/products/arduino-nano-esp32", + "url": "https://docs.arduino.cc/hardware/nano-esp32/", "vendor": "Arduino" } diff --git a/ports/esp32/boards/ESP32_GENERIC/board.md b/ports/esp32/boards/ESP32_GENERIC/board.md index 9346d18d84..06972e436f 100644 --- a/ports/esp32/boards/ESP32_GENERIC/board.md +++ b/ports/esp32/boards/ESP32_GENERIC/board.md @@ -1,5 +1,5 @@ The following files are firmware that should work on most ESP32-based boards -with 4MiB of flash, including WROOM WROVER, SOLO, PICO, and MINI modules. +with 4MiB or more of flash, including WROOM WROVER, SOLO, PICO, and MINI modules. This board has multiple variants available: diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/board.md b/ports/esp32/boards/ESP32_GENERIC_C3/board.md index 1604b879c5..6097eb85df 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C3/board.md +++ b/ports/esp32/boards/ESP32_GENERIC_C3/board.md @@ -1,6 +1,6 @@ The following files are firmware images that should work on most -ESP32-C3-based boards with 4MiB of flash, including WROOM and MINI modules, -that use the revision 3 silicon (or newer). +ESP32-C3-based boards with 4MiB or more of flash, including WROOM and MINI +modules, that use the revision 3 silicon (or newer). USB serial/JTAG support is enabled on pin 18 and 19. Note that this is not a full USB stack, the C3 just provides a CDC/ACM class serial diff --git a/ports/esp32/boards/ESP32_GENERIC_S2/board.md b/ports/esp32/boards/ESP32_GENERIC_S2/board.md index 3e46c02467..d065ecbbb9 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S2/board.md +++ b/ports/esp32/boards/ESP32_GENERIC_S2/board.md @@ -1,5 +1,5 @@ The following files are firmware that should work on most ESP32-S2-based -boards with 4MiB of flash, including WROOM, WROVER, and MINI modules. +boards with 4MiB or more of flash, including WROOM, WROVER, and MINI modules. This firmware supports configurations with and without SPIRAM (also known as PSRAM) and will auto-detect a connected SPIRAM chip at startup and allocate diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/board.json b/ports/esp32/boards/ESP32_GENERIC_S3/board.json index fd0c9edce0..7a546d35fc 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_S3/board.json @@ -21,7 +21,6 @@ "url": "https://www.espressif.com/en/products/modules", "vendor": "Espressif", "variants": { - "SPIRAM_OCT": "Support for Octal-SPIRAM", - "FLASH_4M": "4MiB flash" + "SPIRAM_OCT": "Support for Octal-SPIRAM" } } diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/board.md b/ports/esp32/boards/ESP32_GENERIC_S3/board.md index 16930c9193..68ce0917e0 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/board.md +++ b/ports/esp32/boards/ESP32_GENERIC_S3/board.md @@ -1,9 +1,7 @@ The following files are firmware that should work on most ESP32-S3-based -boards with 4/8MiB of flash, including WROOM and MINI modules. +boards with 4MiB or more of flash, including WROOM and MINI modules. This firmware supports configurations with and without SPIRAM (also known as PSRAM) and will auto-detect a connected SPIRAM chip at startup and allocate the MicroPython heap accordingly. However if your board has Octal SPIRAM, then use the "spiram-oct" variant. - -If your board has 4MiB flash (including ESP32-S3FH4R2 based ones with embedded flash), then use the "flash-4m" build. diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake deleted file mode 100644 index e832cdb60f..0000000000 --- a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake +++ /dev/null @@ -1,4 +0,0 @@ -set(SDKCONFIG_DEFAULTS - ${SDKCONFIG_DEFAULTS} - boards/ESP32_GENERIC_S3/sdkconfig.flash_4m -) diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board index 9839b0d300..369330682f 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board +++ b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board @@ -1,8 +1,2 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y - -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m deleted file mode 100644 index c967c46ae6..0000000000 --- a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_8MB= - -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB.csv" diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json new file mode 100644 index 0000000000..d2ada7cccd --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json @@ -0,0 +1,23 @@ +{ + "deploy": [ + "../deploy_nativeusb.md" + ], + "deploy_options": { + "flash_offset": "0" + }, + "docs": "", + "features": [ + "BLE", + "External Flash", + "RGB LED", + "WiFi" + ], + "images": [ + "GAR-PYBSTICK26-C3-mUSB-00.JPG" + ], + "mcu": "esp32c3", + "product": "PYBSTICK26_ESP32C3", + "thumbnail": "", + "url": "https://shop.mchobby.be/fr/pybstick/2505-pybstick26-esp32-c3-micropython-et-arduino-3232100025059.html", + "vendor": "McHobby" +} diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake new file mode 100644 index 0000000000..81cfff1d77 --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake @@ -0,0 +1,7 @@ +set(IDF_TARGET esp32c3) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble + boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board +) diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h new file mode 100644 index 0000000000..c9796d1f65 --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h @@ -0,0 +1,10 @@ +#define MICROPY_HW_BOARD_NAME "PYBSTICK26_ESP32C3" +#define MICROPY_HW_MCU_NAME "ESP32C3" +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "pybstick26_esp32c3" + +#define MICROPY_HW_I2C0_SCL (1) +#define MICROPY_HW_I2C0_SDA (0) + +#define MICROPY_HW_SPI1_MOSI (7) +#define MICROPY_HW_SPI1_MISO (2) +#define MICROPY_HW_SPI1_SCK (6) diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv new file mode 100644 index 0000000000..d786654903 --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv @@ -0,0 +1,13 @@ +GP0,GPIO0 +GP1,GPIO1 +GP2,GPIO2 +GP3,GPIO3 +GP4,GPIO4 +GP5,GPIO5 +GP6,GPIO6 +GP7,GPIO7 +GP8,GPIO8 +GP9,GPIO9 +GP10,GPIO10 +GP20,GPIO20 +GP21,GPIO21 diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board new file mode 100644 index 0000000000..70e5f1f0d1 --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board @@ -0,0 +1,4 @@ +CONFIG_ESP32C3_REV_MIN_3=y + +# Workaround for https://github.com/espressif/esp-idf/issues/14456 +CONFIG_ESP_SYSTEM_HW_STACK_GUARD=n diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json index d68a9baadd..7521c01a6b 100644 --- a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json @@ -19,8 +19,8 @@ "lilygo-ttgo-lora-32-v1-6.jpg" ], "mcu": "esp32", - "product": "LILYGO TTGO LoRa32", + "product": "TTGO LoRa32", "thumbnail": "", - "url": "http://www.lilygo.cn/prod_view.aspx?TypeId=50060&Id=1270&FId=t3:50060:3", + "url": "https://www.lilygo.cc/products/lora3", "vendor": "LILYGO" } diff --git a/ports/esp32/boards/M5STACK_ATOM/board.json b/ports/esp32/boards/M5STACK_ATOM/board.json index 3a1e7ce359..c3908a4779 100644 --- a/ports/esp32/boards/M5STACK_ATOM/board.json +++ b/ports/esp32/boards/M5STACK_ATOM/board.json @@ -18,8 +18,8 @@ "m5stack_atom.jpg" ], "mcu": "esp32", - "product": "M5 Stack Atom", + "product": "Atom", "thumbnail": "", - "url": "https://m5stack.com/", - "vendor": "M5 Stack" + "url": "https://shop.m5stack.com/products/atom-matrix-esp32-development-kit", + "vendor": "M5Stack" } diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json index fe0e97f9f9..26b1a4d54a 100644 --- a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json +++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json @@ -20,5 +20,5 @@ "product": "AtomS3 Lite", "thumbnail": "", "url": "https://shop.m5stack.com/products/atoms3-lite-esp32s3-dev-kit", - "vendor": "M5 Stack" + "vendor": "M5Stack" } diff --git a/ports/esp32/boards/M5STACK_NANOC6/board.json b/ports/esp32/boards/M5STACK_NANOC6/board.json index 087851ae5e..ddf3406c83 100644 --- a/ports/esp32/boards/M5STACK_NANOC6/board.json +++ b/ports/esp32/boards/M5STACK_NANOC6/board.json @@ -18,7 +18,7 @@ "m5stack_nanoc6.jpg" ], "mcu": "esp32c6", - "product": "M5Stack NanoC6", + "product": "NanoC6", "url": "https://shop.m5stack.com/products/m5stack-nanoc6-dev-kit", "vendor": "M5Stack" } diff --git a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json index 3eb9a5e0a7..198f1d4b6d 100644 --- a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json +++ b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json @@ -18,8 +18,8 @@ "ESP32-EVB_Rev_K1.png" ], "mcu": "esp32", - "product": "Olimex ESP32 EVB", + "product": "ESP32 EVB", "thumbnail": "", - "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB/open-source-hardware", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB", + "vendor": "Olimex" } diff --git a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json index cda26178d7..1b00d213c7 100644 --- a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json +++ b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json @@ -19,8 +19,8 @@ "ESP32-POE-ISO-1.jpg" ], "mcu": "esp32", - "product": "Olimex ESP32 POE", + "product": "ESP32 POE", "thumbnail": "", - "url": "https://www.olimex.com/", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE", + "vendor": "Olimex" } diff --git a/ports/esp32/boards/SIL_WESP32/board.json b/ports/esp32/boards/SIL_WESP32/board.json index 53a50f3286..7126fa7ea1 100644 --- a/ports/esp32/boards/SIL_WESP32/board.json +++ b/ports/esp32/boards/SIL_WESP32/board.json @@ -18,7 +18,7 @@ "wesp32-top.jpg" ], "mcu": "esp32", - "product": "SIL WESP32", + "product": "wESP32", "thumbnail": "", "url": "https://wesp32.com/", "vendor": "Silicognition" diff --git a/ports/esp32/boards/SIL_WESP32/sdkconfig.board b/ports/esp32/boards/SIL_WESP32/sdkconfig.board index 741a4db439..36155cd5e3 100644 --- a/ports/esp32/boards/SIL_WESP32/sdkconfig.board +++ b/ports/esp32/boards/SIL_WESP32/sdkconfig.board @@ -1,9 +1,6 @@ -# 16 MB flash +# 8 MB+ flash -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB= -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y -CONFIG_ESPTOOLPY_FLASHSIZE="16MB" +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y # Fast flash @@ -14,7 +11,7 @@ CONFIG_ESP32_REV_MIN_1=y # OTA CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB-ota.csv" +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiBplus-ota.csv" # Network name diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json new file mode 100644 index 0000000000..8bce1a0013 --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json @@ -0,0 +1,23 @@ +{ + "deploy": [ + "../deploy.md" + ], + "deploy_options": { + "flash_offset": "0x1000" + }, + "docs": "", + "features": [ + "BLE", + "External Flash", + "WiFi", + "USB-C" + ], + "images": [ + "19177-Sparkfun_IoT_Redboard-ESP32.jpg" + ], + "mcu": "esp32", + "product": "ESP32 / WROOM", + "thumbnail": "", + "url": "https://www.sparkfun.com/sparkfun-iot-redboard-esp32-development-board.html", + "vendor": "SparkFun" +} diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py new file mode 100644 index 0000000000..73446ecac9 --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py @@ -0,0 +1,2 @@ +include("$(PORT_DIR)/boards/manifest.py") +require("sdcard") diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake new file mode 100644 index 0000000000..0ffcc38c87 --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake @@ -0,0 +1,7 @@ +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble + boards/sdkconfig.240mhz +) + +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h new file mode 100644 index 0000000000..4fe888ac17 --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h @@ -0,0 +1,14 @@ +// Board and hardware specific configuration + +#define MICROPY_HW_BOARD_NAME "SparkFun IoT RedBoard ESP32" +#define MICROPY_HW_MCU_NAME "ESP32" + +// Enable UART REPL for modules that have an external USB-UART and don't use native USB. +#define MICROPY_HW_ENABLE_UART_REPL (1) + +#define MICROPY_HW_I2C0_SCL (22) +#define MICROPY_HW_I2C0_SDA (21) + +#define MICROPY_HW_SPI1_SCK (18) +#define MICROPY_HW_SPI1_MOSI (23) +#define MICROPY_HW_SPI1_MISO (19) diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv new file mode 100644 index 0000000000..d37c51af8f --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv @@ -0,0 +1,22 @@ +TX,GPIO1 +RX,GPIO3 +ALERT,GPIO4 +TCK,GPIO13 +TMS,GPIO14 +CS,GPIO5 +PICO,GPIO23 +POCI,GPIO19 +SCK,GPIO18 +SDA,GPIO21 +SCL,GPIO22 +A0,GPIO36 +A3,GPIO39 +A4,GPIO32 +A5,GPIO33 +A6,GPIO34 +A7,GPIO35 +LED,GPIO18 +LED_BLUE,GPIO18 +BLUE_LED,GPIO18 +RGB_LED,GPIO2 +NEOPIXEL,GPIO2 diff --git a/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board index 9ab58f215f..162de0da14 100644 --- a/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board +++ b/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board @@ -3,11 +3,6 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_SPIRAM_MEMTEST= -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv" - # LWIP CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS2" # end of LWIP diff --git a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board index 3ca0c4b243..3092e35598 100644 --- a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board +++ b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB= -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3" diff --git a/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board index d0355a94dc..25b8d7689d 100644 --- a/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board +++ b/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board @@ -3,11 +3,6 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_AFTER_NORESET=y CONFIG_SPIRAM_MEMTEST= -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3Neo" diff --git a/ports/esp32/boards/UM_NANOS3/sdkconfig.board b/ports/esp32/boards/UM_NANOS3/sdkconfig.board index 2a39c64337..e06f7a4245 100644 --- a/ports/esp32/boards/UM_NANOS3/sdkconfig.board +++ b/ports/esp32/boards/UM_NANOS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMNanoS3" diff --git a/ports/esp32/boards/UM_OMGS3/sdkconfig.board b/ports/esp32/boards/UM_OMGS3/sdkconfig.board index 84a8ce449e..8a0bf0b13a 100644 --- a/ports/esp32/boards/UM_OMGS3/sdkconfig.board +++ b/ports/esp32/boards/UM_OMGS3/sdkconfig.board @@ -2,12 +2,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_AFTER_NORESET=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMOMGS3" diff --git a/ports/esp32/boards/UM_PROS3/sdkconfig.board b/ports/esp32/boards/UM_PROS3/sdkconfig.board index 5752e03e60..75ca58d80b 100644 --- a/ports/esp32/boards/UM_PROS3/sdkconfig.board +++ b/ports/esp32/boards/UM_PROS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB= -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMProS3" diff --git a/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board b/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board index ef3f38af47..7d244fdc16 100644 --- a/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board +++ b/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board @@ -2,12 +2,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_AFTER_NORESET=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMRGBTouchMini" diff --git a/ports/esp32/boards/UM_TINYC6/sdkconfig.board b/ports/esp32/boards/UM_TINYC6/sdkconfig.board index 7917467b12..213d28d8b4 100644 --- a/ports/esp32/boards/UM_TINYC6/sdkconfig.board +++ b/ports/esp32/boards/UM_TINYC6/sdkconfig.board @@ -2,9 +2,4 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_AFTER_NORESET=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" diff --git a/ports/esp32/boards/UM_TINYS3/sdkconfig.board b/ports/esp32/boards/UM_TINYS3/sdkconfig.board index d1d19761a0..2474c5fb27 100644 --- a/ports/esp32/boards/UM_TINYS3/sdkconfig.board +++ b/ports/esp32/boards/UM_TINYS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyS3" diff --git a/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board b/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board index 1380e15ce8..10121d235d 100644 --- a/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board +++ b/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyWATCHS3" diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index 530db42711..30740af434 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -50,7 +50,6 @@ CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=2 CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=n -CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK=y # UDP CONFIG_LWIP_PPP_SUPPORT=y @@ -98,7 +97,7 @@ CONFIG_ULP_COPROC_RESERVE_MEM=2040 # For cmake build CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB.csv" +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiBplus.csv" # To reduce iRAM usage CONFIG_ESP32_WIFI_IRAM_OPT=n @@ -118,7 +117,6 @@ CONFIG_ADC_CAL_LUT_ENABLE=y CONFIG_UART_ISR_IN_IRAM=y # IDF 5 deprecated -CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y CONFIG_ETH_USE_SPI_ETHERNET=y diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index c54f5a5403..09b1203913 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -80,9 +80,7 @@ list(APPEND MICROPY_SOURCE_DRIVERS ${MICROPY_DIR}/drivers/dht/dht.c ) -list(APPEND GIT_SUBMODULES lib/tinyusb) if(MICROPY_PY_TINYUSB) - set(TINYUSB_SRC "${MICROPY_DIR}/lib/tinyusb/src") string(TOUPPER OPT_MCU_${IDF_TARGET} tusb_mcu) list(APPEND MICROPY_DEF_TINYUSB @@ -90,12 +88,6 @@ if(MICROPY_PY_TINYUSB) ) list(APPEND MICROPY_SOURCE_TINYUSB - ${TINYUSB_SRC}/tusb.c - ${TINYUSB_SRC}/common/tusb_fifo.c - ${TINYUSB_SRC}/device/usbd.c - ${TINYUSB_SRC}/device/usbd_control.c - ${TINYUSB_SRC}/class/cdc/cdc_device.c - ${TINYUSB_SRC}/portable/synopsys/dwc2/dcd_dwc2.c ${MICROPY_DIR}/shared/tinyusb/mp_usbd.c ${MICROPY_DIR}/shared/tinyusb/mp_usbd_cdc.c ${MICROPY_DIR}/shared/tinyusb/mp_usbd_descriptor.c @@ -103,13 +95,8 @@ if(MICROPY_PY_TINYUSB) ) list(APPEND MICROPY_INC_TINYUSB - ${TINYUSB_SRC} ${MICROPY_DIR}/shared/tinyusb/ ) - - list(APPEND MICROPY_LINK_TINYUSB - -Wl,--wrap=dcd_event_handler - ) endif() list(APPEND MICROPY_SOURCE_PORT @@ -251,6 +238,7 @@ endif() # Set compile options for this port. target_compile_definitions(${MICROPY_TARGET} PUBLIC + ${MICROPY_DEF_COMPONENT} ${MICROPY_DEF_CORE} ${MICROPY_DEF_BOARD} ${MICROPY_DEF_TINYUSB} @@ -263,15 +251,12 @@ target_compile_definitions(${MICROPY_TARGET} PUBLIC # Disable some warnings to keep the build output clean. target_compile_options(${MICROPY_TARGET} PUBLIC + ${MICROPY_COMPILE_COMPONENT} -Wno-clobbered -Wno-deprecated-declarations -Wno-missing-field-initializers ) -target_link_options(${MICROPY_TARGET} PUBLIC - ${MICROPY_LINK_TINYUSB} -) - # Additional include directories needed for private NimBLE headers. target_include_directories(${MICROPY_TARGET} PUBLIC ${IDF_PATH}/components/bt/host/nimble/nimble diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index be1725c370..02acaa22da 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -30,7 +30,7 @@ #include "py/mphal.h" #include "adc.h" -#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" #define ADCBLOCK1 (&madcblock_obj[0]) #define ADCBLOCK2 (&madcblock_obj[1]) @@ -126,20 +126,8 @@ static const machine_adc_obj_t madc_obj[] = { #endif }; -// These values are initialised to 0, which means the corresponding ADC channel is not initialised. -// The madc_atten_get/madc_atten_set functions store (atten+1) here so that the uninitialised state -// can be distinguished from the initialised state. static uint8_t madc_obj_atten[MP_ARRAY_SIZE(madc_obj)]; -static inline adc_atten_t madc_atten_get(const machine_adc_obj_t *self) { - uint8_t value = madc_obj_atten[self - &madc_obj[0]]; - return value == 0 ? ADC_ATTEN_MAX : value - 1; -} - -static inline void madc_atten_set(const machine_adc_obj_t *self, adc_atten_t atten) { - madc_obj_atten[self - &madc_obj[0]] = atten + 1; -} - const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id) { for (int i = 0; i < MP_ARRAY_SIZE(madc_obj); i++) { const machine_adc_obj_t *adc = &madc_obj[i]; @@ -152,22 +140,7 @@ const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_ static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { const machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, madc_atten_get(self)); -} - -static void madc_atten_helper(const machine_adc_obj_t *self, mp_int_t atten) { - esp_err_t err = ESP_FAIL; - if (self->block->unit_id == ADC_UNIT_1) { - err = adc1_config_channel_atten(self->channel_id, atten); - } else { - #if SOC_ADC_PERIPH_NUM >= 2 - err = adc2_config_channel_atten(self->channel_id, atten); - #endif - } - if (err != ESP_OK) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid atten")); - } - madc_atten_set(self, atten); + mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, mp_machine_adc_atten_get_helper(self)); } void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -182,18 +155,32 @@ void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_pos_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_int_t atten = args[ARG_atten].u_int; - if (atten != -1) { - madc_atten_helper(self, atten); - } else if (madc_atten_get(self) == ADC_ATTEN_MAX) { - madc_atten_helper(self, ADC_ATTEN_DB_0); + + if (!self->block->handle) { + adc_oneshot_unit_init_cfg_t init_config = { + .unit_id = self->block->unit_id + }; + check_esp_err(adc_oneshot_new_unit(&init_config, &self->block->handle)); } + + mp_int_t atten = args[ARG_atten].u_int; + mp_machine_adc_atten_set_helper(self, atten != -1 ? atten : ADC_ATTEN_MAX); + mp_machine_adc_block_width_set_helper(self->block, ADC_WIDTH_MAX); + apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self)); + } static void mp_machine_adc_init_helper(machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { madc_init_helper(self, n_pos_args, pos_args, kw_args); } +static void mp_machine_adc_deinit(machine_adc_obj_t *self) { + if (self->block->handle) { + check_esp_err(adc_oneshot_del_unit(self->block->handle)); + self->block->handle = NULL; + } +} + static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_pos_args, size_t n_kw_args, const mp_obj_t *args) { mp_arg_check_num(n_pos_args, n_kw_args, 1, MP_OBJ_FUN_ARGS_MAX, true); gpio_num_t gpio_id = machine_pin_get_id(args[0]); @@ -202,10 +189,6 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_pos_ mp_raise_ValueError(MP_ERROR_TEXT("invalid pin")); } - if (self->block->width == -1) { - madcblock_bits_helper(self->block, self->block->bits); - } - mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw_args, args + n_pos_args); madc_init_helper(self, n_pos_args - 1, args + 1, &kw_args); @@ -225,20 +208,46 @@ static mp_int_t mp_machine_adc_read(machine_adc_obj_t *self) { static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) { mp_uint_t raw = madcblock_read_helper(self->block, self->channel_id); // Scale raw reading to 16 bit value using a Taylor expansion (for 8 <= bits <= 16) - mp_int_t bits = self->block->bits; + mp_int_t bits = mp_machine_adc_width_get_helper(self); mp_uint_t u16 = raw << (16 - bits) | raw >> (2 * bits - 16); return u16; } static mp_int_t mp_machine_adc_read_uv(machine_adc_obj_t *self) { - adc_atten_t atten = madc_atten_get(self); - return madcblock_read_uv_helper(self->block, self->channel_id, atten); + return madcblock_read_uv_helper(self->block, self->channel_id, mp_machine_adc_atten_get_helper(self)); +} + +mp_int_t mp_machine_adc_atten_get_helper(const machine_adc_obj_t *self) { + uint8_t value = madc_obj_atten[self - &madc_obj[0]]; + return value == 0 ? ADC_ATTEN_MAX : value - 1; +} + +void mp_machine_adc_atten_set_helper(const machine_adc_obj_t *self, mp_int_t atten) { + if (atten < ADC_ATTEN_MIN || atten > ADC_ATTEN_MAX) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid attenuation")); + } + + madc_obj_atten[self - &madc_obj[0]] = atten + 1; } static void mp_machine_adc_atten_set(machine_adc_obj_t *self, mp_int_t atten) { - madc_atten_helper(self, atten); + mp_machine_adc_atten_set_helper(self, atten); + apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self)); +} + +mp_int_t mp_machine_adc_width_get_helper(const machine_adc_obj_t *self) { + return self->block->bitwidth; +} + +void mp_machine_adc_block_width_set_helper(machine_adc_block_obj_t *self, mp_int_t width) { + if (width < ADC_WIDTH_MIN || width > ADC_WIDTH_MAX) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid bit-width")); + } + + self->bitwidth = width; } static void mp_machine_adc_width_set(machine_adc_obj_t *self, mp_int_t width) { - madcblock_bits_helper(self->block, width); + mp_machine_adc_block_width_set_helper(self->block, width); + apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self)); } diff --git a/ports/esp32/machine_adc_block.c b/ports/esp32/machine_adc_block.c index a373603b7e..6b06b432c2 100644 --- a/ports/esp32/machine_adc_block.c +++ b/ports/esp32/machine_adc_block.c @@ -29,25 +29,21 @@ #include "py/mphal.h" #include "adc.h" -#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" machine_adc_block_obj_t madcblock_obj[] = { - {{&machine_adc_block_type}, ADC_UNIT_1, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}}, + {{&machine_adc_block_type}, ADC_UNIT_1, NULL, ADC_WIDTH_MAX, {0}}, #if SOC_ADC_PERIPH_NUM > 1 - {{&machine_adc_block_type}, ADC_UNIT_2, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}}, + {{&machine_adc_block_type}, ADC_UNIT_2, NULL, ADC_WIDTH_MAX, {0}}, #endif }; static void mp_machine_adc_block_print(const mp_print_t *print, machine_adc_block_obj_t *self) { - mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bits); + mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bitwidth); } static void mp_machine_adc_block_bits_set(machine_adc_block_obj_t *self, mp_int_t bits) { - if (bits != -1) { - madcblock_bits_helper(self, bits); - } else if (self->width == -1) { - madcblock_bits_helper(self, self->bits); - } + mp_machine_adc_block_width_set_helper(self, bits); } static machine_adc_block_obj_t *mp_machine_adc_block_get(mp_int_t unit) { diff --git a/ports/esp32/machine_i2c.c b/ports/esp32/machine_i2c.c index 12b86e4aa0..259101ee7e 100644 --- a/ports/esp32/machine_i2c.c +++ b/ports/esp32/machine_i2c.c @@ -35,9 +35,14 @@ #if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C #ifndef MICROPY_HW_I2C0_SCL +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 +#define MICROPY_HW_I2C0_SCL (GPIO_NUM_9) +#define MICROPY_HW_I2C0_SDA (GPIO_NUM_8) +#else #define MICROPY_HW_I2C0_SCL (GPIO_NUM_18) #define MICROPY_HW_I2C0_SDA (GPIO_NUM_19) #endif +#endif #ifndef MICROPY_HW_I2C1_SCL #if CONFIG_IDF_TARGET_ESP32 diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index d0c4ee1a7e..4ab79f0a26 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -325,14 +325,17 @@ static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_ mp_raise_ValueError(MP_ERROR_TEXT("bad wake value")); } + #if SOC_TOUCH_SENSOR_SUPPORTED if (machine_rtc_config.wake_on_touch) { // not compatible mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif if (!RTC_IS_VALID_EXT_PIN(index)) { mp_raise_ValueError(MP_ERROR_TEXT("invalid pin for wake")); } + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin == -1) { machine_rtc_config.ext0_pin = index; } else if (machine_rtc_config.ext0_pin != index) { @@ -341,10 +344,13 @@ static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_ machine_rtc_config.ext0_level = trigger == GPIO_INTR_LOW_LEVEL ? 0 : 1; machine_rtc_config.ext0_wake_types = wake; + #endif } else { + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin == index) { machine_rtc_config.ext0_pin = -1; } + #endif if (handler == mp_const_none) { handler = MP_OBJ_NULL; diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 7826769556..7cb84640ee 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -6,7 +6,8 @@ * Copyright (c) 2016-2021 Damien P. George * Copyright (c) 2018 Alan Dragomirecky * Copyright (c) 2020 Antoine Aubert - * Copyright (c) 2021 Ihor Nehrutsa + * Copyright (c) 2021, 2023-2025 Ihor Nehrutsa + * Copyright (c) 2024 Yoann Darche * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,79 +33,41 @@ #include <math.h> #include "py/mphal.h" -#include "driver/ledc.h" #include "esp_err.h" +#include "driver/ledc.h" +#include "soc/ledc_periph.h" #include "soc/gpio_sig_map.h" - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) #include "esp_clk_tree.h" -#endif - -#define PWM_DBG(...) -// #define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n"); - -// Total number of channels -#define PWM_CHANNEL_MAX (LEDC_SPEED_MODE_MAX * LEDC_CHANNEL_MAX) - -typedef struct _chan_t { - // Which channel has which GPIO pin assigned? - // (-1 if not assigned) - gpio_num_t pin; - // Which channel has which timer assigned? - // (-1 if not assigned) - int timer_idx; -} chan_t; +#include "py/mpprint.h" -// List of PWM channels -static chan_t chans[PWM_CHANNEL_MAX]; +#define debug_printf(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, " | %d at %s\n", __LINE__, __FILE__); -// channel_idx is an index (end-to-end sequential numbering) for all channels -// available on the chip and described in chans[] -#define CHANNEL_IDX(mode, channel) (mode * LEDC_CHANNEL_MAX + channel) -#define CHANNEL_IDX_TO_MODE(channel_idx) (channel_idx / LEDC_CHANNEL_MAX) -#define CHANNEL_IDX_TO_CHANNEL(channel_idx) (channel_idx % LEDC_CHANNEL_MAX) +#define FADE 0 -// Total number of timers -#define PWM_TIMER_MAX (LEDC_SPEED_MODE_MAX * LEDC_TIMER_MAX) +// 10-bit user interface resolution compatible with esp8266 PWM.duty() +#define UI_RES_10_BIT (10) +#define DUTY_10 UI_RES_10_BIT +// Maximum duty value on 10-bit resolution is 1024 but reduced to 1023 in UI +#define MAX_10_DUTY (1U << UI_RES_10_BIT) -// List of timer configs -static ledc_timer_config_t timers[PWM_TIMER_MAX]; +// 16-bit user interface resolution used in PWM.duty_u16() +#define UI_RES_16_BIT (16) +#define DUTY_16 UI_RES_16_BIT +// Maximum duty value on 16-bit resolution is 65536 but reduced to 65535 in UI +#define MAX_16_DUTY (1U << UI_RES_16_BIT) -// timer_idx is an index (end-to-end sequential numbering) for all timers -// available on the chip and configured in timers[] -#define TIMER_IDX(mode, timer) (mode * LEDC_TIMER_MAX + timer) -#define TIMER_IDX_TO_MODE(timer_idx) (timer_idx / LEDC_TIMER_MAX) -#define TIMER_IDX_TO_TIMER(timer_idx) (timer_idx % LEDC_TIMER_MAX) +// ns user interface used in PWM.duty_ns() +#define DUTY_NS (1) -// Params for PWM operation // 5khz is default frequency -#define PWM_FREQ (5000) - -// 10-bit resolution (compatible with esp8266 PWM) -#define PWM_RES_10_BIT (LEDC_TIMER_10_BIT) - -// Maximum duty value on 10-bit resolution -#define MAX_DUTY_U10 ((1 << PWM_RES_10_BIT) - 1) -// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html#supported-range-of-frequency-and-duty-resolutions -// duty() uses 10-bit resolution or less -// duty_u16() and duty_ns() use 16-bit resolution or less - -// Possible highest resolution in device -#if (LEDC_TIMER_BIT_MAX - 1) < LEDC_TIMER_16_BIT -#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1) -#else -#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used -#endif -// Duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer -#define UI_RES_16_BIT (16) -// Maximum duty value on highest user interface resolution -#define UI_MAX_DUTY ((1 << UI_RES_16_BIT) - 1) -// How much to shift from the HIGHEST_PWM_RES duty resolution to the user interface duty resolution UI_RES_16_BIT -#define UI_RES_SHIFT (UI_RES_16_BIT - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3 - -#if SOC_LEDC_SUPPORT_REF_TICK +#define PWM_FREQ (5000) +// default duty 50% +#define PWM_DUTY ((1U << UI_RES_16_BIT) / 2) + +// All chips except esp32 and esp32s2 do not have timer-specific clock sources, which means clock source for all timers must be the same one. +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 // If the PWM frequency is less than EMPIRIC_FREQ, then LEDC_REF_CLK_HZ(1 MHz) source is used, else LEDC_APB_CLK_HZ(80 MHz) source is used -#define EMPIRIC_FREQ (10) // Hz +#define EMPIRIC_FREQ (10) // Hz #endif // Config of timer upon which we run all PWM'ed GPIO pins @@ -113,490 +76,675 @@ static bool pwm_inited = false; // MicroPython PWM object struct typedef struct _machine_pwm_obj_t { mp_obj_base_t base; - gpio_num_t pin; - bool active; - int mode; - int channel; - int timer; - int duty_x; // PWM_RES_10_BIT if duty(), HIGHEST_PWM_RES if duty_u16(), -HIGHEST_PWM_RES if duty_ns() - int duty_u10; // stored values from previous duty setters - int duty_u16; // - / - - int duty_ns; // - / - + int8_t pin; + int8_t mode; + int8_t channel; + int8_t timer; + bool lightsleep; + int32_t freq; + int8_t duty_scale; // DUTY_10 if duty(), DUTY_16 if duty_u16(), DUTY_NS if duty_ns() + int duty_ui; // saved values of UI duty + int channel_duty; // saved values of UI duty, calculated to raw channel->duty + bool output_invert; + bool output_invert_prev; } machine_pwm_obj_t; -static bool is_timer_in_use(int current_channel_idx, int timer_idx); -static void set_duty_u16(machine_pwm_obj_t *self, int duty); -static void set_duty_u10(machine_pwm_obj_t *self, int duty); -static void set_duty_ns(machine_pwm_obj_t *self, int ns); +typedef struct _chans_t { + int8_t pin; // Which channel has which GPIO pin assigned? (-1 if not assigned) + int8_t timer; // Which channel has which timer assigned? (-1 if not assigned) + bool lightsleep; // Is light sleep enable has been set for this pin +} chans_t; + +// List of PWM channels +static chans_t chans[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX]; + +typedef struct _timers_t { + int32_t freq; + int8_t duty_resolution; + ledc_clk_cfg_t clk_cfg; +} timers_t; + +// List of PWM timers +static timers_t timers[LEDC_SPEED_MODE_MAX][LEDC_TIMER_MAX]; + +// register-unregister channel +static void register_channel(int mode, int channel, int pin, int timer) { + chans[mode][channel].pin = pin; + chans[mode][channel].timer = timer; +} + +static void unregister_channel(int mode, int channel) { + register_channel(mode, channel, -1, -1); + chans[mode][channel].lightsleep = false; +} + +static void unregister_timer(int mode, int timer) { + timers[mode][timer].freq = -1; // unused timer freq is -1 + timers[mode][timer].duty_resolution = 0; + timers[mode][timer].clk_cfg = LEDC_AUTO_CLK; +} static void pwm_init(void) { - // Initial condition: no channels assigned - for (int i = 0; i < PWM_CHANNEL_MAX; ++i) { - chans[i].pin = -1; - chans[i].timer_idx = -1; + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + // Initial condition: no channels assigned + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + unregister_channel(mode, channel); + } + // Initial condition: no timers assigned + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + unregister_timer(mode, timer); + } } +} - // Prepare all timers config - // Initial condition: no timers assigned - for (int i = 0; i < PWM_TIMER_MAX; ++i) { - timers[i].duty_resolution = HIGHEST_PWM_RES; - // unset timer is -1 - timers[i].freq_hz = -1; - timers[i].speed_mode = TIMER_IDX_TO_MODE(i); - timers[i].timer_num = TIMER_IDX_TO_TIMER(i); - timers[i].clk_cfg = LEDC_AUTO_CLK; // will reinstall later according to the EMPIRIC_FREQ +// Returns true if the timer is in use in addition to current channel +static bool is_timer_in_use(int mode, int current_channel, int timer) { + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if ((channel != current_channel) && (chans[mode][channel].timer == timer) && (chans[mode][channel].pin >= 0)) { + return true; + } } + return false; } // Deinit channel and timer if the timer is unused -static void pwm_deinit(int channel_idx) { - // Valid channel? - if ((channel_idx >= 0) && (channel_idx < PWM_CHANNEL_MAX)) { +static void pwm_deinit(int mode, int channel, int level) { + // Is valid channel? + if ((mode >= 0) && (mode < LEDC_SPEED_MODE_MAX) && (channel >= 0) && (channel < LEDC_CHANNEL_MAX)) { // Clean up timer if necessary - int timer_idx = chans[channel_idx].timer_idx; - if (timer_idx != -1) { - if (!is_timer_in_use(channel_idx, timer_idx)) { - check_esp_err(ledc_timer_pause(TIMER_IDX_TO_MODE(timer_idx), TIMER_IDX_TO_TIMER(timer_idx))); - ledc_timer_config_t timer_config = { + int timer = chans[mode][channel].timer; + if (timer >= 0) { + if (!is_timer_in_use(mode, channel, timer)) { + check_esp_err(ledc_timer_pause(mode, timer)); + ledc_timer_config_t ledc_timer = { .deconfigure = true, - .speed_mode = TIMER_IDX_TO_MODE(timer_idx), - .timer_num = TIMER_IDX_TO_TIMER(timer_idx), + .speed_mode = mode, + .timer_num = timer, }; - check_esp_err(ledc_timer_config(&timer_config)); - // Flag it unused - timers[chans[channel_idx].timer_idx].freq_hz = -1; + ledc_timer_config(&ledc_timer); + unregister_timer(mode, timer); } } - int pin = chans[channel_idx].pin; - if (pin != -1) { - int mode = CHANNEL_IDX_TO_MODE(channel_idx); - int channel = CHANNEL_IDX_TO_CHANNEL(channel_idx); - // Mark it unused, and tell the hardware to stop routing - check_esp_err(ledc_stop(mode, channel, 0)); - // Disable ledc signal for the pin - // esp_rom_gpio_connect_out_signal(pin, SIG_GPIO_OUT_IDX, false, false); - if (mode == LEDC_LOW_SPEED_MODE) { - esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + channel, false, true); - } else { - #if LEDC_SPEED_MODE_MAX > 1 - #if CONFIG_IDF_TARGET_ESP32 - esp_rom_gpio_connect_out_signal(pin, LEDC_HS_SIG_OUT0_IDX + channel, false, true); - #else - #error Add supported CONFIG_IDF_TARGET_ESP32_xxx - #endif - #endif + int pin = chans[mode][channel].pin; + if (pin >= 0) { + // Disable LEDC output, and set idle level + check_esp_err(ledc_stop(mode, channel, level)); + if (chans[mode][channel].lightsleep) { + // Enable SLP_SEL to change GPIO status automantically in lightsleep. + check_esp_err(gpio_sleep_sel_en(pin)); + chans[mode][channel].lightsleep = false; } } - chans[channel_idx].pin = -1; - chans[channel_idx].timer_idx = -1; + unregister_channel(mode, channel); } } // This called from Ctrl-D soft reboot void machine_pwm_deinit_all(void) { if (pwm_inited) { - for (int channel_idx = 0; channel_idx < PWM_CHANNEL_MAX; ++channel_idx) { - pwm_deinit(channel_idx); + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + pwm_deinit(mode, channel, 0); + } } + #if FADE + ledc_fade_func_uninstall(); + #endif pwm_inited = false; } } -static void configure_channel(machine_pwm_obj_t *self) { - ledc_channel_config_t cfg = { - .channel = self->channel, - .duty = (1 << (timers[TIMER_IDX(self->mode, self->timer)].duty_resolution)) / 2, - .gpio_num = self->pin, - .intr_type = LEDC_INTR_DISABLE, - .speed_mode = self->mode, - .timer_sel = self->timer, - }; - if (ledc_channel_config(&cfg) != ESP_OK) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("PWM not supported on Pin(%d)"), self->pin); - } -} - -static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) { - esp_err_t err; - if (freq != timer->freq_hz) { - // Configure the new frequency and resolution - timer->freq_hz = freq; - - #if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK - timer->clk_cfg = LEDC_USE_PLL_DIV_CLK; - #elif SOC_LEDC_SUPPORT_APB_CLOCK - timer->clk_cfg = LEDC_USE_APB_CLK; - #elif SOC_LEDC_SUPPORT_XTAL_CLOCK - timer->clk_cfg = LEDC_USE_XTAL_CLK; - #else - #error No supported PWM / LEDC clocks. - #endif - #if SOC_LEDC_SUPPORT_REF_TICK - if (freq < EMPIRIC_FREQ) { - timer->clk_cfg = LEDC_USE_REF_TICK; - } - #endif - uint32_t src_clk_freq = 0; - err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_freq); - if (err != ESP_OK) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unable to query source clock frequency %d"), (int)timer->clk_cfg); - } - - timer->duty_resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer->freq_hz); - - // Set frequency - err = ledc_timer_config(timer); - if (err != ESP_OK) { - if (err == ESP_FAIL) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unreachable frequency %d"), freq); - } else { - check_esp_err(err); - } - } - // Reset the timer if low speed - if (self->mode == LEDC_LOW_SPEED_MODE) { - check_esp_err(ledc_timer_rst(self->mode, self->timer)); - } - } - - // Save the same duty cycle when frequency is changed - if (self->duty_x == HIGHEST_PWM_RES) { - set_duty_u16(self, self->duty_u16); - } else if (self->duty_x == PWM_RES_10_BIT) { - set_duty_u10(self, self->duty_u10); - } else if (self->duty_x == -HIGHEST_PWM_RES) { - set_duty_ns(self, self->duty_ns); +static void pwm_is_active(machine_pwm_obj_t *self) { + if (self->timer < 0) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("PWM is inactive")); } } // Calculate the duty parameters based on an ns value static int ns_to_duty(machine_pwm_obj_t *self, int ns) { - ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int64_t duty = ((int64_t)ns * UI_MAX_DUTY * timer.freq_hz + 500000000LL) / 1000000000LL; + pwm_is_active(self); + int64_t duty = ((int64_t)ns * (int64_t)MAX_16_DUTY * self->freq + 500000000LL) / 1000000000LL; if ((ns > 0) && (duty == 0)) { duty = 1; - } else if (duty > UI_MAX_DUTY) { - duty = UI_MAX_DUTY; + } else if (duty > MAX_16_DUTY) { + duty = MAX_16_DUTY; } return duty; } static int duty_to_ns(machine_pwm_obj_t *self, int duty) { - ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int64_t ns = ((int64_t)duty * 1000000000LL + (int64_t)timer.freq_hz * UI_MAX_DUTY / 2) / ((int64_t)timer.freq_hz * UI_MAX_DUTY); - return ns; + pwm_is_active(self); + return ((int64_t)duty * 1000000000LL + (int64_t)self->freq * (int64_t)(MAX_16_DUTY / 2)) / ((int64_t)self->freq * (int64_t)MAX_16_DUTY); } -#define get_duty_raw(self) ledc_get_duty(self->mode, self->channel) +// Reconfigure PWM pin output as input/output. +static void reconfigure_pin(machine_pwm_obj_t *self) { + #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0) + // This allows to read the pin level. + gpio_set_direction(self->pin, GPIO_MODE_INPUT_OUTPUT); + #endif + esp_rom_gpio_connect_out_signal(self->pin, ledc_periph_signal[self->mode].sig_out0_idx + self->channel, self->output_invert, 0); +} -static void pwm_is_active(machine_pwm_obj_t *self) { - if (self->active == false) { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("PWM inactive")); +static void apply_duty(machine_pwm_obj_t *self) { + pwm_is_active(self); + + int duty = 0; + if (self->duty_scale == DUTY_16) { + duty = self->duty_ui; + } else if (self->duty_scale == DUTY_10) { + duty = self->duty_ui << (UI_RES_16_BIT - UI_RES_10_BIT); + } else if (self->duty_scale == DUTY_NS) { + duty = ns_to_duty(self, self->duty_ui); + } + self->channel_duty = duty >> (UI_RES_16_BIT - timers[self->mode][self->timer].duty_resolution); + if ((chans[self->mode][self->channel].pin == -1) || (self->output_invert_prev != self->output_invert)) { + self->output_invert_prev = self->output_invert; + // New PWM assignment + ledc_channel_config_t cfg = { + .channel = self->channel, + .duty = self->channel_duty, + .gpio_num = self->pin, + .intr_type = LEDC_INTR_DISABLE, + .speed_mode = self->mode, + .timer_sel = self->timer, + .hpoint = 0, + .flags.output_invert = self->output_invert, + }; + check_esp_err(ledc_channel_config(&cfg)); + reconfigure_pin(self); + } else { + #if FADE + check_esp_err(ledc_set_duty_and_update(self->mode, self->channel, self->channel_duty, 0)); + #else + check_esp_err(ledc_set_duty(self->mode, self->channel, self->channel_duty)); + check_esp_err(ledc_update_duty(self->mode, self->channel)); + #endif + } + if (self->lightsleep) { + // Disable SLP_SEL to change GPIO status automantically in lightsleep. + check_esp_err(gpio_sleep_sel_dis(self->pin)); + chans[self->mode][self->channel].lightsleep = true; } + register_channel(self->mode, self->channel, self->pin, self->timer); +} + +static uint32_t find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) { + unsigned int resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer_freq); + if (resolution > UI_RES_16_BIT) { + // limit resolution to user interface + resolution = UI_RES_16_BIT; + } + // Note: On ESP32, ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32C6, ESP32H2, ESP32P4, due to a hardware bug, + // 100% duty cycle (i.e. 2**duty_res) is not reachable when the binded timer selects the maximum duty + // resolution. For example, the max duty resolution on ESP32C3 is 14-bit width, then set duty to (2**14) + // will mess up the duty calculation in hardware. + // Reduce the resolution from 14 to 13 bits to resolve the hardware bug. + if (resolution >= SOC_LEDC_TIMER_BIT_WIDTH) { + resolution -= 1; + } + return resolution; } static uint32_t get_duty_u16(machine_pwm_obj_t *self) { pwm_is_active(self); - int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution; - int duty = ledc_get_duty(self->mode, self->channel); - if (resolution <= UI_RES_16_BIT) { - duty <<= (UI_RES_16_BIT - resolution); + int duty = ledc_get_duty(self->mode, self->channel) << (UI_RES_16_BIT - timers[self->mode][self->timer].duty_resolution); + if (duty != MAX_16_DUTY) { + return duty; } else { - duty >>= (resolution - UI_RES_16_BIT); + return MAX_16_DUTY - 1; } - return duty; } static uint32_t get_duty_u10(machine_pwm_obj_t *self) { - pwm_is_active(self); - return get_duty_u16(self) >> 6; // Scale down from 16 bit to 10 bit resolution + // Scale down from 16 bit to 10 bit resolution + return get_duty_u16(self) >> (UI_RES_16_BIT - UI_RES_10_BIT); } static uint32_t get_duty_ns(machine_pwm_obj_t *self) { - pwm_is_active(self); return duty_to_ns(self, get_duty_u16(self)); } -static void set_duty_u16(machine_pwm_obj_t *self, int duty) { - pwm_is_active(self); - if ((duty < 0) || (duty > UI_MAX_DUTY)) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), UI_MAX_DUTY); +static void check_duty_u16(machine_pwm_obj_t *self, int duty) { + if ((duty < 0) || (duty > MAX_16_DUTY - 1)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), MAX_16_DUTY); } - ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int channel_duty; - if (timer.duty_resolution <= UI_RES_16_BIT) { - channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution); - } else { - channel_duty = duty << (timer.duty_resolution - UI_RES_16_BIT); + if (duty == MAX_16_DUTY - 1) { + duty = MAX_16_DUTY; } - int max_duty = (1 << timer.duty_resolution) - 1; - if (channel_duty < 0) { - channel_duty = 0; - } else if (channel_duty > max_duty) { - channel_duty = max_duty; - } - check_esp_err(ledc_set_duty(self->mode, self->channel, channel_duty)); - check_esp_err(ledc_update_duty(self->mode, self->channel)); + self->duty_scale = DUTY_16; + self->duty_ui = duty; +} - /* - // Bug: Sometimes duty is not set right now. - // Not a bug. It's a feature. The duty is applied at the beginning of the next signal period. - // Bug: It has been experimentally established that the duty is set during 2 signal periods, but 1 period is expected. - // See https://github.com/espressif/esp-idf/issues/7288 - if (duty != get_duty_u16(self)) { - PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz); - esp_rom_delay_us(2 * 1000000 / timer.freq_hz); - if (duty != get_duty_u16(self)) { - PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz); - } - } - */ +static void set_duty_u16(machine_pwm_obj_t *self, int duty) { + check_duty_u16(self, duty); + apply_duty(self); +} - self->duty_x = HIGHEST_PWM_RES; - self->duty_u16 = duty; +static void check_duty_u10(machine_pwm_obj_t *self, int duty) { + if ((duty < 0) || (duty > MAX_10_DUTY - 1)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_10_DUTY - 1); + } + if (duty == MAX_10_DUTY - 1) { + duty = MAX_10_DUTY; + } + self->duty_scale = DUTY_10; + self->duty_ui = duty; } static void set_duty_u10(machine_pwm_obj_t *self, int duty) { - pwm_is_active(self); - if ((duty < 0) || (duty > MAX_DUTY_U10)) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_DUTY_U10); + check_duty_u10(self, duty); + apply_duty(self); +} + +static void check_duty_ns(machine_pwm_obj_t *self, int ns) { + if ((ns < 0) || (ns > duty_to_ns(self, MAX_16_DUTY))) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, MAX_16_DUTY)); } - set_duty_u16(self, duty << (UI_RES_16_BIT - PWM_RES_10_BIT)); - self->duty_x = PWM_RES_10_BIT; - self->duty_u10 = duty; + self->duty_scale = DUTY_NS; + self->duty_ui = ns; } static void set_duty_ns(machine_pwm_obj_t *self, int ns) { - pwm_is_active(self); - if ((ns < 0) || (ns > duty_to_ns(self, UI_MAX_DUTY))) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, UI_MAX_DUTY)); + check_duty_ns(self, ns); + apply_duty(self); +} + +#if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) +// This check if a clock is already set in the timer list, if yes, return the LEDC_XXX value +static ledc_clk_cfg_t find_clock_in_use() { + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if (timers[mode][timer].clk_cfg != LEDC_AUTO_CLK) { + return timers[mode][timer].clk_cfg; + } + } } - set_duty_u16(self, ns_to_duty(self, ns)); - self->duty_x = -HIGHEST_PWM_RES; - self->duty_ns = ns; + return LEDC_AUTO_CLK; } -/******************************************************************************/ +// Check if a timer is already set with a different clock source +static bool is_timer_with_different_clock(int mode, int current_timer, ledc_clk_cfg_t clk_cfg) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if ((timer != current_timer) && (clk_cfg != LEDC_AUTO_CLK) && (timers[mode][timer].clk_cfg != LEDC_AUTO_CLK) && (timers[mode][timer].clk_cfg != clk_cfg)) { + return true; + } + } + return false; +} +#endif -#define SAME_FREQ_ONLY (true) -#define SAME_FREQ_OR_FREE (false) -#define ANY_MODE (-1) +static void check_freq_ranges(machine_pwm_obj_t *self, int freq, int upper_freq) { + if ((freq <= 0) || (freq > upper_freq)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("frequency must be from 1Hz to %dMHz"), upper_freq / 1000000); + } +} -// Return timer_idx. Use TIMER_IDX_TO_MODE(timer_idx) and TIMER_IDX_TO_TIMER(timer_idx) to get mode and timer -static int find_timer(unsigned int freq, bool same_freq_only, int mode) { - int free_timer_idx_found = -1; - // Find a free PWM Timer using the same freq - for (int timer_idx = 0; timer_idx < PWM_TIMER_MAX; ++timer_idx) { - if ((mode == ANY_MODE) || (mode == TIMER_IDX_TO_MODE(timer_idx))) { - if (timers[timer_idx].freq_hz == freq) { - // A timer already uses the same freq. Use it now. - return timer_idx; - } - if (!same_freq_only && (free_timer_idx_found == -1) && (timers[timer_idx].freq_hz == -1)) { - free_timer_idx_found = timer_idx; - // Continue to check if a channel with the same freq is in use. +// Set timer frequency +static void set_freq(machine_pwm_obj_t *self, unsigned int freq) { + self->freq = freq; + if ((timers[self->mode][self->timer].freq != freq) || (self->lightsleep)) { + ledc_timer_config_t timer = {}; + timer.speed_mode = self->mode; + timer.timer_num = self->timer; + timer.freq_hz = freq; + timer.deconfigure = false; + + timer.clk_cfg = LEDC_AUTO_CLK; + #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) + ledc_clk_cfg_t clk_cfg = find_clock_in_use(); + if (clk_cfg != LEDC_AUTO_CLK) { + timer.clk_cfg = clk_cfg; + } else + #endif + if (self->lightsleep) { + timer.clk_cfg = LEDC_USE_RC_FAST_CLK; // 8 or 20 MHz + } else { + #if SOC_LEDC_SUPPORT_APB_CLOCK + timer.clk_cfg = LEDC_USE_APB_CLK; // 80 MHz + #elif SOC_LEDC_SUPPORT_PLL_DIV_CLOCK + timer.clk_cfg = LEDC_USE_PLL_DIV_CLK; // 60 or 80 or 96 MHz + #elif SOC_LEDC_SUPPORT_XTAL_CLOCK + timer.clk_cfg = LEDC_USE_XTAL_CLK; // 40 MHz + #else + #error No supported PWM / LEDC clocks. + #endif + + #ifdef EMPIRIC_FREQ // ESP32 and ESP32S2 only + if (freq < EMPIRIC_FREQ) { + timer.clk_cfg = LEDC_USE_REF_TICK; // 1 MHz } + #endif } - } + #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) + // Check for clock source conflict + clk_cfg = find_clock_in_use(); + if ((clk_cfg != LEDC_AUTO_CLK) && (clk_cfg != timer.clk_cfg)) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("one or more active timers use a different clock source, not supported by the current SoC.")); + } + #endif - return free_timer_idx_found; + uint32_t src_clk_freq = 0; + check_esp_err(esp_clk_tree_src_get_freq_hz(timer.clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &src_clk_freq)); + // machine.freq(20_000_000) reduces APB_CLK_FREQ to 20MHz and the highest PWM frequency to 10MHz + check_freq_ranges(self, freq, src_clk_freq / 2); + + // Configure the new resolution + timer.duty_resolution = find_suitable_duty_resolution(src_clk_freq, self->freq); + // Configure timer - Set frequency + if ((timers[self->mode][self->timer].duty_resolution == timer.duty_resolution) && (timers[self->mode][self->timer].clk_cfg == timer.clk_cfg)) { + check_esp_err(ledc_set_freq(self->mode, self->timer, freq)); + } else { + check_esp_err(ledc_timer_config(&timer)); + } + // Reset the timer if low speed + if (self->mode == LEDC_LOW_SPEED_MODE) { + check_esp_err(ledc_timer_rst(self->mode, self->timer)); + } + timers[self->mode][self->timer].freq = freq; + timers[self->mode][self->timer].duty_resolution = timer.duty_resolution; + timers[self->mode][self->timer].clk_cfg = timer.clk_cfg; + } } -// Return true if the timer is in use in addition to current channel -static bool is_timer_in_use(int current_channel_idx, int timer_idx) { - for (int i = 0; i < PWM_CHANNEL_MAX; ++i) { - if ((i != current_channel_idx) && (chans[i].timer_idx == timer_idx)) { +static bool is_free_channels(int mode, int pin) { + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if ((chans[mode][channel].pin < 0) || (chans[mode][channel].pin == pin)) { return true; } } + return false; +} +static bool is_free_timers(int mode, int32_t freq) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if ((timers[mode][timer].freq < 0) || (timers[mode][timer].freq == freq)) { + return true; + } + } return false; } -// Find a free PWM channel, also spot if our pin is already mentioned. -// Return channel_idx. Use CHANNEL_IDX_TO_MODE(channel_idx) and CHANNEL_IDX_TO_CHANNEL(channel_idx) to get mode and channel -static int find_channel(int pin, int mode) { - int avail_idx = -1; - int channel_idx; - for (channel_idx = 0; channel_idx < PWM_CHANNEL_MAX; ++channel_idx) { - if ((mode == ANY_MODE) || (mode == CHANNEL_IDX_TO_MODE(channel_idx))) { - if (chans[channel_idx].pin == pin) { - break; +// Find self channel or free channel +static void find_channel(machine_pwm_obj_t *self, int *ret_mode, int *ret_channel, int32_t freq) { + // Try to find self channel first + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + #if SOC_LEDC_SUPPORT_HS_MODE + if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) { + continue; + } + #endif + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if (chans[mode][channel].pin == self->pin) { + *ret_mode = mode; + *ret_channel = channel; + return; } - if ((avail_idx == -1) && (chans[channel_idx].pin == -1)) { - avail_idx = channel_idx; + } + } + // Find free channel + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + #if SOC_LEDC_SUPPORT_HS_MODE + if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) { + continue; + } + #endif + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if ((chans[mode][channel].pin < 0) && is_free_timers(mode, freq)) { + *ret_mode = mode; + *ret_channel = channel; + return; } } } - if (channel_idx >= PWM_CHANNEL_MAX) { - channel_idx = avail_idx; +} + +// Returns timer with the same frequency, freq == -1 means free timer +static void find_timer(machine_pwm_obj_t *self, int freq, int *ret_mode, int *ret_timer) { + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + #if SOC_LEDC_SUPPORT_HS_MODE + if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) { + continue; + } + #endif + if (is_free_channels(mode, self->pin)) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if (timers[mode][timer].freq == freq) { + *ret_mode = mode; + *ret_timer = timer; + return; + } + } + } } - return channel_idx; } -/******************************************************************************/ +// Try to find a timer with the same frequency in the current mode, otherwise in the next mode. +// If no existing timer and channel was found, then try to find free timer in any mode. +// If the mode or channel is changed, release the channel and register a new channel in the next mode. +static void select_timer(machine_pwm_obj_t *self, int freq) { + // mode, channel, timer may be -1(not defined) or actual values + int mode = -1; + int timer = -1; + // Check if an already running timer with the required frequency is running + find_timer(self, freq, &mode, &timer); + if (timer < 0) { + // Try to reuse self timer + if ((self->mode >= 0) && (self->channel >= 0)) { + if (!is_timer_in_use(self->mode, self->channel, self->timer)) { + mode = self->mode; + timer = self->timer; + } + } + // If no existing timer and channel was found, then try to find free timer in any mode + if (timer < 0) { + find_timer(self, -1, &mode, &timer); + } + } + if (timer < 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("out of %sPWM timers:%d"), self->lightsleep ? "light sleep capable " : "", self->lightsleep ? LEDC_TIMER_MAX : LEDC_SPEED_MODE_MAX *LEDC_TIMER_MAX); + } + // If the timer is found, then register + if (self->timer != timer) { + unregister_channel(self->mode, self->channel); + // Rregister the channel to the timer + self->mode = mode; + self->timer = timer; + register_channel(self->mode, self->channel, -1, self->timer); + } + #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) + if (is_timer_with_different_clock(self->mode, self->timer, timers[self->mode][self->timer].clk_cfg)) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("one or more active timers use a different clock source, not supported by the current SoC.")); + } + #endif +} + +static void set_freq_duty(machine_pwm_obj_t *self, unsigned int freq) { + select_timer(self, freq); + set_freq(self, freq); + apply_duty(self); +} + +// ****************************************************************************** // MicroPython bindings for PWM static void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "PWM(Pin(%u)", self->pin); - if (self->active) { + if (self->timer >= 0) { mp_printf(print, ", freq=%u", ledc_get_freq(self->mode, self->timer)); - - if (self->duty_x == PWM_RES_10_BIT) { + if (self->duty_scale == DUTY_10) { mp_printf(print, ", duty=%d", get_duty_u10(self)); - } else if (self->duty_x == -HIGHEST_PWM_RES) { + } else if (self->duty_scale == DUTY_NS) { mp_printf(print, ", duty_ns=%d", get_duty_ns(self)); } else { mp_printf(print, ", duty_u16=%d", get_duty_u16(self)); } - int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution; - mp_printf(print, ", resolution=%d", resolution); - - mp_printf(print, ", (duty=%.2f%%, resolution=%.3f%%)", 100.0 * get_duty_raw(self) / (1 << resolution), 100.0 * 1 / (1 << resolution)); // percents - - mp_printf(print, ", mode=%d, channel=%d, timer=%d", self->mode, self->channel, self->timer); + if (self->output_invert) { + mp_printf(print, ", invert=True"); + } + if (self->lightsleep) { + mp_printf(print, ", lightsleep=True"); + } + mp_printf(print, ")"); + + #if MICROPY_ERROR_REPORTING > MICROPY_ERROR_REPORTING_NORMAL + mp_printf(print, " # duty=%.2f%%", 100.0 * get_duty_u16(self) / MAX_16_DUTY); + mp_printf(print, ", raw_duty=%d, resolution=%d", ledc_get_duty(self->mode, self->channel), timers[self->mode][self->timer].duty_resolution); + mp_printf(print, ", mode=%d, timer=%d, channel=%d", self->mode, self->timer, self->channel); + int clk_cfg = timers[self->mode][self->timer].clk_cfg; + mp_printf(print, ", clk_cfg=%d=", clk_cfg); + if (clk_cfg == LEDC_USE_RC_FAST_CLK) { + mp_printf(print, "RC_FAST_CLK"); + } + #if SOC_LEDC_SUPPORT_APB_CLOCK + else if (clk_cfg == LEDC_USE_APB_CLK) { + mp_printf(print, "APB_CLK"); + } + #endif + #if SOC_LEDC_SUPPORT_XTAL_CLOCK + else if (clk_cfg == LEDC_USE_XTAL_CLK) { + mp_printf(print, "XTAL_CLK"); + } + #endif + #if SOC_LEDC_SUPPORT_REF_TICK + else if (clk_cfg == LEDC_USE_REF_TICK) { + mp_printf(print, "REF_TICK"); + } + #endif + #if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK + else if (clk_cfg == LEDC_USE_PLL_DIV_CLK) { + mp_printf(print, "PLL_CLK"); + } + #endif + else if (clk_cfg == LEDC_AUTO_CLK) { + mp_printf(print, "AUTO_CLK"); + } else { + mp_printf(print, "UNKNOWN"); + } + #endif + } else { + mp_printf(print, ")"); } - mp_printf(print, ")"); } // This called from pwm.init() method +// +// Check the current mode. +// If the frequency is changed, try to find a timer with the same frequency +// in the current mode, otherwise in the new mode. +// If the mode is changed, release the channel and select a new channel in the new mode. +// Then set the frequency with the same duty. static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns }; - static const mp_arg_t allowed_args[] = { + + enum { ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_lightsleep }; + mp_arg_t allowed_args[] = { { MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_duty, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_duty_u16, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_duty_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = self->output_invert} }, + { MP_QSTR_lightsleep, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = self->lightsleep} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - int channel_idx = find_channel(self->pin, ANY_MODE); - if (channel_idx == -1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), PWM_CHANNEL_MAX); // in all modes + self->lightsleep = args[ARG_lightsleep].u_bool; + + int freq = args[ARG_freq].u_int; + if (freq != -1) { + check_freq_ranges(self, freq, 40000000); } int duty = args[ARG_duty].u_int; int duty_u16 = args[ARG_duty_u16].u_int; int duty_ns = args[ARG_duty_ns].u_int; - if (((duty != -1) && (duty_u16 != -1)) || ((duty != -1) && (duty_ns != -1)) || ((duty_u16 != -1) && (duty_ns != -1))) { - mp_raise_ValueError(MP_ERROR_TEXT("only one of parameters 'duty', 'duty_u16' or 'duty_ns' is allowed")); + if (duty_u16 >= 0) { + check_duty_u16(self, duty_u16); + } else if (duty_ns >= 0) { + check_duty_ns(self, duty_ns); + } else if (duty >= 0) { + check_duty_u10(self, duty); + } else if (self->duty_scale == 0) { + self->duty_scale = DUTY_16; + self->duty_ui = PWM_DUTY; + } + + self->output_invert = args[ARG_invert].u_bool; + + // Check the current mode and channel + int mode = -1; + int channel = -1; + find_channel(self, &mode, &channel, freq); + if (channel < 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("out of %sPWM channels:%d"), self->lightsleep ? "light sleep capable " : "", self->lightsleep ? LEDC_CHANNEL_MAX : LEDC_SPEED_MODE_MAX *LEDC_CHANNEL_MAX); } + self->mode = mode; + self->channel = channel; - int freq = args[ARG_freq].u_int; // Check if freq wasn't passed as an argument - if (freq == -1) { + if ((freq == -1) && (mode >= 0) && (channel >= 0)) { // Check if already set, otherwise use the default freq. // It is possible in case: // pwm = PWM(pin, freq=1000, duty=256) // pwm = PWM(pin, duty=128) - if (chans[channel_idx].timer_idx != -1) { - freq = timers[chans[channel_idx].timer_idx].freq_hz; + if (chans[mode][channel].timer >= 0) { + freq = timers[mode][chans[mode][channel].timer].freq; } if (freq <= 0) { freq = PWM_FREQ; } } - if ((freq <= 0) || (freq > 40000000)) { - mp_raise_ValueError(MP_ERROR_TEXT("frequency must be from 1Hz to 40MHz")); - } - - int timer_idx; - int current_timer_idx = chans[channel_idx].timer_idx; - bool current_in_use = is_timer_in_use(channel_idx, current_timer_idx); - if (current_in_use) { - timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, CHANNEL_IDX_TO_MODE(channel_idx)); - } else { - timer_idx = chans[channel_idx].timer_idx; - } - - if (timer_idx == -1) { - timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, ANY_MODE); - } - if (timer_idx == -1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), PWM_TIMER_MAX); // in all modes - } - - int mode = TIMER_IDX_TO_MODE(timer_idx); - if (CHANNEL_IDX_TO_MODE(channel_idx) != mode) { - // unregister old channel - chans[channel_idx].pin = -1; - chans[channel_idx].timer_idx = -1; - // find new channel - channel_idx = find_channel(self->pin, mode); - if (CHANNEL_IDX_TO_MODE(channel_idx) != mode) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), PWM_CHANNEL_MAX); // in current mode - } - } - self->mode = mode; - self->timer = TIMER_IDX_TO_TIMER(timer_idx); - self->channel = CHANNEL_IDX_TO_CHANNEL(channel_idx); - - // New PWM assignment - if ((chans[channel_idx].pin == -1) || (chans[channel_idx].timer_idx != timer_idx)) { - configure_channel(self); - chans[channel_idx].pin = self->pin; - } - chans[channel_idx].timer_idx = timer_idx; - self->active = true; - - // Set timer frequency - set_freq(self, freq, &timers[timer_idx]); + set_freq_duty(self, freq); +} - // Set duty cycle? - if (duty_u16 != -1) { - set_duty_u16(self, duty_u16); - } else if (duty_ns != -1) { - set_duty_ns(self, duty_ns); - } else if (duty != -1) { - set_duty_u10(self, duty); - } else if (self->duty_x == 0) { - set_duty_u10(self, (1 << PWM_RES_10_BIT) / 2); // 50% - } +static void self_reset(machine_pwm_obj_t *self) { + self->mode = -1; + self->channel = -1; + self->timer = -1; + self->freq = -1; + self->duty_scale = 0; + self->duty_ui = 0; + self->channel_duty = -1; + self->output_invert = false; + self->output_invert_prev = false; + self->lightsleep = false; } // This called from PWM() constructor static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 2, true); - gpio_num_t pin_id = machine_pin_get_id(args[0]); - - // create PWM object from the given pin - machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); - self->pin = pin_id; - self->active = false; - self->mode = -1; - self->channel = -1; - self->timer = -1; - self->duty_x = 0; // start the PWM subsystem if it's not already running if (!pwm_inited) { pwm_init(); + #if FADE + ledc_fade_func_install(0); + #endif pwm_inited = true; } - // start the PWM running for this channel + // create PWM object from the given pin + machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); + self_reset(self); + + self->pin = machine_pin_get_id(args[0]); + + // Process the remaining parameters. mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); mp_machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); - return MP_OBJ_FROM_PTR(self); } // This called from pwm.deinit() method static void mp_machine_pwm_deinit(machine_pwm_obj_t *self) { - int channel_idx = CHANNEL_IDX(self->mode, self->channel); - pwm_deinit(channel_idx); - self->active = false; - self->mode = -1; - self->channel = -1; - self->timer = -1; - self->duty_x = 0; + pwm_deinit(self->mode, self->channel, self->output_invert); + self_reset(self); } // Set and get methods of PWM class @@ -608,57 +756,11 @@ static mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { static void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { pwm_is_active(self); - if ((freq <= 0) || (freq > 40000000)) { - mp_raise_ValueError(MP_ERROR_TEXT("frequency must be from 1Hz to 40MHz")); - } - if (freq == timers[TIMER_IDX(self->mode, self->timer)].freq_hz) { + check_freq_ranges(self, freq, 40000000); + if (freq == timers[self->mode][self->timer].freq) { return; } - - int current_timer_idx = chans[CHANNEL_IDX(self->mode, self->channel)].timer_idx; - bool current_in_use = is_timer_in_use(CHANNEL_IDX(self->mode, self->channel), current_timer_idx); - - // Check if an already running timer with the same freq is running - int new_timer_idx = find_timer(freq, SAME_FREQ_ONLY, self->mode); - - // If no existing timer was found, and the current one is in use, then find a new one - if ((new_timer_idx == -1) && current_in_use) { - // Have to find a new timer - new_timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, self->mode); - - if (new_timer_idx == -1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), PWM_TIMER_MAX); // in current mode - } - } - - if ((new_timer_idx != -1) && (new_timer_idx != current_timer_idx)) { - // Bind the channel to the new timer - chans[self->channel].timer_idx = new_timer_idx; - - if (ledc_bind_channel_timer(self->mode, self->channel, TIMER_IDX_TO_TIMER(new_timer_idx)) != ESP_OK) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("failed to bind timer to channel")); - } - - if (!current_in_use) { - // Free the old timer - check_esp_err(ledc_timer_pause(self->mode, self->timer)); - ledc_timer_config_t timer_config = { - .deconfigure = true, - .speed_mode = self->mode, - .timer_num = self->timer, - }; - check_esp_err(ledc_timer_config(&timer_config)); - // Flag it unused - timers[current_timer_idx].freq_hz = -1; - } - - current_timer_idx = new_timer_idx; - } - self->mode = TIMER_IDX_TO_MODE(current_timer_idx); - self->timer = TIMER_IDX_TO_TIMER(current_timer_idx); - - // Set the frequency - set_freq(self, freq, &timers[current_timer_idx]); + set_freq_duty(self, freq); } static mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) { diff --git a/ports/esp32/machine_rtc.c b/ports/esp32/machine_rtc.c index a2f4bb132d..ee6b1ad5cb 100644 --- a/ports/esp32/machine_rtc.c +++ b/ports/esp32/machine_rtc.c @@ -82,8 +82,12 @@ _USER_MEM_ATTR uint8_t rtc_user_mem_data[MICROPY_HW_RTC_USER_MEM_MAX]; static const machine_rtc_obj_t machine_rtc_obj = {{&machine_rtc_type}}; machine_rtc_config_t machine_rtc_config = { + #if SOC_PM_SUPPORT_EXT1_WAKEUP .ext1_pins = 0, + #endif + #if SOC_PM_SUPPORT_EXT0_WAKEUP .ext0_pin = -1 + #endif }; static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { diff --git a/ports/esp32/machine_rtc.h b/ports/esp32/machine_rtc.h index ce2a5482a2..72717c5936 100644 --- a/ports/esp32/machine_rtc.h +++ b/ports/esp32/machine_rtc.h @@ -31,13 +31,25 @@ #include "modmachine.h" typedef struct { + #if SOC_PM_SUPPORT_EXT1_WAKEUP uint64_t ext1_pins; // set bit == pin# + #endif + #if SOC_PM_SUPPORT_EXT0_WAKEUP int8_t ext0_pin; // just the pin#, -1 == None + #endif + #if SOC_TOUCH_SENSOR_SUPPORTED bool wake_on_touch : 1; + #endif + #if SOC_ULP_SUPPORTED bool wake_on_ulp : 1; + #endif + #if SOC_PM_SUPPORT_EXT0_WAKEUP bool ext0_level : 1; wake_type_t ext0_wake_types; + #endif + #if SOC_PM_SUPPORT_EXT1_WAKEUP bool ext1_level : 1; + #endif } machine_rtc_config_t; extern machine_rtc_config_t machine_rtc_config; diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c index 82b432bbac..a104288f6e 100644 --- a/ports/esp32/machine_timer.c +++ b/ports/esp32/machine_timer.c @@ -50,12 +50,13 @@ const mp_obj_type_t machine_timer_type; static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +static mp_obj_t machine_timer_deinit(mp_obj_t self_in); void machine_timer_deinit_all(void) { // Disable, deallocate and remove all timers from list machine_timer_obj_t **t = &MP_STATE_PORT(machine_timer_obj_head); while (*t != NULL) { - machine_timer_disable(*t); + machine_timer_deinit(*t); machine_timer_obj_t *next = (*t)->next; m_del_obj(machine_timer_obj_t, *t); *t = next; @@ -96,6 +97,7 @@ machine_timer_obj_t *machine_timer_create(mp_uint_t timer) { self = mp_obj_malloc(machine_timer_obj_t, &machine_timer_type); self->group = group; self->index = index; + self->handle = NULL; // Add the timer to the linked-list of timers self->next = MP_STATE_PORT(machine_timer_obj_head); @@ -131,9 +133,8 @@ void machine_timer_disable(machine_timer_obj_t *self) { } if (self->handle) { - // Free the interrupt handler. - esp_intr_free(self->handle); - self->handle = NULL; + // Disable the interrupt + ESP_ERROR_CHECK(esp_intr_disable(self->handle)); } // We let the disabled timer stay in the list, as it might be @@ -150,12 +151,16 @@ static void machine_timer_isr(void *self_in) { if (self->repeat) { timer_ll_enable_alarm(self->hal_context.dev, self->index, true); } - mp_sched_schedule(self->callback, self); - mp_hal_wake_main_task_from_isr(); + self->handler(self); } } -void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) { +static void machine_timer_isr_handler(machine_timer_obj_t *self) { + mp_sched_schedule(self->callback, self); + mp_hal_wake_main_task_from_isr(); +} + +void machine_timer_enable(machine_timer_obj_t *self) { // Initialise the timer. timer_hal_init(&self->hal_context, self->group, self->index); timer_ll_enable_counter(self->hal_context.dev, self->index, false); @@ -167,10 +172,17 @@ void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) { // Allocate and enable the alarm interrupt. timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), false); timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index)); - ESP_ERROR_CHECK( - esp_intr_alloc(timer_group_periph_signals.groups[self->group].timer_irq_id[self->index], - TIMER_FLAGS, timer_isr, self, &self->handle) - ); + if (self->handle) { + ESP_ERROR_CHECK(esp_intr_enable(self->handle)); + } else { + ESP_ERROR_CHECK(esp_intr_alloc( + timer_group_periph_signals.groups[self->group].timer_irq_id[self->index], + TIMER_FLAGS, + machine_timer_isr, + self, + &self->handle + )); + } timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), true); // Enable the alarm to trigger at the given period. @@ -224,16 +236,22 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n } self->repeat = args[ARG_mode].u_int; + self->handler = machine_timer_isr_handler; self->callback = args[ARG_callback].u_obj; - self->handle = NULL; - machine_timer_enable(self, machine_timer_isr); + machine_timer_enable(self); return mp_const_none; } static mp_obj_t machine_timer_deinit(mp_obj_t self_in) { - machine_timer_disable(self_in); + machine_timer_obj_t *self = self_in; + + machine_timer_disable(self); + if (self->handle) { + ESP_ERROR_CHECK(esp_intr_free(self->handle)); + self->handle = NULL; + } return mp_const_none; } @@ -246,6 +264,9 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init) static mp_obj_t machine_timer_value(mp_obj_t self_in) { machine_timer_obj_t *self = self_in; + if (self->handle == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("timer not set")); + } uint64_t result = timer_ll_get_counter_value(self->hal_context.dev, self->index); return MP_OBJ_NEW_SMALL_INT((mp_uint_t)(result / (TIMER_SCALE / 1000))); // value in ms } diff --git a/ports/esp32/machine_timer.h b/ports/esp32/machine_timer.h index 914bedd86b..10fe2f39c9 100644 --- a/ports/esp32/machine_timer.h +++ b/ports/esp32/machine_timer.h @@ -55,12 +55,13 @@ typedef struct _machine_timer_obj_t { mp_obj_t callback; intr_handle_t handle; + void (*handler)(struct _machine_timer_obj_t *timer); struct _machine_timer_obj_t *next; } machine_timer_obj_t; machine_timer_obj_t *machine_timer_create(mp_uint_t timer); -void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)); +void machine_timer_enable(machine_timer_obj_t *self); void machine_timer_disable(machine_timer_obj_t *self); #endif // MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index 73089ef463..982d9a7e27 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -59,6 +59,7 @@ #define UART_IRQ_BREAK (1 << UART_BREAK) #define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_RXIDLE | UART_IRQ_BREAK) #define RXIDLE_TIMER_MIN (5000) // 500 us +#define UART_QUEUE_SIZE (3) enum { RXIDLE_INACTIVE, @@ -109,30 +110,19 @@ static const char *_parity_name[] = {"None", "1", "0"}; { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \ { MP_ROM_QSTR(MP_QSTR_IRQ_BREAK), MP_ROM_INT(UART_IRQ_BREAK) }, \ -static void uart_timer_callback(void *self_in) { - machine_timer_obj_t *self = self_in; - - uint32_t intr_status = timer_ll_get_intr_status(self->hal_context.dev); - - if (intr_status & TIMER_LL_EVENT_ALARM(self->index)) { - timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index)); - if (self->repeat) { - timer_ll_enable_alarm(self->hal_context.dev, self->index, true); - } - } - +static void uart_timer_callback(machine_timer_obj_t *timer) { // The UART object is referred here by the callback field. - machine_uart_obj_t *uart = (machine_uart_obj_t *)self->callback; - if (uart->rxidle_state == RXIDLE_ALERT) { + machine_uart_obj_t *self = (machine_uart_obj_t *)timer->callback; + if (self->rxidle_state == RXIDLE_ALERT) { // At the first call, just switch the state - uart->rxidle_state = RXIDLE_ARMED; - } else if (uart->rxidle_state == RXIDLE_ARMED) { + self->rxidle_state = RXIDLE_ARMED; + } else if (self->rxidle_state == RXIDLE_ARMED) { // At the second call, run the irq callback and stop the timer - uart->rxidle_state = RXIDLE_STANDBY; - uart->mp_irq_flags = UART_IRQ_RXIDLE; - mp_irq_handler(uart->mp_irq_obj); + self->rxidle_state = RXIDLE_STANDBY; + self->mp_irq_flags = UART_IRQ_RXIDLE; + mp_irq_handler(self->mp_irq_obj); mp_hal_wake_main_task_from_isr(); - machine_timer_disable(uart->rxidle_timer); + machine_timer_disable(self->rxidle_timer); } } @@ -149,9 +139,7 @@ static void uart_event_task(void *self_in) { if (self->mp_irq_trigger & UART_IRQ_RXIDLE) { if (self->rxidle_state != RXIDLE_INACTIVE) { if (self->rxidle_state == RXIDLE_STANDBY) { - self->rxidle_timer->repeat = true; - self->rxidle_timer->handle = NULL; - machine_timer_enable(self->rxidle_timer, uart_timer_callback); + machine_timer_enable(self->rxidle_timer); } } self->rxidle_state = RXIDLE_ALERT; @@ -174,6 +162,13 @@ static void uart_event_task(void *self_in) { } } +static inline void uart_event_task_create(machine_uart_obj_t *self) { + if (xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self, + ESP_TASKD_EVENT_PRIO, (TaskHandle_t *)&self->uart_event_task, MP_TASK_COREID) != pdPASS) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("failed to create UART event task")); + } +} + static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); uint32_t baudrate; @@ -250,7 +245,7 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, // wait for all data to be transmitted before changing settings uart_wait_tx_done(self->uart_num, pdMS_TO_TICKS(1000)); - if (args[ARG_txbuf].u_int >= 0 || args[ARG_rxbuf].u_int >= 0) { + if ((args[ARG_txbuf].u_int >= 0 && args[ARG_txbuf].u_int != self->txbuf) || (args[ARG_rxbuf].u_int >= 0 && args[ARG_rxbuf].u_int != self->rxbuf)) { // must reinitialise driver to change the tx/rx buffer size #if MICROPY_HW_ENABLE_UART_REPL if (self->uart_num == MICROPY_HW_UART_REPL) { @@ -275,9 +270,12 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, check_esp_err(uart_get_word_length(self->uart_num, &uartcfg.data_bits)); check_esp_err(uart_get_parity(self->uart_num, &uartcfg.parity)); check_esp_err(uart_get_stop_bits(self->uart_num, &uartcfg.stop_bits)); - check_esp_err(uart_driver_delete(self->uart_num)); + mp_machine_uart_deinit(self); check_esp_err(uart_param_config(self->uart_num, &uartcfg)); - check_esp_err(uart_driver_install(self->uart_num, self->rxbuf, self->txbuf, 0, NULL, 0)); + check_esp_err(uart_driver_install(self->uart_num, self->rxbuf, self->txbuf, UART_QUEUE_SIZE, &self->uart_queue, 0)); + if (self->mp_irq_obj != NULL && self->mp_irq_obj->handler != mp_const_none) { + uart_event_task_create(self); + } } // set baudrate @@ -437,7 +435,8 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->timeout_char = 0; self->invert = 0; self->flowcontrol = 0; - self->uart_event_task = 0; + self->uart_event_task = NULL; + self->uart_queue = NULL; self->rxidle_state = RXIDLE_INACTIVE; switch (uart_num) { @@ -470,12 +469,13 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg { // Remove any existing configuration check_esp_err(uart_driver_delete(self->uart_num)); + self->uart_queue = NULL; // init the peripheral // Setup check_esp_err(uart_param_config(self->uart_num, &uartcfg)); - check_esp_err(uart_driver_install(uart_num, self->rxbuf, self->txbuf, 3, &self->uart_queue, 0)); + check_esp_err(uart_driver_install(uart_num, self->rxbuf, self->txbuf, UART_QUEUE_SIZE, &self->uart_queue, 0)); } mp_map_t kw_args; @@ -489,7 +489,12 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg } static void mp_machine_uart_deinit(machine_uart_obj_t *self) { + if (self->uart_event_task != NULL) { + vTaskDelete(self->uart_event_task); + self->uart_event_task = NULL; + } check_esp_err(uart_driver_delete(self->uart_num)); + self->uart_queue = NULL; } static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) { @@ -535,11 +540,11 @@ static void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger } self->rxidle_period = period; self->rxidle_timer->period = period; + self->rxidle_timer->handler = uart_timer_callback; // The Python callback is not used. So use this // data field to hold a reference to the UART object. self->rxidle_timer->callback = self; self->rxidle_timer->repeat = true; - self->rxidle_timer->handle = NULL; self->rxidle_state = RXIDLE_STANDBY; } } @@ -568,6 +573,12 @@ static const mp_irq_methods_t uart_irq_methods = { }; static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { + #if MICROPY_HW_ENABLE_UART_REPL + if (self->uart_num == MICROPY_HW_UART_REPL) { + mp_raise_ValueError(MP_ERROR_TEXT("UART does not support IRQs")); + } + #endif + if (self->mp_irq_obj == NULL) { self->mp_irq_trigger = 0; self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self)); @@ -597,9 +608,8 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args uart_irq_configure_timer(self, trigger); // Start a task for handling events - if (handler != mp_const_none && self->uart_event_task == NULL) { - xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self, - ESP_TASKD_EVENT_PRIO, (TaskHandle_t *)&self->uart_event_task, MP_TASK_COREID); + if (handler != mp_const_none && self->uart_event_task == NULL && self->uart_queue != NULL) { + uart_event_task_create(self); } else if (handler == mp_const_none && self->uart_event_task != NULL) { vTaskDelete(self->uart_event_task); self->uart_event_task = NULL; diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 4f0c27ee07..f048aa85f5 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -38,6 +38,7 @@ #include "nvs_flash.h" #include "esp_task.h" #include "esp_event.h" +#include "esp_flash.h" #include "esp_log.h" #include "esp_memory_utils.h" #include "esp_psram.h" @@ -214,18 +215,45 @@ void boardctrl_startup(void) { nvs_flash_erase(); nvs_flash_init(); } + + // Query the physical size of the SPI flash and store it in the size + // variable of the global, default SPI flash handle. + esp_flash_get_physical_size(NULL, &esp_flash_default_chip->size); + + // If there is no filesystem partition (no "vfs" or "ffat"), add a "vfs" partition + // that extends from the end of the application partition up to the end of flash. + if (esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "vfs") == NULL + && esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "ffat") == NULL) { + // No "vfs" or "ffat" partition, so try to create one. + + // Find the end of the last partition that exists in the partition table. + size_t offset = 0; + esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL); + while (iter != NULL) { + const esp_partition_t *part = esp_partition_get(iter); + offset = MAX(offset, part->address + part->size); + iter = esp_partition_next(iter); + } + + // If we found the application partition and there is some space between the end of + // that and the end of flash, create a "vfs" partition taking up all of that space. + if (offset > 0 && esp_flash_default_chip->size > offset) { + size_t size = esp_flash_default_chip->size - offset; + esp_partition_register_external(esp_flash_default_chip, offset, size, "vfs", ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL); + } + } } -void app_main(void) { +void MICROPY_ESP_IDF_ENTRY(void) { // Hook for a board to run code at start up. - // This defaults to initialising NVS. + // This defaults to initialising NVS and detecting the flash size. MICROPY_BOARD_STARTUP(); // Create and transfer control to the MicroPython task. xTaskCreatePinnedToCore(mp_task, "mp_task", MICROPY_TASK_STACK_SIZE / sizeof(StackType_t), NULL, MP_TASK_PRIORITY, &mp_main_task_handle, MP_TASK_COREID); } -void nlr_jump_fail(void *val) { +MP_WEAK void nlr_jump_fail(void *val) { printf("NLR jump failed, val=%p\n", val); esp_restart(); } diff --git a/ports/esp32/main/idf_component.yml b/ports/esp32/main/idf_component.yml index ccbde1f27e..f7773f4f4e 100644 --- a/ports/esp32/main/idf_component.yml +++ b/ports/esp32/main/idf_component.yml @@ -5,5 +5,10 @@ dependencies: rules: - if: "target in [esp32s2, esp32s3]" version: "~1.0.0" + espressif/lan867x: + version: "~1.0.0" + rules: + - if: "target == esp32" + - if: "idf_version >=5.3" idf: version: ">=5.2.0" diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 164b191826..fcd6ed9fa8 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -30,7 +30,6 @@ #include <time.h> #include <sys/time.h> #include "driver/gpio.h" -#include "driver/adc.h" #include "esp_heap_caps.h" #include "multi_heap.h" @@ -48,22 +47,30 @@ #include "../multi_heap_platform.h" #include "../heap_private.h" +#if SOC_TOUCH_SENSOR_SUPPORTED static mp_obj_t esp32_wake_on_touch(const mp_obj_t wake) { + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin != -1) { mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif machine_rtc_config.wake_on_touch = mp_obj_is_true(wake); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(esp32_wake_on_touch_obj, esp32_wake_on_touch); +#endif +#if SOC_PM_SUPPORT_EXT0_WAKEUP static mp_obj_t esp32_wake_on_ext0(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + #if SOC_TOUCH_SENSOR_SUPPORTED if (machine_rtc_config.wake_on_touch) { mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif + enum {ARG_pin, ARG_level}; const mp_arg_t allowed_args[] = { { MP_QSTR_pin, MP_ARG_OBJ, {.u_obj = mp_obj_new_int(machine_rtc_config.ext0_pin)} }, @@ -90,7 +97,9 @@ static mp_obj_t esp32_wake_on_ext0(size_t n_args, const mp_obj_t *pos_args, mp_m return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext0_obj, 0, esp32_wake_on_ext0); +#endif +#if SOC_PM_SUPPORT_EXT1_WAKEUP static mp_obj_t esp32_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum {ARG_pins, ARG_level}; const mp_arg_t allowed_args[] = { @@ -126,15 +135,20 @@ static mp_obj_t esp32_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_m return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext1_obj, 0, esp32_wake_on_ext1); +#endif +#if SOC_ULP_SUPPORTED static mp_obj_t esp32_wake_on_ulp(const mp_obj_t wake) { + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin != -1) { mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif machine_rtc_config.wake_on_ulp = mp_obj_is_true(wake); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(esp32_wake_on_ulp_obj, esp32_wake_on_ulp); +#endif #if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP static mp_obj_t esp32_gpio_deep_sleep_hold(const mp_obj_t enable) { @@ -212,13 +226,64 @@ static mp_obj_t esp32_idf_heap_info(const mp_obj_t cap_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(esp32_idf_heap_info_obj, esp32_idf_heap_info); +#if CONFIG_FREERTOS_USE_TRACE_FACILITY +static mp_obj_t esp32_idf_task_info(void) { + const size_t task_count_max = uxTaskGetNumberOfTasks(); + TaskStatus_t *task_array = m_new(TaskStatus_t, task_count_max); + uint32_t total_time; + const size_t task_count = uxTaskGetSystemState(task_array, task_count_max, &total_time); + + mp_obj_list_t *task_list = MP_OBJ_TO_PTR(mp_obj_new_list(task_count, NULL)); + for (size_t i = 0; i < task_count; i++) { + mp_obj_t task_data[] = { + mp_obj_new_int_from_uint((mp_uint_t)task_array[i].xHandle), + mp_obj_new_str(task_array[i].pcTaskName, strlen(task_array[i].pcTaskName)), + MP_OBJ_NEW_SMALL_INT(task_array[i].eCurrentState), + MP_OBJ_NEW_SMALL_INT(task_array[i].uxCurrentPriority), + #if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS + mp_obj_new_int_from_uint(task_array[i].ulRunTimeCounter), + #else + mp_const_none, + #endif + mp_obj_new_int_from_uint(task_array[i].usStackHighWaterMark), + #if CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID + MP_OBJ_NEW_SMALL_INT(task_array[i].xCoreID), + #else + mp_const_none, + #endif + }; + task_list->items[i] = mp_obj_new_tuple(7, task_data); + } + + m_del(TaskStatus_t, task_array, task_count_max); + mp_obj_t task_stats[] = { + #if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS + MP_OBJ_NEW_SMALL_INT(total_time), + #else + mp_const_none, + #endif + task_list + }; + return mp_obj_new_tuple(2, task_stats); +} +static MP_DEFINE_CONST_FUN_OBJ_0(esp32_idf_task_info_obj, esp32_idf_task_info); +#endif + static const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_esp32) }, + #if SOC_TOUCH_SENSOR_SUPPORTED { MP_ROM_QSTR(MP_QSTR_wake_on_touch), MP_ROM_PTR(&esp32_wake_on_touch_obj) }, + #endif + #if SOC_PM_SUPPORT_EXT0_WAKEUP { MP_ROM_QSTR(MP_QSTR_wake_on_ext0), MP_ROM_PTR(&esp32_wake_on_ext0_obj) }, + #endif + #if SOC_PM_SUPPORT_EXT1_WAKEUP { MP_ROM_QSTR(MP_QSTR_wake_on_ext1), MP_ROM_PTR(&esp32_wake_on_ext1_obj) }, + #endif + #if SOC_ULP_SUPPORTED { MP_ROM_QSTR(MP_QSTR_wake_on_ulp), MP_ROM_PTR(&esp32_wake_on_ulp_obj) }, + #endif #if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP { MP_ROM_QSTR(MP_QSTR_gpio_deep_sleep_hold), MP_ROM_PTR(&esp32_gpio_deep_sleep_hold_obj) }, #endif @@ -228,6 +293,9 @@ static const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_mcu_temperature), MP_ROM_PTR(&esp32_mcu_temperature_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_idf_heap_info), MP_ROM_PTR(&esp32_idf_heap_info_obj) }, + #if CONFIG_FREERTOS_USE_TRACE_FACILITY + { MP_ROM_QSTR(MP_QSTR_idf_task_info), MP_ROM_PTR(&esp32_idf_task_info_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_NVS), MP_ROM_PTR(&esp32_nvs_type) }, { MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) }, diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 0c1b94d02d..0d7ea44c66 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -178,7 +178,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { machine_sleep_helper(MACHINE_WAKE_SLEEP, n_args, args); }; -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { machine_sleep_helper(MACHINE_WAKE_DEEPSLEEP, n_args, args); mp_machine_reset(); }; @@ -221,7 +221,7 @@ static mp_int_t mp_machine_reset_cause(void) { #include "esp32s3/rom/usb/chip_usb_dw_wrapper.h" #endif -NORETURN static void machine_bootloader_rtc(void) { +MP_NORETURN static void machine_bootloader_rtc(void) { #if CONFIG_IDF_TARGET_ESP32S3 && MICROPY_HW_USB_CDC usb_usj_mode(); usb_dc_prepare_persist(); @@ -233,7 +233,7 @@ NORETURN static void machine_bootloader_rtc(void) { #endif #ifdef MICROPY_BOARD_ENTER_BOOTLOADER -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); for (;;) { } @@ -254,7 +254,7 @@ static mp_obj_t machine_wake_reason(size_t n_args, const mp_obj_t *pos_args, mp_ } static MP_DEFINE_CONST_FUN_OBJ_KW(machine_wake_reason_obj, 0, machine_wake_reason); -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { esp_restart(); } diff --git a/ports/esp32/modnetwork.h b/ports/esp32/modnetwork.h index 387f961976..ba69d5cc0f 100644 --- a/ports/esp32/modnetwork.h +++ b/ports/esp32/modnetwork.h @@ -28,7 +28,32 @@ #include "esp_netif.h" -enum { PHY_LAN8710, PHY_LAN8720, PHY_IP101, PHY_RTL8201, PHY_DP83848, PHY_KSZ8041, PHY_KSZ8081, PHY_KSZ8851SNL = 100, PHY_DM9051, PHY_W5500 }; +// lan867x component requires newer IDF version +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && CONFIG_IDF_TARGET_ESP32 +#define PHY_LAN867X_ENABLED (1) +#else +#define PHY_LAN867X_ENABLED (0) +#endif + +// PHY_GENERIC support requires newer IDF version +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) && CONFIG_IDF_TARGET_ESP32 +#define PHY_GENERIC_ENABLED (1) +#else +#define PHY_GENERIC_ENABLED (0) +#endif + +enum { + // PHYs supported by the internal Ethernet MAC: + PHY_LAN8710, PHY_LAN8720, PHY_IP101, PHY_RTL8201, PHY_DP83848, PHY_KSZ8041, PHY_KSZ8081, + #if PHY_LAN867X_ENABLED + PHY_LAN8670, + #endif + #if PHY_GENERIC_ENABLED + PHY_GENERIC, + #endif + // PHYs which are actually SPI Ethernet MAC+PHY chips: + PHY_KSZ8851SNL = 100, PHY_DM9051, PHY_W5500 +}; #define IS_SPI_PHY(NUM) (NUM >= 100) enum { ETH_INITIALIZED, ETH_STARTED, ETH_STOPPED, ETH_CONNECTED, ETH_DISCONNECTED, ETH_GOT_IP }; @@ -52,7 +77,7 @@ extern const mp_obj_type_t esp_network_wlan_type; MP_DECLARE_CONST_FUN_OBJ_0(esp_network_initialize_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_get_wlan_obj); MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_get_lan_obj); -MP_DECLARE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj); +extern const struct _mp_obj_type_t esp_network_ppp_lwip_type; MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_ifconfig_obj); MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj); MP_DECLARE_CONST_FUN_OBJ_KW(esp_nic_ipconfig_obj); @@ -61,7 +86,7 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_phy_mode_obj); mp_obj_t esp_ifname(esp_netif_t *netif); -NORETURN void esp_exceptions_helper(esp_err_t e); +MP_NORETURN void esp_exceptions_helper(esp_err_t e); static inline void esp_exceptions(esp_err_t e) { if (e != ESP_OK) { diff --git a/ports/esp32/modnetwork_globals.h b/ports/esp32/modnetwork_globals.h index 1aad785ee3..12252ddbc5 100644 --- a/ports/esp32/modnetwork_globals.h +++ b/ports/esp32/modnetwork_globals.h @@ -8,7 +8,7 @@ { MP_ROM_QSTR(MP_QSTR_LAN), MP_ROM_PTR(&esp_network_get_lan_obj) }, #endif #if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT) -{ MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&esp_network_ppp_make_new_obj) }, +{ MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&esp_network_ppp_lwip_type) }, #endif { MP_ROM_QSTR(MP_QSTR_phy_mode), MP_ROM_PTR(&esp_network_phy_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_network_ipconfig_obj) }, @@ -47,6 +47,12 @@ { MP_ROM_QSTR(MP_QSTR_PHY_DP83848), MP_ROM_INT(PHY_DP83848) }, { MP_ROM_QSTR(MP_QSTR_PHY_KSZ8041), MP_ROM_INT(PHY_KSZ8041) }, { MP_ROM_QSTR(MP_QSTR_PHY_KSZ8081), MP_ROM_INT(PHY_KSZ8081) }, +#if PHY_LAN867X_ENABLED +{ MP_ROM_QSTR(MP_QSTR_PHY_LAN8670), MP_ROM_INT(PHY_LAN8670) }, +#endif +#if PHY_GENERIC_ENABLED +{ MP_ROM_QSTR(MP_QSTR_PHY_GENERIC), MP_ROM_INT(PHY_GENERIC) }, +#endif #if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL { MP_ROM_QSTR(MP_QSTR_PHY_KSZ8851SNL), MP_ROM_INT(PHY_KSZ8851SNL) }, diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 7ea5e855d3..2050d1d04d 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -611,7 +611,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); // XXX this can end up waiting a very long time if the content is dribbled in one character // at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not // good behaviour. -static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, +static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, mp_int_t flags, struct sockaddr *from, socklen_t *from_len, int *errcode) { socket_obj_t *sock = MP_OBJ_TO_PTR(self_in); @@ -645,7 +645,7 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, if (release_gil) { MP_THREAD_GIL_EXIT(); } - int r = lwip_recvfrom(sock->fd, buf, size, 0, from, from_len); + int r = lwip_recvfrom(sock->fd, buf, size, flags, from, from_len); if (release_gil) { MP_THREAD_GIL_ENTER(); } @@ -655,7 +655,7 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, if (r >= 0) { return r; } - if (errno != EWOULDBLOCK) { + if (errno != EWOULDBLOCK || (flags & MSG_DONTWAIT)) { *errcode = errno; return MP_STREAM_ERROR; } @@ -666,14 +666,17 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, return MP_STREAM_ERROR; } -mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, +mp_obj_t _socket_recvfrom(size_t n_args, const mp_obj_t *args, struct sockaddr *from, socklen_t *from_len) { - size_t len = mp_obj_get_int(len_in); + mp_obj_t self_in = args[0]; + size_t len = mp_obj_get_int(args[1]); + int flags = n_args > 2 ? mp_obj_get_int(args[2]) : 0; + vstr_t vstr; vstr_init_len(&vstr, len); int errcode; - mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, from, from_len, &errcode); + mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, flags, from, from_len, &errcode); if (ret == MP_STREAM_ERROR) { mp_raise_OSError(errcode); } @@ -682,17 +685,17 @@ mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, return mp_obj_new_bytes_from_vstr(&vstr); } -static mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { - return _socket_recvfrom(self_in, len_in, NULL, NULL); +static mp_obj_t socket_recv(size_t n_args, const mp_obj_t *args) { + return _socket_recvfrom(n_args, args, NULL, NULL); } -static MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recv_obj, 2, 3, socket_recv); -static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { +static mp_obj_t socket_recvfrom(size_t n_args, const mp_obj_t *args) { struct sockaddr from; socklen_t fromlen = sizeof(from); mp_obj_t tuple[2]; - tuple[0] = _socket_recvfrom(self_in, len_in, &from, &fromlen); + tuple[0] = _socket_recvfrom(n_args, args, &from, &fromlen); uint8_t *ip = (uint8_t *)&((struct sockaddr_in *)&from)->sin_addr; mp_uint_t port = lwip_ntohs(((struct sockaddr_in *)&from)->sin_port); @@ -700,7 +703,7 @@ static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { return mp_obj_new_tuple(2, tuple); } -static MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recvfrom_obj, 2, 3, socket_recvfrom); int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { int sentlen = 0; @@ -789,7 +792,7 @@ static mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile); static mp_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { - return _socket_read_data(self_in, buf, size, NULL, NULL, errcode); + return _socket_read_data(self_in, buf, size, 0, NULL, NULL, errcode); } static mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { @@ -1010,6 +1013,8 @@ static const mp_rom_map_elem_t mp_module_socket_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SO_BINDTODEVICE), MP_ROM_INT(SO_BINDTODEVICE) }, { MP_ROM_QSTR(MP_QSTR_IP_ADD_MEMBERSHIP), MP_ROM_INT(IP_ADD_MEMBERSHIP) }, { MP_ROM_QSTR(MP_QSTR_TCP_NODELAY), MP_ROM_INT(TCP_NODELAY) }, + { MP_ROM_QSTR(MP_QSTR_MSG_PEEK), MP_ROM_INT(MSG_PEEK) }, + { MP_ROM_QSTR(MP_QSTR_MSG_DONTWAIT), MP_ROM_INT(MSG_DONTWAIT) }, }; static MP_DEFINE_CONST_DICT(mp_module_socket_globals, mp_module_socket_globals_table); diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index b5b7d63a56..a6f103cdef 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -62,7 +62,8 @@ #define MICROPY_STACK_CHECK_MARGIN (1024) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) -#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) // Debugging Note: Increase the error reporting level to view + // __FUNCTION__, __LINE__, __FILE__ in check_esp_err() exceptions #define MICROPY_WARNINGS (1) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_STREAMS_POSIX_API (1) @@ -123,6 +124,7 @@ #define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/esp32/machine_adc.c" #define MICROPY_PY_MACHINE_ADC_ATTEN_WIDTH (1) #define MICROPY_PY_MACHINE_ADC_INIT (1) +#define MICROPY_PY_MACHINE_ADC_DEINIT (1) #define MICROPY_PY_MACHINE_ADC_READ (1) #define MICROPY_PY_MACHINE_ADC_READ_UV (1) #define MICROPY_PY_MACHINE_ADC_BLOCK (1) @@ -391,3 +393,8 @@ void boardctrl_startup(void); #ifndef MICROPY_PY_STRING_TX_GIL_THRESHOLD #define MICROPY_PY_STRING_TX_GIL_THRESHOLD (20) #endif + +// Code can override this to provide a custom ESP-IDF entry point. +#ifndef MICROPY_ESP_IDF_ENTRY +#define MICROPY_ESP_IDF_ENTRY app_main +#endif diff --git a/ports/esp32/mpnimbleport.c b/ports/esp32/mpnimbleport.c index ce4b77727a..77185883fd 100644 --- a/ports/esp32/mpnimbleport.c +++ b/ports/esp32/mpnimbleport.c @@ -58,7 +58,7 @@ void mp_bluetooth_nimble_port_start(void) { nimble_port_freertos_init(ble_host_task); } -void mp_bluetooth_nimble_port_shutdown(void) { +int mp_bluetooth_nimble_port_shutdown(void) { DEBUG_printf("mp_bluetooth_nimble_port_shutdown\n"); #if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK @@ -79,6 +79,8 @@ void mp_bluetooth_nimble_port_shutdown(void) { // Mark stack as shutdown. mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; + + return 0; } #endif diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c index 962b5780d0..67917e33e5 100644 --- a/ports/esp32/mpthreadport.c +++ b/ports/esp32/mpthreadport.c @@ -41,10 +41,16 @@ #define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + MICROPY_STACK_CHECK_MARGIN) #define MP_THREAD_PRIORITY (ESP_TASK_PRIO_MIN + 1) +typedef enum { + MP_THREAD_RUN_STATE_NEW, + MP_THREAD_RUN_STATE_RUNNING, + MP_THREAD_RUN_STATE_FINISHED, +} mp_thread_run_state_t; + // this structure forms a linked list, one node per active thread typedef struct _mp_thread_t { TaskHandle_t id; // system id of thread - int ready; // whether the thread is ready and running + mp_thread_run_state_t run_state; // current run state of the thread void *arg; // thread Python args, a GC root pointer void *stack; // pointer to the stack size_t stack_len; // number of words in the stack @@ -60,18 +66,16 @@ void mp_thread_init(void *stack, uint32_t stack_len) { mp_thread_set_state(&mp_state_ctx.thread); // create the first entry in the linked list of all threads thread_entry0.id = xTaskGetCurrentTaskHandle(); - thread_entry0.ready = 1; + thread_entry0.run_state = MP_THREAD_RUN_STATE_RUNNING; thread_entry0.arg = NULL; thread_entry0.stack = stack; thread_entry0.stack_len = stack_len; thread_entry0.next = NULL; + thread = &thread_entry0; mp_thread_mutex_init(&thread_mutex); // memory barrier to ensure above data is committed __sync_synchronize(); - - // vTaskPreDeletionHook needs the thread ready after thread_mutex is ready - thread = &thread_entry0; } void mp_thread_gc_others(void) { @@ -82,7 +86,7 @@ void mp_thread_gc_others(void) { if (th->id == xTaskGetCurrentTaskHandle()) { continue; } - if (!th->ready) { + if (th->run_state != MP_THREAD_RUN_STATE_RUNNING) { continue; } gc_collect_root(th->stack, th->stack_len); @@ -106,7 +110,7 @@ void mp_thread_start(void) { mp_thread_mutex_lock(&thread_mutex, 1); for (mp_thread_t *th = thread; th != NULL; th = th->next) { if (th->id == xTaskGetCurrentTaskHandle()) { - th->ready = 1; + th->run_state = MP_THREAD_RUN_STATE_RUNNING; break; } } @@ -116,12 +120,22 @@ void mp_thread_start(void) { static void *(*ext_thread_entry)(void *) = NULL; static void freertos_entry(void *arg) { + // Run the Python code. if (ext_thread_entry) { ext_thread_entry(arg); } - vTaskDelete(NULL); - for (;;) {; + + // Remove the thread from the linked-list of active threads. + mp_thread_mutex_lock(&thread_mutex, 1); + for (mp_thread_t **th = &thread; *th != NULL; th = &(*th)->next) { + if ((*th)->id == xTaskGetCurrentTaskHandle()) { + *th = (*th)->next; + } } + mp_thread_mutex_unlock(&thread_mutex); + + // Delete this FreeRTOS task (this call to vTaskDelete will not return). + vTaskDelete(NULL); } mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_size, int priority, char *name) { @@ -147,7 +161,7 @@ mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_s } // add thread to linked list of all threads - th->ready = 0; + th->run_state = MP_THREAD_RUN_STATE_NEW; th->arg = arg; th->stack = pxTaskGetStackStart(th->id); th->stack_len = *stack_size / sizeof(uintptr_t); @@ -167,33 +181,7 @@ void mp_thread_finish(void) { mp_thread_mutex_lock(&thread_mutex, 1); for (mp_thread_t *th = thread; th != NULL; th = th->next) { if (th->id == xTaskGetCurrentTaskHandle()) { - th->ready = 0; - break; - } - } - mp_thread_mutex_unlock(&thread_mutex); -} - -// This is called either from vTaskDelete() or from the FreeRTOS idle task, so -// may not be within Python context. Therefore MP_STATE_THREAD may not be valid -// and it does not have the GIL. -void vTaskPreDeletionHook(void *tcb) { - if (thread == NULL) { - // threading not yet initialised - return; - } - mp_thread_t *prev = NULL; - mp_thread_mutex_lock(&thread_mutex, 1); - for (mp_thread_t *th = thread; th != NULL; prev = th, th = th->next) { - // unlink the node from the list - if ((void *)th->id == tcb) { - if (prev != NULL) { - prev->next = th->next; - } else { - // move the start pointer - thread = th->next; - } - // The "th" memory will eventually be reclaimed by the GC. + th->run_state = MP_THREAD_RUN_STATE_FINISHED; break; } } @@ -221,32 +209,22 @@ void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { } void mp_thread_deinit(void) { - for (;;) { - // Find a task to delete - TaskHandle_t id = NULL; - mp_thread_mutex_lock(&thread_mutex, 1); - for (mp_thread_t *th = thread; th != NULL; th = th->next) { - // Don't delete the current task - if (th->id != xTaskGetCurrentTaskHandle()) { - id = th->id; - break; - } - } - mp_thread_mutex_unlock(&thread_mutex); + // The current task should be thread_entry0 and should be the last in the linked list. + assert(thread_entry0.id == xTaskGetCurrentTaskHandle()); + assert(thread_entry0.next == NULL); - if (id == NULL) { - // No tasks left to delete - break; - } else { - // Call FreeRTOS to delete the task (it will call vTaskPreDeletionHook) - vTaskDelete(id); + // Delete all tasks except the main one. + mp_thread_mutex_lock(&thread_mutex, 1); + for (mp_thread_t *th = thread; th != NULL; th = th->next) { + if (th != &thread_entry0) { + vTaskDelete(th->id); } } -} - -#else + thread = &thread_entry0; + mp_thread_mutex_unlock(&thread_mutex); -void vTaskPreDeletionHook(void *tcb) { + // Give the idle task a chance to run, to clean up any deleted tasks. + vTaskDelay(1); } #endif // MICROPY_PY_THREAD diff --git a/ports/esp32/network_common.c b/ports/esp32/network_common.c index fce8a0304c..bd34f1b41f 100644 --- a/ports/esp32/network_common.c +++ b/ports/esp32/network_common.c @@ -45,7 +45,7 @@ #include "lwip/sockets.h" #include "lwip/dns.h" -NORETURN void esp_exceptions_helper(esp_err_t e) { +MP_NORETURN void esp_exceptions_helper(esp_err_t e) { switch (e) { case ESP_ERR_WIFI_NOT_INIT: mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Not Initialized")); @@ -77,6 +77,8 @@ NORETURN void esp_exceptions_helper(esp_err_t e) { mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Would Block")); case ESP_ERR_WIFI_NOT_CONNECT: mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Not Connected")); + case ESP_ERR_NO_MEM: + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("WiFi Out of Memory")); default: mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("Wifi Unknown Error 0x%04x"), e); } diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c index e1a8c95785..309ee0b14a 100644 --- a/ports/esp32/network_lan.c +++ b/ports/esp32/network_lan.c @@ -45,6 +45,10 @@ #include "modnetwork.h" #include "extmod/modnetwork.h" +#if PHY_LAN867X_ENABLED +#include "esp_eth_phy_lan867x.h" +#endif + typedef struct _lan_if_obj_t { base_if_obj_t base; bool initialized; @@ -156,6 +160,12 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar args[ARG_phy_type].u_int != PHY_RTL8201 && args[ARG_phy_type].u_int != PHY_KSZ8041 && args[ARG_phy_type].u_int != PHY_KSZ8081 && + #if PHY_LAN867X_ENABLED + args[ARG_phy_type].u_int != PHY_LAN8670 && + #endif + #if PHY_GENERIC_ENABLED + args[ARG_phy_type].u_int != PHY_GENERIC && + #endif #if CONFIG_ETH_USE_SPI_ETHERNET #if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL args[ARG_phy_type].u_int != PHY_KSZ8851SNL && @@ -231,7 +241,17 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar case PHY_KSZ8081: self->phy = esp_eth_phy_new_ksz80xx(&phy_config); break; + #if PHY_LAN867X_ENABLED + case PHY_LAN8670: + self->phy = esp_eth_phy_new_lan867x(&phy_config); + break; + #endif + #if PHY_GENERIC_ENABLED + case PHY_GENERIC: + self->phy = esp_eth_phy_new_generic(&phy_config); + break; #endif + #endif // CONFIG_IDF_TARGET_ESP32 #if CONFIG_ETH_USE_SPI_ETHERNET #if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL case PHY_KSZ8851SNL: { diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index 4dd5a3718c..8b700c98ef 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2018 "Eric Poulsen" <eric@zyxod.com> + * Copyright (c) 2024 Damien P. George * * Based on the ESP IDF example code which is Public Domain / CC0 * @@ -26,172 +27,265 @@ * THE SOFTWARE. */ +// This file is intended to closely match extmod/network_ppp_lwip.c. Changes can +// and should probably be applied to both files. Compare them directly by using: +// git diff --no-index extmod/network_ppp_lwip.c ports/esp32/network_ppp.c + #include "py/runtime.h" #include "py/mphal.h" -#include "py/objtype.h" #include "py/stream.h" -#include "shared/netutils/netutils.h" -#include "modmachine.h" -#include "ppp_set_auth.h" -#include "netif/ppp/ppp.h" -#include "netif/ppp/pppos.h" -#include "lwip/err.h" -#include "lwip/sockets.h" -#include "lwip/sys.h" -#include "lwip/netdb.h" +#if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT) + #include "lwip/dns.h" +#include "netif/ppp/ppp.h" #include "netif/ppp/pppapi.h" +#include "netif/ppp/pppos.h" -#if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT) +// Includes for port-specific changes compared to network_ppp_lwip.c +#include "shared/netutils/netutils.h" +#include "ppp_set_auth.h" -#define PPP_CLOSE_TIMEOUT_MS (4000) +// Enable this to see the serial data going between the PPP layer. +#define PPP_TRACE_IN_OUT (0) -typedef struct _ppp_if_obj_t { +typedef enum { + STATE_INACTIVE, + STATE_ACTIVE, + STATE_ERROR, + STATE_CONNECTING, + STATE_CONNECTED, +} network_ppp_state_t; + +typedef struct _network_ppp_obj_t { mp_obj_base_t base; - bool active; - bool connected; - volatile bool clean_close; - ppp_pcb *pcb; + network_ppp_state_t state; + int error_code; mp_obj_t stream; - SemaphoreHandle_t inactiveWaitSem; - volatile TaskHandle_t client_task_handle; - struct netif pppif; -} ppp_if_obj_t; + ppp_pcb *pcb; + struct netif netif; +} network_ppp_obj_t; -const mp_obj_type_t ppp_if_type; +const mp_obj_type_t esp_network_ppp_lwip_type; + +static mp_obj_t network_ppp___del__(mp_obj_t self_in); + +static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) { + if (self->stream == mp_const_none) { + return; + } -static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { - ppp_if_obj_t *self = ctx; - struct netif *pppif = ppp_netif(self->pcb); + // Disable UART IRQ. + mp_obj_t dest[3]; + mp_load_method(self->stream, MP_QSTR_irq, dest); + dest[2] = mp_const_none; + mp_call_method_n_kw(1, 0, dest); +} +static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { + network_ppp_obj_t *self = ctx; switch (err_code) { case PPPERR_NONE: - #if CONFIG_LWIP_IPV6 - self->connected = (pppif->ip_addr.u_addr.ip4.addr != 0); - #else - self->connected = (pppif->ip_addr.addr != 0); - #endif // CONFIG_LWIP_IPV6 + self->state = STATE_CONNECTED; break; case PPPERR_USER: - self->clean_close = true; - break; - case PPPERR_CONNECT: - self->connected = false; + if (self->state >= STATE_ERROR) { + // Indicate that we are no longer connected and thus + // only need to free the PPP PCB, not close it. + self->state = STATE_ACTIVE; + } + // Clean up the PPP PCB. + network_ppp___del__(MP_OBJ_FROM_PTR(self)); break; default: + self->state = STATE_ERROR; + self->error_code = err_code; break; } } -static mp_obj_t ppp_make_new(mp_obj_t stream) { +static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_obj_t stream = all_args[0]; + if (stream != mp_const_none) { mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); } - ppp_if_obj_t *self = mp_obj_malloc_with_finaliser(ppp_if_obj_t, &ppp_if_type); + network_ppp_obj_t *self = mp_obj_malloc_with_finaliser(network_ppp_obj_t, type); + self->state = STATE_INACTIVE; self->stream = stream; - self->active = false; - self->connected = false; - self->clean_close = false; - self->client_task_handle = NULL; + self->pcb = NULL; return MP_OBJ_FROM_PTR(self); } -MP_DEFINE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj, ppp_make_new); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) -static u32_t ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx) -#else -static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) -#endif -{ - ppp_if_obj_t *self = ctx; - - mp_obj_t stream = self->stream; - if (stream == mp_const_none) { - return 0; +static mp_obj_t network_ppp___del__(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->state >= STATE_ACTIVE) { + if (self->state >= STATE_ERROR) { + // Still connected over the stream. + // Force the connection to close, with nocarrier=1. + self->state = STATE_INACTIVE; + pppapi_close(self->pcb, 1); + } + network_ppp_stream_uart_irq_disable(self); + // Free PPP PCB and reset state. + self->state = STATE_INACTIVE; + pppapi_free(self->pcb); + self->pcb = NULL; } - - int err; - return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE); + return mp_const_none; } +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp___del___obj, network_ppp___del__); -static void pppos_client_task(void *self_in) { - ppp_if_obj_t *self = (ppp_if_obj_t *)self_in; - uint8_t buf[256]; +static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); - int len = 0; - while (ulTaskNotifyTake(pdTRUE, len <= 0) == 0) { - mp_obj_t stream = self->stream; - if (stream == mp_const_none) { - len = 0; - } else { - int err; - len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0); - if (len > 0) { - pppos_input_tcpip(self->pcb, (u8_t *)buf, len); - } - } + if (self->state <= STATE_ERROR) { + return MP_OBJ_NEW_SMALL_INT(-MP_EPERM); } - self->client_task_handle = NULL; - vTaskDelete(NULL); - for (;;) { + mp_int_t total_len = 0; + mp_obj_t stream = self->stream; + while (stream != mp_const_none) { + uint8_t buf[256]; + int err; + mp_uint_t len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0); + if (len == 0) { + break; + } + #if PPP_TRACE_IN_OUT + mp_printf(&mp_plat_print, "ppp_in(n=%u,data=", len); + for (size_t i = 0; i < len; ++i) { + mp_printf(&mp_plat_print, "%02x:", buf[i]); + } + mp_printf(&mp_plat_print, ")\n"); + #endif + pppos_input(self->pcb, (u8_t *)buf, len); + total_len += len; } + + return MP_OBJ_NEW_SMALL_INT(total_len); } +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_poll_obj, 1, 2, network_ppp_poll); -static mp_obj_t ppp_active(size_t n_args, const mp_obj_t *args) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); +static void network_ppp_stream_uart_irq_enable(network_ppp_obj_t *self) { + if (self->stream == mp_const_none) { + return; + } - if (n_args > 1) { - if (mp_obj_is_true(args[1])) { - if (self->active) { - return mp_const_true; - } + // Enable UART IRQ to call PPP.poll() when incoming data is ready. + mp_obj_t dest[4]; + mp_load_method(self->stream, MP_QSTR_irq, dest); + dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self)); + dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE); + mp_call_method_n_kw(2, 0, dest); +} - self->pcb = pppapi_pppos_create(&self->pppif, ppp_output_callback, ppp_status_cb, self); +static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + if (n_args != 1 && kwargs->used != 0) { + mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed")); + } + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); - if (self->pcb == NULL) { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("init failed")); - } - self->active = true; - } else { - if (!self->active) { - return mp_const_false; + if (kwargs->used != 0) { + for (size_t i = 0; i < kwargs->alloc; i++) { + if (mp_map_slot_is_filled(kwargs, i)) { + switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { + case MP_QSTR_stream: { + if (kwargs->table[i].value != mp_const_none) { + mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } + if (self->state >= STATE_ACTIVE) { + network_ppp_stream_uart_irq_disable(self); + } + self->stream = kwargs->table[i].value; + if (self->state >= STATE_ACTIVE) { + network_ppp_stream_uart_irq_enable(self); + } + break; + } + default: + break; + } } + } + return mp_const_none; + } - if (self->client_task_handle != NULL) { // is connecting or connected? - // Wait for PPPERR_USER, with timeout - pppapi_close(self->pcb, 0); - uint32_t t0 = mp_hal_ticks_ms(); - while (!self->clean_close && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) { - mp_hal_delay_ms(10); - } + if (n_args != 2) { + mp_raise_TypeError(MP_ERROR_TEXT("can query only one param")); + } - // Shutdown task - xTaskNotifyGive(self->client_task_handle); - t0 = mp_hal_ticks_ms(); - while (self->client_task_handle != NULL && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) { - mp_hal_delay_ms(10); + mp_obj_t val = mp_const_none; + + switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_stream: { + val = self->stream; + break; + } + case MP_QSTR_ifname: { + if (self->pcb != NULL) { + struct netif *pppif = ppp_netif(self->pcb); + char ifname[NETIF_NAMESIZE + 1] = {0}; + netif_index_to_name(netif_get_index(pppif), ifname); + if (ifname[0] != 0) { + val = mp_obj_new_str_from_cstr((char *)ifname); } } - - // Release PPP - pppapi_free(self->pcb); - self->pcb = NULL; - self->active = false; - self->connected = false; - self->clean_close = false; + break; } + default: + mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); + } + + return val; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_config_obj, 1, network_ppp_config); + +static mp_obj_t network_ppp_status(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->state == STATE_ERROR) { + return MP_OBJ_NEW_SMALL_INT(-self->error_code); + } else { + return MP_OBJ_NEW_SMALL_INT(self->state); + } +} +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_status_obj, network_ppp_status); + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +static u32_t network_ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx) +#else +static u32_t network_ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) +#endif +{ + network_ppp_obj_t *self = ctx; + #if PPP_TRACE_IN_OUT + mp_printf(&mp_plat_print, "ppp_out(n=%u,data=", len); + for (size_t i = 0; i < len; ++i) { + mp_printf(&mp_plat_print, "%02x:", ((const uint8_t *)data)[i]); + } + mp_printf(&mp_plat_print, ")\n"); + #endif + mp_obj_t stream = self->stream; + if (stream == mp_const_none) { + return 0; } - return mp_obj_new_bool(self->active); + int err; + // The return value from this output callback is the number of bytes written out. + // If it's less than the requested number of bytes then lwIP will propagate out an error. + return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE); } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_active_obj, 1, 2, ppp_active); -static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { - enum { ARG_authmode, ARG_username, ARG_password }; +static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + enum { ARG_security, ARG_user, ARG_key, ARG_authmode, ARG_username, ARG_password }; static const mp_arg_t allowed_args[] = { + { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_user, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + // Deprecated arguments for backwards compatibility { MP_QSTR_authmode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PPPAUTHTYPE_NONE} }, { MP_QSTR_username, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_password, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, @@ -200,17 +294,34 @@ static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw mp_arg_val_t parsed_args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, parsed_args); - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + // Use deprecated arguments as defaults + if (parsed_args[ARG_security].u_int == -1) { + parsed_args[ARG_security].u_int = parsed_args[ARG_authmode].u_int; + } + if (parsed_args[ARG_user].u_obj == mp_const_none) { + parsed_args[ARG_user].u_obj = parsed_args[ARG_username].u_obj; + } + if (parsed_args[ARG_key].u_obj == mp_const_none) { + parsed_args[ARG_key].u_obj = parsed_args[ARG_password].u_obj; + } + + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (self->state == STATE_INACTIVE) { + self->pcb = pppapi_pppos_create(&self->netif, network_ppp_output_callback, network_ppp_status_cb, self); + if (self->pcb == NULL) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppos_create failed")); + } + self->state = STATE_ACTIVE; - if (!self->active) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("must be active")); + network_ppp_stream_uart_irq_enable(self); } - if (self->client_task_handle != NULL) { + if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) { mp_raise_OSError(MP_EALREADY); } - switch (parsed_args[ARG_authmode].u_int) { + switch (parsed_args[ARG_security].u_int) { case PPPAUTHTYPE_NONE: case PPPAUTHTYPE_PAP: case PPPAUTHTYPE_CHAP: @@ -219,39 +330,49 @@ static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw mp_raise_ValueError(MP_ERROR_TEXT("invalid auth")); } - if (parsed_args[ARG_authmode].u_int != PPPAUTHTYPE_NONE) { - const char *username_str = mp_obj_str_get_str(parsed_args[ARG_username].u_obj); - const char *password_str = mp_obj_str_get_str(parsed_args[ARG_password].u_obj); - pppapi_set_auth(self->pcb, parsed_args[ARG_authmode].u_int, username_str, password_str); + if (parsed_args[ARG_security].u_int != PPPAUTHTYPE_NONE) { + const char *user_str = mp_obj_str_get_str(parsed_args[ARG_user].u_obj); + const char *key_str = mp_obj_str_get_str(parsed_args[ARG_key].u_obj); + pppapi_set_auth(self->pcb, parsed_args[ARG_security].u_int, user_str, key_str); } - if (pppapi_set_default(self->pcb) != ESP_OK) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("set default failed")); + + if (pppapi_set_default(self->pcb) != ERR_OK) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_set_default failed")); } ppp_set_usepeerdns(self->pcb, true); - if (pppapi_connect(self->pcb, 0) != ESP_OK) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("connect failed")); + if (pppapi_connect(self->pcb, 0) != ERR_OK) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_connect failed")); } - if (xTaskCreatePinnedToCore(pppos_client_task, "ppp", 2048, self, 1, (TaskHandle_t *)&self->client_task_handle, MP_TASK_COREID) != pdPASS) { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("failed to create worker task")); - } + self->state = STATE_CONNECTING; + + // Do a poll in case there is data waiting on the input stream. + network_ppp_poll(1, args); return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_KW(ppp_connect_obj, 1, ppp_connect_py); +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_connect_obj, 1, network_ppp_connect); -static mp_obj_t ppp_delete(mp_obj_t self_in) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_obj_t args[] = {self, mp_const_false}; - ppp_active(2, args); +static mp_obj_t network_ppp_disconnect(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) { + // Initiate close and wait for PPPERR_USER callback. + pppapi_close(self->pcb, 0); + } return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_1(ppp_delete_obj, ppp_delete); +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_disconnect_obj, network_ppp_disconnect); + +static mp_obj_t network_ppp_isconnected(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(self->state == STATE_CONNECTED); +} +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_isconnected_obj, network_ppp_isconnected); -static mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); +static mp_obj_t network_ppp_ifconfig(size_t n_args, const mp_obj_t *args) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (n_args == 1) { // get const ip_addr_t *dns; @@ -282,11 +403,11 @@ static mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) { return mp_const_none; } } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_ifconfig_obj, 1, 2, ppp_ifconfig); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_ifconfig_obj, 1, 2, network_ppp_ifconfig); -static mp_obj_t ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { +static mp_obj_t network_ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (kwargs->used == 0) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (self->pcb == NULL) { mp_raise_ValueError(MP_ERROR_TEXT("PPP not active")); } @@ -318,94 +439,64 @@ static mp_obj_t ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwar } return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_KW(ppp_ipconfig_obj, 1, ppp_ipconfig); +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_ipconfig_obj, 1, network_ppp_ipconfig); -static mp_obj_t ppp_status(mp_obj_t self_in) { - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(ppp_status_obj, ppp_status); - -static mp_obj_t ppp_isconnected(mp_obj_t self_in) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_bool(self->connected); -} -static MP_DEFINE_CONST_FUN_OBJ_1(ppp_isconnected_obj, ppp_isconnected); - -static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { - if (n_args != 1 && kwargs->used != 0) { - mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed")); - } - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); - - if (kwargs->used != 0) { - for (size_t i = 0; i < kwargs->alloc; i++) { - if (mp_map_slot_is_filled(kwargs, i)) { - switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { - case MP_QSTR_stream: { - if (kwargs->table[i].value != mp_const_none) { - mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); - } - self->stream = kwargs->table[i].value; - break; - } - default: - break; - } +static mp_obj_t network_ppp_active(size_t n_args, const mp_obj_t *args) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (n_args > 1) { + if (mp_obj_is_true(args[1])) { + if (self->state >= STATE_ACTIVE) { + return mp_const_true; } - } - return mp_const_none; - } - if (n_args != 2) { - mp_raise_TypeError(MP_ERROR_TEXT("can query only one param")); - } - - mp_obj_t val = mp_const_none; + self->pcb = pppapi_pppos_create(&self->netif, network_ppp_output_callback, network_ppp_status_cb, self); + if (self->pcb == NULL) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppos_create failed")); + } + self->state = STATE_ACTIVE; - switch (mp_obj_str_get_qstr(args[1])) { - case MP_QSTR_stream: { - val = self->stream; - break; - } - case MP_QSTR_ifname: { - if (self->pcb != NULL) { - struct netif *pppif = ppp_netif(self->pcb); - char ifname[NETIF_NAMESIZE + 1] = {0}; - netif_index_to_name(netif_get_index(pppif), ifname); - if (ifname[0] != 0) { - val = mp_obj_new_str_from_cstr((char *)ifname); - } + network_ppp_stream_uart_irq_enable(self); + } else { + if (self->state < STATE_ACTIVE) { + return mp_const_false; } - break; + + network_ppp___del__(MP_OBJ_FROM_PTR(self)); } - default: - mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); } - - return val; + return mp_obj_new_bool(self->state >= STATE_ACTIVE); } -static MP_DEFINE_CONST_FUN_OBJ_KW(ppp_config_obj, 1, ppp_config); - -static const mp_rom_map_elem_t ppp_if_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&ppp_active_obj) }, - { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&ppp_connect_obj) }, - { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&ppp_isconnected_obj) }, - { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&ppp_status_obj) }, - { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&ppp_config_obj) }, - { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&ppp_ifconfig_obj) }, - { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&ppp_ipconfig_obj) }, - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ppp_delete_obj) }, +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_active_obj, 1, 2, network_ppp_active); + +static const mp_rom_map_elem_t network_ppp_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&network_ppp___del___obj) }, + { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_ppp_config_obj) }, + { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_ppp_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&network_ppp_connect_obj) }, + { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&network_ppp_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_ppp_isconnected_obj) }, + { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_ppp_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_ppp_ipconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&network_ppp_poll_obj) }, + + { MP_ROM_QSTR(MP_QSTR_SEC_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) }, + { MP_ROM_QSTR(MP_QSTR_SEC_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) }, + { MP_ROM_QSTR(MP_QSTR_SEC_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) }, + + // Deprecated interface for backwards compatibility + { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&network_ppp_active_obj) }, { MP_ROM_QSTR(MP_QSTR_AUTH_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) }, { MP_ROM_QSTR(MP_QSTR_AUTH_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) }, { MP_ROM_QSTR(MP_QSTR_AUTH_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) }, }; -static MP_DEFINE_CONST_DICT(ppp_if_locals_dict, ppp_if_locals_dict_table); +static MP_DEFINE_CONST_DICT(network_ppp_locals_dict, network_ppp_locals_dict_table); MP_DEFINE_CONST_OBJ_TYPE( - ppp_if_type, + esp_network_ppp_lwip_type, MP_QSTR_PPP, MP_TYPE_FLAG_NONE, - locals_dict, &ppp_if_locals_dict + make_new, network_ppp_make_new, + locals_dict, &network_ppp_locals_dict ); #endif diff --git a/ports/esp32/partitions-16MiB-ota.csv b/ports/esp32/partitions-16MiB-ota.csv deleted file mode 100644 index a6f83bc46b..0000000000 --- a/ports/esp32/partitions-16MiB-ota.csv +++ /dev/null @@ -1,10 +0,0 @@ -# Partition table for MicroPython with OTA support using 16MB flash -# Notes: the offset of the partition table itself is set in -# $IDF_PATH/components/partition_table/Kconfig.projbuild. -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x4000, -otadata, data, ota, 0xd000, 0x2000, -phy_init, data, phy, 0xf000, 0x1000, -ota_0, app, ota_0, 0x10000, 0x270000, -ota_1, app, ota_1, 0x280000, 0x270000, -vfs, data, fat, 0x4f0000, 0xb10000, diff --git a/ports/esp32/partitions-16MiB.csv b/ports/esp32/partitions-16MiB.csv deleted file mode 100644 index ae926c7b94..0000000000 --- a/ports/esp32/partitions-16MiB.csv +++ /dev/null @@ -1,7 +0,0 @@ -# Notes: the offset of the partition table itself is set in -# $IDF_PATH/components/partition_table/Kconfig.projbuild. -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0xE00000, diff --git a/ports/esp32/partitions-2MiB.csv b/ports/esp32/partitions-2MiB.csv index ea6626825c..5449201a7a 100644 --- a/ports/esp32/partitions-2MiB.csv +++ b/ports/esp32/partitions-2MiB.csv @@ -4,4 +4,3 @@ nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x160000, -vfs, data, fat, 0x170000, 0x50000, diff --git a/ports/esp32/partitions-32MiB.csv b/ports/esp32/partitions-32MiB.csv deleted file mode 100644 index 31591c9949..0000000000 --- a/ports/esp32/partitions-32MiB.csv +++ /dev/null @@ -1,7 +0,0 @@ -# Notes: the offset of the partition table itself is set in -# $IDF_PATH/components/partition_table/Kconfig.projbuild. -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0x1E00000, diff --git a/ports/esp32/partitions-4MiB-ota.csv b/ports/esp32/partitions-4MiB-ota.csv index 094ad76660..9cbb422799 100644 --- a/ports/esp32/partitions-4MiB-ota.csv +++ b/ports/esp32/partitions-4MiB-ota.csv @@ -7,4 +7,3 @@ otadata, data, ota, 0xd000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, ota_0, app, ota_0, 0x10000, 0x180000, ota_1, app, ota_1, 0x190000, 0x180000, -vfs, data, fat, 0x310000, 0x0f0000, diff --git a/ports/esp32/partitions-4MiB-romfs.csv b/ports/esp32/partitions-4MiB-romfs.csv index dd02506e54..29033ff3ab 100644 --- a/ports/esp32/partitions-4MiB-romfs.csv +++ b/ports/esp32/partitions-4MiB-romfs.csv @@ -5,4 +5,3 @@ nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x1D0000, romfs, data, 0x8f, 0x1E0000, 0x20000, -vfs, data, fat, 0x200000, 0x200000, diff --git a/ports/esp32/partitions-4MiB.csv b/ports/esp32/partitions-4MiBplus.csv index 53f0f16744..460e5cc0e9 100644 --- a/ports/esp32/partitions-4MiB.csv +++ b/ports/esp32/partitions-4MiBplus.csv @@ -1,7 +1,10 @@ +# This partition table is for devices with 4MiB or more of flash. +# The first 2MiB is used for bootloader, nvs, phy_init and firmware. +# The remaining flash is for the user filesystem(s). + # Notes: the offset of the partition table itself is set in # $IDF_PATH/components/partition_table/Kconfig.projbuild. # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0x200000, diff --git a/ports/esp32/partitions-8MiB.csv b/ports/esp32/partitions-8MiB.csv deleted file mode 100644 index 582d3b50e5..0000000000 --- a/ports/esp32/partitions-8MiB.csv +++ /dev/null @@ -1,7 +0,0 @@ -# Notes: the offset of the partition table itself is set in -# $IDF_PATH/components/partition_table/Kconfig.projbuild. -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0x600000, diff --git a/ports/esp32/partitions-32MiB-ota.csv b/ports/esp32/partitions-8MiBplus-ota.csv index 7366a2ad8d..09a8e6d702 100644 --- a/ports/esp32/partitions-32MiB-ota.csv +++ b/ports/esp32/partitions-8MiBplus-ota.csv @@ -1,4 +1,7 @@ -# Partition table for MicroPython with OTA support using 32MB flash +# This partition table is for devices with 8MiB or more of flash and OTA support. +# The first 5056kiB is used for bootloader, nvs, phy_init and firmware. +# The remaining flash is for the user filesystem(s). + # Notes: the offset of the partition table itself is set in # $IDF_PATH/components/partition_table/Kconfig.projbuild. # Name, Type, SubType, Offset, Size, Flags @@ -7,4 +10,3 @@ otadata, data, ota, 0xd000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, ota_0, app, ota_0, 0x10000, 0x270000, ota_1, app, ota_1, 0x280000, 0x270000, -vfs, data, fat, 0x4f0000, 0x1B10000, diff --git a/ports/esp32/tools/metrics_esp32.py b/ports/esp32/tools/metrics_esp32.py index 5e65a78c97..9efaae63a9 100755 --- a/ports/esp32/tools/metrics_esp32.py +++ b/ports/esp32/tools/metrics_esp32.py @@ -37,7 +37,7 @@ import sys import subprocess from dataclasses import dataclass -IDF_VERS = ("v5.2.2",) +IDF_VERS = ("v5.4.1",) BUILDS = ( ("ESP32_GENERIC", ""), diff --git a/ports/esp8266/esp_init_data.c b/ports/esp8266/esp_init_data.c index c369ed58f5..a3dd6ffed1 100644 --- a/ports/esp8266/esp_init_data.c +++ b/ports/esp8266/esp_init_data.c @@ -30,7 +30,7 @@ #include "user_interface.h" #include "extmod/misc.h" -NORETURN void call_user_start(void); +MP_NORETURN void call_user_start(void); void ets_printf(const char *fmt, ...); extern char flashchip; diff --git a/ports/esp8266/esp_mphal.h b/ports/esp8266/esp_mphal.h index 0a4f92ac0b..fb2e441c97 100644 --- a/ports/esp8266/esp_mphal.h +++ b/ports/esp8266/esp_mphal.h @@ -27,11 +27,20 @@ #include "user_interface.h" #include "py/ringbuf.h" #include "shared/runtime/interrupt_char.h" +#include "ets_alt_task.h" #include "xtirq.h" #define MICROPY_BEGIN_ATOMIC_SECTION() esp_disable_irq() #define MICROPY_END_ATOMIC_SECTION(state) esp_enable_irq(state) +// During machine.time_pulse_us, feed WDT every now and then. +#define MICROPY_PY_MACHINE_TIME_PULSE_US_HOOK(dt) \ + do { \ + if ((dt & 0xffff) == 0xffff && !ets_loop_dont_feed_sw_wdt) { \ + system_soft_wdt_feed(); \ + } \ + } while (0) + void mp_sched_keyboard_interrupt(void); struct _mp_print_t; diff --git a/ports/esp8266/main.c b/ports/esp8266/main.c index 2dd7c1dece..da712fce9b 100644 --- a/ports/esp8266/main.c +++ b/ports/esp8266/main.c @@ -49,6 +49,64 @@ static char heap[38 * 1024]; +#if MICROPY_HW_HARD_FAULT_DEBUG + +static void format_hex(uint32_t hex, char *buffer) { + static const char table[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + int offset = 7; + uint32_t value = hex; + while (offset >= 0) { + buffer[offset--] = table[value & 0x0F]; + value >>= 4; + } +} + +static void print_reset_info(void) { + struct rst_info *rst_info = system_get_rst_info(); + if ((rst_info->reason == REASON_WDT_RST) || (rst_info->reason == REASON_EXCEPTION_RST) || (rst_info->reason == REASON_SOFT_WDT_RST)) { + char buffer[8]; + mp_hal_stdout_tx_str("\r\n\r\nThe system restarted due to an error.\r\n\r\nReason: "); + switch (rst_info->reason) { + case REASON_WDT_RST: + mp_hal_stdout_tx_str("WDT"); + break; + + case REASON_EXCEPTION_RST: + mp_hal_stdout_tx_str("EXCEPTION"); + break; + + case REASON_SOFT_WDT_RST: + mp_hal_stdout_tx_str("SOFT_WDT"); + break; + + default: + assert(!"Should not ever get here."); + break; + } + mp_hal_stdout_tx_str(" Cause: "); + format_hex(rst_info->exccause, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" EPC1: "); + format_hex(rst_info->epc1, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" EPC2: "); + format_hex(rst_info->epc2, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" EPC3: "); + format_hex(rst_info->epc3, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" Exception Vector address: "); + format_hex(rst_info->excvaddr, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" DEPC: "); + format_hex(rst_info->depc, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str("\r\n\r\n"); + } +} + +#endif + static void mp_reset(void) { mp_stack_set_top((void *)0x40000000); mp_stack_set_limit(8192); @@ -114,6 +172,10 @@ void init_done(void) { pyexec_event_repl_init(); #endif + #if MICROPY_HW_HARD_FAULT_DEBUG + print_reset_info(); + #endif + #if !MICROPY_REPL_EVENT_DRIVEN soft_reset: for (;;) { diff --git a/ports/esp8266/modmachine.c b/ports/esp8266/modmachine.c index 23ccf8cebf..d43fe38245 100644 --- a/ports/esp8266/modmachine.c +++ b/ports/esp8266/modmachine.c @@ -33,7 +33,6 @@ #include "os_type.h" #include "osapi.h" #include "etshal.h" -#include "ets_alt_task.h" #include "user_interface.h" // #define MACHINE_WAKE_IDLE (0x01) @@ -71,7 +70,7 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { system_update_cpu_freq(freq); } -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { system_restart(); // we must not return @@ -114,7 +113,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { } } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { // default to sleep forever uint32_t sleep_us = 0; @@ -327,32 +326,3 @@ MP_DEFINE_CONST_OBJ_TYPE( print, esp_timer_print, locals_dict, &esp_timer_locals_dict ); - -// Custom version of this function that feeds system WDT if necessary -mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us) { - int nchanges = 2; - uint32_t start = system_get_time(); // in microseconds - for (;;) { - uint32_t dt = system_get_time() - start; - - // Check if pin changed to wanted value - if (mp_hal_pin_read(pin) == pulse_level) { - if (--nchanges == 0) { - return dt; - } - pulse_level = 1 - pulse_level; - start = system_get_time(); - continue; - } - - // Check for timeout - if (dt >= timeout_us) { - return (mp_uint_t)-nchanges; - } - - // Only feed WDT every now and then, to make sure edge timing is accurate - if ((dt & 0xffff) == 0xffff && !ets_loop_dont_feed_sw_wdt) { - system_soft_wdt_feed(); - } - } -} diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 83d80a7c96..03f3bb643d 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -117,6 +117,9 @@ #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ #define MICROPY_ESP8266_APA102 (1) +// Print error information at reboot time if the board crashed. +#define MICROPY_HW_HARD_FAULT_DEBUG (0) + // No blocking wait-for-event on ESP8266, only non-blocking pump of the "OS" event // loop // diff --git a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json index 9f260b3a00..804b4fd734 100644 --- a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json +++ b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json @@ -15,7 +15,7 @@ "Metro_M7.jpg" ], "mcu": "mimxrt", - "product": "Adafruit Metro M7", + "product": "Metro M7", "thumbnail": "", "url": "https://www.adafruit.com/product/4950", "vendor": "Adafruit" diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/board.json b/ports/mimxrt/boards/OLIMEX_RT1010/board.json index 882b931f8b..a6f0316bc8 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/board.json +++ b/ports/mimxrt/boards/OLIMEX_RT1010/board.json @@ -12,8 +12,8 @@ "OLIMEX_RT1010Py.jpg" ], "mcu": "mimxrt", - "product": "Olimex_RT1010Py", + "product": "RT1010-Py", "thumbnail": "", - "url": "https://www.olimex.com/Products/ARM/NXP", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/MicroPython/RT1010-Py", + "vendor": "Olimex" } diff --git a/ports/mimxrt/cyw43_configport.h b/ports/mimxrt/cyw43_configport.h index cf2f5d4b9f..b1dd6fbe6a 100644 --- a/ports/mimxrt/cyw43_configport.h +++ b/ports/mimxrt/cyw43_configport.h @@ -28,12 +28,8 @@ #define MICROPY_INCLUDED_MIMXRT_CYW43_CONFIGPORT_H // The board-level config will be included here, so it can set some CYW43 values. -#include "py/mpconfig.h" -#include "py/mperrno.h" -#include "py/mphal.h" -#include "extmod/modnetwork.h" #include "extmod/mpbthci.h" -#include "pendsv.h" +#include "extmod/cyw43_config_common.h" #include "sdio.h" #define CYW43_USE_SPI (0) @@ -61,55 +57,15 @@ #define CYW43_BT_UART_BAUDRATE_DOWNLOAD_FIRMWARE MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE #endif -#define CYW43_IOCTL_TIMEOUT_US (1000000) -#define CYW43_SLEEP_MAX (50) -#define CYW43_NETUTILS (1) #define CYW43_CLEAR_SDIO_INT (1) -#define CYW43_EPERM MP_EPERM // Operation not permitted -#define CYW43_EIO MP_EIO // I/O error -#define CYW43_EINVAL MP_EINVAL // Invalid argument -#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out - -#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER -#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT -#define CYW43_THREAD_LOCK_CHECK - -#define CYW43_HOST_NAME mod_network_hostname_data - #define CYW43_SDPCM_SEND_COMMON_WAIT __WFI(); #define CYW43_DO_IOCTL_WAIT __WFI(); -#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) - -#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT -#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT -#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE -#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP -#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN - -#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 -#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR - -#define cyw43_hal_ticks_us mp_hal_ticks_us -#define cyw43_hal_ticks_ms mp_hal_ticks_ms - -#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t -#define cyw43_hal_pin_read mp_hal_pin_read -#define cyw43_hal_pin_low mp_hal_pin_low -#define cyw43_hal_pin_high mp_hal_pin_high - -#define cyw43_hal_get_mac mp_hal_get_mac -#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii -#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac - #define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate #define cyw43_hal_uart_write mp_bluetooth_hci_uart_write #define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar -#define cyw43_delay_us mp_hal_delay_us -#define cyw43_delay_ms mp_hal_delay_ms - #define cyw43_bluetooth_controller_init mp_bluetooth_hci_controller_init #define cyw43_bluetooth_controller_deinit mp_bluetooth_hci_controller_deinit #define cyw43_bluetooth_controller_woken mp_bluetooth_hci_controller_woken @@ -135,7 +91,7 @@ #define CYW43_PIN_RFSW_VDD pin_WL_RFSW_VDD #endif -#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) +#undef cyw43_hal_pin_config // mp_hal_pin_config not yet implemented on this port static inline void cyw43_hal_pin_config(cyw43_hal_pin_obj_t pin, uint8_t mode, uint8_t pull, uint8_t alt) { machine_pin_set_mode(pin, mode); @@ -175,6 +131,4 @@ static inline int cyw43_sdio_transfer_cmd53(bool write, uint32_t block_size, uin return sdio_transfer_cmd53(write, block_size, arg, len, buf); } -#define CYW43_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK - #endif // MICROPY_INCLUDED_MIMXRT_CYW43_CONFIGPORT_H diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index 204699bcc4..ad078ff3ac 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -87,7 +87,7 @@ static mp_obj_t mp_machine_unique_id(void) { return mp_obj_new_bytes(id, sizeof(id)); } -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { WDOG_TriggerSystemSoftwareReset(WDOG1); while (true) { ; @@ -131,7 +131,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { mp_raise_NotImplementedError(NULL); } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { if (n_args != 0) { mp_int_t seconds = mp_obj_get_int(args[0]) / 1000; if (seconds > 0) { @@ -159,7 +159,7 @@ NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { } } -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { #if defined(MICROPY_BOARD_ENTER_BOOTLOADER) // If a board has a custom bootloader, call it first. MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 9a7dfc630f..e1c605f452 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -178,20 +178,12 @@ extern const struct _mp_obj_type_t network_lan_type; #define MICROPY_HW_NIC_ETH #endif -#if MICROPY_PY_NETWORK_CYW43 -extern const struct _mp_obj_type_t mp_network_cyw43_type; -#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, -#else -#define MICROPY_HW_NIC_CYW43 -#endif - #ifndef MICROPY_BOARD_NETWORK_INTERFACES #define MICROPY_BOARD_NETWORK_INTERFACES #endif #define MICROPY_PORT_NETWORK_INTERFACES \ MICROPY_HW_NIC_ETH \ - MICROPY_HW_NIC_CYW43 \ MICROPY_BOARD_NETWORK_INTERFACES \ #ifndef MICROPY_BOARD_ROOT_POINTERS diff --git a/ports/minimal/main.c b/ports/minimal/main.c index 5f472c1afd..b9e9034bf5 100644 --- a/ports/minimal/main.c +++ b/ports/minimal/main.c @@ -87,7 +87,7 @@ void nlr_jump_fail(void *val) { } } -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { while (1) { ; } diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 59e74dce42..7b16974f97 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -129,14 +129,7 @@ CFLAGS_MCU_m4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 -mfpu=fpv4-s CFLAGS_MCU_m0 = $(CFLAGS_CORTEX_M) -fshort-enums -mtune=cortex-m0 -mcpu=cortex-m0 -mfloat-abi=soft -# linker wrap does not work with lto on older gcc/binutils: https://sourceware.org/bugzilla/show_bug.cgi?id=24406 -GCC_VERSION = $(shell arm-none-eabi-gcc -dumpversion) -GCC_MAJOR_VERS = $(word 1,$(subst ., ,$(GCC_VERSION))) -ifeq ($(shell test $(GCC_MAJOR_VERS) -ge 10; echo $$?),0) LTO ?= 1 -else -LTO ?= 0 -endif ifeq ($(LTO),1) CFLAGS += -flto @@ -268,7 +261,6 @@ SRC_C += $(addprefix lib/tinyusb/src/,\ portable/nordic/nrf5x/dcd_nrf5x.c \ ) -LDFLAGS += -Wl,--wrap=dcd_event_handler endif DRIVERS_SRC_C += $(addprefix modules/,\ diff --git a/ports/nrf/boards/ACTINIUS_ICARUS/board.json b/ports/nrf/boards/ACTINIUS_ICARUS/board.json index c1d9f1c4ef..09ed3fcba3 100644 --- a/ports/nrf/boards/ACTINIUS_ICARUS/board.json +++ b/ports/nrf/boards/ACTINIUS_ICARUS/board.json @@ -8,7 +8,7 @@ "icarus-v1.4-front-shadow-p-800.jpg" ], "mcu": "nrf91", - "product": "actinius_icarus", + "product": "Icarus", "thumbnail": "", "url": "https://www.actinius.com/icarus", "vendor": "Actinius" diff --git a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json index 9079fbd666..72b5135611 100644 --- a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json +++ b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json @@ -15,7 +15,7 @@ "ABX00031_01.iso_998x749.jpg" ], "mcu": "nrf52", - "product": "Arduino Nano 33 BLE Sense", + "product": "Nano 33 BLE Sense", "thumbnail": "", "url": "https://store.arduino.cc/products/arduino-nano-33-ble-sense", "vendor": "Arduino" diff --git a/ports/nrf/boards/ARDUINO_PRIMO/board.json b/ports/nrf/boards/ARDUINO_PRIMO/board.json index f7afed0ced..236acfd448 100644 --- a/ports/nrf/boards/ARDUINO_PRIMO/board.json +++ b/ports/nrf/boards/ARDUINO_PRIMO/board.json @@ -8,8 +8,8 @@ "arduino_primo.jpg" ], "mcu": "nrf52", - "product": "arduino_primo", + "product": "Primo", "thumbnail": "", - "url": "", + "url": "https://docs.arduino.cc/retired/boards/arduino-primo/", "vendor": "Arduino" } diff --git a/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json b/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json index 5b6e5747d5..e910ea8db2 100644 --- a/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json +++ b/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json @@ -8,7 +8,7 @@ "blyst-nano-mod-4_jpg_project-body.jpg" ], "mcu": "nrf52", - "product": "blueio_tag_evim", + "product": "BLUEIO Tag EVIM", "thumbnail": "", "url": "https://www.i-syst.com/index.php/products/blyst-nano", "vendor": "I-SYST" diff --git a/ports/nrf/boards/DVK_BL652/board.json b/ports/nrf/boards/DVK_BL652/board.json index 5c9cfad26e..d0942ca256 100644 --- a/ports/nrf/boards/DVK_BL652/board.json +++ b/ports/nrf/boards/DVK_BL652/board.json @@ -8,8 +8,8 @@ "BL652-SA_JPG-500.jpg" ], "mcu": "nrf52", - "product": "dvk_bl652", + "product": "DVK-BL652", "thumbnail": "", - "url": "https://www.lairdconnect.com/wireless-modules/bluetooth-modules/bluetooth-5-modules/bl652-series-bluetooth-v5-nfc-module", - "vendor": "Laird Connectivity" + "url": "https://www.ezurio.com/wireless-modules/bluetooth-modules/bluetooth-5-modules/bl652-series-bluetooth-v5-nfc-module", + "vendor": "Ezurio" } diff --git a/ports/nrf/boards/EVK_NINA_B1/board.json b/ports/nrf/boards/EVK_NINA_B1/board.json index 657b0fa06d..ef9c6ae5be 100644 --- a/ports/nrf/boards/EVK_NINA_B1/board.json +++ b/ports/nrf/boards/EVK_NINA_B1/board.json @@ -8,7 +8,7 @@ "EVK-NINA-B1_.jpg" ], "mcu": "nrf52", - "product": "evk_nina_b1", + "product": "EVK-NINA-B1", "thumbnail": "", "url": "https://www.u-blox.com/en/product/evk-nina-b1", "vendor": "u-blox" diff --git a/ports/nrf/boards/EVK_NINA_B3/board.json b/ports/nrf/boards/EVK_NINA_B3/board.json index 54e3dc3585..6311b17338 100644 --- a/ports/nrf/boards/EVK_NINA_B3/board.json +++ b/ports/nrf/boards/EVK_NINA_B3/board.json @@ -8,7 +8,7 @@ "EVK-NINA-B3-top.jpg" ], "mcu": "nrf52", - "product": "evk_nina_b3", + "product": "EVK-NINA-B3", "thumbnail": "", "url": "https://www.u-blox.com/en/product/evk-nina-b3", "vendor": "u-blox" diff --git a/ports/nrf/boards/IBK_BLYST_NANO/board.json b/ports/nrf/boards/IBK_BLYST_NANO/board.json index 562c33607b..ddbbef66f5 100644 --- a/ports/nrf/boards/IBK_BLYST_NANO/board.json +++ b/ports/nrf/boards/IBK_BLYST_NANO/board.json @@ -8,7 +8,7 @@ "blyst-nano-fingertip-close_jpg_content-body-gallery.jpg" ], "mcu": "nrf52", - "product": "ibk_blyst_nano", + "product": "IBK BLYST Nano", "thumbnail": "", "url": "https://www.i-syst.com/products/blyst-nano", "vendor": "I-SYST" diff --git a/ports/nrf/boards/IDK_BLYST_NANO/board.json b/ports/nrf/boards/IDK_BLYST_NANO/board.json index 199721698d..cb692f44cd 100644 --- a/ports/nrf/boards/IDK_BLYST_NANO/board.json +++ b/ports/nrf/boards/IDK_BLYST_NANO/board.json @@ -8,7 +8,7 @@ "blyst-nano-fingertip-close_jpg_content-body-gallery.jpg" ], "mcu": "nrf52", - "product": "idk_blyst_nano", + "product": "IDK BLYST Nano", "thumbnail": "", "url": "https://www.i-syst.com/products/blyst-nano", "vendor": "I-SYST" diff --git a/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json b/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json index 99d0caad23..102102b6c2 100644 --- a/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json +++ b/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json @@ -8,7 +8,7 @@ "dongle_pcba_case.jpg" ], "mcu": "nrf52", - "product": "nrf52840-mdk-usb-dongle", + "product": "nrf52840 MDK USB Dongle", "thumbnail": "", "url": "https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle", "vendor": "Makerdiary" diff --git a/ports/nrf/boards/PCA10028/mpconfigboard.h b/ports/nrf/boards/PCA10028/mpconfigboard.h index 3dc8ed3459..df2e4e85d9 100644 --- a/ports/nrf/boards/PCA10028/mpconfigboard.h +++ b/ports/nrf/boards/PCA10028/mpconfigboard.h @@ -60,3 +60,8 @@ #define MICROPY_HW_SPI0_MISO (28) #define HELP_TEXT_BOARD_LED "1,2,3,4" + +// The JLink CDC on the PCA10028 cannot accept more than 64 incoming bytes at a time. +// That makes the UART REPL unreliable in general. But it can be improved to some +// extent by setting the raw-paste buffer size to that limit of 64. +#define MICROPY_REPL_STDIN_BUFFER_MAX (64) diff --git a/ports/nrf/boards/PCA10040/mpconfigboard.h b/ports/nrf/boards/PCA10040/mpconfigboard.h index b965bf319d..2b1c4c7ef5 100644 --- a/ports/nrf/boards/PCA10040/mpconfigboard.h +++ b/ports/nrf/boards/PCA10040/mpconfigboard.h @@ -64,3 +64,8 @@ #define MICROPY_HW_PWM2_NAME "PWM2" #define HELP_TEXT_BOARD_LED "1,2,3,4" + +// The JLink CDC on the PCA10040 cannot accept more than 64 incoming bytes at a time. +// That makes the UART REPL unreliable in general. But it can be improved to some +// extent by setting the raw-paste buffer size to that limit of 64. +#define MICROPY_REPL_STDIN_BUFFER_MAX (64) diff --git a/ports/nrf/boards/SEEED_XIAO_NRF52/board.json b/ports/nrf/boards/SEEED_XIAO_NRF52/board.json index d40355bb2e..b00044a56a 100644 --- a/ports/nrf/boards/SEEED_XIAO_NRF52/board.json +++ b/ports/nrf/boards/SEEED_XIAO_NRF52/board.json @@ -16,8 +16,8 @@ "XIAO_nrf52840_front.jpg" ], "mcu": "nrf52", - "product": "SEEED XIAO nRF52840 Sense", + "product": "XIAO nRF52840 Sense", "thumbnail": "", - "url": "https://www.seeedstudio.com", + "url": "https://www.seeedstudio.com/Seeed-XIAO-BLE-Sense-nRF52840-p-5253.html", "vendor": "Seeed Studio" } diff --git a/ports/nrf/boards/WT51822_S4AT/board.json b/ports/nrf/boards/WT51822_S4AT/board.json index 89d008c6f5..2ec42ac414 100644 --- a/ports/nrf/boards/WT51822_S4AT/board.json +++ b/ports/nrf/boards/WT51822_S4AT/board.json @@ -8,8 +8,8 @@ "WT51822-S4AT.jpg" ], "mcu": "nrf51", - "product": "wt51822_s4at", + "product": "WT51822-S4AT", "thumbnail": "", - "url": "http://www.wireless-tag.com/portfolio/wt51822-s4at-2/", + "url": "https://shop.wireless-tag.com/products/esp32-c3-mini-1-10pcs-espressif-esp32-c3-mini-1-4mb-flash-pcb-antenna-15-gpios-wifi-ble-5-module-esp32-c3-module-on-esp32-c3-chip", "vendor": "Wireless-Tag" } diff --git a/ports/nrf/main.c b/ports/nrf/main.c index 29550bd77a..21a71c7c9e 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -39,6 +39,8 @@ #include "py/stackctrl.h" #include "py/gc.h" #include "py/compile.h" +#include "py/persistentcode.h" +#include "extmod/misc.h" #include "extmod/modmachine.h" #include "shared/runtime/pyexec.h" #include "readline.h" @@ -107,7 +109,7 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) { extern uint32_t _heap_start; extern uint32_t _heap_end; -void NORETURN _start(void) { +void MP_NORETURN _start(void) { // Hook for a board to run code at start up, for example check if a // bootloader should be entered instead of the main application. MICROPY_BOARD_STARTUP(); @@ -171,7 +173,8 @@ soft_reset: MP_OBJ_NEW_SMALL_INT(MICROPY_HW_UART_REPL), MP_OBJ_NEW_SMALL_INT(MICROPY_HW_UART_REPL_BAUD), }; - MP_STATE_VM(dupterm_objs[0]) = MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, make_new)((mp_obj_t)&machine_uart_type, MP_ARRAY_SIZE(args), 0, args); + mp_obj_t uart = MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, make_new)((mp_obj_t)&machine_uart_type, MP_ARRAY_SIZE(args), 0, args); + mp_os_dupterm_obj.fun.var(1, &uart); } #endif @@ -353,7 +356,7 @@ void HardFault_Handler(void) { #endif } -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { while (1) { ; } @@ -369,3 +372,16 @@ void MP_WEAK __assert_func(const char *file, int line, const char *func, const c printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); __fatal_error("Assertion failed"); } + +#if MICROPY_EMIT_MACHINE_CODE +void *nrf_native_code_commit(void *buf, unsigned int len, void *reloc) { + (void)len; + if (reloc) { + // Native code in RAM must execute from the IRAM region at 0x00800000, and so relocations + // to text must also point to this region. The MICROPY_MAKE_POINTER_CALLABLE macro will + // adjust the `buf` address from RAM to IRAM. + mp_native_relocate(reloc, buf, (uintptr_t)MICROPY_MAKE_POINTER_CALLABLE(buf) & ~1); + } + return buf; +} +#endif diff --git a/ports/nrf/modules/machine/modmachine.c b/ports/nrf/modules/machine/modmachine.c index de1d0e3124..f543265479 100644 --- a/ports/nrf/modules/machine/modmachine.c +++ b/ports/nrf/modules/machine/modmachine.c @@ -85,8 +85,6 @@ #define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&machine_info_obj) }, \ - { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, \ - { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, \ { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&machine_lightsleep_obj) }, \ { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pin_type) }, \ \ @@ -181,11 +179,11 @@ static mp_obj_t mp_machine_unique_id(void) { } // Resets the board in a manner similar to pushing the external RESET button. -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { NVIC_SystemReset(); } -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); for (;;) { } @@ -199,7 +197,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { __WFE(); } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { mp_machine_reset(); } @@ -214,24 +212,3 @@ static mp_obj_t mp_machine_get_freq(void) { static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { mp_raise_NotImplementedError(NULL); } - -static mp_obj_t machine_enable_irq(void) { - #ifndef BLUETOOTH_SD - __enable_irq(); - #else - - #endif - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_0(machine_enable_irq_obj, machine_enable_irq); - -// Resets the board in a manner similar to pushing the external RESET button. -static mp_obj_t machine_disable_irq(void) { - #ifndef BLUETOOTH_SD - __disable_irq(); - #else - - #endif - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq); diff --git a/ports/nrf/modules/machine/uart.c b/ports/nrf/modules/machine/uart.c index 8d5a73e095..4c75bf8209 100644 --- a/ports/nrf/modules/machine/uart.c +++ b/ports/nrf/modules/machine/uart.c @@ -46,6 +46,11 @@ #include "nrfx_uarte.h" #endif +#if defined(NRF52832) +// The nRF52832 cannot write more than 255 bytes at a time. +#define UART_MAX_TX_CHUNK (255) +#endif + typedef struct _machine_uart_buf_t { uint8_t tx_buf[1]; uint8_t rx_buf[1]; @@ -104,6 +109,7 @@ typedef struct _machine_uart_obj_t { uint16_t timeout_char; // timeout waiting between chars (in ms) uint8_t uart_id; bool initialized; // static flag. Initialized to False + bool attached_to_repl; #if MICROPY_PY_MACHINE_UART_IRQ uint16_t mp_irq_trigger; // user IRQ trigger mask uint16_t mp_irq_flags; // user IRQ active IRQ flags @@ -118,6 +124,13 @@ static machine_uart_obj_t machine_uart_obj[] = { }; void uart_init0(void) { + for (int i = 0; i < MP_ARRAY_SIZE(machine_uart_obj); i++) { + machine_uart_obj[i].attached_to_repl = false; + } +} + +void uart_attach_to_repl(machine_uart_obj_t *self, bool attached) { + self->attached_to_repl = attached; } static int uart_find(mp_obj_t id) { @@ -137,14 +150,16 @@ static void uart_event_handler(nrfx_uart_event_t const *p_event, void *p_context if (p_event->type == NRFX_UART_EVT_RX_DONE) { nrfx_uart_rx(self->p_uart, &self->buf.rx_buf[0], 1); int chr = self->buf.rx_buf[0]; - #if !MICROPY_PY_BLE_NUS && MICROPY_KBD_EXCEPTION - if (chr == mp_interrupt_char) { - self->buf.rx_ringbuf.iget = 0; - self->buf.rx_ringbuf.iput = 0; - mp_sched_keyboard_interrupt(); - } else - #endif - { + if (self->attached_to_repl) { + #if MICROPY_KBD_EXCEPTION + if (chr == mp_interrupt_char) { + mp_sched_keyboard_interrupt(); + } else + #endif + { + ringbuf_put((ringbuf_t *)&stdin_ringbuf, chr); + } + } else { ringbuf_put((ringbuf_t *)&self->buf.rx_ringbuf, chr); } #if MICROPY_PY_MACHINE_UART_IRQ @@ -446,17 +461,29 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf, mp_uin #endif machine_uart_obj_t *self = self_in; - nrfx_err_t err = nrfx_uart_tx(self->p_uart, buf, size); - if (err == NRFX_SUCCESS) { + + // Send data out, in chunks if needed. + mp_uint_t remaining = size; + while (remaining) { + #ifdef UART_MAX_TX_CHUNK + mp_uint_t chunk = MIN(UART_MAX_TX_CHUNK, remaining); + #else + mp_uint_t chunk = remaining; + #endif + nrfx_err_t err = nrfx_uart_tx(self->p_uart, buf, chunk); + if (err != NRFX_SUCCESS) { + *errcode = mp_hal_status_to_errno_table[err]; + return MP_STREAM_ERROR; + } while (nrfx_uart_tx_in_progress(self->p_uart)) { MICROPY_EVENT_POLL_HOOK; } - // return number of bytes written - return size; - } else { - *errcode = mp_hal_status_to_errno_table[err]; - return MP_STREAM_ERROR; + buf += chunk; + remaining -= chunk; } + + // return number of bytes written + return size; } static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { diff --git a/ports/nrf/modules/machine/uart.h b/ports/nrf/modules/machine/uart.h index 741473ab7a..85c2092458 100644 --- a/ports/nrf/modules/machine/uart.h +++ b/ports/nrf/modules/machine/uart.h @@ -34,8 +34,7 @@ typedef struct _machine_uart_obj_t machine_uart_obj_t; void uart_init0(void); -void uart_deinit(void); -void uart_irq_handler(mp_uint_t uart_id); +void uart_attach_to_repl(machine_uart_obj_t *self, bool attached); bool uart_rx_any(machine_uart_obj_t *uart_obj); int uart_rx_char(machine_uart_obj_t *uart_obj); diff --git a/ports/nrf/modules/os/modos.c b/ports/nrf/modules/os/modos.c index f000e1eeb6..97ed1e1bad 100644 --- a/ports/nrf/modules/os/modos.c +++ b/ports/nrf/modules/os/modos.c @@ -30,6 +30,7 @@ #include "py/runtime.h" #include "extmod/modmachine.h" #include "drivers/rng.h" +#include "modules/machine/uart.h" #if MICROPY_PY_OS_URANDOM // Return a bytes object with n random bytes, generated by the hardware random number generator. @@ -46,10 +47,17 @@ static MP_DEFINE_CONST_FUN_OBJ_1(mp_os_urandom_obj, mp_os_urandom); #endif #if MICROPY_PY_OS_DUPTERM -// TODO should accept any object with read/write methods. void mp_os_dupterm_stream_detached_attached(mp_obj_t stream_detached, mp_obj_t stream_attached) { - if (!(stream_attached == mp_const_none || mp_obj_get_type(stream_attached) == &machine_uart_type)) { - mp_raise_ValueError(MP_ERROR_TEXT("need a UART object")); + #if MICROPY_PY_MACHINE_UART + if (mp_obj_get_type(stream_detached) == &machine_uart_type) { + uart_attach_to_repl(MP_OBJ_TO_PTR(stream_detached), false); } + #endif + + #if MICROPY_PY_MACHINE_UART + if (mp_obj_get_type(stream_attached) == &machine_uart_type) { + uart_attach_to_repl(MP_OBJ_TO_PTR(stream_attached), true); + } + #endif } #endif // MICROPY_PY_OS_DUPTERM diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 7cc8a66d98..d52b5745d4 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -181,6 +181,7 @@ #define MICROPY_PY_MACHINE_RESET (1) #define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1) #define MICROPY_PY_MACHINE_BOOTLOADER (1) +#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1) #define MICROPY_PY_MACHINE_PULSE (0) #define MICROPY_PY_MACHINE_SOFTI2C (MICROPY_PY_MACHINE_I2C) @@ -320,7 +321,17 @@ // type definitions for the specific machine -#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) +#if defined(NRF52832) || defined(NRF52840) +// On nRF52, the physical SRAM is mapped to 0x20000000 for data access and 0x00800000 +// for instruction access. So convert addresses to make them executable. +#define MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA (1) +#define MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA (0) +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)(((uintptr_t)(p) - 0x20000000 + 0x00800000) | 1)) +void *nrf_native_code_commit(void *, unsigned int, void *); +#define MP_PLAT_COMMIT_EXEC(buf, len, reloc) nrf_native_code_commit(buf, len, reloc) +#else +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((uintptr_t)(p) | 1)) +#endif #define MP_SSIZE_MAX (0x7fffffff) diff --git a/ports/nrf/mphalport.c b/ports/nrf/mphalport.c index 9bb51deb12..3a3d3ad7a6 100644 --- a/ports/nrf/mphalport.c +++ b/ports/nrf/mphalport.c @@ -202,7 +202,7 @@ const byte mp_hal_status_to_errno_table[4] = { [HAL_TIMEOUT] = MP_ETIMEDOUT, }; -NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { +MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { mp_raise_OSError(mp_hal_status_to_errno_table[status]); } diff --git a/ports/nrf/mphalport.h b/ports/nrf/mphalport.h index a0bca58a62..12e881d7b6 100644 --- a/ports/nrf/mphalport.h +++ b/ports/nrf/mphalport.h @@ -35,6 +35,22 @@ #include "nrfx_config.h" #include "shared/runtime/interrupt_char.h" +// Entering a critical section. +#ifndef BLUETOOTH_SD +#define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq() +#define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state) +#endif + +static inline void enable_irq(mp_uint_t state) { + __set_PRIMASK(state); +} + +static inline mp_uint_t disable_irq(void) { + mp_uint_t state = __get_PRIMASK(); + __disable_irq(); + return state; +} + typedef enum { HAL_OK = 0x00, @@ -47,7 +63,7 @@ extern const unsigned char mp_hal_status_to_errno_table[4]; extern ringbuf_t stdin_ringbuf; -NORETURN void mp_hal_raise(HAL_StatusTypeDef status); +MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status); void mp_hal_set_interrupt_char(int c); // -1 to disable int mp_hal_stdin_rx_chr(void); diff --git a/ports/pic16bit/main.c b/ports/pic16bit/main.c index b1fe7321f0..437f91ca7c 100644 --- a/ports/pic16bit/main.c +++ b/ports/pic16bit/main.c @@ -121,7 +121,7 @@ void nlr_jump_fail(void *val) { } } -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { while (1) { ; } diff --git a/ports/powerpc/main.c b/ports/powerpc/main.c index 4b668c861c..bbd2082b42 100644 --- a/ports/powerpc/main.c +++ b/ports/powerpc/main.c @@ -130,7 +130,7 @@ void nlr_jump_fail(void *val) { } } -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { while (1) { ; } diff --git a/ports/qemu/mcu/arm/errorhandler.c b/ports/qemu/mcu/arm/errorhandler.c index a0e7ef930f..a647c0e153 100644 --- a/ports/qemu/mcu/arm/errorhandler.c +++ b/ports/qemu/mcu/arm/errorhandler.c @@ -63,7 +63,7 @@ static const char *EXCEPTION_NAMES_TABLE[] = { // R0-R15, PSR, Kind uintptr_t registers_copy[18] = { 0 }; -__attribute__((naked)) NORETURN void exception_handler(uintptr_t kind) { +__attribute__((naked)) MP_NORETURN void exception_handler(uintptr_t kind) { // Save registers __asm volatile ( "ldr r1, =registers_copy \n" @@ -137,39 +137,39 @@ __attribute__((naked)) NORETURN void exception_handler(uintptr_t kind) { for (;;) {} } -__attribute__((naked)) NORETURN void NMI_Handler(void) { +__attribute__((naked)) MP_NORETURN void NMI_Handler(void) { exception_handler(NMI); } -__attribute__((naked)) NORETURN void HardFault_Handler(void) { +__attribute__((naked)) MP_NORETURN void HardFault_Handler(void) { exception_handler(HARD_FAULT); } -__attribute__((naked)) NORETURN void MemManage_Handler(void) { +__attribute__((naked)) MP_NORETURN void MemManage_Handler(void) { exception_handler(MEM_MANAGE); } -__attribute__((naked)) NORETURN void BusFault_Handler(void) { +__attribute__((naked)) MP_NORETURN void BusFault_Handler(void) { exception_handler(BUS_FAULT); } -__attribute__((naked)) NORETURN void UsageFault_Handler(void) { +__attribute__((naked)) MP_NORETURN void UsageFault_Handler(void) { exception_handler(USAGE_FAULT); } -__attribute__((naked)) NORETURN void SVC_Handler(void) { +__attribute__((naked)) MP_NORETURN void SVC_Handler(void) { exception_handler(SV_CALL); } -__attribute__((naked)) NORETURN void DebugMon_Handler(void) { +__attribute__((naked)) MP_NORETURN void DebugMon_Handler(void) { exception_handler(DEBUG_MONITOR); } -__attribute__((naked)) NORETURN void PendSV_Handler(void) { +__attribute__((naked)) MP_NORETURN void PendSV_Handler(void) { exception_handler(PENDING_SV); } -__attribute__((naked)) NORETURN void SysTick_Handler(void) { +__attribute__((naked)) MP_NORETURN void SysTick_Handler(void) { exception_handler(SYSTEM_TICK); } diff --git a/ports/qemu/mcu/arm/startup.c b/ports/qemu/mcu/arm/startup.c index dbb75e3392..a7e9efb9fb 100644 --- a/ports/qemu/mcu/arm/startup.c +++ b/ports/qemu/mcu/arm/startup.c @@ -52,7 +52,7 @@ __attribute__((naked)) void Reset_Handler(void) { _start(); } -NORETURN void Default_Handler(void) { +MP_NORETURN void Default_Handler(void) { for (;;) { } } diff --git a/ports/renesas-ra/Makefile b/ports/renesas-ra/Makefile index ec47510d98..fd74c60a85 100644 --- a/ports/renesas-ra/Makefile +++ b/ports/renesas-ra/Makefile @@ -157,9 +157,6 @@ LIBSTDCPP_FILE_NAME = "$(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a)" LDFLAGS += -L"$(shell dirname $(LIBSTDCPP_FILE_NAME))" endif -# Hook tinyusb USB interrupt if used to service usb task. -LDFLAGS += --wrap=dcd_event_handler - # Options for mpy-cross MPY_CROSS_FLAGS += -march=armv7m diff --git a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json index 4c4d4e4326..70a33b3515 100644 --- a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json +++ b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json @@ -15,7 +15,7 @@ "ABX00074_01.iso_1000x750.jpg" ], "mcu": "RA6M5", - "product": "Arduino Portenta C33", + "product": "Portenta C33", "thumbnail": "", "url": "https://store.arduino.cc/pages/portenta-c33", "vendor": "Arduino" diff --git a/ports/renesas-ra/machine_uart.c b/ports/renesas-ra/machine_uart.c index b70978ad7a..196e1ccd6d 100644 --- a/ports/renesas-ra/machine_uart.c +++ b/ports/renesas-ra/machine_uart.c @@ -501,7 +501,7 @@ static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uint if (!uart_tx_busy(self)) { return 0; } - MICROPY_EVENT_POLL_HOOK + mp_event_wait_indefinite(); } while (mp_hal_ticks_ms() < timeout); *errcode = MP_ETIMEDOUT; ret = MP_STREAM_ERROR; diff --git a/ports/renesas-ra/main.c b/ports/renesas-ra/main.c index febb7b6d7a..cfc4611ecb 100644 --- a/ports/renesas-ra/main.c +++ b/ports/renesas-ra/main.c @@ -88,7 +88,7 @@ static machine_uart_obj_t machine_uart_repl_obj; static uint8_t machine_uart_repl_rxbuf[MICROPY_HW_UART_REPL_RXBUF]; #endif -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { for (volatile uint delay = 0; delay < 1000000; delay++) { } led_state(1, 1); diff --git a/ports/renesas-ra/modmachine.c b/ports/renesas-ra/modmachine.c index dc38d809dd..c23ce7e469 100644 --- a/ports/renesas-ra/modmachine.c +++ b/ports/renesas-ra/modmachine.c @@ -184,12 +184,12 @@ static mp_obj_t mp_machine_unique_id(void) { } // Resets the pyboard in a manner similar to pushing the external RESET button. -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { powerctrl_mcu_reset(); } // Activate the bootloader without BOOT* pins. -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { #if MICROPY_HW_ENABLE_STORAGE storage_flush(); #endif @@ -232,7 +232,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { powerctrl_enter_stop_mode(); } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { if (n_args != 0) { mp_obj_t args2[2] = {MP_OBJ_NULL, args[0]}; machine_rtc_wakeup(2, args2); diff --git a/ports/renesas-ra/mpconfigport.h b/ports/renesas-ra/mpconfigport.h index bd936061fa..8a116269bb 100644 --- a/ports/renesas-ra/mpconfigport.h +++ b/ports/renesas-ra/mpconfigport.h @@ -210,19 +210,11 @@ #define MP_STATE_PORT MP_STATE_VM -#if MICROPY_PY_NETWORK_ESP_HOSTED -extern const struct _mp_obj_type_t mod_network_esp_hosted_type; -#define MICROPY_HW_NIC_ESP_HOSTED { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_esp_hosted_type) }, -#else -#define MICROPY_HW_NIC_ESP_HOSTED -#endif - #ifndef MICROPY_BOARD_NETWORK_INTERFACES #define MICROPY_BOARD_NETWORK_INTERFACES #endif #define MICROPY_PORT_NETWORK_INTERFACES \ - MICROPY_HW_NIC_ESP_HOSTED \ MICROPY_BOARD_NETWORK_INTERFACES \ // Miscellaneous settings @@ -251,28 +243,17 @@ typedef unsigned int mp_uint_t; // must be pointer size typedef long mp_off_t; #if MICROPY_PY_THREAD -#define MICROPY_EVENT_POLL_HOOK \ +#define MICROPY_INTERNAL_EVENT_HOOK \ do { \ - extern void mp_handle_pending(bool); \ - mp_handle_pending(true); \ if (pyb_thread_enabled) { \ MP_THREAD_GIL_EXIT(); \ pyb_thread_yield(); \ MP_THREAD_GIL_ENTER(); \ - } else { \ - __WFI(); \ } \ } while (0); #define MICROPY_THREAD_YIELD() pyb_thread_yield() #else -#define MICROPY_EVENT_POLL_HOOK \ - do { \ - extern void mp_handle_pending(bool); \ - mp_handle_pending(true); \ - __WFI(); \ - } while (0); - #define MICROPY_THREAD_YIELD() #endif diff --git a/ports/renesas-ra/mphalport.c b/ports/renesas-ra/mphalport.c index 1c62c23dd7..455b28af19 100644 --- a/ports/renesas-ra/mphalport.c +++ b/ports/renesas-ra/mphalport.c @@ -61,7 +61,7 @@ const byte mp_hal_status_to_errno_table[4] = { [HAL_TIMEOUT] = MP_ETIMEDOUT, }; -NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { +MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { mp_raise_OSError(mp_hal_status_to_errno_table[status]); } @@ -104,7 +104,7 @@ int mp_hal_stdin_rx_chr(void) { return dupterm_c; } #endif - MICROPY_EVENT_POLL_HOOK + mp_event_wait_indefinite(); } } diff --git a/ports/renesas-ra/mphalport.h b/ports/renesas-ra/mphalport.h index 0819abeaf6..4f8be705d1 100644 --- a/ports/renesas-ra/mphalport.h +++ b/ports/renesas-ra/mphalport.h @@ -35,6 +35,14 @@ #define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV) #define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state) +// Port level Wait-for-Event macro +// +// Do not use this macro directly, include py/runtime.h and +// call mp_event_wait_indefinite() or mp_event_wait_ms(timeout). +// Uses WFI which will wake up from regular systick interrupt if not +// before from any other source. +#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) __WFI() + #define MICROPY_PY_LWIP_ENTER #define MICROPY_PY_LWIP_REENTER #define MICROPY_PY_LWIP_EXIT @@ -49,7 +57,7 @@ static inline int mp_hal_status_to_neg_errno(HAL_StatusTypeDef status) { return -mp_hal_status_to_errno_table[status]; } -NORETURN void mp_hal_raise(HAL_StatusTypeDef status); +MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status); void mp_hal_set_interrupt_char(int c); // -1 to disable static inline void mp_hal_wake_main_task_from_isr(void) { diff --git a/ports/renesas-ra/powerctrl.c b/ports/renesas-ra/powerctrl.c index 548d31679a..04c39b3511 100644 --- a/ports/renesas-ra/powerctrl.c +++ b/ports/renesas-ra/powerctrl.c @@ -174,7 +174,7 @@ const lpm_instance_t g_lpm_standby = { #endif -NORETURN void powerctrl_mcu_reset(void) { +MP_NORETURN void powerctrl_mcu_reset(void) { #if BSP_TZ_SECURE_BUILD R_BSP_NonSecureEnter(); #else @@ -185,7 +185,7 @@ NORETURN void powerctrl_mcu_reset(void) { } } -NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) { +MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) { while (1) { ; } @@ -245,7 +245,7 @@ void powerctrl_enter_stop_mode(void) { enable_irq(irq_state); } -NORETURN void powerctrl_enter_standby_mode(void) { +MP_NORETURN void powerctrl_enter_standby_mode(void) { rtc_init_finalise(); #if defined(MICROPY_BOARD_ENTER_STANDBY) diff --git a/ports/renesas-ra/powerctrl.h b/ports/renesas-ra/powerctrl.h index 34c40ea1ad..37932b1002 100644 --- a/ports/renesas-ra/powerctrl.h +++ b/ports/renesas-ra/powerctrl.h @@ -32,11 +32,11 @@ void SystemClock_Config(void); -NORETURN void powerctrl_mcu_reset(void); -NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr); +MP_NORETURN void powerctrl_mcu_reset(void); +MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr); void powerctrl_check_enter_bootloader(void); void powerctrl_enter_stop_mode(void); -NORETURN void powerctrl_enter_standby_mode(void); +MP_NORETURN void powerctrl_enter_standby_mode(void); #endif // MICROPY_INCLUDED_RA_POWERCTRL_H diff --git a/ports/renesas-ra/systick.c b/ports/renesas-ra/systick.c index 6fa02a2b6e..cb528d91d2 100644 --- a/ports/renesas-ra/systick.c +++ b/ports/renesas-ra/systick.c @@ -97,22 +97,24 @@ void HAL_Delay(uint32_t Delay) { // Core delay function that does an efficient sleep and may switch thread context. // If IRQs are enabled then we must have the GIL. -void mp_hal_delay_ms(mp_uint_t Delay) { +void mp_hal_delay_ms(mp_uint_t ms) { if (query_irq() == IRQ_STATE_ENABLED) { // IRQs enabled, so can use systick counter to do the delay uint32_t start = uwTick; + mp_uint_t elapsed = 0; // Wraparound of tick is taken care of by 2's complement arithmetic. do { // This macro will execute the necessary idle behaviour. It may // raise an exception, switch threads or enter sleep mode (waiting for // (at least) the SysTick interrupt). - MICROPY_EVENT_POLL_HOOK - } while (uwTick - start < Delay); + mp_event_wait_ms(ms - elapsed); + elapsed = uwTick - start; + } while (elapsed < ms); } else { // IRQs disabled, so need to use a busy loop for the delay. // To prevent possible overflow of the counter we use a double loop. volatile uint32_t count_1ms; - while (Delay-- > 0) { + while (ms-- > 0) { count_1ms = (MICROPY_HW_MCU_PCLK / 1000 / 10); while (count_1ms-- > 0) { __asm__ __volatile__ ("nop"); diff --git a/ports/renesas-ra/uart.c b/ports/renesas-ra/uart.c index c319b4c0a8..91714bf05b 100644 --- a/ports/renesas-ra/uart.c +++ b/ports/renesas-ra/uart.c @@ -42,7 +42,7 @@ typedef int (*KEYEX_CB)(uint32_t d); -extern void NORETURN __fatal_error(const char *msg); +extern void MP_NORETURN __fatal_error(const char *msg); #if MICROPY_KBD_EXCEPTION extern int mp_interrupt_char; static KEYEX_CB keyex_cb[MICROPY_HW_MAX_UART] = {(KEYEX_CB)NULL}; @@ -477,7 +477,7 @@ bool uart_rx_wait(machine_uart_obj_t *self, uint32_t timeout) { if (HAL_GetTick() - start >= timeout) { return false; // timeout } - MICROPY_EVENT_POLL_HOOK + mp_event_wait_ms(1); } } @@ -498,7 +498,7 @@ bool uart_tx_wait(machine_uart_obj_t *self, uint32_t timeout) { if (HAL_GetTick() - start >= timeout) { return false; // timeout } - MICROPY_EVENT_POLL_HOOK + mp_event_wait_ms(1); } } diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 53d00c7dfd..49ba8d77d6 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -84,6 +84,9 @@ endif() list(APPEND GIT_SUBMODULES lib/mbedtls) list(APPEND GIT_SUBMODULES lib/tinyusb) +# Workaround for pico-sdk host toolchain issue, see directory for details +list(APPEND CMAKE_MODULE_PATH "${MICROPY_PORT_DIR}/tools_patch") + # Include component cmake fragments include(${MICROPY_DIR}/py/py.cmake) include(${MICROPY_DIR}/extmod/extmod.cmake) @@ -522,10 +525,23 @@ target_compile_options(${MICROPY_TARGET} PRIVATE target_link_options(${MICROPY_TARGET} PRIVATE -Wl,--defsym=__micropy_c_heap_size__=${MICROPY_C_HEAP_SIZE} - -Wl,--wrap=dcd_event_handler -Wl,--wrap=runtime_init_clocks ) +if(PICO_FLASH_SIZE_BYTES GREATER 0) + target_link_options(${MICROPY_TARGET} PRIVATE + -Wl,--defsym=__micropy_flash_size__=${PICO_FLASH_SIZE_BYTES} + ) +elseif(PICO_RP2040) + target_link_options(${MICROPY_TARGET} PRIVATE + -Wl,--defsym=__micropy_flash_size__=2048k # Default to 2MB + ) +elseif(PICO_RP2350) + target_link_options(${MICROPY_TARGET} PRIVATE + -Wl,--defsym=__micropy_flash_size__=4096k # Default to 4MB + ) +endif() + if(PICO_RP2350) target_link_options(${MICROPY_TARGET} PRIVATE -Wl,--defsym=__micropy_extra_stack__=4096 @@ -594,14 +610,18 @@ endif() # todo this is a bit brittle, but we want to move a few source files into RAM (which requires # a linker script modification) until we explicitly add macro calls around the function # defs to move them into RAM. -if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM) +if (NOT MICROPY_BOARD_LINKER_SCRIPT) if(PICO_RP2040) - pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2040.ld) + set(MICROPY_BOARD_LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2040.ld) elseif(PICO_RP2350) - pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2350.ld) + set(MICROPY_BOARD_LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2350.ld) endif() endif() +if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM) + pico_set_linker_script(${MICROPY_TARGET} ${MICROPY_BOARD_LINKER_SCRIPT}) +endif() + pico_add_extra_outputs(${MICROPY_TARGET}) pico_find_compiler_with_triples(PICO_COMPILER_SIZE "${PICO_GCC_TRIPLE}" size) @@ -638,9 +658,13 @@ if(NOT PICO_NUM_EXT_GPIOS) set(PICO_NUM_EXT_GPIOS 10) endif() -if(EXISTS "${MICROPY_BOARD_DIR}/pins.csv") - set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_DIR}/pins.csv") - set(GEN_PINS_CSV_ARG --board-csv "${GEN_PINS_BOARD_CSV}") +if(NOT MICROPY_BOARD_PINS) + set(MICROPY_BOARD_PINS "${MICROPY_BOARD_DIR}/pins.csv") +endif() + +if(EXISTS "${MICROPY_BOARD_PINS}") + set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_PINS}") + set(GEN_PINS_CSV_ARG --board-csv "${MICROPY_BOARD_PINS}") endif() target_sources(${MICROPY_TARGET} PRIVATE diff --git a/ports/rp2/Makefile b/ports/rp2/Makefile index 76b5698d2c..2895faaca6 100644 --- a/ports/rp2/Makefile +++ b/ports/rp2/Makefile @@ -67,6 +67,9 @@ all: clean: $(RM) -rf $(BUILD) +deploy: all + $(Q)picotool load -x $(BUILD)/firmware.elf + # First ensure that pico-sdk is initialised, then run CMake with the # UPDATE_SUBMODULES flag to update necessary submodules for this board. # diff --git a/ports/rp2/README.md b/ports/rp2/README.md index 911d797fe0..41fca97f95 100644 --- a/ports/rp2/README.md +++ b/ports/rp2/README.md @@ -47,8 +47,10 @@ pass the board name to the build; e.g. for Raspberry Pi Pico W: ## Deploying firmware to the device Firmware can be deployed to the device by putting it into bootloader mode -(hold down BOOTSEL while powering on or resetting) and then copying -`firmware.uf2` to the USB mass storage device that appears. +(hold down BOOTSEL while powering on or resetting) and then either copying +`firmware.uf2` to the USB mass storage device that appears, or using +`picotool load -x firmware.elf`. The latter command can be accessed +conveniently via `make deploy`. If MicroPython is already installed then the bootloader can be entered by executing `import machine; machine.bootloader()` at the REPL. diff --git a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json index 5639aaa2a4..5914445ed8 100644 --- a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json +++ b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json @@ -17,7 +17,7 @@ "ABX00052_01.iso_999x750.jpg" ], "mcu": "rp2040", - "product": "Arduino Nano RP2040 Connect", + "product": "Nano RP2040 Connect", "thumbnail": "", "url": "https://store-usa.arduino.cc/products/arduino-nano-rp2040-connect", "vendor": "Arduino" diff --git a/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json b/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json index df9dd5d0de..108d632678 100644 --- a/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json +++ b/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json @@ -13,7 +13,7 @@ "pybstick-rp2040-26-broches-micropython-c.jpg" ], "mcu": "rp2040", - "product": "PYBSTICK26 RP2040", + "product": "RP2040 PYBStick", "thumbnail": "", "url": "https://shop.mchobby.be/product.php?id_product=2331", "vendor": "McHobby" diff --git a/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json b/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json index 5358db627e..4b12cfda65 100644 --- a/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json +++ b/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json @@ -15,7 +15,7 @@ "pololu_3pi_2040_robot.jpg" ], "mcu": "rp2040", - "product": "Pololu 3pi+ 2040 Robot", + "product": "3pi+ 2040 Robot", "thumbnail": "", "url": "https://www.pololu.com/3pi", "vendor": "Pololu" diff --git a/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json b/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json index 7ef383a9c4..1ec15ad591 100644 --- a/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json +++ b/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json @@ -16,7 +16,7 @@ "pololu_zumo_2040_robot.jpg" ], "mcu": "rp2040", - "product": "Pololu Zumo 2040 Robot", + "product": "Zumo 2040 Robot", "thumbnail": "", "url": "https://www.pololu.com/zumo", "vendor": "Pololu" diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json index 66cea79dde..5ee6bcb754 100644 --- a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json +++ b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json @@ -14,7 +14,7 @@ "18288-SparkFun_Pro_Micro_-_RP2040-01.jpg" ], "mcu": "rp2040", - "product": "Pro Micro RP2040", + "product": "Pro Micro - RP2040", "thumbnail": "", "url": "https://www.sparkfun.com/products/18288", "vendor": "SparkFun" diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json b/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json index e756c9bff4..ebd68bc5d9 100644 --- a/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json +++ b/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json @@ -17,7 +17,7 @@ "17745-SparkFun_Thing_Plus_-_RP2040-01a.jpg" ], "mcu": "rp2040", - "product": "Thing Plus RP2040", + "product": "Thing Plus - RP2040", "thumbnail": "", "url": "https://www.sparkfun.com/products/17745", "vendor": "SparkFun" diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h index 56071e1873..931391d972 100644 --- a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h @@ -24,5 +24,9 @@ int mp_hal_is_pin_reserved(int n); #define MICROPY_HW_PIN_RESERVED(i) mp_hal_is_pin_reserved(i) +// Set the default I2C to I2C1 on pins 18 and 19 which route to the qwiic connector +#undef PICO_DEFAULT_I2C +#define PICO_DEFAULT_I2C (1) + #define MICROPY_HW_I2C1_SDA (18) #define MICROPY_HW_I2C1_SCL (19) diff --git a/ports/rp2/boards/W5100S_EVB_PICO/board.json b/ports/rp2/boards/W5100S_EVB_PICO/board.json index daaf1cf8ac..3d052de420 100644 --- a/ports/rp2/boards/W5100S_EVB_PICO/board.json +++ b/ports/rp2/boards/W5100S_EVB_PICO/board.json @@ -13,8 +13,8 @@ "W5100S-EVB-Pico.jpg" ], "mcu": "rp2040", - "product": "Wiznet W5100S-EVB-Pico", + "product": "W5100S-EVB-Pico", "thumbnail": "", - "url": "https://www.wiznet.io/product-item/w5100s-evb-pico/", - "vendor": "Wiznet" + "url": "https://docs.wiznet.io/Product/iEthernet/W5100S/w5100s-evb-pico", + "vendor": "WIZnet" } diff --git a/ports/rp2/boards/W5500_EVB_PICO/board.json b/ports/rp2/boards/W5500_EVB_PICO/board.json index e7625c3436..d4298014c0 100644 --- a/ports/rp2/boards/W5500_EVB_PICO/board.json +++ b/ports/rp2/boards/W5500_EVB_PICO/board.json @@ -13,8 +13,8 @@ "W5500-EVB-Pico.jpg" ], "mcu": "rp2040", - "product": "Wiznet W5500-EVB-Pico", + "product": "W5500-EVB-Pico", "thumbnail": "", "url": "https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico", - "vendor": "Wiznet" + "vendor": "WIZnet" } diff --git a/ports/rp2/boards/WEACTSTUDIO/board.json b/ports/rp2/boards/WEACTSTUDIO/board.json index 8881b40e73..58d89ddf2e 100644 --- a/ports/rp2/boards/WEACTSTUDIO/board.json +++ b/ports/rp2/boards/WEACTSTUDIO/board.json @@ -12,7 +12,7 @@ "weact_rp2040.jpg" ], "mcu": "rp2040", - "product": "WeAct Studio RP2040", + "product": "Studio RP2040", "url": "https://github.com/WeActTC/WeActStudio.RP2040CoreBoard", "variants": { "FLASH_2M": "2 MiB Flash", diff --git a/ports/rp2/cyw43_configport.h b/ports/rp2/cyw43_configport.h index 5523305742..2da0b1f8a4 100644 --- a/ports/rp2/cyw43_configport.h +++ b/ports/rp2/cyw43_configport.h @@ -26,34 +26,27 @@ #ifndef MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H #define MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H -// The board-level config will be included here, so it can set some CYW43 values. -#include <stdio.h> -#include "py/mpconfig.h" -#include "py/mperrno.h" -#include "py/mphal.h" -#include "py/runtime.h" -#include "extmod/modnetwork.h" -#include "lwip/apps/mdns.h" -#include "pendsv.h" +#include "extmod/cyw43_config_common.h" #define CYW43_INCLUDE_LEGACY_F1_OVERFLOW_WORKAROUND_VARIABLES (1) #define CYW43_WIFI_NVRAM_INCLUDE_FILE "wifi_nvram_43439.h" -#define CYW43_IOCTL_TIMEOUT_US (1000000) -#define CYW43_SLEEP_MAX (10) -#define CYW43_NETUTILS (1) +#define CYW43_SLEEP_MAX (10) // Unclear why rp2 port overrides the default here #define CYW43_USE_OTP_MAC (1) -#define CYW43_PRINTF(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) -#define CYW43_EPERM MP_EPERM // Operation not permitted -#define CYW43_EIO MP_EIO // I/O error -#define CYW43_EINVAL MP_EINVAL // Invalid argument -#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out +static inline bool cyw43_poll_is_pending(void) { + return pendsv_is_pending(PENDSV_DISPATCH_CYW43); +} + +static inline void cyw43_yield(void) { + if (!cyw43_poll_is_pending()) { + best_effort_wfe_or_timeout(make_timeout_time_ms(1)); + } +} -#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER -#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT -#define CYW43_THREAD_LOCK_CHECK +#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook(); -#define CYW43_HOST_NAME mod_network_hostname_data +// set in SDK board header +#define CYW43_NUM_GPIOS CYW43_WL_GPIO_COUNT #if CYW43_PIN_WL_DYNAMIC @@ -100,36 +93,6 @@ uint cyw43_get_pin_wl(cyw43_pin_index_t pin_id); cyw43_yield(); \ } \ -#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) - -#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT -#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT -#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE -#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP -#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN - -#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 - -// set in SDK board header -#define CYW43_NUM_GPIOS CYW43_WL_GPIO_COUNT - -#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook(); - -#define cyw43_hal_ticks_us mp_hal_ticks_us -#define cyw43_hal_ticks_ms mp_hal_ticks_ms - -#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t -#define cyw43_hal_pin_config mp_hal_pin_config -#define cyw43_hal_pin_read mp_hal_pin_read -#define cyw43_hal_pin_low mp_hal_pin_low -#define cyw43_hal_pin_high mp_hal_pin_high - -#define cyw43_hal_get_mac mp_hal_get_mac -#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii -#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac - -#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) - // Bluetooth requires dynamic memory allocation to load its firmware (the allocation // call is made from pico-sdk). This allocation is always done at thread-level, not // from an IRQ, so is safe to delegate to the MicroPython GC heap. @@ -140,47 +103,4 @@ uint cyw43_get_pin_wl(cyw43_pin_index_t pin_id); #define cyw43_free m_tracked_free #endif -void cyw43_post_poll_hook(void); -static inline bool cyw43_poll_is_pending(void) { - return pendsv_is_pending(PENDSV_DISPATCH_CYW43); -} - -static inline void cyw43_yield(void) { - if (!cyw43_poll_is_pending()) { - best_effort_wfe_or_timeout(make_timeout_time_ms(1)); - } -} - -static inline void cyw43_delay_us(uint32_t us) { - uint32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - } -} - -static inline void cyw43_delay_ms(uint32_t ms) { - // PendSV may be disabled via CYW43_THREAD_ENTER, so this delay is a busy loop. - uint32_t us = ms * 1000; - uint32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - mp_event_handle_nowait(); - } -} - -#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait() - -#if LWIP_MDNS_RESPONDER == 1 - -// Hook for any additional TCP/IP initialization than needs to be done. -// Called after the netif specified by `itf` has been set up. -#ifndef CYW43_CB_TCPIP_INIT_EXTRA -#define CYW43_CB_TCPIP_INIT_EXTRA(self, itf) mdns_resp_add_netif(&self->netif[itf], mod_network_hostname_data) -#endif - -// Hook for any additional TCP/IP deinitialization than needs to be done. -// Called before the netif specified by `itf` is removed. -#ifndef CYW43_CB_TCPIP_DEINIT_EXTRA -#define CYW43_CB_TCPIP_DEINIT_EXTRA(self, itf) mdns_resp_remove_netif(&self->netif[itf]) -#endif - -#endif #endif // MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H diff --git a/ports/rp2/machine_pin.c b/ports/rp2/machine_pin.c index d9ca3f8ce3..7a2de0c0bc 100644 --- a/ports/rp2/machine_pin.c +++ b/ports/rp2/machine_pin.c @@ -46,15 +46,6 @@ #define GPIO_IRQ_ALL (0xf) -// Macros to access the state of the hardware. -#define GPIO_GET_FUNCSEL(id) ((iobank0_hw->io[(id)].ctrl & IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS) >> IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB) -#define GPIO_IS_OUT(id) (sio_hw->gpio_oe & (1 << (id))) -#define GPIO_IS_PULL_UP(id) (padsbank0_hw->io[(id)] & PADS_BANK0_GPIO0_PUE_BITS) -#define GPIO_IS_PULL_DOWN(id) (padsbank0_hw->io[(id)] & PADS_BANK0_GPIO0_PDE_BITS) - -// Open drain behaviour is simulated. -#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1 << (id))) - #ifndef MICROPY_HW_PIN_RESERVED #define MICROPY_HW_PIN_RESERVED(i) (0) #endif @@ -89,7 +80,11 @@ static const mp_irq_methods_t machine_pin_irq_methods; static const int num_intr_regs = sizeof(iobank0_hw->intr) / sizeof(iobank0_hw->intr[0]); // Mask with "1" indicating that the corresponding pin is in simulated open-drain mode. +#if NUM_BANK0_GPIOS > 32 uint64_t machine_pin_open_drain_mask; +#else +uint32_t machine_pin_open_drain_mask; +#endif #if MICROPY_HW_PIN_EXT_COUNT static inline bool is_ext_pin(__unused const machine_pin_obj_t *self) { @@ -198,13 +193,13 @@ const machine_pin_obj_t *machine_pin_find(mp_obj_t pin) { static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_pin_obj_t *self = self_in; - uint funcsel = GPIO_GET_FUNCSEL(self->id); + uint funcsel = gpio_get_function(self->id); qstr mode_qst; if (!is_ext_pin(self)) { if (funcsel == GPIO_FUNC_SIO) { if (GPIO_IS_OPEN_DRAIN(self->id)) { mode_qst = MP_QSTR_OPEN_DRAIN; - } else if (GPIO_IS_OUT(self->id)) { + } else if (gpio_is_dir_out(self->id)) { mode_qst = MP_QSTR_OUT; } else { mode_qst = MP_QSTR_IN; @@ -214,11 +209,11 @@ static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_prin } mp_printf(print, "Pin(%q, mode=%q", self->name, mode_qst); bool pull_up = false; - if (GPIO_IS_PULL_UP(self->id)) { + if (gpio_is_pulled_up(self->id)) { mp_printf(print, ", pull=%q", MP_QSTR_PULL_UP); pull_up = true; } - if (GPIO_IS_PULL_DOWN(self->id)) { + if (gpio_is_pulled_down(self->id)) { if (pull_up) { mp_printf(print, "|%q", MP_QSTR_PULL_DOWN); } else { @@ -298,7 +293,7 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid pin af: %d"), af); } gpio_set_function(self->id, af); - machine_pin_open_drain_mask &= ~(1ULL << self->id); + GPIO_DISABLE_OPEN_DRAIN(self->id); } } @@ -411,7 +406,7 @@ static mp_obj_t machine_pin_toggle(mp_obj_t self_in) { machine_pin_ext_set(self, self->last_output_value ^ 1); #endif } else if (GPIO_IS_OPEN_DRAIN(self->id)) { - if (GPIO_IS_OUT(self->id)) { + if (gpio_is_dir_out(self->id)) { gpio_set_dir(self->id, GPIO_IN); } else { gpio_set_dir(self->id, GPIO_OUT); diff --git a/ports/rp2/machine_timer.c b/ports/rp2/machine_timer.c index 6f8b04f10e..ffb4c70243 100644 --- a/ports/rp2/machine_timer.c +++ b/ports/rp2/machine_timer.c @@ -27,6 +27,7 @@ #include "py/runtime.h" #include "py/mperrno.h" #include "py/mphal.h" +#include "py/gc.h" #include "pico/time.h" #define ALARM_ID_INVALID (-1) @@ -40,13 +41,36 @@ typedef struct _machine_timer_obj_t { uint32_t mode; uint64_t delta_us; // for periodic mode mp_obj_t callback; + bool ishard; } machine_timer_obj_t; const mp_obj_type_t machine_timer_type; static int64_t alarm_callback(alarm_id_t id, void *user_data) { machine_timer_obj_t *self = user_data; - mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self)); + + if (self->ishard) { + // When executing code within a handler we must lock the scheduler to + // prevent any scheduled callbacks from running, and lock the GC to + // prevent any memory allocations. + mp_sched_lock(); + gc_lock(); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_call_function_1(self->callback, MP_OBJ_FROM_PTR(self)); + nlr_pop(); + } else { + // Uncaught exception; disable the callback so it doesn't run again. + self->mode = TIMER_MODE_ONE_SHOT; + mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in timer callback\n"); + mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + gc_unlock(); + mp_sched_unlock(); + } else { + mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self)); + } + if (self->mode == TIMER_MODE_ONE_SHOT) { return 0; } else { @@ -66,13 +90,14 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr } static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, }; + enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, ARG_hard, }; static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIMER_MODE_PERIODIC} }, { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, { MP_QSTR_tick_hz, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} }, { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_hard, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, }; // Parse args @@ -96,6 +121,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_ar } self->callback = args[ARG_callback].u_obj; + self->ishard = args[ARG_hard].u_bool; self->alarm_id = alarm_pool_add_alarm_in_us(self->pool, self->delta_us, alarm_callback, self, true); if (self->alarm_id == -1) { mp_raise_OSError(MP_ENOMEM); diff --git a/ports/rp2/memmap_mp_rp2040.ld b/ports/rp2/memmap_mp_rp2040.ld index a5799cd88b..5c8d9f4718 100644 --- a/ports/rp2/memmap_mp_rp2040.ld +++ b/ports/rp2/memmap_mp_rp2040.ld @@ -23,7 +23,7 @@ MEMORY { - FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k + FLASH(rx) : ORIGIN = 0x10000000, LENGTH = __micropy_flash_size__ RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k diff --git a/ports/rp2/memmap_mp_rp2350.ld b/ports/rp2/memmap_mp_rp2350.ld index 1e1cbbfd70..1c4770efe1 100644 --- a/ports/rp2/memmap_mp_rp2350.ld +++ b/ports/rp2/memmap_mp_rp2350.ld @@ -23,7 +23,7 @@ MEMORY { - FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 4096k + FLASH(rx) : ORIGIN = 0x10000000, LENGTH = __micropy_flash_size__ RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 58a3a8ae4d..1f1b0e2f59 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -65,7 +65,7 @@ static mp_obj_t mp_machine_unique_id(void) { return mp_obj_new_bytes(id.id, sizeof(id.id)); } -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { watchdog_reboot(0, SRAM_END, 0); for (;;) { __wfi(); @@ -82,7 +82,7 @@ static mp_int_t mp_machine_reset_cause(void) { return reset_cause; } -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB; reset_usb_boot(0, 0); @@ -137,6 +137,12 @@ static void mp_machine_idle(void) { MICROPY_INTERNAL_WFE(1); } +static void alarm_sleep_callback(uint alarm_id) { +} + +// Set this to 1 to enable some debug of the interrupt that woke the device +#define DEBUG_LIGHTSLEEP 0 + static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { mp_int_t delay_ms = 0; bool use_timer_alarm = false; @@ -167,6 +173,16 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { } #endif + #if MICROPY_PY_THREAD + static bool in_lightsleep; + if (in_lightsleep) { + // The other CPU is also in machine.lightsleep() + MICROPY_END_ATOMIC_SECTION(my_interrupts); + return; + } + in_lightsleep = true; + #endif + #if MICROPY_HW_ENABLE_USBDEV // Only disable the USB clock if a USB host has not configured the device // or if going to DORMANT mode. @@ -206,6 +222,15 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { // Disable ROSC. rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB; + #if DEBUG_LIGHTSLEEP + #if PICO_RP2040 + uint32_t pending_intr = 0; + #else + uint32_t pending_intr[2] = { 0 }; + #endif + #endif + + bool alarm_armed = false; if (n_args == 0) { #if MICROPY_PY_NETWORK_CYW43 gpio_set_dormant_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true); @@ -214,16 +239,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { } else { uint32_t save_sleep_en0 = clocks_hw->sleep_en0; uint32_t save_sleep_en1 = clocks_hw->sleep_en1; - bool timer3_enabled = irq_is_enabled(3); - - const uint32_t alarm_num = 3; - const uint32_t irq_num = TIMER_ALARM_IRQ_NUM(timer_hw, alarm_num); if (use_timer_alarm) { - // Make sure ALARM3/IRQ3 is enabled on _this_ core - if (!timer3_enabled) { - irq_set_enabled(irq_num, true); - } - hw_set_bits(&timer_hw->inte, 1u << alarm_num); // Use timer alarm to wake. clocks_hw->sleep_en0 = 0x0; #if PICO_RP2040 @@ -233,8 +249,11 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { #else #error Unknown processor #endif - timer_hw->intr = 1u << alarm_num; // clear any IRQ - timer_hw->alarm[alarm_num] = timer_hw->timerawl + delay_ms * 1000; + hardware_alarm_claim(MICROPY_HW_LIGHTSLEEP_ALARM_NUM); + hardware_alarm_set_callback(MICROPY_HW_LIGHTSLEEP_ALARM_NUM, alarm_sleep_callback); + if (hardware_alarm_set_target(MICROPY_HW_LIGHTSLEEP_ALARM_NUM, make_timeout_time_ms(delay_ms)) == PICO_OK) { + alarm_armed = true; + } } else { // TODO: Use RTC alarm to wake. clocks_hw->sleep_en0 = 0x0; @@ -264,10 +283,17 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { #endif // Go into low-power mode. - __wfi(); + if (alarm_armed) { + __wfi(); - if (!timer3_enabled) { - irq_set_enabled(irq_num, false); + #if DEBUG_LIGHTSLEEP + #if PICO_RP2040 + pending_intr = nvic_hw->ispr; + #else + pending_intr[0] = nvic_hw->ispr[0]; + pending_intr[1] = nvic_hw->ispr[1]; + #endif + #endif } clocks_hw->sleep_en0 = save_sleep_en0; clocks_hw->sleep_en1 = save_sleep_en1; @@ -282,9 +308,37 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { // Re-sync mp_hal_time_ns() counter with aon timer. mp_hal_time_ns_set_from_rtc(); + + // Note: This must be done after MICROPY_END_ATOMIC_SECTION + if (use_timer_alarm) { + if (alarm_armed) { + hardware_alarm_cancel(MICROPY_HW_LIGHTSLEEP_ALARM_NUM); + } + hardware_alarm_set_callback(MICROPY_HW_LIGHTSLEEP_ALARM_NUM, NULL); + hardware_alarm_unclaim(MICROPY_HW_LIGHTSLEEP_ALARM_NUM); + + #if DEBUG_LIGHTSLEEP + // Check irq.h for the list of IRQ's + // for rp2040 00000042: TIMER_IRQ_1 woke the device as expected + // 00000020: USBCTRL_IRQ woke the device (probably early) + // For rp2350 00000000:00000002: TIMER0_IRQ_1 woke the device as expected + // 00000000:00004000: USBCTRL_IRQ woke the device (probably early) + #if PICO_RP2040 + mp_printf(MP_PYTHON_PRINTER, "lightsleep: pending_intr=%08lx\n", pending_intr); + #else + mp_printf(MP_PYTHON_PRINTER, "lightsleep: pending_intr=%08lx:%08lx\n", pending_intr[1], pending_intr[0]); + #endif + #endif + } + + #if MICROPY_PY_THREAD + // Clearing the flag here is atomic, and we know we're the ones who set it + // (higher up, inside the critical section) + in_lightsleep = false; + #endif } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { mp_machine_lightsleep(n_args, args); mp_machine_reset(); } diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 3d65737266..35afea4fac 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -198,8 +198,9 @@ #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) // Hardware timer alarm index. Available range 0-3. -// Number 3 is currently used by pico-sdk (PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM) +// Number 3 is currently used by pico-sdk alarm pool (PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM) #define MICROPY_HW_SOFT_TIMER_ALARM_NUM (2) +#define MICROPY_HW_LIGHTSLEEP_ALARM_NUM (1) // fatfs configuration #define MICROPY_FATFS_ENABLE_LFN (2) @@ -241,46 +242,11 @@ #endif #endif -#if MICROPY_PY_NETWORK_CYW43 -extern const struct _mp_obj_type_t mp_network_cyw43_type; -#define MICROPY_HW_NIC_CYW43 \ - { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, \ - { MP_ROM_QSTR(MP_QSTR_STAT_IDLE), MP_ROM_INT(CYW43_LINK_DOWN) }, \ - { MP_ROM_QSTR(MP_QSTR_STAT_CONNECTING), MP_ROM_INT(CYW43_LINK_JOIN) }, \ - { MP_ROM_QSTR(MP_QSTR_STAT_WRONG_PASSWORD), MP_ROM_INT(CYW43_LINK_BADAUTH) }, \ - { MP_ROM_QSTR(MP_QSTR_STAT_NO_AP_FOUND), MP_ROM_INT(CYW43_LINK_NONET) }, \ - { MP_ROM_QSTR(MP_QSTR_STAT_CONNECT_FAIL), MP_ROM_INT(CYW43_LINK_FAIL) }, \ - { MP_ROM_QSTR(MP_QSTR_STAT_GOT_IP), MP_ROM_INT(CYW43_LINK_UP) }, -#else -#define MICROPY_HW_NIC_CYW43 -#endif - -#if MICROPY_PY_NETWORK_NINAW10 -// This Network interface requires the extended socket state. -#ifndef MICROPY_PY_SOCKET_EXTENDED_STATE -#define MICROPY_PY_SOCKET_EXTENDED_STATE (1) -#endif -extern const struct _mp_obj_type_t mod_network_nic_type_nina; -#define MICROPY_HW_NIC_NINAW10 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_nic_type_nina) }, -#else -#define MICROPY_HW_NIC_NINAW10 -#endif - -#if MICROPY_PY_NETWORK_WIZNET5K -extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k; -#define MICROPY_HW_NIC_WIZNET5K { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) }, -#else -#define MICROPY_HW_NIC_WIZNET5K -#endif - #ifndef MICROPY_BOARD_NETWORK_INTERFACES #define MICROPY_BOARD_NETWORK_INTERFACES #endif #define MICROPY_PORT_NETWORK_INTERFACES \ - MICROPY_HW_NIC_CYW43 \ - MICROPY_HW_NIC_NINAW10 \ - MICROPY_HW_NIC_WIZNET5K \ MICROPY_BOARD_NETWORK_INTERFACES \ // Additional entries for use with pendsv_schedule_dispatch. diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index caecb69509..b581b3b59f 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -266,17 +266,7 @@ void soft_timer_init(void) { } void mp_wfe_or_timeout(uint32_t timeout_ms) { - soft_timer_entry_t timer; - - // Note the timer doesn't have an associated callback, it just exists to create a - // hardware interrupt to wake the CPU - soft_timer_static_init(&timer, SOFT_TIMER_MODE_ONE_SHOT, 0, NULL); - soft_timer_insert(&timer, timeout_ms); - - __wfe(); - - // Clean up the timer node if it's not already - soft_timer_remove(&timer); + best_effort_wfe_or_timeout(delayed_by_ms(get_absolute_time(), timeout_ms)); } int mp_hal_is_pin_reserved(int n) { diff --git a/ports/rp2/mphalport.h b/ports/rp2/mphalport.h index 956db3ec70..5012b682b9 100644 --- a/ports/rp2/mphalport.h +++ b/ports/rp2/mphalport.h @@ -123,7 +123,18 @@ static inline mp_uint_t mp_hal_get_cpu_freq(void) { #define MP_HAL_PIN_PULL_UP (1) #define MP_HAL_PIN_PULL_DOWN (2) +// Open drain behaviour is simulated. +#if NUM_BANK0_GPIOS > 32 extern uint64_t machine_pin_open_drain_mask; +#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1ULL << id)) +#define GPIO_ENABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask |= (1ULL << id)) +#define GPIO_DISABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask &= ~(1ULL << id)) +#else +extern uint32_t machine_pin_open_drain_mask; +#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1U << id)) +#define GPIO_ENABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask |= (1U << id)) +#define GPIO_DISABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask &= ~(1U << id)) +#endif mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in); @@ -133,13 +144,13 @@ static inline unsigned int mp_hal_pin_name(mp_hal_pin_obj_t pin) { static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) { gpio_set_dir(pin, GPIO_IN); - machine_pin_open_drain_mask &= ~(1 << pin); + GPIO_DISABLE_OPEN_DRAIN(pin); gpio_set_function(pin, GPIO_FUNC_SIO); } static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) { gpio_set_dir(pin, GPIO_OUT); - machine_pin_open_drain_mask &= ~(1 << pin); + GPIO_DISABLE_OPEN_DRAIN(pin); gpio_set_function(pin, GPIO_FUNC_SIO); } @@ -151,7 +162,7 @@ static inline void mp_hal_pin_open_drain_with_value(mp_hal_pin_obj_t pin, int v) gpio_put(pin, 0); gpio_set_dir(pin, GPIO_OUT); } - machine_pin_open_drain_mask |= 1 << pin; + GPIO_ENABLE_OPEN_DRAIN(pin); gpio_set_function(pin, GPIO_FUNC_SIO); } diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c index e1e1567828..fed34be380 100644 --- a/ports/rp2/mpnetworkport.c +++ b/ports/rp2/mpnetworkport.c @@ -31,6 +31,7 @@ #if MICROPY_PY_LWIP #include "shared/runtime/softtimer.h" +#include "lwip/netif.h" #include "lwip/timeouts.h" // Poll lwIP every 64ms by default @@ -39,6 +40,9 @@ // Soft timer for running lwIP in the background. static soft_timer_entry_t mp_network_soft_timer; +// Callback for change of netif state +NETIF_DECLARE_EXT_CALLBACK(netif_callback) + #if MICROPY_PY_NETWORK_CYW43 #include "lib/cyw43-driver/src/cyw43.h" #include "lib/cyw43-driver/src/cyw43_stats.h" @@ -137,17 +141,48 @@ static void mp_network_soft_timer_callback(soft_timer_entry_t *self) { #if MICROPY_PY_NETWORK_WIZNET5K wiznet5k_poll(); #endif + + // Only keep the timer running if any TCP sockets are active, or any netif is up + struct netif *netif; + extern void *tcp_active_pcbs; + bool keep_running = (tcp_active_pcbs != NULL); + if (!keep_running) { + NETIF_FOREACH(netif) { + if (netif->flags & NETIF_FLAG_LINK_UP) { + keep_running = true; + break; + } + } + } + + // Periodic timer will re-queue as soon as this handler exits, + // one shot timer will not + mp_network_soft_timer.mode = keep_running ? SOFT_TIMER_MODE_PERIODIC : SOFT_TIMER_MODE_ONE_SHOT; } +static void mp_network_netif_status_cb(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args); + void mod_network_lwip_init(void) { soft_timer_static_init( &mp_network_soft_timer, - SOFT_TIMER_MODE_PERIODIC, + SOFT_TIMER_MODE_ONE_SHOT, LWIP_TICK_RATE_MS, mp_network_soft_timer_callback ); - soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS); + if (netif_callback.callback_fn == NULL) { + netif_add_ext_callback(&netif_callback, mp_network_netif_status_cb); + } +} + +static void mp_network_netif_status_cb(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args) { + // Start the network soft timer any time an interface comes up, unless + // it's already running + if (reason == LWIP_NSC_LINK_CHANGED && args->link_changed.state + && mp_network_soft_timer.mode == SOFT_TIMER_MODE_ONE_SHOT) { + mp_network_soft_timer.mode = SOFT_TIMER_MODE_PERIODIC; + soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS); + } } #endif // MICROPY_PY_LWIP diff --git a/ports/rp2/rp2_dma.c b/ports/rp2/rp2_dma.c index 78f69e6452..94c61e226e 100644 --- a/ports/rp2/rp2_dma.c +++ b/ports/rp2/rp2_dma.c @@ -315,7 +315,7 @@ static mp_obj_t rp2_dma_pack_ctrl(size_t n_pos_args, const mp_obj_t *pos_args, m // Pack keyword settings into a control register value, using either the default for this // DMA channel or the provided defaults rp2_dma_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - mp_uint_t value = DEFAULT_DMA_CONFIG | ((self->channel & 0xf) << 11); + mp_uint_t value = DEFAULT_DMA_CONFIG | ((self->channel & 0xf) << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB); if (n_pos_args > 1) { mp_raise_TypeError(MP_ERROR_TEXT("pack_ctrl only takes keyword arguments")); diff --git a/ports/rp2/rp2_flash.c b/ports/rp2/rp2_flash.c index a9beabf051..d6b9e13653 100644 --- a/ports/rp2/rp2_flash.c +++ b/ports/rp2/rp2_flash.c @@ -49,6 +49,19 @@ static_assert(MICROPY_HW_ROMFS_BYTES % 4096 == 0, "ROMFS size must be a multiple of 4K"); static_assert(MICROPY_HW_FLASH_STORAGE_BYTES % 4096 == 0, "Flash storage size must be a multiple of 4K"); +#ifndef MICROPY_HW_FLASH_MAX_FREQ +// Emulate Pico SDK's SYS_CLK_HZ / PICO_FLASH_SPI_CLKDIV behaviour by default. +// On RP2040 if PICO_USE_FASTEST_SUPPORTED_CLOCK is set then SYS_CLK_HZ can be +// 200MHz, potentially putting timings derived from PICO_FLASH_SPI_CLKDIV +// out of range. +#ifdef PICO_FLASH_SPI_CLKDIV +#define MICROPY_HW_FLASH_MAX_FREQ (SYS_CLK_HZ / PICO_FLASH_SPI_CLKDIV) +#else +// A default PICO_FLASH_SPI_CLKDIV of 4 is set in boot2_generic_03h.S +#define MICROPY_HW_FLASH_MAX_FREQ (SYS_CLK_HZ / 4) +#endif +#endif + #ifndef MICROPY_HW_FLASH_STORAGE_BASE #define MICROPY_HW_FLASH_STORAGE_BASE (PICO_FLASH_SIZE_BYTES - MICROPY_HW_FLASH_STORAGE_BYTES) #endif @@ -104,9 +117,8 @@ static bool use_multicore_lockout(void) { // and core1 locked out if relevant. static void __no_inline_not_in_flash_func(rp2_flash_set_timing_internal)(int clock_hz) { - // Use the minimum divisor assuming a 133MHz flash. - const int max_flash_freq = 133000000; - int divisor = (clock_hz + max_flash_freq - 1) / max_flash_freq; + // Use the minimum divisor based upon our target MICROPY_HW_FLASH_MAX_FREQ + int divisor = (clock_hz + MICROPY_HW_FLASH_MAX_FREQ - 1) / MICROPY_HW_FLASH_MAX_FREQ; #if PICO_RP2350 // Make sure flash is deselected - QMI doesn't appear to have a busy flag(!) diff --git a/ports/rp2/tools_patch/Findpioasm.cmake b/ports/rp2/tools_patch/Findpioasm.cmake new file mode 100644 index 0000000000..72e6bf6fb6 --- /dev/null +++ b/ports/rp2/tools_patch/Findpioasm.cmake @@ -0,0 +1,58 @@ +# Finds (or builds) the pioasm executable +# +# This will define the following imported targets +# +# pioasm +# + +# This is a temporary patched copy of pico-sdk file Findpioasm.cmake to work around +# a host toolchain issue with GCC 15.1: +# https://github.com/raspberrypi/pico-sdk/issues/2448 + +if (NOT TARGET pioasm) + # todo we would like to use pckgconfig to look for it first + # see https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/ + + include(ExternalProject) + + set(PIOASM_SOURCE_DIR ${PICO_SDK_PATH}/tools/pioasm) + set(PIOASM_BINARY_DIR ${CMAKE_BINARY_DIR}/pioasm) + set(PIOASM_INSTALL_DIR ${CMAKE_BINARY_DIR}/pioasm-install CACHE PATH "Directory where pioasm has been installed" FORCE) + + set(pioasmBuild_TARGET pioasmBuild) + set(pioasm_TARGET pioasm) + + if (NOT TARGET ${pioasmBuild_TARGET}) + pico_message_debug("PIOASM will need to be built") +# message("Adding external project ${pioasmBuild_Target} in ${CMAKE_CURRENT_LIST_DIR}}") + ExternalProject_Add(${pioasmBuild_TARGET} + PREFIX pioasm + SOURCE_DIR ${PIOASM_SOURCE_DIR} + BINARY_DIR ${PIOASM_BINARY_DIR} + INSTALL_DIR ${PIOASM_INSTALL_DIR} + CMAKE_ARGS + "--no-warn-unused-cli" + "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}" + "-DPIOASM_FLAT_INSTALL=1" + "-DCMAKE_INSTALL_PREFIX=${PIOASM_INSTALL_DIR}" + "-DCMAKE_RULE_MESSAGES=OFF" # quieten the build + "-DCMAKE_INSTALL_MESSAGE=NEVER" # quieten the install + # Toolchain workaround follows + "-DCMAKE_CXX_FLAGS=-include cstdint" + CMAKE_CACHE_ARGS "-DPIOASM_EXTRA_SOURCE_FILES:STRING=${PIOASM_EXTRA_SOURCE_FILES}" + BUILD_ALWAYS 1 # force dependency checking + EXCLUDE_FROM_ALL TRUE + ) + endif() + + if (CMAKE_HOST_WIN32) + set(pioasm_EXECUTABLE ${PIOASM_INSTALL_DIR}/pioasm/pioasm.exe) + else() + set(pioasm_EXECUTABLE ${PIOASM_INSTALL_DIR}/pioasm/pioasm) + endif() + add_executable(${pioasm_TARGET} IMPORTED GLOBAL) + set_property(TARGET ${pioasm_TARGET} PROPERTY IMPORTED_LOCATION + ${pioasm_EXECUTABLE}) + + add_dependencies(${pioasm_TARGET} ${pioasmBuild_TARGET}) +endif() diff --git a/ports/samd/Makefile b/ports/samd/Makefile index 005664f178..bec530d803 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -113,8 +113,6 @@ LIBSTDCPP_FILE_NAME = "$(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a)" LDFLAGS += -L"$(shell dirname $(LIBSTDCPP_FILE_NAME))" endif -LDFLAGS += --wrap=dcd_event_handler - MPY_CROSS_FLAGS += -march=$(MPY_CROSS_MCU_ARCH) SRC_C += \ diff --git a/ports/samd/boards/MINISAM_M4/mpconfigboard.h b/ports/samd/boards/MINISAM_M4/mpconfigboard.h index 6d908bdcb8..30a7a80454 100644 --- a/ports/samd/boards/MINISAM_M4/mpconfigboard.h +++ b/ports/samd/boards/MINISAM_M4/mpconfigboard.h @@ -7,4 +7,4 @@ #define MICROPY_HW_DEFAULT_I2C_ID (2) #define MICROPY_HW_DEFAULT_SPI_ID (1) -#define MICROPY_HW_QSPIFLASH GD25Q16C +#define MICROPY_HW_QSPIFLASH W25Q16JV_IQ diff --git a/ports/samd/boards/SAMD_GENERIC_D21X18/board.json b/ports/samd/boards/SAMD_GENERIC_D21X18/board.json index a14730bd1b..c00d78addd 100644 --- a/ports/samd/boards/SAMD_GENERIC_D21X18/board.json +++ b/ports/samd/boards/SAMD_GENERIC_D21X18/board.json @@ -11,6 +11,7 @@ ], "mcu": "samd21", "vendor": "Microchip", + "url": "https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/32-bit-mcus/sam-32-bit-mcus/sam-d", "product": "Generic SAMD21J18", "thumbnail": "" } diff --git a/ports/samd/boards/SAMD_GENERIC_D51X19/board.json b/ports/samd/boards/SAMD_GENERIC_D51X19/board.json index 21cb114bf6..2be5f20851 100644 --- a/ports/samd/boards/SAMD_GENERIC_D51X19/board.json +++ b/ports/samd/boards/SAMD_GENERIC_D51X19/board.json @@ -11,6 +11,7 @@ ], "mcu": "samd51", "vendor": "Microchip", + "url": "https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/32-bit-mcus/sam-32-bit-mcus/sam-d", "product": "Generic SAMD51P19", "thumbnail": "" } diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/board.json b/ports/samd/boards/SAMD_GENERIC_D51X20/board.json index ae4f83472e..8384484327 100644 --- a/ports/samd/boards/SAMD_GENERIC_D51X20/board.json +++ b/ports/samd/boards/SAMD_GENERIC_D51X20/board.json @@ -11,6 +11,7 @@ ], "mcu": "samd51", "vendor": "Microchip", + "url": "https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/32-bit-mcus/sam-32-bit-mcus/sam-d", "product": "Generic SAMD51P20", "thumbnail": "" } diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk index ddba3dcbba..b240c2587f 100644 --- a/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk +++ b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk @@ -1,13 +1,11 @@ MCU_SERIES = SAMD51 CMSIS_MCU = SAMD51P20A -LD_FILES = boards/samd51x19a.ld sections.ld +LD_FILES = boards/samd51x20a.ld sections.ld TEXT0 = 0x4000 - # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings # The size of a MCU flash filesystem will be -# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_VFSROMSIZE +# 1008k - MICROPY_HW_CODESIZE # The default for MICROPY_HW_VFSROMSIZE is 64K MICROPY_HW_CODESIZE ?= 752K -MICROPY_HW_VFSROMSIZE ?= 128K diff --git a/ports/samd/boards/SEEED_XIAO_SAMD21/board.json b/ports/samd/boards/SEEED_XIAO_SAMD21/board.json index 4d19f6b945..0d6b2fc30d 100644 --- a/ports/samd/boards/SEEED_XIAO_SAMD21/board.json +++ b/ports/samd/boards/SEEED_XIAO_SAMD21/board.json @@ -10,7 +10,7 @@ "seeeduino-xiao.jpg" ], "mcu": "samd21", - "product": "Seeeduino XIAO (SAMD21)", + "product": "XIAO SAMD21", "thumbnail": "", "url": "https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html", "vendor": "Seeed Studio" diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json new file mode 100644 index 0000000000..789bd97730 --- /dev/null +++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json @@ -0,0 +1,21 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Battery Charging", + "DAC", + "External Flash", + "USB", + "RGB LED" + ], + "images": [ + "sparkfun_readboard_turbo.jpg" + ], + "mcu": "samd21", + "product": "SparkFun RedBoard Turbo", + "thumbnail": "", + "url": "https://www.sparkfun.com/products/14812", + "vendor": "SparkFun" +} diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h new file mode 100644 index 0000000000..cd0ec51acc --- /dev/null +++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h @@ -0,0 +1,11 @@ +#define MICROPY_HW_BOARD_NAME "SparkFun RedBoard Turbo" +#define MICROPY_HW_MCU_NAME "SAMD21G18A" + +#define MICROPY_HW_XOSC32K (1) + +#define MICROPY_HW_SPIFLASH (1) +#define MICROPY_HW_SPIFLASH_ID (5) + +#define MICROPY_HW_DEFAULT_UART_ID (0) +#define MICROPY_HW_DEFAULT_I2C_ID (3) +#define MICROPY_HW_DEFAULT_SPI_ID (4) diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk new file mode 100644 index 0000000000..6ea327a650 --- /dev/null +++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk @@ -0,0 +1,8 @@ +MCU_SERIES = SAMD21 +CMSIS_MCU = SAMD21G18A +LD_FILES = boards/samd21x18a.ld sections.ld +TEXT0 = 0x2000 + +# The ?='s allow overriding in mpconfigboard.mk. +# MicroPython settings +MICROPY_HW_CODESIZE ?= 232K diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv new file mode 100644 index 0000000000..f065fc8917 --- /dev/null +++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv @@ -0,0 +1,47 @@ +# The lines contain pairs of Pin name and Pin number. +# Pin names must be valid Python identifiers. +# Pin numbers have the form Pxnn, with x being A, B, C or D. +# Lines starting with # or empty lines are ignored. + +USB_DM,PA24 +USB_DP,PA25 + +SWCLK,PA30 +SWDIO,PA31 + +D0,PA11 +D1,PA10 +D2,PA14 +D3,PA09 +D4,PA08 +D5,PA15 +D6,PA20 +D7,PA21 +D8,PA06 +D9,PA07 +D10,PA18 +D11,PA16 +D12,PA19 +D13,PA17 +A0,PA02 +A1,PB08 +A2,PB09 +A3,PA04 +A4,PA05 +A5,PB02 +AREF,PA03 +RX,PA11 +TX,PA10 +SCL,PA23 +SDA,PA22 +MOSI,PB10 +MISO,PA12 +SCK,PB11 +FLASH_MOSI,PB22 +FLASH_MISO,PB03 +FLASH_SCK,PB23 +FLASH_CS,PA13 +NEOPIXEL,PA30 + +LED_TX,PA27 +LED_RX,PA31 diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json new file mode 100644 index 0000000000..e5c5411a0c --- /dev/null +++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json @@ -0,0 +1,19 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Battery Charging", + "DAC", + "USB" + ], + "images": [ + "sparkfun_sam21_dev_breakout.jpg" + ], + "mcu": "samd21", + "product": "SparkFun SAMD21 Dev Breakout", + "thumbnail": "", + "url": "https://www.sparkfun.com/products/13672", + "vendor": "SparkFun" +} diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h new file mode 100644 index 0000000000..0679a9f4e4 --- /dev/null +++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h @@ -0,0 +1,8 @@ +#define MICROPY_HW_BOARD_NAME "SparkFun SAMD21 Dev Breakout" +#define MICROPY_HW_MCU_NAME "SAMD21G18A" + +#define MICROPY_HW_XOSC32K (1) + +#define MICROPY_HW_DEFAULT_UART_ID (0) +#define MICROPY_HW_DEFAULT_I2C_ID (3) +#define MICROPY_HW_DEFAULT_SPI_ID (4) diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk new file mode 100644 index 0000000000..8696c966bc --- /dev/null +++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk @@ -0,0 +1,4 @@ +MCU_SERIES = SAMD21 +CMSIS_MCU = SAMD21G18A +LD_FILES = boards/samd21x18a.ld sections.ld +TEXT0 = 0x2000 diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv new file mode 100644 index 0000000000..794be31800 --- /dev/null +++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv @@ -0,0 +1,45 @@ +# The lines contain pairs of Pin name and Pin number. +# Pin names must be valid Python identifiers. +# Pin numbers have the form Pxnn, with x being A, B, C or D. +# Lines starting with # or empty lines are ignored. + +USB_DM,PA24 +USB_DP,PA25 + +SWCLK,PA30 +SWDIO,PA31 + +D0,PA11 +D1,PA10 +D2,PA14 +D3,PA09 +D4,PA08 +D5,PA15 +D6,PA20 +D7,PA21 +D8,PA06 +D9,PA07 +D10,PA18 +D11,PA16 +D12,PA19 +D13,PA17 +A0,PA02 +A1,PB08 +A2,PB09 +A3,PA04 +A4,PA05 +A5,PB02 +AREF,PA03 +RX,PA11 +TX,PA10 +SCL,PA23 +SDA,PA22 +MOSI,PB10 +MISO,PA12 +SCK,PB11 +D38,PA13 +D30,PB22 +D31,PB23 + +LED_TX,PA27 +LED_RX,PB03 diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json index ee9ca9d368..6e89cebadd 100644 --- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json +++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json @@ -13,8 +13,8 @@ "sparkfun_samd51_thing_plus.jpg" ], "mcu": "samd51", - "product": "Sparkfun SAMD51 Thing Plus", + "product": "SAMD51 Thing Plus", "thumbnail": "", "url": "https://www.sparkfun.com/products/14713", - "vendor": "Sparkfun" + "vendor": "SparkFun" } diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h index fe2226a59e..9e0db7875f 100644 --- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h +++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h @@ -1,4 +1,4 @@ -#define MICROPY_HW_BOARD_NAME "Sparkfun SAMD51 Thing Plus" +#define MICROPY_HW_BOARD_NAME "SparkFun SAMD51 Thing Plus" #define MICROPY_HW_MCU_NAME "SAMD51J20A" #define MICROPY_HW_XOSC32K (1) diff --git a/ports/samd/machine_i2c.c b/ports/samd/machine_i2c.c index 03af3d29ed..172518523d 100644 --- a/ports/samd/machine_i2c.c +++ b/ports/samd/machine_i2c.c @@ -37,9 +37,9 @@ #include "genhdr/pins.h" #include "clock_config.h" -#define DEFAULT_I2C_FREQ (400000) -#define RISETIME_NS (200) -#define I2C_TIMEOUT (100) +#define DEFAULT_I2C_FREQ (400000) +#define RISETIME_NS (200) +#define DEFAULT_I2C_TIMEOUT (50000) #define IS_BUS_BUSY (i2c->I2CM.STATUS.bit.BUSSTATE == 3) #define NACK_RECVD (i2c->I2CM.STATUS.bit.RXNACK == 1) @@ -67,6 +67,7 @@ typedef struct _machine_i2c_obj_t { uint8_t state; uint32_t freq; uint32_t timeout; + uint32_t timer; size_t len; uint8_t *buf; } machine_i2c_obj_t; @@ -88,7 +89,7 @@ void common_i2c_irq_handler(int i2c_id) { if (self->len > 0) { *(self->buf)++ = i2c->I2CM.DATA.reg; self->len--; - self->timeout = I2C_TIMEOUT; + self->timer = self->timeout; } if (self->len > 0) { // no ACK at the last byte PREPARE_ACK; // Send ACK @@ -105,7 +106,7 @@ void common_i2c_irq_handler(int i2c_id) { } else if (self->len > 0) { // data to be sent i2c->I2CM.DATA.bit.DATA = *(self->buf)++; self->len--; - self->timeout = I2C_TIMEOUT; + self->timer = self->timeout; } else { // No data left, if there was any. self->state = state_done; i2c->I2CM.INTFLAG.reg |= SERCOM_I2CM_INTFLAG_MB; @@ -120,12 +121,13 @@ void common_i2c_irq_handler(int i2c_id) { static void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "I2C(%u, freq=%u, scl=\"%q\", sda=\"%q\")", - self->id, self->freq, pin_find_by_id(self->scl)->name, pin_find_by_id(self->sda)->name); + mp_printf(print, "I2C(%u, freq=%u, scl=\"%q\", sda=\"%q\", timeout=%u)", + self->id, self->freq, pin_find_by_id(self->scl)->name, pin_find_by_id(self->sda)->name, + self->timeout * 1000); } mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_id, ARG_freq, ARG_scl, ARG_sda }; + enum { ARG_id, ARG_freq, ARG_scl, ARG_sda, ARG_timeout }; static const mp_arg_t allowed_args[] = { #if MICROPY_HW_DEFAULT_I2C_ID < 0 { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, @@ -140,6 +142,7 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, #endif + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_I2C_TIMEOUT} }, }; // Parse args. @@ -168,6 +171,8 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n } MP_STATE_PORT(sercom_table[self->id]) = self; self->freq = args[ARG_freq].u_int; + // The unit for ARG_timeout is us, but the code uses ms. + self->timeout = args[ARG_timeout].u_int / 1000; // Configure the Pin mux. mp_hal_set_pin_mux(self->scl, scl_pad_config.alt_fct); @@ -224,13 +229,13 @@ static int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si machine_i2c_obj_t *self = (machine_i2c_obj_t *)self_in; Sercom *i2c = self->instance; - self->timeout = I2C_TIMEOUT; + self->timer = self->timeout; self->len = len; self->buf = buf; // Wait a while if the bus is busy - while (IS_BUS_BUSY && self->timeout) { + while (IS_BUS_BUSY && self->timer) { MICROPY_EVENT_POLL_HOOK - if (--self->timeout == 0) { + if (--self->timer == 0) { return -MP_ETIMEDOUT; } } @@ -242,9 +247,9 @@ static int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si i2c->I2CM.ADDR.bit.ADDR = (addr << 1) | READ_MODE; // Transfer the data - self->timeout = I2C_TIMEOUT; - while (self->state == state_busy && self->timeout) { - self->timeout--; + self->timer = self->timeout; + while (self->state == state_busy && self->timer) { + self->timer--; MICROPY_EVENT_POLL_HOOK } i2c->I2CM.INTENCLR.reg = SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB | SERCOM_I2CM_INTENSET_ERROR; @@ -256,7 +261,7 @@ static int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si } else if (self->state == state_buserr) { SET_STOP_STATE; return -MP_EIO; - } else if (self->timeout == 0) { + } else if (self->timer == 0) { SET_STOP_STATE; return -MP_ETIMEDOUT; } diff --git a/ports/samd/machine_rtc.c b/ports/samd/machine_rtc.c index 74c2266d60..4b03488b71 100644 --- a/ports/samd/machine_rtc.c +++ b/ports/samd/machine_rtc.c @@ -133,6 +133,7 @@ static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args, } #endif + mp_hal_time_ns_set_from_rtc(); return mp_const_none; } } diff --git a/ports/samd/main.c b/ports/samd/main.c index 2bbaf63e6e..a7da95582f 100644 --- a/ports/samd/main.c +++ b/ports/samd/main.c @@ -28,6 +28,7 @@ #include "py/runtime.h" #include "py/gc.h" #include "py/mperrno.h" +#include "py/mphal.h" #include "py/stackctrl.h" #include "shared/readline/readline.h" #include "shared/runtime/gchelper.h" @@ -45,6 +46,7 @@ extern void sercom_deinit_all(void); void samd_main(void) { mp_stack_set_top(&_estack); mp_stack_set_limit(&_estack - &_sstack - 1024); + mp_hal_time_ns_set_from_rtc(); for (;;) { gc_init(&_sheap, &_eheap); diff --git a/ports/samd/mcu/samd51/mpconfigmcu.h b/ports/samd/mcu/samd51/mpconfigmcu.h index 831949bab5..8cce90b886 100644 --- a/ports/samd/mcu/samd51/mpconfigmcu.h +++ b/ports/samd/mcu/samd51/mpconfigmcu.h @@ -23,7 +23,7 @@ unsigned long trng_random_u32(void); #define MICROPY_FATFS_MAX_SS (4096) #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ -#define VFS_BLOCK_SIZE_BYTES (1536) // +#define VFS_BLOCK_SIZE_BYTES (2048) // #ifndef MICROPY_HW_UART_TXBUF #define MICROPY_HW_UART_TXBUF (1) diff --git a/ports/samd/modmachine.c b/ports/samd/modmachine.c index 65dbf8a7f5..c20ce8d641 100644 --- a/ports/samd/modmachine.c +++ b/ports/samd/modmachine.c @@ -42,7 +42,7 @@ #define DBL_TAP_ADDR ((volatile uint32_t *)(HSRAM_ADDR + HSRAM_SIZE - 4)) #endif // A board may define a DPL_TAP_ADDR_ALT, which will be set as well -// Needed at the moment for Sparkfun SAMD51 Thing Plus +// Needed at the moment for SparkFun SAMD51 Thing Plus #define DBL_TAP_MAGIC_LOADER 0xf01669ef #define DBL_TAP_MAGIC_RESET 0xf02669ef @@ -65,7 +65,7 @@ extern bool EIC_occured; extern uint32_t _dbl_tap_addr; -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { *DBL_TAP_ADDR = DBL_TAP_MAGIC_RESET; #ifdef DBL_TAP_ADDR_ALT *DBL_TAP_ADDR_ALT = DBL_TAP_MAGIC_RESET; @@ -73,7 +73,7 @@ NORETURN static void mp_machine_reset(void) { NVIC_SystemReset(); } -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { *DBL_TAP_ADDR = DBL_TAP_MAGIC_LOADER; #ifdef DBL_TAP_ADDR_ALT *DBL_TAP_ADDR_ALT = DBL_TAP_MAGIC_LOADER; @@ -164,7 +164,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { set_cpu_freq(freq); } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { mp_machine_lightsleep(n_args, args); mp_machine_reset(); } diff --git a/ports/samd/modtime.c b/ports/samd/modtime.c index 83072dd16f..0bed3cb83a 100644 --- a/ports/samd/modtime.c +++ b/ports/samd/modtime.c @@ -28,6 +28,8 @@ #include "shared/timeutils/timeutils.h" #include "modmachine.h" +static uint64_t time_us_64_offset_from_epoch; + // Return the localtime as an 8-tuple. static mp_obj_t mp_time_localtime_get(void) { timeutils_struct_time_t tm; @@ -54,3 +56,15 @@ static mp_obj_t mp_time_time_get(void) { return mp_obj_new_int_from_uint(timeutils_mktime( tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec)); } + +void mp_hal_time_ns_set_from_rtc(void) { + timeutils_struct_time_t tm; + rtc_gettime(&tm); + uint64_t time_us = (uint64_t)timeutils_mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec) * 1000000ULL; + time_us_64_offset_from_epoch = time_us - mp_hal_ticks_us_64(); +} + +uint64_t mp_hal_time_ns(void) { + return (time_us_64_offset_from_epoch + mp_hal_ticks_us_64()) * 1000ULL; +} diff --git a/ports/samd/mphalport.h b/ports/samd/mphalport.h index 6c9e525bb9..69e2b60640 100644 --- a/ports/samd/mphalport.h +++ b/ports/samd/mphalport.h @@ -47,6 +47,7 @@ extern int mp_interrupt_char; extern ringbuf_t stdin_ringbuf; extern volatile uint32_t systick_ms; uint64_t mp_hal_ticks_us_64(void); +void mp_hal_time_ns_set_from_rtc(void); void mp_hal_set_interrupt_char(int c); @@ -94,10 +95,6 @@ static inline mp_uint_t mp_hal_ticks_cpu(void) { } #endif -static inline uint64_t mp_hal_time_ns(void) { - return mp_hal_ticks_us_64() * 1000; -} - // C-level pin HAL #include "py/obj.h" diff --git a/ports/samd/samd_qspiflash.c b/ports/samd/samd_qspiflash.c index e25ddf21ef..f314a95519 100644 --- a/ports/samd/samd_qspiflash.c +++ b/ports/samd/samd_qspiflash.c @@ -105,7 +105,6 @@ static const external_flash_device possible_devices[] = { #define EXTERNAL_FLASH_DEVICE_COUNT MP_ARRAY_SIZE(possible_devices) static external_flash_device const *flash_device; -static external_flash_device generic_config = GENERIC; extern const mp_obj_type_t samd_qspiflash_type; // The QSPIflash object is a singleton @@ -248,15 +247,6 @@ static uint8_t get_baud(int32_t freq_mhz) { return baud; } -int get_sfdp_table(uint8_t *table, int maxlen) { - uint8_t header[16]; - read_memory_single(QSPI_CMD_READ_SFDP_PARAMETER, 0, header, sizeof(header)); - int len = MIN(header[11] * 4, maxlen); - int addr = header[12] + (header[13] << 8) + (header[14] << 16); - read_memory_single(QSPI_CMD_READ_SFDP_PARAMETER, addr, table, len); - return len; -} - static mp_obj_t samd_qspiflash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { mp_arg_check_num(n_args, n_kw, 0, 0, false); @@ -297,19 +287,6 @@ static mp_obj_t samd_qspiflash_make_new(const mp_obj_type_t *type, size_t n_args uint8_t jedec_ids[3]; read_command(QSPI_CMD_READ_JEDEC_ID, jedec_ids, sizeof(jedec_ids)); - // Read the common sfdp table - // Check the device addr length, support of 1-1-4 mode and get the sector size - uint8_t sfdp_table[128]; - int len = get_sfdp_table(sfdp_table, sizeof(sfdp_table)); - if (len >= 29) { - self->sectorsize = 1 << sfdp_table[28]; - bool addr4b = ((sfdp_table[2] >> 1) & 0x03) == 0x02; - bool supports_qspi_114 = (sfdp_table[2] & 0x40) != 0; - if (addr4b || !supports_qspi_114) { - mp_raise_ValueError(MP_ERROR_TEXT("QSPI mode not supported")); - } - } - // Check, if the flash device is known and get it's properties. flash_device = NULL; for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) { @@ -321,15 +298,8 @@ static mp_obj_t samd_qspiflash_make_new(const mp_obj_type_t *type, size_t n_args break; } } - - // If the flash device is not known, try generic config options if (flash_device == NULL) { - if (jedec_ids[0] == 0xc2) { // Macronix devices - generic_config.quad_enable_bit_mask = 0x04; - generic_config.single_status_byte = true; - } - generic_config.total_size = 1 << jedec_ids[2]; - flash_device = &generic_config; + mp_raise_ValueError(MP_ERROR_TEXT("QSPI device not supported")); } self->size = flash_device->total_size; diff --git a/ports/samd/samd_spiflash.c b/ports/samd/samd_spiflash.c index 8ada7e9609..63dc0a8304 100644 --- a/ports/samd/samd_spiflash.c +++ b/ports/samd/samd_spiflash.c @@ -67,6 +67,19 @@ typedef struct _spiflash_obj_t { extern const mp_obj_type_t samd_spiflash_type; +typedef struct _spiflash_jedec_id_t { + uint32_t jedec_id; + uint32_t mask; + uint32_t size; +} spiflash_jedec_id_t; + +spiflash_jedec_id_t jedec_id_table[] = { + { 0x1f8401, 0xffff00, 512 * 1024 }, // Adesto/Renesas 4 MBit + { 0x1f2400, 0xffff00, 512 * 1024 }, // Adesto 4 MBit + { 0x1f4501, 0xffff00, 1024 * 1024 }, // Adesto/Renesas/Atmel 8 MBit + { 0xc84013, 0xffffff, 512 * 1024 }, // Gigadevices 4 MBit +}; + // The SPIflash object is a singleton static spiflash_obj_t spiflash_obj = { { &samd_spiflash_type }, NULL, 0, false, PAGE_SIZE, SECTOR_SIZE, NULL, 0 @@ -124,6 +137,7 @@ static void write_enable(spiflash_obj_t *self) { mp_hal_pin_write(self->cs, 1); } +#if !defined(MICROPY_HW_SPIFLASH_SIZE) // Write status register 1 static void write_sr1(spiflash_obj_t *self, uint8_t value) { uint8_t msg[2]; @@ -134,6 +148,7 @@ static void write_sr1(spiflash_obj_t *self, uint8_t value) { spi_transfer(self->spi, 2, msg, NULL); mp_hal_pin_write(self->cs, 1); } +#endif static void get_sfdp(spiflash_obj_t *self, uint32_t addr, uint8_t *buffer, int size) { uint8_t dummy[1]; @@ -170,32 +185,46 @@ static mp_obj_t spiflash_make_new(const mp_obj_type_t *type, size_t n_args, size // Get the flash size from the device ID (default) uint8_t id[3]; get_id(self, id); - bool read_sfdp = true; - - if (id[1] == 0x84 && id[2] == 1) { // Adesto - self->size = 512 * 1024; - } else if (id[0] == 0x1f && id[1] == 0x45 && id[2] == 1) { // Adesto/Renesas 8 MBit - self->size = 1024 * 1024; - read_sfdp = false; - self->sectorsize = 4096; - self->addr_is_32bit = false; - // Globally unlock the sectors, which are locked after power on. - write_enable(self); - write_sr1(self, 0); - } else { - self->size = 1 << id[2]; + + #if defined(MICROPY_HW_SPIFLASH_SIZE) + self->size = MICROPY_HW_SPIFLASH_SIZE; + #else + // Assume as default that the size is coded into the last JEDEC ID byte. + self->size = 1 << id[2]; + // Look for specific flash devices with different encoding. + uint32_t jedec_id = (id[0] << 16) | (id[1] << 8) | id[2]; + for (int i = 0; i < MP_ARRAY_SIZE(jedec_id_table); i++) { + if (jedec_id_table[i].jedec_id == (jedec_id & jedec_id_table[i].mask)) { + self->size = jedec_id_table[i].size; + // Globally unlock the sectors, which may be locked after power on. + write_enable(self); + write_sr1(self, 0); + break; + } } + #endif - // Get the addr_is_32bit flag and the sector size - if (read_sfdp) { - uint8_t buffer[128]; - get_sfdp(self, 0, buffer, 16); // get the header + // Get the flash size, addr_is_32bit flag and sector size from SFDP, if present. + uint8_t buffer[128]; + get_sfdp(self, 0, buffer, 16); // get the header + if (*(uint32_t *)buffer == 0x50444653) { // Header signature "SFDP" int len = MIN(buffer[11] * 4, sizeof(buffer)); if (len >= 29) { int addr = buffer[12] + (buffer[13] << 8) + (buffer[14] << 16); get_sfdp(self, addr, buffer, len); // Get the JEDEC mandatory table self->sectorsize = 1 << buffer[28]; self->addr_is_32bit = ((buffer[2] >> 1) & 0x03) != 0; + #if !defined(MICROPY_HW_SPIFLASH_SIZE) + // Get the bit size from the SFDP data + uint32_t size = *(uint32_t *)(buffer + 4); + if (size & 0x8000000) { + // Byte size is 2 ** lower_31_bits / 8 + self->size = 1 << ((size & 0x7fffffff) >> 3); + } else { + // Byte size is lower_31_bits / 8 + 1 + self->size = ((size & 0x7fffffff) >> 3) + 1; + } + #endif } } self->commands = self->addr_is_32bit ? _COMMANDS_32BIT : _COMMANDS_24BIT; diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 8ac9a8af03..eabbd64a3b 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -417,6 +417,12 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ ) endif +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),h7)) +HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ + hal_uart_ex.c \ + ) +endif + USBDEV_SRC_C += $(addprefix $(USBDEV_DIR)/,\ core/src/usbd_core.c \ core/src/usbd_ctlreq.c \ diff --git a/ports/stm32/boardctrl.c b/ports/stm32/boardctrl.c index 8f0d066f37..ea95c8d2d5 100644 --- a/ports/stm32/boardctrl.c +++ b/ports/stm32/boardctrl.c @@ -35,7 +35,7 @@ #include "led.h" #include "usrsw.h" -NORETURN void boardctrl_fatal_error(const char *msg) { +MP_NORETURN void boardctrl_fatal_error(const char *msg) { for (volatile uint delay = 0; delay < 10000000; delay++) { } led_state(1, 1); diff --git a/ports/stm32/boardctrl.h b/ports/stm32/boardctrl.h index 8f4ce30eff..1a03925ef4 100644 --- a/ports/stm32/boardctrl.h +++ b/ports/stm32/boardctrl.h @@ -114,7 +114,7 @@ typedef struct _boardctrl_state_t { bool log_soft_reset; } boardctrl_state_t; -NORETURN void boardctrl_fatal_error(const char *msg); +MP_NORETURN void boardctrl_fatal_error(const char *msg); void boardctrl_maybe_enter_mboot(size_t n_args, const void *args); void boardctrl_before_soft_reset_loop(boardctrl_state_t *state); void boardctrl_top_soft_reset_loop(boardctrl_state_t *state); diff --git a/ports/stm32/boards/ARDUINO_GIGA/board.json b/ports/stm32/boards/ARDUINO_GIGA/board.json index 53c636c774..a99e747017 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/board.json +++ b/ports/stm32/boards/ARDUINO_GIGA/board.json @@ -15,7 +15,7 @@ "ABX00063_01.front_1000x750.jpg" ], "mcu": "stm32h7", - "product": "Arduino Giga", + "product": "Giga", "thumbnail": "", "url": "https://store.arduino.cc/products/giga-r1-wifi", "vendor": "Arduino" diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h index cef45d730c..44f6ce66bc 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h @@ -31,6 +31,7 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENABLE_MMCARD (0) #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) #define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1) +#define MICROPY_GC_SPLIT_HEAP (1) // ROMFS config #define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) diff --git a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld index e7bb950dbe..dceb1a7489 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld +++ b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld @@ -12,6 +12,7 @@ MEMORY SRAM2 (xrw) : ORIGIN = 0x30020000, LENGTH = 128K /* SRAM2 D2 */ SRAM3 (xrw) : ORIGIN = 0x30040000, LENGTH = 32K /* SRAM3 D2 */ SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */ + SDRAM (xrw) : ORIGIN = 0x60000000, LENGTH = 8M /* SDRAM */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */ FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */ FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */ @@ -53,3 +54,27 @@ _openamp_shm_region_start = ORIGIN(SRAM4); _openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4); INCLUDE common_blifs.ld + +SECTIONS +{ + /* GC blocks addresses and sizes */ + .gc.blocks.table (READONLY) : { + . = ALIGN(4); + _gc_blocks_table_start = .; + + LONG (ORIGIN(SRAM1)); + LONG (128K); + + LONG (ORIGIN(SDRAM) + 0M); + LONG (2M); + + LONG (ORIGIN(SDRAM) + 2M); + LONG (2M); + + LONG (ORIGIN(SDRAM) + 4M); + LONG (4M); + + _gc_blocks_table_end = .; + . = ALIGN(4); + } > FLASH_TEXT +} diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json b/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json index a4c81d69d4..129d9f2fc7 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json @@ -15,7 +15,7 @@ "ABX00051_01.iso_1000x750.jpg" ], "mcu": "stm32h7", - "product": "Arduino Nicla Vision", + "product": "Nicla Vision", "thumbnail": "", "url": "https://store.arduino.cc/products/nicla-vision", "vendor": "Arduino" diff --git a/ports/stm32/boards/ARDUINO_OPTA/board.json b/ports/stm32/boards/ARDUINO_OPTA/board.json index 3955d7df31..dd6c39a191 100644 --- a/ports/stm32/boards/ARDUINO_OPTA/board.json +++ b/ports/stm32/boards/ARDUINO_OPTA/board.json @@ -14,7 +14,7 @@ "AFX00002_01.iso_1000x750.jpg" ], "mcu": "stm32h7", - "product": "Arduino Opta WiFi", + "product": "Opta WiFi", "thumbnail": "", "url": "https://store.arduino.cc/products/opta-wifi", "vendor": "Arduino" diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json b/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json index f39d7d4c47..c97b9f165c 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json @@ -16,7 +16,7 @@ "ABX00042_01.iso_1000x750.jpg" ], "mcu": "stm32h7", - "product": "Arduino Portenta H7", + "product": "Portenta H7", "thumbnail": "", "url": "https://store.arduino.cc/products/portenta-h7", "vendor": "Arduino" diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h index 90a1469056..a9ecf38fbf 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h @@ -30,7 +30,6 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENABLE_SDCARD (1) #define MICROPY_HW_ENABLE_MMCARD (0) #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) -#define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1) // ROMFS config #define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) diff --git a/ports/stm32/boards/HYDRABUS/board.json b/ports/stm32/boards/HYDRABUS/board.json index 20a2ac5eae..24df6ac99f 100644 --- a/ports/stm32/boards/HYDRABUS/board.json +++ b/ports/stm32/boards/HYDRABUS/board.json @@ -10,6 +10,6 @@ "mcu": "stm32f4", "product": "HydraBus v1.0", "thumbnail": "", - "url": "", + "url": "https://github.com/hydrabus/hydrabus", "vendor": "HydraBus" } diff --git a/ports/stm32/boards/LIMIFROG/board.json b/ports/stm32/boards/LIMIFROG/board.json index f516963a5b..36c80f8f72 100644 --- a/ports/stm32/boards/LIMIFROG/board.json +++ b/ports/stm32/boards/LIMIFROG/board.json @@ -10,6 +10,6 @@ "mcu": "stm32l4", "product": "LimiFrog", "thumbnail": "", - "url": "", + "url": "https://github.com/LimiFrog", "vendor": "LimiFrog" } diff --git a/ports/stm32/boards/OLIMEX_E407/board.json b/ports/stm32/boards/OLIMEX_E407/board.json index c14755c564..408df5a320 100644 --- a/ports/stm32/boards/OLIMEX_E407/board.json +++ b/ports/stm32/boards/OLIMEX_E407/board.json @@ -8,8 +8,8 @@ "olimex_e407.jpg" ], "mcu": "stm32f4", - "product": "E407", + "product": "STM32-E407", "thumbnail": "", - "url": "", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/ARM/ST/STM32-E407", + "vendor": "Olimex" } diff --git a/ports/stm32/boards/OLIMEX_H407/board.json b/ports/stm32/boards/OLIMEX_H407/board.json index 9ecc860ddc..27d2940513 100644 --- a/ports/stm32/boards/OLIMEX_H407/board.json +++ b/ports/stm32/boards/OLIMEX_H407/board.json @@ -8,8 +8,8 @@ "olimex_h407.jpg" ], "mcu": "stm32f4", - "product": "H407", + "product": "STM32-H407", "thumbnail": "", - "url": "", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/ARM/ST/STM32-H407", + "vendor": "Olimex" } diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json index 0bd3573d64..5efd188618 100644 --- a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json +++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json @@ -8,8 +8,8 @@ "sparkfun_micromod_stm32.jpg" ], "mcu": "stm32f4", - "product": "Micromod STM32", + "product": "MicroMod STM32", "thumbnail": "", - "url": "", - "vendor": "Sparkfun" + "url": "https://www.sparkfun.com/products/17713", + "vendor": "SparkFun" } diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h index d2f44cf6cc..1e17905bb0 100644 --- a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h +++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h @@ -1,4 +1,4 @@ -// The Sparkfun MicroMod spec uses a zero-based peripheral numbering scheme. +// The SparkFun MicroMod spec uses a zero-based peripheral numbering scheme. // In cases where the 0th peripheral is the default, the "0" is omitted from // the name (e.g. "I2C" instead of "I2C0"). // diff --git a/ports/stm32/boards/stm32g474_af.csv b/ports/stm32/boards/stm32g474_af.csv index 5853f5f900..59a0e91a14 100644 --- a/ports/stm32/boards/stm32g474_af.csv +++ b/ports/stm32/boards/stm32g474_af.csv @@ -2,12 +2,12 @@ Port ,Pin ,AF0 ,AF1 ,AF2 , ,I2C4/SYS_AF ,LPTIM1/TIM2/5/15/16/17,I2C1/3/TIM1/2/3/4/5/8/20/15/COMP1,QUADSPI1/I2C3/4/SAI1/USB/HRTIM1/TIM8/20/15/COMP3,I2C1/2/3/4/TIM1/8/16/17,QUADSPI1/SPI1/2/3/4/I2S2/3/I2C4/UART4/5/TIM8/Infrared,QUADSPI1/SPI2/3/I2S2/3/TIM1/5/8/20/Infrared,USART1/2/3/CAN/COMP7/5/6,I2C3/4/UART4/5/LPUART1/COMP1/2/7/4/5/6/3,CAN/TIM1/8/15/CAN1/2,QUADSPI1/TIM2/3/4/8/17,LPTIM1/TIM1/8/CAN1/3,FMC/LPUART1/SAI1/HRTIM1/TIM1,SAI1SAI1/HRTIM1/OPAMP2,UART4/5/SAI1/TIM2/15/UCPD1,SYS ,ADC PortA,PA0 , ,TIM2_CH1 ,TIM5_CH1 , , , , ,USART2_CTS ,COMP1_OUT ,TIM8_BKIN ,TIM8_ETR , , , ,TIM2_ETR ,EVENTOUT,ADC12_IN1 PortA,PA1 ,RTC_REFIN ,TIM2_CH2 ,TIM5_CH2 , , , , ,USART2_RTS_DE , ,TIM15_CH1N , , , , , ,EVENTOUT,ADC12_IN2 -PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3 , , , , ,USART2_TX ,COMP2_OUT ,TIM15_CH1 ,QUADSPI1_BK1_NCS , ,LPUART1_TX , ,UCPD1_FRSTX ,EVENTOUT,ADC1_IN3 -PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4 ,SAI1_CK1 , , , ,USART2_RX , ,TIM15_CH2 ,QUADSPI1_CLK , ,LPUART1_RX ,SAI1_MCLK_A , ,EVENTOUT,ADC1_IN4 +PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3 , , , , ,USART2_TX ,COMP2_OUT ,TIM15_CH1 ,QUADSPI_BK1_NCS , ,LPUART1_TX , ,UCPD1_FRSTX ,EVENTOUT,ADC1_IN3 +PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4 ,SAI1_CK1 , , , ,USART2_RX , ,TIM15_CH2 ,QUADSPI_CLK , ,LPUART1_RX ,SAI1_MCLK_A , ,EVENTOUT,ADC1_IN4 PortA,PA4 , , ,TIM3_CH2 , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_CK , , , , , ,SAI1_FS_B , ,EVENTOUT,ADC2_IN17 PortA,PA5 , ,TIM2_CH1 ,TIM2_ETR , , ,SPI1_SCK , , , , , , , , ,UCPD1_FRSTX ,EVENTOUT,ADC2_IN13 -PortA,PA6 , ,TIM16_CH1 ,TIM3_CH1 , ,TIM8_BKIN ,SPI1_MISO ,TIM1_BKIN , ,COMP1_OUT , ,QUADSPI1_BK1_IO3 , ,LPUART1_CTS , , ,EVENTOUT,ADC2_IN3 -PortA,PA7 , ,TIM17_CH1 ,TIM3_CH2 , ,TIM8_CH1N ,SPI1_MOSI ,TIM1_CH1N , ,COMP2_OUT , ,QUADSPI1_BK1_IO2 , , , ,UCPD1_FRSTX ,EVENTOUT,ADC2_IN4 +PortA,PA6 , ,TIM16_CH1 ,TIM3_CH1 , ,TIM8_BKIN ,SPI1_MISO ,TIM1_BKIN , ,COMP1_OUT , ,QUADSPI_BK1_IO3 , ,LPUART1_CTS , , ,EVENTOUT,ADC2_IN3 +PortA,PA7 , ,TIM17_CH1 ,TIM3_CH2 , ,TIM8_CH1N ,SPI1_MOSI ,TIM1_CH1N , ,COMP2_OUT , ,QUADSPI_BK1_IO2 , , , ,UCPD1_FRSTX ,EVENTOUT,ADC2_IN4 PortA,PA8 ,MCO , ,I2C3_SCL , ,I2C2_SDA ,I2S2_MCK ,TIM1_CH1 ,USART1_CK ,COMP7_OUT , ,TIM4_ETR ,CAN3_RX ,SAI1_CK2 ,HRTIM1_CHA1 ,SAI1_SCK_A ,EVENTOUT,ADC5_IN1 PortA,PA9 , , ,I2C3_SMBA , ,I2C2_SCL ,I2S3_MCK ,TIM1_CH2 ,USART1_TX ,COMP5_OUT ,TIM15_BKIN ,TIM2_CH3 , , ,HRTIM1_CHA2 ,SAI1_FS_A ,EVENTOUT,ADC5_IN2 PortA,PA10, ,TIM17_BKIN , ,USB_CRS_SYNC ,I2C2_SMBA ,SPI2_MISO ,TIM1_CH3 ,USART1_RX ,COMP6_OUT , ,TIM2_CH4 ,TIM8_BKIN ,SAI1_D1 ,HRTIM1_CHB1 ,SAI1_SD_A ,EVENTOUT, @@ -16,9 +16,9 @@ PortA,PA12, ,TIM16_CH1 , PortA,PA13,SWDIOJTMS ,TIM16_CH1N , ,I2C4_SCL ,I2C1_SCL ,IR_OUT , ,USART3_CTS , , ,TIM4_CH3 , , ,SAI1_SD_B , ,EVENTOUT, PortA,PA14,SWCLKJTCK ,LPTIM1_OUT , ,I2C4_SMBA ,I2C1_SDA ,TIM8_CH2 ,TIM1_BKIN ,USART2_TX , , , , , ,SAI1_FS_B , ,EVENTOUT, PortA,PA15,JTDI ,TIM2_CH1 ,TIM8_CH1 , ,I2C1_SCL ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_RX ,UART4_RTS_DE ,TIM1_BKIN , ,CAN3_TX , ,HRTIM1_FLT2 ,TIM2_ETR ,EVENTOUT, -PortB,PB0 , , ,TIM3_CH3 , ,TIM8_CH2N , ,TIM1_CH2N , , , ,QUADSPI1_BK1_IO1 , , ,HRTIM1_FLT5 ,UCPD1_FRSTX ,EVENTOUT,ADC3_IN12/ADC1_IN15 -PortB,PB1 , , ,TIM3_CH4 , ,TIM8_CH3N , ,TIM1_CH3N , ,COMP4_OUT , ,QUADSPI1_BK1_IO0 , ,LPUART1_RTS_DE ,HRTIM1_SCOUT , ,EVENTOUT,ADC3_IN1/ADC1_IN12 -PortB,PB2 ,RTC_OUT2 ,LPTIM1_OUT ,TIM5_CH1 ,TIM20_CH1 ,I2C3_SMBA , , , , , ,QUADSPI1_BK2_IO1 , , ,HRTIM1_SCIN , ,EVENTOUT,ADC2_IN12 +PortB,PB0 , , ,TIM3_CH3 , ,TIM8_CH2N , ,TIM1_CH2N , , , ,QUADSPI_BK1_IO1 , , ,HRTIM1_FLT5 ,UCPD1_FRSTX ,EVENTOUT,ADC3_IN12/ADC1_IN15 +PortB,PB1 , , ,TIM3_CH4 , ,TIM8_CH3N , ,TIM1_CH3N , ,COMP4_OUT , ,QUADSPI_BK1_IO0 , ,LPUART1_RTS_DE ,HRTIM1_SCOUT , ,EVENTOUT,ADC3_IN1/ADC1_IN12 +PortB,PB2 ,RTC_OUT2 ,LPTIM1_OUT ,TIM5_CH1 ,TIM20_CH1 ,I2C3_SMBA , , , , , ,QUADSPI_BK2_IO1 , , ,HRTIM1_SCIN , ,EVENTOUT,ADC2_IN12 PortB,PB3 ,JTDOTRACESWO,TIM2_CH2 ,TIM4_ETR ,USB_CRS_SYNC ,TIM8_CH1N ,SPI1_SCK ,SPI3_SCK/I2S3_CK ,USART2_TX , , ,TIM3_ETR ,CAN3_RX ,HRTIM1_SCOUT ,HRTIM1_EEV9 ,SAI1_SCK_B ,EVENTOUT, PortB,PB4 ,JTRST ,TIM16_CH1 ,TIM3_CH1 , ,TIM8_CH2N ,SPI1_MISO ,SPI3_MISO ,USART2_RX ,UART5_RTS_DE , ,TIM17_BKIN ,CAN3_TX , ,HRTIM1_EEV7 ,SAI1_MCLK_B ,EVENTOUT, PortB,PB5 , ,TIM16_BKIN ,TIM3_CH2 ,TIM8_CH3N ,I2C1_SMBA ,SPI1_MOSI ,SPI3_MOSI/I2S3_SD ,USART2_CK ,I2C3_SDA ,CAN2_RX ,TIM17_CH1 ,LPTIM1_IN1 ,SAI1_SD_B ,HRTIM1_EEV6 ,UART5_CTS ,EVENTOUT, @@ -26,17 +26,17 @@ PortB,PB6 , ,TIM16_CH1N ,TIM4_CH1 PortB,PB7 , ,TIM17_CH1N ,TIM4_CH2 ,I2C4_SDA ,I2C1_SDA ,TIM8_BKIN , ,USART1_RX ,COMP3_OUT , ,TIM3_CH4 ,LPTIM1_IN2 ,FMC_NL ,HRTIM1_EEV3 ,UART4_CTS ,EVENTOUT, PortB,PB8 , ,TIM16_CH1 ,TIM4_CH3 ,SAI1_CK1 ,I2C1_SCL , , ,USART3_RX ,COMP1_OUT ,CAN1_RX ,TIM8_CH2 , ,TIM1_BKIN ,HRTIM1_EEV8 ,SAI1_MCLK_A ,EVENTOUT, PortB,PB9 , ,TIM17_CH1 ,TIM4_CH4 ,SAI1_D2 ,I2C1_SDA , ,IR_OUT ,USART3_TX ,COMP2_OUT ,CAN1_TX ,TIM8_CH3 , ,TIM1_CH3N ,HRTIM1_EEV5 ,SAI1_FS_A ,EVENTOUT, -PortB,PB10, ,TIM2_CH3 , , , , , ,USART3_TX ,LPUART1_RX , ,QUADSPI1_CLK , ,TIM1_BKIN ,HRTIM1_FLT3 ,SAI1_SCK_A ,EVENTOUT, -PortB,PB11, ,TIM2_CH4 , , , , , ,USART3_RX ,LPUART1_TX , ,QUADSPI1_BK1_NCS , , ,HRTIM1_FLT4 , ,EVENTOUT,ADC12_IN14 +PortB,PB10, ,TIM2_CH3 , , , , , ,USART3_TX ,LPUART1_RX , ,QUADSPI_CLK , ,TIM1_BKIN ,HRTIM1_FLT3 ,SAI1_SCK_A ,EVENTOUT, +PortB,PB11, ,TIM2_CH4 , , , , , ,USART3_RX ,LPUART1_TX , ,QUADSPI_BK1_NCS , , ,HRTIM1_FLT4 , ,EVENTOUT,ADC12_IN14 PortB,PB12, , ,TIM5_ETR , ,I2C2_SMBA ,SPI2_NSS/I2S2_WS ,TIM1_BKIN ,USART3_CK ,LPUART1_RTS_DE ,CAN2_RX , , , ,HRTIM1_CHC1 , ,EVENTOUT,ADC4_IN3/ADC1_IN11 PortB,PB13, , , , , ,SPI2_SCK/I2S2_CK ,TIM1_CH1N ,USART3_CTS ,LPUART1_CTS ,CAN2_TX , , , ,HRTIM1_CHC2 , ,EVENTOUT,ADC3_IN5 PortB,PB14, ,TIM15_CH1 , , , ,SPI2_MISO ,TIM1_CH2N ,USART3_RTS_DE ,COMP4_OUT , , , , ,HRTIM1_CHD1 , ,EVENTOUT,ADC4_IN4/ADC1_IN5 PortB,PB15,RTC_REFIN ,TIM15_CH2 ,TIM15_CH1N ,COMP3_OUT ,TIM1_CH3N ,SPI2_MOSI/I2S2_SD , , , , , , , ,HRTIM1_CHD2 , ,EVENTOUT,ADC4_IN5/ADC2_IN15 PortC,PC0 , ,LPTIM1_IN1 ,TIM1_CH1 , , , , , ,LPUART1_RX , , , , , , ,EVENTOUT,ADC12_IN6 -PortC,PC1 , ,LPTIM1_OUT ,TIM1_CH2 , , , , , ,LPUART1_TX , ,QUADSPI1_BK2_IO0 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN7 -PortC,PC2 , ,LPTIM1_IN2 ,TIM1_CH3 ,COMP3_OUT , , ,TIM20_CH2 , , , ,QUADSPI1_BK2_IO1 , , , , ,EVENTOUT,ADC12_IN8 -PortC,PC3 , ,LPTIM1_ETR ,TIM1_CH4 ,SAI1_D1 , , ,TIM1_BKIN2 , , , ,QUADSPI1_BK2_IO2 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN9 -PortC,PC4 , , ,TIM1_ETR , ,I2C2_SCL , , ,USART1_TX , , ,QUADSPI1_BK2_IO3 , , , , ,EVENTOUT,ADC2_IN5 +PortC,PC1 , ,LPTIM1_OUT ,TIM1_CH2 , , , , , ,LPUART1_TX , ,QUADSPI_BK2_IO0 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN7 +PortC,PC2 , ,LPTIM1_IN2 ,TIM1_CH3 ,COMP3_OUT , , ,TIM20_CH2 , , , ,QUADSPI_BK2_IO1 , , , , ,EVENTOUT,ADC12_IN8 +PortC,PC3 , ,LPTIM1_ETR ,TIM1_CH4 ,SAI1_D1 , , ,TIM1_BKIN2 , , , ,QUADSPI_BK2_IO2 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN9 +PortC,PC4 , , ,TIM1_ETR , ,I2C2_SCL , , ,USART1_TX , , ,QUADSPI_BK2_IO3 , , , , ,EVENTOUT,ADC2_IN5 PortC,PC5 , , ,TIM15_BKIN ,SAI1_D3 , , ,TIM1_CH4N ,USART1_RX , , , , , ,HRTIM1_EEV10 , ,EVENTOUT,ADC2_IN11 PortC,PC6 , , ,TIM3_CH1 ,HRTIM1_EEV10 ,TIM8_CH1 , ,I2S2_MCK ,COMP6_OUT ,I2C4_SCL , , , , ,HRTIM1_CHF1 , ,EVENTOUT, PortC,PC7 , , ,TIM3_CH2 ,HRTIM1_FLT5 ,TIM8_CH2 , ,I2S3_MCK ,COMP5_OUT ,I2C4_SDA , , , , ,HRTIM1_CHF2 , ,EVENTOUT, @@ -51,11 +51,11 @@ PortC,PC15, , , PortD,PD0 , , , , , , ,TIM8_CH4N , , ,CAN1_RX , , ,FMC_D2 , , ,EVENTOUT, PortD,PD1 , , , , ,TIM8_CH4 , ,TIM8_BKIN2 , , ,CAN1_TX , , ,FMC_D3 , , ,EVENTOUT, PortD,PD2 , , ,TIM3_ETR , ,TIM8_BKIN ,UART5_RX , , , , , , , , , ,EVENTOUT, -PortD,PD3 , , ,TIM2_CH1/TIM2_ETR , , , , ,USART2_CTS , , ,QUADSPI1_BK2_NCS , ,FMC_CLK , , ,EVENTOUT, -PortD,PD4 , , ,TIM2_CH2 , , , , ,USART2_RTS_DE , , ,QUADSPI1_BK2_IO0 , ,FMC_NOE , , ,EVENTOUT, -PortD,PD5 , , , , , , , ,USART2_TX , , ,QUADSPI1_BK2_IO1 , ,FMC_NWE , , ,EVENTOUT, -PortD,PD6 , , ,TIM2_CH4 ,SAI1_D1 , , , ,USART2_RX , , ,QUADSPI1_BK2_IO2 , ,FMC_NWAIT ,SAI1_SD_A , ,EVENTOUT, -PortD,PD7 , , ,TIM2_CH3 , , , , ,USART2_CK , , ,QUADSPI1_BK2_IO3 , ,FMC_NCE/FMC_NE1 , , ,EVENTOUT, +PortD,PD3 , , ,TIM2_CH1/TIM2_ETR , , , , ,USART2_CTS , , ,QUADSPI_BK2_NCS , ,FMC_CLK , , ,EVENTOUT, +PortD,PD4 , , ,TIM2_CH2 , , , , ,USART2_RTS_DE , , ,QUADSPI_BK2_IO0 , ,FMC_NOE , , ,EVENTOUT, +PortD,PD5 , , , , , , , ,USART2_TX , , ,QUADSPI_BK2_IO1 , ,FMC_NWE , , ,EVENTOUT, +PortD,PD6 , , ,TIM2_CH4 ,SAI1_D1 , , , ,USART2_RX , , ,QUADSPI_BK2_IO2 , ,FMC_NWAIT ,SAI1_SD_A , ,EVENTOUT, +PortD,PD7 , , ,TIM2_CH3 , , , , ,USART2_CK , , ,QUADSPI_BK2_IO3 , ,FMC_NCE/FMC_NE1 , , ,EVENTOUT, PortD,PD8 , , , , , , , ,USART3_TX , , , , ,FMC_D13 , , ,EVENTOUT,ADC4_IN12/ADC5_IN12 PortD,PD9 , , , , , , , ,USART3_RX , , , , ,FMC_D14 , , ,EVENTOUT,ADC4_IN13/ADC5_IN13 PortD,PD10, , , , , , , ,USART3_CK , , , , ,FMC_D15 , , ,EVENTOUT,ADC345_IN7 @@ -74,23 +74,23 @@ PortE,PE6 ,TRACED3 , , PortE,PE7 , , ,TIM1_ETR , , , , , , , , , ,FMC_D4 ,SAI1_SD_B , ,EVENTOUT,ADC3_IN4 PortE,PE8 , ,TIM5_CH3 ,TIM1_CH1N , , , , , , , , , ,FMC_D5 ,SAI1_SCK_B , ,EVENTOUT,ADC345_IN6 PortE,PE9 , ,TIM5_CH4 ,TIM1_CH1 , , , , , , , , , ,FMC_D6 ,SAI1_FS_B , ,EVENTOUT,ADC3_IN2 -PortE,PE10, , ,TIM1_CH2N , , , , , , , ,QUADSPI1_CLK , ,FMC_D7 ,SAI1_MCLK_B , ,EVENTOUT,ADC345_IN14 -PortE,PE11, , ,TIM1_CH2 , , ,SPI4_NSS , , , , ,QUADSPI1_BK1_NCS , ,FMC_D8 , , ,EVENTOUT,ADC345_IN15 -PortE,PE12, , ,TIM1_CH3N , , ,SPI4_SCK , , , , ,QUADSPI1_BK1_IO0 , ,FMC_D9 , , ,EVENTOUT,ADC345_IN16 -PortE,PE13, , ,TIM1_CH3 , , ,SPI4_MISO , , , , ,QUADSPI1_BK1_IO1 , ,FMC_D10 , , ,EVENTOUT,ADC3_IN3 -PortE,PE14, , ,TIM1_CH4 , , ,SPI4_MOSI ,TIM1_BKIN2 , , , ,QUADSPI1_BK1_IO2 , ,FMC_D11 , , ,EVENTOUT,ADC4_IN1 -PortE,PE15, , ,TIM1_BKIN , , , ,TIM1_CH4N ,USART3_RX , , ,QUADSPI1_BK1_IO3 , ,FMC_D12 , , ,EVENTOUT,ADC4_IN2 +PortE,PE10, , ,TIM1_CH2N , , , , , , , ,QUADSPI_CLK , ,FMC_D7 ,SAI1_MCLK_B , ,EVENTOUT,ADC345_IN14 +PortE,PE11, , ,TIM1_CH2 , , ,SPI4_NSS , , , , ,QUADSPI_BK1_NCS , ,FMC_D8 , , ,EVENTOUT,ADC345_IN15 +PortE,PE12, , ,TIM1_CH3N , , ,SPI4_SCK , , , , ,QUADSPI_BK1_IO0 , ,FMC_D9 , , ,EVENTOUT,ADC345_IN16 +PortE,PE13, , ,TIM1_CH3 , , ,SPI4_MISO , , , , ,QUADSPI_BK1_IO1 , ,FMC_D10 , , ,EVENTOUT,ADC3_IN3 +PortE,PE14, , ,TIM1_CH4 , , ,SPI4_MOSI ,TIM1_BKIN2 , , , ,QUADSPI_BK1_IO2 , ,FMC_D11 , , ,EVENTOUT,ADC4_IN1 +PortE,PE15, , ,TIM1_BKIN , , , ,TIM1_CH4N ,USART3_RX , , ,QUADSPI_BK1_IO3 , ,FMC_D12 , , ,EVENTOUT,ADC4_IN2 PortF,PF0 , , , , ,I2C2_SDA ,SPI2_NSS/I2S2_WS ,TIM1_CH3N , , , , , , , , ,EVENTOUT,ADC1_IN10 PortF,PF1 , , , , , ,SPI2_SCK/I2S2_CK , , , , , , , , , ,EVENTOUT,ADC2_IN10 PortF,PF2 , , ,TIM20_CH3 , ,I2C2_SMBA , , , , , , , ,FMC_A2 , , ,EVENTOUT, PortF,PF3 , , ,TIM20_CH4 , ,I2C3_SCL , , , , , , , ,FMC_A3 , , ,EVENTOUT, PortF,PF4 , , ,COMP1_OUT ,TIM20_CH1N ,I2C3_SDA , , , , , , , ,FMC_A4 , , ,EVENTOUT, PortF,PF5 , , ,TIM20_CH2N , , , , , , , , , ,FMC_A5 , , ,EVENTOUT, -PortF,PF6 , ,TIM5_ETR ,TIM4_CH4 ,SAI1_SD_B ,I2C2_SCL , ,TIM5_CH1 ,USART3_RTS , , ,QUADSPI1_BK1_IO3 , , , , ,EVENTOUT, -PortF,PF7 , , ,TIM20_BKIN , , , ,TIM5_CH2 , , , ,QUADSPI1_BK1_IO2 , ,FMC_A1 ,SAI1_MCLK_B , ,EVENTOUT, -PortF,PF8 , , ,TIM20_BKIN2 , , , ,TIM5_CH3 , , , ,QUADSPI1_BK1_IO0 , ,FMC_A24 ,SAI1_SCK_B , ,EVENTOUT, -PortF,PF9 , , ,TIM20_BKIN ,TIM15_CH1 , ,SPI2_SCK ,TIM5_CH4 , , , ,QUADSPI1_BK1_IO1 , ,FMC_A25 ,SAI1_FS_B , ,EVENTOUT, -PortF,PF10, , ,TIM20_BKIN2 ,TIM15_CH2 , ,SPI2_SCK , , , , ,QUADSPI1_CLK , ,FMC_A0 ,SAI1_D3 , ,EVENTOUT, +PortF,PF6 , ,TIM5_ETR ,TIM4_CH4 ,SAI1_SD_B ,I2C2_SCL , ,TIM5_CH1 ,USART3_RTS , , ,QUADSPI_BK1_IO3 , , , , ,EVENTOUT, +PortF,PF7 , , ,TIM20_BKIN , , , ,TIM5_CH2 , , , ,QUADSPI_BK1_IO2 , ,FMC_A1 ,SAI1_MCLK_B , ,EVENTOUT, +PortF,PF8 , , ,TIM20_BKIN2 , , , ,TIM5_CH3 , , , ,QUADSPI_BK1_IO0 , ,FMC_A24 ,SAI1_SCK_B , ,EVENTOUT, +PortF,PF9 , , ,TIM20_BKIN ,TIM15_CH1 , ,SPI2_SCK ,TIM5_CH4 , , , ,QUADSPI_BK1_IO1 , ,FMC_A25 ,SAI1_FS_B , ,EVENTOUT, +PortF,PF10, , ,TIM20_BKIN2 ,TIM15_CH2 , ,SPI2_SCK , , , , ,QUADSPI_CLK , ,FMC_A0 ,SAI1_D3 , ,EVENTOUT, PortF,PF11, , ,TIM20_ETR , , , , , , , , , ,FMC_NE4 , , ,EVENTOUT, PortF,PF12, , ,TIM20_CH1 , , , , , , , , , ,FMC_A6 , , ,EVENTOUT, PortF,PF13, , ,TIM20_CH2 , ,I2C4_SMBA , , , , , , , ,FMC_A7 , , ,EVENTOUT, diff --git a/ports/stm32/boards/stm32h7xx_hal_conf_base.h b/ports/stm32/boards/stm32h7xx_hal_conf_base.h index 670dee383f..1953ba020b 100644 --- a/ports/stm32/boards/stm32h7xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32h7xx_hal_conf_base.h @@ -90,6 +90,7 @@ #include "stm32h7xx_hal_spi.h" #include "stm32h7xx_hal_tim.h" #include "stm32h7xx_hal_uart.h" +#include "stm32h7xx_hal_uart_ex.h" #include "stm32h7xx_hal_usart.h" #include "stm32h7xx_hal_wwdg.h" #include "stm32h7xx_ll_adc.h" diff --git a/ports/stm32/cyw43_configport.h b/ports/stm32/cyw43_configport.h index c26c554761..6528dbc625 100644 --- a/ports/stm32/cyw43_configport.h +++ b/ports/stm32/cyw43_configport.h @@ -27,14 +27,9 @@ #ifndef MICROPY_INCLUDED_STM32_CYW43_CONFIGPORT_H #define MICROPY_INCLUDED_STM32_CYW43_CONFIGPORT_H -// The board-level config will be included here, so it can set some CYW43 values. -#include "py/mpconfig.h" -#include "py/mperrno.h" -#include "py/mphal.h" -#include "extmod/modnetwork.h" -#include "extmod/mpbthci.h" #include "extint.h" -#include "pendsv.h" +#include "extmod/mpbthci.h" +#include "extmod/cyw43_config_common.h" #include "sdio.h" #define CYW43_USE_SPI (0) @@ -62,50 +57,12 @@ #define CYW43_BT_UART_BAUDRATE_DOWNLOAD_FIRMWARE MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE #endif -#define CYW43_IOCTL_TIMEOUT_US (1000000) -#define CYW43_SLEEP_MAX (50) -#define CYW43_NETUTILS (1) #define CYW43_CLEAR_SDIO_INT (1) -#define CYW43_EPERM MP_EPERM // Operation not permitted -#define CYW43_EIO MP_EIO // I/O error -#define CYW43_EINVAL MP_EINVAL // Invalid argument -#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out - -#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER -#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT -#define CYW43_THREAD_LOCK_CHECK - -#define CYW43_HOST_NAME mod_network_hostname_data - #define CYW43_SDPCM_SEND_COMMON_WAIT __WFI(); #define CYW43_DO_IOCTL_WAIT __WFI(); #define CYW43_HAL_UART_READCHAR_BLOCKING_WAIT __WFI() -#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) - -#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT -#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT -#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE -#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP -#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN - -#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 -#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR - -#define cyw43_hal_ticks_us mp_hal_ticks_us -#define cyw43_hal_ticks_ms mp_hal_ticks_ms - -#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t -#define cyw43_hal_pin_config mp_hal_pin_config -#define cyw43_hal_pin_read mp_hal_pin_read -#define cyw43_hal_pin_low mp_hal_pin_low -#define cyw43_hal_pin_high mp_hal_pin_high - -#define cyw43_hal_get_mac mp_hal_get_mac -#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii -#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac - #define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate #define cyw43_hal_uart_write mp_bluetooth_hci_uart_write #define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar @@ -132,24 +89,6 @@ #define CYW43_PIN_RFSW_SELECT pyb_pin_WL_GPIO_1 #endif -#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) - -void cyw43_post_poll_hook(void); - -static inline void cyw43_delay_us(uint32_t us) { - uint32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - } -} - -static inline void cyw43_delay_ms(uint32_t ms) { - uint32_t us = ms * 1000; - uint32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - MICROPY_EVENT_POLL_HOOK; - } -} - static inline void cyw43_hal_pin_config_irq_falling(cyw43_hal_pin_obj_t pin, int enable) { if (enable) { extint_set(pin, GPIO_MODE_IT_FALLING); @@ -184,6 +123,4 @@ static inline int cyw43_sdio_transfer_cmd53(bool write, uint32_t block_size, uin return sdio_transfer_cmd53(write, block_size, arg, len, buf); } -#define CYW43_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK - #endif // MICROPY_INCLUDED_STM32_CYW43_CONFIGPORT_H diff --git a/ports/stm32/machine_adc.c b/ports/stm32/machine_adc.c index 8f07075914..c3211ea4f4 100644 --- a/ports/stm32/machine_adc.c +++ b/ports/stm32/machine_adc.c @@ -445,10 +445,13 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp static uint32_t adc_read_channel(ADC_TypeDef *adc) { uint32_t value; - #if defined(STM32G4) - // For STM32G4 there is errata 2.7.7, "Wrong ADC result if conversion done late after - // calibration or previous conversion". According to the errata, this can be avoided - // by performing two consecutive ADC conversions and keeping the second result. + #if defined(STM32G4) || defined(STM32WB) + // For STM32G4 errata 2.7.7 / STM32WB errata 2.7.1: + // "Wrong ADC result if conversion done late after calibration or previous conversion" + // states an incorrect reading is returned if more than 1ms has elapsed since the last + // reading or calibration. According to the errata, this can be avoided by performing + // two consecutive ADC conversions and keeping the second result. + // Note: On STM32WB55 @ 64Mhz each ADC read takes ~ 3us. for (uint8_t i = 0; i < 2; i++) #endif { diff --git a/ports/stm32/main.c b/ports/stm32/main.c index e8395013b9..5e114f562f 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -521,6 +521,23 @@ soft_reset: // GC init gc_init(MICROPY_HEAP_START, MICROPY_HEAP_END); + // Add additional GC blocks (if enabled). + #if MICROPY_GC_SPLIT_HEAP + typedef struct { + uint8_t *addr; + uint32_t size; + } gc_blocks_table_t; + + extern const gc_blocks_table_t _gc_blocks_table_start; + extern const gc_blocks_table_t _gc_blocks_table_end; + + for (gc_blocks_table_t const *block = &_gc_blocks_table_start; block < &_gc_blocks_table_end; block++) { + if (block->size) { + gc_add(block->addr, block->addr + block->size); + } + } + #endif + #if MICROPY_ENABLE_PYSTACK static mp_obj_t pystack[384]; mp_pystack_init(pystack, &pystack[384]); diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 01f8892a51..ff44dac630 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -176,7 +176,7 @@ void HAL_Delay(uint32_t ms) { mp_hal_delay_ms(ms); } -NORETURN static void __fatal_error(const char *msg) { +MP_NORETURN static void __fatal_error(const char *msg) { NVIC_SystemReset(); for (;;) { } @@ -1443,7 +1443,7 @@ static int pyb_usbdd_shutdown(void) { /******************************************************************************/ // main -NORETURN static __attribute__((naked)) void branch_to_application(uint32_t r0, uint32_t bl_addr) { +MP_NORETURN static __attribute__((naked)) void branch_to_application(uint32_t r0, uint32_t bl_addr) { __asm volatile ( "ldr r2, [r1, #0]\n" // get address of stack pointer "msr msp, r2\n" // set stack pointer diff --git a/ports/stm32/mboot/mboot.h b/ports/stm32/mboot/mboot.h index 1fabff0080..bc1320c5cd 100644 --- a/ports/stm32/mboot/mboot.h +++ b/ports/stm32/mboot/mboot.h @@ -36,7 +36,7 @@ #define ELEM_DATA_START (&_estack[0]) #define ELEM_DATA_MAX (&_estack[ELEM_DATA_SIZE]) -#define NORETURN __attribute__((noreturn)) +#define MP_NORETURN __attribute__((noreturn)) #define MP_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) // The default UI code in ui.c only works if there is at least one LED configured. diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 620ae468cb..f3bdf1bc50 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -291,12 +291,12 @@ static mp_obj_t mp_machine_unique_id(void) { } // Resets the pyboard in a manner similar to pushing the external RESET button. -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { powerctrl_mcu_reset(); } // Activate the bootloader without BOOT* pins. -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { #if MICROPY_HW_ENABLE_USB pyb_usb_dev_deinit(); #endif diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index d1d6fe249b..bfaa3fb0ba 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -205,20 +205,6 @@ extern const struct _mp_obj_type_t network_lan_type; #define MICROPY_HW_NIC_ETH #endif -#if MICROPY_PY_NETWORK_CYW43 -extern const struct _mp_obj_type_t mp_network_cyw43_type; -#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, -#else -#define MICROPY_HW_NIC_CYW43 -#endif - -#if MICROPY_PY_NETWORK_WIZNET5K -extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k; -#define MICROPY_HW_NIC_WIZNET5K { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) }, -#else -#define MICROPY_HW_NIC_WIZNET5K -#endif - // extra constants #define MICROPY_PORT_CONSTANTS \ MACHINE_BUILTIN_MODULE_CONSTANTS \ @@ -231,8 +217,6 @@ extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k; #define MICROPY_PORT_NETWORK_INTERFACES \ MICROPY_HW_NIC_ETH \ - MICROPY_HW_NIC_CYW43 \ - MICROPY_HW_NIC_WIZNET5K \ MICROPY_BOARD_NETWORK_INTERFACES \ #define MP_STATE_PORT MP_STATE_VM diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index dfd50cebd3..b4b2267fa0 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -20,7 +20,7 @@ const byte mp_hal_status_to_errno_table[4] = { uint8_t mp_hal_unique_id_address[12]; #endif -NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { +MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { mp_raise_OSError(mp_hal_status_to_errno_table[status]); } diff --git a/ports/stm32/mphalport.h b/ports/stm32/mphalport.h index e520bc54ae..03b0f8e772 100644 --- a/ports/stm32/mphalport.h +++ b/ports/stm32/mphalport.h @@ -37,7 +37,7 @@ static inline int mp_hal_status_to_neg_errno(HAL_StatusTypeDef status) { return -mp_hal_status_to_errno_table[status]; } -NORETURN void mp_hal_raise(HAL_StatusTypeDef status); +MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status); void mp_hal_set_interrupt_char(int c); // -1 to disable // Atomic section helpers. diff --git a/ports/stm32/mpu.h b/ports/stm32/mpu.h index a87c04a58d..5756cb0560 100644 --- a/ports/stm32/mpu.h +++ b/ports/stm32/mpu.h @@ -28,7 +28,7 @@ #include "irq.h" -#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F7) || defined(STM32H7) || defined(STM32WB) +#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) || defined(STM32WB) #define MPU_REGION_ETH (MPU_REGION_NUMBER0) #define MPU_REGION_QSPI1 (MPU_REGION_NUMBER1) diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index eea009e2d7..e3e2fcdd44 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -115,7 +115,7 @@ static inline void powerctrl_disable_hsi_if_unused(void) { #endif } -NORETURN void powerctrl_mcu_reset(void) { +MP_NORETURN void powerctrl_mcu_reset(void) { #if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET *BL_STATE_PTR = BL_STATE_INVALID; #if __DCACHE_PRESENT == 1 @@ -125,7 +125,7 @@ NORETURN void powerctrl_mcu_reset(void) { NVIC_SystemReset(); } -NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, uint32_t bl_addr) { +MP_NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, uint32_t bl_addr) { __asm volatile ( "ldr r2, [r1, #0]\n" // get address of stack pointer "msr msp, r2\n" // get stack pointer @@ -135,7 +135,7 @@ NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, ui MP_UNREACHABLE; } -NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) { +MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) { #if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET // Enter the bootloader via a reset, so everything is reset (including WDT). @@ -1016,7 +1016,7 @@ void powerctrl_enter_stop_mode(void) { enable_irq(irq_state); } -NORETURN void powerctrl_enter_standby_mode(void) { +MP_NORETURN void powerctrl_enter_standby_mode(void) { rtc_init_finalise(); #if defined(MICROPY_BOARD_ENTER_STANDBY) diff --git a/ports/stm32/powerctrl.h b/ports/stm32/powerctrl.h index 5b92405611..05a70e52c6 100644 --- a/ports/stm32/powerctrl.h +++ b/ports/stm32/powerctrl.h @@ -39,14 +39,14 @@ static inline void stm32_system_init(void) { void SystemClock_Config(void); -NORETURN void powerctrl_mcu_reset(void); -NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr); +MP_NORETURN void powerctrl_mcu_reset(void); +MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr); void powerctrl_check_enter_bootloader(void); void powerctrl_config_systick(void); int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk_mhz, bool need_pllsai); int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t apb2); void powerctrl_enter_stop_mode(void); -NORETURN void powerctrl_enter_standby_mode(void); +MP_NORETURN void powerctrl_enter_standby_mode(void); #endif // MICROPY_INCLUDED_STM32_POWERCTRL_H diff --git a/ports/stm32/qspi.c b/ports/stm32/qspi.c index 781aae803e..2ef9a4d018 100644 --- a/ports/stm32/qspi.c +++ b/ports/stm32/qspi.c @@ -213,11 +213,13 @@ static int qspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) { qspi_memory_map(); break; case MP_QSPI_IOCTL_MEMORY_MODIFIED: { + #if defined(__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) uintptr_t *addr_len = (uintptr_t *)arg; volatile void *addr = (volatile void *)(QSPI_MAP_ADDR + addr_len[0]); size_t len = addr_len[1]; SCB_InvalidateICache_by_Addr(addr, len); SCB_InvalidateDCache_by_Addr(addr, len); + #endif break; } } diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index 91db91395e..55fa622142 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -653,7 +653,7 @@ bool uart_init(machine_uart_obj_t *uart_obj, huart.Init.HwFlowCtl = flow; huart.Init.OverSampling = UART_OVERSAMPLING_16; - #if defined(STM32G4) // H7 and WB also have fifo.. + #if defined(STM32G4) || defined(STM32H7) // WB also has a fifo.. huart.FifoMode = UART_FIFOMODE_ENABLE; #endif @@ -701,6 +701,12 @@ bool uart_init(machine_uart_obj_t *uart_obj, uart_obj->char_width = CHAR_WIDTH_8BIT; } + #if defined(STM32H7) + HAL_UARTEx_SetTxFifoThreshold(&huart, UART_TXFIFO_THRESHOLD_1_8); + HAL_UARTEx_SetRxFifoThreshold(&huart, UART_RXFIFO_THRESHOLD_1_8); + HAL_UARTEx_EnableFifoMode(&huart); + #endif + uart_obj->mp_irq_trigger = 0; uart_obj->mp_irq_obj = NULL; @@ -1141,6 +1147,9 @@ size_t uart_tx_data(machine_uart_obj_t *self, const void *src_in, size_t num_cha // timeout_char by FIFO size + 1. // STM32G4 has 8 words FIFO. timeout = (8 + 1) * self->timeout_char; + #elif defined(STM32H7) + // STM32H7 has 16 words FIFO. + timeout = (16 + 1) * self->timeout_char; #else // The timeout specified here is for waiting for the TX data register to // become empty (ie between chars), as well as for the final char to be diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 88fa1af045..3c54d156c3 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -256,21 +256,24 @@ endif include $(TOP)/py/mkrules.mk -.PHONY: test test_full +.PHONY: test test_full_no_native test_full test: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py $(eval DIRNAME=ports/$(notdir $(CURDIR))) cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py -test_full: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py +test_full_no_native: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py $(eval DIRNAME=ports/$(notdir $(CURDIR))) cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py -d thread - cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --emit native cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) -d basics float micropython - cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) --emit native -d basics float micropython cat $(TOP)/tests/basics/0prelim.py | ./$(BUILD)/$(PROG) | grep -q 'abc' +test_full: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py test_full_no_native + $(eval DIRNAME=ports/$(notdir $(CURDIR))) + cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --emit native + cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) --emit native -d basics float micropython + test_gcov: test_full gcov -o $(BUILD)/py $(TOP)/py/*.c gcov -o $(BUILD)/extmod $(TOP)/extmod/*.c diff --git a/ports/unix/README.md b/ports/unix/README.md index b7aa6e3fef..656d4303d3 100644 --- a/ports/unix/README.md +++ b/ports/unix/README.md @@ -155,3 +155,21 @@ The default compiler optimisation level is -Os, or -Og if `DEBUG=1` is set. Setting the variable `COPT` will explicitly set the optimisation level. For example `make [other arguments] COPT=-O0 DEBUG=1` will build a binary with no optimisations, assertions enabled, and debug symbols. + +### Sanitizers + +Sanitizers are extra runtime checks supported by gcc and clang. The CI process +supports building with the "undefined behavior" (UBSan) or "address" (ASan) +sanitizers. The script `tools/ci.sh` is the source of truth about how to build +and run in these modes. + +Several classes of checks are disabled via compiler flags: + +* In the undefined behavior sanitizer, checks based on the presence of the + `non_null` attribute are disabled because the code makes technically incorrect + calls like `memset(NULL, 0, 0)`. A future C standard is likely to permit such + calls. +* In the address sanitizer, `detect_stack_use_after_return` is disabled. This + check is intended to make sure locals in a "returned from" stack frame are not + used. However, this mode interferes with various assumptions that + MicroPython's stack checking, NLR, and GC rely on. diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index 2b65b47fc5..b041141f0f 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -184,6 +184,18 @@ static void pairheap_test(size_t nops, int *ops) { mp_printf(&mp_plat_print, "\n"); } +static mp_sched_node_t mp_coverage_sched_node; +static bool coverage_sched_function_continue; + +static void coverage_sched_function(mp_sched_node_t *node) { + (void)node; + mp_printf(&mp_plat_print, "scheduled function\n"); + if (coverage_sched_function_continue) { + // Re-scheduling node will cause it to run again next time scheduled functions are run + mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function); + } +} + // function to run extra tests for things that can't be checked by scripts static mp_obj_t extra_coverage(void) { // mp_printf (used by ports that don't have a native printf) @@ -208,6 +220,7 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%X\n", 0x80000000); // should print unsigned mp_printf(&mp_plat_print, "abc\n%"); // string ends in middle of format specifier mp_printf(&mp_plat_print, "%%\n"); // literal % character + mp_printf(&mp_plat_print, ".%-3s.\n", "a"); // left adjust } // GC @@ -463,6 +476,18 @@ static mp_obj_t extra_coverage(void) { mp_int_t value_signed; mpz_as_int_checked(&mpz, &value_signed); mp_printf(&mp_plat_print, "%d\n", (int)value_signed); + + // hash the zero mpz integer + mpz_set_from_int(&mpz, 0); + mp_printf(&mp_plat_print, "%d\n", mpz_hash(&mpz)); + + // convert the mpz zero integer to int + mp_printf(&mp_plat_print, "%d\n", mpz_as_int_checked(&mpz, &value_signed)); + mp_printf(&mp_plat_print, "%d\n", value_signed); + + // mpz_set_from_float with 0 as argument + mpz_set_from_float(&mpz, 0); + mp_printf(&mp_plat_print, "%f\n", mpz_as_float(&mpz)); } // runtime utils @@ -621,6 +646,19 @@ static mp_obj_t extra_coverage(void) { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); } mp_handle_pending(true); + + coverage_sched_function_continue = true; + mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function); + for (int i = 0; i < 3; ++i) { + mp_printf(&mp_plat_print, "loop\n"); + mp_handle_pending(true); + } + // Clear this flag to prevent the function scheduling itself again + coverage_sched_function_continue = false; + // Will only run the first time through this loop, then not scheduled again + for (int i = 0; i < 3; ++i) { + mp_handle_pending(true); + } } // ringbuf diff --git a/ports/unix/main.c b/ports/unix/main.c index 9f51573fbf..530e20a386 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -775,7 +775,7 @@ MP_NOINLINE int main_(int argc, char **argv) { #endif #if MICROPY_PY_BLUETOOTH - void mp_bluetooth_deinit(void); + int mp_bluetooth_deinit(void); mp_bluetooth_deinit(); #endif diff --git a/ports/unix/modsocket.c b/ports/unix/modsocket.c index 6d6059ae44..2aaa21183a 100644 --- a/ports/unix/modsocket.c +++ b/ports/unix/modsocket.c @@ -701,6 +701,7 @@ static const mp_rom_map_elem_t mp_module_socket_globals_table[] = { C(MSG_DONTROUTE), C(MSG_DONTWAIT), + C(MSG_PEEK), C(SOL_SOCKET), C(SO_BROADCAST), diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index 04b5b8ae1a..cfefeb4672 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -44,6 +44,7 @@ #undef MICROPY_VFS_ROM_IOCTL #define MICROPY_VFS_ROM_IOCTL (1) #define MICROPY_PY_CRYPTOLIB_CTR (1) +#define MICROPY_SCHEDULER_STATIC_NODES (1) // Enable os.uname for attrtuple coverage test #define MICROPY_PY_OS_UNAME (1) diff --git a/ports/webassembly/main.c b/ports/webassembly/main.c index c542f0cd72..770dfbe0ca 100644 --- a/ports/webassembly/main.c +++ b/ports/webassembly/main.c @@ -233,7 +233,7 @@ void nlr_jump_fail(void *val) { } } -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { while (1) { ; } diff --git a/ports/webassembly/objpyproxy.js b/ports/webassembly/objpyproxy.js index 3b94f8aadc..0eafd0dec5 100644 --- a/ports/webassembly/objpyproxy.js +++ b/ports/webassembly/objpyproxy.js @@ -148,6 +148,12 @@ const py_proxy_handler = { }; }, has(target, prop) { + // avoid throwing on `Symbol() in proxy` checks + if (typeof prop !== "string") { + // returns true only on iterator because other + // symbols are not considered in the `get` trap + return prop === Symbol.iterator; + } return Module.ccall( "proxy_c_to_js_has_attr", "number", diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index fabc9072d6..4e140d5edb 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -242,7 +242,7 @@ typedef long mp_off_t; // CL specific overrides from mpconfig -#define NORETURN __declspec(noreturn) +#define MP_NORETURN __declspec(noreturn) #define MP_WEAK #define MP_NOINLINE __declspec(noinline) #define MP_ALWAYSINLINE __forceinline @@ -266,6 +266,11 @@ typedef long mp_off_t; #endif #endif +// VC++ 2017 fixes +#if (_MSC_VER < 1920) +#define MICROPY_PY_MATH_COPYSIGN_FIX_NAN (1) +#endif + // CL specific definitions #ifndef __cplusplus diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index 4f457f4a58..debf2bd2c1 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -128,6 +128,7 @@ add_dependencies(${MICROPY_TARGET} zephyr_generated_headers) target_sources(app PRIVATE src/zephyr_start.c src/zephyr_getchar.c + src/usbd.c ) target_link_libraries(app PRIVATE ${MICROPY_TARGET}) diff --git a/ports/zephyr/Kconfig b/ports/zephyr/Kconfig index f8d1543155..6db0133f4f 100644 --- a/ports/zephyr/Kconfig +++ b/ports/zephyr/Kconfig @@ -49,6 +49,14 @@ config MICROPY_FROZEN_MANIFEST depends on MICROPY_FROZEN_MODULES default "boards/manifest.py" +config MICROPY_USB_DEVICE_VID + hex "USB VID" + default 0x2fe3 + +config MICROPY_USB_DEVICE_PID + hex "USB PID" + default 0x0001 + endmenu # MicroPython Options source "Kconfig.zephyr" diff --git a/ports/zephyr/boards/beagleconnect_freedom.conf b/ports/zephyr/boards/beagleconnect_freedom.conf index 1e3f6037bd..e31e09444f 100644 --- a/ports/zephyr/boards/beagleconnect_freedom.conf +++ b/ports/zephyr/boards/beagleconnect_freedom.conf @@ -1,4 +1,5 @@ # Hardware features +CONFIG_ADC=y CONFIG_PWM=y CONFIG_I2C=y CONFIG_SPI=y @@ -6,3 +7,13 @@ CONFIG_SPI=y # Flash drivers CONFIG_FLASH=y CONFIG_FLASH_MAP=y + +# Networking +CONFIG_BT=n +CONFIG_NET_IPV4=n +CONFIG_NET_CONFIG_NEED_IPV6=y +CONFIG_NET_CONFIG_NEED_IPV4=n +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_L2_IEEE802154=y +CONFIG_NET_DHCPV4=n +CONFIG_NET_CONFIG_IEEE802154_CHANNEL=1 diff --git a/ports/zephyr/boards/beagleconnect_freedom.overlay b/ports/zephyr/boards/beagleconnect_freedom.overlay deleted file mode 100644 index 7dd4469c99..0000000000 --- a/ports/zephyr/boards/beagleconnect_freedom.overlay +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2024 Ayush Singh <ayush@beagleboard.org> - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&pinctrl { - /* MB1 PWM */ - pwm0_default: pwm0_default { - pinmux = <17 IOC_PORT_MCU_PORT_EVENT1>; - bias-disable; - drive-strength = <2>; - }; - - /* MB2 PWM */ - pwm1_default: pwm1_default { - pinmux = <19 IOC_PORT_MCU_PORT_EVENT3>; - bias-disable; - drive-strength = <2>; - }; -}; - -&gpt0 { - status = "okay"; -}; - -&gpt1 { - status = "okay"; -}; - -&pwm0 { - status = "okay"; - pinctrl-0 = <&pwm0_default>; - pinctrl-names = "default"; -}; - -&pwm1 { - status = "okay"; - pinctrl-0 = <&pwm1_default>; - pinctrl-names = "default"; -}; diff --git a/ports/zephyr/boards/beagleplay_cc1352p7.conf b/ports/zephyr/boards/beagleplay_cc1352p7.conf new file mode 100644 index 0000000000..f63d184e38 --- /dev/null +++ b/ports/zephyr/boards/beagleplay_cc1352p7.conf @@ -0,0 +1,13 @@ +# Flash drivers +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y + +# Networking +CONFIG_BT=n +CONFIG_NET_IPV4=n +CONFIG_NET_CONFIG_NEED_IPV6=y +CONFIG_NET_CONFIG_NEED_IPV4=n +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_L2_IEEE802154=y +CONFIG_NET_DHCPV4=n +CONFIG_NET_CONFIG_IEEE802154_CHANNEL=1 diff --git a/ports/zephyr/boards/nrf52840dk_nrf52840.conf b/ports/zephyr/boards/nrf52840dk_nrf52840.conf index e9ec78b64f..dc2ed1c4b4 100644 --- a/ports/zephyr/boards/nrf52840dk_nrf52840.conf +++ b/ports/zephyr/boards/nrf52840dk_nrf52840.conf @@ -1,8 +1,13 @@ CONFIG_NETWORKING=n CONFIG_BT=y CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_GATT_DYNAMIC_DB=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_CENTRAL=y +CONFIG_BT_GATT_CLIENT=y +CONFIG_BT_L2CAP_TX_MTU=252 +CONFIG_BT_BUF_ACL_RX_SIZE=256 +CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n CONFIG_MICROPY_HEAP_SIZE=98304 CONFIG_MAIN_STACK_SIZE=8192 diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 206b7f92d3..45af7b0c72 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -63,6 +63,10 @@ static char heap[MICROPY_HEAP_SIZE]; +#if defined(CONFIG_USB_DEVICE_STACK_NEXT) +extern int mp_usbd_init(void); +#endif // defined(CONFIG_USB_DEVICE_STACK_NEXT) + void init_zephyr(void) { // We now rely on CONFIG_NET_APP_SETTINGS to set up bootstrap // network addresses. @@ -143,6 +147,10 @@ soft_reset: usb_enable(NULL); #endif + #ifdef CONFIG_USB_DEVICE_STACK_NEXT + mp_usbd_init(); + #endif + #if MICROPY_VFS vfs_init(); #endif @@ -211,7 +219,7 @@ mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); #endif -NORETURN void nlr_jump_fail(void *val) { +MP_NORETURN void nlr_jump_fail(void *val) { while (1) { ; } diff --git a/ports/zephyr/modbluetooth_zephyr.c b/ports/zephyr/modbluetooth_zephyr.c index 8947bdf535..c1b8ddfd8b 100644 --- a/ports/zephyr/modbluetooth_zephyr.c +++ b/ports/zephyr/modbluetooth_zephyr.c @@ -31,8 +31,12 @@ #if MICROPY_PY_BLUETOOTH +#include <zephyr/types.h> #include <zephyr/bluetooth/bluetooth.h> #include <zephyr/bluetooth/hci.h> +#include <zephyr/bluetooth/conn.h> +#include <zephyr/bluetooth/uuid.h> +#include <zephyr/bluetooth/gatt.h> #include "extmod/modbluetooth.h" #define DEBUG_printf(...) // printk("BLE: " __VA_ARGS__) @@ -44,6 +48,23 @@ #define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV +#define MP_BLUETOOTH_ZEPHYR_MAX_SERVICES (8) + +/* This masks Permission bits from GATT API */ +#define GATT_PERM_MASK (BT_GATT_PERM_READ | \ + BT_GATT_PERM_READ_AUTHEN | \ + BT_GATT_PERM_READ_ENCRYPT | \ + BT_GATT_PERM_WRITE | \ + BT_GATT_PERM_WRITE_AUTHEN | \ + BT_GATT_PERM_WRITE_ENCRYPT | \ + BT_GATT_PERM_PREPARE_WRITE) + +#define GATT_PERM_ENC_READ_MASK (BT_GATT_PERM_READ_ENCRYPT | \ + BT_GATT_PERM_READ_AUTHEN) + +#define GATT_PERM_ENC_WRITE_MASK (BT_GATT_PERM_WRITE_ENCRYPT | \ + BT_GATT_PERM_WRITE_AUTHEN) + enum { MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF, MP_BLUETOOTH_ZEPHYR_BLE_STATE_ACTIVE, @@ -56,9 +77,42 @@ enum { MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_ACTIVE, }; +union uuid_u { + struct bt_uuid uuid; + struct bt_uuid_16 u16; + struct bt_uuid_32 u32; + struct bt_uuid_128 u128; +}; + +struct add_characteristic { + uint8_t properties; + uint8_t permissions; + const struct bt_uuid *uuid; +}; + +struct add_descriptor { + uint8_t permissions; + const struct bt_uuid *uuid; +}; + +typedef struct _mp_bt_zephyr_conn_t { + struct bt_conn *conn; + struct _mp_bt_zephyr_conn_t *next; +} mp_bt_zephyr_conn_t; + typedef struct _mp_bluetooth_zephyr_root_pointers_t { + // list of objects to be tracked by the gc + mp_obj_t objs_list; + // Characteristic (and descriptor) value storage. mp_gatts_db_t gatts_db; + + // Service definitions. + size_t n_services; + struct bt_gatt_service *services[MP_BLUETOOTH_ZEPHYR_MAX_SERVICES]; + + // active connections + mp_bt_zephyr_conn_t *connections; } mp_bluetooth_zephyr_root_pointers_t; static int mp_bluetooth_zephyr_ble_state; @@ -69,6 +123,98 @@ static struct k_timer mp_bluetooth_zephyr_gap_scan_timer; static struct bt_le_scan_cb mp_bluetooth_zephyr_gap_scan_cb_struct; #endif +static struct bt_data bt_ad_data[8]; +static size_t bt_ad_len = 0; +static struct bt_data bt_sd_data[8]; +static size_t bt_sd_len = 0; + +static mp_bt_zephyr_conn_t *mp_bt_zephyr_next_conn; + +static mp_bt_zephyr_conn_t *mp_bt_zephyr_find_connection(uint8_t conn_handle); +static void mp_bt_zephyr_insert_connection(mp_bt_zephyr_conn_t *connection); +static void mp_bt_zephyr_remove_connection(uint8_t conn_handle); +static void mp_bt_zephyr_connected(struct bt_conn *connected, uint8_t err); +static void mp_bt_zephyr_disconnected(struct bt_conn *disconn, uint8_t reason); +static struct bt_uuid *create_zephyr_uuid(const mp_obj_bluetooth_uuid_t *uuid); +static void gatt_db_add(const struct bt_gatt_attr *pattern, struct bt_gatt_attr *attr, size_t user_data_len); +static void add_service(const struct bt_uuid *u, struct bt_gatt_attr *attr); +static void add_characteristic(struct add_characteristic *ch, struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_value); +static void add_ccc(struct bt_gatt_attr *attr, struct bt_gatt_attr *attr_desc); +static void add_cep(const struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_desc); +static void add_descriptor(struct bt_gatt_attr *chrc, struct add_descriptor *d, struct bt_gatt_attr *attr_desc); +static void mp_bt_zephyr_gatt_indicate_done(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err); +static struct bt_gatt_attr *mp_bt_zephyr_find_attr_by_handle(uint16_t value_handle); + +static struct bt_conn_cb mp_bt_zephyr_conn_callbacks = { + .connected = mp_bt_zephyr_connected, + .disconnected = mp_bt_zephyr_disconnected, +}; + +static mp_bt_zephyr_conn_t *mp_bt_zephyr_find_connection(uint8_t conn_handle) { + struct bt_conn_info info; + for (mp_bt_zephyr_conn_t *connection = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; connection != NULL; connection = connection->next) { + if (connection->conn) { + bt_conn_get_info(connection->conn, &info); + if (info.id == conn_handle) { + return connection; + } + } + } + return NULL; +} + +static void mp_bt_zephyr_insert_connection(mp_bt_zephyr_conn_t *connection) { + connection->next = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = connection; +} + +static void mp_bt_zephyr_remove_connection(uint8_t conn_handle) { + struct bt_conn_info info; + mp_bt_zephyr_conn_t *prev = NULL; + for (mp_bt_zephyr_conn_t *connection = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; connection != NULL; connection = connection->next) { + if (connection->conn) { + bt_conn_get_info(connection->conn, &info); + if (info.id == conn_handle) { + // unlink this item and the gc will eventually collect it + if (prev != NULL) { + prev->next = connection->next; + } else { + // move the start pointer + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = connection->next; + } + break; + } else { + prev = connection; + } + } + } +} + +static void mp_bt_zephyr_connected(struct bt_conn *conn, uint8_t err) { + struct bt_conn_info info; + bt_conn_get_info(conn, &info); + + if (err) { + uint8_t addr[6] = {0}; + DEBUG_printf("Connection from central failed (err %u)\n", err); + mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, info.id, 0xff, addr); + } else { + DEBUG_printf("Central connected with id %d\n", info.id); + mp_bt_zephyr_next_conn->conn = bt_conn_ref(conn); + mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_CONNECT, info.id, info.le.dst->type, info.le.dst->a.val); + mp_bt_zephyr_insert_connection(mp_bt_zephyr_next_conn); + } +} + +static void mp_bt_zephyr_disconnected(struct bt_conn *conn, uint8_t reason) { + struct bt_conn_info info; + bt_conn_get_info(conn, &info); + DEBUG_printf("Central disconnected (id %d reason %u)\n", info.id, reason); + bt_conn_unref(conn); + mp_bt_zephyr_remove_connection(info.id); + mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, info.id, info.le.dst->type, info.le.dst->a.val); +} + static int bt_err_to_errno(int err) { // Zephyr uses errno codes directly, but they are negative. return -err; @@ -128,6 +274,11 @@ int mp_bluetooth_init(void) { MP_STATE_PORT(bluetooth_zephyr_root_pointers) = m_new0(mp_bluetooth_zephyr_root_pointers_t, 1); mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db); + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = NULL; + mp_bt_zephyr_next_conn = NULL; + + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list = mp_obj_new_list(0, NULL); + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE mp_bluetooth_zephyr_gap_scan_state = MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_INACTIVE; k_timer_init(&mp_bluetooth_zephyr_gap_scan_timer, gap_scan_cb_timeout, NULL); @@ -137,6 +288,9 @@ int mp_bluetooth_init(void) { #endif if (mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF) { + + bt_conn_cb_register(&mp_bt_zephyr_conn_callbacks); + // bt_enable can only be called once. int ret = bt_enable(NULL); if (ret) { @@ -151,15 +305,22 @@ int mp_bluetooth_init(void) { return 0; } -void mp_bluetooth_deinit(void) { +int mp_bluetooth_deinit(void) { DEBUG_printf("mp_bluetooth_deinit %d\n", mp_bluetooth_zephyr_ble_state); if (mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF || mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED) { - return; + return 0; } mp_bluetooth_gap_advertise_stop(); + #if CONFIG_BT_GATT_DYNAMIC_DB + for (size_t i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; ++i) { + bt_gatt_service_unregister(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]); + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i] = NULL; + } + #endif + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE mp_bluetooth_gap_scan_stop(); bt_le_scan_cb_unregister(&mp_bluetooth_zephyr_gap_scan_cb_struct); @@ -170,6 +331,8 @@ void mp_bluetooth_deinit(void) { mp_bluetooth_zephyr_ble_state = MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED; MP_STATE_PORT(bluetooth_zephyr_root_pointers) = NULL; + mp_bt_zephyr_next_conn = NULL; + return 0; } bool mp_bluetooth_is_active(void) { @@ -191,7 +354,7 @@ void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr) { } void mp_bluetooth_set_address_mode(uint8_t addr_mode) { - // TODO: implement + mp_raise_OSError(MP_EOPNOTSUPP); } size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) { @@ -232,15 +395,11 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons mp_bluetooth_gap_advertise_stop(); - struct bt_data bt_ad_data[8]; - size_t bt_ad_len = 0; if (adv_data) { bt_ad_len = MP_ARRAY_SIZE(bt_ad_data); mp_bluetooth_prepare_bt_data(adv_data, adv_data_len, bt_ad_data, &bt_ad_len); } - struct bt_data bt_sd_data[8]; - size_t bt_sd_len = 0; if (sr_data) { bt_sd_len = MP_ARRAY_SIZE(bt_sd_data); mp_bluetooth_prepare_bt_data(sr_data, sr_data_len, bt_sd_data, &bt_sd_len); @@ -259,6 +418,10 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons .peer = NULL, }; + // pre-allocate a new connection structure as we cannot allocate this inside the connection callback + mp_bt_zephyr_next_conn = m_new0(mp_bt_zephyr_conn_t, 1); + mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(mp_bt_zephyr_next_conn)); + return bt_err_to_errno(bt_le_adv_start(¶m, bt_ad_data, bt_ad_len, bt_sd_data, bt_sd_len)); } @@ -271,6 +434,8 @@ void mp_bluetooth_gap_advertise_stop(void) { } int mp_bluetooth_gatts_register_service_begin(bool append) { + #if CONFIG_BT_GATT_DYNAMIC_DB + if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } @@ -280,25 +445,190 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { return MP_EOPNOTSUPP; } + // Unregister and unref any previous service definitions. + for (size_t i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; ++i) { + bt_gatt_service_unregister(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]); + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i] = NULL; + } + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services = 0; + // Reset the gatt characteristic value db. mp_bluetooth_gatts_db_reset(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db); + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = NULL; + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list = mp_obj_new_list(0, NULL); + mp_bt_zephyr_next_conn = NULL; + return 0; + + #else return MP_EOPNOTSUPP; + #endif } int mp_bluetooth_gatts_register_service_end(void) { - return MP_EOPNOTSUPP; + return 0; } int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, mp_obj_bluetooth_uuid_t **characteristic_uuids, uint16_t *characteristic_flags, mp_obj_bluetooth_uuid_t **descriptor_uuids, uint16_t *descriptor_flags, uint8_t *num_descriptors, uint16_t *handles, size_t num_characteristics) { + #if CONFIG_BT_GATT_DYNAMIC_DB + if (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services >= MP_BLUETOOTH_ZEPHYR_MAX_SERVICES) { + return MP_E2BIG; + } + + // first of all allocate the entire memory for all the attributes that this service is composed of + // 1 for the service itself, 2 for each characteristic (the declaration and the value), and one for each descriptor + size_t total_descriptors = 0; + for (size_t i = 0; i < num_characteristics; ++i) { + total_descriptors += num_descriptors[i]; + // we have to add the CCC manually + if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) { + total_descriptors += 1; + } + } + size_t total_attributes = 1 + (num_characteristics * 2) + total_descriptors; + + // allocate one extra so that we can know later where the final attribute is + struct bt_gatt_attr *svc_attributes = m_new(struct bt_gatt_attr, total_attributes + 1); + mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(svc_attributes)); + + size_t handle_index = 0; + size_t descriptor_index = 0; + size_t attr_index = 0; + // bitfield of the handles we should ignore, should be more than enough for most applications + uint64_t attrs_to_ignore = 0; + uint64_t attrs_are_chrs = 0; + uint64_t chr_has_ccc = 0; + + add_service(create_zephyr_uuid(service_uuid), &svc_attributes[attr_index]); + attr_index += 1; + + for (size_t i = 0; i < num_characteristics; ++i) { + + struct add_characteristic add_char; + add_char.uuid = create_zephyr_uuid(characteristic_uuids[i]); + add_char.permissions = 0; + add_char.properties = 0; + if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ) { + add_char.permissions |= BT_GATT_PERM_READ; + add_char.properties |= BT_GATT_CHRC_READ; + } + if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY) { + add_char.properties |= BT_GATT_CHRC_NOTIFY; + } + if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE) { + add_char.properties |= BT_GATT_CHRC_INDICATE; + } + if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE | MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE_NO_RESPONSE)) { + add_char.permissions |= BT_GATT_PERM_WRITE; + add_char.properties |= (BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP); + } + + add_characteristic(&add_char, &svc_attributes[attr_index], &svc_attributes[attr_index + 1]); + + struct bt_gatt_attr *curr_char = &svc_attributes[attr_index]; + attrs_are_chrs |= (1 << attr_index); + if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) { + chr_has_ccc |= (1 << attr_index); + } + attr_index += 1; + attrs_to_ignore |= (1 << attr_index); // ignore the value handle + attr_index += 1; + + if (num_descriptors[i] > 0) { + for (size_t j = 0; j < num_descriptors[i]; ++j) { + + struct add_descriptor add_desc; + add_desc.uuid = create_zephyr_uuid(descriptor_uuids[descriptor_index]); + add_desc.permissions = 0; + if (descriptor_flags[descriptor_index] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ) { + add_desc.permissions |= BT_GATT_PERM_READ; + } + if (descriptor_flags[descriptor_index] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE | MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE_NO_RESPONSE)) { + add_desc.permissions |= BT_GATT_PERM_WRITE; + } + + add_descriptor(curr_char, &add_desc, &svc_attributes[attr_index]); + attr_index += 1; + + descriptor_index++; + } + } + + // to support indications and notifications we must add the CCC descriptor manually + if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) { + struct add_descriptor add_desc; + mp_obj_bluetooth_uuid_t ccc_uuid; + ccc_uuid.base.type = &mp_type_bluetooth_uuid; + ccc_uuid.data[0] = BT_UUID_GATT_CCC_VAL & 0xff; + ccc_uuid.data[1] = (BT_UUID_GATT_CCC_VAL >> 8) & 0xff; + ccc_uuid.type = MP_BLUETOOTH_UUID_TYPE_16; + add_desc.uuid = create_zephyr_uuid(&ccc_uuid); + add_desc.permissions = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE; + + attrs_to_ignore |= (1 << attr_index); + + add_descriptor(curr_char, &add_desc, &svc_attributes[attr_index]); + attr_index += 1; + } + } + + struct bt_gatt_service *service = m_new(struct bt_gatt_service, 1); + mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(service)); + service->attrs = svc_attributes; + service->attr_count = attr_index; + // invalidate the last attribute uuid pointer so that we new this is the end of attributes for this service + svc_attributes[attr_index].uuid = NULL; + + // Note: advertising must be stopped for gatts registration to work + + int err = bt_gatt_service_register(service); + if (err) { + return bt_err_to_errno(err); + } + + // now that the service has been registered, we can assign the handles for the characteristics and the descriptors + // we are not interested in the handle of the service itself, so we start the loop from index 1 + for (int i = 1; i < total_attributes; i++) { + // store all the relevant handles (characteristics and descriptors defined in Python) + if (!((uint64_t)(attrs_to_ignore >> i) & (uint64_t)0x01)) { + if (svc_attributes[i].user_data == NULL) { + mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN); + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle); + svc_attributes[i].user_data = entry->data; + } else if (((uint64_t)(attrs_are_chrs >> i) & (uint64_t)0x01)) { + if (svc_attributes[i + 1].user_data == NULL) { + mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN); + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle); + svc_attributes[i + 1].user_data = entry->data; + + if (((uint64_t)(chr_has_ccc >> i) & (uint64_t)0x01)) { + // create another database entry for the ccc of this characteristic + mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle + 2, 1); + } + } + } + handles[handle_index++] = svc_attributes[i].handle; + } + } + + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services++] = service; + + return 0; + + #else return MP_EOPNOTSUPP; + #endif } int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - return MP_EOPNOTSUPP; + mp_bt_zephyr_conn_t *connection = mp_bt_zephyr_find_connection(conn_handle); + if (connection) { + return bt_conn_disconnect(connection->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } + return MP_ENOENT; } int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len) { @@ -312,24 +642,155 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - if (send_update) { - return MP_EOPNOTSUPP; + + int err = mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len); + + if ((err == 0) && send_update) { + struct bt_gatt_attr *attr_val = mp_bt_zephyr_find_attr_by_handle(value_handle + 1); + mp_bluetooth_gatts_db_entry_t *ccc_entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle + 2); + + if (ccc_entry && (ccc_entry->data[0] == BT_GATT_CCC_NOTIFY)) { + err = bt_gatt_notify(NULL, attr_val, value, value_len); + } else if (ccc_entry && (ccc_entry->data[0] == BT_GATT_CCC_INDICATE)) { + struct bt_gatt_indicate_params params = { + .uuid = NULL, + .attr = attr_val, + .func = mp_bt_zephyr_gatt_indicate_done, + .destroy = NULL, + .data = value, + .len = value_len + }; + err = bt_gatt_indicate(NULL, ¶ms); + } + } + return err; +} + +static void mp_bt_zephyr_gatt_indicate_done(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err) { + struct bt_conn_info info; + bt_conn_get_info(conn, &info); + uint16_t chr_handle = params->attr->handle - 1; + mp_bluetooth_gatts_on_indicate_complete(info.id, chr_handle, err); +} + +static ssize_t mp_bt_zephyr_gatts_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) { + // we receive the value handle, but to look up in the gatts db we need the characteristic handle, and that is is the value handle minus 1 + uint16_t _handle = attr->handle - 1; + + DEBUG_printf("BLE attr read for handle %d\n", attr->handle); + + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle); + if (!entry) { + // it could be a descriptor instead + _handle = attr->handle; + entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle); + if (!entry) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_HANDLE); + } + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, entry->data, entry->data_len); +} + +static ssize_t mp_bt_zephyr_gatts_attr_write(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + struct bt_conn_info info; + bt_conn_get_info(conn, &info); + + DEBUG_printf("BLE attr write for handle %d\n", attr->handle); + + // the characteristic handle is the value handle minus 1 + uint16_t _handle = attr->handle - 1; + + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle); + if (!entry) { + // it could be a descriptor instead + _handle = attr->handle; + entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle); + if (!entry) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_HANDLE); + } + } + + // Don't write anything if prepare flag is set + if (flags & BT_GATT_WRITE_FLAG_PREPARE) { + return 0; + } + + if (offset > entry->data_alloc) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + if ((offset + len) > entry->data_alloc) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + if (entry->append) { + offset = entry->data_len; + } + + // copy the data into the buffer in the gatts database + memcpy(&entry->data[offset], buf, len); + entry->data_len = offset + len; + + mp_bluetooth_gatts_on_write(info.id, _handle); + + return len; +} + +static struct bt_gatt_attr *mp_bt_zephyr_find_attr_by_handle(uint16_t value_handle) { + for (int i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; i++) { + int j = 0; + while (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j].uuid != NULL) { + if (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j].handle == value_handle) { + return &MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j]; + } + j++; + } } - return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len); + return NULL; } int mp_bluetooth_gatts_notify_indicate(uint16_t conn_handle, uint16_t value_handle, int gatts_op, const uint8_t *value, size_t value_len) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - return MP_EOPNOTSUPP; + + int err = MP_ENOENT; + mp_bt_zephyr_conn_t *connection = mp_bt_zephyr_find_connection(conn_handle); + + if (connection) { + struct bt_gatt_attr *attr_val = mp_bt_zephyr_find_attr_by_handle(value_handle + 1); + + if (attr_val) { + switch (gatts_op) { + case MP_BLUETOOTH_GATTS_OP_NOTIFY: { + err = bt_gatt_notify(connection->conn, attr_val, value, value_len); + break; + } + case MP_BLUETOOTH_GATTS_OP_INDICATE: { + struct bt_gatt_indicate_params params = { + .uuid = NULL, + .attr = attr_val, + .func = mp_bt_zephyr_gatt_indicate_done, + .destroy = NULL, + .data = value, + .len = value_len + }; + err = bt_gatt_indicate(connection->conn, ¶ms); + break; + } + } + } + } + + return err; } int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - return MP_EOPNOTSUPP; + return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, len, append); } int mp_bluetooth_get_preferred_mtu(void) { @@ -404,6 +865,133 @@ int mp_bluetooth_gap_peripheral_connect_cancel(void) { #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +// Note: modbluetooth UUIDs store their data in LE. +static struct bt_uuid *create_zephyr_uuid(const mp_obj_bluetooth_uuid_t *uuid) { + struct bt_uuid *result = (struct bt_uuid *)m_new(union uuid_u, 1); + mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(result)); + if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { + bt_uuid_create(result, uuid->data, 2); + } else if (uuid->type == MP_BLUETOOTH_UUID_TYPE_32) { + bt_uuid_create(result, uuid->data, 4); + } else { // MP_BLUETOOTH_UUID_TYPE_128 + bt_uuid_create(result, uuid->data, 16); + } + return result; +} + +static void gatt_db_add(const struct bt_gatt_attr *pattern, struct bt_gatt_attr *attr, size_t user_data_len) { + const union uuid_u *u = CONTAINER_OF(pattern->uuid, union uuid_u, uuid); + size_t uuid_size = sizeof(u->u16); + + if (u->uuid.type == BT_UUID_TYPE_32) { + uuid_size = sizeof(u->u32); + } else if (u->uuid.type == BT_UUID_TYPE_128) { + uuid_size = sizeof(u->u128); + } + + memcpy(attr, pattern, sizeof(*attr)); + + // Store the UUID. + attr->uuid = (const struct bt_uuid *)m_new(union uuid_u, 1); + mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(attr->uuid)); + memcpy((void *)attr->uuid, &u->uuid, uuid_size); + + // Copy user_data to the buffer. + if (user_data_len) { + attr->user_data = m_new(uint8_t, user_data_len); + mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(attr->user_data)); + memcpy(attr->user_data, pattern->user_data, user_data_len); + } +} + +static void add_service(const struct bt_uuid *u, struct bt_gatt_attr *attr) { + union uuid_u *uuid = (union uuid_u *)u; + + size_t uuid_size = sizeof(uuid->u16); + + if (uuid->uuid.type == BT_UUID_TYPE_32) { + uuid_size = sizeof(uuid->u32); + } else if (uuid->uuid.type == BT_UUID_TYPE_128) { + uuid_size = sizeof(uuid->u128); + } + + gatt_db_add(&(struct bt_gatt_attr)BT_GATT_PRIMARY_SERVICE(&uuid->uuid), attr, uuid_size); +} + +static void add_characteristic(struct add_characteristic *ch, struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_value) { + struct bt_gatt_chrc *chrc_data; + + // Add Characteristic Declaration + gatt_db_add(&(struct bt_gatt_attr) + BT_GATT_ATTRIBUTE(BT_UUID_GATT_CHRC, + BT_GATT_PERM_READ, + bt_gatt_attr_read_chrc, NULL, + (&(struct bt_gatt_chrc) {})), attr_chrc, sizeof(*chrc_data)); + + // Allow prepare writes + ch->permissions |= BT_GATT_PERM_PREPARE_WRITE; + + // Add Characteristic Value + gatt_db_add(&(struct bt_gatt_attr) + BT_GATT_ATTRIBUTE(ch->uuid, + ch->permissions & GATT_PERM_MASK, + mp_bt_zephyr_gatts_attr_read, mp_bt_zephyr_gatts_attr_write, NULL), attr_value, 0); + + chrc_data = attr_chrc->user_data; + chrc_data->properties = ch->properties; + chrc_data->uuid = attr_value->uuid; +} + +static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) { + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, attr->handle); + entry->data[0] = value; +} + +static struct bt_gatt_attr ccc_definition = BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE); + +static void add_ccc(struct bt_gatt_attr *attr, struct bt_gatt_attr *attr_desc) { + struct bt_gatt_chrc *chrc = attr->user_data; + + // Check characteristic properties + if (!(chrc->properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE))) { + mp_raise_OSError(MP_EINVAL); + } + + // Add CCC descriptor to GATT database + gatt_db_add(&ccc_definition, attr_desc, 0); +} + +static void add_cep(const struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_desc) { + struct bt_gatt_chrc *chrc = attr_chrc->user_data; + struct bt_gatt_cep cep_value; + + // Extended Properties bit shall be set + if (!(chrc->properties & BT_GATT_CHRC_EXT_PROP)) { + mp_raise_OSError(MP_EINVAL); + } + + cep_value.properties = 0x0000; + + // Add CEP descriptor to GATT database + gatt_db_add(&(struct bt_gatt_attr)BT_GATT_CEP(&cep_value), attr_desc, sizeof(cep_value)); +} + +static void add_descriptor(struct bt_gatt_attr *chrc, struct add_descriptor *d, struct bt_gatt_attr *attr_desc) { + if (!bt_uuid_cmp(d->uuid, BT_UUID_GATT_CEP)) { + add_cep(chrc, attr_desc); + } else if (!bt_uuid_cmp(d->uuid, BT_UUID_GATT_CCC)) { + add_ccc(chrc, attr_desc); + } else { + // Allow prepare writes + d->permissions |= BT_GATT_PERM_PREPARE_WRITE; + + gatt_db_add(&(struct bt_gatt_attr) + BT_GATT_DESCRIPTOR(d->uuid, + d->permissions & GATT_PERM_MASK, + mp_bt_zephyr_gatts_attr_read, mp_bt_zephyr_gatts_attr_write, NULL), attr_desc, 0); + } +} + MP_REGISTER_ROOT_POINTER(struct _mp_bluetooth_zephyr_root_pointers_t *bluetooth_zephyr_root_pointers); #endif // MICROPY_PY_BLUETOOTH diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index e015776a4e..3a6e934862 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -139,8 +139,8 @@ void mp_hal_signal_event(void); #define MICROPY_HW_MCU_NAME "unknown-cpu" #endif -typedef int mp_int_t; // must be pointer size -typedef unsigned mp_uint_t; // must be pointer size +typedef intptr_t mp_int_t; // must be pointer size +typedef uintptr_t mp_uint_t; // must be pointer size typedef long mp_off_t; #define MP_STATE_PORT MP_STATE_VM diff --git a/ports/zephyr/src/usbd.c b/ports/zephyr/src/usbd.c new file mode 100644 index 0000000000..2444706cbe --- /dev/null +++ b/ports/zephyr/src/usbd.c @@ -0,0 +1,183 @@ +/* +* This file is part of the MicroPython project, http://micropython.org/ +* +* The MIT License (MIT) +* +* Copyright (c) 2024-2025 MASSDRIVER EI (massdriver.space) +* +* 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 <stdint.h> + +#include <zephyr/device.h> +#include <zephyr/usb/usbd.h> +#include <zephyr/usb/bos.h> + +#if defined(CONFIG_USB_DEVICE_STACK_NEXT) + +#include <zephyr/logging/log.h> +LOG_MODULE_REGISTER(mp_usbd); + +/* By default, do not register the USB DFU class DFU mode instance. */ +static const char *const blocklist[] = { + "dfu_dfu", + NULL, +}; + +USBD_DEVICE_DEFINE(mp_usbd, + DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), + CONFIG_MICROPY_USB_DEVICE_VID, CONFIG_MICROPY_USB_DEVICE_PID); + +USBD_DESC_LANG_DEFINE(mp_lang); +USBD_DESC_MANUFACTURER_DEFINE(mp_mfr, "Zephyr Project"); +USBD_DESC_PRODUCT_DEFINE(mp_product, "Micropython on Zephyr RTOS"); +USBD_DESC_SERIAL_NUMBER_DEFINE(mp_sn); + +USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration"); +USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration"); + +/* not self-powered, no remote wakeup */ +static const uint8_t attributes = 0; + +/* Full speed configuration +* power = 250 * 2 mA = 500mA +*/ +USBD_CONFIGURATION_DEFINE(mp_fs_config, + attributes, + 250, &fs_cfg_desc); + +/* High speed configuration */ +USBD_CONFIGURATION_DEFINE(mp_hs_config, + attributes, + 250, &hs_cfg_desc); + +static void mp_fix_code_triple(struct usbd_context *uds_ctx, + const enum usbd_speed speed) { + /* Always use class code information from Interface Descriptors */ + if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) || + IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) || + IS_ENABLED(CONFIG_USBD_CDC_NCM_CLASS) || + IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS)) { + /* + * Class with multiple interfaces have an Interface + * Association Descriptor available, use an appropriate triple + * to indicate it. + */ + usbd_device_set_code_triple(uds_ctx, speed, + USB_BCC_MISCELLANEOUS, 0x02, 0x01); + } else { + usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0); + } +} + +struct usbd_context *mp_usbd_init_device(usbd_msg_cb_t msg_cb) { + int err; + + err = usbd_add_descriptor(&mp_usbd, &mp_lang); + if (err) { + LOG_ERR("Failed to initialize language descriptor (%d)", err); + return NULL; + } + + err = usbd_add_descriptor(&mp_usbd, &mp_mfr); + if (err) { + LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err); + return NULL; + } + + err = usbd_add_descriptor(&mp_usbd, &mp_product); + if (err) { + LOG_ERR("Failed to initialize product descriptor (%d)", err); + return NULL; + } + + err = usbd_add_descriptor(&mp_usbd, &mp_sn); + if (err) { + LOG_ERR("Failed to initialize SN descriptor (%d)", err); + return NULL; + } + + if (usbd_caps_speed(&mp_usbd) == USBD_SPEED_HS) { + err = usbd_add_configuration(&mp_usbd, USBD_SPEED_HS, + &mp_hs_config); + if (err) { + LOG_ERR("Failed to add High-Speed configuration"); + return NULL; + } + + err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_HS, 1, blocklist); + if (err) { + LOG_ERR("Failed to add register classes"); + return NULL; + } + + mp_fix_code_triple(&mp_usbd, USBD_SPEED_HS); + } + + err = usbd_add_configuration(&mp_usbd, USBD_SPEED_FS, + &mp_fs_config); + if (err) { + LOG_ERR("Failed to add Full-Speed configuration"); + return NULL; + } + + err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_FS, 1, blocklist); + if (err) { + LOG_ERR("Failed to add register classes"); + return NULL; + } + + mp_fix_code_triple(&mp_usbd, USBD_SPEED_FS); + + if (msg_cb != NULL) { + err = usbd_msg_register_cb(&mp_usbd, msg_cb); + if (err) { + LOG_ERR("Failed to register message callback"); + return NULL; + } + } + + err = usbd_init(&mp_usbd); + if (err) { + LOG_ERR("Failed to initialize device support"); + return NULL; + } + + return &mp_usbd; +} + +static struct usbd_context *mp_usbd_context; + +int mp_usbd_init(void) { + int err; + + mp_usbd_context = mp_usbd_init_device(NULL); + if (mp_usbd_context == NULL) { + return -ENODEV; + } + + err = usbd_enable(mp_usbd_context); + if (err) { + return err; + } + + return 0; +} + +#endif // defined(CONFIG_USB_DEVICE_STACK_NEXT) diff --git a/ports/zephyr/zephyr_storage.c b/ports/zephyr/zephyr_storage.c index 484feb1130..40bcef7338 100644 --- a/ports/zephyr/zephyr_storage.c +++ b/ports/zephyr/zephyr_storage.c @@ -139,6 +139,20 @@ MP_DEFINE_CONST_OBJ_TYPE( #endif // CONFIG_DISK_ACCESS #ifdef CONFIG_FLASH_MAP + +#define FLASH_AREA_DEFINE_LABEL(part) CONCAT(MP_QSTR_ID_, DT_STRING_TOKEN(part, label)) +#define FLASH_AREA_DEFINE_NB(part) CONCAT(MP_QSTR_ID_, DT_FIXED_PARTITION_ID(part)) + +#define FLASH_AREA_DEFINE_GETNAME(part) COND_CODE_1(DT_NODE_HAS_PROP(part, label), \ + (FLASH_AREA_DEFINE_LABEL(part)), (FLASH_AREA_DEFINE_NB(part))) + +#define FLASH_AREA_DEFINE_DEFINE(part) { MP_ROM_QSTR(FLASH_AREA_DEFINE_GETNAME(part)), MP_ROM_INT(DT_FIXED_PARTITION_ID(part)) }, + +#define FLASH_AREA_DEFINE(part) COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_MTD_FROM_FIXED_PARTITION(part)), \ + (FLASH_AREA_DEFINE_DEFINE(part)), ()) + +#define FOREACH_PARTITION(n) DT_FOREACH_CHILD(n, FLASH_AREA_DEFINE) + const mp_obj_type_t zephyr_flash_area_type; typedef struct _zephyr_flash_area_obj_t { @@ -244,9 +258,8 @@ static const mp_rom_map_elem_t zephyr_flash_area_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&zephyr_flash_area_readblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&zephyr_flash_area_writeblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&zephyr_flash_area_ioctl_obj) }, - #if FIXED_PARTITION_EXISTS(storage_partition) - { MP_ROM_QSTR(MP_QSTR_STORAGE), MP_ROM_INT(FIXED_PARTITION_ID(storage_partition)) }, - #endif + /* Generate list of partition IDs from Zephyr Devicetree */ + DT_FOREACH_STATUS_OKAY(fixed_partitions, FOREACH_PARTITION) }; static MP_DEFINE_CONST_DICT(zephyr_flash_area_locals_dict, zephyr_flash_area_locals_dict_table); |