From cf43df4caa5a9cff4be149517504b1bb0e091436 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 4 Mar 2023 11:36:54 +0100 Subject: [PATCH 001/371] nrf/modules/machine/pwm: Add paramter checks and error messages. --- ports/nrf/modules/machine/pwm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ports/nrf/modules/machine/pwm.c b/ports/nrf/modules/machine/pwm.c index 7c84a8c885f9..9ec36d1e1437 100644 --- a/ports/nrf/modules/machine/pwm.c +++ b/ports/nrf/modules/machine/pwm.c @@ -97,7 +97,6 @@ STATIC int hard_pwm_find(mp_obj_t id) { if (pwm_id >= 0 && pwm_id < MP_ARRAY_SIZE(machine_hard_pwm_obj)) { return pwm_id; } - mp_raise_ValueError(MP_ERROR_TEXT("PWM doesn't exist")); } return -1; } @@ -231,19 +230,22 @@ STATIC mp_obj_t machine_hard_pwm_make_new(mp_arg_val_t *args) { enum { ARG_id, ARG_pin, ARG_freq, ARG_period, ARG_duty, ARG_pulse_width, ARG_mode }; // get static peripheral object int pwm_id = hard_pwm_find(args[ARG_id].u_obj); + if (pwm_id < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid or missing PWM id")); + } const machine_hard_pwm_obj_t *self = &machine_hard_pwm_obj[pwm_id]; // check if PWM pin is set if (args[ARG_pin].u_obj != MP_OBJ_NULL) { self->p_config->pwm_pin = mp_hal_get_pin_obj(args[ARG_pin].u_obj)->pin; } else { - // TODO: raise exception. + mp_raise_ValueError(MP_ERROR_TEXT("Pin missing")); } if (args[ARG_freq].u_obj != MP_OBJ_NULL) { self->p_config->freq = mp_obj_get_int(args[ARG_freq].u_obj); } else { - self->p_config->freq = 50; // 50 Hz by default. + self->p_config->freq = 2; // 4 MHz by default. } if (args[ARG_period].u_obj != MP_OBJ_NULL) { @@ -294,7 +296,7 @@ STATIC void machine_hard_pwm_init(mp_obj_t self_in, mp_arg_val_t *args) { uint16_t pulse_width = ((self->p_config->period * self->p_config->duty) / 100); - // If manual period has been set, override duty-cycle. + // If manual pulse width has been set, override duty-cycle. if (self->p_config->pulse_width > 0) { pulse_width = self->p_config->pulse_width; } From a1f838cdf1239acc43d73ffc450cf98cad6a304a Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 4 Mar 2023 17:51:18 +0100 Subject: [PATCH 002/371] nrf/modules/machine/pwm: Use extmod/machine_pwm.c for PWM module. This is a breaking change, making the hardware PWM on the nrf port compatible with the other ports providing machine.PWM. Frequency range 4Hz - ~5.4 MHz. The base clock range is 125kHz to 16 MHz, and the divider range is 3 - 32767. The hardware supports up to four outputs per PWM device with different duty cycles, but only one output is (and was) supported. --- ports/nrf/Makefile | 1 - .../arduino_nano_33_ble_sense/mpconfigboard.h | 1 + .../nrf/boards/arduino_primo/mpconfigboard.h | 1 + .../boards/blueio_tag_evim/mpconfigboard.h | 1 + ports/nrf/boards/evk_nina_b3/mpconfigboard.h | 1 + ports/nrf/boards/feather52/mpconfigboard.h | 1 + .../nrf/boards/ibk_blyst_nano/mpconfigboard.h | 1 + .../nrf/boards/idk_blyst_nano/mpconfigboard.h | 1 + .../nrf52840-mdk-usb-dongle/mpconfigboard.h | 1 + .../nrf/boards/particle_xenon/mpconfigboard.h | 1 + ports/nrf/boards/pca10040/mpconfigboard.h | 1 + ports/nrf/boards/pca10056/mpconfigboard.h | 1 + ports/nrf/boards/pca10059/mpconfigboard.h | 1 + .../boards/seeed_xiao_nrf52/mpconfigboard.h | 1 + ports/nrf/main.c | 4 + ports/nrf/modules/machine/pwm.c | 425 +++++++++--------- ports/nrf/modules/machine/pwm.h | 1 + ports/nrf/mpconfigport.h | 8 + 18 files changed, 247 insertions(+), 205 deletions(-) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 9ba62675773a..61ae72d71428 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -330,7 +330,6 @@ DRIVERS_SRC_C += $(addprefix modules/,\ machine/pin.c \ machine/timer.c \ machine/rtcounter.c \ - machine/pwm.c \ machine/temp.c \ uos/moduos.c \ uos/microbitfs.c \ diff --git a/ports/nrf/boards/arduino_nano_33_ble_sense/mpconfigboard.h b/ports/nrf/boards/arduino_nano_33_ble_sense/mpconfigboard.h index 0e6c5a44abf4..71349e08c4c8 100644 --- a/ports/nrf/boards/arduino_nano_33_ble_sense/mpconfigboard.h +++ b/ports/nrf/boards/arduino_nano_33_ble_sense/mpconfigboard.h @@ -14,6 +14,7 @@ #define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) NANO33_board_enter_bootloader() #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/arduino_primo/mpconfigboard.h b/ports/nrf/boards/arduino_primo/mpconfigboard.h index 9d58a823cd2e..c9de2ff918b1 100644 --- a/ports/nrf/boards/arduino_primo/mpconfigboard.h +++ b/ports/nrf/boards/arduino_primo/mpconfigboard.h @@ -32,6 +32,7 @@ #define MICROPY_PY_MUSIC (1) #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/blueio_tag_evim/mpconfigboard.h b/ports/nrf/boards/blueio_tag_evim/mpconfigboard.h index 1b58a4d8c1f6..03a1b893fdd4 100644 --- a/ports/nrf/boards/blueio_tag_evim/mpconfigboard.h +++ b/ports/nrf/boards/blueio_tag_evim/mpconfigboard.h @@ -32,6 +32,7 @@ #define MICROPY_PY_MUSIC (1) #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/evk_nina_b3/mpconfigboard.h b/ports/nrf/boards/evk_nina_b3/mpconfigboard.h index e554b65207ab..eab232848bce 100644 --- a/ports/nrf/boards/evk_nina_b3/mpconfigboard.h +++ b/ports/nrf/boards/evk_nina_b3/mpconfigboard.h @@ -46,6 +46,7 @@ // Peripherals Config #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/feather52/mpconfigboard.h b/ports/nrf/boards/feather52/mpconfigboard.h index f653832c620d..cef44ca4032f 100644 --- a/ports/nrf/boards/feather52/mpconfigboard.h +++ b/ports/nrf/boards/feather52/mpconfigboard.h @@ -29,6 +29,7 @@ #define MICROPY_PY_SYS_PLATFORM "nrf52" #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h b/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h index c367c92b7fb3..c263ade7d6f6 100644 --- a/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h +++ b/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h @@ -32,6 +32,7 @@ #define MICROPY_PY_MUSIC (1) #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/idk_blyst_nano/mpconfigboard.h b/ports/nrf/boards/idk_blyst_nano/mpconfigboard.h index f0760ec2c01e..727a30cf109a 100644 --- a/ports/nrf/boards/idk_blyst_nano/mpconfigboard.h +++ b/ports/nrf/boards/idk_blyst_nano/mpconfigboard.h @@ -32,6 +32,7 @@ #define MICROPY_PY_MUSIC (1) #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.h b/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.h index d502c86575c2..feafe8a14314 100644 --- a/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.h +++ b/ports/nrf/boards/nrf52840-mdk-usb-dongle/mpconfigboard.h @@ -29,6 +29,7 @@ #define MICROPY_PY_SYS_PLATFORM "nrf52840-MDK-USB-Dongle" #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/particle_xenon/mpconfigboard.h b/ports/nrf/boards/particle_xenon/mpconfigboard.h index d77e104ce2c8..012a04458eeb 100644 --- a/ports/nrf/boards/particle_xenon/mpconfigboard.h +++ b/ports/nrf/boards/particle_xenon/mpconfigboard.h @@ -29,6 +29,7 @@ #define MICROPY_PY_SYS_PLATFORM "PARTICLE-XENON" #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/pca10040/mpconfigboard.h b/ports/nrf/boards/pca10040/mpconfigboard.h index 7202f6a0b132..00a56c2ea2c5 100644 --- a/ports/nrf/boards/pca10040/mpconfigboard.h +++ b/ports/nrf/boards/pca10040/mpconfigboard.h @@ -29,6 +29,7 @@ #define MICROPY_PY_SYS_PLATFORM "nrf52-DK" #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/pca10056/mpconfigboard.h b/ports/nrf/boards/pca10056/mpconfigboard.h index f7daf48a6222..fa39764a7eb9 100644 --- a/ports/nrf/boards/pca10056/mpconfigboard.h +++ b/ports/nrf/boards/pca10056/mpconfigboard.h @@ -29,6 +29,7 @@ #define MICROPY_PY_SYS_PLATFORM "nrf52840-PDK" #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/pca10059/mpconfigboard.h b/ports/nrf/boards/pca10059/mpconfigboard.h index f4c78915c6a1..904a0871e4da 100644 --- a/ports/nrf/boards/pca10059/mpconfigboard.h +++ b/ports/nrf/boards/pca10059/mpconfigboard.h @@ -29,6 +29,7 @@ #define MICROPY_PY_SYS_PLATFORM "nrf52840-Dongle" #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/seeed_xiao_nrf52/mpconfigboard.h b/ports/nrf/boards/seeed_xiao_nrf52/mpconfigboard.h index 1a2d14f1d4e8..18fbe327d08c 100644 --- a/ports/nrf/boards/seeed_xiao_nrf52/mpconfigboard.h +++ b/ports/nrf/boards/seeed_xiao_nrf52/mpconfigboard.h @@ -34,6 +34,7 @@ #define MICROPY_HW_USB_CDC (1) #define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_HW_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/main.c b/ports/nrf/main.c index 339633d86374..197fea9ab640 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -280,6 +280,10 @@ int main(int argc, char **argv) { } } + #if MICROPY_PY_MACHINE_HW_PWM + pwm_deinit_all(); + #endif + mp_deinit(); printf("MPY: soft reboot\n"); diff --git a/ports/nrf/modules/machine/pwm.c b/ports/nrf/modules/machine/pwm.c index 9ec36d1e1437..525c3aba6b63 100644 --- a/ports/nrf/modules/machine/pwm.c +++ b/ports/nrf/modules/machine/pwm.c @@ -42,278 +42,317 @@ #include "nrfx_pwm.h" #endif +#define PWM_MAX_BASE_FREQ (16000000) +#define PWM_MIN_BASE_FREQ (125000) +#define PWM_MAX_PERIOD (32768) + typedef enum { - MODE_LOW_HIGH, - MODE_HIGH_LOW + MODE_HIGH_LOW, + MODE_LOW_HIGH } pwm_mode_t; +typedef enum { + DUTY_NOT_SET, + DUTY_PERCENT, + DUTY_U16, + DUTY_NS +} pwm_duty_t; + typedef struct { - uint8_t pwm_pin; - uint8_t duty; - uint16_t pulse_width; - uint16_t period; - nrf_pwm_clk_t freq; - pwm_mode_t mode; + uint8_t pwm_pin; + uint8_t duty_mode; + int8_t freq_div; + bool defer_start; + uint32_t duty; + uint32_t freq; + bool mode; } machine_pwm_config_t; -typedef struct _machine_hard_pwm_obj_t { - mp_obj_base_t base; - const nrfx_pwm_t * p_pwm; - machine_pwm_config_t * p_config; -} machine_hard_pwm_obj_t; +typedef struct _machine_pwm_obj_t { + mp_obj_base_t base; + const nrfx_pwm_t *p_pwm; + machine_pwm_config_t *p_config; +} machine_pwm_obj_t; STATIC const nrfx_pwm_t machine_hard_pwm_instances[] = { -#if defined(NRF52_SERIES) + #if defined(NRF52_SERIES) NRFX_PWM_INSTANCE(0), NRFX_PWM_INSTANCE(1), NRFX_PWM_INSTANCE(2), -#if NRF52840 + #if NRF52840 NRFX_PWM_INSTANCE(3), -#endif -#endif + #endif + #endif }; STATIC machine_pwm_config_t hard_configs[MP_ARRAY_SIZE(machine_hard_pwm_instances)]; +STATIC uint8_t pwm_used[MP_ARRAY_SIZE(machine_hard_pwm_instances)]; -STATIC const machine_hard_pwm_obj_t machine_hard_pwm_obj[] = { -#if defined(NRF52_SERIES) +STATIC const machine_pwm_obj_t machine_hard_pwm_obj[] = { + #if defined(NRF52_SERIES) {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[0], .p_config = &hard_configs[0]}, {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[1], .p_config = &hard_configs[1]}, {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[2], .p_config = &hard_configs[2]}, -#if NRF52840 + #if NRF52840 {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[3], .p_config = &hard_configs[3]}, -#endif -#endif + #endif + #endif }; void pwm_init0(void) { } - -STATIC int hard_pwm_find(mp_obj_t id) { - if (mp_obj_is_int(id)) { - // given an integer id - int pwm_id = mp_obj_get_int(id); - if (pwm_id >= 0 && pwm_id < MP_ARRAY_SIZE(machine_hard_pwm_obj)) { - return pwm_id; +// Find a free PWM +STATIC int hard_pwm_find(int pin) { + // check, if a PWM object can be reused. + for (int i = 0; i < MP_ARRAY_SIZE(machine_hard_pwm_obj); i++) { + if (machine_hard_pwm_obj[i].p_config->pwm_pin == pin) { + return i; } } - return -1; + // if not, look for a free object. + for (int i = 0; i < MP_ARRAY_SIZE(machine_hard_pwm_obj); i++) { + if (pwm_used[i] == 0) { + return i; + } + } + mp_raise_ValueError(MP_ERROR_TEXT("no free PWM id")); } -STATIC void machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { - machine_hard_pwm_obj_t *self = self_in; - mp_printf(print, "PWM(%u)", self->p_pwm->drv_inst_idx); +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 = self_in; + static char *duty_suffix[] = { "", "", "_u16", "_ns" }; + mp_printf(print, "", + self->p_config->pwm_pin, self->p_config->freq, + duty_suffix[self->p_config->duty_mode], self->p_config->duty, + self->p_config->mode, self->p_pwm->drv_inst_idx); } /******************************************************************************/ /* MicroPython bindings for machine API */ -STATIC mp_obj_t machine_hard_pwm_make_new(mp_arg_val_t *args); -STATIC void machine_hard_pwm_init(mp_obj_t self, mp_arg_val_t *args); -STATIC void machine_hard_pwm_deinit(mp_obj_t self); -STATIC mp_obj_t machine_hard_pwm_freq(mp_obj_t self, mp_arg_val_t *args); - -/* common code for both soft and hard implementations *************************/ - -STATIC mp_obj_t machine_pwm_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_pin, ARG_freq, ARG_period, ARG_duty, ARG_pulse_width, ARG_mode }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(-1)} }, - { MP_QSTR_pin, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_duty, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_pulse_width, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - }; - - // parse args - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - if (args[ARG_id].u_obj == MP_OBJ_NEW_SMALL_INT(-1)) { - // TODO: implement soft PWM - // return machine_soft_pwm_make_new(args); - return mp_const_none; - } else { - // hardware peripheral id given - return machine_hard_pwm_make_new(args); - } -} +STATIC void machine_hard_pwm_start(const machine_pwm_obj_t *self); +STATIC void mp_machine_pwm_deinit(const machine_pwm_obj_t *self); +STATIC void mp_machine_pwm_freq_set(const machine_pwm_obj_t *self, mp_int_t freq); +STATIC void mp_machine_pwm_duty_set(const machine_pwm_obj_t *self, mp_int_t duty); +STATIC void mp_machine_pwm_duty_set_u16(const machine_pwm_obj_t *self, mp_int_t duty_u16); +STATIC void mp_machine_pwm_duty_set_ns(const machine_pwm_obj_t *self, mp_int_t duty_ns); + +static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_freq, MP_ARG_KW_ONLY | 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_INT, {.u_int = -1} }, + { MP_QSTR_id, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, +}; -STATIC mp_obj_t machine_pwm_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_INIT_pin }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} } - }; +STATIC void mp_machine_pwm_init_helper(const machine_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_pin, ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_id }; - // parse args - mp_obj_t self = 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); + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - // dispatch to specific implementation - if (mp_obj_get_type(self) == &machine_pwm_type) { - machine_hard_pwm_init(self, args); + self->p_config->defer_start = true; + if (args[ARG_freq].u_int != -1) { + mp_machine_pwm_freq_set(self, args[ARG_freq].u_int); + } + if (args[ARG_duty].u_int != -1) { + mp_machine_pwm_duty_set(self, args[ARG_duty].u_int); + } + if (args[ARG_duty_u16].u_int != -1) { + mp_machine_pwm_duty_set_u16(self, args[ARG_duty_u16].u_int); } + if (args[ARG_duty_ns].u_int != -1) { + mp_machine_pwm_duty_set_ns(self, args[ARG_duty_ns].u_int); + } + if (args[ARG_invert].u_int != -1) { + self->p_config->mode = args[ARG_invert].u_int ? MODE_LOW_HIGH : MODE_HIGH_LOW; + } + self->p_config->defer_start = false; - return mp_const_none; + machine_hard_pwm_start(self); } -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pwm_init_obj, 1, machine_pwm_init); -STATIC mp_obj_t machine_pwm_deinit(mp_obj_t self) { - // dispatch to specific implementation - if (mp_obj_get_type(self) == &machine_pwm_type) { - machine_hard_pwm_deinit(self); - } - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pwm_deinit_obj, machine_pwm_deinit); -STATIC mp_obj_t machine_pwm_freq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_FREQ_freq }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} }, - }; +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 *all_args) { + enum { ARG_pin, ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_id }; - mp_obj_t self = pos_args[0]; + // parse args 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); + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // check if the PWM pin is given. + int pwm_pin; + if (args[ARG_pin].u_obj != MP_OBJ_NULL) { + pwm_pin = mp_hal_get_pin_obj(args[ARG_pin].u_obj)->pin; + } else { + mp_raise_ValueError(MP_ERROR_TEXT("Pin missing")); + } - if (mp_obj_get_type(self) == &machine_pwm_type) { - machine_hard_pwm_freq(self, args); + int pwm_id = -1; + if (args[ARG_id].u_int != -1) { + // get static peripheral object + if (args[ARG_id].u_int >= 0 && args[ARG_id].u_int < MP_ARRAY_SIZE(machine_hard_pwm_obj)) { + pwm_id = args[ARG_id].u_int; + } } else { - // soft pwm + pwm_id = hard_pwm_find(pwm_pin); + } + if (pwm_id < 0) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid PWM id")); } + const machine_pwm_obj_t *self = &machine_hard_pwm_obj[pwm_id]; + self->p_config->pwm_pin = pwm_pin; + self->p_config->defer_start = false; + self->p_config->duty_mode = DUTY_NOT_SET; + self->p_config->duty = 0; + self->p_config->freq = 0; + self->p_config->freq_div = -1; + self->p_config->mode = MODE_HIGH_LOW; + + // start the PWM running for this channel + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, all_args + n_args); + mp_machine_pwm_init_helper(self, n_args, all_args, &kw_args); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mp_machine_pwm_freq_obj, 1, machine_pwm_freq); -STATIC mp_obj_t machine_pwm_period(size_t n_args, const mp_obj_t *args) { - return mp_const_none; +void pwm_deinit_all(void) { + for (int i = 0; i < MP_ARRAY_SIZE(machine_hard_pwm_obj); i++) { + mp_machine_pwm_deinit(&machine_hard_pwm_obj[i]); + } } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_pwm_period_obj, 1, 2, machine_pwm_period); -STATIC mp_obj_t machine_pwm_duty(size_t n_args, const mp_obj_t *args) { - return mp_const_none; +STATIC void mp_machine_pwm_deinit(const machine_pwm_obj_t *self) { + pwm_used[self->p_pwm->drv_inst_idx] = 0; + nrfx_pwm_stop(self->p_pwm, true); + nrfx_pwm_uninit(self->p_pwm); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_pwm_duty_obj, 1, 2, machine_pwm_duty); - -STATIC const mp_rom_map_elem_t machine_pwm_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pwm_init_obj) }, - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_pwm_deinit_obj) }, - - { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&mp_machine_pwm_freq_obj) }, - { MP_ROM_QSTR(MP_QSTR_period), MP_ROM_PTR(&mp_machine_pwm_period_obj) }, - { MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&mp_machine_pwm_duty_obj) }, - - { MP_ROM_QSTR(MP_QSTR_FREQ_16MHZ), MP_ROM_INT(NRF_PWM_CLK_16MHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_8MHZ), MP_ROM_INT(NRF_PWM_CLK_8MHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_4MHZ), MP_ROM_INT(NRF_PWM_CLK_4MHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_2MHZ), MP_ROM_INT(NRF_PWM_CLK_2MHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_1MHZ), MP_ROM_INT(NRF_PWM_CLK_1MHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_500KHZ), MP_ROM_INT(NRF_PWM_CLK_500kHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_250KHZ), MP_ROM_INT(NRF_PWM_CLK_250kHz) }, - { MP_ROM_QSTR(MP_QSTR_FREQ_125KHZ), MP_ROM_INT(NRF_PWM_CLK_125kHz) }, - - { MP_ROM_QSTR(MP_QSTR_MODE_LOW_HIGH), MP_ROM_INT(MODE_LOW_HIGH) }, - { MP_ROM_QSTR(MP_QSTR_MODE_HIGH_LOW), MP_ROM_INT(MODE_HIGH_LOW) }, -}; -STATIC MP_DEFINE_CONST_DICT(machine_pwm_locals_dict, machine_pwm_locals_dict_table); +STATIC mp_obj_t mp_machine_pwm_freq_get(const machine_pwm_obj_t *self) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->freq); +} -/* code for hard implementation ***********************************************/ +STATIC void mp_machine_pwm_freq_set(const machine_pwm_obj_t *self, mp_int_t freq) { -STATIC mp_obj_t machine_hard_pwm_make_new(mp_arg_val_t *args) { - enum { ARG_id, ARG_pin, ARG_freq, ARG_period, ARG_duty, ARG_pulse_width, ARG_mode }; - // get static peripheral object - int pwm_id = hard_pwm_find(args[ARG_id].u_obj); - if (pwm_id < 0) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid or missing PWM id")); + uint8_t div = 0; + if (freq > (PWM_MAX_BASE_FREQ / 3) || freq <= (PWM_MIN_BASE_FREQ / PWM_MAX_PERIOD)) { + mp_raise_ValueError(MP_ERROR_TEXT("frequency out of range")); } - const machine_hard_pwm_obj_t *self = &machine_hard_pwm_obj[pwm_id]; - - // check if PWM pin is set - if (args[ARG_pin].u_obj != MP_OBJ_NULL) { - self->p_config->pwm_pin = mp_hal_get_pin_obj(args[ARG_pin].u_obj)->pin; - } else { - mp_raise_ValueError(MP_ERROR_TEXT("Pin missing")); + for (div = 0; div < 8; div++) { + if (PWM_MAX_BASE_FREQ / (1 << div) / freq < PWM_MAX_PERIOD) { + break; + } } + self->p_config->freq_div = div; + self->p_config->freq = freq; + machine_hard_pwm_start(self); +} - if (args[ARG_freq].u_obj != MP_OBJ_NULL) { - self->p_config->freq = mp_obj_get_int(args[ARG_freq].u_obj); +STATIC mp_obj_t mp_machine_pwm_duty_get(const machine_pwm_obj_t *self) { + if (self->p_config->duty_mode == DUTY_PERCENT) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty); + } else if (self->p_config->duty_mode == DUTY_U16) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty * 100 / 65536); } else { - self->p_config->freq = 2; // 4 MHz by default. + return MP_OBJ_NEW_SMALL_INT(-1); } +} - if (args[ARG_period].u_obj != MP_OBJ_NULL) { - self->p_config->period = mp_obj_get_int(args[ARG_period].u_obj); - } else { - mp_raise_ValueError(MP_ERROR_TEXT("PWM period must be within 16000 cycles")); - } +STATIC void mp_machine_pwm_duty_set(const machine_pwm_obj_t *self, mp_int_t duty) { + self->p_config->duty = duty; + self->p_config->duty_mode = DUTY_PERCENT; + machine_hard_pwm_start(self); +} - if (args[ARG_duty].u_obj != MP_OBJ_NULL) { - self->p_config->duty = mp_obj_get_int(args[ARG_duty].u_obj); +STATIC mp_obj_t mp_machine_pwm_duty_get_u16(const machine_pwm_obj_t *self) { + if (self->p_config->duty_mode == DUTY_U16) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty); + } else if (self->p_config->duty_mode == DUTY_PERCENT) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty * 65536 / 100); } else { - self->p_config->duty = 50; // 50% by default. + return MP_OBJ_NEW_SMALL_INT(-1); } +} - if (args[ARG_pulse_width].u_obj != MP_OBJ_NULL) { - self->p_config->pulse_width = mp_obj_get_int(args[ARG_pulse_width].u_obj); - } else { - self->p_config->pulse_width = 0; - } +STATIC void mp_machine_pwm_duty_set_u16(const machine_pwm_obj_t *self, mp_int_t duty) { + self->p_config->duty = duty; + self->p_config->duty_mode = DUTY_U16; + machine_hard_pwm_start(self); +} - if (args[ARG_mode].u_obj != MP_OBJ_NULL) { - self->p_config->mode = mp_obj_get_int(args[ARG_mode].u_obj); +STATIC mp_obj_t mp_machine_pwm_duty_get_ns(const machine_pwm_obj_t *self) { + if (self->p_config->duty_mode == DUTY_NS) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty); } else { - self->p_config->mode = MODE_HIGH_LOW; + return MP_OBJ_NEW_SMALL_INT(-1); } +} - return MP_OBJ_FROM_PTR(self); +STATIC void mp_machine_pwm_duty_set_ns(const machine_pwm_obj_t *self, mp_int_t duty) { + self->p_config->duty = duty; + self->p_config->duty_mode = DUTY_NS; + machine_hard_pwm_start(self); } -STATIC void machine_hard_pwm_init(mp_obj_t self_in, mp_arg_val_t *args) { - machine_hard_pwm_obj_t *self = self_in; +/* code for hard implementation ***********************************************/ + +STATIC void machine_hard_pwm_start(const machine_pwm_obj_t *self) { nrfx_pwm_config_t config; + // check if ready to go + if (self->p_config->defer_start == true || self->p_config->freq_div < 0 || self->p_config->duty_mode == DUTY_NOT_SET) { + return; // Not ready yet. + } + pwm_used[self->p_pwm->drv_inst_idx] = 1; + config.output_pins[0] = self->p_config->pwm_pin; config.output_pins[1] = NRFX_PWM_PIN_NOT_USED; config.output_pins[2] = NRFX_PWM_PIN_NOT_USED; config.output_pins[3] = NRFX_PWM_PIN_NOT_USED; - config.irq_priority = 6; - config.base_clock = self->p_config->freq; - config.count_mode = NRF_PWM_MODE_UP; - config.top_value = self->p_config->period; - config.load_mode = NRF_PWM_LOAD_INDIVIDUAL; - config.step_mode = NRF_PWM_STEP_AUTO; + uint32_t tick_freq = PWM_MAX_BASE_FREQ / (1 << self->p_config->freq_div); + uint32_t period = tick_freq / self->p_config->freq; - nrfx_pwm_init(self->p_pwm, &config, NULL, NULL); + config.irq_priority = 6; + config.base_clock = self->p_config->freq_div; + config.count_mode = NRF_PWM_MODE_UP; + config.top_value = period; + config.load_mode = NRF_PWM_LOAD_INDIVIDUAL; + config.step_mode = NRF_PWM_STEP_AUTO; + + nrfx_pwm_stop(self->p_pwm, true); + nrfx_pwm_uninit(self->p_pwm); - uint16_t pulse_width = ((self->p_config->period * self->p_config->duty) / 100); + nrfx_pwm_init(self->p_pwm, &config, NULL, NULL); - // If manual pulse width has been set, override duty-cycle. - if (self->p_config->pulse_width > 0) { - pulse_width = self->p_config->pulse_width; + uint16_t pulse_width; + if (self->p_config->duty_mode == DUTY_PERCENT) { + pulse_width = ((period * self->p_config->duty) / 100); + } else if (self->p_config->duty_mode == DUTY_U16) { + pulse_width = ((period * self->p_config->duty) / 65536); + } + if (self->p_config->duty_mode == DUTY_NS) { + pulse_width = (uint64_t)self->p_config->duty * tick_freq / 1000000000ULL; } // TODO: Move DMA buffer to global memory. volatile static uint16_t pwm_seq[4]; if (self->p_config->mode == MODE_HIGH_LOW) { - pwm_seq[0] = self->p_config->period - pulse_width; - pwm_seq[1] = self->p_config->period - pulse_width; + pwm_seq[0] = 0x8000 | pulse_width; } else { - pwm_seq[0] = self->p_config->period - pulse_width; - pwm_seq[1] = self->p_config->period - pulse_width; + pwm_seq[0] = pulse_width; } - pwm_seq[2] = self->p_config->period - pulse_width; - pwm_seq[3] = self->p_config->period - pulse_width; + // Outputs 1..3 are not used for now + // pwm_seq[1] = 0x8000 | pulse_width; + // pwm_seq[2] = 0x8000 | pulse_width; + // pwm_seq[3] = 0x8000 | pulse_width; const nrf_pwm_sequence_t pwm_sequence = { .values.p_raw = (const uint16_t *)&pwm_seq, @@ -323,31 +362,9 @@ STATIC void machine_hard_pwm_init(mp_obj_t self_in, mp_arg_val_t *args) { }; nrfx_pwm_simple_playback(self->p_pwm, - &pwm_sequence, - 0, // Loop disabled. - 0); -} - -STATIC void machine_hard_pwm_deinit(mp_obj_t self_in) { - machine_hard_pwm_obj_t *self = self_in; - (void)self; - nrfx_pwm_stop(self->p_pwm, true); - nrfx_pwm_uninit(self->p_pwm); + &pwm_sequence, + 0, // Loop disabled. + 0); } -STATIC mp_obj_t machine_hard_pwm_freq(mp_obj_t self_in, mp_arg_val_t *args) { - machine_hard_pwm_obj_t *self = self_in; - (void)self; - return mp_const_none; -} - -MP_DEFINE_CONST_OBJ_TYPE( - machine_pwm_type, - MP_QSTR_PWM, - MP_TYPE_FLAG_NONE, - make_new, machine_pwm_make_new, - print, machine_pwm_print, - locals_dict, &machine_pwm_locals_dict - ); - #endif // MICROPY_PY_MACHINE_HW_PWM diff --git a/ports/nrf/modules/machine/pwm.h b/ports/nrf/modules/machine/pwm.h index ab2d927fa4e5..4c0528fb4cf3 100644 --- a/ports/nrf/modules/machine/pwm.h +++ b/ports/nrf/modules/machine/pwm.h @@ -25,5 +25,6 @@ */ void pwm_init0(void); +void pwm_deinit_all(void); extern const mp_obj_type_t machine_pwm_type; diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 629ba5e94652..852a744c1bc1 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -196,6 +196,14 @@ #define MICROPY_PY_MACHINE_SOFT_PWM (0) #endif +#define MICROPY_PY_MACHINE_PWM_INIT (1) +#define MICROPY_PY_MACHINE_PWM_DUTY (1) +#define MICROPY_PY_MACHINE_PWM_DUTY_U16_NS (1) + +#if MICROPY_PY_MACHINE_HW_PWM +#define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/nrf/modules/machine/pwm.c" +#endif + #ifndef MICROPY_PY_MACHINE_TIMER_NRF #define MICROPY_PY_MACHINE_TIMER_NRF (1) #endif From e3b877826cb950bb5c6ec2b4369081255c423662 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 5 Mar 2023 21:14:21 +0100 Subject: [PATCH 003/371] nrf/modules/machine/soft_pwm: Add PWM for nrf51x boards using soft PWM. Using extmod/machine_pwm.c for the Python bindings and the existing softpwm.c driver, by just adding the interface. Properties: - Frequency range 1-3906 Hz. - All PWM outputs run at the same frequency but can have different duty cycles. - Limited to the P0.x pins. Since it uses the existing softpwm.c mechanism, it will be affected by playing music with the music class. --- ports/nrf/boards/microbit/mpconfigboard.h | 1 + ports/nrf/boards/pca10000/mpconfigboard.h | 3 + ports/nrf/boards/pca10001/mpconfigboard.h | 3 + ports/nrf/boards/pca10028/mpconfigboard.h | 3 + ports/nrf/boards/pca10031/mpconfigboard.h | 3 + ports/nrf/modules/machine/modmachine.c | 4 +- ports/nrf/modules/machine/soft_pwm.c | 214 ++++++++++++++++++++++ ports/nrf/mpconfigport.h | 2 + 8 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 ports/nrf/modules/machine/soft_pwm.c diff --git a/ports/nrf/boards/microbit/mpconfigboard.h b/ports/nrf/boards/microbit/mpconfigboard.h index 63f45587af8d..4c6e05120db8 100644 --- a/ports/nrf/boards/microbit/mpconfigboard.h +++ b/ports/nrf/boards/microbit/mpconfigboard.h @@ -30,6 +30,7 @@ #define MICROPY_PY_MACHINE_UART (1) #define MICROPY_PY_MUSIC (1) +#define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_SOFT_PWM (1) #define MICROPY_PY_MACHINE_HW_SPI (1) #define MICROPY_PY_MACHINE_RTCOUNTER (1) diff --git a/ports/nrf/boards/pca10000/mpconfigboard.h b/ports/nrf/boards/pca10000/mpconfigboard.h index 61a03d42a837..e56e0edf8373 100644 --- a/ports/nrf/boards/pca10000/mpconfigboard.h +++ b/ports/nrf/boards/pca10000/mpconfigboard.h @@ -35,6 +35,9 @@ #define MICROPY_PY_MACHINE_ADC (0) #define MICROPY_PY_MACHINE_TEMP (1) +#define MICROPY_PY_MACHINE_PWM (1) +#define MICROPY_PY_MACHINE_SOFT_PWM (1) + #define MICROPY_HW_ENABLE_RNG (1) #define MICROPY_HW_HAS_LED (1) diff --git a/ports/nrf/boards/pca10001/mpconfigboard.h b/ports/nrf/boards/pca10001/mpconfigboard.h index 7eb02ecfeb4a..a2e845539847 100644 --- a/ports/nrf/boards/pca10001/mpconfigboard.h +++ b/ports/nrf/boards/pca10001/mpconfigboard.h @@ -35,6 +35,9 @@ #define MICROPY_PY_MACHINE_ADC (1) #define MICROPY_PY_MACHINE_TEMP (1) +#define MICROPY_PY_MACHINE_PWM (1) +#define MICROPY_PY_MACHINE_SOFT_PWM (1) + #define MICROPY_HW_ENABLE_RNG (1) #define MICROPY_HW_HAS_LED (1) diff --git a/ports/nrf/boards/pca10028/mpconfigboard.h b/ports/nrf/boards/pca10028/mpconfigboard.h index 6b1852d82790..7abb5845e3f8 100644 --- a/ports/nrf/boards/pca10028/mpconfigboard.h +++ b/ports/nrf/boards/pca10028/mpconfigboard.h @@ -35,6 +35,9 @@ #define MICROPY_PY_MACHINE_ADC (1) #define MICROPY_PY_MACHINE_TEMP (1) +#define MICROPY_PY_MACHINE_PWM (1) +#define MICROPY_PY_MACHINE_SOFT_PWM (1) + #define MICROPY_HW_ENABLE_RNG (1) #define MICROPY_HW_HAS_LED (1) diff --git a/ports/nrf/boards/pca10031/mpconfigboard.h b/ports/nrf/boards/pca10031/mpconfigboard.h index 42f9b8c6392c..f162366233fd 100644 --- a/ports/nrf/boards/pca10031/mpconfigboard.h +++ b/ports/nrf/boards/pca10031/mpconfigboard.h @@ -35,6 +35,9 @@ #define MICROPY_PY_MACHINE_ADC (1) #define MICROPY_PY_MACHINE_TEMP (1) +#define MICROPY_PY_MACHINE_PWM (1) +#define MICROPY_PY_MACHINE_SOFT_PWM (1) + #define MICROPY_HW_ENABLE_RNG (1) #define MICROPY_HW_HAS_LED (1) diff --git a/ports/nrf/modules/machine/modmachine.c b/ports/nrf/modules/machine/modmachine.c index 895e1ae9f990..c689f4529766 100644 --- a/ports/nrf/modules/machine/modmachine.c +++ b/ports/nrf/modules/machine/modmachine.c @@ -42,7 +42,7 @@ #include "spi.h" #include "i2c.h" #include "timer.h" -#if MICROPY_PY_MACHINE_HW_PWM +#if MICROPY_PY_MACHINE_HW_PWM || MICROPY_PY_MACHINE_SOFT_PWM #include "pwm.h" #endif #if MICROPY_PY_MACHINE_ADC @@ -235,7 +235,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { #if MICROPY_PY_MACHINE_TIMER_NRF { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, #endif -#if MICROPY_PY_MACHINE_HW_PWM +#if MICROPY_PY_MACHINE_HW_PWM || MICROPY_PY_MACHINE_SOFT_PWM { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) }, #endif #if MICROPY_PY_MACHINE_TEMP diff --git a/ports/nrf/modules/machine/soft_pwm.c b/ports/nrf/modules/machine/soft_pwm.c new file mode 100644 index 000000000000..27783328eb8c --- /dev/null +++ b/ports/nrf/modules/machine/soft_pwm.c @@ -0,0 +1,214 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Robert Hammelrath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "py/runtime.h" +#include "py/mphal.h" + +#if MICROPY_PY_MACHINE_SOFT_PWM + +#include "softpwm.h" + +typedef enum { + DUTY_NOT_SET = 0, + DUTY, + DUTY_U16, + DUTY_NS +} pwm_duty_t; + +typedef struct _machine_pwm_obj_t { + mp_obj_base_t base; + uint8_t pwm_pin; + bool defer_start; + uint8_t duty_mode; + uint32_t duty; + uint32_t freq; +} machine_pwm_obj_t; + +#define SOFT_PWM_BASE_FREQ (1000000) +#define DUTY_FULL_SCALE (1024) + +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 = self_in; + static char *duty_suffix[] = { "", "", "_u16", "_ns" }; + mp_printf(print, "", + self->pwm_pin, self->freq, + duty_suffix[self->duty_mode], self->duty); +} + +// MicroPython bindings for machine API + +STATIC void machine_soft_pwm_start(machine_pwm_obj_t *self); +STATIC void mp_machine_pwm_deinit(machine_pwm_obj_t *self); +STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq); +STATIC void mp_machine_pwm_duty_set(machine_pwm_obj_t *self, mp_int_t duty); +STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty_u16); +STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty_ns); + +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[] = { + { MP_QSTR_freq, MP_ARG_KW_ONLY | 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_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); + + self->defer_start = true; + if (args[ARG_freq].u_int != -1) { + mp_machine_pwm_freq_set(self, args[ARG_freq].u_int); + } + if (args[ARG_duty].u_int != -1) { + mp_machine_pwm_duty_set(self, args[ARG_duty].u_int); + } + if (args[ARG_duty_u16].u_int != -1) { + mp_machine_pwm_duty_set_u16(self, args[ARG_duty_u16].u_int); + } + if (args[ARG_duty_ns].u_int != -1) { + mp_machine_pwm_duty_set_ns(self, args[ARG_duty_ns].u_int); + } + self->defer_start = false; + // (Re-)start the PWM. + machine_soft_pwm_start(self); +} + +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) { + enum { ARG_pin, ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_id }; + + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // check if the PWM pin is valid. + int pwm_pin = mp_hal_get_pin_obj(args[0])->pin; + if (pwm_pin > 31) { + mp_raise_ValueError(MP_ERROR_TEXT("Pin number >31")); + } + + machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type);; + self->defer_start = false; + self->pwm_pin = pwm_pin; + self->duty_mode = DUTY_NOT_SET; + self->duty = 0; + self->freq = 0; + + // parse the remaining arguments and start the PWM + 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); +} + +STATIC void mp_machine_pwm_deinit(machine_pwm_obj_t *self) { + pwm_release(self->pwm_pin); +} + +STATIC mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { + return MP_OBJ_NEW_SMALL_INT(self->freq); +} + +STATIC void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { + + if (freq > (SOFT_PWM_BASE_FREQ / 256)) { + mp_raise_ValueError(MP_ERROR_TEXT("frequency out of range")); + } + self->freq = freq; + machine_soft_pwm_start(self); +} + +STATIC mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) { + if (self->duty_mode) { + return MP_OBJ_NEW_SMALL_INT(self->duty); + } else if (self->duty_mode == DUTY_U16) { + return MP_OBJ_NEW_SMALL_INT(self->duty * 100 / 65536); + } else { + return MP_OBJ_NEW_SMALL_INT(-1); + } +} + +STATIC void mp_machine_pwm_duty_set(machine_pwm_obj_t *self, mp_int_t duty) { + self->duty = duty; + self->duty_mode = DUTY; + machine_soft_pwm_start(self); +} + +STATIC mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) { + if (self->duty_mode == DUTY_U16) { + return MP_OBJ_NEW_SMALL_INT(self->duty); + } else if (self->duty_mode == DUTY) { + return MP_OBJ_NEW_SMALL_INT(self->duty * 65536 / 100); + } else { + return MP_OBJ_NEW_SMALL_INT(-1); + } +} + +STATIC void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty) { + self->duty = duty; + self->duty_mode = DUTY_U16; + machine_soft_pwm_start(self); +} + +STATIC mp_obj_t mp_machine_pwm_duty_get_ns(machine_pwm_obj_t *self) { + if (self->duty_mode == DUTY_NS) { + return MP_OBJ_NEW_SMALL_INT(self->duty); + } else { + return MP_OBJ_NEW_SMALL_INT(-1); + } +} + +STATIC void mp_machine_pwm_duty_set_ns(machine_pwm_obj_t *self, mp_int_t duty) { + self->duty = duty; + self->duty_mode = DUTY_NS; + machine_soft_pwm_start(self); +} + +/* Interface for the implementation */ + +STATIC void machine_soft_pwm_start(machine_pwm_obj_t *self) { + + // check if ready to go + if (self->defer_start == true || self->freq == 0 || self->duty_mode == DUTY_NOT_SET) { + return; // Not ready yet. + } + + int ret = pwm_set_period_us(SOFT_PWM_BASE_FREQ / self->freq); + + if (ret >= 0) { + int duty_width; + if (self->duty_mode == DUTY) { + duty_width = self->duty * DUTY_FULL_SCALE / 100; + } else if (self->duty_mode == DUTY_U16) { + duty_width = self->duty * DUTY_FULL_SCALE / 65536; + }if (self->duty_mode == DUTY_NS) { + duty_width = (uint64_t)self->duty * self->freq * DUTY_FULL_SCALE / 1000000000ULL; + } + pwm_set_duty_cycle(self->pwm_pin, duty_width); + } +} + +#endif // MICROPY_PY_MACHINE_HW_PWM diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 852a744c1bc1..4762693c8129 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -202,6 +202,8 @@ #if MICROPY_PY_MACHINE_HW_PWM #define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/nrf/modules/machine/pwm.c" +#elif MICROPY_PY_MACHINE_SOFT_PWM +#define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/nrf/modules/machine/soft_pwm.c" #endif #ifndef MICROPY_PY_MACHINE_TIMER_NRF From ed1f42cb493cf70c326e9a986d2c03ba5da52641 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 8 Mar 2023 11:55:24 +0100 Subject: [PATCH 004/371] nrf/modules/machine/pwm: Support using all 4 channels of a PWM module. These have the same frequency, but can have different duty cycle and polarity. pwm.deinit() stops all channels of a module, but does not release the module. pwm.init() without arguments restarts all outputs. --- ports/nrf/modules/machine/pwm.c | 203 ++++++++++++++++++-------------- 1 file changed, 116 insertions(+), 87 deletions(-) diff --git a/ports/nrf/modules/machine/pwm.c b/ports/nrf/modules/machine/pwm.c index 525c3aba6b63..8e435bcf3a7f 100644 --- a/ports/nrf/modules/machine/pwm.c +++ b/ports/nrf/modules/machine/pwm.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2016-2018 Glenn Ruben Bakke + * Copyright (c) 2023 Robert Hammelrath * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -47,31 +48,40 @@ #define PWM_MAX_PERIOD (32768) typedef enum { - MODE_HIGH_LOW, + MODE_HIGH_LOW = 0, MODE_LOW_HIGH } pwm_mode_t; typedef enum { - DUTY_NOT_SET, + DUTY_NOT_SET = 0, DUTY_PERCENT, DUTY_U16, DUTY_NS } pwm_duty_t; +typedef enum { + FREE = 0, + STOPPED, + RUNNING +} pwm_run_t; + typedef struct { - uint8_t pwm_pin; - uint8_t duty_mode; - int8_t freq_div; + uint8_t pwm_pin[NRF_PWM_CHANNEL_COUNT]; + pwm_mode_t mode[NRF_PWM_CHANNEL_COUNT]; + pwm_duty_t duty_mode[NRF_PWM_CHANNEL_COUNT]; + uint32_t duty[NRF_PWM_CHANNEL_COUNT]; + pwm_run_t active; bool defer_start; - uint32_t duty; + int8_t freq_div; uint32_t freq; - bool mode; } machine_pwm_config_t; typedef struct _machine_pwm_obj_t { mp_obj_base_t base; const nrfx_pwm_t *p_pwm; machine_pwm_config_t *p_config; + uint8_t id; + uint8_t channel; } machine_pwm_obj_t; STATIC const nrfx_pwm_t machine_hard_pwm_instances[] = { @@ -86,46 +96,57 @@ STATIC const nrfx_pwm_t machine_hard_pwm_instances[] = { }; STATIC machine_pwm_config_t hard_configs[MP_ARRAY_SIZE(machine_hard_pwm_instances)]; -STATIC uint8_t pwm_used[MP_ARRAY_SIZE(machine_hard_pwm_instances)]; STATIC const machine_pwm_obj_t machine_hard_pwm_obj[] = { #if defined(NRF52_SERIES) - {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[0], .p_config = &hard_configs[0]}, - {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[1], .p_config = &hard_configs[1]}, - {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[2], .p_config = &hard_configs[2]}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[0], .p_config = &hard_configs[0], 0, 0}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[0], .p_config = &hard_configs[0], 0, 1}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[0], .p_config = &hard_configs[0], 0, 2}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[0], .p_config = &hard_configs[0], 0, 3}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[1], .p_config = &hard_configs[1], 1, 0}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[1], .p_config = &hard_configs[1], 1, 1}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[1], .p_config = &hard_configs[1], 1, 2}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[1], .p_config = &hard_configs[1], 1, 3}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[2], .p_config = &hard_configs[2], 2, 0}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[2], .p_config = &hard_configs[2], 2, 1}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[2], .p_config = &hard_configs[2], 2, 2}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[2], .p_config = &hard_configs[2], 2, 3}, #if NRF52840 - {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[3], .p_config = &hard_configs[3]}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[3], .p_config = &hard_configs[3], 3, 0}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[3], .p_config = &hard_configs[3], 3, 1}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[3], .p_config = &hard_configs[3], 3, 2}, + {{&machine_pwm_type}, .p_pwm = &machine_hard_pwm_instances[3], .p_config = &hard_configs[3], 3, 3}, #endif #endif }; void pwm_init0(void) { + for (int i = 0; i < MP_ARRAY_SIZE(hard_configs); i++) { + hard_configs[i].active = FREE; + hard_configs[i].freq_div = -1; + hard_configs[i].freq = 0; + memset(hard_configs[i].duty_mode, DUTY_NOT_SET, NRF_PWM_CHANNEL_COUNT); + } } -// Find a free PWM -STATIC int hard_pwm_find(int pin) { - // check, if a PWM object can be reused. - for (int i = 0; i < MP_ARRAY_SIZE(machine_hard_pwm_obj); i++) { - if (machine_hard_pwm_obj[i].p_config->pwm_pin == pin) { - return i; +// Find a free PWM object +STATIC int hard_pwm_find() { + // look for a free module. + for (int j = 0; j < MP_ARRAY_SIZE(hard_configs); j++) { + if (hard_configs[j].active == FREE) { + return j * NRF_PWM_CHANNEL_COUNT; } } - // if not, look for a free object. - for (int i = 0; i < MP_ARRAY_SIZE(machine_hard_pwm_obj); i++) { - if (pwm_used[i] == 0) { - return i; - } - } - mp_raise_ValueError(MP_ERROR_TEXT("no free PWM id")); + mp_raise_ValueError(MP_ERROR_TEXT("no free PWM object")); } 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 = self_in; static char *duty_suffix[] = { "", "", "_u16", "_ns" }; - mp_printf(print, "", - self->p_config->pwm_pin, self->p_config->freq, - duty_suffix[self->p_config->duty_mode], self->p_config->duty, - self->p_config->mode, self->p_pwm->drv_inst_idx); + mp_printf(print, "", + self->p_config->pwm_pin[self->channel], self->p_config->freq, + duty_suffix[self->p_config->duty_mode[self->channel]], self->p_config->duty[self->channel], + self->p_config->mode[self->channel], self->id, self->channel); } /******************************************************************************/ @@ -146,10 +167,11 @@ static const mp_arg_t allowed_args[] = { { MP_QSTR_duty_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_id, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, }; STATIC void mp_machine_pwm_init_helper(const machine_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_pin, ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_id }; + enum { ARG_pin, ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_id, ARG_channel }; 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); @@ -168,7 +190,7 @@ STATIC void mp_machine_pwm_init_helper(const machine_pwm_obj_t *self, size_t n_a mp_machine_pwm_duty_set_ns(self, args[ARG_duty_ns].u_int); } if (args[ARG_invert].u_int != -1) { - self->p_config->mode = args[ARG_invert].u_int ? MODE_LOW_HIGH : MODE_HIGH_LOW; + self->p_config->mode[self->channel] = args[ARG_invert].u_int ? MODE_LOW_HIGH : MODE_HIGH_LOW; } self->p_config->defer_start = false; @@ -177,7 +199,7 @@ STATIC void mp_machine_pwm_init_helper(const machine_pwm_obj_t *self, size_t n_a 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 *all_args) { - enum { ARG_pin, ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_id }; + enum { ARG_pin, ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_id, ARG_channel }; // parse args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -191,26 +213,33 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args mp_raise_ValueError(MP_ERROR_TEXT("Pin missing")); } + // Get the PWM object number + // If just the ID is given, use channel 0 + // If none is given, attempt to find an unused object. int pwm_id = -1; if (args[ARG_id].u_int != -1) { - // get static peripheral object - if (args[ARG_id].u_int >= 0 && args[ARG_id].u_int < MP_ARRAY_SIZE(machine_hard_pwm_obj)) { - pwm_id = args[ARG_id].u_int; + if (args[ARG_id].u_int >= 0 && args[ARG_id].u_int < MP_ARRAY_SIZE(machine_hard_pwm_instances)) { + pwm_id = args[ARG_id].u_int * NRF_PWM_CHANNEL_COUNT; + if (args[ARG_channel].u_int != -1) { + if (args[ARG_channel].u_int >= 0 && args[ARG_channel].u_int < NRF_PWM_CHANNEL_COUNT) { + pwm_id += args[ARG_channel].u_int; + } + } } } else { - pwm_id = hard_pwm_find(pwm_pin); + // no ID given, search for a free ID. + pwm_id = hard_pwm_find(); } if (pwm_id < 0) { mp_raise_ValueError(MP_ERROR_TEXT("invalid PWM id")); } const machine_pwm_obj_t *self = &machine_hard_pwm_obj[pwm_id]; - self->p_config->pwm_pin = pwm_pin; + int pwm_channel = pwm_id % NRF_PWM_CHANNEL_COUNT; + self->p_config->pwm_pin[pwm_channel] = pwm_pin; + self->p_config->duty_mode[pwm_channel] = DUTY_NOT_SET; + self->p_config->duty[pwm_channel] = 0; + self->p_config->mode[pwm_channel] = MODE_HIGH_LOW; self->p_config->defer_start = false; - self->p_config->duty_mode = DUTY_NOT_SET; - self->p_config->duty = 0; - self->p_config->freq = 0; - self->p_config->freq_div = -1; - self->p_config->mode = MODE_HIGH_LOW; // start the PWM running for this channel mp_map_t kw_args; @@ -220,14 +249,17 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args return MP_OBJ_FROM_PTR(self); } +// Stop all PWM modules and release them void pwm_deinit_all(void) { - for (int i = 0; i < MP_ARRAY_SIZE(machine_hard_pwm_obj); i++) { - mp_machine_pwm_deinit(&machine_hard_pwm_obj[i]); + for (int i = 0; i < MP_ARRAY_SIZE(machine_hard_pwm_instances); i++) { + mp_machine_pwm_deinit(&machine_hard_pwm_obj[i * NRF_PWM_CHANNEL_COUNT]); } + pwm_init0(); } +// Stop the PWM module, but do not release it. STATIC void mp_machine_pwm_deinit(const machine_pwm_obj_t *self) { - pwm_used[self->p_pwm->drv_inst_idx] = 0; + self->p_config->active = STOPPED; nrfx_pwm_stop(self->p_pwm, true); nrfx_pwm_uninit(self->p_pwm); } @@ -253,48 +285,48 @@ STATIC void mp_machine_pwm_freq_set(const machine_pwm_obj_t *self, mp_int_t freq } STATIC mp_obj_t mp_machine_pwm_duty_get(const machine_pwm_obj_t *self) { - if (self->p_config->duty_mode == DUTY_PERCENT) { - return MP_OBJ_NEW_SMALL_INT(self->p_config->duty); - } else if (self->p_config->duty_mode == DUTY_U16) { - return MP_OBJ_NEW_SMALL_INT(self->p_config->duty * 100 / 65536); + if (self->p_config->duty_mode[self->channel] == DUTY_PERCENT) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel]); + } else if (self->p_config->duty_mode[self->channel] == DUTY_U16) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 100 / 65536); } else { return MP_OBJ_NEW_SMALL_INT(-1); } } STATIC void mp_machine_pwm_duty_set(const machine_pwm_obj_t *self, mp_int_t duty) { - self->p_config->duty = duty; - self->p_config->duty_mode = DUTY_PERCENT; + self->p_config->duty[self->channel] = duty; + self->p_config->duty_mode[self->channel] = DUTY_PERCENT; machine_hard_pwm_start(self); } STATIC mp_obj_t mp_machine_pwm_duty_get_u16(const machine_pwm_obj_t *self) { - if (self->p_config->duty_mode == DUTY_U16) { - return MP_OBJ_NEW_SMALL_INT(self->p_config->duty); - } else if (self->p_config->duty_mode == DUTY_PERCENT) { - return MP_OBJ_NEW_SMALL_INT(self->p_config->duty * 65536 / 100); + if (self->p_config->duty_mode[self->channel] == DUTY_U16) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel]); + } else if (self->p_config->duty_mode[self->channel] == DUTY_PERCENT) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 65536 / 100); } else { return MP_OBJ_NEW_SMALL_INT(-1); } } STATIC void mp_machine_pwm_duty_set_u16(const machine_pwm_obj_t *self, mp_int_t duty) { - self->p_config->duty = duty; - self->p_config->duty_mode = DUTY_U16; + self->p_config->duty[self->channel] = duty; + self->p_config->duty_mode[self->channel] = DUTY_U16; machine_hard_pwm_start(self); } STATIC mp_obj_t mp_machine_pwm_duty_get_ns(const machine_pwm_obj_t *self) { - if (self->p_config->duty_mode == DUTY_NS) { - return MP_OBJ_NEW_SMALL_INT(self->p_config->duty); + if (self->p_config->duty_mode[self->channel] == DUTY_NS) { + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel]); } else { return MP_OBJ_NEW_SMALL_INT(-1); } } STATIC void mp_machine_pwm_duty_set_ns(const machine_pwm_obj_t *self, mp_int_t duty) { - self->p_config->duty = duty; - self->p_config->duty_mode = DUTY_NS; + self->p_config->duty[self->channel] = duty; + self->p_config->duty_mode[self->channel] = DUTY_NS; machine_hard_pwm_start(self); } @@ -305,15 +337,16 @@ STATIC void machine_hard_pwm_start(const machine_pwm_obj_t *self) { nrfx_pwm_config_t config; // check if ready to go - if (self->p_config->defer_start == true || self->p_config->freq_div < 0 || self->p_config->duty_mode == DUTY_NOT_SET) { + if (self->p_config->defer_start == true || self->p_config->freq_div < 0 || self->p_config->duty_mode[self->channel] == DUTY_NOT_SET) { return; // Not ready yet. } - pwm_used[self->p_pwm->drv_inst_idx] = 1; - config.output_pins[0] = self->p_config->pwm_pin; - config.output_pins[1] = NRFX_PWM_PIN_NOT_USED; - config.output_pins[2] = NRFX_PWM_PIN_NOT_USED; - config.output_pins[3] = NRFX_PWM_PIN_NOT_USED; + self->p_config->active = RUNNING; + + config.output_pins[0] = self->p_config->duty_mode[0] != DUTY_NOT_SET ? self->p_config->pwm_pin[0] : NRFX_PWM_PIN_NOT_USED; + config.output_pins[1] = self->p_config->duty_mode[1] != DUTY_NOT_SET ? self->p_config->pwm_pin[1] : NRFX_PWM_PIN_NOT_USED; + config.output_pins[2] = self->p_config->duty_mode[2] != DUTY_NOT_SET ? self->p_config->pwm_pin[2] : NRFX_PWM_PIN_NOT_USED; + config.output_pins[3] = self->p_config->duty_mode[3] != DUTY_NOT_SET ? self->p_config->pwm_pin[3] : NRFX_PWM_PIN_NOT_USED; uint32_t tick_freq = PWM_MAX_BASE_FREQ / (1 << self->p_config->freq_div); uint32_t period = tick_freq / self->p_config->freq; @@ -330,29 +363,25 @@ STATIC void machine_hard_pwm_start(const machine_pwm_obj_t *self) { nrfx_pwm_init(self->p_pwm, &config, NULL, NULL); - uint16_t pulse_width; - if (self->p_config->duty_mode == DUTY_PERCENT) { - pulse_width = ((period * self->p_config->duty) / 100); - } else if (self->p_config->duty_mode == DUTY_U16) { - pulse_width = ((period * self->p_config->duty) / 65536); - } - if (self->p_config->duty_mode == DUTY_NS) { - pulse_width = (uint64_t)self->p_config->duty * tick_freq / 1000000000ULL; - } - - // TODO: Move DMA buffer to global memory. volatile static uint16_t pwm_seq[4]; - if (self->p_config->mode == MODE_HIGH_LOW) { - pwm_seq[0] = 0x8000 | pulse_width; - } else { - pwm_seq[0] = pulse_width; - } + for (int i = 0; i < NRF_PWM_CHANNEL_COUNT; i++) { + uint16_t pulse_width = 0; + if (self->p_config->duty_mode[i] == DUTY_PERCENT) { + pulse_width = ((period * self->p_config->duty[i]) / 100); + } else if (self->p_config->duty_mode[i] == DUTY_U16) { + pulse_width = ((period * self->p_config->duty[i]) / 65536); + } + if (self->p_config->duty_mode[i] == DUTY_NS) { + pulse_width = (uint64_t)self->p_config->duty[i] * tick_freq / 1000000000ULL; + } - // Outputs 1..3 are not used for now - // pwm_seq[1] = 0x8000 | pulse_width; - // pwm_seq[2] = 0x8000 | pulse_width; - // pwm_seq[3] = 0x8000 | pulse_width; + if (self->p_config->mode[i] == MODE_HIGH_LOW) { + pwm_seq[i] = 0x8000 | pulse_width; + } else { + pwm_seq[i] = pulse_width; + } + } const nrf_pwm_sequence_t pwm_sequence = { .values.p_raw = (const uint16_t *)&pwm_seq, From f450e94ba03a22cafc38d50f69e8af1c1599c401 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 10 Mar 2023 10:54:00 +1100 Subject: [PATCH 005/371] ports: Rename remaining "Micro Python" to "MicroPython". Signed-off-by: Damien George --- ports/esp32/machine_pwm.c | 2 +- ports/nrf/boards/arduino_primo/mpconfigboard.h | 2 +- ports/nrf/boards/blueio_tag_evim/mpconfigboard.h | 2 +- ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h | 2 +- ports/nrf/boards/idk_blyst_nano/mpconfigboard.h | 2 +- ports/nrf/boards/microbit/modules/iters.c | 2 +- ports/nrf/boards/microbit/modules/microbitconstimage.c | 2 +- ports/nrf/boards/microbit/modules/microbitconstimage.h | 2 +- ports/nrf/boards/microbit/modules/microbitdisplay.c | 2 +- ports/nrf/boards/microbit/modules/microbitimage.c | 2 +- ports/nrf/boards/microbit/modules/modmicrobit.c | 2 +- ports/nrf/modules/uos/microbitfs.c | 2 +- ports/nrf/modules/uos/microbitfs.h | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 99bef88e9a08..79e11d02784e 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/ports/nrf/boards/arduino_primo/mpconfigboard.h b/ports/nrf/boards/arduino_primo/mpconfigboard.h index c9de2ff918b1..271cef0323df 100644 --- a/ports/nrf/boards/arduino_primo/mpconfigboard.h +++ b/ports/nrf/boards/arduino_primo/mpconfigboard.h @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/ports/nrf/boards/blueio_tag_evim/mpconfigboard.h b/ports/nrf/boards/blueio_tag_evim/mpconfigboard.h index 03a1b893fdd4..0ca3ca16127a 100644 --- a/ports/nrf/boards/blueio_tag_evim/mpconfigboard.h +++ b/ports/nrf/boards/blueio_tag_evim/mpconfigboard.h @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h b/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h index c263ade7d6f6..63d2944f6c25 100644 --- a/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h +++ b/ports/nrf/boards/ibk_blyst_nano/mpconfigboard.h @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/ports/nrf/boards/idk_blyst_nano/mpconfigboard.h b/ports/nrf/boards/idk_blyst_nano/mpconfigboard.h index 727a30cf109a..73b1c6ff7459 100644 --- a/ports/nrf/boards/idk_blyst_nano/mpconfigboard.h +++ b/ports/nrf/boards/idk_blyst_nano/mpconfigboard.h @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/ports/nrf/boards/microbit/modules/iters.c b/ports/nrf/boards/microbit/modules/iters.c index e6762421eb57..b16a4c665837 100644 --- a/ports/nrf/boards/microbit/modules/iters.c +++ b/ports/nrf/boards/microbit/modules/iters.c @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/ports/nrf/boards/microbit/modules/microbitconstimage.c b/ports/nrf/boards/microbit/modules/microbitconstimage.c index fa079845974d..d6f6ab0a9d80 100644 --- a/ports/nrf/boards/microbit/modules/microbitconstimage.c +++ b/ports/nrf/boards/microbit/modules/microbitconstimage.c @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/ports/nrf/boards/microbit/modules/microbitconstimage.h b/ports/nrf/boards/microbit/modules/microbitconstimage.h index ca67b5c467ff..45d58c4a094f 100644 --- a/ports/nrf/boards/microbit/modules/microbitconstimage.h +++ b/ports/nrf/boards/microbit/modules/microbitconstimage.h @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/ports/nrf/boards/microbit/modules/microbitdisplay.c b/ports/nrf/boards/microbit/modules/microbitdisplay.c index e4e4d13fe449..ff3c499fa325 100644 --- a/ports/nrf/boards/microbit/modules/microbitdisplay.c +++ b/ports/nrf/boards/microbit/modules/microbitdisplay.c @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/ports/nrf/boards/microbit/modules/microbitimage.c b/ports/nrf/boards/microbit/modules/microbitimage.c index 17d737dba5e5..fc6b18870bdc 100644 --- a/ports/nrf/boards/microbit/modules/microbitimage.c +++ b/ports/nrf/boards/microbit/modules/microbitimage.c @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/ports/nrf/boards/microbit/modules/modmicrobit.c b/ports/nrf/boards/microbit/modules/modmicrobit.c index c6aa445b1c9a..06b3e5f6ab91 100644 --- a/ports/nrf/boards/microbit/modules/modmicrobit.c +++ b/ports/nrf/boards/microbit/modules/modmicrobit.c @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/ports/nrf/modules/uos/microbitfs.c b/ports/nrf/modules/uos/microbitfs.c index 6d697e1d1a0d..fb88d47cc910 100644 --- a/ports/nrf/modules/uos/microbitfs.c +++ b/ports/nrf/modules/uos/microbitfs.c @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/ports/nrf/modules/uos/microbitfs.h b/ports/nrf/modules/uos/microbitfs.h index d9efaabf10ae..645a1e328961 100644 --- a/ports/nrf/modules/uos/microbitfs.h +++ b/ports/nrf/modules/uos/microbitfs.h @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * From b3c8ab37ec86d19277e9d1dd10b5741ab93022ed Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 9 Mar 2023 13:50:21 +1100 Subject: [PATCH 006/371] py/gc: Make gc_dump_info/gc_dump_alloc_table take a printer as argument. So that callers can redirect the output if needed. Signed-off-by: Damien George --- ports/minimal/main.c | 2 +- ports/nrf/modules/machine/modmachine.c | 2 +- ports/powerpc/main.c | 2 +- ports/renesas-ra/modmachine.c | 2 +- ports/stm32/modmachine.c | 2 +- ports/teensy/modpyb.c | 2 +- ports/unix/gccollect.c | 5 ----- ports/zephyr/main.c | 1 - py/gc.c | 30 +++++++++++++------------- py/gc.h | 6 +++--- py/modmicropython.c | 4 ++-- shared/runtime/pyexec.c | 2 +- 12 files changed, 27 insertions(+), 33 deletions(-) diff --git a/ports/minimal/main.c b/ports/minimal/main.c index 881a6d2364ef..4eb6ca65af0d 100644 --- a/ports/minimal/main.c +++ b/ports/minimal/main.c @@ -69,7 +69,7 @@ void gc_collect(void) { gc_collect_start(); gc_collect_root(&dummy, ((mp_uint_t)stack_top - (mp_uint_t)&dummy) / sizeof(mp_uint_t)); gc_collect_end(); - gc_dump_info(); + gc_dump_info(&mp_plat_print); } #endif diff --git a/ports/nrf/modules/machine/modmachine.c b/ports/nrf/modules/machine/modmachine.c index c689f4529766..369d957ff1e4 100644 --- a/ports/nrf/modules/machine/modmachine.c +++ b/ports/nrf/modules/machine/modmachine.c @@ -133,7 +133,7 @@ STATIC mp_obj_t machine_info(mp_uint_t n_args, const mp_obj_t *args) { if (n_args == 1) { // arg given means dump gc allocation table - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); } return mp_const_none; diff --git a/ports/powerpc/main.c b/ports/powerpc/main.c index ed82c517ebc1..11013f175530 100644 --- a/ports/powerpc/main.c +++ b/ports/powerpc/main.c @@ -108,7 +108,7 @@ void gc_collect(void) { gc_collect_start(); gc_collect_root(&dummy, ((mp_uint_t)stack_top - (mp_uint_t)&dummy) / sizeof(mp_uint_t)); gc_collect_end(); - gc_dump_info(); + gc_dump_info(&mp_plat_print); } mp_lexer_t *mp_lexer_new_from_file(const char *filename) { diff --git a/ports/renesas-ra/modmachine.c b/ports/renesas-ra/modmachine.c index 7db36298db4a..ecb028983c6c 100644 --- a/ports/renesas-ra/modmachine.c +++ b/ports/renesas-ra/modmachine.c @@ -154,7 +154,7 @@ STATIC mp_obj_t machine_info(size_t n_args, const mp_obj_t *args) { if (n_args == 1) { // arg given means dump gc allocation table - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); } return mp_const_none; diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 9f85e358117c..dee9689e3bab 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -244,7 +244,7 @@ STATIC mp_obj_t machine_info(size_t n_args, const mp_obj_t *args) { if (n_args == 1) { // arg given means dump gc allocation table - gc_dump_alloc_table(); + gc_dump_alloc_table(print); } return mp_const_none; diff --git a/ports/teensy/modpyb.c b/ports/teensy/modpyb.c index 3939607ad06c..b48fecc18f39 100644 --- a/ports/teensy/modpyb.c +++ b/ports/teensy/modpyb.c @@ -111,7 +111,7 @@ STATIC mp_obj_t pyb_info(uint n_args, const mp_obj_t *args) { if (n_args == 1) { // arg given means dump gc allocation table - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); } return mp_const_none; diff --git a/ports/unix/gccollect.c b/ports/unix/gccollect.c index 79b17663c32e..94c9c61ea4d6 100644 --- a/ports/unix/gccollect.c +++ b/ports/unix/gccollect.c @@ -34,8 +34,6 @@ #if MICROPY_ENABLE_GC void gc_collect(void) { - // gc_dump_info(); - gc_collect_start(); gc_helper_collect_regs_and_stack(); #if MICROPY_PY_THREAD @@ -45,9 +43,6 @@ void gc_collect(void) { mp_unix_mark_exec(); #endif gc_collect_end(); - - // printf("-----\n"); - // gc_dump_info(); } #endif // MICROPY_ENABLE_GC diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 869449e7df47..a2dfb16602b1 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -186,7 +186,6 @@ void gc_collect(void) { gc_collect_start(); gc_collect_root(&dummy, ((mp_uint_t)MP_STATE_THREAD(stack_top) - (mp_uint_t)&dummy) / sizeof(mp_uint_t)); gc_collect_end(); - // gc_dump_info(); } #if !MICROPY_READER_VFS diff --git a/py/gc.c b/py/gc.c index f11c34aefe51..ba5c569d50f8 100644 --- a/py/gc.c +++ b/py/gc.c @@ -726,7 +726,7 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) { #endif #if EXTENSIVE_HEAP_PROFILING - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); #endif return ret_ptr; @@ -806,7 +806,7 @@ void gc_free(void *ptr) { GC_EXIT(); #if EXTENSIVE_HEAP_PROFILING - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); #endif } @@ -960,7 +960,7 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { GC_EXIT(); #if EXTENSIVE_HEAP_PROFILING - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); #endif return ptr_in; @@ -985,7 +985,7 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { #endif #if EXTENSIVE_HEAP_PROFILING - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); #endif return ptr_in; @@ -1019,23 +1019,23 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { } #endif // Alternative gc_realloc impl -void gc_dump_info(void) { +void gc_dump_info(const mp_print_t *print) { gc_info_t info; gc_info(&info); - mp_printf(&mp_plat_print, "GC: total: %u, used: %u, free: %u\n", + mp_printf(print, "GC: total: %u, used: %u, free: %u\n", (uint)info.total, (uint)info.used, (uint)info.free); - mp_printf(&mp_plat_print, " No. of 1-blocks: %u, 2-blocks: %u, max blk sz: %u, max free sz: %u\n", + mp_printf(print, " No. of 1-blocks: %u, 2-blocks: %u, max blk sz: %u, max free sz: %u\n", (uint)info.num_1block, (uint)info.num_2block, (uint)info.max_block, (uint)info.max_free); } -void gc_dump_alloc_table(void) { +void gc_dump_alloc_table(const mp_print_t *print) { GC_ENTER(); static const size_t DUMP_BYTES_PER_LINE = 64; for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { #if !EXTENSIVE_HEAP_PROFILING // When comparing heap output we don't want to print the starting // pointer of the heap because it changes from run to run. - mp_printf(&mp_plat_print, "GC memory layout; from %p:", area->gc_pool_start); + mp_printf(print, "GC memory layout; from %p:", area->gc_pool_start); #endif for (size_t bl = 0; bl < area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; bl++) { if (bl % DUMP_BYTES_PER_LINE == 0) { @@ -1048,7 +1048,7 @@ void gc_dump_alloc_table(void) { } if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE) { // there are at least 2 lines containing only free blocks, so abbreviate their printing - mp_printf(&mp_plat_print, "\n (%u lines all free)", (uint)(bl2 - bl) / DUMP_BYTES_PER_LINE); + mp_printf(print, "\n (%u lines all free)", (uint)(bl2 - bl) / DUMP_BYTES_PER_LINE); bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1)); if (bl >= area->gc_alloc_table_byte_len * BLOCKS_PER_ATB) { // got to end of heap @@ -1058,7 +1058,7 @@ void gc_dump_alloc_table(void) { } // print header for new line of blocks // (the cast to uint32_t is for 16-bit ports) - mp_printf(&mp_plat_print, "\n%08x: ", (uint)(bl * BYTES_PER_BLOCK)); + mp_printf(print, "\n%08x: ", (uint)(bl * BYTES_PER_BLOCK)); } int c = ' '; switch (ATB_GET_KIND(area, bl)) { @@ -1151,9 +1151,9 @@ void gc_dump_alloc_table(void) { c = 'm'; break; } - mp_printf(&mp_plat_print, "%c", c); + mp_printf(print, "%c", c); } - mp_print_str(&mp_plat_print, "\n"); + mp_print_str(print, "\n"); } GC_EXIT(); } @@ -1185,13 +1185,13 @@ void gc_test(void) { } printf("Before GC:\n"); - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); printf("Starting GC...\n"); gc_collect_start(); gc_collect_root(ptrs, sizeof(ptrs) / sizeof(void *)); gc_collect_end(); printf("After GC:\n"); - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); } #endif diff --git a/py/gc.h b/py/gc.h index bb4204b06f43..8431c0a6cf1a 100644 --- a/py/gc.h +++ b/py/gc.h @@ -28,7 +28,7 @@ #include #include -#include "py/mpconfig.h" +#include "py/mpprint.h" void gc_init(void *start, void *end); @@ -72,7 +72,7 @@ typedef struct _gc_info_t { } gc_info_t; void gc_info(gc_info_t *info); -void gc_dump_info(void); -void gc_dump_alloc_table(void); +void gc_dump_info(const mp_print_t *print); +void gc_dump_alloc_table(const mp_print_t *print); #endif // MICROPY_INCLUDED_PY_GC_H diff --git a/py/modmicropython.c b/py/modmicropython.c index c717926e6631..bdb1e8b9b4c0 100644 --- a/py/modmicropython.c +++ b/py/modmicropython.c @@ -81,10 +81,10 @@ mp_obj_t mp_micropython_mem_info(size_t n_args, const mp_obj_t *args) { mp_printf(&mp_plat_print, "stack: " UINT_FMT "\n", mp_stack_usage()); #endif #if MICROPY_ENABLE_GC - gc_dump_info(); + gc_dump_info(&mp_plat_print); if (n_args == 1) { // arg given means dump gc allocation table - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); } #else (void)n_args; diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index b6e7d8fe68e9..40491650e421 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -167,7 +167,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input #if MICROPY_ENABLE_GC // run collection and print GC info gc_collect(); - gc_dump_info(); + gc_dump_info(&mp_plat_print); #endif } #endif From 78dc2db2ba7e14a44f00fc07dd37f3323fcaf251 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 9 Mar 2023 13:56:23 +1100 Subject: [PATCH 007/371] py/mpconfig: Provide config option for internal printf printer. The C-level printf is usually used for internal debugging prints, and a port/board may want to redirect this somewhere other than stdout. Signed-off-by: Damien George --- py/mpconfig.h | 5 +++++ shared/libc/printf.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index 564580c02df6..db9c623f49fe 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -904,6 +904,11 @@ typedef double mp_float_t; #define MICROPY_USE_INTERNAL_PRINTF (1) #endif +// The mp_print_t printer used for printf output when MICROPY_USE_INTERNAL_PRINTF is enabled +#ifndef MICROPY_INTERNAL_PRINTF_PRINTER +#define MICROPY_INTERNAL_PRINTF_PRINTER (&mp_plat_print) +#endif + // Support for internal scheduler #ifndef MICROPY_ENABLE_SCHEDULER #define MICROPY_ENABLE_SCHEDULER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) diff --git a/shared/libc/printf.c b/shared/libc/printf.c index e8db2b999c12..6b5373188fd8 100644 --- a/shared/libc/printf.c +++ b/shared/libc/printf.c @@ -60,13 +60,13 @@ int snprintf(char *str, size_t size, const char *fmt, ...); int printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - int ret = mp_vprintf(&mp_plat_print, fmt, ap); + int ret = mp_vprintf(MICROPY_INTERNAL_PRINTF_PRINTER, fmt, ap); va_end(ap); return ret; } int vprintf(const char *fmt, va_list ap) { - return mp_vprintf(&mp_plat_print, fmt, ap); + return mp_vprintf(MICROPY_INTERNAL_PRINTF_PRINTER, fmt, ap); } // need this because gcc optimises printf("%c", c) -> putchar(c), and printf("a") -> putchar('a') From f3a596db7dffa97c507bc80b78d8160c5eae276d Mon Sep 17 00:00:00 2001 From: Laurens Valk Date: Thu, 9 Mar 2023 15:52:51 +0100 Subject: [PATCH 008/371] py/builtinimport: Fix unix port build with external imports disabled. Without this, building the unix port variants gives: ports/unix/main.c:667: undefined reference to `mp_obj_is_package', when MICROPY_ENABLE_EXTERNAL_IMPORT is 0. Signed-off-by: Laurens Valk --- py/builtinimport.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/py/builtinimport.c b/py/builtinimport.c index 6f1f7b485f04..e620c03774c0 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -574,6 +574,10 @@ mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) { #else // MICROPY_ENABLE_EXTERNAL_IMPORT +bool mp_obj_is_package(mp_obj_t module) { + return false; +} + mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) { // Check that it's not a relative import if (n_args >= 5 && MP_OBJ_SMALL_INT_VALUE(args[4]) != 0) { From 4376c969f6f185ffe34041d1914246f74beded9d Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 8 Mar 2023 16:17:54 +0100 Subject: [PATCH 009/371] all: Fix Python comparison to None and True, and use "not in". These are basic PEP8 recommendations. --- ports/cc3200/tools/update-wipy.py | 2 +- ports/nrf/examples/powerup.py | 4 ++-- ports/rp2/modules/rp2.py | 2 +- ports/stm32/boards/pllvalues.py | 2 +- tools/mpremote/mpremote/commands.py | 4 ++-- tools/uf2conv.py | 10 +++++----- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ports/cc3200/tools/update-wipy.py b/ports/cc3200/tools/update-wipy.py index e0e1266f7a37..8008b3860535 100644 --- a/ports/cc3200/tools/update-wipy.py +++ b/ports/cc3200/tools/update-wipy.py @@ -45,7 +45,7 @@ def transfer_file(args): if "250" in ftp.cwd("/flash"): if not ftp_directory_exists(ftp, "sys"): print("/flash/sys directory does not exist") - if not "550" in ftp.mkd("sys"): + if "550" not in ftp.mkd("sys"): print("/flash/sys directory created") else: print("Error: cannot create /flash/sys directory") diff --git a/ports/nrf/examples/powerup.py b/ports/nrf/examples/powerup.py index bbb1e20b67ec..156193d002f8 100644 --- a/ports/nrf/examples/powerup.py +++ b/ports/nrf/examples/powerup.py @@ -120,13 +120,13 @@ def battery_level(self): return int(self.char_batt_lvl.read()[0]) def speed(self, new_speed=None): - if new_speed == None: + if new_speed is None: return int(self.char_control_speed.read()[0]) else: self.char_control_speed.write(bytearray([new_speed])) def angle(self, new_angle=None): - if new_angle == None: + if new_angle is None: return int(self.char_control_angle.read()[0]) else: self.char_control_angle.write(bytearray([new_angle])) diff --git a/ports/rp2/modules/rp2.py b/ports/rp2/modules/rp2.py index 1e4bb26cf0f3..f145cbd60771 100644 --- a/ports/rp2/modules/rp2.py +++ b/ports/rp2/modules/rp2.py @@ -116,7 +116,7 @@ def word(self, instr, label=None): if label is None: label = 0 else: - if not label in self.labels: + if label not in self.labels: raise PIOASMError("unknown label {}".format(label)) label = self.labels[label] self.prog[_PROG_DATA].append(instr | label) diff --git a/ports/stm32/boards/pllvalues.py b/ports/stm32/boards/pllvalues.py index e0fff40dd3fe..76320cc9c383 100644 --- a/ports/stm32/boards/pllvalues.py +++ b/ports/stm32/boards/pllvalues.py @@ -94,7 +94,7 @@ def compute_pll2(hse, sys, relax_pll48): fallback = None for P in mcu.range_p: # VCO_OUT must be between 192MHz and 432MHz - if not sys * P in mcu.range_vco_out: + if sys * P not in mcu.range_vco_out: continue NbyM = float(sys * P) / hse # float for Python 2 # scan M diff --git a/tools/mpremote/mpremote/commands.py b/tools/mpremote/mpremote/commands.py index d05a27d9f278..fe0283ce6fb4 100644 --- a/tools/mpremote/mpremote/commands.py +++ b/tools/mpremote/mpremote/commands.py @@ -122,9 +122,9 @@ def _list_recursive(files, path): if command == "cat": # Don't be verbose by default when using cat, so output can be # redirected to something. - verbose = args.verbose == True + verbose = args.verbose is True else: - verbose = args.verbose != False + verbose = args.verbose is not False if command == "cp" and args.recursive: if paths[-1] != ":": diff --git a/tools/uf2conv.py b/tools/uf2conv.py index d3b1d9a2652a..ef3e48144903 100644 --- a/tools/uf2conv.py +++ b/tools/uf2conv.py @@ -85,9 +85,9 @@ def convert_from_uf2(buf): if datalen > 476: assert False, "Invalid UF2 data size at " + ptr newaddr = hd[3] - if (hd[2] & 0x2000) and (currfamilyid == None): + if (hd[2] & 0x2000) and (currfamilyid is None): currfamilyid = hd[7] - if curraddr == None or ((hd[2] & 0x2000) and hd[7] != currfamilyid): + if curraddr is None or ((hd[2] & 0x2000) and hd[7] != currfamilyid): currfamilyid = hd[7] curraddr = newaddr if familyid == 0x0 or familyid == hd[7]: @@ -111,7 +111,7 @@ def convert_from_uf2(buf): families_found[hd[7]] = newaddr else: families_found[hd[7]] = newaddr - if prev_flag == None: + if prev_flag is None: prev_flag = hd[2] if prev_flag != hd[2]: all_flags_same = False @@ -234,7 +234,7 @@ def convert_from_hex_to_uf2(buf): break elif tp == 0: addr = upper + ((rec[1] << 8) | rec[2]) - if appstartaddr == None: + if appstartaddr is None: appstartaddr = addr i = 4 while i < len(rec) - 1: @@ -419,7 +419,7 @@ def error(msg): ) if args.convert or ext != "uf2": drives = [] - if args.output == None: + if args.output is None: args.output = "flash." + ext else: drives = get_drives() From 668a7bd28a49980b239fd7666684885382526988 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 10 Mar 2023 18:39:14 +1100 Subject: [PATCH 010/371] py/makeversionhdr.py: Always add micro to version string even if it's 0. Moving forward, tags in this repository will always have three components. Signed-off-by: Damien George --- py/makeversionhdr.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index 58c4421c59bf..e682a4e139cc 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -78,9 +78,7 @@ def get_version_info_from_mpconfig(repo_path): ver_minor = int(line.strip().split()[2]) elif line.startswith("#define MICROPY_VERSION_MICRO "): ver_micro = int(line.strip().split()[2]) - git_tag = "v%d.%d" % (ver_major, ver_minor) - if ver_micro != 0: - git_tag += ".%d" % (ver_micro,) + git_tag = "v%d.%d.%d" % (ver_major, ver_minor, ver_micro) return git_tag, "" return None From 416707eefee7a0dbb4b1604a0633f34725aa0290 Mon Sep 17 00:00:00 2001 From: IhorNehrutsa Date: Mon, 31 Oct 2022 09:57:54 +0200 Subject: [PATCH 011/371] esp32/machine_pin: Use const for size of machine_pin_irq_handler array. --- ports/esp32/machine_pin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index 8a892d231630..f103b96963ce 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -732,4 +732,4 @@ STATIC MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &machine_pin_irq_locals_dict ); -MP_REGISTER_ROOT_POINTER(mp_obj_t machine_pin_irq_handler[49]); // 49 is the biggest of GPIO_NUM_MAX's +MP_REGISTER_ROOT_POINTER(mp_obj_t machine_pin_irq_handler[GPIO_PIN_COUNT]); From d5c45a80d250021527c737d194b6debe10ab9dbb Mon Sep 17 00:00:00 2001 From: pmendham Date: Sun, 12 Mar 2023 18:29:57 +0000 Subject: [PATCH 012/371] embed: Fix arguments to mp_raw_code_load_mem. Update arguments to mp_raw_code_load_mem so that the embed port can build when MICROPY_PERSISTENT_CODE_LOAD is enabled. --- ports/embed/port/embed_util.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ports/embed/port/embed_util.c b/ports/embed/port/embed_util.c index a3850f3c0fed..14f50897705e 100644 --- a/ports/embed/port/embed_util.c +++ b/ports/embed/port/embed_util.c @@ -66,7 +66,9 @@ void mp_embed_exec_mpy(const uint8_t *mpy, size_t len) { // Execute the given .mpy data. mp_module_context_t *ctx = m_new_obj(mp_module_context_t); ctx->module.globals = mp_globals_get(); - mp_compiled_module_t cm = mp_raw_code_load_mem(mpy, len, ctx); + mp_compiled_module_t cm; + cm.context = ctx; + mp_raw_code_load_mem(mpy, len, &cm); mp_obj_t f = mp_make_function_from_raw_code(cm.rc, ctx, MP_OBJ_NULL); mp_call_function_0(f); nlr_pop(); From 05bb26010e4a466a82cfed179f8d8d0b406a78ca Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 10 Mar 2023 12:59:43 +0100 Subject: [PATCH 013/371] samd: Always provide the machine.RTC class. Even if boards do not have a clock crystal. In that case, the clock quality will be very poor. Always having machine.RTC means that the date/time can be set in a way that is consistent with other ports. This commit also removes the special code in modutime.c for devices without the RTC class. Signed-off-by: Damien George --- ports/samd/mcu/samd21/mpconfigmcu.h | 5 ----- ports/samd/mcu/samd51/mpconfigmcu.h | 6 ------ ports/samd/modmachine.h | 3 +++ ports/samd/modutime.c | 25 +------------------------ ports/samd/mpconfigport.h | 1 + 5 files changed, 5 insertions(+), 35 deletions(-) diff --git a/ports/samd/mcu/samd21/mpconfigmcu.h b/ports/samd/mcu/samd21/mpconfigmcu.h index db4e58753c5f..0c5bfe5ae7d8 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.h +++ b/ports/samd/mcu/samd21/mpconfigmcu.h @@ -31,11 +31,6 @@ unsigned long trng_random_u32(int delay); #define MICROPY_HW_UART_TXBUF (1) #endif -#ifndef MICROPY_PY_MACHINE_RTC -#if MICROPY_HW_XOSC32K -#define MICROPY_PY_MACHINE_RTC (1) -#endif -#endif #define MICROPY_PY_UOS_URANDOM (1) #ifndef MICROPY_PY_MACHINE_PIN_BOARD_CPU diff --git a/ports/samd/mcu/samd51/mpconfigmcu.h b/ports/samd/mcu/samd51/mpconfigmcu.h index 64261951bafc..5ac431b29707 100644 --- a/ports/samd/mcu/samd51/mpconfigmcu.h +++ b/ports/samd/mcu/samd51/mpconfigmcu.h @@ -28,12 +28,6 @@ #define MICROPY_PY_URANDOM_SEED_INIT_FUNC (trng_random_u32()) unsigned long trng_random_u32(void); -#ifndef MICROPY_PY_MACHINE_RTC -#if MICROPY_HW_XOSC32K -#define MICROPY_PY_MACHINE_RTC (1) -#endif -#endif - #ifndef MICROPY_PY_MACHINE_PIN_BOARD_CPU #define MICROPY_PY_MACHINE_PIN_BOARD_CPU (1) #endif diff --git a/ports/samd/modmachine.h b/ports/samd/modmachine.h index e8738df4b5ed..8b3059d9fc71 100644 --- a/ports/samd/modmachine.h +++ b/ports/samd/modmachine.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_SAMD_MODMACHINE_H #include "py/obj.h" +#include "shared/timeutils/timeutils.h" extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t machine_dac_type; @@ -43,4 +44,6 @@ extern const mp_obj_type_t machine_rtc_type; NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args); +void rtc_gettime(timeutils_struct_time_t *tm); + #endif // MICROPY_INCLUDED_SAMD_MODMACHINE_H diff --git a/ports/samd/modutime.c b/ports/samd/modutime.c index 6b04134497fb..6fce203154ba 100644 --- a/ports/samd/modutime.c +++ b/ports/samd/modutime.c @@ -27,19 +27,13 @@ #include "py/runtime.h" #include "extmod/utime_mphal.h" #include "shared/timeutils/timeutils.h" -#include "mphalport.h" - -#if !MICROPY_PY_MACHINE_RTC -uint32_t time_offset = 0; -#endif // !MICROPY_PY_MACHINE_RTC +#include "modmachine.h" // localtime([secs]) STATIC mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { timeutils_struct_time_t tm; mp_int_t seconds; - #if MICROPY_PY_MACHINE_RTC - extern void rtc_gettime(timeutils_struct_time_t *tm); if (n_args == 0 || args[0] == mp_const_none) { rtc_gettime(&tm); } else { @@ -47,16 +41,6 @@ STATIC mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); } - #else - if (n_args == 0 || args[0] == mp_const_none) { - seconds = mp_hal_ticks_ms_64() / 1000 + time_offset; - } else { - seconds = mp_obj_get_int(args[0]); - time_offset = seconds - mp_hal_ticks_ms_64() / 1000; - } - timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); - - #endif // MICROPY_PY_MACHINE_RTC mp_obj_t tuple[8] = { tuple[0] = mp_obj_new_int(tm.tm_year), tuple[1] = mp_obj_new_int(tm.tm_mon), @@ -90,17 +74,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(time_mktime_obj, time_mktime); // time() STATIC mp_obj_t time_time(void) { - #if MICROPY_PY_MACHINE_RTC - extern void rtc_gettime(timeutils_struct_time_t *tm); timeutils_struct_time_t tm; rtc_gettime(&tm); 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)); - - #else - return mp_obj_new_int_from_uint(mp_hal_ticks_ms_64() / 1000 + time_offset); - - #endif // MICROPY_PY_MACHINE_RTC } STATIC MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index 613c24c97057..a048ad54be06 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -92,6 +92,7 @@ #define MICROPY_PY_UZLIB (1) #define MICROPY_PY_UASYNCIO (1) #define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_RTC (1) #define MICROPY_PY_MACHINE_SOFTI2C (1) #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SOFTSPI (1) From fa8ebb1390549314485fc26e586573cff526807a Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 17 Mar 2023 13:16:06 -0500 Subject: [PATCH 014/371] py/obj: Add MP_NOINLINE to mp_obj_malloc_helper. As the comment in py/obj.h says: > Implementing this as a call rather than inline saves 8 bytes per usage. So in order to get this savings, we need to tell the compiler to never inline the function. Signed-off-by: David Lechner --- py/obj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/obj.c b/py/obj.c index eb17308fed88..3b0fd171ccb9 100644 --- a/py/obj.c +++ b/py/obj.c @@ -38,7 +38,7 @@ #include "py/stream.h" // for mp_obj_print // Allocates an object and also sets type, for mp_obj_malloc{,_var} macros. -void *mp_obj_malloc_helper(size_t num_bytes, const mp_obj_type_t *type) { +MP_NOINLINE void *mp_obj_malloc_helper(size_t num_bytes, const mp_obj_type_t *type) { mp_obj_base_t *base = (mp_obj_base_t *)m_malloc(num_bytes); base->type = type; return base; From 44ec57f13ac9fb998fd2dcf92b08a634a0c2bb3d Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 17 Mar 2023 13:45:57 -0500 Subject: [PATCH 015/371] examples/usercmodule/cexample: Use mp_obj_malloc(). Example code should use mp_obj_malloc() as well since people will likely copy this code. Signed-off-by: David Lechner --- examples/usercmodule/cexample/examplemodule.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/usercmodule/cexample/examplemodule.c b/examples/usercmodule/cexample/examplemodule.c index b2457b28011a..ccce03bcbdcf 100644 --- a/examples/usercmodule/cexample/examplemodule.c +++ b/examples/usercmodule/cexample/examplemodule.c @@ -43,8 +43,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(example_Timer_time_obj, example_Timer_time); // the user instantiates a Timer object. STATIC mp_obj_t example_Timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { // Allocates the new object and sets the type. - example_Timer_obj_t *self = m_new_obj(example_Timer_obj_t); - self->base.type = (mp_obj_type_t *)type; + example_Timer_obj_t *self = mp_obj_malloc(example_Timer_obj_t, type); // Initializes the time for this Timer instance. self->start_time = mp_hal_ticks_ms(); From a197823eb77c57d061763c91be35f9c8f69e24ee Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Mon, 13 Mar 2023 14:16:23 +0100 Subject: [PATCH 016/371] ports: Fix MCU tags in Arduino board.json files. --- ports/nrf/boards/arduino_nano_33_ble_sense/board.json | 2 +- ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json | 2 +- ports/stm32/boards/ARDUINO_GIGA/board.json | 2 +- ports/stm32/boards/ARDUINO_NICLA_VISION/board.json | 2 +- ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) 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 9488c75f8419..65a8e2435059 100644 --- a/ports/nrf/boards/arduino_nano_33_ble_sense/board.json +++ b/ports/nrf/boards/arduino_nano_33_ble_sense/board.json @@ -17,7 +17,7 @@ "images": [ "ABX00031_01.iso_998x749.jpg" ], - "mcu": "nRF52840", + "mcu": "nrf52", "product": "Arduino Nano 33 BLE Sense", "thumbnail": "", "url": "https://store.arduino.cc/products/arduino-nano-33-ble-sense", diff --git a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json index ca7ba089b894..5bd03e1e0b7e 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 @@ "images": [ "ABX00052_01.iso_999x750.jpg" ], - "mcu": "RP2040", + "mcu": "rp2040", "product": "Arduino Nano RP2040 Connect", "thumbnail": "", "url": "https://store-usa.arduino.cc/products/arduino-nano-rp2040-connect", diff --git a/ports/stm32/boards/ARDUINO_GIGA/board.json b/ports/stm32/boards/ARDUINO_GIGA/board.json index 3a6ddc9ed1ff..f0595d4ad533 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/board.json +++ b/ports/stm32/boards/ARDUINO_GIGA/board.json @@ -13,7 +13,7 @@ "images": [ "ABX00063_01.front_1000x750.jpg" ], - "mcu": "STM32H747", + "mcu": "stm32h7", "product": "Arduino Giga", "thumbnail": "", "url": "https://store.arduino.cc/products/giga-r1-wifi", diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json b/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json index c7f707d7bf56..801bc320b555 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json @@ -13,7 +13,7 @@ "images": [ "ABX00051_01.iso_1000x750.jpg" ], - "mcu": "STM32H747", + "mcu": "stm32h7", "product": "Arduino Nicla Vision", "thumbnail": "", "url": "https://store.arduino.cc/products/nicla-vision", diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json b/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json index 65cf4d4f8649..6467e43bb973 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json @@ -16,7 +16,7 @@ "images": [ "ABX00042_01.iso_1000x750.jpg" ], - "mcu": "STM32H747", + "mcu": "stm32h7", "product": "Arduino Portenta H7", "thumbnail": "", "url": "https://store.arduino.cc/products/portenta-h7", From 42511b5291f32fdcb074eb5984abc52952e5a9ab Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 20 Mar 2023 15:24:45 +1100 Subject: [PATCH 017/371] stm32/boards/NUCLEO_H743ZI2: Fix MCU tag in board.json file. Signed-off-by: Damien George --- ports/stm32/boards/NUCLEO_H743ZI2/board.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/boards/NUCLEO_H743ZI2/board.json b/ports/stm32/boards/NUCLEO_H743ZI2/board.json index 30361e6f1594..65f01de56559 100644 --- a/ports/stm32/boards/NUCLEO_H743ZI2/board.json +++ b/ports/stm32/boards/NUCLEO_H743ZI2/board.json @@ -5,7 +5,7 @@ "docs": "", "features": [], "images": [], - "mcu": "stm32", + "mcu": "stm32h7", "product": "Nucleo H743ZI2", "thumbnail": "", "url": "", From 7ea192af05c2c0fb0d2992a13f6f58e763d81673 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 12 Sep 2022 13:59:38 +0200 Subject: [PATCH 018/371] nrf/modules/machine/uart: Add timeout keyword options and "any" method. Changes in this commit: - Add the timeout and timeout_char keyword options. - Make uart.read() non-blocking. - Add uart.any(). - Add ioctl MP_STREAM_POLL handling. - Change uart.write() into non-busy waiting. uart.write() still waits until all data has been sent, but calls MICROPY_EVENT_POLL_HOOK while waiting. uart.write() uses DMA for transfer. One option would be to add a small local buffer, such that transfers up to the size of the buffer could be done without waiting. - As a side effect to the change of uart.write(), uart.txdone() and ioctl flush now report/wait correctly for the end of transmission. - Change machine_hard_uart_buf_t in machine_hard_uart_obj_t to an instance of that struct, rather than a pointer to one. --- ports/nrf/modules/machine/uart.c | 107 ++++++++++++++++++++----------- ports/nrf/modules/machine/uart.h | 8 +-- 2 files changed, 74 insertions(+), 41 deletions(-) diff --git a/ports/nrf/modules/machine/uart.c b/ports/nrf/modules/machine/uart.c index eee5b8b79eac..778b1c155d03 100644 --- a/ports/nrf/modules/machine/uart.c +++ b/ports/nrf/modules/machine/uart.c @@ -98,15 +98,15 @@ typedef struct _machine_hard_uart_buf_t { typedef struct _machine_hard_uart_obj_t { mp_obj_base_t base; const nrfx_uart_t * p_uart; // Driver instance - machine_hard_uart_buf_t *buf; + machine_hard_uart_buf_t buf; + uint16_t timeout; // timeout waiting for first char (in ms) + uint16_t timeout_char; // timeout waiting between chars (in ms) } machine_hard_uart_obj_t; static const nrfx_uart_t instance0 = NRFX_UART_INSTANCE(0); -STATIC machine_hard_uart_buf_t machine_hard_uart_buf[1]; - -STATIC const machine_hard_uart_obj_t machine_hard_uart_obj[] = { - {{&machine_uart_type}, .p_uart = &instance0, .buf = &machine_hard_uart_buf[0]}, +STATIC machine_hard_uart_obj_t machine_hard_uart_obj[] = { + {{&machine_uart_type}, .p_uart = &instance0} }; void uart_init0(void) { @@ -124,45 +124,45 @@ STATIC int uart_find(mp_obj_t id) { STATIC void uart_event_handler(nrfx_uart_event_t const *p_event, void *p_context) { machine_hard_uart_obj_t *self = p_context; if (p_event->type == NRFX_UART_EVT_RX_DONE) { - int chr = self->buf->rx_buf[0]; - nrfx_uart_rx(self->p_uart, &self->buf->rx_buf[0], 1); + 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; + self->buf.rx_ringbuf.iget = 0; + self->buf.rx_ringbuf.iput = 0; mp_sched_keyboard_interrupt(); } else #endif { - ringbuf_put((ringbuf_t*)&self->buf->rx_ringbuf, chr); + ringbuf_put((ringbuf_t *)&self->buf.rx_ringbuf, chr); } } } -bool uart_rx_any(const machine_hard_uart_obj_t *self) { - return self->buf->rx_ringbuf.iput != self->buf->rx_ringbuf.iget; +bool uart_rx_any(machine_hard_uart_obj_t *self) { + return self->buf.rx_ringbuf.iput != self->buf.rx_ringbuf.iget; } -int uart_rx_char(const machine_hard_uart_obj_t * self) { - return ringbuf_get((ringbuf_t*)&self->buf->rx_ringbuf); +int uart_rx_char(machine_hard_uart_obj_t *self) { + return ringbuf_get((ringbuf_t *)&self->buf.rx_ringbuf); } -STATIC nrfx_err_t uart_tx_char(const machine_hard_uart_obj_t * self, int c) { +STATIC nrfx_err_t uart_tx_char(machine_hard_uart_obj_t *self, int c) { while (nrfx_uart_tx_in_progress(self->p_uart)) { ; } - self->buf->tx_buf[0] = c; - return nrfx_uart_tx(self->p_uart, &self->buf->tx_buf[0], 1); + self->buf.tx_buf[0] = c; + return nrfx_uart_tx(self->p_uart, &self->buf.tx_buf[0], 1); } -void uart_tx_strn(const machine_hard_uart_obj_t *uart_obj, const char *str, uint len) { +void uart_tx_strn(machine_hard_uart_obj_t *uart_obj, const char *str, uint len) { for (const char *top = str + len; str < top; str++) { uart_tx_char(uart_obj, *str); } } -void uart_tx_strn_cooked(const machine_hard_uart_obj_t *uart_obj, const char *str, uint len) { +void uart_tx_strn_cooked(machine_hard_uart_obj_t *uart_obj, const char *str, uint len) { for (const char *top = str + len; str < top; str++) { if (*str == '\n') { uart_tx_char(uart_obj, '\r'); @@ -184,10 +184,12 @@ STATIC void machine_hard_uart_print(const mp_print_t *print, mp_obj_t self_in, m /// - `id`is bus id. /// - `baudrate` is the clock rate. STATIC mp_obj_t machine_hard_uart_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_baudrate }; + enum { ARG_id, ARG_baudrate, ARG_timeout, ARG_timeout_char }; static const mp_arg_t allowed_args[] = { { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_baudrate, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 9600} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, }; // parse args @@ -196,7 +198,7 @@ STATIC mp_obj_t machine_hard_uart_make_new(const mp_obj_type_t *type, size_t n_a // get static peripheral object int uart_id = uart_find(args[ARG_id].u_obj); - const machine_hard_uart_obj_t * self = &machine_hard_uart_obj[uart_id]; + machine_hard_uart_obj_t *self = &machine_hard_uart_obj[uart_id]; nrfx_uart_config_t config; @@ -238,19 +240,21 @@ STATIC mp_obj_t machine_hard_uart_make_new(const mp_obj_type_t *type, size_t n_a config.pselrts = MICROPY_HW_UART1_RTS; config.pselcts = MICROPY_HW_UART1_CTS; #endif + self->timeout = args[ARG_timeout].u_int; + self->timeout_char = args[ARG_timeout_char].u_int; // Set context to this instance of UART config.p_context = (void *)self; // Initialise ring buffer - self->buf->rx_ringbuf.buf = self->buf->rx_ringbuf_array; - self->buf->rx_ringbuf.size = sizeof(self->buf->rx_ringbuf_array); - self->buf->rx_ringbuf.iget = 0; - self->buf->rx_ringbuf.iput = 0; + self->buf.rx_ringbuf.buf = self->buf.rx_ringbuf_array; + self->buf.rx_ringbuf.size = sizeof(self->buf.rx_ringbuf_array); + self->buf.rx_ringbuf.iget = 0; + self->buf.rx_ringbuf.iput = 0; // Enable event callback and start asynchronous receive nrfx_uart_init(self->p_uart, &config, uart_event_handler); - nrfx_uart_rx(self->p_uart, &self->buf->rx_buf[0], 1); + nrfx_uart_rx(self->p_uart, &self->buf.rx_buf[0], 1); #if NRFX_UART_ENABLED nrfx_uart_rx_enable(self->p_uart); @@ -286,20 +290,29 @@ STATIC mp_obj_t machine_hard_uart_readchar(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_hard_uart_readchar_obj, machine_hard_uart_readchar); +// uart.any() +STATIC mp_obj_t machine_uart_any(mp_obj_t self_in) { + machine_hard_uart_obj_t *self = self_in; + return MP_OBJ_NEW_SMALL_INT(ringbuf_avail((ringbuf_t *)&self->buf.rx_ringbuf)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_any_obj, machine_uart_any); + // uart.sendbreak() STATIC mp_obj_t machine_hard_uart_sendbreak(mp_obj_t self_in) { return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_hard_uart_sendbreak_obj, machine_hard_uart_sendbreak); -// Since uart.write() waits up to the last byte, uart.txdone() always returns True. +// uart.txdone() STATIC mp_obj_t machine_uart_txdone(mp_obj_t self_in) { - return mp_const_true; + machine_hard_uart_obj_t *self = self_in; + return mp_obj_new_bool(!nrfx_uart_tx_in_progress(self->p_uart)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_txdone_obj, machine_uart_txdone); STATIC const mp_rom_map_elem_t machine_hard_uart_locals_dict_table[] = { // instance methods + { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&machine_uart_any_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, @@ -321,14 +334,25 @@ STATIC const mp_rom_map_elem_t machine_hard_uart_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(machine_hard_uart_locals_dict, machine_hard_uart_locals_dict_table); STATIC mp_uint_t machine_hard_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { - const machine_hard_uart_obj_t *self = self_in; + machine_hard_uart_obj_t *self = self_in; byte *buf = buf_in; + uint32_t t = self->timeout + mp_hal_ticks_ms(); // read the data for (size_t i = 0; i < size; i++) { while (!uart_rx_any(self)) { + if ((int32_t)(mp_hal_ticks_ms() - t) >= 0) { // timed out + if (i == 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } else { + return i; + } + } + MICROPY_EVENT_POLL_HOOK; } buf[i] = uart_rx_char(self); + t = self->timeout_char + mp_hal_ticks_ms(); } return size; @@ -336,14 +360,12 @@ STATIC mp_uint_t machine_hard_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_ STATIC mp_uint_t machine_hard_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { machine_hard_uart_obj_t *self = self_in; - const byte *buf = buf_in; - - nrfx_err_t err = NRFX_SUCCESS; - for (int i = 0; i < size; i++) { - err = uart_tx_char(self, (int)((uint8_t *)buf)[i]); - } + nrfx_err_t err = nrfx_uart_tx(self->p_uart, buf_in, size); if (err == NRFX_SUCCESS) { + while (nrfx_uart_tx_in_progress(self->p_uart)) { + MICROPY_EVENT_POLL_HOOK; + } // return number of bytes written return size; } else { @@ -355,9 +377,20 @@ STATIC mp_uint_t machine_hard_uart_write(mp_obj_t self_in, const void *buf_in, m STATIC mp_uint_t machine_hard_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { machine_hard_uart_obj_t *self = self_in; (void)self; + mp_uint_t ret = 0; - if (request == MP_STREAM_FLUSH) { - // Since uart.write() waits up to the last byte, uart.flush() always succeds. + if (request == MP_STREAM_POLL) { + uintptr_t flags = arg; + if ((flags & MP_STREAM_POLL_RD) && uart_rx_any(self) != 0) { + ret |= MP_STREAM_POLL_RD; + } + if ((flags & MP_STREAM_POLL_WR) && !nrfx_uart_tx_in_progress(self->p_uart)) { + ret |= MP_STREAM_POLL_WR; + } + } else if (request == MP_STREAM_FLUSH) { + while (nrfx_uart_tx_in_progress(self->p_uart)) { + MICROPY_EVENT_POLL_HOOK; + } return 0; } return MP_STREAM_ERROR; diff --git a/ports/nrf/modules/machine/uart.h b/ports/nrf/modules/machine/uart.h index d3e23645b902..eee0738fe1f9 100644 --- a/ports/nrf/modules/machine/uart.h +++ b/ports/nrf/modules/machine/uart.h @@ -38,9 +38,9 @@ void uart_init0(void); void uart_deinit(void); void uart_irq_handler(mp_uint_t uart_id); -bool uart_rx_any(const machine_hard_uart_obj_t * uart_obj); -int uart_rx_char(const machine_hard_uart_obj_t * uart_obj); -void uart_tx_strn(const machine_hard_uart_obj_t * uart_obj, const char *str, uint len); -void uart_tx_strn_cooked(const machine_hard_uart_obj_t *uart_obj, const char *str, uint len); +bool uart_rx_any(machine_hard_uart_obj_t *uart_obj); +int uart_rx_char(machine_hard_uart_obj_t *uart_obj); +void uart_tx_strn(machine_hard_uart_obj_t *uart_obj, const char *str, uint len); +void uart_tx_strn_cooked(machine_hard_uart_obj_t *uart_obj, const char *str, uint len); #endif From 2cd3a7b45c246e2c226ca5ec87563ecad29b4165 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 19 Mar 2023 12:58:05 +0100 Subject: [PATCH 019/371] nrf/nrfx_config: Use the UARTE definitions and drivers for the NRF52xx. Suggested by @ricksorensen after testing. These match better the hardware of the NRF52xx. --- ports/nrf/nrfx_config.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/nrf/nrfx_config.h b/ports/nrf/nrfx_config.h index f1f859564010..505014a2f269 100644 --- a/ports/nrf/nrfx_config.h +++ b/ports/nrf/nrfx_config.h @@ -78,6 +78,10 @@ #define NRFX_UART_ENABLED 1 #define NRFX_UART0_ENABLED 1 #define NRFX_UART1_ENABLED 1 +#elif defined(NRF52_SERIES) + #define NRFX_UARTE_ENABLED 1 + #define NRFX_UARTE0_ENABLED 1 + #define NRFX_UARTE1_ENABLED 1 #else #define NRFX_UARTE_ENABLED 1 #define NRFX_UARTE0_ENABLED 1 From 3bbf2ef3fb502285f3cc12952abf420811e9e618 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 19 Mar 2023 13:00:43 +0100 Subject: [PATCH 020/371] nrf/modules/machine: Use a dedicated function for machine.idle(). Calling MICROPY_EVENT_POLL_HOOK. That allows Ctrl-C to break loops with idle(). --- ports/nrf/modules/machine/modmachine.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ports/nrf/modules/machine/modmachine.c b/ports/nrf/modules/machine/modmachine.c index 369d957ff1e4..b080a526cf7b 100644 --- a/ports/nrf/modules/machine/modmachine.c +++ b/ports/nrf/modules/machine/modmachine.c @@ -160,6 +160,12 @@ NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_bootloader_obj, 0, 1, machine_bootloader); +STATIC mp_obj_t machine_idle(void) { + MICROPY_EVENT_POLL_HOOK; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle); + STATIC mp_obj_t machine_lightsleep(void) { __WFE(); return mp_const_none; @@ -206,7 +212,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_bootloader), MP_ROM_PTR(&machine_bootloader_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_idle), MP_ROM_PTR(&machine_lightsleep_obj) }, + { MP_ROM_QSTR(MP_QSTR_idle), MP_ROM_PTR(&machine_idle_obj) }, { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&machine_lightsleep_obj) }, { MP_ROM_QSTR(MP_QSTR_lightsleep), MP_ROM_PTR(&machine_lightsleep_obj) }, { MP_ROM_QSTR(MP_QSTR_deepsleep), MP_ROM_PTR(&machine_deepsleep_obj) }, From a2b31f968d8df46fc2e7acd224011415e6ed43a2 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 10 Mar 2023 21:12:18 +0100 Subject: [PATCH 021/371] nrf/modules/machine: Support the freq=n argument for machine.I2C. Mostly for compatibility. Effective values are 100000, 250000 and 400000. The supplied values are mapped to these. --- ports/nrf/modules/machine/i2c.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/ports/nrf/modules/machine/i2c.c b/ports/nrf/modules/machine/i2c.c index ac331b8d7af6..fad1b7336c95 100644 --- a/ports/nrf/modules/machine/i2c.c +++ b/ports/nrf/modules/machine/i2c.c @@ -48,6 +48,7 @@ #define nrfx_twi_config_t nrfx_twim_config_t #define nrfx_twi_init nrfx_twim_init +#define nrfx_twi_uninit nrfx_twim_uninit #define nrfx_twi_enable nrfx_twim_enable #define nrfx_twi_xfer nrfx_twim_xfer #define nrfx_twi_disable nrfx_twim_disable @@ -59,6 +60,8 @@ #define NRFX_TWI_INSTANCE NRFX_TWIM_INSTANCE +#define NRF_TWI_FREQ_100K NRF_TWIM_FREQ_100K +#define NRF_TWI_FREQ_250K NRF_TWIM_FREQ_250K #define NRF_TWI_FREQ_400K NRF_TWIM_FREQ_400K #endif @@ -96,11 +99,12 @@ STATIC void machine_hard_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp mp_obj_t machine_hard_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { MP_MACHINE_I2C_CHECK_FOR_LEGACY_SOFTI2C_CONSTRUCTION(n_args, n_kw, all_args); - enum { ARG_id, ARG_scl, ARG_sda }; + enum { ARG_id, ARG_scl, ARG_sda, ARG_freq }; static const mp_arg_t allowed_args[] = { { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, }; // parse args @@ -115,10 +119,21 @@ mp_obj_t machine_hard_i2c_make_new(const mp_obj_type_t *type, size_t n_args, siz config.scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj)->pin; config.sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj)->pin; - config.frequency = NRF_TWI_FREQ_400K; + int freq = NRF_TWI_FREQ_400K; + if (args[ARG_freq].u_int != -1) { + if (args[ARG_freq].u_int <= 150000) { + freq = NRF_TWI_FREQ_100K; + } else if (args[ARG_freq].u_int < 320000) { + freq = NRF_TWI_FREQ_250K; + } + } + config.frequency = freq; config.hold_bus_uninit = false; + // First reset the TWI + nrfx_twi_uninit(&self->p_twi); + // Set context to this object. nrfx_twi_init(&self->p_twi, &config, NULL, (void *)self); From be686e634eede19f33612122d7254007a3d72bd0 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 18 Mar 2023 09:08:08 +0100 Subject: [PATCH 022/371] rp2: Allow disabling USB via MICROPY_HW_ENABLE_USBDEV config. Previously, setting MICROPY_HW_ENABLE_USBDEV to 0 caused build errors. The change affects the nrf and samd ports as well, so MICROPY_HW_ENABLE_USBDEV had to be explicitly enabled there. The configuration options MICROPY_HW_ENABLE_USBDEV and MICROPY_HW_ENABLE_UART_REPL are independent, and can be enabled or disabled by a board. Signed-off-by: Damien George --- ports/nrf/mpconfigport.h | 1 + ports/rp2/mpconfigport.h | 4 ++++ ports/rp2/usbd.c | 6 ++++++ ports/samd/mpconfigport.h | 1 + shared/tinyusb/mp_cdc_common.c | 2 +- shared/tinyusb/mp_usbd.c | 6 ++++++ shared/tinyusb/mp_usbd_descriptor.c | 7 ++++++- 7 files changed, 25 insertions(+), 2 deletions(-) diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 4762693c8129..8875e3faaf7c 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -141,6 +141,7 @@ #define MICROPY_CAN_OVERRIDE_BUILTINS (1) #define MICROPY_USE_INTERNAL_ERRNO (1) #if MICROPY_HW_USB_CDC_1200BPS_TOUCH +#define MICROPY_HW_ENABLE_USBDEV (1) #define MICROPY_ENABLE_SCHEDULER (1) #define MICROPY_SCHEDULER_STATIC_NODES (1) #endif diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index fc86e33e7e89..d862812f9bb7 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -36,8 +36,12 @@ // Board and hardware specific configuration #define MICROPY_HW_MCU_NAME "RP2040" +#ifndef MICROPY_HW_ENABLE_UART_REPL #define MICROPY_HW_ENABLE_UART_REPL (0) // useful if there is no USB +#endif +#ifndef MICROPY_HW_ENABLE_USBDEV #define MICROPY_HW_ENABLE_USBDEV (1) +#endif #if MICROPY_HW_ENABLE_USBDEV // Enable USB-CDC serial port diff --git a/ports/rp2/usbd.c b/ports/rp2/usbd.c index e17c546c6101..e51cef0ff7e4 100644 --- a/ports/rp2/usbd.c +++ b/ports/rp2/usbd.c @@ -24,6 +24,10 @@ * THE SOFTWARE. */ +#include "py/mpconfig.h" + +#if MICROPY_HW_ENABLE_USBDEV + #include "mp_usbd.h" #include "string.h" #include "tusb.h" @@ -42,3 +46,5 @@ void mp_usbd_port_get_serial_number(char *serial_buf) { } serial_buf[hexlen] = 0; } + +#endif diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index a048ad54be06..153ad75820a5 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -60,6 +60,7 @@ #define MICROPY_ENABLE_SCHEDULER (1) #define MICROPY_SCHEDULER_STATIC_NODES (1) #define MICROPY_MODULE_WEAK_LINKS (1) +#define MICROPY_HW_ENABLE_USBDEV (1) #define MICROPY_HW_USB_CDC_1200BPS_TOUCH (1) // Control over Python builtins diff --git a/shared/tinyusb/mp_cdc_common.c b/shared/tinyusb/mp_cdc_common.c index 00b72e74b776..cd4f5d1013b6 100644 --- a/shared/tinyusb/mp_cdc_common.c +++ b/shared/tinyusb/mp_cdc_common.c @@ -28,7 +28,7 @@ #include "py/mphal.h" #include "modmachine.h" -#if MICROPY_HW_USB_CDC_1200BPS_TOUCH +#if MICROPY_HW_USB_CDC_1200BPS_TOUCH && MICROPY_HW_ENABLE_USBDEV #include "tusb.h" diff --git a/shared/tinyusb/mp_usbd.c b/shared/tinyusb/mp_usbd.c index 91d35a2b4a07..0edc4898818e 100644 --- a/shared/tinyusb/mp_usbd.c +++ b/shared/tinyusb/mp_usbd.c @@ -26,6 +26,10 @@ #include +#include "py/mpconfig.h" + +#if MICROPY_HW_ENABLE_USBDEV + #ifndef NO_QSTR #include "tusb.h" // TinyUSB is not avaiable when running the string preprocessor #include "device/usbd.h" @@ -35,3 +39,5 @@ void usbd_task(void) { tud_task_ext(0, false); } + +#endif diff --git a/shared/tinyusb/mp_usbd_descriptor.c b/shared/tinyusb/mp_usbd_descriptor.c index 3a21baf5bf28..c6d205c92179 100644 --- a/shared/tinyusb/mp_usbd_descriptor.c +++ b/shared/tinyusb/mp_usbd_descriptor.c @@ -25,7 +25,10 @@ * THE SOFTWARE. */ -#include "mpconfigport.h" +#include "py/mpconfig.h" + +#if MICROPY_HW_ENABLE_USBDEV + #include "tusb.h" #include "mp_usbd.h" #include "mp_usbd_internal.h" @@ -126,3 +129,5 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { (void)index; return mp_usbd_desc_cfg_static; } + +#endif From 711bac511e547096fe1e58af3ba70d31b244731f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 18 Mar 2023 08:06:43 +0100 Subject: [PATCH 023/371] rp2/main: Keep UART REPL with DEBUG=1 and MICROPY_HW_ENABLE_UART_REPL=1. For builds with DEBUG=1 and MICROPY_HW_ENABLE_UART_REPL=1, calling stdio_init_all() in main() detaches the UART input from REPL. This change suppresses calling stdio_init_all() then. --- ports/rp2/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/rp2/main.c b/ports/rp2/main.c index afc22aee72f0..9e69d159e17e 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -76,6 +76,10 @@ int main(int argc, char **argv) { bi_decl(bi_program_feature("UART REPL")) setup_default_uart(); mp_uart_init(); + #else + #ifndef NDEBUG + stdio_init_all(); + #endif #endif #if MICROPY_HW_ENABLE_USBDEV @@ -90,10 +94,6 @@ int main(int argc, char **argv) { mp_thread_init(); #endif - #ifndef NDEBUG - stdio_init_all(); - #endif - // Start and initialise the RTC datetime_t t = { .year = 2021, From 2e5f07102080a53ff548b1e29f8510bab13b4bdc Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 21 Dec 2022 11:52:47 +1100 Subject: [PATCH 024/371] lib/stm32lib: Update library to get H7 v1.11.0. Signed-off-by: Damien George --- lib/stm32lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stm32lib b/lib/stm32lib index a9f8fee7bb0c..fdb1ec6879bc 160000 --- a/lib/stm32lib +++ b/lib/stm32lib @@ -1 +1 @@ -Subproject commit a9f8fee7bb0cb4ac1b4ff719b6b5b285f613f352 +Subproject commit fdb1ec6879bc8b7bb54ded73bac04839003c5a07 From 3187e4c7e7fe0f0596453dbdb3e909a299c7de7d Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 21 Dec 2022 11:53:09 +1100 Subject: [PATCH 025/371] stm32/mphalport: Update HAL version to 1.11.0 to match stm32lib. Signed-off-by: Damien George --- ports/stm32/mphalport.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/mphalport.h b/ports/stm32/mphalport.h index 5c587c016d04..728f5fefbded 100644 --- a/ports/stm32/mphalport.h +++ b/ports/stm32/mphalport.h @@ -14,7 +14,7 @@ #elif defined(STM32G4) #define MICROPY_PLATFORM_VERSION "HAL1.3.0" #elif defined(STM32H7) -#define MICROPY_PLATFORM_VERSION "HAL1.6.0" +#define MICROPY_PLATFORM_VERSION "HAL1.11.0" #elif defined(STM32L0) #define MICROPY_PLATFORM_VERSION "HAL1.11.2" #elif defined(STM32L1) From 13fcd8440e58f0a4eb14d8c2762c73b05bf3e8e6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 20 Dec 2022 17:00:59 +1100 Subject: [PATCH 026/371] stm32: Add support for STM32H723 MCUs. Signed-off-by: Damien George --- ports/stm32/Makefile | 10 +++------- ports/stm32/adc.c | 3 ++- ports/stm32/flash.c | 9 ++++++++- ports/stm32/machine_adc.c | 6 ++++++ ports/stm32/main.c | 2 ++ ports/stm32/powerctrl.c | 8 ++++---- ports/stm32/usbd_conf.c | 23 ++++++++++++++++++++++- 7 files changed, 47 insertions(+), 14 deletions(-) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index fa0f1c5eb42c..49b5f6f114f6 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -437,14 +437,10 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ ) endif -ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),g0 g4)) -HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_, hal_fdcan.c) -endif - -ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32H743xx STM32H747xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ)) -HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_, hal_fdcan.c) -else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 f4 f7 h7)) +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 f4 f7)) HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_, hal_can.c) +else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),g0 g4 h7)) +HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_, hal_fdcan.c) else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),l4)) HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/Legacy/stm32$(MCU_SERIES)xx_, hal_can.c) $(BUILD)/$(STM32LIB_HAL_BASE)/Src/Legacy/stm32$(MCU_SERIES)xx_hal_can.o: CFLAGS += -Wno-error=cpp diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index 2df1bde19aa4..ba09c612d7cf 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -160,7 +160,8 @@ #define VBAT_DIV (4) #elif defined(STM32G0) || defined(STM32G4) #define VBAT_DIV (3) -#elif defined(STM32H743xx) || defined(STM32H747xx) || \ +#elif defined(STM32H723xx) || defined(STM32H733xx) || \ + defined(STM32H743xx) || defined(STM32H747xx) || \ defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || \ defined(STM32H7B3xx) || defined(STM32H7B3xxQ) || \ defined(STM32H750xx) diff --git a/ports/stm32/flash.c b/ports/stm32/flash.c index 16af49c3cfb8..2cc6c390ba01 100644 --- a/ports/stm32/flash.c +++ b/ports/stm32/flash.c @@ -133,7 +133,14 @@ static const flash_layout_t flash_layout[] = { #error Unsupported processor #endif -#if (defined(STM32L4) && defined(SYSCFG_MEMRMP_FB_MODE)) || defined(STM32H7) +#if defined(STM32H723xx) + +// get the bank of a given flash address +static uint32_t get_bank(uint32_t addr) { + return FLASH_BANK_1; +} + +#elif (defined(STM32L4) && defined(SYSCFG_MEMRMP_FB_MODE)) || defined(STM32H7) // get the bank of a given flash address static uint32_t get_bank(uint32_t addr) { diff --git a/ports/stm32/machine_adc.c b/ports/stm32/machine_adc.c index 1a478cd1cffb..34319e975332 100644 --- a/ports/stm32/machine_adc.c +++ b/ports/stm32/machine_adc.c @@ -315,7 +315,13 @@ STATIC void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp #if defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) ADC_Common_TypeDef *adc_common = ADC12_COMMON; #elif defined(STM32H7) + #if defined(ADC_VER_V5_V90) + if (adc != ADC3) { + adc->PCSEL_RES0 |= 1 << channel; + } + #else adc->PCSEL |= 1 << channel; + #endif ADC_Common_TypeDef *adc_common = adc == ADC3 ? ADC3_COMMON : ADC12_COMMON; #elif defined(STM32L4) ADC_Common_TypeDef *adc_common = ADCx_COMMON; diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 511da7d8afb6..e5ad14fcf770 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -379,8 +379,10 @@ void stm32_main(uint32_t reset_mode) { // Enable D2 SRAM1/2/3 clocks. __HAL_RCC_D2SRAM1_CLK_ENABLE(); __HAL_RCC_D2SRAM2_CLK_ENABLE(); + #if defined(__HAL_RCC_D2SRAM3_CLK_ENABLE) __HAL_RCC_D2SRAM3_CLK_ENABLE(); #endif + #endif MICROPY_BOARD_EARLY_INIT(); diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index 498fb79d71c8..0ab80b487a67 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -33,11 +33,9 @@ #if defined(STM32H7) #define RCC_SR RSR -#if defined(STM32H743xx) || defined(STM32H750xx) -#define RCC_SR_SFTRSTF RCC_RSR_SFTRSTF -#elif defined(STM32H747xx) +#if defined(STM32H747xx) #define RCC_SR_SFTRSTF RCC_RSR_SFT2RSTF -#elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) +#else #define RCC_SR_SFTRSTF RCC_RSR_SFTRSTF #endif #define RCC_SR_RMVF RCC_RSR_RMVF @@ -48,6 +46,8 @@ defined(STM32H7B3xx) || defined(STM32H7B3xxQ) // TODO #define POWERCTRL_GET_VOLTAGE_SCALING() PWR_REGULATOR_VOLTAGE_SCALE0 +#elif defined(STM32H723xx) +#define POWERCTRL_GET_VOLTAGE_SCALING() LL_PWR_GetRegulVoltageScaling() #else #define POWERCTRL_GET_VOLTAGE_SCALING() \ (((PWR->CSR1 & PWR_CSR1_ACTVOS) && (SYSCFG->PWRCR & SYSCFG_PWRCR_ODEN)) ? \ diff --git a/ports/stm32/usbd_conf.c b/ports/stm32/usbd_conf.c index d0f519d45670..b6880da2d618 100644 --- a/ports/stm32/usbd_conf.c +++ b/ports/stm32/usbd_conf.c @@ -135,6 +135,26 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { if (hpcd->Instance == USB_OTG_HS) { #if MICROPY_HW_USB_HS_IN_FS + // Configure USB GPIO's. + + #if defined(STM32H723xx) + + // These MCUs don't have an alternate function for USB but rather require + // the pins to be disconnected from all peripherals, ie put in analog mode. + + #if defined(MICROPY_HW_USB_OTG_ID_PIN) + const uint32_t otg_alt = GPIO_AF10_OTG1_FS; + #endif + + mp_hal_pin_config(pin_A11, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); + mp_hal_pin_config_speed(pin_A11, GPIO_SPEED_FREQ_VERY_HIGH); + mp_hal_pin_config(pin_A12, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); + mp_hal_pin_config_speed(pin_A12, GPIO_SPEED_FREQ_VERY_HIGH); + + #else + + // Other MCUs have an alternate function for GPIO's to be in USB mode. + #if defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) const uint32_t otg_alt = GPIO_AF10_OTG1_FS; #elif defined(STM32H7) @@ -143,12 +163,13 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { const uint32_t otg_alt = GPIO_AF12_OTG_HS_FS; #endif - // Configure USB FS GPIOs mp_hal_pin_config(pin_B14, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, otg_alt); mp_hal_pin_config_speed(pin_B14, GPIO_SPEED_FREQ_VERY_HIGH); mp_hal_pin_config(pin_B15, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, otg_alt); mp_hal_pin_config_speed(pin_B15, GPIO_SPEED_FREQ_VERY_HIGH); + #endif + #if defined(MICROPY_HW_USB_VBUS_DETECT_PIN) // Configure VBUS Pin mp_hal_pin_config(MICROPY_HW_USB_VBUS_DETECT_PIN, MP_HAL_PIN_MODE_INPUT, MP_HAL_PIN_PULL_NONE, 0); From d995c010428420c7690d5cd59b9580c3f4f9649a Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 20 Dec 2022 16:40:59 +1100 Subject: [PATCH 027/371] stm32/boards: Add ld and af.csv for H723. Signed-off-by: Damien George --- ports/stm32/boards/stm32h723.ld | 46 +++++++++++ ports/stm32/boards/stm32h723_af.csv | 116 ++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 ports/stm32/boards/stm32h723.ld create mode 100644 ports/stm32/boards/stm32h723_af.csv diff --git a/ports/stm32/boards/stm32h723.ld b/ports/stm32/boards/stm32h723.ld new file mode 100644 index 000000000000..d1e934ed312c --- /dev/null +++ b/ports/stm32/boards/stm32h723.ld @@ -0,0 +1,46 @@ +/* + GNU linker script for STM32H723 +*/ + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 768K /* sectors 0-5 */ + FLASH_APP (rx) : ORIGIN = 0x08020000, LENGTH = 640K /* sectors 1-5 */ + FLASH_FS (r) : ORIGIN = 0x080c0000, LENGTH = 256K /* sectors 6-7 */ + DTCM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K /* Used for FS storage cache */ + RAM (xrw) : ORIGIN = 0x24000000, LENGTH = 128K /* AXI SRAM (could extend +192K from ITCM) */ + RAM_SRAM1 (xrw) : ORIGIN = 0x30000000, LENGTH = 16K /* SRAM1 */ +} + +/* produce a link error if there is not this amount of RAM for these sections */ +_minimum_stack_size = 2K; +_minimum_heap_size = 16K; + +/* Define the stack. The stack is full descending so begins just above last byte + of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ +_estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve; +_sstack = _estack - 16K; /* tunable */ + +/* RAM extents for the garbage collector */ +_ram_start = ORIGIN(RAM); +_ram_end = ORIGIN(RAM) + LENGTH(RAM); +_heap_start = _ebss; /* heap starts just after statically allocated memory */ +_heap_end = _sstack; + +/* Location of filesystem RAM cache */ +_micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(DTCM); +_micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM); + +/* Location of filesystem flash storage */ +_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); +_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); + +/* Define output sections */ +SECTIONS +{ + .eth_buffers (NOLOAD) : { + . = ABSOLUTE(0x30000000); + *eth.o*(.bss.eth_dma) + } >RAM_SRAM1 +} diff --git a/ports/stm32/boards/stm32h723_af.csv b/ports/stm32/boards/stm32h723_af.csv new file mode 100644 index 000000000000..fe16b378dfa4 --- /dev/null +++ b/ports/stm32/boards/stm32h723_af.csv @@ -0,0 +1,116 @@ +Port,,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11,AF12,AF13,AF14,AF15, +,,SYS,FMC/LPTIM1/SAI4/TIM16/17/TIM1x/TIM2x,FDCAN3/PDM_SAI1/TIM3/4/5/12/15,DFSDM1/LCD/LPTIM2/3/4/5/LPUART1/OCTOSPIM_P1/2/TIM8,CEC/DCMI/PSSI/DFSDM1/I2C1/2/3/4/5/LPTIM2/OCTOSPIM_P1/TIM15/USART1/10,CEC/FDCAN3/SPI1/I2S1/SPI2/I2S2/SPI3/I2S3/SPI4/5/6,DFSDM1/I2C4/5/OCTOSPIM_P1/SAI1/SPI3/I2S3/UART4,SDMMC1/SPI2/I2S2/SPI3/I2S3/SPI6/UART7/USART1/2/3/6,LPUART1/SAI4/SDMMC1/SPDIFRX1/SPI6/UART4/5/8,FDCAN1/2/FMC/LCD/OCTOSPIM_P1/2/SAI4/SDMMC2/SPDIFRX1/TIM13/14,CRS/FMC/LCD/OCTOSPIM_P1/OTG1_FS/OTG1_HS/SAI4/SDMMC2/TIM8,DFSDM1/ETH/I2C4/LCD/MDIOS/OCTOSPIM_P1/SDMMC2/SWPMI1/TIM1x/TIM8/UART7/9/USART10,FMC/LCD/MDIOS/OCTOSPIM_P1/SDMMC1/TIM1x/TIM8,COMP/DCMI/PSSI/LCD/TIM1x/TIM23,LCD/TIM24/UART5,SYS,ADC +PortA,PA0,,TIM2_CH1/TIM2_ETR,TIM5_CH1,TIM8_ETR,TIM15_BKIN,SPI6_NSS/I2S6_WS,,USART2_CTS/USART2_NSS,UART4_TX,SDMMC2_CMD,SAI4_SD_B,ETH_MII_CRS,FMC_A19,,,EVENTOUT,ADC123_IN0 +PortA,PA1,,TIM2_CH2,TIM5_CH2,LPTIM3_OUT,TIM15_CH1N,,,USART2_RTS/USART2_DE,UART4_RX,OCTOSPIM_P1_IO3,SAI4_MCLK_B,ETH_MII_RX_CLK/ETH_RMII_REF_CLK,OCTOSPIM_P1_DQS,,LCD_R2,EVENTOUT,ADC123_IN1 +PortA,PA2,,TIM2_CH3,TIM5_CH3,LPTIM4_OUT,TIM15_CH1,,OCTOSPIM_P1_IO0,USART2_TX,SAI4_SCK_B,,,ETH_MDIO,MDIOS_MDIO,,LCD_R1,EVENTOUT,ADC123_IN2 +PortA,PA3,,TIM2_CH4,TIM5_CH4,LPTIM5_OUT,TIM15_CH2,I2S6_MCK,OCTOSPIM_P1_IO2,USART2_RX,,LCD_B2,OTG_HS_ULPI_D0,ETH_MII_COL,OCTOSPIM_P1_CLK,,LCD_B5,EVENTOUT,ADC123_IN3 +PortA,PA4,D1PWREN,,TIM5_ETR,,,SPI1_NSS/I2S1_WS,SPI3_NSS/I2S3_WS,USART2_CK,SPI6_NSS/I2S6_WS,,,,FMC_D8/FMC_AD8,DCMI_HSYNC/PSSI_DE,LCD_VSYNC,EVENTOUT,ADC12_IN4 +PortA,PA5,D2PWREN,TIM2_CH1/TIM2_ETR,,TIM8_CH1N,,SPI1_SCK/I2S1_CK,,,SPI6_SCK/I2S6_CK,,OTG_HS_ULPI_CK,,FMC_D9/FMC_AD9,PSSI_D14,LCD_R4,EVENTOUT,ADC12_IN5 +PortA,PA6,,TIM1_BKIN,TIM3_CH1,TIM8_BKIN,,SPI1_MISO/I2S1_SDI,OCTOSPIM_P1_IO3,,SPI6_MISO/I2S6_SDI,TIM13_CH1,TIM8_BKIN_COMP12,MDIOS_MDC,TIM1_BKIN_COMP12,DCMI_PIXCLK/PSSI_PDCK,LCD_G2,EVENTOUT,ADC12_IN6 +PortA,PA7,,TIM1_CH1N,TIM3_CH2,TIM8_CH1N,,SPI1_MOSI/I2S1_SDO,,,SPI6_MOSI/I2S6_SDO,TIM14_CH1,OCTOSPIM_P1_IO2,ETH_MII_RX_DV/ETH_RMII_CRS_DV,FMC_SDNWE,,LCD_VSYNC,EVENTOUT,ADC12_IN7 +PortA,PA8,MCO1,TIM1_CH1,,TIM8_BKIN2,I2C3_SCL,,I2C5_SCL,USART1_CK,,,OTG_HS_SOF,UART7_RX,TIM8_BKIN2_COMP12,LCD_B3,LCD_R6,EVENTOUT, +PortA,PA9,,TIM1_CH2,,LPUART1_TX,I2C3_SMBA,SPI2_SCK/I2S2_CK,I2C5_SMBA,USART1_TX,,,,ETH_TX_ER,,DCMI_D0/PSSI_D0,LCD_R5,EVENTOUT, +PortA,PA10,,TIM1_CH3,,LPUART1_RX,,,,USART1_RX,,,OTG_HS_ID,MDIOS_MDIO,LCD_B4,DCMI_D1/PSSI_D1,LCD_B1,EVENTOUT, +PortA,PA11,,TIM1_CH4,,LPUART1_CTS,,SPI2_NSS/I2S2_WS,UART4_RX,USART1_CTS/USART1_NSS,,FDCAN1_RX,,,,,LCD_R4,EVENTOUT, +PortA,PA12,,TIM1_ETR,,LPUART1_RTS/LPUART1_DE,,SPI2_SCK/I2S2_CK,UART4_TX,USART1_RTS/USART1_DE,SAI4_FS_B,FDCAN1_TX,,,TIM1_BKIN2,,LCD_R5,EVENTOUT, +PortA,PA13,JTMS/SWDIO,,,,,,,,,,,,,,,EVENTOUT, +PortA,PA14,JTCK/SWCLK,,,,,,,,,,,,,,,EVENTOUT, +PortA,PA15,JTDI,TIM2_CH1/TIM2_ETR,,,CEC,SPI1_NSS/I2S1_WS,SPI3_NSS/I2S3_WS,SPI6_NSS/I2S6_WS,UART4_RTS/UART4_DE,LCD_R3,,UART7_TX,,,LCD_B6,EVENTOUT, +PortB,PB0,,TIM1_CH2N,TIM3_CH3,TIM8_CH2N,OCTOSPIM_P1_IO1,,DFSDM1_CKOUT,,UART4_CTS,LCD_R3,OTG_HS_ULPI_D1,ETH_MII_RXD2,,,LCD_G1,EVENTOUT,ADC12_IN8 +PortB,PB1,,TIM1_CH3N,TIM3_CH4,TIM8_CH3N,OCTOSPIM_P1_IO0,,DFSDM1_DATIN1,,,LCD_R6,OTG_HS_ULPI_D2,ETH_MII_RXD3,,,LCD_G0,EVENTOUT,ADC12_IN9 +PortB,PB2,RTC_OUT,SAI4_D1,SAI1_D1,,DFSDM1_CKIN1,,SAI1_SD_A,SPI3_MOSI/I2S3_SDO,SAI4_SD_A,OCTOSPIM_P1_CLK,OCTOSPIM_P1_DQS,ETH_TX_ER,,TIM23_ETR,,EVENTOUT, +PortB,PB3,JTDO/TRACESWO,TIM2_CH2,,,,SPI1_SCK/I2S1_CK,SPI3_SCK/I2S3_CK,,SPI6_SCK/I2S6_CK,SDMMC2_D2,CRS_SYNC,UART7_RX,,,TIM24_ETR,EVENTOUT, +PortB,PB4,NJTRST,TIM16_BKIN,TIM3_CH1,,,SPI1_MISO/I2S1_SDI,SPI3_MISO/I2S3_SDI,SPI2_NSS/I2S2_WS,SPI6_MISO/I2S6_SDI,SDMMC2_D3,,UART7_TX,,,,EVENTOUT, +PortB,PB5,,TIM17_BKIN,TIM3_CH2,LCD_B5,I2C1_SMBA,SPI1_MOSI/I2S1_SDO,I2C4_SMBA,SPI3_MOSI/I2S3_SDO,SPI6_MOSI/I2S6_SDO,FDCAN2_RX,OTG_HS_ULPI_D7,ETH_PPS_OUT,FMC_SDCKE1,DCMI_D10/PSSI_D10,UART5_RX,EVENTOUT, +PortB,PB6,,TIM16_CH1N,TIM4_CH1,,I2C1_SCL,CEC,I2C4_SCL,USART1_TX,LPUART1_TX,FDCAN2_TX,OCTOSPIM_P1_NCS,DFSDM1_DATIN5,FMC_SDNE1,DCMI_D5/PSSI_D5,UART5_TX,EVENTOUT, +PortB,PB7,,TIM17_CH1N,TIM4_CH2,,I2C1_SDA,,I2C4_SDA,USART1_RX,LPUART1_RX,,,DFSDM1_CKIN5,FMC_NL,DCMI_VSYNC/PSSI_RDY,,EVENTOUT, +PortB,PB8,,TIM16_CH1,TIM4_CH3,DFSDM1_CKIN7,I2C1_SCL,,I2C4_SCL,SDMMC1_CKIN,UART4_RX,FDCAN1_RX,SDMMC2_D4,ETH_MII_TXD3,SDMMC1_D4,DCMI_D6/PSSI_D6,LCD_B6,EVENTOUT, +PortB,PB9,,TIM17_CH1,TIM4_CH4,DFSDM1_DATIN7,I2C1_SDA,SPI2_NSS/I2S2_WS,I2C4_SDA,SDMMC1_CDIR,UART4_TX,FDCAN1_TX,SDMMC2_D5,I2C4_SMBA,SDMMC1_D5,DCMI_D7/PSSI_D7,LCD_B7,EVENTOUT, +PortB,PB10,,TIM2_CH3,,LPTIM2_IN1,I2C2_SCL,SPI2_SCK/I2S2_CK,DFSDM1_DATIN7,USART3_TX,,OCTOSPIM_P1_NCS,OTG_HS_ULPI_D3,ETH_MII_RX_ER,,,LCD_G4,EVENTOUT, +PortB,PB11,,TIM2_CH4,,LPTIM2_ETR,I2C2_SDA,,DFSDM1_CKIN7,USART3_RX,,,OTG_HS_ULPI_D4,ETH_MII_TX_EN/ETH_RMII_TX_EN,,,LCD_G5,EVENTOUT, +PortB,PB12,,TIM1_BKIN,,OCTOSPIM_P1_NCLK,I2C2_SMBA,SPI2_NSS/I2S2_WS,DFSDM1_DATIN1,USART3_CK,,FDCAN2_RX,OTG_HS_ULPI_D5,ETH_MII_TXD0/ETH_RMII_TXD0,OCTOSPIM_P1_IO0,TIM1_BKIN_COMP12,UART5_RX,EVENTOUT, +PortB,PB13,,TIM1_CH1N,,LPTIM2_OUT,OCTOSPIM_P1_IO2,SPI2_SCK/I2S2_CK,DFSDM1_CKIN1,USART3_CTS/USART3_NSS,,FDCAN2_TX,OTG_HS_ULPI_D6,ETH_MII_TXD1/ETH_RMII_TXD1,SDMMC1_D0,DCMI_D2/PSSI_D2,UART5_TX,EVENTOUT, +PortB,PB14,,TIM1_CH2N,TIM12_CH1,TIM8_CH2N,USART1_TX,SPI2_MISO/I2S2_SDI,DFSDM1_DATIN2,USART3_RTS/USART3_DE,UART4_RTS/UART4_DE,SDMMC2_D0,,,FMC_D10/FMC_AD10,,LCD_CLK,EVENTOUT, +PortB,PB15,RTC_REFIN,TIM1_CH3N,TIM12_CH2,TIM8_CH3N,USART1_RX,SPI2_MOSI/I2S2_SDO,DFSDM1_CKIN2,,UART4_CTS,SDMMC2_D1,,,FMC_D11/FMC_AD11,,LCD_G7,EVENTOUT, +PortC,PC0,,FMC_D12/FMC_AD12,,DFSDM1_CKIN0,,,DFSDM1_DATIN4,,SAI4_FS_B,FMC_A25,OTG_HS_ULPI_STP,LCD_G2,FMC_SDNWE,,LCD_R5,EVENTOUT,ADC123_IN10 +PortC,PC1,TRACED0,SAI4_D1,SAI1_D1,DFSDM1_DATIN0,DFSDM1_CKIN4,SPI2_MOSI/I2S2_SDO,SAI1_SD_A,,SAI4_SD_A,SDMMC2_CK,OCTOSPIM_P1_IO4,ETH_MDC,MDIOS_MDC,,LCD_G5,EVENTOUT,ADC123_IN11 +PortC,PC2,PWR_DEEPSLEEP,,,DFSDM1_CKIN1,OCTOSPIM_P1_IO5,SPI2_MISO/I2S2_SDI,DFSDM1_CKOUT,,,OCTOSPIM_P1_IO2,OTG_HS_ULPI_DIR,ETH_MII_TXD2,FMC_SDNE0,,,EVENTOUT,ADC123_IN12 +PortC,PC3,PWR_SLEEP,,,DFSDM1_DATIN1,OCTOSPIM_P1_IO6,SPI2_MOSI/I2S2_SDO,,,,OCTOSPIM_P1_IO0,OTG_HS_ULPI_NXT,ETH_MII_TX_CLK,FMC_SDCKE0,,,EVENTOUT,ADC123_IN13 +PortC,PC4,PWR_DEEPSLEEP,FMC_A22,,DFSDM1_CKIN2,,I2S1_MCK,,,,SPDIFRX1_IN3,SDMMC2_CKIN,ETH_MII_RXD0/ETH_RMII_RXD0,FMC_SDNE0,,LCD_R7,EVENTOUT,ADC12_IN14 +PortC,PC5,PWR_SLEEP,SAI4_D3,SAI1_D3,DFSDM1_DATIN2,PSSI_D15,,,,,SPDIFRX1_IN4,OCTOSPIM_P1_DQS,ETH_MII_RXD1/ETH_RMII_RXD1,FMC_SDCKE0,COMP1_OUT,LCD_DE,EVENTOUT,ADC12_IN15 +PortC,PC6,,,TIM3_CH1,TIM8_CH1,DFSDM1_CKIN3,I2S2_MCK,,USART6_TX,SDMMC1_D0DIR,FMC_NWAIT,SDMMC2_D6,,SDMMC1_D6,DCMI_D0/PSSI_D0,LCD_HSYNC,EVENTOUT, +PortC,PC7,DBTRGIO,,TIM3_CH2,TIM8_CH2,DFSDM1_DATIN3,,I2S3_MCK,USART6_RX,SDMMC1_D123DIR,FMC_NE1,SDMMC2_D7,SWPMI_TX,SDMMC1_D7,DCMI_D1/PSSI_D1,LCD_G6,EVENTOUT, +PortC,PC8,TRACED1,,TIM3_CH3,TIM8_CH3,,,,USART6_CK,UART5_RTS/UART5_DE,FMC_NE2/FMC_NCE,FMC_INT,SWPMI_RX,SDMMC1_D0,DCMI_D2/PSSI_D2,,EVENTOUT, +PortC,PC9,MCO2,,TIM3_CH4,TIM8_CH4,I2C3_SDA,I2S_CKIN,I2C5_SDA,,UART5_CTS,OCTOSPIM_P1_IO0,LCD_G3,SWPMI_SUSPEND,SDMMC1_D1,DCMI_D3/PSSI_D3,LCD_B2,EVENTOUT, +PortC,PC10,,,,DFSDM1_CKIN5,I2C5_SDA,,SPI3_SCK/I2S3_CK,USART3_TX,UART4_TX,OCTOSPIM_P1_IO1,LCD_B1,SWPMI_RX,SDMMC1_D2,DCMI_D8/PSSI_D8,LCD_R2,EVENTOUT, +PortC,PC11,,,,DFSDM1_DATIN5,I2C5_SCL,,SPI3_MISO/I2S3_SDI,USART3_RX,UART4_RX,OCTOSPIM_P1_NCS,,,SDMMC1_D3,DCMI_D4/PSSI_D4,LCD_B4,EVENTOUT, +PortC,PC12,TRACED3,FMC_D6/FMC_AD6,TIM15_CH1,,I2C5_SMBA,SPI6_SCK/I2S6_CK,SPI3_MOSI/I2S3_SDO,USART3_CK,UART5_TX,,,,SDMMC1_CK,DCMI_D9/PSSI_D9,LCD_R6,EVENTOUT, +PortC,PC13,,,,,,,,,,,,,,,,EVENTOUT, +PortC,PC14,,,,,,,,,,,,,,,,EVENTOUT, +PortC,PC15,,,,,,,,,,,,,,,,EVENTOUT, +PortD,PD0,,,,DFSDM1_CKIN6,,,,,UART4_RX,FDCAN1_RX,,UART9_CTS,FMC_D2/FMC_AD2,,LCD_B1,EVENTOUT, +PortD,PD1,,,,DFSDM1_DATIN6,,,,,UART4_TX,FDCAN1_TX,,,FMC_D3/FMC_AD3,,,EVENTOUT, +PortD,PD2,TRACED2,FMC_D7/FMC_AD7,TIM3_ETR,,TIM15_BKIN,,,,UART5_RX,LCD_B7,,,SDMMC1_CMD,DCMI_D11/PSSI_D11,LCD_B2,EVENTOUT, +PortD,PD3,,,,DFSDM1_CKOUT,,SPI2_SCK/I2S2_CK,,USART2_CTS/USART2_NSS,,,,,FMC_CLK,DCMI_D5/PSSI_D5,LCD_G7,EVENTOUT, +PortD,PD4,,,,,,,,USART2_RTS/USART2_DE,,,OCTOSPIM_P1_IO4,,FMC_NOE,,,EVENTOUT, +PortD,PD5,,,,,,,,USART2_TX,,,OCTOSPIM_P1_IO5,,FMC_NWE,,,EVENTOUT, +PortD,PD6,,SAI4_D1,SAI1_D1,DFSDM1_CKIN4,DFSDM1_DATIN1,SPI3_MOSI/I2S3_SDO,SAI1_SD_A,USART2_RX,SAI4_SD_A,,OCTOSPIM_P1_IO6,SDMMC2_CK,FMC_NWAIT,DCMI_D10/PSSI_D10,LCD_B2,EVENTOUT, +PortD,PD7,,,,DFSDM1_DATIN4,,SPI1_MOSI/I2S1_SDO,DFSDM1_CKIN1,USART2_CK,,SPDIFRX1_IN1,OCTOSPIM_P1_IO7,SDMMC2_CMD,FMC_NE1,,,EVENTOUT, +PortD,PD8,,,,DFSDM1_CKIN3,,,,USART3_TX,,SPDIFRX1_IN2,,,FMC_D13/FMC_AD13,,,EVENTOUT, +PortD,PD9,,,,DFSDM1_DATIN3,,,,USART3_RX,,,,,FMC_D14/FMC_AD14,,,EVENTOUT, +PortD,PD10,,,,DFSDM1_CKOUT,,,,USART3_CK,,,,,FMC_D15/FMC_AD15,,LCD_B3,EVENTOUT, +PortD,PD11,,,,LPTIM2_IN2,I2C4_SMBA,,,USART3_CTS/USART3_NSS,,OCTOSPIM_P1_IO0,SAI4_SD_A,,FMC_A16/FMC_CLE,,,EVENTOUT, +PortD,PD12,,LPTIM1_IN1,TIM4_CH1,LPTIM2_IN1,I2C4_SCL,FDCAN3_RX,,USART3_RTS/USART3_DE,,OCTOSPIM_P1_IO1,SAI4_FS_A,,FMC_A17/FMC_ALE,DCMI_D12/PSSI_D12,,EVENTOUT, +PortD,PD13,,LPTIM1_OUT,TIM4_CH2,,I2C4_SDA,FDCAN3_TX,,,,OCTOSPIM_P1_IO3,SAI4_SCK_A,UART9_RTS/UART9_DE,FMC_A18,DCMI_D13/PSSI_D13,,EVENTOUT, +PortD,PD14,,,TIM4_CH3,,,,,,UART8_CTS,,,UART9_RX,FMC_D0/FMC_AD0,,,EVENTOUT, +PortD,PD15,,,TIM4_CH4,,,,,,UART8_RTS/UART8_DE,,,UART9_TX,FMC_D1/FMC_AD1,,,EVENTOUT, +PortE,PE0,,LPTIM1_ETR,TIM4_ETR,,LPTIM2_ETR,,,,UART8_RX,,SAI4_MCLK_A,,FMC_NBL0,DCMI_D2/PSSI_D2,LCD_R0,EVENTOUT, +PortE,PE1,,LPTIM1_IN2,,,,,,,UART8_TX,,,,FMC_NBL1,DCMI_D3/PSSI_D3,LCD_R6,EVENTOUT, +PortE,PE2,TRACECLK,,SAI1_CK1,,USART10_RX,SPI4_SCK,SAI1_MCLK_A,,SAI4_MCLK_A,OCTOSPIM_P1_IO2,SAI4_CK1,ETH_MII_TXD3,FMC_A23,,,EVENTOUT, +PortE,PE3,TRACED0,,,,TIM15_BKIN,,SAI1_SD_B,,SAI4_SD_B,,,USART10_TX,FMC_A19,,,EVENTOUT, +PortE,PE4,TRACED1,,SAI1_D2,DFSDM1_DATIN3,TIM15_CH1N,SPI4_NSS,SAI1_FS_A,,SAI4_FS_A,,SAI4_D2,,FMC_A20,DCMI_D4/PSSI_D4,LCD_B0,EVENTOUT, +PortE,PE5,TRACED2,,SAI1_CK2,DFSDM1_CKIN3,TIM15_CH1,SPI4_MISO,SAI1_SCK_A,,SAI4_SCK_A,,SAI4_CK2,,FMC_A21,DCMI_D6/PSSI_D6,LCD_G0,EVENTOUT, +PortE,PE6,TRACED3,TIM1_BKIN2,SAI1_D1,,TIM15_CH2,SPI4_MOSI,SAI1_SD_A,,SAI4_SD_A,SAI4_D1,SAI4_MCLK_B,TIM1_BKIN2_COMP12,FMC_A22,DCMI_D7/PSSI_D7,LCD_G1,EVENTOUT, +PortE,PE7,,TIM1_ETR,,DFSDM1_DATIN2,,,,UART7_RX,,,OCTOSPIM_P1_IO4,,FMC_D4/FMC_AD4,,,EVENTOUT, +PortE,PE8,,TIM1_CH1N,,DFSDM1_CKIN2,,,,UART7_TX,,,OCTOSPIM_P1_IO5,,FMC_D5/FMC_AD5,COMP2_OUT,,EVENTOUT, +PortE,PE9,,TIM1_CH1,,DFSDM1_CKOUT,,,,UART7_RTS/UART7_DE,,,OCTOSPIM_P1_IO6,,FMC_D6/FMC_AD6,,,EVENTOUT, +PortE,PE10,,TIM1_CH2N,,DFSDM1_DATIN4,,,,UART7_CTS,,,OCTOSPIM_P1_IO7,,FMC_D7/FMC_AD7,,,EVENTOUT, +PortE,PE11,,TIM1_CH2,,DFSDM1_CKIN4,,SPI4_NSS,,,,,SAI4_SD_B,OCTOSPIM_P1_NCS,FMC_D8/FMC_AD8,,LCD_G3,EVENTOUT, +PortE,PE12,,TIM1_CH3N,,DFSDM1_DATIN5,,SPI4_SCK,,,,,SAI4_SCK_B,,FMC_D9/FMC_AD9,COMP1_OUT,LCD_B4,EVENTOUT, +PortE,PE13,,TIM1_CH3,,DFSDM1_CKIN5,,SPI4_MISO,,,,,SAI4_FS_B,,FMC_D10/FMC_AD10,COMP2_OUT,LCD_DE,EVENTOUT, +PortE,PE14,,TIM1_CH4,,,,SPI4_MOSI,,,,,SAI4_MCLK_B,,FMC_D11/FMC_AD11,,LCD_CLK,EVENTOUT, +PortE,PE15,,TIM1_BKIN,,,,,,,,,,USART10_CK,FMC_D12/FMC_AD12,TIM1_BKIN_COMP12,LCD_R7,EVENTOUT, +PortF,PF0,,,,,I2C2_SDA,,I2C5_SDA,,,OCTOSPIM_P2_IO0,,,FMC_A0,TIM23_CH1,,EVENTOUT, +PortF,PF1,,,,,I2C2_SCL,,I2C5_SCL,,,OCTOSPIM_P2_IO1,,,FMC_A1,TIM23_CH2,,EVENTOUT, +PortF,PF2,,,,,I2C2_SMBA,,I2C5_SMBA,,,OCTOSPIM_P2_IO2,,,FMC_A2,TIM23_CH3,,EVENTOUT, +PortF,PF3,,,,,,,,,,OCTOSPIM_P2_IO3,,,FMC_A3,TIM23_CH4,,EVENTOUT,ADC3_IN9 +PortF,PF4,,,,,,,,,,OCTOSPIM_P2_CLK,,,FMC_A4,,,EVENTOUT,ADC3_IN14 +PortF,PF5,,,,,,,,,,OCTOSPIM_P2_NCLK,,,FMC_A5,,,EVENTOUT,ADC3_IN15 +PortF,PF6,,TIM16_CH1,FDCAN3_RX,,,SPI5_NSS,SAI1_SD_B,UART7_RX,SAI4_SD_B,,OCTOSPIM_P1_IO3,,,TIM23_CH1,,EVENTOUT,ADC3_IN4 +PortF,PF7,,TIM17_CH1,FDCAN3_TX,,,SPI5_SCK,SAI1_MCLK_B,UART7_TX,SAI4_MCLK_B,,OCTOSPIM_P1_IO2,,,TIM23_CH2,,EVENTOUT,ADC3_IN5 +PortF,PF8,,TIM16_CH1N,,,,SPI5_MISO,SAI1_SCK_B,UART7_RTS/UART7_DE,SAI4_SCK_B,TIM13_CH1,OCTOSPIM_P1_IO0,,,TIM23_CH3,,EVENTOUT,ADC3_IN6 +PortF,PF9,,TIM17_CH1N,,,,SPI5_MOSI,SAI1_FS_B,UART7_CTS,SAI4_FS_B,TIM14_CH1,OCTOSPIM_P1_IO1,,,TIM23_CH4,,EVENTOUT,ADC3_IN7 +PortF,PF10,,TIM16_BKIN,SAI1_D3,,PSSI_D15,,,,,OCTOSPIM_P1_CLK,SAI4_D3,,,DCMI_D11/PSSI_D11,LCD_DE,EVENTOUT,ADC3_IN8 +PortF,PF11,,,,,,SPI5_MOSI,,,,OCTOSPIM_P1_NCLK,SAI4_SD_B,,FMC_NRAS,DCMI_D12/PSSI_D12,TIM24_CH1,EVENTOUT, +PortF,PF12,,,,,,,,,,OCTOSPIM_P2_DQS,,,FMC_A6,,TIM24_CH2,EVENTOUT, +PortF,PF13,,,,DFSDM1_DATIN6,I2C4_SMBA,,,,,,,,FMC_A7,,TIM24_CH3,EVENTOUT, +PortF,PF14,,,,DFSDM1_CKIN6,I2C4_SCL,,,,,,,,FMC_A8,,TIM24_CH4,EVENTOUT, +PortF,PF15,,,,,I2C4_SDA,,,,,,,,FMC_A9,,,EVENTOUT, +PortG,PG0,,,,,,,,,,OCTOSPIM_P2_IO4,,UART9_RX,FMC_A10,,,EVENTOUT, +PortG,PG1,,,,,,,,,,OCTOSPIM_P2_IO5,,UART9_TX,FMC_A11,,,EVENTOUT, +PortG,PG2,,,,TIM8_BKIN,,,,,,,,TIM8_BKIN_COMP12,FMC_A12,,TIM24_ETR,EVENTOUT, +PortG,PG3,,,,TIM8_BKIN2,,,,,,,,TIM8_BKIN2_COMP12,FMC_A13,TIM23_ETR,,EVENTOUT, +PortG,PG4,,TIM1_BKIN2,,,,,,,,,,TIM1_BKIN2_COMP12,FMC_A14/FMC_BA0,,,EVENTOUT, +PortG,PG5,,TIM1_ETR,,,,,,,,,,,FMC_A15/FMC_BA1,,,EVENTOUT, +PortG,PG6,,TIM17_BKIN,,,,,,,,,OCTOSPIM_P1_NCS,,FMC_NE3,DCMI_D12/PSSI_D12,LCD_R7,EVENTOUT, +PortG,PG7,,,,,,,SAI1_MCLK_A,USART6_CK,,OCTOSPIM_P2_DQS,,,FMC_INT,DCMI_D13/PSSI_D13,LCD_CLK,EVENTOUT, +PortG,PG8,,,,TIM8_ETR,,SPI6_NSS/I2S6_WS,,USART6_RTS/USART6_DE,SPDIFRX1_IN3,,,ETH_PPS_OUT,FMC_SDCLK,,LCD_G7,EVENTOUT, +PortG,PG9,,,FDCAN3_TX,,,SPI1_MISO/I2S1_SDI,,USART6_RX,SPDIFRX1_IN4,OCTOSPIM_P1_IO6,SAI4_FS_B,SDMMC2_D0,FMC_NE2/FMC_NCE,DCMI_VSYNC/PSSI_RDY,,EVENTOUT, +PortG,PG10,,,FDCAN3_RX,OCTOSPIM_P2_IO6,,SPI1_NSS/I2S1_WS,,,,LCD_G3,SAI4_SD_B,SDMMC2_D1,FMC_NE3,DCMI_D2/PSSI_D2,LCD_B2,EVENTOUT, +PortG,PG11,,LPTIM1_IN2,,,USART10_RX,SPI1_SCK/I2S1_CK,,,SPDIFRX1_IN1,OCTOSPIM_P2_IO7,SDMMC2_D2,ETH_MII_TX_EN/ETH_RMII_TX_EN,,DCMI_D3/PSSI_D3,LCD_B3,EVENTOUT, +PortG,PG12,,LPTIM1_IN1,,OCTOSPIM_P2_NCS,USART10_TX,SPI6_MISO/I2S6_SDI,,USART6_RTS/USART6_DE,SPDIFRX1_IN2,LCD_B4,SDMMC2_D3,ETH_MII_TXD1/ETH_RMII_TXD1,FMC_NE4,TIM23_CH1,LCD_B1,EVENTOUT, +PortG,PG13,TRACED0,LPTIM1_OUT,,,USART10_CTS/USART10_NSS,SPI6_SCK/I2S6_CK,,USART6_CTS/USART6_NSS,,,SDMMC2_D6,ETH_MII_TXD0/ETH_RMII_TXD0,FMC_A24,TIM23_CH2,LCD_R0,EVENTOUT, +PortG,PG14,TRACED1,LPTIM1_ETR,,,USART10_RTS/USART10_DE,SPI6_MOSI/I2S6_SDO,,USART6_TX,,OCTOSPIM_P1_IO7,SDMMC2_D7,ETH_MII_TXD1/ETH_RMII_TXD1,FMC_A25,TIM23_CH3,LCD_B0,EVENTOUT, +PortG,PG15,,,,,,,,USART6_CTS/USART6_NSS,,OCTOSPIM_P2_DQS,,USART10_CK,FMC_NCAS,DCMI_D13/PSSI_D13,,EVENTOUT, +PortH,PH0,,,,,,,,,,,,,,,,EVENTOUT, +PortH,PH1,,,,,,,,,,,,,,,,EVENTOUT, From 051e2900d97e6727034b4b59f18ec7abc517c9a8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 20 Dec 2022 16:41:15 +1100 Subject: [PATCH 028/371] stm32/boards/NUCLEO_H723ZG: Add new H723 board. The following have been tested and are working: - 550MHz CPU frequency - UART REPL via ST-Link - USB REPL and mass storage - 3x LEDs and 1x user button - Ethernet Signed-off-by: Damien George --- ports/stm32/boards/NUCLEO_H723ZG/board.json | 15 ++ ports/stm32/boards/NUCLEO_H723ZG/board_init.c | 7 + ports/stm32/boards/NUCLEO_H723ZG/manifest.py | 2 + .../boards/NUCLEO_H723ZG/mpconfigboard.h | 116 ++++++++++++++++ .../boards/NUCLEO_H723ZG/mpconfigboard.mk | 25 ++++ ports/stm32/boards/NUCLEO_H723ZG/pins.csv | 130 ++++++++++++++++++ .../boards/NUCLEO_H723ZG/stm32h7xx_hal_conf.h | 19 +++ 7 files changed, 314 insertions(+) create mode 100644 ports/stm32/boards/NUCLEO_H723ZG/board.json create mode 100644 ports/stm32/boards/NUCLEO_H723ZG/board_init.c create mode 100644 ports/stm32/boards/NUCLEO_H723ZG/manifest.py create mode 100644 ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.h create mode 100644 ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk create mode 100644 ports/stm32/boards/NUCLEO_H723ZG/pins.csv create mode 100644 ports/stm32/boards/NUCLEO_H723ZG/stm32h7xx_hal_conf.h diff --git a/ports/stm32/boards/NUCLEO_H723ZG/board.json b/ports/stm32/boards/NUCLEO_H723ZG/board.json new file mode 100644 index 000000000000..bf7dc0e2fb29 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H723ZG/board.json @@ -0,0 +1,15 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [], + "images": [ + "nucleo_h723zg.jpg" + ], + "mcu": "stm32h7", + "product": "Nucleo H723ZG", + "thumbnail": "", + "url": "", + "vendor": "ST Microelectronics" +} diff --git a/ports/stm32/boards/NUCLEO_H723ZG/board_init.c b/ports/stm32/boards/NUCLEO_H723ZG/board_init.c new file mode 100644 index 000000000000..96a2896e0858 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H723ZG/board_init.c @@ -0,0 +1,7 @@ +#include "py/mphal.h" + +void NUCLEO_H723ZG_board_early_init(void) { + // Turn off the USB switch. + mp_hal_pin_output(pyb_pin_OTG_FS_POWER); + mp_hal_pin_low(pyb_pin_OTG_FS_POWER); +} diff --git a/ports/stm32/boards/NUCLEO_H723ZG/manifest.py b/ports/stm32/boards/NUCLEO_H723ZG/manifest.py new file mode 100644 index 000000000000..ebfecd4844ad --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H723ZG/manifest.py @@ -0,0 +1,2 @@ +include("$(PORT_DIR)/boards/manifest.py") +require("bundle-networking") diff --git a/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.h b/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.h new file mode 100644 index 000000000000..095b7f4b1556 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.h @@ -0,0 +1,116 @@ +#define MICROPY_HW_BOARD_NAME "NUCLEO_H723ZG" +#define MICROPY_HW_MCU_NAME "STM32H723ZGT6" + +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_RNG (0) // RNG needs proper configuration +#define MICROPY_HW_ENABLE_ADC (1) +#define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_HW_ENABLE_USB (1) +#define MICROPY_HW_ENABLE_SDCARD (1) +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_HAS_FLASH (1) + +#define MICROPY_BOARD_EARLY_INIT NUCLEO_H723ZG_board_early_init + +// There is no external HS crystal, instead it comes from ST-LINK MCO output which is 8MHz. +// The following gives a 550MHz CPU speed. +#define MICROPY_HW_CLK_USE_BYPASS (1) +#define MICROPY_HW_CLK_PLLM (4) +#define MICROPY_HW_CLK_PLLN (275) +#define MICROPY_HW_CLK_PLLP (1) +#define MICROPY_HW_CLK_PLLQ (4) +#define MICROPY_HW_CLK_PLLR (2) +#define MICROPY_HW_CLK_PLLVCI (RCC_PLL1VCIRANGE_1) +#define MICROPY_HW_CLK_PLLVCO (RCC_PLL1VCOWIDE) +#define MICROPY_HW_CLK_PLLFRAC (0) + +// The USB clock is set using PLL3 +#define MICROPY_HW_CLK_PLL3M (4) +#define MICROPY_HW_CLK_PLL3N (120) +#define MICROPY_HW_CLK_PLL3P (2) +#define MICROPY_HW_CLK_PLL3Q (5) +#define MICROPY_HW_CLK_PLL3R (2) +#define MICROPY_HW_CLK_PLL3VCI (RCC_PLL3VCIRANGE_1) +#define MICROPY_HW_CLK_PLL3VCO (RCC_PLL3VCOWIDE) +#define MICROPY_HW_CLK_PLL3FRAC (0) + +// 4 wait states +#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_4 + +// The board has an external 32kHz crystal attached +#define MICROPY_HW_RTC_USE_LSE (1) + +// UART config +#define MICROPY_HW_UART2_TX (pin_D5) +#define MICROPY_HW_UART2_RX (pin_D6) +#define MICROPY_HW_UART2_RTS (pin_D4) +#define MICROPY_HW_UART2_CTS (pin_D3) +#define MICROPY_HW_UART3_TX (pin_D8) +#define MICROPY_HW_UART3_RX (pin_D9) +#define MICROPY_HW_UART5_TX (pin_B6) +#define MICROPY_HW_UART5_RX (pin_B12) +#define MICROPY_HW_UART6_TX (pin_C6) +#define MICROPY_HW_UART6_RX (pin_C7) +#define MICROPY_HW_UART7_TX (pin_F7) +#define MICROPY_HW_UART7_RX (pin_F6) +#define MICROPY_HW_UART8_TX (pin_E1) +#define MICROPY_HW_UART8_RX (pin_E0) + +#define MICROPY_HW_UART_REPL PYB_UART_3 +#define MICROPY_HW_UART_REPL_BAUD 115200 + +// I2C buses +#define MICROPY_HW_I2C1_SCL (pin_B8) +#define MICROPY_HW_I2C1_SDA (pin_B9) +#define MICROPY_HW_I2C2_SCL (pin_F1) +#define MICROPY_HW_I2C2_SDA (pin_F0) +#define MICROPY_HW_I2C4_SCL (pin_F14) +#define MICROPY_HW_I2C4_SDA (pin_F15) + +// SPI buses +#define MICROPY_HW_SPI3_NSS (pin_A4) +#define MICROPY_HW_SPI3_SCK (pin_B3) +#define MICROPY_HW_SPI3_MISO (pin_B4) +#define MICROPY_HW_SPI3_MOSI (pin_B5) + +// USRSW is pulled low. Pressing the button makes the input go high. +#define MICROPY_HW_USRSW_PIN (pin_C13) +#define MICROPY_HW_USRSW_PULL (GPIO_NOPULL) +#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_RISING) +#define MICROPY_HW_USRSW_PRESSED (1) + +// LEDs +#define MICROPY_HW_LED1 (pin_B0) // green +#define MICROPY_HW_LED2 (pin_E1) // yellow +#define MICROPY_HW_LED3 (pin_B14) // red +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) + +// USB config +#define MICROPY_HW_USB_HS (1) +#define MICROPY_HW_USB_HS_IN_FS (1) +#define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_A9) +#define MICROPY_HW_USB_OTG_ID_PIN (pin_A10) + +// FDCAN bus +#define MICROPY_HW_CAN1_NAME "FDCAN1" +#define MICROPY_HW_CAN1_TX (pin_D1) +#define MICROPY_HW_CAN1_RX (pin_D0) + +// SD card detect switch +#define MICROPY_HW_SDCARD_DETECT_PIN (pin_G2) +#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_PULLUP) +#define MICROPY_HW_SDCARD_DETECT_PRESENT (GPIO_PIN_RESET) + +// Ethernet via RMII +#define MICROPY_HW_ETH_MDC (pin_C1) +#define MICROPY_HW_ETH_MDIO (pin_A2) +#define MICROPY_HW_ETH_RMII_REF_CLK (pin_A1) +#define MICROPY_HW_ETH_RMII_CRS_DV (pin_A7) +#define MICROPY_HW_ETH_RMII_RXD0 (pin_C4) +#define MICROPY_HW_ETH_RMII_RXD1 (pin_C5) +#define MICROPY_HW_ETH_RMII_TX_EN (pin_G11) +#define MICROPY_HW_ETH_RMII_TXD0 (pin_G13) +#define MICROPY_HW_ETH_RMII_TXD1 (pin_B13) + +void NUCLEO_H723ZG_board_early_init(void); diff --git a/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk new file mode 100644 index 000000000000..287b51f6d83f --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H723ZG/mpconfigboard.mk @@ -0,0 +1,25 @@ +USE_MBOOT ?= 0 + +# MCU settings +MCU_SERIES = h7 +CMSIS_MCU = STM32H723xx +MICROPY_FLOAT_IMPL = double +AF_FILE = boards/stm32h723_af.csv + +ifeq ($(USE_MBOOT),1) +# When using Mboot everything goes after the bootloader +LD_FILES = boards/stm32h723.ld boards/common_bl.ld +TEXT0_ADDR = 0x08020000 +else +# When not using Mboot everything goes at the start of flash +LD_FILES = boards/stm32h723.ld boards/common_basic.ld +TEXT0_ADDR = 0x08000000 +endif + +# MicroPython settings +MICROPY_PY_LWIP = 1 +MICROPY_PY_USSL = 1 +MICROPY_SSL_MBEDTLS = 1 +MICROPY_VFS_LFS2 = 1 + +FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/NUCLEO_H723ZG/pins.csv b/ports/stm32/boards/NUCLEO_H723ZG/pins.csv new file mode 100644 index 000000000000..450d6e432ae3 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H723ZG/pins.csv @@ -0,0 +1,130 @@ +A0,PA3 +A1,PC0 +A2,PC3 +A3,PB1 +A4,PC2 +A5,PF10 +A6,PF4 +A7,PF5 +A8,PF6 +D0,PB7 +D1,PB6 +D2,PG14 +D3,PE13 +D4,PE14 +D5,PE11 +D6,PE9 +D7,PG12 +D8,PF3 +D9,PD15 +D10,PD14 +D11,PB5 +D12,PA6 +D13,PA7 +D14,PB9 +D15,PB8 +D16,PC6 +D17,PB15 +D18,PB13 +D19,PB12 +D20,PA15 +D21,PC7 +D22,PB5 +D23,PB3 +D24,PA4 +D25,PB4 +D26,PG6 +D27,PB2 +D28,PD13 +D29,PD12 +D30,PD11 +D31,PE2 +D32,PA0 +D33,PB0 +D34,PE0 +D35,PB11 +D36,PB10 +D37,PE15 +D38,PE6 +D39,PE12 +D40,PE10 +D41,PE7 +D42,PE8 +D43,PC8 +D44,PC9 +D45,PC10 +D46,PC11 +D47,PC12 +D48,PD2 +D49,PG2 +D50,PG3 +D51,PD7 +D52,PD6 +D53,PD5 +D54,PD4 +D55,PD3 +D56,PE2 +D57,PE4 +D58,PE5 +D59,PE6 +D60,PE3 +D61,PF8 +D62,PF7 +D63,PF9 +D64,PG1 +D65,PG0 +D66,PD1 +D67,PD0 +D68,PF0 +D69,PF1 +D70,PF2 +D71,PE9 +D72,PB2 +DAC1,PA4 +DAC2,PA5 +LED1,PB0 +LED2,PE1 +LED3,PB14 +SW,PC13 +I2C1_SDA,PB9 +I2C1_SCL,PB8 +I2C2_SDA,PF0 +I2C2_SCL,PF1 +I2C4_SCL,PF14 +I2C4_SDA,PF15 +SD_D0,PC8 +SD_D1,PC9 +SD_D2,PC10 +SD_D3,PC11 +SD_CMD,PD2 +SD_CK,PC12 +SD_SW,PG2 +OTG_FS_POWER,PD10 +OTG_FS_OVER_CURRENT,PG7 +USB_VBUS,PA9 +USB_ID,PA10 +USB_DM,PA11 +USB_DP,PA12 +UART2_TX,PD5 +UART2_RX,PD6 +UART2_RTS,PD4 +UART2_CTS,PD3 +UART3_TX,PD8 +UART3_RX,PD9 +UART5_TX,PB6 +UART5_RX,PB12 +UART6_TX,PC6 +UART6_RX,PC7 +UART7_TX,PF7 +UART7_RX,PF6 +UART8_TX,PE1 +UART8_RX,PE0 +ETH_MDC,PC1 +ETH_MDIO,PA2 +ETH_RMII_REF_CLK,PA1 +ETH_RMII_CRS_DV,PA7 +ETH_RMII_RXD0,PC4 +ETH_RMII_RXD1,PC5 +ETH_RMII_TX_EN,PG11 +ETH_RMII_TXD0,PG13 +ETH_RMII_TXD1,PB13 diff --git a/ports/stm32/boards/NUCLEO_H723ZG/stm32h7xx_hal_conf.h b/ports/stm32/boards/NUCLEO_H723ZG/stm32h7xx_hal_conf.h new file mode 100644 index 000000000000..45400cdcd734 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H723ZG/stm32h7xx_hal_conf.h @@ -0,0 +1,19 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2019 Damien P. George + */ +#ifndef MICROPY_INCLUDED_STM32H7XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32H7XX_HAL_CONF_H + +#include "boards/stm32h7xx_hal_conf_base.h" + +// Oscillator values in Hz +#define HSE_VALUE (8000000) +#define LSE_VALUE (32768) +#define EXTERNAL_CLOCK_VALUE (12288000) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (5000) +#define LSE_STARTUP_TIMEOUT (5000) + +#endif // MICROPY_INCLUDED_STM32H7XX_HAL_CONF_H From d677023b3decec2f10d6b00b84b34032db4a0fd4 Mon Sep 17 00:00:00 2001 From: Jeremy Rand Date: Thu, 16 Mar 2023 21:27:05 -0400 Subject: [PATCH 029/371] extmod/vfs_posix: Do not filter '..*' in ilistdir when filtering '..'. When iterating over os.ilistdir(), the special directories '.' and '..' are filtered from the results. But the code inadvertently also filtered any file/directory which happened to match '..*'. This change fixes the filter. Fixes issue #11032. Signed-off-by: Jeremy Rand --- extmod/vfs_posix.c | 2 +- tests/extmod/vfs_posix_ilistdir_filter.py | 47 +++++++++++++++++++ tests/extmod/vfs_posix_ilistdir_filter.py.exp | 1 + 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/extmod/vfs_posix_ilistdir_filter.py create mode 100644 tests/extmod/vfs_posix_ilistdir_filter.py.exp diff --git a/extmod/vfs_posix.c b/extmod/vfs_posix.c index 0a8274373419..9b856e1f01cc 100644 --- a/extmod/vfs_posix.c +++ b/extmod/vfs_posix.c @@ -192,7 +192,7 @@ STATIC mp_obj_t vfs_posix_ilistdir_it_iternext(mp_obj_t self_in) { MP_THREAD_GIL_ENTER(); const char *fn = dirent->d_name; - if (fn[0] == '.' && (fn[1] == 0 || fn[1] == '.')) { + if (fn[0] == '.' && (fn[1] == 0 || (fn[1] == '.' && fn[2] == 0))) { // skip . and .. continue; } diff --git a/tests/extmod/vfs_posix_ilistdir_filter.py b/tests/extmod/vfs_posix_ilistdir_filter.py new file mode 100644 index 000000000000..c32d1249710d --- /dev/null +++ b/tests/extmod/vfs_posix_ilistdir_filter.py @@ -0,0 +1,47 @@ +# Test ilistdir filter of . and .. for VfsPosix. + +try: + import os + + os.VfsPosix +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +def test(testdir): + vfs = os.VfsPosix(testdir) + + dirs = [".a", "..a", "...a", "a.b", "a..b"] + + for dir in dirs: + vfs.mkdir(dir) + + dirs = [] + for entry in vfs.ilistdir("/"): + dirs.append(entry[0]) + dirs.sort() + + print(dirs) + + +# We need an empty directory for testing. +# Skip the test if it already exists. +temp_dir = "vfs_posix_ilistdir_filter_test_dir" +try: + os.stat(temp_dir) + print("SKIP") + raise SystemExit +except OSError: + pass + +os.mkdir(temp_dir) + +try: + test(temp_dir) +finally: + # Remove tempdir. + for td in os.listdir(temp_dir): + os.rmdir("/".join((temp_dir, td))) + + os.rmdir(temp_dir) diff --git a/tests/extmod/vfs_posix_ilistdir_filter.py.exp b/tests/extmod/vfs_posix_ilistdir_filter.py.exp new file mode 100644 index 000000000000..2f510738365c --- /dev/null +++ b/tests/extmod/vfs_posix_ilistdir_filter.py.exp @@ -0,0 +1 @@ +['...a', '..a', '.a', 'a..b', 'a.b'] From 5d4bfce034ace816d67081d6286185d2e35b7125 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 16 Dec 2022 17:30:26 +1100 Subject: [PATCH 030/371] py/mpstate: Add mp_thread_is_main_thread() helper macro. Signed-off-by: Damien George --- py/mpstate.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/py/mpstate.h b/py/mpstate.h index ba47c7648235..4c9380097a53 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -297,8 +297,10 @@ extern mp_state_ctx_t mp_state_ctx; #if MICROPY_PY_THREAD extern mp_state_thread_t *mp_thread_get_state(void); #define MP_STATE_THREAD(x) (mp_thread_get_state()->x) +#define mp_thread_is_main_thread() (mp_thread_get_state() == &mp_state_ctx.thread) #else #define MP_STATE_THREAD(x) MP_STATE_MAIN_THREAD(x) +#define mp_thread_is_main_thread() (true) #endif #endif // MICROPY_INCLUDED_PY_MPSTATE_H From d54208a2ff2ff8c2104597f715a586541ac6e663 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 16 Dec 2022 17:31:21 +1100 Subject: [PATCH 031/371] py/scheduler: Implement VM abort flag and mp_sched_vm_abort(). This is intended to be used by the very outer caller of the VM/runtime. It allows setting a top-level NLR handler that can be jumped to directly, in order to forcefully abort the VM/runtime. Enable using: #define MICROPY_ENABLE_VM_ABORT (1) Set up the handler at the top level using: nlr_buf_t nlr; nlr.ret_val = NULL; if (nlr_push(&nlr) == 0) { nlr_set_abort(&nlr); // call into the VM/runtime ... nlr_pop(); } else { if (nlr.ret_val == NULL) { // handle abort ... } else { // handle other exception that propagated to the top level ... } } nlr_set_abort(NULL); Schedule an abort, eg from an interrupt handler, using: mp_sched_vm_abort(); Signed-off-by: Damien George --- py/mpconfig.h | 9 +++++++++ py/mpstate.h | 5 +++++ py/nlr.c | 7 +++++++ py/nlr.h | 17 +++++++++++++++-- py/runtime.h | 3 +++ py/scheduler.c | 19 +++++++++++++++++++ py/vm.c | 4 ++++ 7 files changed, 62 insertions(+), 2 deletions(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index db9c623f49fe..80e58d1cc41f 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -909,6 +909,11 @@ typedef double mp_float_t; #define MICROPY_INTERNAL_PRINTF_PRINTER (&mp_plat_print) #endif +// Whether to support mp_sched_vm_abort to asynchronously abort to the top level. +#ifndef MICROPY_ENABLE_VM_ABORT +#define MICROPY_ENABLE_VM_ABORT (0) +#endif + // Support for internal scheduler #ifndef MICROPY_ENABLE_SCHEDULER #define MICROPY_ENABLE_SCHEDULER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) @@ -1740,6 +1745,10 @@ typedef double mp_float_t; #define MICROPY_WRAP_MP_SCHED_SCHEDULE(f) f #endif +#ifndef MICROPY_WRAP_MP_SCHED_VM_ABORT +#define MICROPY_WRAP_MP_SCHED_VM_ABORT(f) f +#endif + /*****************************************************************************/ /* Miscellaneous settings */ diff --git a/py/mpstate.h b/py/mpstate.h index 4c9380097a53..f6b911af5658 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -226,6 +226,11 @@ typedef struct _mp_state_vm_t { uint8_t sched_idx; #endif + #if MICROPY_ENABLE_VM_ABORT + bool vm_abort; + nlr_buf_t *nlr_abort; + #endif + #if MICROPY_PY_THREAD_GIL // This is a global mutex used to make the VM/runtime thread-safe. mp_thread_mutex_t gil_mutex; diff --git a/py/nlr.c b/py/nlr.c index a35e7d229c51..92db8ffb1979 100644 --- a/py/nlr.c +++ b/py/nlr.c @@ -49,3 +49,10 @@ void nlr_pop(void) { nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); *top = (*top)->prev; } + +#if MICROPY_ENABLE_VM_ABORT +NORETURN void nlr_jump_abort(void) { + MP_STATE_THREAD(nlr_top) = MP_STATE_VM(nlr_abort); + nlr_jump(NULL); +} +#endif diff --git a/py/nlr.h b/py/nlr.h index 338a67570409..09ef66ef7202 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -101,9 +101,16 @@ typedef struct _nlr_buf_t nlr_buf_t; struct _nlr_buf_t { - // the entries here must all be machine word size + // The entries in this struct must all be machine word size. + + // Pointer to the previous nlr_buf_t in the chain. + // Or NULL if it's the top-level one. nlr_buf_t *prev; - void *ret_val; // always a concrete object (an exception instance) + + // The exception that is being raised: + // - NULL means the jump is because of a VM abort (only if MICROPY_ENABLE_VM_ABORT enabled) + // - otherwise it's always a concrete object (an exception instance) + void *ret_val; #if MICROPY_NLR_SETJMP jmp_buf jmpbuf; @@ -149,6 +156,12 @@ unsigned int nlr_push_tail(nlr_buf_t *top); void nlr_pop(void); NORETURN void nlr_jump(void *val); +#if MICROPY_ENABLE_VM_ABORT +#define nlr_set_abort(buf) MP_STATE_VM(nlr_abort) = buf +#define nlr_get_abort() MP_STATE_VM(nlr_abort) +NORETURN void nlr_jump_abort(void); +#endif + // This must be implemented by a port. It's called by nlr_jump // if no nlr buf has been pushed. It must not return, but rather // should bail out with a fatal error. diff --git a/py/runtime.h b/py/runtime.h index 36b3caa6c73e..d57c25c92c8c 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -75,6 +75,9 @@ void mp_deinit(void); void mp_sched_exception(mp_obj_t exc); void mp_sched_keyboard_interrupt(void); +#if MICROPY_ENABLE_VM_ABORT +void mp_sched_vm_abort(void); +#endif void mp_handle_pending(bool raise_exc); #if MICROPY_ENABLE_SCHEDULER diff --git a/py/scheduler.c b/py/scheduler.c index db090b09911c..165b26dc8652 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -51,6 +51,12 @@ void MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(mp_sched_keyboard_interrupt)(void) } #endif +#if MICROPY_ENABLE_VM_ABORT +void MICROPY_WRAP_MP_SCHED_VM_ABORT(mp_sched_vm_abort)(void) { + MP_STATE_VM(vm_abort) = true; +} +#endif + #if MICROPY_ENABLE_SCHEDULER #define IDX_MASK(i) ((i) & (MICROPY_SCHEDULER_DEPTH - 1)) @@ -203,6 +209,17 @@ MP_REGISTER_ROOT_POINTER(mp_sched_item_t sched_queue[MICROPY_SCHEDULER_DEPTH]); // Called periodically from the VM or from "waiting" code (e.g. sleep) to // process background tasks and pending exceptions (e.g. KeyboardInterrupt). void mp_handle_pending(bool raise_exc) { + // Handle pending VM abort. + #if MICROPY_ENABLE_VM_ABORT + if (MP_STATE_VM(vm_abort) && mp_thread_is_main_thread()) { + MP_STATE_VM(vm_abort) = false; + if (raise_exc && nlr_get_abort() != NULL) { + nlr_jump_abort(); + } + } + #endif + + // Handle any pending exception. if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); @@ -215,6 +232,8 @@ void mp_handle_pending(bool raise_exc) { } MICROPY_END_ATOMIC_SECTION(atomic_state); } + + // Handle any pending callbacks. #if MICROPY_ENABLE_SCHEDULER if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { mp_sched_run_pending(); diff --git a/py/vm.c b/py/vm.c index 9273dda02eea..385d13ee4097 100644 --- a/py/vm.c +++ b/py/vm.c @@ -1333,6 +1333,10 @@ unwind_jump:; // No scheduler: Just check pending exception. MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL #endif + #if MICROPY_ENABLE_VM_ABORT + // Check if the VM should abort execution. + || MP_STATE_VM(vm_abort) + #endif ) { MARK_EXC_IP_SELECTIVE(); mp_handle_pending(true); From b5ceb9d5773d04a69dc92cb56e8e6b97043409b3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 22 Mar 2023 15:17:55 +1100 Subject: [PATCH 032/371] tools/pyboard.py: Fix joining of path in filesystem_command. This was broken by 5327cd1021dc92cad428ff44cb114c4a94c0bc45 Signed-off-by: Damien George --- tools/pyboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pyboard.py b/tools/pyboard.py index 9e0b0f18ebd5..29af27f02ba2 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -671,7 +671,7 @@ def fname_cp_dest(src, dest): if dest is None or dest == "": dest = src elif dest == ".": - dest = "/".join(".", src) + dest = "./" + src elif dest.endswith("/"): dest += src return dest From b7ea90d4cbcc3c718d2bc7d68015cb220cf50897 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 8 Mar 2023 20:59:24 +0100 Subject: [PATCH 033/371] rp2/machine_uart: Fix setting of UART LCR parameters. Prior to this change, setting of UART parameters like parity, stop bits or data bits did not work correctly. As suggested by @iabdalkader, adding __DSB() fixes the problem, making sure that changes to the UART LCR_H register are seen by the peripheral. Note: the FIFO is already enabled in the call to uart_init(), so the call to uart_set_fifo_enabled() is not required, but kept for visibility. Fixes issue #10976. --- ports/rp2/machine_uart.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index e4881cd4f117..09870e78f0d7 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -336,7 +336,9 @@ STATIC void machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, co uart_init(self->uart, self->baudrate); uart_set_format(self->uart, self->bits, self->stop, self->parity); + __DSB(); // make sure UARTLCR_H register is written to uart_set_fifo_enabled(self->uart, true); + __DSB(); // make sure UARTLCR_H register is written to gpio_set_function(self->tx, GPIO_FUNC_UART); gpio_set_function(self->rx, GPIO_FUNC_UART); if (self->invert & UART_INVERT_RX) { From c7923b113905b3a2b4f51aa4bde017c42453cf7f Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 21 Mar 2023 17:19:27 +1100 Subject: [PATCH 034/371] stm32: Add support for USB on G0 MCUs. Signed-off-by: Damien George --- ports/stm32/boards/stm32g0xx_hal_conf_base.h | 58 ++++++++++---------- ports/stm32/powerctrlboot.c | 11 ++-- ports/stm32/stm32_it.c | 10 +++- ports/stm32/usb.c | 2 +- ports/stm32/usbd_cdc_interface.c | 13 ++++- ports/stm32/usbd_conf.c | 41 ++++++++++++-- 6 files changed, 91 insertions(+), 44 deletions(-) diff --git a/ports/stm32/boards/stm32g0xx_hal_conf_base.h b/ports/stm32/boards/stm32g0xx_hal_conf_base.h index fc49ca945f3f..5ddcb0fa434b 100644 --- a/ports/stm32/boards/stm32g0xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32g0xx_hal_conf_base.h @@ -27,16 +27,42 @@ #ifndef MICROPY_INCLUDED_STM32G0XX_HAL_CONF_BASE_H #define MICROPY_INCLUDED_STM32G0XX_HAL_CONF_BASE_H +// Enable various HAL modules +#define HAL_MODULE_ENABLED +#define HAL_ADC_MODULE_ENABLED +#define HAL_CORTEX_MODULE_ENABLED +#define HAL_DMA_MODULE_ENABLED +#define HAL_EXTI_MODULE_ENABLED +#define HAL_FLASH_MODULE_ENABLED +#define HAL_GPIO_MODULE_ENABLED +#define HAL_I2C_MODULE_ENABLED +#define HAL_PCD_MODULE_ENABLED +#define HAL_PWR_MODULE_ENABLED +#define HAL_RCC_MODULE_ENABLED +#define HAL_RTC_MODULE_ENABLED +#define HAL_SPI_MODULE_ENABLED +#define HAL_TIM_MODULE_ENABLED +#define HAL_UART_MODULE_ENABLED +#define HAL_USART_MODULE_ENABLED + // Oscillator values in Hz -// These must come before the HAL headers because stm32g0xx_ll_rcc.h will define HSI_VALUE unless already defined #define HSI_VALUE (16000000) #define LSI_VALUE (32000) #if defined(STM32G0C1xx) || defined(STM32G0B1xx) || defined(STM32G0B0xx) #define HSI48_VALUE 48000000 #endif -// Include various HAL modules for convenience +// SysTick has the highest priority +#define TICK_INT_PRIORITY (0x00) + +// Miscellaneous HAL settings +#define USE_RTOS 0 +#define PREFETCH_ENABLE 1 +#define INSTRUCTION_CACHE_ENABLE 1 +#define USE_SPI_CRC 1 +#define USE_HAL_CRYP_SUSPEND_RESUME 1 +// Include various HAL modules for convenience #include "stm32g0xx_hal_rcc.h" #include "stm32g0xx_hal_gpio.h" #include "stm32g0xx_hal_dma.h" @@ -68,38 +94,10 @@ #include "stm32g0xx_hal_uart.h" #include "stm32g0xx_hal_usart.h" #include "stm32g0xx_hal_wwdg.h" - #include "stm32g0xx_ll_lpuart.h" #include "stm32g0xx_ll_rtc.h" #include "stm32g0xx_ll_usart.h" -// Enable various HAL modules -#define HAL_MODULE_ENABLED -#define HAL_ADC_MODULE_ENABLED -#define HAL_CORTEX_MODULE_ENABLED -#define HAL_DMA_MODULE_ENABLED -#define HAL_EXTI_MODULE_ENABLED -#define HAL_FLASH_MODULE_ENABLED -#define HAL_GPIO_MODULE_ENABLED -#define HAL_I2C_MODULE_ENABLED -#define HAL_PWR_MODULE_ENABLED -#define HAL_RCC_MODULE_ENABLED -#define HAL_RTC_MODULE_ENABLED -#define HAL_SPI_MODULE_ENABLED -#define HAL_TIM_MODULE_ENABLED -#define HAL_UART_MODULE_ENABLED -#define HAL_USART_MODULE_ENABLED - -// SysTick has the highest priority -#define TICK_INT_PRIORITY (0x00) - -// Miscellaneous HAL settings -#define USE_RTOS 0 -#define PREFETCH_ENABLE 1 -#define INSTRUCTION_CACHE_ENABLE 1 -#define USE_SPI_CRC 1 -#define USE_HAL_CRYP_SUSPEND_RESUME 1 - // HAL parameter assertions are disabled #define assert_param(expr) ((void)0) diff --git a/ports/stm32/powerctrlboot.c b/ports/stm32/powerctrlboot.c index 912c8633ab6d..e970b81a9fcc 100644 --- a/ports/stm32/powerctrlboot.c +++ b/ports/stm32/powerctrlboot.c @@ -173,15 +173,14 @@ void SystemClock_Config(void) { #if MICROPY_HW_ENABLE_RNG || MICROPY_HW_ENABLE_USB // Enable the 48MHz internal oscillator - RCC->CRRCR |= RCC_CRRCR_HSI48ON; - RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; - SYSCFG->CFGR3 |= SYSCFG_CFGR3_ENREF_HSI48; - while (!(RCC->CRRCR & RCC_CRRCR_HSI48RDY)) { + RCC->CR |= RCC_CR_HSI48ON; + RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN; + while (!(RCC->CR & RCC_CR_HSI48RDY)) { // Wait for HSI48 to be ready } - // Select RC48 as HSI48 for USB and RNG - RCC->CCIPR |= RCC_CCIPR_HSI48SEL; + // Select HSI48 for USB + RCC->CCIPR2 &= ~(3 << RCC_CCIPR2_USBSEL_Pos); #if MICROPY_HW_ENABLE_USB // Synchronise HSI48 with 1kHz USB SoF diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c index c2604992186f..ea9873094f1c 100644 --- a/ports/stm32/stm32_it.c +++ b/ports/stm32/stm32_it.c @@ -296,7 +296,15 @@ void DebugMon_Handler(void) { /* file (startup_stm32f4xx.s). */ /******************************************************************************/ -#if defined(STM32L0) || defined(STM32L432xx) +#if defined(STM32G0) + +#if MICROPY_HW_USB_FS +void USB_UCPD1_2_IRQHandler(void) { + HAL_PCD_IRQHandler(&pcd_fs_handle); +} +#endif + +#elif defined(STM32L0) || defined(STM32L432xx) #if MICROPY_HW_USB_FS void USB_IRQHandler(void) { diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index 12c5e497de84..87306075a9af 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -67,7 +67,7 @@ #define MAX_ENDPOINT(dev_id) ((dev_id) == USB_PHY_FS_ID ? 3 : 5) #elif defined(STM32F7) #define MAX_ENDPOINT(dev_id) ((dev_id) == USB_PHY_FS_ID ? 5 : 8) -#elif defined(STM32H7) +#elif defined(STM32G0) || defined(STM32H7) #define MAX_ENDPOINT(dev_id) (8) #endif diff --git a/ports/stm32/usbd_cdc_interface.c b/ports/stm32/usbd_cdc_interface.c index 474aced7abf2..5432370a7468 100644 --- a/ports/stm32/usbd_cdc_interface.c +++ b/ports/stm32/usbd_cdc_interface.c @@ -50,6 +50,15 @@ #if MICROPY_HW_ENABLE_USB +#if !MICROPY_HW_USB_IS_MULTI_OTG +#define USE_USB_CNTR_SOFM (1) +#elif defined(STM32G0) +#define USE_USB_CNTR_SOFM (1) +#define USB USB_DRD_FS +#else +#define USE_USB_CNTR_SOFM (0) +#endif + // CDC control commands #define CDC_SEND_ENCAPSULATED_COMMAND 0x00 #define CDC_GET_ENCAPSULATED_RESPONSE 0x01 @@ -152,7 +161,7 @@ int8_t usbd_cdc_control(usbd_cdc_state_t *cdc_in, uint8_t cmd, uint8_t *pbuf, ui // configure its serial port (in most cases to disable local echo) cdc->connect_state = USBD_CDC_CONNECT_STATE_CONNECTING; usbd_cdc_connect_tx_timer = 8; // wait for 8 SOF IRQs - #if !MICROPY_HW_USB_IS_MULTI_OTG + #if USE_USB_CNTR_SOFM USB->CNTR |= USB_CNTR_SOFM; #else PCD_HandleTypeDef *hpcd = cdc->base.usbd->pdev->pData; @@ -263,7 +272,7 @@ void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) { --usbd_cdc_connect_tx_timer; } else { usbd_cdc_msc_hid_state_t *usbd = ((USBD_HandleTypeDef *)hpcd->pData)->pClassData; - #if !MICROPY_HW_USB_IS_MULTI_OTG + #if USE_USB_CNTR_SOFM USB->CNTR &= ~USB_CNTR_SOFM; #else hpcd->Instance->GINTMSK &= ~USB_OTG_GINTMSK_SOFM; diff --git a/ports/stm32/usbd_conf.c b/ports/stm32/usbd_conf.c index b6880da2d618..3891d09ce169 100644 --- a/ports/stm32/usbd_conf.c +++ b/ports/stm32/usbd_conf.c @@ -49,6 +49,10 @@ PCD_HandleTypeDef pcd_hs_handle; #define USB_OTG_FS USB #endif +#if defined(STM32G0) +#define USB_OTG_FS USB_DRD_FS +#endif + /******************************************************************************* PCD BSP Routines *******************************************************************************/ @@ -61,6 +65,22 @@ PCD_HandleTypeDef pcd_hs_handle; void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { #if MICROPY_HW_USB_FS if (hpcd->Instance == USB_OTG_FS) { + // Configure USB GPIO's. + + #if defined(STM32G0) + + // These MCUs don't have an alternate function for USB but rather require + // the pins to be disconnected from all peripherals, ie put in analog mode. + + mp_hal_pin_config(pin_A11, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); + mp_hal_pin_config_speed(pin_A11, GPIO_SPEED_FREQ_VERY_HIGH); + mp_hal_pin_config(pin_A12, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); + mp_hal_pin_config_speed(pin_A12, GPIO_SPEED_FREQ_VERY_HIGH); + + #else + + // Other MCUs have an alternate function for GPIO's to be in USB mode. + #if defined(STM32H7) const uint32_t otg_alt = GPIO_AF10_OTG1_FS; #elif defined(STM32L0) @@ -78,6 +98,8 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { mp_hal_pin_config(pin_A12, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, otg_alt); mp_hal_pin_config_speed(pin_A12, GPIO_SPEED_FREQ_VERY_HIGH); + #endif + #if defined(MICROPY_HW_USB_VBUS_DETECT_PIN) // USB VBUS detect pin is always A9 mp_hal_pin_config(MICROPY_HW_USB_VBUS_DETECT_PIN, MP_HAL_PIN_MODE_INPUT, MP_HAL_PIN_PULL_NONE, 0); @@ -88,14 +110,16 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { mp_hal_pin_config(MICROPY_HW_USB_OTG_ID_PIN, MP_HAL_PIN_MODE_ALT_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, otg_alt); #endif - #if defined(STM32H7) + #if defined(STM32G0) + __HAL_RCC_USB_CLK_SLEEP_ENABLE(); + #elif defined(STM32H7) // Keep USB clock running during sleep or else __WFI() will disable the USB __HAL_RCC_USB2_OTG_FS_CLK_SLEEP_ENABLE(); __HAL_RCC_USB2_OTG_FS_ULPI_CLK_SLEEP_DISABLE(); #endif // Enable USB FS Clocks - #if !MICROPY_HW_USB_IS_MULTI_OTG + #if !MICROPY_HW_USB_IS_MULTI_OTG || defined(STM32G0) __HAL_RCC_USB_CLK_ENABLE(); #else __USB_OTG_FS_CLK_ENABLE(); @@ -113,7 +137,10 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { #endif // Configure and enable USB FS interrupt - #if defined(STM32L0) + #if defined(STM32G0) + NVIC_SetPriority(USB_UCPD1_2_IRQn, IRQ_PRI_OTG_FS); + HAL_NVIC_EnableIRQ(USB_UCPD1_2_IRQn); + #elif defined(STM32L0) NVIC_SetPriority(USB_IRQn, IRQ_PRI_OTG_FS); HAL_NVIC_EnableIRQ(USB_IRQn); #elif defined(STM32L432xx) @@ -235,7 +262,11 @@ void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd) { #if MICROPY_HW_USB_FS if (hpcd->Instance == USB_OTG_FS) { /* Disable USB FS Clocks */ + #if defined(STM32G0) + __HAL_RCC_USB_CLK_DISABLE(); + #else __USB_OTG_FS_CLK_DISABLE(); + #endif return; } #endif @@ -413,7 +444,9 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev, int high_speed, const pcd_fs_handle.Init.lpm_enable = DISABLE; pcd_fs_handle.Init.battery_charging_enable = DISABLE; #if MICROPY_HW_USB_IS_MULTI_OTG + #if !defined(STM32G0) pcd_fs_handle.Init.use_dedicated_ep1 = 0; + #endif pcd_fs_handle.Init.dma_enable = 0; #if !defined(MICROPY_HW_USB_VBUS_DETECT_PIN) pcd_fs_handle.Init.vbus_sensing_enable = 0; // No VBUS Sensing on USB0 @@ -430,7 +463,7 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev, int high_speed, const HAL_PCD_Init(&pcd_fs_handle); // Set FIFO buffer sizes - #if !MICROPY_HW_USB_IS_MULTI_OTG + #if !MICROPY_HW_USB_IS_MULTI_OTG || defined(STM32G0) uint32_t fifo_offset = USBD_PMA_RESERVE; // need to reserve some data at start of FIFO for (size_t i = 0; i < USBD_PMA_NUM_FIFO; ++i) { uint16_t ep_addr = ((i & 1) * 0x80) | (i >> 1); From 31638473b7205b37fe0095ae9181e37da28fe6d7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 21 Mar 2023 17:43:11 +1100 Subject: [PATCH 035/371] stm32/mboot: Add support for G0 MCUs. Signed-off-by: Damien George --- ports/stm32/boards/stm32g0b1xe.ld | 1 + ports/stm32/i2cslave.h | 4 ++++ ports/stm32/mboot/Makefile | 7 ++++++- ports/stm32/mboot/main.c | 19 +++++++++++++++++-- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/ports/stm32/boards/stm32g0b1xe.ld b/ports/stm32/boards/stm32g0b1xe.ld index 4fe61a472ff7..8ec81e5bf9bb 100644 --- a/ports/stm32/boards/stm32g0b1xe.ld +++ b/ports/stm32/boards/stm32g0b1xe.ld @@ -3,6 +3,7 @@ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 352K + FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 320K FLASH_FS (rx) : ORIGIN = 0x08058000, LENGTH = 160K /* starting @ 352K */ } diff --git a/ports/stm32/i2cslave.h b/ports/stm32/i2cslave.h index 4a51bf378e92..3f9022aa70b7 100644 --- a/ports/stm32/i2cslave.h +++ b/ports/stm32/i2cslave.h @@ -43,6 +43,10 @@ static inline void i2c_slave_init(i2c_slave_t *i2c, int irqn, int irq_pri, int a RCC->APB1ENR |= 1 << (RCC_APB1ENR_I2C1EN_Pos + i2c_idx); volatile uint32_t tmp = RCC->APB1ENR; // Delay after enabling clock (void)tmp; + #elif defined(STM32G0) + RCC->APBENR1 |= 1 << (RCC_APBENR1_I2C1EN_Pos + i2c_idx); + volatile uint32_t tmp = RCC->APBENR1; // Delay after enabling clock + (void)tmp; #elif defined(STM32H7) RCC->APB1LENR |= 1 << (RCC_APB1LENR_I2C1EN_Pos + i2c_idx); volatile uint32_t tmp = RCC->APB1LENR; // Delay after enabling clock diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 053b317e17c3..4e3f7597f7af 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -134,7 +134,12 @@ SRC_C += \ SRC_O += \ $(STARTUP_FILE) \ $(SYSTEM_FILE) \ - ports/stm32/resethandler.o \ + +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 g0 l0)) +SRC_O += ports/stm32/resethandler_m0.o +else +SRC_O += ports/stm32/resethandler.o +endif ifeq ($(MBOOT_ENABLE_PACKING), 1) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 59106beb76bd..e2183f9c7237 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -371,6 +371,9 @@ void SystemClock_Config(void) { #if defined(STM32F4) || defined(STM32F7) #define AHBxENR AHB1ENR #define AHBxENR_GPIOAEN_Pos RCC_AHB1ENR_GPIOAEN_Pos +#elif defined(STM32G0) +#define AHBxENR IOPENR +#define AHBxENR_GPIOAEN_Pos RCC_IOPENR_GPIOAEN_Pos #elif defined(STM32H7) #define AHBxENR AHB4ENR #define AHBxENR_GPIOAEN_Pos RCC_AHB4ENR_GPIOAEN_Pos @@ -406,7 +409,9 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed) { /******************************************************************************/ // FLASH -#if defined(STM32WB) +#if defined(STM32G0) +#define FLASH_END (FLASH_BASE + FLASH_SIZE - 1) +#elif defined(STM32WB) #define FLASH_END FLASH_END_ADDR #endif @@ -426,6 +431,8 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed) { #define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT #elif defined(STM32F765xx) || defined(STM32F767xx) || defined(STM32F769xx) #define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/04*032Kg,01*128Kg,07*256Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT +#elif defined(STM32G0) +#define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/256*02Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT #elif defined(STM32H743xx) #define FLASH_LAYOUT_STR "@Internal Flash /0x08000000/16*128Kg" MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT #elif defined(STM32H750xx) @@ -1349,7 +1356,9 @@ void stm32_main(uint32_t initial_r0) { #endif #endif + #if __CORTEX_M >= 0x03 NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); + #endif #if USE_CACHE && defined(STM32F7) SCB_EnableICache(); @@ -1547,7 +1556,13 @@ void I2Cx_EV_IRQHandler(void) { #if !USE_USB_POLLING -#if defined(STM32WB) +#if defined(STM32G0) + +void USB_UCPD1_2_IRQHandler(void) { + HAL_PCD_IRQHandler(&pcd_fs_handle); +} + +#elif defined(STM32WB) void USB_LP_IRQHandler(void) { HAL_PCD_IRQHandler(&pcd_fs_handle); From 31e7a0587d7eabe668101d26b26a0190120465c8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 21 Mar 2023 17:43:20 +1100 Subject: [PATCH 036/371] stm32/boards/NUCLEO_G0B1RE: Add config for USB and mboot. But leave these disabled. Signed-off-by: Damien George --- ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.h | 7 +++++++ ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.mk | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.h b/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.h index 6ba3e0115c9d..092ee177925e 100644 --- a/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.h @@ -6,6 +6,7 @@ #define MICROPY_HW_ENABLE_RNG (0) #define MICROPY_HW_ENABLE_RTC (1) #define MICROPY_HW_ENABLE_DAC (0) +#define MICROPY_HW_ENABLE_USB (0) // can be enabled if USB cable connected to PA11/PA12 #define MICROPY_PY_PYB_LEGACY (0) #define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1) @@ -87,3 +88,9 @@ #define MICROPY_HW_LED1 (pin_A5) // Green LD2 LED on Nucleo #define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) #define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) + +// USB config +#define MICROPY_HW_USB_FS (1) +#define MICROPY_HW_USB_MAIN_DEV (USB_PHY_FS_ID) +#define MICROPY_HW_USB_MSC (0) +#define MICROPY_HW_USB_HID (0) diff --git a/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.mk index b8bbfb453439..abc9b43ef036 100644 --- a/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_G0B1RE/mpconfigboard.mk @@ -1,7 +1,13 @@ MCU_SERIES = g0 CMSIS_MCU = STM32G0B1xx AF_FILE = boards/stm32g0b1_af.csv + +ifeq ($(USE_MBOOT),1) +LD_FILES = boards/stm32g0b1xe.ld boards/common_bl.ld +TEXT0_ADDR = 0x08008000 +else LD_FILES = boards/stm32g0b1xe.ld boards/common_basic.ld +endif # LTO reduces final binary size, may be slower to build depending on gcc version and hardware LTO ?= 1 From af426348661d2b6c3f6bbbdf6ae493f505ddee01 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 17 Mar 2023 13:55:35 +1100 Subject: [PATCH 037/371] tests/run-multitests.py: Support outputting test metrics. If a multitest calls `multitest.output_metric(...)` then that output will be collected separately, not considered as part of the test verification output, and instead be printed at the end. This is useful for tests that want to output performance/timing metrics that may change from one run to the next. Signed-off-by: Damien George --- tests/run-multitests.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/run-multitests.py b/tests/run-multitests.py index b316ce69f6e6..81db31ea94e8 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -93,6 +93,9 @@ def get_network_ip(): @staticmethod def expect_reboot(resume, delay_ms=0): print("WAIT_FOR_REBOOT", resume, delay_ms) + @staticmethod + def output_metric(data): + print("OUTPUT_METRIC", data) {} @@ -312,6 +315,7 @@ def run_test_on_instances(test_file, num_instances, instances): skip = False injected_globals = "" output = [[] for _ in range(num_instances)] + output_metrics = [] # If the test calls get_network_ip() then inject HOST_IP so that devices can know # the IP address of the host. Do this lazily to not require a TCP/IP connection @@ -400,6 +404,8 @@ def run_test_on_instances(test_file, num_instances, instances): for instance2 in instances: if instance2 is not instance: instance2.write(bytes(out, "ascii") + b"\r\n") + elif out.startswith("OUTPUT_METRIC "): + output_metrics.append(out.split(" ", 1)[1]) else: output[idx].append(out) if err is not None: @@ -421,7 +427,7 @@ def run_test_on_instances(test_file, num_instances, instances): output_str += "--- instance{} ---\n".format(idx) output_str += "\n".join(lines) + "\n" - return error, skip, output_str + return error, skip, output_str, output_metrics def wait_for_reboot(instance, extra_timeout_ms=0): @@ -481,7 +487,9 @@ def run_tests(test_files, instances_truth, instances_test): sys.stdout.flush() # Run test on test instances - error, skip, output_test = run_test_on_instances(test_file, num_instances, instances_test) + error, skip, output_test, output_metrics = run_test_on_instances( + test_file, num_instances, instances_test + ) if not skip: # Check if truth exists in a file, and read it in @@ -491,7 +499,7 @@ def run_tests(test_files, instances_truth, instances_test): output_truth = f.read() else: # Run test on truth instances to get expected output - _, _, output_truth = run_test_on_instances( + _, _, output_truth, _ = run_test_on_instances( test_file, num_instances, instances_truth ) @@ -520,6 +528,11 @@ def run_tests(test_files, instances_truth, instances_test): print("### DIFF ###") print_diff(output_truth, output_test) + # Print test output metrics, if there are any. + if output_metrics: + for metric in output_metrics: + print(test_file, ": ", metric, sep="") + if cmd_args.show_output: print() From 6c7624896063e58f67d732b5f080e96f36bc5eda Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 17 Mar 2023 13:57:42 +1100 Subject: [PATCH 038/371] tests/multi_bluetooth: Use multitest.output_metric in BLE perf tests. Signed-off-by: Damien George --- tests/multi_bluetooth/perf_gatt_char_write.py | 2 +- tests/multi_bluetooth/perf_gatt_notify.py | 2 +- tests/multi_bluetooth/perf_gatt_notify.py.exp | 5 +++++ tests/multi_bluetooth/perf_l2cap.py | 2 +- tests/multi_bluetooth/perf_l2cap.py.exp | 4 ++++ 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/multi_bluetooth/perf_gatt_char_write.py b/tests/multi_bluetooth/perf_gatt_char_write.py index 00607f009012..681fc052c6be 100644 --- a/tests/multi_bluetooth/perf_gatt_char_write.py +++ b/tests/multi_bluetooth/perf_gatt_char_write.py @@ -128,7 +128,7 @@ def instance1(): ticks_end = time.ticks_ms() ticks_total = time.ticks_diff(ticks_end, ticks_start) - print( + multitest.output_metric( "Did {} writes in {} ms. {} ms/write, {} bytes/sec".format( _NUM_NOTIFICATIONS, ticks_total, diff --git a/tests/multi_bluetooth/perf_gatt_notify.py b/tests/multi_bluetooth/perf_gatt_notify.py index 88986dda3d9b..8acccc6470d6 100644 --- a/tests/multi_bluetooth/perf_gatt_notify.py +++ b/tests/multi_bluetooth/perf_gatt_notify.py @@ -90,7 +90,7 @@ def instance0(): ticks_end = time.ticks_ms() ticks_total = time.ticks_diff(ticks_end, ticks_start) - print( + multitest.output_metric( "Acknowledged {} notifications in {} ms. {} ms/notification.".format( _NUM_NOTIFICATIONS, ticks_total, ticks_total // _NUM_NOTIFICATIONS ) diff --git a/tests/multi_bluetooth/perf_gatt_notify.py.exp b/tests/multi_bluetooth/perf_gatt_notify.py.exp index e69de29bb2d1..5a4385530e60 100644 --- a/tests/multi_bluetooth/perf_gatt_notify.py.exp +++ b/tests/multi_bluetooth/perf_gatt_notify.py.exp @@ -0,0 +1,5 @@ +--- instance0 --- +gap_advertise +gap_disconnect: True +--- instance1 --- +gap_connect diff --git a/tests/multi_bluetooth/perf_l2cap.py b/tests/multi_bluetooth/perf_l2cap.py index 0603a02a81dc..a9538542330b 100644 --- a/tests/multi_bluetooth/perf_l2cap.py +++ b/tests/multi_bluetooth/perf_l2cap.py @@ -134,7 +134,7 @@ def instance1(): ble.l2cap_disconnect(conn_handle, cid) wait_for_event(_IRQ_L2CAP_DISCONNECT, TIMEOUT_MS) - print( + multitest.output_metric( "Received {}/{} bytes in {} ms. {} B/s".format( recv_bytes, recv_correct, total_ticks, recv_bytes * 1000 // total_ticks ) diff --git a/tests/multi_bluetooth/perf_l2cap.py.exp b/tests/multi_bluetooth/perf_l2cap.py.exp index e69de29bb2d1..5e4b7a75578c 100644 --- a/tests/multi_bluetooth/perf_l2cap.py.exp +++ b/tests/multi_bluetooth/perf_l2cap.py.exp @@ -0,0 +1,4 @@ +--- instance0 --- + +--- instance1 --- + From 38e7b842c6bc8122753cbf0845eb141f28fbcb72 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 22 Mar 2023 00:14:46 +1100 Subject: [PATCH 039/371] ports: Implement simple write polling for stdout. This is a best-effort implementation of write polling. It's difficult to do correctly because if there are multiple output streams (eg UART and USB CDC) then some may not be writeable while others are. A full solution should also have a return value from mp_hal_stdout_tx_strn(), returning the number of bytes written to the stream(s). That's also hard to define. The renesas-ra and stm32 ports already implement a similar best-effort mechanism for write polling. Fixes issue #11026. Signed-off-by: Damien George --- ports/esp32/mphalport.c | 3 +++ ports/esp8266/esp_mphal.c | 3 +++ ports/mimxrt/mphalport.c | 3 +++ ports/nrf/drivers/bluetooth/ble_uart.c | 3 +++ ports/nrf/drivers/usb/usb_cdc.c | 3 +++ ports/nrf/mphalport.c | 3 +++ ports/pic16bit/pic16bit_mphal.c | 3 +++ ports/rp2/mphalport.c | 9 +++++++++ ports/samd/mphalport.c | 4 ++++ ports/teensy/teensy_hal.c | 5 +++++ 10 files changed, 39 insertions(+) diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index 2aaeb9755f97..b187b422d50b 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -88,6 +88,9 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { if ((poll_flags & MP_STREAM_POLL_RD) && stdin_ringbuf.iget != stdin_ringbuf.iput) { ret |= MP_STREAM_POLL_RD; } + if (poll_flags & MP_STREAM_POLL_WR) { + ret |= MP_STREAM_POLL_WR; + } return ret; } diff --git a/ports/esp8266/esp_mphal.c b/ports/esp8266/esp_mphal.c index 3cb4807333c4..219e61484126 100644 --- a/ports/esp8266/esp_mphal.c +++ b/ports/esp8266/esp_mphal.c @@ -62,6 +62,9 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { if ((poll_flags & MP_STREAM_POLL_RD) && stdin_ringbuf.iget != stdin_ringbuf.iput) { ret |= MP_STREAM_POLL_RD; } + if (poll_flags & MP_STREAM_POLL_WR) { + ret |= mp_uos_dupterm_poll(poll_flags); + } return ret; } diff --git a/ports/mimxrt/mphalport.c b/ports/mimxrt/mphalport.c index 2730b9a855d3..beb471ab801e 100644 --- a/ports/mimxrt/mphalport.c +++ b/ports/mimxrt/mphalport.c @@ -86,6 +86,9 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) { ret |= MP_STREAM_POLL_RD; } + if ((poll_flags & MP_STREAM_POLL_WR) && tud_cdc_connected() && tud_cdc_write_available() > 0) { + ret |= MP_STREAM_POLL_WR; + } #if MICROPY_PY_OS_DUPTERM ret |= mp_uos_dupterm_poll(poll_flags); #endif diff --git a/ports/nrf/drivers/bluetooth/ble_uart.c b/ports/nrf/drivers/bluetooth/ble_uart.c index 3020f1af6267..320657370335 100644 --- a/ports/nrf/drivers/bluetooth/ble_uart.c +++ b/ports/nrf/drivers/bluetooth/ble_uart.c @@ -164,6 +164,9 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { && !isBufferEmpty(mp_rx_ring_buffer)) { ret |= MP_STREAM_POLL_RD; } + if ((poll_flags & MP_STREAM_POLL_WR) && ble_uart_enabled()) { + ret |= MP_STREAM_POLL_WR; + } return ret; } #endif diff --git a/ports/nrf/drivers/usb/usb_cdc.c b/ports/nrf/drivers/usb/usb_cdc.c index 16d69fff66a3..e33a42548271 100644 --- a/ports/nrf/drivers/usb/usb_cdc.c +++ b/ports/nrf/drivers/usb/usb_cdc.c @@ -213,6 +213,9 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { ret |= MP_STREAM_POLL_RD; } } + if (poll_flags & MP_STREAM_POLL_WR) { + ret |= MP_STREAM_POLL_WR; + } return ret; } diff --git a/ports/nrf/mphalport.c b/ports/nrf/mphalport.c index 9405cb2e3abd..a2eec7fceb1d 100644 --- a/ports/nrf/mphalport.c +++ b/ports/nrf/mphalport.c @@ -179,6 +179,9 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { && uart_rx_any(MP_STATE_PORT(board_stdio_uart))) { ret |= MP_STREAM_POLL_RD; } + if ((poll_flags & MP_STREAM_POLL_WR) && MP_STATE_PORT(board_stdio_uart) != NULL) { + ret |= MP_STREAM_POLL_WR; + } return ret; } diff --git a/ports/pic16bit/pic16bit_mphal.c b/ports/pic16bit/pic16bit_mphal.c index 48e8af87decb..b2c80ba1be78 100644 --- a/ports/pic16bit/pic16bit_mphal.c +++ b/ports/pic16bit/pic16bit_mphal.c @@ -57,6 +57,9 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { if ((poll_flags & MP_STREAM_POLL_RD) && uart_rx_any()) { ret |= MP_STREAM_POLL_RD; } + if (poll_flags & MP_STREAM_POLL_WR) { + ret |= MP_STREAM_POLL_WR; + } return ret; } diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index 510d486d96a5..f56c2bda1362 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -98,6 +98,15 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) { ret |= MP_STREAM_POLL_RD; } + if (poll_flags & MP_STREAM_POLL_WR) { + #if MICROPY_HW_ENABLE_UART_REPL + ret |= MP_STREAM_POLL_WR; + #else + if (tud_cdc_connected() && tud_cdc_write_available() > 0) { + ret |= MP_STREAM_POLL_WR; + } + #endif + } #endif #if MICROPY_PY_OS_DUPTERM ret |= mp_uos_dupterm_poll(poll_flags); diff --git a/ports/samd/mphalport.c b/ports/samd/mphalport.c index d7511e61fbf5..3dc6b70a8d9e 100644 --- a/ports/samd/mphalport.c +++ b/ports/samd/mphalport.c @@ -162,6 +162,10 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { ret |= MP_STREAM_POLL_RD; } + if ((poll_flags & MP_STREAM_POLL_WR) && tud_cdc_connected() && tud_cdc_write_available() > 0) { + ret |= MP_STREAM_POLL_WR; + } + #if MICROPY_PY_OS_DUPTERM ret |= mp_uos_dupterm_poll(poll_flags); #endif diff --git a/ports/teensy/teensy_hal.c b/ports/teensy/teensy_hal.c index 93103319b585..62a826517721 100644 --- a/ports/teensy/teensy_hal.c +++ b/ports/teensy/teensy_hal.c @@ -32,6 +32,11 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { ret |= MP_STREAM_POLL_RD; } } + if (poll_flags & MP_STREAM_POLL_WR) { + if (MP_STATE_PORT(pyb_stdio_uart) != NULL || usb_vcp_is_enabled()) { + ret |= MP_STREAM_POLL_WR; + } + } return ret; } From 283c1ba07e054af70e7efac3d90917e29c3cca21 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 28 Mar 2023 10:01:30 -0500 Subject: [PATCH 040/371] py/obj: Fix spelling of staticmethod. Signed-off-by: David Lechner --- py/obj.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/obj.h b/py/obj.h index 8a0a256708f2..d41511b0e98f 100644 --- a/py/obj.h +++ b/py/obj.h @@ -418,7 +418,7 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t; #define MP_DEFINE_CONST_DICT(dict_name, table_name) MP_DEFINE_CONST_DICT_WITH_SIZE(dict_name, table_name, MP_ARRAY_SIZE(table_name)) -// These macros are used to declare and define constant staticmethond and classmethod objects +// These macros are used to declare and define constant staticmethod and classmethod objects // You can put "static" in front of the definitions to make them local #define MP_DECLARE_CONST_STATICMETHOD_OBJ(obj_name) extern const mp_rom_obj_static_class_method_t obj_name From a4672149b61e453335d68ec12f640aae4b05d604 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 29 Mar 2023 23:15:16 +1100 Subject: [PATCH 041/371] tests/extmod/vfs_fat_ilistdir_del.py: Use 512-byte erase block size. Following other vfs_fat tests, so the test works on ports like stm32 that only support 512-byte block size. Signed-off-by: Damien George --- tests/extmod/vfs_fat_ilistdir_del.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/extmod/vfs_fat_ilistdir_del.py b/tests/extmod/vfs_fat_ilistdir_del.py index 4389f822bb6f..ccdacc57c25e 100644 --- a/tests/extmod/vfs_fat_ilistdir_del.py +++ b/tests/extmod/vfs_fat_ilistdir_del.py @@ -11,7 +11,7 @@ class RAMBlockDevice: - ERASE_BLOCK_SIZE = 4096 + ERASE_BLOCK_SIZE = 512 def __init__(self, blocks): self.data = bytearray(blocks * self.ERASE_BLOCK_SIZE) @@ -72,7 +72,7 @@ def test(bdev, vfs_class): try: - bdev = RAMBlockDevice(30) + bdev = RAMBlockDevice(50) except MemoryError: print("SKIP") raise SystemExit From 0a3600a9ade773e54cbd70e93a671ec089c3b0fe Mon Sep 17 00:00:00 2001 From: Sebastian Romero Date: Thu, 23 Mar 2023 14:58:30 +0100 Subject: [PATCH 042/371] stm32/boards/ARDUINO_NICLA_VISION: Fix incorrect bootloader PID. --- ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk index 47a47d8f9f73..ebe9775e8077 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.mk @@ -5,7 +5,7 @@ CFLAGS += -DCORE_CM7 # Arduino bootloader PID:VID BOOTLOADER_DFU_USB_VID = 0x2341 -BOOTLOADER_DFU_USB_PID = 0x035b +BOOTLOADER_DFU_USB_PID = 0x035f # MCU settings MCU_SERIES = h7 From 783ddfc264c8c36253a4279ef501f02f8cac1200 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 4 Apr 2023 11:57:25 +1000 Subject: [PATCH 043/371] shared/tinyusb: Allow max USB descriptor string to be configured. Signed-off-by: Damien George --- ports/rp2/usbd.c | 2 +- shared/tinyusb/mp_usbd.h | 2 +- shared/tinyusb/mp_usbd_descriptor.c | 6 +++--- shared/tinyusb/tusb_config.h | 7 ++++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ports/rp2/usbd.c b/ports/rp2/usbd.c index e51cef0ff7e4..568b6284169b 100644 --- a/ports/rp2/usbd.c +++ b/ports/rp2/usbd.c @@ -38,7 +38,7 @@ void mp_usbd_port_get_serial_number(char *serial_buf) { pico_get_unique_board_id(&id); // convert to hex int hexlen = sizeof(id.id) * 2; - MP_STATIC_ASSERT(hexlen <= USBD_DESC_STR_MAX); + MP_STATIC_ASSERT(hexlen <= MICROPY_HW_USB_DESC_STR_MAX); for (int i = 0; i < hexlen; i += 2) { static const char *hexdig = "0123456789abcdef"; serial_buf[i] = hexdig[id.id[i / 2] >> 4]; diff --git a/shared/tinyusb/mp_usbd.h b/shared/tinyusb/mp_usbd.h index a118bc34d92d..3a93b929c586 100644 --- a/shared/tinyusb/mp_usbd.h +++ b/shared/tinyusb/mp_usbd.h @@ -33,7 +33,7 @@ void mp_usbd_task(void); // Function to be implemented in port code. -// Can write a string up to USBD_DESC_STR_MAX characters long, plus terminating byte. +// Can write a string up to MICROPY_HW_USB_DESC_STR_MAX characters long, plus terminating byte. extern void mp_usbd_port_get_serial_number(char *buf); #endif // MICROPY_INCLUDED_SHARED_TINYUSB_USBD_H diff --git a/shared/tinyusb/mp_usbd_descriptor.c b/shared/tinyusb/mp_usbd_descriptor.c index c6d205c92179..8fab599b673c 100644 --- a/shared/tinyusb/mp_usbd_descriptor.c +++ b/shared/tinyusb/mp_usbd_descriptor.c @@ -67,8 +67,8 @@ const uint8_t mp_usbd_desc_cfg_static[USBD_STATIC_DESC_LEN] = { }; const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { - char serial_buf[USBD_DESC_STR_MAX + 1]; // Includes terminating NUL byte - static uint16_t desc_wstr[USBD_DESC_STR_MAX + 1]; // Includes prefix uint16_t + char serial_buf[MICROPY_HW_USB_DESC_STR_MAX + 1]; // Includes terminating NUL byte + static uint16_t desc_wstr[MICROPY_HW_USB_DESC_STR_MAX + 1]; // Includes prefix uint16_t const char *desc_str; uint16_t desc_len; @@ -109,7 +109,7 @@ const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { // Convert from narrow string to wide string desc_len = 2; - for (int i = 0; i < USBD_DESC_STR_MAX && desc_str[i] != 0; i++) { + for (int i = 0; i < MICROPY_HW_USB_DESC_STR_MAX && desc_str[i] != 0; i++) { desc_wstr[1 + i] = desc_str[i]; desc_len += 2; } diff --git a/shared/tinyusb/tusb_config.h b/shared/tinyusb/tusb_config.h index 23dbd14a52d5..a6410a1c9301 100644 --- a/shared/tinyusb/tusb_config.h +++ b/shared/tinyusb/tusb_config.h @@ -27,8 +27,7 @@ #ifndef MICROPY_INCLUDED_SHARED_TINYUSB_TUSB_CONFIG_H #define MICROPY_INCLUDED_SHARED_TINYUSB_TUSB_CONFIG_H -#include -#include "mpconfigport.h" +#include "py/mpconfig.h" #if MICROPY_HW_ENABLE_USBDEV @@ -90,7 +89,9 @@ #define USBD_MAX_POWER_MA (250) -#define USBD_DESC_STR_MAX (20) +#ifndef MICROPY_HW_USB_DESC_STR_MAX +#define MICROPY_HW_USB_DESC_STR_MAX (20) +#endif #if CFG_TUD_CDC #define USBD_ITF_CDC (0) // needs 2 interfaces From 9f74ffb6eb8a65206bc7a31240e07054b19d7afc Mon Sep 17 00:00:00 2001 From: Jos Verlinde Date: Sun, 19 Mar 2023 21:42:56 +0000 Subject: [PATCH 044/371] tools/pyboard.py: Fix ESPxx boards hanging in bootloader after reset. This is a follow up to d263438a6e365d3199494498a9b734cda29dde52, which solved one problem (reset on disconnect) but introduced a second one (hang in bootloader). To solve both probles, False/False is needed for DTR/RTS for ESPxx, but that would then block stm32 and others. Any unconditional combination of DTR/RTS ends up blocking normal operation on some type of board or another. A simple overview (for windows only): DTR CTS ESP8266/ESP32 STM32/SAMD51/RP2040 unspecified unspecified Reset on disconnect OK True False Hang in bootloader OK False False OK No Repl True True Reset on disconnect No Repl False True Reset on disconnect No Repl serial.manufacturer: wch.cn/Silicon Labs Microsoft serial.description: USB-SERIAL CH340 / USB Serial Device CP210x USB to UART Bridge The updated logic will only set the DTR/RTS signals for boards that do not use standard Microsoft drivers (based on the manufacturer). It would also be possible to check against a list of known driver manufactures (like wch.cn or Silicon Labs) but this would require a list of known drivers for all ports. Signed-off-by: Jos Verlinde --- tools/pyboard.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/pyboard.py b/tools/pyboard.py index 29af27f02ba2..c0bb778a321e 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -287,11 +287,15 @@ def __init__( for attempt in range(wait + 1): try: if os.name == "nt": - # Windows does not set DTR or RTS by default self.serial = serial.Serial(**serial_kwargs) - self.serial.dtr = True - self.serial.rts = False self.serial.port = device + portinfo = list(serial.tools.list_ports.grep(device)) # type: ignore + if portinfo and portinfo[0].manufacturer != "Microsoft": + # ESP8266/ESP32 boards use RTS/CTS for flashing and boot mode selection. + # DTR False: to avoid using the reset button will hang the MCU in bootloader mode + # RTS False: to prevent pulses on rts on serial.close() that would POWERON_RESET an ESPxx + self.serial.dtr = False # DTR False = gpio0 High = Normal boot + self.serial.rts = False # RTS False = EN High = MCU enabled self.serial.open() else: self.serial = serial.Serial(device, **serial_kwargs) From 5652f1f661479f23468c90c531927f5f0a727979 Mon Sep 17 00:00:00 2001 From: Jan Hrudka Date: Fri, 31 Mar 2023 21:12:40 +0200 Subject: [PATCH 045/371] stm32/flash: Fix get_bank function for STM32H750. STM32H750 has only 1 flash bank so function get_bank should always return FLASH_BANK_1. --- ports/stm32/flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/flash.c b/ports/stm32/flash.c index 2cc6c390ba01..12fa523cd56e 100644 --- a/ports/stm32/flash.c +++ b/ports/stm32/flash.c @@ -133,7 +133,7 @@ static const flash_layout_t flash_layout[] = { #error Unsupported processor #endif -#if defined(STM32H723xx) +#if defined(STM32H723xx) || defined(STM32H750xx) // get the bank of a given flash address static uint32_t get_bank(uint32_t addr) { From 9025671e7216d9fd73526a681568f78a330a6414 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 26 Mar 2023 19:50:39 +0200 Subject: [PATCH 046/371] drivers/ninaw10: Fix ESP32 input-only pins. ESP32 pins 34, 35, 36 and 39 are input only, and should not be configured as output. --- drivers/ninaw10/machine_pin_nina.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/ninaw10/machine_pin_nina.c b/drivers/ninaw10/machine_pin_nina.c index 3d98af0ef728..025ecb66af0d 100644 --- a/drivers/ninaw10/machine_pin_nina.c +++ b/drivers/ninaw10/machine_pin_nina.c @@ -43,6 +43,7 @@ #define NINA_GPIO_MODE (0x50) #define NINA_GPIO_READ (0x53) #define NINA_GPIO_WRITE (0x51) +#define NINA_GPIO_IS_INPUT_ONLY(p) ((p >= 34 && p <= 36) || (p == 39)) static uint8_t pin_map[MICROPY_HW_PIN_EXT_COUNT] = { 27, // LEDR @@ -82,12 +83,17 @@ void machine_pin_ext_config(machine_pin_obj_t *self, int mode, int value) { } else if (mode == MACHINE_PIN_MODE_OUT) { mode = NINA_GPIO_OUTPUT; self->is_output = true; - machine_pin_ext_set(self, value); } else { mp_raise_ValueError("only Pin.OUT and Pin.IN are supported for this pin"); } if (self->id >= 0 && self->id < MICROPY_HW_PIN_EXT_COUNT) { uint8_t buf[] = {pin_map[self->id], mode}; + if (mode == NINA_GPIO_OUTPUT) { + if (NINA_GPIO_IS_INPUT_ONLY(buf[0])) { + mp_raise_ValueError("only Pin.IN is supported for this pin"); + } + machine_pin_ext_set(self, value); + } nina_ioctl(NINA_GPIO_MODE, sizeof(buf), buf, 0); } } From 3d46fe67bf0bfc848741a76e945b16b2e45e74cb Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 29 Mar 2023 20:42:44 +0200 Subject: [PATCH 047/371] extmod/network_ninaw10: Check socket types when creating new sockets. The NINA socket types have the same values as modnetwork, but that may change in the future. So check the socket types passed to socket() and convert them (if needed) to their respective Nina socket types. Also remove the unnecessary socket type check code from bind(), as pointed out by @robert-hh. --- extmod/network_ninaw10.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/extmod/network_ninaw10.c b/extmod/network_ninaw10.c index 8e806cb423f2..1f2e72707fbf 100644 --- a/extmod/network_ninaw10.c +++ b/extmod/network_ninaw10.c @@ -486,13 +486,29 @@ STATIC int network_ninaw10_socket_listening(mod_network_socket_obj_t *socket, in STATIC int network_ninaw10_socket_socket(mod_network_socket_obj_t *socket, int *_errno) { debug_printf("socket_socket(%d %d %d)\n", socket->domain, socket->type, socket->proto); + uint8_t socket_type; + + switch (socket->type) { + case MOD_NETWORK_SOCK_STREAM: + socket_type = NINA_SOCKET_TYPE_TCP; + break; + + case MOD_NETWORK_SOCK_DGRAM: + socket_type = NINA_SOCKET_TYPE_UDP; + break; + + default: + *_errno = MP_EINVAL; + return -1; + } + if (socket->domain != MOD_NETWORK_AF_INET) { *_errno = MP_EAFNOSUPPORT; return -1; } // open socket - int fd = nina_socket_socket(socket->type, socket->proto); + int fd = nina_socket_socket(socket_type, socket->proto); if (fd < 0) { nina_socket_errno(_errno); debug_printf("socket_socket() -> errno %d\n", *_errno); @@ -522,20 +538,6 @@ STATIC void network_ninaw10_socket_close(mod_network_socket_obj_t *socket) { STATIC int network_ninaw10_socket_bind(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { debug_printf("socket_bind(%d, %d)\n", socket->fileno, port); - uint8_t type; - switch (socket->type) { - case MOD_NETWORK_SOCK_STREAM: - type = NINA_SOCKET_TYPE_TCP; - break; - - case MOD_NETWORK_SOCK_DGRAM: - type = NINA_SOCKET_TYPE_UDP; - break; - - default: - *_errno = MP_EINVAL; - return -1; - } int ret = nina_socket_bind(socket->fileno, ip, port); if (ret < 0) { From 11b5ee0d7c6b386c71f630dd58eed906de49fa65 Mon Sep 17 00:00:00 2001 From: Oliver Joos Date: Sat, 11 Mar 2023 15:50:16 +0100 Subject: [PATCH 048/371] extmod/network_cyw43: Fix setting hostname using config() method. This bug is probably a typo. args[0] is the cyw43 object itself. While the value of a kwargs is in e->value. --- extmod/network_cyw43.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/network_cyw43.c b/extmod/network_cyw43.c index 5204c4ebd6f0..ba44db44ac77 100644 --- a/extmod/network_cyw43.c +++ b/extmod/network_cyw43.c @@ -474,7 +474,7 @@ STATIC mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map case MP_QSTR_hostname: { // TODO: Deprecated. Use network.hostname(name) instead. size_t len; - const char *str = mp_obj_str_get_data(args[0], &len); + const char *str = mp_obj_str_get_data(e->value, &len); if (len >= MICROPY_PY_NETWORK_HOSTNAME_MAX_LEN) { mp_raise_ValueError(NULL); } From f34af3e42e6e823f5fbff1f8e729fe14460ea4ca Mon Sep 17 00:00:00 2001 From: Oliver Joos Date: Sat, 11 Mar 2023 15:59:31 +0100 Subject: [PATCH 049/371] extmod/network_cyw43: Add support to get STA RSSI using status() method. This enables the use of WLAN(0).status('rssi') to get current RSSI of the AP that the STA is connected to. Signed-off-by: Damien George --- extmod/network_cyw43.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extmod/network_cyw43.c b/extmod/network_cyw43.c index ba44db44ac77..dc685031dc41 100644 --- a/extmod/network_cyw43.c +++ b/extmod/network_cyw43.c @@ -314,6 +314,14 @@ STATIC mp_obj_t network_cyw43_status(size_t n_args, const mp_obj_t *args) { // one argument: return status based on query parameter switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_rssi: { + if (self->itf != CYW43_ITF_STA) { + mp_raise_ValueError(MP_ERROR_TEXT("STA required")); + } + int32_t rssi; + cyw43_wifi_get_rssi(self->cyw, &rssi); + return mp_obj_new_int(rssi); + } case MP_QSTR_stations: { // return list of connected stations if (self->itf != CYW43_ITF_AP) { From b4a0390cbe1a2d6e61549133f24257a632e7d233 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 4 Apr 2023 22:53:55 +0200 Subject: [PATCH 050/371] extmod/network_ninaw10: Add missing raw socket type to socket(). This regression was introduced by 3d46fe67bf0bfc848741a76e945b16b2e45e74cb. --- extmod/network_ninaw10.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extmod/network_ninaw10.c b/extmod/network_ninaw10.c index 1f2e72707fbf..00f53eff137a 100644 --- a/extmod/network_ninaw10.c +++ b/extmod/network_ninaw10.c @@ -497,6 +497,10 @@ STATIC int network_ninaw10_socket_socket(mod_network_socket_obj_t *socket, int * socket_type = NINA_SOCKET_TYPE_UDP; break; + case MOD_NETWORK_SOCK_RAW: + socket_type = NINA_SOCKET_TYPE_RAW; + break; + default: *_errno = MP_EINVAL; return -1; From 408556504cc80c785fff494b1773c23f37a62f36 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 11 Mar 2023 17:06:16 +0100 Subject: [PATCH 051/371] nrf/modules/machine/pwm: Fix resource conflict, and change id to device. Changes in this commit: - Move the pwm_seq array to the p_config data structure. That prevents potential resource collisions between PWM devices. - Rename the keyword argument 'id' to 'device'. That's consistent with the SAMD port as the other port allowing to specify it. --- ports/nrf/modules/machine/pwm.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/ports/nrf/modules/machine/pwm.c b/ports/nrf/modules/machine/pwm.c index 8e435bcf3a7f..96917769eed4 100644 --- a/ports/nrf/modules/machine/pwm.c +++ b/ports/nrf/modules/machine/pwm.c @@ -70,6 +70,7 @@ typedef struct { pwm_mode_t mode[NRF_PWM_CHANNEL_COUNT]; pwm_duty_t duty_mode[NRF_PWM_CHANNEL_COUNT]; uint32_t duty[NRF_PWM_CHANNEL_COUNT]; + uint16_t pwm_seq[4]; pwm_run_t active; bool defer_start; int8_t freq_div; @@ -137,7 +138,7 @@ STATIC int hard_pwm_find() { return j * NRF_PWM_CHANNEL_COUNT; } } - mp_raise_ValueError(MP_ERROR_TEXT("no free PWM object")); + mp_raise_ValueError(MP_ERROR_TEXT("all PWM devices in use")); } STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { @@ -166,12 +167,12 @@ static const mp_arg_t allowed_args[] = { { 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_INT, {.u_int = -1} }, - { MP_QSTR_id, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_device, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, }; STATIC void mp_machine_pwm_init_helper(const machine_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_pin, ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_id, ARG_channel }; + enum { ARG_pin, ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_device, ARG_channel }; 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); @@ -199,7 +200,7 @@ STATIC void mp_machine_pwm_init_helper(const machine_pwm_obj_t *self, size_t n_a 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 *all_args) { - enum { ARG_pin, ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_id, ARG_channel }; + enum { ARG_pin, ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_device, ARG_channel }; // parse args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -217,9 +218,9 @@ STATIC mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args // If just the ID is given, use channel 0 // If none is given, attempt to find an unused object. int pwm_id = -1; - if (args[ARG_id].u_int != -1) { - if (args[ARG_id].u_int >= 0 && args[ARG_id].u_int < MP_ARRAY_SIZE(machine_hard_pwm_instances)) { - pwm_id = args[ARG_id].u_int * NRF_PWM_CHANNEL_COUNT; + if (args[ARG_device].u_int != -1) { + if (args[ARG_device].u_int >= 0 && args[ARG_device].u_int < MP_ARRAY_SIZE(machine_hard_pwm_instances)) { + pwm_id = args[ARG_device].u_int * NRF_PWM_CHANNEL_COUNT; if (args[ARG_channel].u_int != -1) { if (args[ARG_channel].u_int >= 0 && args[ARG_channel].u_int < NRF_PWM_CHANNEL_COUNT) { pwm_id += args[ARG_channel].u_int; @@ -363,28 +364,25 @@ STATIC void machine_hard_pwm_start(const machine_pwm_obj_t *self) { nrfx_pwm_init(self->p_pwm, &config, NULL, NULL); - volatile static uint16_t pwm_seq[4]; - for (int i = 0; i < NRF_PWM_CHANNEL_COUNT; i++) { uint16_t pulse_width = 0; if (self->p_config->duty_mode[i] == DUTY_PERCENT) { pulse_width = ((period * self->p_config->duty[i]) / 100); } else if (self->p_config->duty_mode[i] == DUTY_U16) { pulse_width = ((period * self->p_config->duty[i]) / 65536); - } - if (self->p_config->duty_mode[i] == DUTY_NS) { + } else if (self->p_config->duty_mode[i] == DUTY_NS) { pulse_width = (uint64_t)self->p_config->duty[i] * tick_freq / 1000000000ULL; } if (self->p_config->mode[i] == MODE_HIGH_LOW) { - pwm_seq[i] = 0x8000 | pulse_width; + self->p_config->pwm_seq[i] = 0x8000 | pulse_width; } else { - pwm_seq[i] = pulse_width; + self->p_config->pwm_seq[i] = pulse_width; } } const nrf_pwm_sequence_t pwm_sequence = { - .values.p_raw = (const uint16_t *)&pwm_seq, + .values.p_raw = (const uint16_t *)&self->p_config->pwm_seq, .length = 4, .repeats = 0, .end_delay = 0 From a529e0e8cf1bcc5d1e6fc9ab5e4bfeb11c302a53 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 21 Mar 2023 08:08:30 +0100 Subject: [PATCH 052/371] nrf/nrfx_config: Use UARTE for nrf52xxx devices. It was incomplete. --- ports/nrf/Makefile | 8 +++----- ports/nrf/nrfx_config.h | 4 +++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 61ae72d71428..54b69b63707a 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -233,11 +233,6 @@ SRC_LIB_C += $(addprefix lib/,\ libm/roundf.c \ ) -SRC_NRFX += $(addprefix lib/nrfx/drivers/src/,\ - nrfx_uarte.c \ - nrfx_twim.c \ - ) - include drivers/secureboot/secureboot.mk endif @@ -263,11 +258,13 @@ endif SRC_NRFX += $(addprefix lib/nrfx/drivers/src/,\ prs/nrfx_prs.c \ nrfx_uart.c \ + nrfx_uarte.c \ nrfx_adc.c \ nrfx_saadc.c \ nrfx_temp.c \ nrfx_rng.c \ nrfx_twi.c \ + nrfx_twim.c \ nrfx_spi.c \ nrfx_spim.c \ nrfx_rtc.c \ @@ -319,6 +316,7 @@ SRC_C += $(addprefix lib/tinyusb/src/,\ tusb.c \ portable/nordic/nrf5x/dcd_nrf5x.c \ ) + endif DRIVERS_SRC_C += $(addprefix modules/,\ diff --git a/ports/nrf/nrfx_config.h b/ports/nrf/nrfx_config.h index 505014a2f269..4669a0905641 100644 --- a/ports/nrf/nrfx_config.h +++ b/ports/nrf/nrfx_config.h @@ -74,14 +74,16 @@ #endif #endif -#if defined(NRF51) || defined(NRF52_SERIES) +#if defined(NRF51) #define NRFX_UART_ENABLED 1 #define NRFX_UART0_ENABLED 1 #define NRFX_UART1_ENABLED 1 #elif defined(NRF52_SERIES) #define NRFX_UARTE_ENABLED 1 #define NRFX_UARTE0_ENABLED 1 + #if NRF52840 || NRF52840_XXAA #define NRFX_UARTE1_ENABLED 1 + #endif #else #define NRFX_UARTE_ENABLED 1 #define NRFX_UARTE0_ENABLED 1 From eb6e5143c422e13f9b07790a88473e4813024e92 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Wed, 22 Mar 2023 20:31:50 +0100 Subject: [PATCH 053/371] nrf/modules/machine/uart: Prevent UART lock-up after a receive error. Like frame error, overrun, etc. Fix is provided by @ricksorensen. --- ports/nrf/modules/machine/uart.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/nrf/modules/machine/uart.c b/ports/nrf/modules/machine/uart.c index 778b1c155d03..fd176549d0f9 100644 --- a/ports/nrf/modules/machine/uart.c +++ b/ports/nrf/modules/machine/uart.c @@ -77,6 +77,7 @@ typedef struct _machine_hard_uart_buf_t { #define NRF_UART_HWFC_DISABLED NRF_UARTE_HWFC_DISABLED #define NRF_UART_PARITY_EXCLUDED NRF_UARTE_PARITY_EXCLUDED #define NRFX_UART_EVT_RX_DONE NRFX_UARTE_EVT_RX_DONE +#define NRFX_UART_EVT_ERROR NRFX_UARTE_EVT_ERROR #define NRF_UART_BAUDRATE_1200 NRF_UARTE_BAUDRATE_1200 #define NRF_UART_BAUDRATE_2400 NRF_UARTE_BAUDRATE_2400 @@ -136,6 +137,9 @@ STATIC void uart_event_handler(nrfx_uart_event_t const *p_event, void *p_context { ringbuf_put((ringbuf_t *)&self->buf.rx_ringbuf, chr); } + } else if (p_event->type == NRFX_UART_EVT_ERROR) { + // Perform a read to unlock UART in case of an error + nrfx_uart_rx(self->p_uart, &self->buf.rx_buf[0], 1); } } From db4b416ea824e66414530b84928671f701ac5b84 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 4 Apr 2023 11:04:17 +0200 Subject: [PATCH 054/371] mimxrt/pendsv: Clean up PendSV code. The dispatch active flag is only set once and never reset, so it will always call the dispatch handler (once enabled), and it's not really needed because it doesn't make things more efficient. Also remove unused included headers. --- ports/mimxrt/pendsv.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/ports/mimxrt/pendsv.c b/ports/mimxrt/pendsv.c index 7638b160d861..11f2ef5e1c86 100644 --- a/ports/mimxrt/pendsv.c +++ b/ports/mimxrt/pendsv.c @@ -27,23 +27,16 @@ #include #include "py/runtime.h" -#include "shared/runtime/interrupt_char.h" #include "pendsv.h" -#include "lib/nxp_driver/sdk/CMSIS/Include/core_cm7.h" #define NVIC_PRIORITYGROUP_4 ((uint32_t)0x00000003) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 15, 0) #if defined(PENDSV_DISPATCH_NUM_SLOTS) -uint32_t pendsv_dispatch_active; pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS]; #endif void pendsv_init(void) { - #if defined(PENDSV_DISPATCH_NUM_SLOTS) - pendsv_dispatch_active = false; - #endif - // set PendSV interrupt at lowest priority NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_PENDSV); } @@ -51,11 +44,10 @@ void pendsv_init(void) { #if defined(PENDSV_DISPATCH_NUM_SLOTS) void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { pendsv_dispatch_table[slot] = f; - pendsv_dispatch_active = true; SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; } -void pendsv_dispatch_handler(void) { +void PendSV_Handler(void) { for (size_t i = 0; i < PENDSV_DISPATCH_NUM_SLOTS; ++i) { if (pendsv_dispatch_table[i] != NULL) { pendsv_dispatch_t f = pendsv_dispatch_table[i]; @@ -64,10 +56,4 @@ void pendsv_dispatch_handler(void) { } } } - -void PendSV_Handler(void) { - if (pendsv_dispatch_active) { - pendsv_dispatch_handler(); - } -} #endif From c046b23ea29e0183c899a8dbe1da3bed3440a255 Mon Sep 17 00:00:00 2001 From: David Grayson Date: Tue, 4 Apr 2023 12:05:45 -0700 Subject: [PATCH 055/371] shared/runtime/pyexec: Don't allow Ctrl+C to interrupt frozen boot code. Helps prevent the filesystem from getting formatted by mistake, among other things. For example, on a Pico board, entering Ctrl+D and Ctrl+C fast many times will eventually wipe the filesystem (without warning or notice). Further rationale: Ctrl+C is used a lot by automation scripts (eg mpremote) and UI's (eg Mu, Thonny) to get the board into a known state. If the board is not responding for a short time then it's not possible to know if it's just a slow start up (eg in _boot.py), or an infinite loop in the main application. The former should not be interrupted, but the latter should. The only way to distinguish these two cases would be to wait "long enough", and if there's nothing on the serial after "long enough" then assume it's running the application and Ctrl+C should break out of it. But defining "long enough" is impossible for all the different boards and their possible behaviour. The solution in this commit is to make it so that frozen start-up code cannot be interrupted by Ctrl+C. That code then effectively acts like normal C start-up code, which also cannot be interrupted. Note: on the stm32 port this was never seen as an issue because all start-up code is in C. But now other ports start to put more things in _boot.py and so this problem crops up. Signed-off-by: David Grayson --- ports/esp32/main.c | 2 +- ports/esp8266/main.c | 2 +- ports/mimxrt/main.c | 2 +- ports/minimal/main.c | 2 +- ports/nrf/main.c | 2 +- ports/powerpc/main.c | 2 +- ports/renesas-ra/main.c | 2 +- ports/rp2/main.c | 4 ++-- ports/samd/main.c | 2 +- ports/stm32/main.c | 2 +- ports/teensy/main.c | 4 ++-- shared/runtime/pyexec.c | 15 ++++++++++----- shared/runtime/pyexec.h | 2 +- 13 files changed, 24 insertions(+), 19 deletions(-) diff --git a/ports/esp32/main.c b/ports/esp32/main.c index e2a803fcb621..e7d7626a6e2b 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -161,7 +161,7 @@ void mp_task(void *pvParameter) { #endif // run boot-up scripts - pyexec_frozen_module("_boot.py"); + pyexec_frozen_module("_boot.py", false); pyexec_file_if_exists("boot.py"); if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) { int ret = pyexec_file_if_exists("main.py"); diff --git a/ports/esp8266/main.c b/ports/esp8266/main.c index 583540a81c76..238490ebefe9 100644 --- a/ports/esp8266/main.c +++ b/ports/esp8266/main.c @@ -74,7 +74,7 @@ STATIC void mp_reset(void) { } #if MICROPY_MODULE_FROZEN - pyexec_frozen_module("_boot.py"); + pyexec_frozen_module("_boot.py", false); pyexec_file_if_exists("boot.py"); if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) { pyexec_file_if_exists("main.py"); diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index 907de373dee6..1a85f21c6d13 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -85,7 +85,7 @@ int main(void) { readline_init0(); // Execute _boot.py to set up the filesystem. - pyexec_frozen_module("_boot.py"); + pyexec_frozen_module("_boot.py", false); // Execute user scripts. int ret = pyexec_file_if_exists("boot.py"); diff --git a/ports/minimal/main.c b/ports/minimal/main.c index 4eb6ca65af0d..499099753184 100644 --- a/ports/minimal/main.c +++ b/ports/minimal/main.c @@ -55,7 +55,7 @@ int main(int argc, char **argv) { // do_str("print('hello world!', list(x+1 for x in range(10)), end='eol\\n')", MP_PARSE_SINGLE_INPUT); // do_str("for i in range(10):\r\n print(i)", MP_PARSE_FILE_INPUT); #else - pyexec_frozen_module("frozentest.py"); + pyexec_frozen_module("frozentest.py", false); #endif mp_deinit(); return 0; diff --git a/ports/nrf/main.c b/ports/nrf/main.c index 197fea9ab640..f64107f8907f 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -184,7 +184,7 @@ int main(int argc, char **argv) { int ret = mp_vfs_mount_and_chdir_protected((mp_obj_t)&nrf_flash_obj, mount_point); if ((ret == -MP_ENODEV) || (ret == -MP_EIO)) { - pyexec_frozen_module("_mkfs.py"); // Frozen script for formatting flash filesystem. + pyexec_frozen_module("_mkfs.py", false); // Frozen script for formatting flash filesystem. ret = mp_vfs_mount_and_chdir_protected((mp_obj_t)&nrf_flash_obj, mount_point); } diff --git a/ports/powerpc/main.c b/ports/powerpc/main.c index 11013f175530..f3abc64aa739 100644 --- a/ports/powerpc/main.c +++ b/ports/powerpc/main.c @@ -95,7 +95,7 @@ int main(int argc, char **argv) { pyexec_friendly_repl(); #endif #else - pyexec_frozen_module("frozentest.py"); + pyexec_frozen_module("frozentest.py", false); #endif mp_deinit(); return 0; diff --git a/ports/renesas-ra/main.c b/ports/renesas-ra/main.c index d403dbacbe43..6ba26cd7baba 100644 --- a/ports/renesas-ra/main.c +++ b/ports/renesas-ra/main.c @@ -325,7 +325,7 @@ void ra_main(uint32_t reset_mode) { // Run optional frozen boot code. #ifdef MICROPY_BOARD_FROZEN_BOOT_FILE - pyexec_frozen_module(MICROPY_BOARD_FROZEN_BOOT_FILE); + pyexec_frozen_module(MICROPY_BOARD_FROZEN_BOOT_FILE, false); #endif // Run boot.py (or whatever else a board configures at this stage). diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 9e69d159e17e..059449695cb6 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -168,9 +168,9 @@ int main(int argc, char **argv) { // Execute _boot.py to set up the filesystem. #if MICROPY_VFS_FAT && MICROPY_HW_USB_MSC - pyexec_frozen_module("_boot_fat.py"); + pyexec_frozen_module("_boot_fat.py", false); #else - pyexec_frozen_module("_boot.py"); + pyexec_frozen_module("_boot.py", false); #endif // Execute user scripts. diff --git a/ports/samd/main.c b/ports/samd/main.c index 725fd651d02c..bc0e45ee64db 100644 --- a/ports/samd/main.c +++ b/ports/samd/main.c @@ -52,7 +52,7 @@ void samd_main(void) { readline_init0(); // Execute _boot.py to set up the filesystem. - pyexec_frozen_module("_boot.py"); + pyexec_frozen_module("_boot.py", false); // Execute user scripts. int ret = pyexec_file_if_exists("boot.py"); diff --git a/ports/stm32/main.c b/ports/stm32/main.c index e5ad14fcf770..cc0367b824e0 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -559,7 +559,7 @@ void stm32_main(uint32_t reset_mode) { // Run optional frozen boot code. #ifdef MICROPY_BOARD_FROZEN_BOOT_FILE - pyexec_frozen_module(MICROPY_BOARD_FROZEN_BOOT_FILE); + pyexec_frozen_module(MICROPY_BOARD_FROZEN_BOOT_FILE, false); #endif // Run boot.py (or whatever else a board configures at this stage). diff --git a/ports/teensy/main.c b/ports/teensy/main.c index 37a04c74f8af..fa27326e932d 100644 --- a/ports/teensy/main.c +++ b/ports/teensy/main.c @@ -298,7 +298,7 @@ int main(void) { #endif #if MICROPY_MODULE_FROZEN - pyexec_frozen_module("boot.py"); + pyexec_frozen_module("boot.py", false); #else if (!pyexec_file_if_exists("/boot.py")) { flash_error(4); @@ -310,7 +310,7 @@ int main(void) { // run main script #if MICROPY_MODULE_FROZEN - pyexec_frozen_module("main.py"); + pyexec_frozen_module("main.py", true); #else { vstr_t *vstr = vstr_new(16); diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 40491650e421..ec0ff87f1d01 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -57,6 +57,7 @@ STATIC bool repl_display_debugging_info = 0; #define EXEC_FLAG_SOURCE_IS_VSTR (1 << 4) #define EXEC_FLAG_SOURCE_IS_FILENAME (1 << 5) #define EXEC_FLAG_SOURCE_IS_READER (1 << 6) +#define EXEC_FLAG_NO_INTERRUPT (1 << 7) // parses, compiles and executes the code in the lexer // frees the lexer before returning @@ -113,7 +114,9 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input } // execute code - mp_hal_set_interrupt_char(CHAR_CTRL_C); // allow ctrl-C to interrupt us + if (!(exec_flags & EXEC_FLAG_NO_INTERRUPT)) { + mp_hal_set_interrupt_char(CHAR_CTRL_C); + } #if MICROPY_REPL_INFO start = mp_hal_ticks_ms(); #endif @@ -686,7 +689,7 @@ int pyexec_file(const char *filename) { int pyexec_file_if_exists(const char *filename) { #if MICROPY_MODULE_FROZEN if (mp_find_frozen_module(filename, NULL, NULL) == MP_IMPORT_STAT_FILE) { - return pyexec_frozen_module(filename); + return pyexec_frozen_module(filename, true); } #endif if (mp_import_stat(filename) != MP_IMPORT_STAT_FILE) { @@ -696,20 +699,22 @@ int pyexec_file_if_exists(const char *filename) { } #if MICROPY_MODULE_FROZEN -int pyexec_frozen_module(const char *name) { +int pyexec_frozen_module(const char *name, bool allow_keyboard_interrupt) { void *frozen_data; int frozen_type; mp_find_frozen_module(name, &frozen_type, &frozen_data); + mp_uint_t exec_flags = allow_keyboard_interrupt ? 0 : EXEC_FLAG_NO_INTERRUPT; switch (frozen_type) { #if MICROPY_MODULE_FROZEN_STR case MP_FROZEN_STR: - return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, 0); + return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, exec_flags); #endif #if MICROPY_MODULE_FROZEN_MPY case MP_FROZEN_MPY: - return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_RAW_CODE); + return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, exec_flags | + EXEC_FLAG_SOURCE_IS_RAW_CODE); #endif default: diff --git a/shared/runtime/pyexec.h b/shared/runtime/pyexec.h index 981e7dca9fc6..64c5ef94340d 100644 --- a/shared/runtime/pyexec.h +++ b/shared/runtime/pyexec.h @@ -46,7 +46,7 @@ int pyexec_raw_repl(void); int pyexec_friendly_repl(void); int pyexec_file(const char *filename); int pyexec_file_if_exists(const char *filename); -int pyexec_frozen_module(const char *name); +int pyexec_frozen_module(const char *name, bool allow_keyboard_interrupt); void pyexec_event_repl_init(void); int pyexec_event_repl_process_char(int c); extern uint8_t pyexec_repl_active; From cfd3b70934791abc07f9476a956781de92ddf715 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 31 Mar 2023 14:08:13 +1100 Subject: [PATCH 056/371] tools/manifestfile.py: Add support for publishing packages to PyPI. This adds a new MODE_PYPROJECT, which gives basic support to allow packaging a small subset of micropython-lib packages to PyPI. This change allows a package in micropython-lib to: - Add a "pypi" name to its metadata indicating that it's based on a PyPI package. - Add "stdlib" to its metadata indicating that it's a micropython version of a stdlib package. - Add a "pypi_publish" name to its metadata to indicate that it can be published to PyPI (this can be different to the package name, e.g. "foo" might want to be published as "micropython-foo"). When a package requires() another one, if it's in MODE_PYPROJECT then if the package is from pypi then it will record that as a pypi dependency instead (or no dependency at all if it's from stdlib). Also allows require() to explicitly specify the pypi name. Signed-off-by: Jim Mussared --- tools/manifestfile.py | 204 ++++++++++++++++++++++++++++++++---------- 1 file changed, 159 insertions(+), 45 deletions(-) diff --git a/tools/manifestfile.py b/tools/manifestfile.py index fb9691d2931a..f9b8a70357c9 100644 --- a/tools/manifestfile.py +++ b/tools/manifestfile.py @@ -39,7 +39,8 @@ MODE_FREEZE = 1 # Only allow include/require/module/package. MODE_COMPILE = 2 - +# Same as compile, but handles require(..., pypi="name") as a requirements.txt entry. +MODE_PYPROJECT = 3 # In compile mode, .py -> KIND_COMPILE_AS_MPY # In freeze mode, .py -> KIND_FREEZE_AS_MPY, .mpy->KIND_FREEZE_MPY @@ -66,6 +67,15 @@ class ManifestFileError(Exception): pass +class ManifestIgnoreException(Exception): + pass + + +class ManifestUsePyPIException(Exception): + def __init__(self, pypi_name): + self.pypi_name = pypi_name + + # The set of files that this manifest references. ManifestOutput = namedtuple( "ManifestOutput", @@ -81,23 +91,75 @@ class ManifestFileError(Exception): ) -# Represent the metadata for a package. -class ManifestMetadata: - def __init__(self): +# Represents the metadata for a package. +class ManifestPackageMetadata: + def __init__(self, is_require=False): + self._is_require = is_require + self._initialised = False + self.version = None self.description = None self.license = None self.author = None - def update(self, description=None, version=None, license=None, author=None): - if description: - self.description = description - if version: - self.version = version - if license: - self.license = version - if author: - self.author = author + # Annotate a package as being from the python standard library. + self.stdlib = False + + # Allows a python-ecosys package to be annotated with the + # corresponding name in PyPI. e.g. micropython-lib/urequests is based + # on pypi/requests. + self.pypi = None + # For a micropython package, this is the name that we will publish it + # to PyPI as. e.g. micropython-lib/senml publishes as + # pypi/micropython-senml. + self.pypi_publish = None + + def update( + self, + mode, + description=None, + version=None, + license=None, + author=None, + stdlib=False, + pypi=None, + pypi_publish=None, + ): + if self._initialised: + raise ManifestFileError("Duplicate call to metadata().") + + # In MODE_PYPROJECT, if this manifest is being evaluated as a result + # of a require(), then figure out if it should be replaced by a PyPI + # dependency instead. + if mode == MODE_PYPROJECT and self._is_require: + if stdlib: + # No dependency required at all for CPython. + raise ManifestIgnoreException + if pypi_publish or pypi: + # In the case where a package is both based on a PyPI package and + # provides one, preference depending on the published one. + # (This should be pretty rare). + raise ManifestUsePyPIException(pypi_publish or pypi) + + self.description = description + self.version = version + self.license = version + self.author = author + self.pypi = pypi + self.pypi_publish = pypi_publish + self._initialised = True + + def check_initialised(self, mode): + # Ensure that metadata() is the first thing a manifest.py does. + # This is to ensure that we early-exit if it should be replaced by a pypi dependency. + if mode in (MODE_COMPILE, MODE_PYPROJECT): + if not self._initialised: + raise ManifestFileError("metadata() must be the first command in a manifest file.") + + def __str__(self): + return "version={} description={} license={} author={} pypi={} pypi_publish={}".format( + self.version, self.description, self.license, self.author, self.pypi, self.pypi_publish + ) # Turns a dict of options into a object with attributes used to turn the @@ -120,16 +182,18 @@ def __getattr__(self, name): class ManifestFile: def __init__(self, mode, path_vars=None): - # Either MODE_FREEZE or MODE_COMPILE. + # See MODE_* constants above. self._mode = mode - # Path substition variables. + # Path substitution variables. self._path_vars = path_vars or {} # List of files (as ManifestFileResult) references by this manifest. self._manifest_files = [] + # List of PyPI dependencies (when mode=MODE_PYPROJECT). + self._pypi_dependencies = [] # Don't allow including the same file twice. self._visited = set() # Stack of metadata for each level. - self._metadata = [ManifestMetadata()] + self._metadata = [ManifestPackageMetadata()] def _resolve_path(self, path): # Convert path to an absolute path, applying variable substitutions. @@ -140,26 +204,39 @@ def _resolve_path(self, path): def _manifest_globals(self, kwargs): # This is the "API" available to a manifest file. - return { + g = { "metadata": self.metadata, "include": self.include, "require": self.require, "package": self.package, "module": self.module, - "freeze": self.freeze, - "freeze_as_str": self.freeze_as_str, - "freeze_as_mpy": self.freeze_as_mpy, - "freeze_mpy": self.freeze_mpy, "options": IncludeOptions(**kwargs), } + # Extra legacy functions only for freeze mode. + if self._mode == MODE_FREEZE: + g.update( + { + "freeze": self.freeze, + "freeze_as_str": self.freeze_as_str, + "freeze_as_mpy": self.freeze_as_mpy, + "freeze_mpy": self.freeze_mpy, + } + ) + + return g + def files(self): return self._manifest_files + def pypi_dependencies(self): + # In MODE_PYPROJECT, this will return a list suitable for requirements.txt. + return self._pypi_dependencies + def execute(self, manifest_file): if manifest_file.endswith(".py"): # Execute file from filesystem. - self.include(manifest_file, top_level=True) + self.include(manifest_file) else: # Execute manifest code snippet. try: @@ -173,7 +250,7 @@ def _add_file(self, full_path, target_path, kind=KIND_AUTO, opt=None): stat = os.stat(full_path) timestamp = stat.st_mtime except OSError: - raise ManifestFileError("cannot stat {}".format(full_path)) + raise ManifestFileError("Cannot stat {}".format(full_path)) # Map the AUTO kinds to their actual kind based on mode and extension. _, ext = os.path.splitext(full_path) @@ -231,19 +308,21 @@ def _search(self, base_path, package_path, files, exts, kind, opt=None, strict=F if base_path: os.chdir(prev_cwd) - def metadata(self, description=None, version=None, license=None, author=None): + def metadata(self, **kwargs): """ From within a manifest file, use this to set the metadata for the package described by current manifest. After executing a manifest file (via execute()), call this to obtain the metadata for the top-level manifest file. - """ - self._metadata[-1].update(description, version, license, author) + See ManifestPackageMetadata.update() for valid kwargs. + """ + if kwargs: + self._metadata[-1].update(self._mode, **kwargs) return self._metadata[-1] - def include(self, manifest_path, top_level=False, **kwargs): + def include(self, manifest_path, is_require=False, **kwargs): """ Include another manifest. @@ -269,9 +348,12 @@ def include(self, manifest_path, top_level=False, **kwargs): if options.extra_features: # freeze extra modules. """ + if is_require: + self._metadata[-1].check_initialised(self._mode) + if not isinstance(manifest_path, str): for m in manifest_path: - self.include(m) + self.include(m, **kwargs) else: manifest_path = self._resolve_path(manifest_path) # Including a directory grabs the manifest.py inside it. @@ -280,29 +362,50 @@ def include(self, manifest_path, top_level=False, **kwargs): if manifest_path in self._visited: return self._visited.add(manifest_path) - if not top_level: - self._metadata.append(ManifestMetadata()) - with open(manifest_path) as f: - # Make paths relative to this manifest file while processing it. - # Applies to includes and input files. - prev_cwd = os.getcwd() - os.chdir(os.path.dirname(manifest_path)) - try: - exec(f.read(), self._manifest_globals(kwargs)) - except Exception as er: - raise ManifestFileError( - "Error in manifest file: {}: {}".format(manifest_path, er) - ) - os.chdir(prev_cwd) - if not top_level: + if is_require: + # This include is the result of require("name"), so push a new + # package metadata onto the stack. + self._metadata.append(ManifestPackageMetadata(is_require=True)) + try: + with open(manifest_path) as f: + # Make paths relative to this manifest file while processing it. + # Applies to includes and input files. + prev_cwd = os.getcwd() + os.chdir(os.path.dirname(manifest_path)) + try: + exec(f.read(), self._manifest_globals(kwargs)) + finally: + os.chdir(prev_cwd) + except ManifestIgnoreException: + # e.g. MODE_PYPROJECT and this was a stdlib dependency. No-op. + pass + except ManifestUsePyPIException as e: + # e.g. MODE_PYPROJECT and this was a package from + # python-ecosys. Add PyPI dependency instead. + self._pypi_dependencies.append(e.pypi_name) + except Exception as e: + raise ManifestFileError("Error in manifest file: {}: {}".format(manifest_path, e)) + if is_require: self._metadata.pop() - def require(self, name, version=None, unix_ffi=False, **kwargs): + def require(self, name, version=None, unix_ffi=False, pypi=None, **kwargs): """ Require a module by name from micropython-lib. Optionally specify unix_ffi=True to use a module from the unix-ffi directory. + + Optionally specify pipy="package-name" to indicate that this should + use the named package from PyPI when building for CPython. """ + self._metadata[-1].check_initialised(self._mode) + + if self._mode == MODE_PYPROJECT and pypi: + # In PYPROJECT mode, allow overriding the PyPI dependency name + # explicitly. Otherwise if the dependent package has metadata + # (pypi_publish) or metadata(pypi) we will use that. + self._pypi_dependencies.append(pypi) + return + if self._path_vars["MPY_LIB_DIR"]: lib_dirs = ["micropython", "python-stdlib", "python-ecosys"] if unix_ffi: @@ -316,7 +419,7 @@ def require(self, name, version=None, unix_ffi=False, **kwargs): os.path.join(self._path_vars["MPY_LIB_DIR"], lib_dir) ): if os.path.basename(root) == name and "manifest.py" in filenames: - self.include(root, **kwargs) + self.include(root, is_require=True, **kwargs) return raise ValueError("Library not found in local micropython-lib: {}".format(name)) @@ -338,6 +441,8 @@ def package(self, package_path, files=None, base_path=".", opt=None): To restrict to certain files in the package use files (note: paths should be relative to the package): package("foo", files=["bar/baz.py"]) """ + self._metadata[-1].check_initialised(self._mode) + # Include "base_path/package_path/**/*.py" --> "package_path/**/*.py" self._search(base_path, package_path, files, exts=(".py",), kind=KIND_AUTO, opt=opt) @@ -351,6 +456,8 @@ def module(self, module_path, base_path=".", opt=None): Otherwise use base_path to locate the file: module("foo.py", "src/drivers") """ + self._metadata[-1].check_initialised(self._mode) + # Include "base_path/module_path" --> "module_path" base_path = self._resolve_path(base_path) _, ext = os.path.splitext(module_path) @@ -454,6 +561,7 @@ def main(): cmd_parser = argparse.ArgumentParser(description="List the files referenced by a manifest.") cmd_parser.add_argument("--freeze", action="store_true", help="freeze mode") cmd_parser.add_argument("--compile", action="store_true", help="compile mode") + cmd_parser.add_argument("--pyproject", action="store_true", help="pyproject mode") cmd_parser.add_argument( "--lib", default=os.path.join(os.path.dirname(__file__), "../lib/micropython-lib"), @@ -481,6 +589,8 @@ def main(): mode = MODE_FREEZE elif args.compile: mode = MODE_COMPILE + elif args.pyproject: + mode = MODE_PYPROJECT else: print("Error: No mode specified.", file=sys.stderr) exit(1) @@ -492,8 +602,12 @@ def main(): except ManifestFileError as er: print(er, file=sys.stderr) exit(1) + print(m.metadata()) for f in m.files(): print(f) + if mode == MODE_PYPROJECT: + for r in m.pypi_dependencies(): + print("pypi-require:", r) if __name__ == "__main__": From a22136a7322616e768962804075380103d34a09b Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Tue, 21 Feb 2023 16:44:08 +0100 Subject: [PATCH 057/371] py/makeqstrdefs.py: Fix handling GreenHills C/C++ preprocessor output. The GreenHills preprocessor produces #line directives without a file name, which the regular expression used to distiguish between "# file..." (GCC and similar) and "#line file..." (Microsoft C and similar) does not match, aborting processing. Besides, the regular expression was unnecessarily wide, matching lines containing a "#", followed by any number of 'l','i','n', and 'e' characters. Signed-off-by: Alex Riesen --- py/makeqstrdefs.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/py/makeqstrdefs.py b/py/makeqstrdefs.py index c445d6d1fe0c..13a8b54d67cf 100644 --- a/py/makeqstrdefs.py +++ b/py/makeqstrdefs.py @@ -86,7 +86,8 @@ def write_out(fname, output): def process_file(f): - re_line = re.compile(r"#[line]*\s\d+\s\"([^\"]+)\"") + # match gcc-like output (# n "file") and msvc-like output (#line n "file") + re_line = re.compile(r"^#(?:line)?\s+\d+\s\"([^\"]+)\"") if args.mode == _MODE_QSTR: re_match = re.compile(r"MP_QSTR_[_a-zA-Z0-9]+") elif args.mode == _MODE_COMPRESS: @@ -100,10 +101,8 @@ def process_file(f): for line in f: if line.isspace(): continue - # match gcc-like output (# n "file") and msvc-like output (#line n "file") - if line.startswith(("# ", "#line")): - m = re_line.match(line) - assert m is not None + m = re_line.match(line) + if m: fname = m.group(1) if not is_c_source(fname) and not is_cxx_source(fname): continue From ed5e3598f16a24022e78755e1a86b2d6ed12e5ca Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 28 Feb 2023 22:11:45 +0100 Subject: [PATCH 058/371] mimxrt/Makefile: Fix internal flash configuration and build. --- ports/mimxrt/Makefile | 54 +++++++++++++++----------- ports/mimxrt/boards/MIMXRT1064.ld | 10 +---- ports/mimxrt/hal/flexspi_hyper_flash.h | 5 ++- ports/mimxrt/hal/flexspi_nor_flash.h | 6 ++- 4 files changed, 41 insertions(+), 34 deletions(-) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index b73d3f4db9aa..800370e0ad4b 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -228,21 +228,33 @@ SHARED_SRC_C += \ shared/runtime/sys_stdio_mphal.c \ shared/timeutils/timeutils.c \ -# Add sources for respective board flash type -ifeq ($(MICROPY_HW_FLASH_TYPE),$(filter $(MICROPY_HW_FLASH_TYPE),qspi_nor_flash qspi_hyper_flash)) - # Add hal/flexspi_nor_flash.c or hal/flashspi_hyper_flash.c respectively - SRC_HAL_C += hal/flexspi_$(subst qspi_,,$(MICROPY_HW_FLASH_TYPE)).c - # - # Add custom (board specific) or default configuration - ifeq ($(MICROPY_HW_BOARD_FLASH_FILES),1) - SRC_HAL_C += $(BOARD_DIR)/$(MICROPY_HW_FLASH_TYPE)_config.c - else - SRC_HAL_C += hal/$(MICROPY_HW_FLASH_TYPE)_config.c - endif +# Set flash driver name, base address and internal flash flag, based on the flash type. +ifeq ($(MICROPY_HW_FLASH_TYPE),$(filter $(MICROPY_HW_FLASH_TYPE),qspi_nor_flash)) + MICROPY_HW_FLASH_BASE = 0x60000000 + FLEXSPI_FLASH_TYPE = $(MICROPY_HW_FLASH_TYPE) +else ifeq ($(MICROPY_HW_FLASH_TYPE),$(filter $(MICROPY_HW_FLASH_TYPE),qspi_hyper_flash)) + MICROPY_HW_FLASH_BASE = 0x60000000 + FLEXSPI_FLASH_TYPE = $(MICROPY_HW_FLASH_TYPE) +else ifeq ($(MICROPY_HW_FLASH_TYPE),$(filter $(MICROPY_HW_FLASH_TYPE),internal)) + # The internal flash is an SPI NOR Flash. + MICROPY_HW_FLASH_BASE = 0x70000000 + FLEXSPI_FLASH_TYPE = qspi_nor_flash + CFLAGS += -DMICROPY_HW_FLASH_INTERNAL else $(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE)) endif +# Add sources for respective board flash type +# Add hal/flexspi_nor_flash.c or hal/flashspi_hyper_flash.c respectively +SRC_HAL_C += hal/flexspi_$(subst qspi_,,$(FLEXSPI_FLASH_TYPE)).c +# +# Add custom (board specific) or default configuration +ifeq ($(MICROPY_HW_BOARD_FLASH_FILES),1) + SRC_HAL_C += $(BOARD_DIR)/$(FLEXSPI_FLASH_TYPE)_config.c +else + SRC_HAL_C += hal/$(FLEXSPI_FLASH_TYPE)_config.c +endif + # Math library source files ifeq ($(MICROPY_FLOAT_IMPL),double) LIBM_SRC_C += $(addprefix lib/libm_dbl/,\ @@ -341,18 +353,14 @@ CFLAGS += \ -Wno-error=unused-parameter # Configure respective board flash type -ifeq ($(MICROPY_HW_FLASH_TYPE),$(filter $(MICROPY_HW_FLASH_TYPE),qspi_nor_flash qspi_hyper_flash)) - # Add hal/flexspi_nor_flash.h or hal/flexspi_hyper_flash.h respectively - CFLAGS += -DBOARD_FLASH_OPS_HEADER_H=\"hal/flexspi_$(subst qspi_,,$(MICROPY_HW_FLASH_TYPE)).h\" - # - # Add custom (board specific) or default configuration - ifeq ($(MICROPY_HW_BOARD_FLASH_FILES),1) - CFLAGS += -DBOARD_FLASH_CONFIG_HEADER_H=\"$(BOARD)_flexspi_flash_config.h\" - else - CFLAGS += -DBOARD_FLASH_CONFIG_HEADER_H=\"hal/flexspi_flash_config.h\" - endif +# Add hal/flexspi_nor_flash.h or hal/flexspi_hyper_flash.h respectively +CFLAGS += -DBOARD_FLASH_OPS_HEADER_H=\"hal/flexspi_$(subst qspi_,,$(FLEXSPI_FLASH_TYPE)).h\" +# +# Add custom (board specific) or default configuration +ifeq ($(MICROPY_HW_BOARD_FLASH_FILES),1) + CFLAGS += -DBOARD_FLASH_CONFIG_HEADER_H=\"$(BOARD)_flexspi_flash_config.h\" else - $(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE)) + CFLAGS += -DBOARD_FLASH_CONFIG_HEADER_H=\"hal/flexspi_flash_config.h\" endif # Configure floating point support @@ -399,7 +407,7 @@ LDFLAGS += \ # the C preprocessor. Therefore keep LDDEFINES separated from LDFLAGS! LDDEFINES = \ - -DMICROPY_HW_FLASH_TYPE=$(MICROPY_HW_FLASH_TYPE) \ + -DMICROPY_HW_FLASH_BASE=$(MICROPY_HW_FLASH_BASE) \ -DMICROPY_HW_FLASH_SIZE=$(MICROPY_HW_FLASH_SIZE) ifdef MICROPY_HW_FLASH_RESERVED diff --git a/ports/mimxrt/boards/MIMXRT1064.ld b/ports/mimxrt/boards/MIMXRT1064.ld index 1fbc855fa13f..708dac4d515c 100644 --- a/ports/mimxrt/boards/MIMXRT1064.ld +++ b/ports/mimxrt/boards/MIMXRT1064.ld @@ -3,15 +3,7 @@ reserved_size = MICROPY_HW_FLASH_RESERVED; #endif -#if MICROPY_HW_FLASH_TYPE == qspi_nor_flash -flash_start = 0x60000000; -#elif MICROPY_HW_FLASH_TYPE == qspi_hyper_flash -flash_start = 0x60000000; -#elif MICROPY_HW_FLASH_TYPE == internal -flash_start = 0x70000000; -#else -#error Unknown MICROPY_HW_FLASH_TYPE -#endif +flash_start = MICROPY_HW_FLASH_BASE; flash_size = MICROPY_HW_FLASH_SIZE; flash_end = DEFINED(reserved_size) ? ((flash_start) + (flash_size - reserved_size)) : ((flash_start) + (flash_size)); flash_config_start = flash_start; diff --git a/ports/mimxrt/hal/flexspi_hyper_flash.h b/ports/mimxrt/hal/flexspi_hyper_flash.h index fe9acea429f6..3c016bc78e33 100644 --- a/ports/mimxrt/hal/flexspi_hyper_flash.h +++ b/ports/mimxrt/hal/flexspi_hyper_flash.h @@ -31,7 +31,10 @@ #include "fsl_flexspi.h" // #include BOARD_FLASH_CONFIG_HEADER_H -#if defined MIMXRT117x_SERIES +#if defined MICROPY_HW_FLASH_INTERNAL +#define BOARD_FLEX_SPI FLEXSPI2 +#define BOARD_FLEX_SPI_ADDR_BASE FlexSPI2_AMBA_BASE +#elif defined MIMXRT117x_SERIES #define BOARD_FLEX_SPI FLEXSPI1 #define BOARD_FLEX_SPI_ADDR_BASE FlexSPI1_AMBA_BASE #else diff --git a/ports/mimxrt/hal/flexspi_nor_flash.h b/ports/mimxrt/hal/flexspi_nor_flash.h index 69d57da6919e..edc81e37f129 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.h +++ b/ports/mimxrt/hal/flexspi_nor_flash.h @@ -27,9 +27,13 @@ #define MICROPY_INCLUDED_MIMXRT_HAL_FLEXSPI_NOR_FLASH_H #include "fsl_flexspi.h" +#include "mpconfigboard.h" #include BOARD_FLASH_CONFIG_HEADER_H -#if defined MIMXRT117x_SERIES +#if defined MICROPY_HW_FLASH_INTERNAL +#define BOARD_FLEX_SPI FLEXSPI2 +#define BOARD_FLEX_SPI_ADDR_BASE FlexSPI2_AMBA_BASE +#elif defined MIMXRT117x_SERIES #define BOARD_FLEX_SPI FLEXSPI1 #define BOARD_FLEX_SPI_ADDR_BASE FlexSPI1_AMBA_BASE #else From 944b4c205896588bc8653e360d048da6c80fa7a5 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 28 Feb 2023 22:14:31 +0100 Subject: [PATCH 059/371] mimxrt/boards/MIMXRT1064_EVK: Fix board config to use internal flash. This commit is necessary to make MicroPython work on this eval kit out of the box, as the eval kit ships with the HyperFlash physically disconnected from the bus (refer to the schematics or the user guide) and the QSPI connected instead, but the fuses/board/pins are configured to boot from internal flash (on FlexSPI2). Note that enabling the HyperFlash is not trivial, as it requires soldering and desoldering of many small footprint resistors and changing fuses. --- ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk index 08696d72ff44..b0bd7ec6ace4 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk @@ -3,8 +3,8 @@ MCU_VARIANT = MIMXRT1064DVL6A MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 -MICROPY_HW_FLASH_TYPE = qspi_hyper_flash -MICROPY_HW_FLASH_SIZE = 0x4000000 # 64MB +MICROPY_HW_FLASH_TYPE = internal +MICROPY_HW_FLASH_SIZE = 0x400000 # 4MB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB From 8b72721b29b9454191b83353ca6895f32361abc5 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 7 Mar 2023 19:34:10 +0100 Subject: [PATCH 060/371] mimxrt/mpconfigport: Allow configuring different network interfaces. This commit allows boards to disable Ethernet and keep the networking stack enabled, to use an alternate networking interface, such as WiFi. Note that the `network` and `socket` modules are now enabled by default for a board. --- ports/mimxrt/eth.c | 4 ++-- ports/mimxrt/mpconfigport.h | 23 ++++++++++++----------- ports/mimxrt/network_lan.c | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/ports/mimxrt/eth.c b/ports/mimxrt/eth.c index f1814286e621..d0df7610cc27 100644 --- a/ports/mimxrt/eth.c +++ b/ports/mimxrt/eth.c @@ -31,7 +31,7 @@ #include "py/mperrno.h" #include "ticks.h" -#if defined(MICROPY_HW_ETH_MDC) +#if defined(IOMUX_TABLE_ENET) #include "pin.h" #include "shared/netutils/netutils.h" @@ -639,4 +639,4 @@ void eth_low_power_mode(eth_t *self, bool enable) { ENET_EnableSleepMode(ENET, enable); #endif } -#endif // defined(MICROPY_HW_ETH_MDC) +#endif // defined(IOMUX_TABLE_ENET) diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 873e9e0d189b..13b456932c71 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -102,16 +102,19 @@ uint32_t 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) */ -// If MICROPY_PY_LWIP is defined, add network support -#if MICROPY_PY_LWIP - +#ifndef MICROPY_PY_NETWORK #define MICROPY_PY_NETWORK (1) +#endif +#ifndef MICROPY_PY_USOCKET #define MICROPY_PY_USOCKET (1) -#define MICROPY_PY_UWEBSOCKET (1) -#define MICROPY_PY_WEBREPL (1) -#define MICROPY_PY_UHASHLIB_SHA1 (1) -#define MICROPY_PY_LWIP_SOCK_RAW (1) -#define MICROPY_HW_ETH_MDC (1) +#endif +#define MICROPY_PY_UWEBSOCKET (MICROPY_PY_LWIP) +#define MICROPY_PY_WEBREPL (MICROPY_PY_LWIP) +#define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) +#define MICROPY_PY_USSL_FINALISER (MICROPY_PY_USSL) +// #define MICROPY_PY_UHASHLIB_MD5 (MICROPY_PY_USSL) +#define MICROPY_PY_UHASHLIB_SHA1 (MICROPY_PY_USSL) +// #define MICROPY_PY_UCRYPTOLIB (MICROPY_PY_USSL) // Prevent the "LWIP task" from running. #define MICROPY_PY_LWIP_ENTER MICROPY_PY_PENDSV_ENTER @@ -122,8 +125,6 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-mimxrt" #endif -#endif - // For regular code that wants to prevent "background tasks" from running. // These background tasks (LWIP, Bluetooth) run in PENDSV context. // TODO: Check for the settings of the STM32 port in irq.h @@ -165,7 +166,7 @@ static inline void restore_irq_pri(uint32_t basepri) { #define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq() #define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state) -#if defined(MICROPY_HW_ETH_MDC) +#if defined(IOMUX_TABLE_ENET) extern const struct _mp_obj_type_t network_lan_type; #define MICROPY_HW_NIC_ETH { MP_ROM_QSTR(MP_QSTR_LAN), MP_ROM_PTR(&network_lan_type) }, #else diff --git a/ports/mimxrt/network_lan.c b/ports/mimxrt/network_lan.c index 39565c591760..355698e0b588 100644 --- a/ports/mimxrt/network_lan.c +++ b/ports/mimxrt/network_lan.c @@ -28,7 +28,7 @@ #include "py/mphal.h" #include "extmod/modnetwork.h" -#if defined(MICROPY_HW_ETH_MDC) +#if defined(IOMUX_TABLE_ENET) #include "fsl_phy.h" #include "eth.h" @@ -264,4 +264,4 @@ MP_DEFINE_CONST_OBJ_TYPE( ); -#endif // defined(MICROPY_HW_ETH_MDC) +#endif // defined(IOMUX_TABLE_ENET) From bde222ce849d3d247f588bc6db119b3d2f45faf8 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sun, 9 Apr 2023 19:20:09 +0200 Subject: [PATCH 061/371] mimxrt/modmachine: Implement machine.bootloader(). If a board defines a custom bootloader entry function it will be called first, if not and the ROM API supports RUN bootloader API, then `machine.bootloader()` will jump to the ROM serial downloader in USB mode. --- ports/mimxrt/Makefile | 1 + ports/mimxrt/modmachine.c | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 800370e0ad4b..abde70f12c94 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -136,6 +136,7 @@ SRC_HAL_IMX_C += \ $(MCU_DIR)/drivers/fsl_lpuart.c \ $(MCU_DIR)/drivers/fsl_pit.c \ $(MCU_DIR)/drivers/fsl_pwm.c \ + $(MCU_DIR)/drivers/fsl_romapi.c \ $(MCU_DIR)/drivers/fsl_sai.c \ $(MCU_DIR)/drivers/fsl_snvs_lp.c \ $(MCU_DIR)/drivers/fsl_wdog.c \ diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index 9097289e79bd..5c897578d415 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -38,6 +38,7 @@ #include "pin.h" #include "modmachine.h" #include "fsl_wdog.h" +#include "fsl_romapi.h" #if MICROPY_PY_MACHINE @@ -108,6 +109,24 @@ STATIC mp_obj_t machine_enable_irq(mp_obj_t state_in) { } MP_DEFINE_CONST_FUN_OBJ_1(machine_enable_irq_obj, machine_enable_irq); +NORETURN mp_obj_t 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); + #elif FSL_ROM_HAS_RUNBOOTLOADER_API + // If not, enter ROM bootloader in serial downloader / USB mode. + uint32_t arg = 0xEB110000; + ROM_RunBootloader(&arg); + #else + // No custom bootloader, or run bootloader API, then just reset. + WDOG_TriggerSystemSoftwareReset(WDOG1); + #endif + while (1) { + ; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_bootloader_obj, 0, 1, machine_bootloader); + STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) }, { MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) }, @@ -144,6 +163,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_bootloader), MP_ROM_PTR(&machine_bootloader_obj) }, #if MICROPY_PY_MACHINE_BITSTREAM { MP_ROM_QSTR(MP_QSTR_bitstream), MP_ROM_PTR(&machine_bitstream_obj) }, From 0acc73344a38cb04eb014bbf68aba31135ceff47 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Wed, 12 Apr 2023 16:58:56 +0200 Subject: [PATCH 062/371] mimxrt: Fix the build for boards without ROM API. --- ports/mimxrt/Makefile | 4 ++-- ports/mimxrt/modmachine.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index abde70f12c94..4e4fd1752847 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -136,7 +136,6 @@ SRC_HAL_IMX_C += \ $(MCU_DIR)/drivers/fsl_lpuart.c \ $(MCU_DIR)/drivers/fsl_pit.c \ $(MCU_DIR)/drivers/fsl_pwm.c \ - $(MCU_DIR)/drivers/fsl_romapi.c \ $(MCU_DIR)/drivers/fsl_sai.c \ $(MCU_DIR)/drivers/fsl_snvs_lp.c \ $(MCU_DIR)/drivers/fsl_wdog.c \ @@ -153,7 +152,8 @@ endif ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES), MIMXRT1015 MIMXRT1021 MIMXRT1052 MIMXRT1062 MIMXRT1064 MIMXRT1176)) SRC_HAL_IMX_C += \ - $(MCU_DIR)/drivers/fsl_qtmr.c + $(MCU_DIR)/drivers/fsl_qtmr.c \ + $(MCU_DIR)/drivers/fsl_romapi.c endif ifeq ($(MCU_SERIES), MIMXRT1176) diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index 5c897578d415..049499cb2ed0 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -38,7 +38,9 @@ #include "pin.h" #include "modmachine.h" #include "fsl_wdog.h" +#if FSL_FEATURE_BOOT_ROM_HAS_ROMAPI #include "fsl_romapi.h" +#endif #if MICROPY_PY_MACHINE From b525f1c9ec8ffa9009754578932f3fad5f63026b Mon Sep 17 00:00:00 2001 From: Jonas Scharpf Date: Wed, 5 Apr 2023 07:48:53 +0200 Subject: [PATCH 063/371] tools/mpremote: Add ctrl-x as additonal mpremote disconnect shortcut. The mpremote REPL can now be closed with either ctrl+] or ctrl+x, which gives users a choice, useful if the ']' key is difficult to access. Fixes issue #11197. Signed-off-by: Jonas Scharpf --- tools/mpremote/mpremote/repl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mpremote/mpremote/repl.py b/tools/mpremote/mpremote/repl.py index 3d6ca1881b1e..d5eb2b87120a 100644 --- a/tools/mpremote/mpremote/repl.py +++ b/tools/mpremote/mpremote/repl.py @@ -8,7 +8,7 @@ def do_repl_main_loop(state, console_in, console_out_write, *, code_to_inject, f console_in.waitchar(state.pyb.serial) c = console_in.readchar() if c: - if c == b"\x1d": # ctrl-], quit + if c in (b"\x1d", b"\x18"): # ctrl-] or ctrl-x, quit break elif c == b"\x04": # ctrl-D # special handling needed for ctrl-D if filesystem is mounted @@ -56,7 +56,7 @@ def do_repl(state, args): file_to_inject = args.inject_file print("Connected to MicroPython at %s" % state.pyb.device_name) - print("Use Ctrl-] to exit this shell") + print("Use Ctrl-] or Ctrl-x to exit this shell") if capture_file is not None: print('Capturing session to file "%s"' % capture_file) capture_file = open(capture_file, "wb") From bf3eb9dc3911b8e4e7da3054ffece61ae5e2ef64 Mon Sep 17 00:00:00 2001 From: "David (Pololu)" Date: Wed, 12 Apr 2023 18:58:44 -0700 Subject: [PATCH 064/371] rp2/machine_i2c: Add timeout parameter for machine.I2C(). This commit adds support for the `timeout` keyword argument to machine.I2C on the rp2 port, following how it's done on other ports. The main motivation here is avoid the interpreter crashing due to infinite loops when SDA is stuck low, which is quite common if the board gets reset while reading from an I2C device. A default timeout of 50ms is chosen because it's consistent with: - Commit a707fe50b085ec83722106609f6fd219faf9f030 which used a timeout of 50,000us for zero-length writes on the rp2 port. - The machine.SoftI2C class which uses 50,000us as the default timeout. - The stm32 port's hardware I2C, which uses 50,000us for I2C_POLL_DEFAULT_TIMEOUT_US. This commit also fixes the default timeout on the esp32 port to be consistent with the above, and updates the documentation for machine.I2C to document this keyword argument. --- docs/library/machine.I2C.rst | 4 +++- ports/esp32/machine_i2c.c | 2 +- ports/rp2/machine_i2c.c | 16 ++++++++++------ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/library/machine.I2C.rst b/docs/library/machine.I2C.rst index 0eb1b67f586d..bfc9f7ebcc9b 100644 --- a/docs/library/machine.I2C.rst +++ b/docs/library/machine.I2C.rst @@ -52,7 +52,7 @@ Example usage:: Constructors ------------ -.. class:: I2C(id, *, scl, sda, freq=400000) +.. class:: I2C(id, *, scl, sda, freq=400000, timeout=50000) Construct and return a new I2C object using the following parameters: @@ -62,6 +62,8 @@ Constructors - *sda* should be a pin object specifying the pin to use for SDA. - *freq* should be an integer which sets the maximum frequency for SCL. + - *timeout* is the maximum time in microseconds to allow for I2C + transactions. This parameter is not allowed on some ports. Note that some ports/boards will have default values of *scl* and *sda* that can be changed in this constructor. Others will have fixed values diff --git a/ports/esp32/machine_i2c.c b/ports/esp32/machine_i2c.c index c805dab87ee2..9ec39e564966 100644 --- a/ports/esp32/machine_i2c.c +++ b/ports/esp32/machine_i2c.c @@ -62,7 +62,7 @@ #error "unsupported I2C for ESP32 SoC variant" #endif -#define I2C_DEFAULT_TIMEOUT_US (10000) // 10ms +#define I2C_DEFAULT_TIMEOUT_US (50000) // 50ms typedef struct _machine_hw_i2c_obj_t { mp_obj_base_t base; diff --git a/ports/rp2/machine_i2c.c b/ports/rp2/machine_i2c.c index 85d12c771368..fc4c0723a50a 100644 --- a/ports/rp2/machine_i2c.c +++ b/ports/rp2/machine_i2c.c @@ -33,6 +33,7 @@ #include "hardware/i2c.h" #define DEFAULT_I2C_FREQ (400000) +#define DEFAULT_I2C_TIMEOUT (50000) #ifndef MICROPY_HW_I2C0_SCL #if PICO_DEFAULT_I2C == 0 @@ -65,6 +66,7 @@ typedef struct _machine_i2c_obj_t { uint8_t scl; uint8_t sda; uint32_t freq; + uint32_t timeout; } machine_i2c_obj_t; STATIC machine_i2c_obj_t machine_i2c_obj[] = { @@ -74,17 +76,18 @@ STATIC machine_i2c_obj_t machine_i2c_obj[] = { 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=%u, sda=%u)", - self->i2c_id, self->freq, self->scl, self->sda); + mp_printf(print, "I2C(%u, freq=%u, scl=%u, sda=%u, timeout=%u)", + self->i2c_id, self->freq, self->scl, self->sda, self->timeout); } 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[] = { { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_freq, MP_ARG_INT, {.u_int = DEFAULT_I2C_FREQ} }, { MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_I2C_TIMEOUT} }, }; // Parse args. @@ -121,6 +124,7 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n self->freq = args[ARG_freq].u_int; i2c_init(self->i2c_inst, self->freq); self->freq = i2c_set_baudrate(self->i2c_inst, self->freq); + self->timeout = args[ARG_timeout].u_int; gpio_set_function(self->scl, GPIO_FUNC_I2C); gpio_set_function(self->sda, GPIO_FUNC_I2C); gpio_set_pulls(self->scl, true, 0); @@ -135,14 +139,14 @@ STATIC int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si int ret; bool nostop = !(flags & MP_MACHINE_I2C_FLAG_STOP); if (flags & MP_MACHINE_I2C_FLAG_READ) { - ret = i2c_read_blocking(self->i2c_inst, addr, buf, len, nostop); + ret = i2c_read_timeout_us(self->i2c_inst, addr, buf, len, nostop, self->timeout); } else { if (len == 0) { // Workaround issue with hardware I2C not accepting zero-length writes. mp_machine_soft_i2c_obj_t soft_i2c = { .base = { &mp_machine_soft_i2c_type }, .us_delay = 500000 / self->freq + 1, - .us_timeout = 50000, + .us_timeout = self->timeout, .scl = self->scl, .sda = self->sda, }; @@ -157,7 +161,7 @@ STATIC int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si gpio_set_function(self->sda, GPIO_FUNC_I2C); return ret; } else { - ret = i2c_write_blocking(self->i2c_inst, addr, buf, len, nostop); + ret = i2c_write_timeout_us(self->i2c_inst, addr, buf, len, nostop, self->timeout); } } if (ret < 0) { From ec1eeccab45af6893f19cd07292808bbe7c62cf5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 21 Apr 2023 18:17:34 +1000 Subject: [PATCH 065/371] shared/tinyusb: Revert setting of CFG_TUD_CDC_EP_BUFSIZE to 256. This reverts commit 0613d3e3561a82fffa36594647ef916b19bc912b. The value of CFG_TUD_CDC_EP_BUFSIZE should match the endpoint size, otherwise data may stay in the lower layers of the USB stack (and not passed up to the higher layers) until a total of CFG_TUD_CDC_EP_BUFSIZE bytes have been received, which may not happen for a long time. Fixes issue #11253. Signed-off-by: Damien George --- shared/tinyusb/tusb_config.h | 1 - 1 file changed, 1 deletion(-) diff --git a/shared/tinyusb/tusb_config.h b/shared/tinyusb/tusb_config.h index a6410a1c9301..28bee09a594f 100644 --- a/shared/tinyusb/tusb_config.h +++ b/shared/tinyusb/tusb_config.h @@ -59,7 +59,6 @@ // CDC Configuration #if CFG_TUD_CDC -#define CFG_TUD_CDC_EP_BUFSIZE (256) #define CFG_TUD_CDC_RX_BUFSIZE (256) #define CFG_TUD_CDC_TX_BUFSIZE (256) #endif From 9e6885ad820a9f42bf93df56eb3f3be0c8639622 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 22 Apr 2023 00:39:31 +1000 Subject: [PATCH 066/371] extmod/btstack: Switch to use hci_dump_init instead of hci_dump_open. The latter is no longer available in the version of BTstack now in use by this repository. Signed-off-by: Damien George --- extmod/btstack/btstack.mk | 1 + extmod/btstack/btstack_config_common.h | 1 + ports/stm32/mpbtstackport.c | 3 ++- ports/unix/mpbtstackport_common.c | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/extmod/btstack/btstack.mk b/extmod/btstack/btstack.mk index ef730d2cf33e..281d032ae110 100644 --- a/extmod/btstack/btstack.mk +++ b/extmod/btstack/btstack.mk @@ -35,6 +35,7 @@ INC += -I$(BTSTACK_DIR)/3rd-party/yxml SRC_BTSTACK_C = \ $(addprefix lib/btstack/src/, $(SRC_FILES)) \ $(addprefix lib/btstack/src/ble/, $(filter-out %_tlv.c, $(SRC_BLE_FILES))) \ + lib/btstack/platform/embedded/hci_dump_embedded_stdout.c \ ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),1) ifeq ($(MICROPY_BLUETOOTH_BTSTACK_H4),1) diff --git a/extmod/btstack/btstack_config_common.h b/extmod/btstack/btstack_config_common.h index 8118c4c1a958..0f616f7505e6 100644 --- a/extmod/btstack/btstack_config_common.h +++ b/extmod/btstack/btstack_config_common.h @@ -7,6 +7,7 @@ #define ENABLE_LE_CENTRAL // #define ENABLE_CLASSIC #define ENABLE_L2CAP_LE_CREDIT_BASED_FLOW_CONTROL_MODE +#define ENABLE_PRINTF_HEXDUMP // #define ENABLE_LOG_INFO // #define ENABLE_LOG_DEBUG #define ENABLE_LOG_ERROR diff --git a/ports/stm32/mpbtstackport.c b/ports/stm32/mpbtstackport.c index 301ac30e2060..728594d19c96 100644 --- a/ports/stm32/mpbtstackport.c +++ b/ports/stm32/mpbtstackport.c @@ -32,6 +32,7 @@ #include "lib/btstack/src/btstack.h" #include "lib/btstack/src/hci_transport_h4.h" +#include "lib/btstack/platform/embedded/hci_dump_embedded_stdout.h" #include "extmod/mpbthci.h" #include "extmod/btstack/btstack_hci_uart.h" #include "extmod/btstack/modbluetooth_btstack.h" @@ -140,7 +141,7 @@ void mp_bluetooth_hci_poll(void) { void mp_bluetooth_btstack_port_init(void) { btstack_run_loop_init(&mp_btstack_runloop_stm32); - // hci_dump_open(NULL, HCI_DUMP_STDOUT); + // hci_dump_init(hci_dump_embedded_stdout_get_instance()); const hci_transport_t *transport = hci_transport_h4_instance_for_uart(&mp_bluetooth_btstack_hci_uart_block); hci_init(transport, &hci_transport_config_uart); diff --git a/ports/unix/mpbtstackport_common.c b/ports/unix/mpbtstackport_common.c index 66a3a0536aed..36412e96f4e0 100644 --- a/ports/unix/mpbtstackport_common.c +++ b/ports/unix/mpbtstackport_common.c @@ -35,6 +35,7 @@ #include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" #include "lib/btstack/platform/embedded/hal_cpu.h" #include "lib/btstack/platform/embedded/hal_time_ms.h" +#include "lib/btstack/platform/embedded/hci_dump_embedded_stdout.h" #include "extmod/btstack/modbluetooth_btstack.h" @@ -81,7 +82,7 @@ uint32_t hal_time_ms(void) { void mp_bluetooth_btstack_port_init(void) { btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); - // hci_dump_open(NULL, HCI_DUMP_STDOUT); + // hci_dump_init(hci_dump_embedded_stdout_get_instance()); #if MICROPY_BLUETOOTH_BTSTACK_H4 mp_bluetooth_btstack_port_init_h4(); From bc9ec1cf718361c1f361bb4ff6de3c660d84696d Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 2 Mar 2023 15:40:59 +1100 Subject: [PATCH 067/371] extmod/modbluetooth: Merge gatts_notify/indicate implementation. Makes gatts_notify and gatts_indicate work in the same way: by default they send the DB value, but you can manually override the payload. In other words, makes gatts_indicate work the same as gatts_notify. Note: This removes support for queuing notifications and indications on btstack when the ACL buffer is full. This functionality will be reimplemented in a future commit. Signed-off-by: Jim Mussared --- docs/library/bluetooth.rst | 19 ++-- extmod/btstack/modbluetooth_btstack.c | 125 +++++--------------------- extmod/modbluetooth.c | 32 ++++--- extmod/modbluetooth.h | 16 ++-- extmod/nimble/modbluetooth_nimble.c | 51 ++++++----- ports/zephyr/modbluetooth_zephyr.c | 18 +--- 6 files changed, 88 insertions(+), 173 deletions(-) diff --git a/docs/library/bluetooth.rst b/docs/library/bluetooth.rst index 8f7041e8d3fb..63578af16e4c 100644 --- a/docs/library/bluetooth.rst +++ b/docs/library/bluetooth.rst @@ -514,19 +514,24 @@ writes from a client to a given characteristic, use Sends a notification request to a connected client. - If *data* is not ``None``, then that value is sent to the client as part of - the notification. The local value will not be modified. + If *data* is ``None`` (the default), then the current local value (as set + with :meth:`gatts_write `) will be sent. - Otherwise, if *data* is ``None``, then the current local value (as - set with :meth:`gatts_write `) will be sent. + Otherwise, if *data* is not ``None``, then that value is sent to the client + as part of the notification. The local value will not be modified. **Note:** The notification will be sent regardless of the subscription status of the client to this characteristic. -.. method:: BLE.gatts_indicate(conn_handle, value_handle, /) +.. method:: BLE.gatts_indicate(conn_handle, value_handle, data=None, /) - Sends an indication request containing the characteristic's current value to - a connected client. + Sends a indication request to a connected client. + + If *data* is ``None`` (the default), then the current local value (as set + with :meth:`gatts_write `) will be sent. + + Otherwise, if *data* is not ``None``, then that value is sent to the client + as part of the indication. The local value will not be modified. On acknowledgment (or failure, e.g. timeout), the ``_IRQ_GATTS_INDICATE_DONE`` event will be raised. diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index e9c0037394b2..243b6e38caa1 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -130,8 +130,6 @@ STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(uint16_t uuid16, const uint8_t *uu // Pending operation types. enum { // Queued for sending when possible. - MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY, // Waiting for context callback - MP_BLUETOOTH_BTSTACK_PENDING_INDICATE, // Waiting for context callback MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, // Waiting for conn handle // Hold buffer pointer until complete. MP_BLUETOOTH_BTSTACK_PENDING_WRITE, // Waiting for write done event @@ -150,11 +148,7 @@ struct _mp_btstack_pending_op_t { uint16_t conn_handle; uint16_t value_handle; - // For notify/indicate only. - // context_registration.context will point back to this struct. - btstack_context_callback_registration_t context_registration; - - // For notify/indicate/write-without-response, this is the actual buffer to send. + // For write-without-response, this is the actual buffer to send. // For write-with-response, just holding onto the buffer for GC ref. size_t len; uint8_t buf[]; @@ -170,30 +164,6 @@ STATIC void btstack_remove_pending_operation(mp_btstack_pending_op_t *pending_op } } -// Called in response to a gatts_notify/indicate being unable to complete, which then calls -// att_server_request_to_send_notification. -// We now have an opportunity to re-try the operation with an empty ACL buffer. -STATIC void btstack_notify_indicate_ready_handler(void *context) { - MICROPY_PY_BLUETOOTH_ENTER - mp_btstack_pending_op_t *pending_op = (mp_btstack_pending_op_t *)context; - DEBUG_printf("btstack_notify_indicate_ready_handler op_type=%d conn_handle=%d value_handle=%d len=%zu\n", pending_op->op_type, pending_op->conn_handle, pending_op->value_handle, pending_op->len); - if (pending_op->op_type == MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY) { - int err = att_server_notify(pending_op->conn_handle, pending_op->value_handle, pending_op->buf, pending_op->len); - DEBUG_printf("btstack_notify_indicate_ready_handler: sending notification err=%d\n", err); - assert(err == ERROR_CODE_SUCCESS); - (void)err; - } else { - assert(pending_op->op_type == MP_BLUETOOTH_BTSTACK_PENDING_INDICATE); - int err = att_server_indicate(pending_op->conn_handle, pending_op->value_handle, NULL, 0); - DEBUG_printf("btstack_notify_indicate_ready_handler: sending indication err=%d\n", err); - assert(err == ERROR_CODE_SUCCESS); - (void)err; - } - // Can't free the pending op as we're in IRQ context. Leave it for the GC. - btstack_remove_pending_operation(pending_op, false /* del */); - MICROPY_PY_BLUETOOTH_EXIT -} - // Register a pending background operation -- copies the buffer, and makes it known to the GC. STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_type, uint16_t conn_handle, uint16_t value_handle, const uint8_t *buf, size_t len) { DEBUG_printf("btstack_enqueue_pending_operation op_type=%d conn_handle=%d value_handle=%d len=%zu\n", op_type, conn_handle, value_handle, len); @@ -204,11 +174,6 @@ STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_ty pending_op->len = len; memcpy(pending_op->buf, buf, len); - if (op_type == MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY || op_type == MP_BLUETOOTH_BTSTACK_PENDING_INDICATE) { - pending_op->context_registration.callback = &btstack_notify_indicate_ready_handler; - pending_op->context_registration.context = pending_op; - } - MICROPY_PY_BLUETOOTH_ENTER bool added = btstack_linked_list_add(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops, (btstack_linked_item_t *)pending_op); assert(added); @@ -854,7 +819,7 @@ void mp_bluetooth_set_io_capability(uint8_t capability) { #endif // MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) { - uint8_t *value = NULL; + const uint8_t *value = NULL; size_t value_len = 0; mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, BTSTACK_GAP_DEVICE_NAME_HANDLE, &value, &value_len); *buf = value; @@ -1095,7 +1060,7 @@ int mp_bluetooth_gatts_register_service_end(void) { return 0; } -int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) { +int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len) { DEBUG_printf("mp_bluetooth_gatts_read\n"); if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; @@ -1114,85 +1079,41 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); } -int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { - DEBUG_printf("mp_bluetooth_gatts_notify\n"); +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) { + DEBUG_printf("mp_bluetooth_gatts_notify_indicate\n"); if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - // Note: btstack doesn't appear to support sending a notification without a value, so include the stored value. - uint8_t *data = NULL; - size_t len = 0; - mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len); - return mp_bluetooth_gatts_notify_send(conn_handle, value_handle, data, len); -} - -int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) { - DEBUG_printf("mp_bluetooth_gatts_notify_send\n"); - - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; + if (!value) { + // NULL value means "use DB value". + mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &value, &value_len); } + int err = ERROR_CODE_UNKNOWN_HCI_COMMAND; + // Attempt to send immediately. If it succeeds, btstack will copy the buffer. MICROPY_PY_BLUETOOTH_ENTER - int err = att_server_notify(conn_handle, value_handle, value, value_len); - MICROPY_PY_BLUETOOTH_EXIT - - if (err == BTSTACK_ACL_BUFFERS_FULL) { - DEBUG_printf("mp_bluetooth_gatts_notify_send: ACL buffer full, scheduling callback\n"); - // Schedule callback, making a copy of the buffer. - mp_btstack_pending_op_t *pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY, conn_handle, value_handle, value, value_len); - - err = att_server_request_to_send_notification(&pending_op->context_registration, conn_handle); - - if (err != ERROR_CODE_SUCCESS) { - // Failure. Unref and free the pending operation. - btstack_remove_pending_operation(pending_op, true /* del */); - } - - return 0; - } else { - return btstack_error_to_errno(err); - } -} - -int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { - DEBUG_printf("mp_bluetooth_gatts_indicate\n"); - - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; + switch (gatts_op) { + case MP_BLUETOOTH_GATTS_OP_NOTIFY: + err = att_server_notify(conn_handle, value_handle, value, value_len); + break; + case MP_BLUETOOTH_GATTS_OP_INDICATE: + // Indicate will raise ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE when + // acknowledged (or timeout/error). + err = att_server_indicate(conn_handle, value_handle, value, value_len); + break; } - - uint8_t *data = NULL; - size_t len = 0; - mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len); - - // Indicate will raise ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE when - // acknowledged (or timeout/error). - - // Attempt to send immediately, will copy buffer. - MICROPY_PY_BLUETOOTH_ENTER - int err = att_server_indicate(conn_handle, value_handle, data, len); MICROPY_PY_BLUETOOTH_EXIT if (err == BTSTACK_ACL_BUFFERS_FULL) { - DEBUG_printf("mp_bluetooth_gatts_indicate: ACL buffer full, scheduling callback\n"); - // Schedule callback, making a copy of the buffer. - mp_btstack_pending_op_t *pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_INDICATE, conn_handle, value_handle, data, len); - - err = att_server_request_to_send_indication(&pending_op->context_registration, conn_handle); - - if (err != ERROR_CODE_SUCCESS) { - // Failure. Unref and free the pending operation. - btstack_remove_pending_operation(pending_op, true /* del */); - } + DEBUG_printf("mp_bluetooth_gatts_notify_indicate: ACL buffer full, scheduling callback\n"); - return 0; - } else { - return btstack_error_to_errno(err); + // TODO: re-implement the handling for this. } + + return btstack_error_to_errno(err); } int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 3a51fb8b2399..e3ef40d78913 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -733,7 +733,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gap_passkey_obj, 4, 4, STATIC mp_obj_t bluetooth_ble_gatts_read(mp_obj_t self_in, mp_obj_t value_handle_in) { (void)self_in; size_t len = 0; - uint8_t *buf; + const uint8_t *buf; mp_bluetooth_gatts_read(mp_obj_get_int(value_handle_in), &buf, &len); return mp_obj_new_bytes(buf, len); } @@ -751,32 +751,30 @@ STATIC mp_obj_t bluetooth_ble_gatts_write(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_write_obj, 3, 4, bluetooth_ble_gatts_write); -STATIC mp_obj_t bluetooth_ble_gatts_notify(size_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t bluetooth_ble_gatts_notify_indicate(size_t n_args, const mp_obj_t *args, int gatts_op) { mp_int_t conn_handle = mp_obj_get_int(args[1]); mp_int_t value_handle = mp_obj_get_int(args[2]); + const uint8_t *value = NULL; + size_t value_len = 0; if (n_args == 4 && args[3] != mp_const_none) { mp_buffer_info_t bufinfo = {0}; mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); - int err = mp_bluetooth_gatts_notify_send(conn_handle, value_handle, bufinfo.buf, bufinfo.len); - bluetooth_handle_errno(err); - return mp_const_none; - } else { - int err = mp_bluetooth_gatts_notify(conn_handle, value_handle); - return bluetooth_handle_errno(err); + value = bufinfo.buf; + value_len = bufinfo.len; } + return bluetooth_handle_errno(mp_bluetooth_gatts_notify_indicate(conn_handle, value_handle, gatts_op, value, value_len)); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_notify_obj, 3, 4, bluetooth_ble_gatts_notify); -STATIC mp_obj_t bluetooth_ble_gatts_indicate(mp_obj_t self_in, mp_obj_t conn_handle_in, mp_obj_t value_handle_in) { - (void)self_in; - mp_int_t conn_handle = mp_obj_get_int(conn_handle_in); - mp_int_t value_handle = mp_obj_get_int(value_handle_in); +STATIC mp_obj_t bluetooth_ble_gatts_notify(size_t n_args, const mp_obj_t *args) { + return bluetooth_ble_gatts_notify_indicate(n_args, args, MP_BLUETOOTH_GATTS_OP_NOTIFY); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_notify_obj, 3, 4, bluetooth_ble_gatts_notify); - int err = mp_bluetooth_gatts_indicate(conn_handle, value_handle); - return bluetooth_handle_errno(err); +STATIC mp_obj_t bluetooth_ble_gatts_indicate(size_t n_args, const mp_obj_t *args) { + return bluetooth_ble_gatts_notify_indicate(n_args, args, MP_BLUETOOTH_GATTS_OP_INDICATE); } -STATIC MP_DEFINE_CONST_FUN_OBJ_3(bluetooth_ble_gatts_indicate_obj, bluetooth_ble_gatts_indicate); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_indicate_obj, 3, 4, bluetooth_ble_gatts_indicate); STATIC mp_obj_t bluetooth_ble_gatts_set_buffer(size_t n_args, const mp_obj_t *args) { mp_int_t value_handle = mp_obj_get_int(args[1]); @@ -1718,7 +1716,7 @@ mp_bluetooth_gatts_db_entry_t *mp_bluetooth_gatts_db_lookup(mp_gatts_db_t db, ui return MP_OBJ_TO_PTR(elem->value); } -int mp_bluetooth_gatts_db_read(mp_gatts_db_t db, uint16_t handle, uint8_t **value, size_t *value_len) { +int mp_bluetooth_gatts_db_read(mp_gatts_db_t db, uint16_t handle, const uint8_t **value, size_t *value_len) { MICROPY_PY_BLUETOOTH_ENTER mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(db, handle); if (entry) { diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index ca6436b678d1..fc7012ecfba0 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -186,6 +186,10 @@ #define MP_BLUETOOTH_PASSKEY_ACTION_DISPLAY (3) #define MP_BLUETOOTH_PASSKEY_ACTION_NUMERIC_COMPARISON (4) +// These are the ops for mp_bluetooth_gatts_notify_indicate. +#define MP_BLUETOOTH_GATTS_OP_NOTIFY (1) +#define MP_BLUETOOTH_GATTS_OP_INDICATE (2) + /* These aren't included in the module for space reasons, but can be used in your Python code if necessary. @@ -333,15 +337,11 @@ int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, m int mp_bluetooth_gatts_register_service_end(void); // Read the value from the local gatts db (likely this has been written by a central). -int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len); +int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len); // Write a value to the local gatts db (ready to be queried by a central). Optionally send notifications/indications. int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len, bool send_update); -// Notify the central that it should do a read. -int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle); -// Notify the central, including a data payload. (Note: does not set the gatts db value). -int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len); -// Indicate the central. -int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle); +// Send a notification/indication to the central, optionally with custom payload (otherwise the DB value is used). +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); // Resize and enable/disable append-mode on a value. // Append-mode means that remote writes will append and local reads will clear after reading. @@ -508,7 +508,7 @@ STATIC inline void mp_bluetooth_gatts_db_reset(mp_gatts_db_t db) { void mp_bluetooth_gatts_db_create_entry(mp_gatts_db_t db, uint16_t handle, size_t len); mp_bluetooth_gatts_db_entry_t *mp_bluetooth_gatts_db_lookup(mp_gatts_db_t db, uint16_t handle); -int mp_bluetooth_gatts_db_read(mp_gatts_db_t db, uint16_t handle, uint8_t **value, size_t *value_len); +int mp_bluetooth_gatts_db_read(mp_gatts_db_t db, uint16_t handle, const uint8_t **value, size_t *value_len); int mp_bluetooth_gatts_db_write(mp_gatts_db_t db, uint16_t handle, const uint8_t *value, size_t value_len); int mp_bluetooth_gatts_db_resize(mp_gatts_db_t db, uint16_t handle, size_t len, bool append); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index b0194446bd4b..b2667300ca76 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -1008,7 +1008,7 @@ int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { return ble_hs_err_to_errno(ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM)); } -int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) { +int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } @@ -1026,35 +1026,40 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t return err; } -// TODO: Could use ble_gatts_chr_updated to send to all subscribed centrals. - -int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { +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; } - // Confusingly, notify/notify_custom/indicate are "gattc" function (even though they're used by peripherals (i.e. gatt servers)). - // See https://www.mail-archive.com/dev@mynewt.apache.org/msg01293.html - return ble_hs_err_to_errno(ble_gattc_notify(conn_handle, value_handle)); -} -int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) { - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - struct os_mbuf *om = ble_hs_mbuf_from_flat(value, value_len); - if (om == NULL) { - return MP_ENOMEM; + int err = BLE_HS_EINVAL; + + // NULL om in the _custom methods means "use DB value" (NimBLE will call + // back into mp_bluetooth_gatts_read for us). + struct os_mbuf *om = NULL; + + if (value) { + om = ble_hs_mbuf_from_flat(value, value_len); + if (om == NULL) { + return MP_ENOMEM; + } } - return ble_hs_err_to_errno(ble_gattc_notify_custom(conn_handle, value_handle, om)); -} -int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; + // Note: Confusingly, Nimble's notify/notify_custom and indicate/indicate_custom + // are "gattc" functions (even though they're used by peripherals, i.e. gatt servers). + // See https://www.mail-archive.com/dev@mynewt.apache.org/msg01293.html + + switch (gatts_op) { + case MP_BLUETOOTH_GATTS_OP_NOTIFY: + err = ble_gattc_notify_custom(conn_handle, value_handle, om); + break; + case MP_BLUETOOTH_GATTS_OP_INDICATE: + // This will raise BLE_GAP_EVENT_NOTIFY_TX with a status when it is + // acknowledged (or timeout/error). + err = ble_gattc_indicate_custom(conn_handle, value_handle, om); + break; } - // This will raise BLE_GAP_EVENT_NOTIFY_TX with a status when it is - // acknowledged (or timeout/error). - return ble_hs_err_to_errno(ble_gattc_indicate(conn_handle, value_handle)); + + return ble_hs_err_to_errno(err); } int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { diff --git a/ports/zephyr/modbluetooth_zephyr.c b/ports/zephyr/modbluetooth_zephyr.c index 4d4b19a1d98c..279e4ca9a03b 100644 --- a/ports/zephyr/modbluetooth_zephyr.c +++ b/ports/zephyr/modbluetooth_zephyr.c @@ -301,7 +301,7 @@ int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { return MP_EOPNOTSUPP; } -int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) { +int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } @@ -318,21 +318,7 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len); } -int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - return MP_EOPNOTSUPP; -} - -int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) { - if (!mp_bluetooth_is_active()) { - return ERRNO_BLUETOOTH_NOT_ACTIVE; - } - return MP_EOPNOTSUPP; -} - -int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { +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; } From 256f47e2f8348d08b53e3c69461cf07903b00367 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Thu, 2 Mar 2023 17:32:13 +1100 Subject: [PATCH 068/371] extmod/btstack: Fix indicate/notify queuing. This adds a mechanism to track a pending notify/indicate operation that is deferred due to the send buffer being full. This uses a tracked alloc that is passed as the content arg to the callback. This replaces the previous mechanism that did this via the global pending op queue, shared with client read/write ops. Signed-off-by: Jim Mussared --- extmod/btstack/modbluetooth_btstack.c | 69 +++++++++++++++++-- ports/rp2/mpconfigport.h | 2 +- ports/stm32/mpconfigport.h | 2 +- ports/unix/mpconfigport.h | 3 + .../unix/variants/coverage/mpconfigvariant.h | 2 +- py/gc.c | 4 +- 6 files changed, 73 insertions(+), 9 deletions(-) diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 243b6e38caa1..8ce2db74ecf6 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -166,7 +166,7 @@ STATIC void btstack_remove_pending_operation(mp_btstack_pending_op_t *pending_op // Register a pending background operation -- copies the buffer, and makes it known to the GC. STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_type, uint16_t conn_handle, uint16_t value_handle, const uint8_t *buf, size_t len) { - DEBUG_printf("btstack_enqueue_pending_operation op_type=%d conn_handle=%d value_handle=%d len=%zu\n", op_type, conn_handle, value_handle, len); + DEBUG_printf("btstack_enqueue_pending_operation op_type=%d conn_handle=%d value_handle=%d len=%lu\n", op_type, conn_handle, value_handle, len); mp_btstack_pending_op_t *pending_op = m_new_obj_var(mp_btstack_pending_op_t, uint8_t, len); pending_op->op_type = op_type; pending_op->conn_handle = conn_handle; @@ -1079,8 +1079,44 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); } +#if !MICROPY_TRACKED_ALLOC +#error "btstack requires MICROPY_TRACKED_ALLOC" +#endif + +typedef struct { + btstack_context_callback_registration_t btstack_registration; + int gatts_op; + uint16_t conn_handle; + uint16_t value_handle; + size_t value_len; + uint8_t value[]; +} notify_indicate_pending_op_t; + +// Called in response to a gatts_notify/indicate being unable to complete, which then calls +// att_server_request_to_send_notification. +STATIC void btstack_notify_indicate_ready_handler(void *context) { + MICROPY_PY_BLUETOOTH_ENTER + notify_indicate_pending_op_t *pending_op = (notify_indicate_pending_op_t *)context; + DEBUG_printf("btstack_notify_indicate_ready_handler gatts_op=%d conn_handle=%d value_handle=%d len=%lu\n", pending_op->gatts_op, pending_op->conn_handle, pending_op->value_handle, pending_op->value_len); + int err = ERROR_CODE_SUCCESS; + switch (pending_op->gatts_op) { + case MP_BLUETOOTH_GATTS_OP_NOTIFY: + err = att_server_notify(pending_op->conn_handle, pending_op->value_handle, pending_op->value, pending_op->value_len); + DEBUG_printf("btstack_notify_indicate_ready_handler: sending notification err=%d\n", err); + break; + case MP_BLUETOOTH_GATTS_OP_INDICATE: + err = att_server_indicate(pending_op->conn_handle, pending_op->value_handle, pending_op->value, pending_op->value_len); + DEBUG_printf("btstack_notify_indicate_ready_handler: sending indication err=%d\n", err); + break; + } + assert(err == ERROR_CODE_SUCCESS); + (void)err; + MICROPY_PY_BLUETOOTH_EXIT + m_tracked_free(pending_op); +} + 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) { - DEBUG_printf("mp_bluetooth_gatts_notify_indicate\n"); + DEBUG_printf("mp_bluetooth_gatts_notify_indicate: gatts_op=%d\n", gatts_op); if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; @@ -1107,10 +1143,33 @@ int mp_bluetooth_gatts_notify_indicate(uint16_t conn_handle, uint16_t value_hand } MICROPY_PY_BLUETOOTH_EXIT - if (err == BTSTACK_ACL_BUFFERS_FULL) { - DEBUG_printf("mp_bluetooth_gatts_notify_indicate: ACL buffer full, scheduling callback\n"); + if (err == BTSTACK_ACL_BUFFERS_FULL || err == ATT_HANDLE_VALUE_INDICATION_IN_PROGRESS) { + DEBUG_printf("mp_bluetooth_gatts_notify_indicate: ACL buffer full / indication in progress, scheduling callback\n"); + + // Copy the value and ask btstack to let us know when it can be sent. + notify_indicate_pending_op_t *pending_op = m_tracked_calloc(1, sizeof(notify_indicate_pending_op_t) + value_len); + pending_op->btstack_registration.context = pending_op; + pending_op->btstack_registration.callback = &btstack_notify_indicate_ready_handler; + pending_op->gatts_op = gatts_op; + pending_op->conn_handle = conn_handle; + pending_op->value_handle = value_handle; + pending_op->value_len = value_len; + memcpy(pending_op->value, value, value_len); + + MICROPY_PY_BLUETOOTH_ENTER + switch (gatts_op) { + case MP_BLUETOOTH_GATTS_OP_NOTIFY: + err = att_server_request_to_send_notification(&pending_op->btstack_registration, conn_handle); + break; + case MP_BLUETOOTH_GATTS_OP_INDICATE: + err = att_server_request_to_send_indication(&pending_op->btstack_registration, conn_handle); + break; + } + MICROPY_PY_BLUETOOTH_EXIT - // TODO: re-implement the handling for this. + if (err != ERROR_CODE_SUCCESS) { + m_tracked_free(pending_op); + } } return btstack_error_to_errno(err); diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index d862812f9bb7..da4548a473b0 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -74,7 +74,7 @@ #define MICROPY_OPT_COMPUTED_GOTO (1) // Python internal features -#define MICROPY_TRACKED_ALLOC (MICROPY_SSL_MBEDTLS) +#define MICROPY_TRACKED_ALLOC (MICROPY_SSL_MBEDTLS || MICROPY_BLUETOOTH_BTSTACK) #define MICROPY_READER_VFS (1) #define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index ee12ab814058..e3283c8f579f 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -65,7 +65,7 @@ #endif // Python internal features -#define MICROPY_TRACKED_ALLOC (MICROPY_SSL_MBEDTLS) +#define MICROPY_TRACKED_ALLOC (MICROPY_SSL_MBEDTLS || MICROPY_BLUETOOTH_BTSTACK) #define MICROPY_READER_VFS (1) #define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index a0b9192bfcf2..8eafb6119beb 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -117,6 +117,9 @@ typedef long mp_off_t; #define MICROPY_HELPER_LEXER_UNIX (1) #define MICROPY_VFS_POSIX (1) #define MICROPY_READER_POSIX (1) +#ifndef MICROPY_TRACKED_ALLOC +#define MICROPY_TRACKED_ALLOC (MICROPY_BLUETOOTH_BTSTACK) +#endif // VFS stat functions should return time values relative to 1970/1/1 #define MICROPY_EPOCH_IS_1970 (1) diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index 6107a4a55625..2a48e57e7f01 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -39,6 +39,6 @@ // Enable additional features. #define MICROPY_DEBUG_PARSE_RULE_NAME (1) -#define MICROPY_TRACKED_ALLOC (1) +#define MICROPY_TRACKED_ALLOC (1) #define MICROPY_WARNINGS_CATEGORY (1) #define MICROPY_PY_UCRYPTOLIB_CTR (1) diff --git a/py/gc.c b/py/gc.c index ba5c569d50f8..ad3e1104073e 100644 --- a/py/gc.c +++ b/py/gc.c @@ -746,7 +746,9 @@ void *gc_alloc_with_finaliser(mp_uint_t n_bytes) { // TODO: freeing here does not call finaliser void gc_free(void *ptr) { if (MP_STATE_THREAD(gc_lock_depth) > 0) { - // TODO how to deal with this error? + // Cannot free while the GC is locked. However free is an optimisation + // to reclaim the memory immediately, this means it will now be left + // until the next collection. return; } From a6aa7397d8e5b309e5675612143d3c5a5e931333 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 3 Mar 2023 00:48:33 +1100 Subject: [PATCH 069/371] extmod/btstack: Include value handle in client read/write events. This replaces the previous pending operation queue (that used to also be shared with pending server notify/indicate ops) with a single pending operation per connection. This allows the value handle to be correctly passed to the Python-level events. Also re-structure GATT client event handling to simplify the packet handler functions. Signed-off-by: Jim Mussared --- docs/library/bluetooth.rst | 2 - extmod/btstack/modbluetooth_btstack.c | 408 ++++++++++++++------------ extmod/btstack/modbluetooth_btstack.h | 9 +- extmod/modbluetooth.c | 3 +- extmod/modbluetooth.h | 2 +- extmod/nimble/modbluetooth_nimble.c | 6 +- 6 files changed, 224 insertions(+), 206 deletions(-) diff --git a/docs/library/bluetooth.rst b/docs/library/bluetooth.rst index 63578af16e4c..dd0f5ffd622a 100644 --- a/docs/library/bluetooth.rst +++ b/docs/library/bluetooth.rst @@ -183,12 +183,10 @@ Event Handling conn_handle, value_handle, char_data = data elif event == _IRQ_GATTC_READ_DONE: # A gattc_read() has completed. - # Note: The value_handle will be zero on btstack (but present on NimBLE). # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, value_handle, status = data elif event == _IRQ_GATTC_WRITE_DONE: # A gattc_write() has completed. - # Note: The value_handle will be zero on btstack (but present on NimBLE). # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, value_handle, status = data elif event == _IRQ_GATTC_NOTIFY: diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 8ce2db74ecf6..1c4358925202 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -93,121 +93,57 @@ STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(uint16_t uuid16, const uint8_t *uu } #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE -// Notes on supporting background ops (e.g. an attempt to gatts_notify while -// an existing notification is in progress): - -// GATTS Notify/Indicate (att_server_notify/indicate) -// * When available, copies buffer immediately. -// * Otherwise fails with BTSTACK_ACL_BUFFERS_FULL -// * Use att_server_request_to_send_notification/indication to get callback -// * Takes btstack_context_callback_registration_t (and takes ownership) and conn_handle. -// * Callback is invoked with just the context member of the btstack_context_callback_registration_t - -// GATTC Write without response (gatt_client_write_value_of_characteristic_without_response) -// * When available, copies buffer immediately. -// * Otherwise, fails with GATT_CLIENT_BUSY. -// * Use gatt_client_request_can_write_without_response_event to get callback -// * Takes btstack_packet_handler_t (function pointer) and conn_handle -// * Callback is invoked, use gatt_event_can_write_without_response_get_handle to get the conn_handle (no other context) -// * There can only be one pending gatt_client_request_can_write_without_response_event (otherwise we fail with EALREADY). - -// GATTC Write with response (gatt_client_write_value_of_characteristic) -// * When peripheral is available, takes ownership of buffer. -// * Otherwise, fails with GATT_CLIENT_IN_WRONG_STATE (we fail the operation). -// * Raises GATT_EVENT_QUERY_COMPLETE to the supplied packet handler. - -// For notify/indicate/write-without-response that proceed immediately, nothing extra required. -// For all other cases, buffer needs to be copied and protected from GC. -// For notify/indicate: -// * btstack_context_callback_registration_t: -// * needs to be malloc'ed -// * needs to be protected from GC -// * context arg needs to point back to the callback registration so it can be freed and un-protected -// For write-without-response -// * only the conn_handle is available in the callback -// * so we need a queue of conn_handle->(value_handle, copied buffer) - -// Pending operation types. -enum { - // Queued for sending when possible. - MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, // Waiting for conn handle - // Hold buffer pointer until complete. - MP_BLUETOOTH_BTSTACK_PENDING_WRITE, // Waiting for write done event -}; - -// Pending operation: -// - Holds a GC reference to the copied outgoing buffer. -// - Provides enough information for the callback handler to execute the desired operation. -struct _mp_btstack_pending_op_t { +#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT +typedef struct _mp_btstack_active_connection_t { btstack_linked_item_t *next; // Must be first field to match btstack_linked_item. - // See enum above. - uint16_t op_type; - - // For all op types. uint16_t conn_handle; - uint16_t value_handle; - - // For write-without-response, this is the actual buffer to send. - // For write-with-response, just holding onto the buffer for GC ref. - size_t len; - uint8_t buf[]; -}; - -// Must hold MICROPY_PY_BLUETOOTH_ENTER. -STATIC void btstack_remove_pending_operation(mp_btstack_pending_op_t *pending_op, bool del) { - bool removed = btstack_linked_list_remove(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops, (btstack_linked_item_t *)pending_op); - assert(removed); - (void)removed; - if (del) { - m_del_var(mp_btstack_pending_op_t, uint8_t, pending_op->len, pending_op); - } -} -// Register a pending background operation -- copies the buffer, and makes it known to the GC. -STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_type, uint16_t conn_handle, uint16_t value_handle, const uint8_t *buf, size_t len) { - DEBUG_printf("btstack_enqueue_pending_operation op_type=%d conn_handle=%d value_handle=%d len=%lu\n", op_type, conn_handle, value_handle, len); - mp_btstack_pending_op_t *pending_op = m_new_obj_var(mp_btstack_pending_op_t, uint8_t, len); - pending_op->op_type = op_type; - pending_op->conn_handle = conn_handle; - pending_op->value_handle = value_handle; - pending_op->len = len; - memcpy(pending_op->buf, buf, len); - - MICROPY_PY_BLUETOOTH_ENTER - bool added = btstack_linked_list_add(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops, (btstack_linked_item_t *)pending_op); - assert(added); + // Read/write. + uint16_t pending_value_handle; + + // Write only. Buffer must be retained until the operation completes. + uint8_t *pending_write_value; + size_t pending_write_value_len; +} mp_btstack_active_connection_t; + +STATIC mp_btstack_active_connection_t *create_active_connection(uint16_t conn_handle) { + DEBUG_printf("create_active_connection: conn_handle=%d\n", conn_handle); + mp_btstack_active_connection_t *conn = m_new(mp_btstack_active_connection_t, 1); + conn->conn_handle = conn_handle; + conn->pending_value_handle = 0xffff; + conn->pending_write_value = NULL; + conn->pending_write_value_len = 0; + bool added = btstack_linked_list_add(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->active_connections, (btstack_linked_item_t *)conn); (void)added; - MICROPY_PY_BLUETOOTH_EXIT - - return pending_op; + assert(added); + return conn; } -#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT - -// Cleans up a pending op of the specified type for this conn_handle (and if specified, value_handle). -// Used by MP_BLUETOOTH_BTSTACK_PENDING_WRITE and MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE. -// At the moment, both will set value_handle=0xffff as the events do not know their value_handle. -// TODO: Can we make btstack give us the value_handle for regular write (with response) so that we -// know for sure that we're using the correct entry. -STATIC mp_btstack_pending_op_t *btstack_finish_pending_operation(uint16_t op_type, uint16_t conn_handle, uint16_t value_handle, bool del) { - MICROPY_PY_BLUETOOTH_ENTER - DEBUG_printf("btstack_finish_pending_operation op_type=%d conn_handle=%d value_handle=%d\n", op_type, conn_handle, value_handle); +STATIC mp_btstack_active_connection_t *find_active_connection(uint16_t conn_handle) { + DEBUG_printf("find_active_connection: conn_handle=%d\n", conn_handle); btstack_linked_list_iterator_t it; - btstack_linked_list_iterator_init(&it, &MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops); + btstack_linked_list_iterator_init(&it, &MP_STATE_PORT(bluetooth_btstack_root_pointers)->active_connections); + mp_btstack_active_connection_t *conn = NULL; while (btstack_linked_list_iterator_has_next(&it)) { - mp_btstack_pending_op_t *pending_op = (mp_btstack_pending_op_t *)btstack_linked_list_iterator_next(&it); - - if (pending_op->op_type == op_type && pending_op->conn_handle == conn_handle && (value_handle == 0xffff || pending_op->value_handle == value_handle)) { - DEBUG_printf("btstack_finish_pending_operation: found value_handle=%d len=%zu\n", pending_op->value_handle, pending_op->len); - btstack_remove_pending_operation(pending_op, del); - MICROPY_PY_BLUETOOTH_EXIT - return del ? NULL : pending_op; + conn = (mp_btstack_active_connection_t *)btstack_linked_list_iterator_next(&it); + DEBUG_printf(" --> iter conn %d\n", conn->conn_handle); + if (conn->conn_handle == conn_handle) { + break; } } - DEBUG_printf("btstack_finish_pending_operation: not found\n"); - MICROPY_PY_BLUETOOTH_EXIT - return NULL; + return conn; +} + +STATIC void remove_active_connection(uint16_t conn_handle) { + DEBUG_printf("remove_active_connection: conn_handle=%d\n", conn_handle); + mp_btstack_active_connection_t *conn = find_active_connection(conn_handle); + if (conn) { + bool removed = btstack_linked_list_remove(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->active_connections, (btstack_linked_item_t *)conn); + (void)removed; + assert(removed); + m_del(mp_btstack_active_connection_t, conn, 1); + } } #endif @@ -255,8 +191,10 @@ STATIC bool controller_static_addr_available = false; STATIC const uint8_t read_static_address_command_complete_prefix[] = { 0x0e, 0x1b, 0x01, 0x09, 0xfc }; #endif -STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t irq) { - DEBUG_printf("btstack_packet_handler(packet_type=%u, packet=%p)\n", packet_type, packet); +STATIC void btstack_packet_handler_generic(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + (void)channel; + (void)size; + DEBUG_printf("btstack_packet_handler_generic(packet_type=%u, packet=%p)\n", packet_type, packet); if (packet_type != HCI_EVENT_PACKET) { return; } @@ -279,6 +217,9 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t // Slave role. irq_event = MP_BLUETOOTH_IRQ_CENTRAL_CONNECT; } + #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT + create_active_connection(conn_handle); + #endif mp_bluetooth_gap_on_connected_disconnected(irq_event, conn_handle, addr_type, addr); break; } @@ -372,6 +313,9 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t } uint8_t addr[6] = {0}; mp_bluetooth_gap_on_connected_disconnected(irq_event, conn_handle, 0xff, addr); + #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT + remove_active_connection(conn_handle); + #endif #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE } else if (event_type == GAP_EVENT_ADVERTISING_REPORT) { DEBUG_printf(" --> gap advertising report\n"); @@ -390,51 +334,6 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t uint16_t conn_handle = gatt_event_mtu_get_handle(packet); uint16_t mtu = gatt_event_mtu_get_MTU(packet); mp_bluetooth_gatts_on_mtu_exchanged(conn_handle, mtu); - } else if (event_type == GATT_EVENT_QUERY_COMPLETE) { - uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); - uint16_t status = gatt_event_query_complete_get_att_status(packet); - DEBUG_printf(" --> gatt query complete irq=%d conn_handle=%d status=%d\n", irq, conn_handle, status); - if (irq == MP_BLUETOOTH_IRQ_GATTC_READ_DONE || irq == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { - // TODO there is no value_handle available to pass here. - // TODO try and get this implemented in btstack. - mp_bluetooth_gattc_on_read_write_status(irq, conn_handle, 0xffff, status); - // Unref the saved buffer for the write operation on this conn_handle. - if (irq == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { - btstack_finish_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE, conn_handle, 0xffff, false /* del */); - } - } else if (irq == MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE || - irq == MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE || - irq == MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE) { - mp_bluetooth_gattc_on_discover_complete(irq, conn_handle, status); - } - } else if (event_type == GATT_EVENT_SERVICE_QUERY_RESULT) { - DEBUG_printf(" --> gatt service query result\n"); - uint16_t conn_handle = gatt_event_service_query_result_get_handle(packet); - gatt_client_service_t service; - gatt_event_service_query_result_get_service(packet, &service); - mp_obj_bluetooth_uuid_t service_uuid = create_mp_uuid(service.uuid16, service.uuid128); - mp_bluetooth_gattc_on_primary_service_result(conn_handle, service.start_group_handle, service.end_group_handle, &service_uuid); - } else if (event_type == GATT_EVENT_CHARACTERISTIC_QUERY_RESULT) { - DEBUG_printf(" --> gatt characteristic query result\n"); - uint16_t conn_handle = gatt_event_characteristic_query_result_get_handle(packet); - gatt_client_characteristic_t characteristic; - gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); - mp_obj_bluetooth_uuid_t characteristic_uuid = create_mp_uuid(characteristic.uuid16, characteristic.uuid128); - mp_bluetooth_gattc_on_characteristic_result(conn_handle, characteristic.value_handle, characteristic.end_handle, characteristic.properties, &characteristic_uuid); - } else if (event_type == GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT) { - DEBUG_printf(" --> gatt descriptor query result\n"); - uint16_t conn_handle = gatt_event_all_characteristic_descriptors_query_result_get_handle(packet); - gatt_client_characteristic_descriptor_t descriptor; - gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &descriptor); - mp_obj_bluetooth_uuid_t descriptor_uuid = create_mp_uuid(descriptor.uuid16, descriptor.uuid128); - mp_bluetooth_gattc_on_descriptor_result(conn_handle, descriptor.handle, &descriptor_uuid); - } else if (event_type == GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) { - DEBUG_printf(" --> gatt characteristic value query result\n"); - uint16_t conn_handle = gatt_event_characteristic_value_query_result_get_handle(packet); - uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet); - uint16_t len = gatt_event_characteristic_value_query_result_get_value_length(packet); - const uint8_t *data = gatt_event_characteristic_value_query_result_get_value(packet); - mp_bluetooth_gattc_on_data_available(MP_BLUETOOTH_IRQ_GATTC_READ_RESULT, conn_handle, value_handle, &data, &len, 1); } else if (event_type == GATT_EVENT_NOTIFICATION) { DEBUG_printf(" --> gatt notification\n"); uint16_t conn_handle = gatt_event_notification_get_handle(packet); @@ -452,28 +351,24 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t } else if (event_type == GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE) { uint16_t conn_handle = gatt_event_can_write_without_response_get_handle(packet); DEBUG_printf(" --> gatt can write without response %d\n", conn_handle); - mp_btstack_pending_op_t *pending_op = btstack_finish_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, conn_handle, 0xffff, false /* !del */); - if (pending_op) { - DEBUG_printf(" --> ready for value_handle=%d len=%zu\n", pending_op->value_handle, pending_op->len); - gatt_client_write_value_of_characteristic_without_response(pending_op->conn_handle, pending_op->value_handle, pending_op->len, (uint8_t *)pending_op->buf); - // Note: Can't "del" the pending_op from IRQ context. Leave it for the GC. + mp_btstack_active_connection_t *conn = find_active_connection(conn_handle); + if (!conn || conn->pending_value_handle == 0xffff || !conn->pending_write_value) { + return; } - + DEBUG_printf(" --> ready for value_handle=%d len=%lu\n", conn->pending_value_handle, conn->pending_write_value_len); + int err = gatt_client_write_value_of_characteristic_without_response(conn_handle, conn->pending_value_handle, conn->pending_write_value_len, conn->pending_write_value); + (void)err; + assert(err == ERROR_CODE_SUCCESS); + conn->pending_value_handle = 0xffff; + m_del(uint8_t, conn->pending_write_value, conn->pending_write_value_len); + conn->pending_write_value = NULL; + conn->pending_write_value_len = 0; #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT } else { DEBUG_printf(" --> hci event type: unknown (0x%02x)\n", event_type); } } -// Because the packet handler callbacks don't support an argument, we use a specific -// handler when we need to provide additional state to the handler (in the "irq" parameter). -// This is the generic handler for when you don't need extra state. -STATIC void btstack_packet_handler_generic(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - (void)channel; - (void)size; - btstack_packet_handler(packet_type, packet, 0); -} - STATIC btstack_packet_callback_registration_t hci_event_callback_registration = { .callback = &btstack_packet_handler_generic }; @@ -483,35 +378,121 @@ STATIC btstack_packet_callback_registration_t hci_event_callback_registration = STATIC void btstack_packet_handler_discover_services(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { (void)channel; (void)size; - btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE); + if (packet_type != HCI_EVENT_PACKET) { + return; + } + uint8_t event_type = hci_event_packet_get_type(packet); + if (event_type == GATT_EVENT_SERVICE_QUERY_RESULT) { + DEBUG_printf(" --> gatt service query result\n"); + uint16_t conn_handle = gatt_event_service_query_result_get_handle(packet); + gatt_client_service_t service; + gatt_event_service_query_result_get_service(packet, &service); + mp_obj_bluetooth_uuid_t service_uuid = create_mp_uuid(service.uuid16, service.uuid128); + mp_bluetooth_gattc_on_primary_service_result(conn_handle, service.start_group_handle, service.end_group_handle, &service_uuid); + } else if (event_type == GATT_EVENT_QUERY_COMPLETE) { + uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); + uint16_t status = gatt_event_query_complete_get_att_status(packet); + DEBUG_printf(" --> gatt query services complete conn_handle=%d status=%d\n", conn_handle, status); + mp_bluetooth_gattc_on_discover_complete(MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE, conn_handle, status); + } } // For when the handler is being used for characteristic discovery. STATIC void btstack_packet_handler_discover_characteristics(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { (void)channel; (void)size; - btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE); + if (packet_type != HCI_EVENT_PACKET) { + return; + } + uint8_t event_type = hci_event_packet_get_type(packet); + if (event_type == GATT_EVENT_CHARACTERISTIC_QUERY_RESULT) { + DEBUG_printf(" --> gatt characteristic query result\n"); + uint16_t conn_handle = gatt_event_characteristic_query_result_get_handle(packet); + gatt_client_characteristic_t characteristic; + gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); + mp_obj_bluetooth_uuid_t characteristic_uuid = create_mp_uuid(characteristic.uuid16, characteristic.uuid128); + mp_bluetooth_gattc_on_characteristic_result(conn_handle, characteristic.value_handle, characteristic.end_handle, characteristic.properties, &characteristic_uuid); + } else if (event_type == GATT_EVENT_QUERY_COMPLETE) { + uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); + uint16_t status = gatt_event_query_complete_get_att_status(packet); + DEBUG_printf(" --> gatt query characteristics complete conn_handle=%d status=%d\n", conn_handle, status); + mp_bluetooth_gattc_on_discover_complete(MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE, conn_handle, status); + } } // For when the handler is being used for descriptor discovery. STATIC void btstack_packet_handler_discover_descriptors(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { (void)channel; (void)size; - btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE); + if (packet_type != HCI_EVENT_PACKET) { + return; + } + uint8_t event_type = hci_event_packet_get_type(packet); + if (event_type == GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT) { + DEBUG_printf(" --> gatt descriptor query result\n"); + uint16_t conn_handle = gatt_event_all_characteristic_descriptors_query_result_get_handle(packet); + gatt_client_characteristic_descriptor_t descriptor; + gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &descriptor); + mp_obj_bluetooth_uuid_t descriptor_uuid = create_mp_uuid(descriptor.uuid16, descriptor.uuid128); + mp_bluetooth_gattc_on_descriptor_result(conn_handle, descriptor.handle, &descriptor_uuid); + } else if (event_type == GATT_EVENT_QUERY_COMPLETE) { + uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); + uint16_t status = gatt_event_query_complete_get_att_status(packet); + DEBUG_printf(" --> gatt query descriptors complete conn_handle=%d status=%d\n", conn_handle, status); + mp_bluetooth_gattc_on_discover_complete(MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE, conn_handle, status); + } } // For when the handler is being used for a read query. STATIC void btstack_packet_handler_read(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { (void)channel; (void)size; - btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_READ_DONE); + if (packet_type != HCI_EVENT_PACKET) { + return; + } + uint8_t event_type = hci_event_packet_get_type(packet); + if (event_type == GATT_EVENT_QUERY_COMPLETE) { + uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); + uint16_t status = gatt_event_query_complete_get_att_status(packet); + DEBUG_printf(" --> gatt query read complete conn_handle=%d status=%d\n", conn_handle, status); + mp_btstack_active_connection_t *conn = find_active_connection(conn_handle); + if (!conn) { + return; + } + mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_READ_DONE, conn_handle, conn->pending_value_handle, status); + conn->pending_value_handle = 0xffff; + } else if (event_type == GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) { + DEBUG_printf(" --> gatt characteristic value query result\n"); + uint16_t conn_handle = gatt_event_characteristic_value_query_result_get_handle(packet); + uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet); + uint16_t len = gatt_event_characteristic_value_query_result_get_value_length(packet); + const uint8_t *data = gatt_event_characteristic_value_query_result_get_value(packet); + mp_bluetooth_gattc_on_data_available(MP_BLUETOOTH_IRQ_GATTC_READ_RESULT, conn_handle, value_handle, &data, &len, 1); + } } // For when the handler is being used for write-with-response. STATIC void btstack_packet_handler_write_with_response(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { (void)channel; (void)size; - btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE); + if (packet_type != HCI_EVENT_PACKET) { + return; + } + uint8_t event_type = hci_event_packet_get_type(packet); + if (event_type == GATT_EVENT_QUERY_COMPLETE) { + uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); + uint16_t status = gatt_event_query_complete_get_att_status(packet); + DEBUG_printf(" --> gatt query write complete conn_handle=%d status=%d\n", conn_handle, status); + mp_btstack_active_connection_t *conn = find_active_connection(conn_handle); + if (!conn) { + return; + } + mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE, conn_handle, conn->pending_value_handle, status); + conn->pending_value_handle = 0xffff; + m_del(uint8_t, conn->pending_write_value, conn->pending_write_value_len); + conn->pending_write_value = NULL; + conn->pending_write_value_len = 0; + } } #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT @@ -898,8 +879,8 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { att_db_util_add_service_uuid16(GAP_SERVICE_UUID); uint16_t handle = att_db_util_add_characteristic_uuid16(GAP_DEVICE_NAME_UUID, ATT_PROPERTY_READ | ATT_PROPERTY_DYNAMIC, ATT_SECURITY_NONE, ATT_SECURITY_NONE, NULL, 0); - assert(handle == BTSTACK_GAP_DEVICE_NAME_HANDLE); (void)handle; + assert(handle == BTSTACK_GAP_DEVICE_NAME_HANDLE); att_db_util_add_service_uuid16(0x1801); att_db_util_add_characteristic_uuid16(0x2a05, ATT_PROPERTY_READ, ATT_SECURITY_NONE, ATT_SECURITY_NONE, NULL, 0); @@ -1372,49 +1353,90 @@ int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - return btstack_error_to_errno(gatt_client_read_value_of_characteristic_using_value_handle(&btstack_packet_handler_read, conn_handle, value_handle)); + + // There can only be a single pending GATT client operation per connection. + mp_btstack_active_connection_t *conn = find_active_connection(conn_handle); + if (!conn) { + DEBUG_printf(" --> no active connection %d\n", conn_handle); + return MP_ENOTCONN; + } + if (conn->pending_value_handle != 0xffff) { + // There's either a read in progress, a write-with-response in progress, or a pending can-write-without-response request outstanding. + DEBUG_printf("--> busy\n"); + return MP_EALREADY; + } + conn->pending_value_handle = value_handle; + int err = gatt_client_read_value_of_characteristic_using_value_handle(&btstack_packet_handler_read, conn_handle, value_handle); + if (err != ERROR_CODE_SUCCESS) { + DEBUG_printf("--> can't send read %d\n", err); + conn->pending_value_handle = 0xffff; + } + return btstack_error_to_errno(err); } -int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) { +int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len, unsigned int mode) { DEBUG_printf("mp_bluetooth_gattc_write\n"); if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - // We should be distinguishing between gatt_client_write_value_of_characteristic vs + // Note: We should be distinguishing between gatt_client_write_value_of_characteristic vs // gatt_client_write_characteristic_descriptor_using_descriptor_handle. // However both are implemented using send_gatt_write_attribute_value_request under the hood, // and we get the exact same event to the packet handler. // Same story for the "without response" version. int err; - mp_btstack_pending_op_t *pending_op = NULL; if (mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) { - // If possible, this will send immediately, copying the buffer directly to the ACL buffer. - err = gatt_client_write_value_of_characteristic_without_response(conn_handle, value_handle, *value_len, (uint8_t *)value); - if (err == GATT_CLIENT_BUSY) { - DEBUG_printf("mp_bluetooth_gattc_write: client busy\n"); - // Can't send right now, need to take a copy of the buffer and add it to the queue. - pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, conn_handle, value_handle, value, *value_len); - // Notify when this conn_handle can write. - err = gatt_client_request_can_write_without_response_event(&btstack_packet_handler_generic, conn_handle); - } else { - DEBUG_printf("mp_bluetooth_gattc_write: other failure: %d\n", err); + // Simplest case -- if the write can be dispatched directly, then the buffer is copied directly to the ACL buffer. + err = gatt_client_write_value_of_characteristic_without_response(conn_handle, value_handle, value_len, (uint8_t *)value); + if (err != GATT_CLIENT_BUSY) { + DEBUG_printf("--> can't send write-without-response %d\n", err); + return btstack_error_to_errno(err); } + } + + // There can only be a single pending read/write request per connection. + mp_btstack_active_connection_t *conn = find_active_connection(conn_handle); + if (!conn) { + DEBUG_printf(" --> no active connection %d\n", conn_handle); + return MP_ENOTCONN; + } + if (conn->pending_value_handle != 0xffff) { + // There's either a read in progress, a write-with-response in progress, or a pending can-write-without-response request outstanding. + DEBUG_printf(" --> busy\n"); + return MP_EALREADY; + } + conn->pending_value_handle = value_handle; + conn->pending_write_value_len = value_len; + conn->pending_write_value = m_new(uint8_t, value_len); + memcpy(conn->pending_write_value, value, value_len); + + if (mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) { + DEBUG_printf(" --> client busy\n"); + // Raise the GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE event when + // write-without-response will succeed. The only way this fails is if + // there's an outstanding request (unlike for the server-equivalent, + // att_server_request_to_send_notification, which has a queue) but + // we've already checked that there isn't one. + err = gatt_client_request_can_write_without_response_event(&btstack_packet_handler_generic, conn_handle); } else if (mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) { - // Pending operation copies the value buffer and keeps a GC reference - // until the response comes back (there is always a response). - pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE, conn_handle, value_handle, value, *value_len); - err = gatt_client_write_value_of_characteristic(&btstack_packet_handler_write_with_response, conn_handle, value_handle, pending_op->len, pending_op->buf); + // Attempt to write immediately. This can fail if there's another + // client operation in progress (e.g. discover). + err = gatt_client_write_value_of_characteristic(&btstack_packet_handler_write_with_response, conn_handle, value_handle, value_len, conn->pending_write_value); } else { return MP_EINVAL; } - if (pending_op && err != ERROR_CODE_SUCCESS) { - // Failure. Unref and free the pending operation. - btstack_remove_pending_operation(pending_op, true /* del */); + if (err != ERROR_CODE_SUCCESS) { + DEBUG_printf("--> write failed %d\n", err); + // We knew that there was no read/write in progress, but some other + // client operation is in progress, so release the pending state. + m_del(uint8_t, conn->pending_write_value, value_len); + conn->pending_write_value_len = 0; + conn->pending_value_handle = 0xffff; } return btstack_error_to_errno(err); diff --git a/extmod/btstack/modbluetooth_btstack.h b/extmod/btstack/modbluetooth_btstack.h index 7890bbfae2c4..7f4a1820737a 100644 --- a/extmod/btstack/modbluetooth_btstack.h +++ b/extmod/btstack/modbluetooth_btstack.h @@ -33,8 +33,6 @@ #include "lib/btstack/src/btstack.h" -typedef struct _mp_btstack_pending_op_t mp_btstack_pending_op_t; - typedef struct _mp_bluetooth_btstack_root_pointers_t { // This stores both the advertising data and the scan response data, concatenated together. uint8_t *adv_data; @@ -44,11 +42,12 @@ typedef struct _mp_bluetooth_btstack_root_pointers_t { // Characteristic (and descriptor) value storage. mp_gatts_db_t gatts_db; - btstack_linked_list_t pending_ops; - - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT // Registration for notify/indicate events. gatt_client_notification_t notification; + + // Active connections (only used for GATT clients). + btstack_linked_list_t active_connections; #endif } mp_bluetooth_btstack_root_pointers_t; diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index e3ef40d78913..e65851d6b4bb 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -841,12 +841,11 @@ STATIC mp_obj_t bluetooth_ble_gattc_write(size_t n_args, const mp_obj_t *args) { mp_obj_t data = args[3]; mp_buffer_info_t bufinfo = {0}; mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); - size_t len = bufinfo.len; unsigned int mode = MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE; if (n_args == 5) { mode = mp_obj_get_int(args[4]); } - return bluetooth_handle_errno(mp_bluetooth_gattc_write(conn_handle, value_handle, bufinfo.buf, &len, mode)); + return bluetooth_handle_errno(mp_bluetooth_gattc_write(conn_handle, value_handle, bufinfo.buf, bufinfo.len, mode)); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gattc_write_obj, 4, 5, bluetooth_ble_gattc_write); diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index fc7012ecfba0..6fe8d66ed382 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -391,7 +391,7 @@ int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle); // Write the value to the remote peripheral. -int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode); +int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len, unsigned int mode); // Initiate MTU exchange for a specific connection using the preferred MTU. int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index b2667300ca76..f806f331760f 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -1451,15 +1451,15 @@ STATIC int ble_gattc_attr_write_cb(uint16_t conn_handle, const struct ble_gatt_e } // Write the value to the remote peripheral. -int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) { +int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len, unsigned int mode) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } int err; if (mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) { - err = ble_gattc_write_no_rsp_flat(conn_handle, value_handle, value, *value_len); + err = ble_gattc_write_no_rsp_flat(conn_handle, value_handle, value, value_len); } else if (mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) { - err = ble_gattc_write_flat(conn_handle, value_handle, value, *value_len, &ble_gattc_attr_write_cb, NULL); + err = ble_gattc_write_flat(conn_handle, value_handle, value, value_len, &ble_gattc_attr_write_cb, NULL); } else { err = BLE_HS_EINVAL; } From dcb863ebfbce3251188ce0cedf584a120d8aeb9e Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Wed, 8 Mar 2023 16:44:43 +1100 Subject: [PATCH 070/371] tests/multi_bluetooth: Use time.sleep_ms instead of time.sleep. On unix, time.sleep is implemented as select(timeout=