diff --git a/docs/library/esp.rst b/docs/library/esp.rst index 8920c8241ea0..9c20b5e8b2d8 100644 --- a/docs/library/esp.rst +++ b/docs/library/esp.rst @@ -62,12 +62,35 @@ Functions .. function:: flash_erase(sector_no) -.. function:: osdebug(level) +.. function:: osdebug(uart_no) - Turn esp os debugging messages on or off. + .. note:: This is the ESP8266 form of this function. - The *level* parameter sets the threshold for the log messages for all esp components. - The log levels are defined as constants: + Change the level of OS serial debug log messages. On boot, + OS serial debug log messages are disabled. + + ``uart_no`` is the number of the UART peripheral which should receive + OS-level output, or ``None`` to disable OS serial debug log messages. + +.. function:: osdebug(uart_no, [level]) + :no-index: + + .. note:: This is the ESP32 form of this function. + + Change the level of OS serial debug log messages. On boot, OS + serial debug log messages are limited to Error output only. + + The behaviour of this function depends on the arguments passed to it. The + following combinations are supported: + + ``osdebug(None)`` restores the default OS debug log message level + (``LOG_ERROR``). + + ``osdebug(0)`` enables all available OS debug log messages (in the + default build configuration this is ``LOG_INFO``). + + ``osdebug(0, level)`` sets the OS debug log message level to the + specified value. The log levels are defined as constants: * ``LOG_NONE`` -- No log output * ``LOG_ERROR`` -- Critical errors, software module can not recover on its own @@ -77,6 +100,15 @@ Functions * ``LOG_VERBOSE`` -- Bigger chunks of debugging information, or frequent messages which can potentially flood the output + .. note:: ``LOG_DEBUG`` and ``LOG_VERBOSE`` are not compiled into the + MicroPython binary by default, to save size. A custom build with a + modified "``sdkconfig``" source file is needed to see any output + at these log levels. + + .. note:: Log output on ESP32 is automatically suspended in "Raw REPL" mode, + to prevent communications issues. This means OS level logging is never + seen when using ``mpremote run`` and similar tools. + .. function:: set_native_code_location(start, length) **Note**: ESP8266 only diff --git a/examples/usercmodule/cppexample/example.cpp b/examples/usercmodule/cppexample/example.cpp index 06809732a4d7..2df832baa76f 100644 --- a/examples/usercmodule/cppexample/example.cpp +++ b/examples/usercmodule/cppexample/example.cpp @@ -1,9 +1,16 @@ extern "C" { #include +#include // Here we implement the function using C++ code, but since it's // declaration has to be compatible with C everything goes in extern "C" scope. mp_obj_t cppfunc(mp_obj_t a_obj, mp_obj_t b_obj) { + // The following no-ops are just here to verify the static assertions used in + // the public API all compile with C++. + MP_STATIC_ASSERT_STR_ARRAY_COMPATIBLE; + if (mp_obj_is_type(a_obj, &mp_type_BaseException)) { + } + // Prove we have (at least) C++11 features. const auto a = mp_obj_get_int(a_obj); const auto b = mp_obj_get_int(b_obj); diff --git a/extmod/extmod.mk b/extmod/extmod.mk index a04856bce205..84573ec86106 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -267,6 +267,9 @@ SRC_THIRDPARTY_C += $(addprefix $(LWIP_DIR)/,\ core/ipv6/nd6.c \ netif/ethernet.c \ ) +ifeq ($(MICROPY_PY_LWIP_LOOPBACK),1) +CFLAGS_EXTMOD += -DLWIP_NETIF_LOOPBACK=1 +endif ifeq ($(MICROPY_PY_LWIP_SLIP),1) CFLAGS_EXTMOD += -DMICROPY_PY_LWIP_SLIP=1 SRC_THIRDPARTY_C += $(LWIP_DIR)/netif/slipif.c diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h index 3e98bb99d702..ec85a7bb3692 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h @@ -1,6 +1,9 @@ #define MICROPY_HW_BOARD_NAME "Arduino Nano ESP32" #define MICROPY_HW_MCU_NAME "ESP32S3" +// Network config +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-nano-esp32" + #define MICROPY_PY_MACHINE_DAC (0) #define MICROPY_HW_I2C0_SCL (12) diff --git a/ports/esp32/boards/ESP32_GENERIC/sdkconfig.d2wd b/ports/esp32/boards/ESP32_GENERIC/sdkconfig.d2wd index f6e07893e937..2ac983693d3f 100644 --- a/ports/esp32/boards/ESP32_GENERIC/sdkconfig.d2wd +++ b/ports/esp32/boards/ESP32_GENERIC/sdkconfig.d2wd @@ -3,6 +3,10 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_PERF=n CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y +# Change maximum log level to error, to reduce firmware size. +CONFIG_LOG_MAXIMUM_LEVEL_ERROR=y +CONFIG_LOG_MAXIMUM_LEVEL_INFO=n + # Disable SPI Ethernet driver to reduce firmware size. CONFIG_ETH_USE_SPI_ETHERNET=n diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index 372633e711a6..2a7c9a1c5b8c 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -17,9 +17,18 @@ CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y # Change default log level to "ERROR" (instead of "INFO") -CONFIG_LOG_DEFAULT_LEVEL_INFO=n CONFIG_LOG_DEFAULT_LEVEL_ERROR=y -CONFIG_LOG_DEFAULT_LEVEL=1 + +# Set the maximum included log level higher than the default, +# so esp.osdebug() can enable more logging at runtime. +# +# To increase the max log verbosity to Debug or Verbose instead, comment +# CONFIG_LOG_MAXIMUM_LEVEL_INFO=y and uncomment one of the other settings. +# +# If not needed, the next line can be commented entirely to save binary size. +CONFIG_LOG_MAXIMUM_LEVEL_INFO=y +#CONFIG_LOG_MAXIMUM_LEVEL_DEBUG=y +#CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE=y # Main XTAL Config # Only on: ESP32 diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index 5d53018f4ba1..77a31ebc0f66 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -323,12 +323,18 @@ STATIC mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *args) { check_esp_err(rmt_wait_tx_done(self->channel_id, portMAX_DELAY)); } + #if !CONFIG_IDF_TARGET_ESP32S3 + check_esp_err(rmt_write_items(self->channel_id, self->items, num_items, false)); + #endif + if (self->loop_en) { check_esp_err(rmt_set_tx_intr_en(self->channel_id, false)); check_esp_err(rmt_set_tx_loop_mode(self->channel_id, true)); } + #if CONFIG_IDF_TARGET_ESP32S3 check_esp_err(rmt_write_items(self->channel_id, self->items, num_items, false)); + #endif return mp_const_none; } diff --git a/ports/esp32/modesp.c b/ports/esp32/modesp.c index 4726ce587420..f51ba6ab34d5 100644 --- a/ports/esp32/modesp.c +++ b/ports/esp32/modesp.c @@ -37,12 +37,12 @@ #include "py/mphal.h" STATIC mp_obj_t esp_osdebug(size_t n_args, const mp_obj_t *args) { - esp_log_level_t level = LOG_LOCAL_LEVEL; + esp_log_level_t level = LOG_LOCAL_LEVEL; // Maximum available level if (n_args == 2) { level = mp_obj_get_int(args[1]); } if (args[0] == mp_const_none) { - // Disable logging + // Set logging back to boot default of ESP_LOG_ERROR esp_log_level_set("*", ESP_LOG_ERROR); } else { // Enable logging at the given level diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index cacc52dec568..ca8c63030cd7 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -36,6 +36,7 @@ #include #include +#include "py/gc.h" #include "py/runtime0.h" #include "py/nlr.h" #include "py/objlist.h" @@ -274,6 +275,13 @@ STATIC mp_obj_t socket_make_new(const mp_obj_type_t *type_in, size_t n_args, siz sock->state = sock->type == SOCK_STREAM ? SOCKET_STATE_NEW : SOCKET_STATE_CONNECTED; sock->fd = lwip_socket(sock->domain, sock->type, sock->proto); + if (sock->fd < 0 && errno == ENFILE) { + // ESP32 LWIP has a hard socket limit, ENFILE is returned when this is + // reached. Similar to the logic elsewhere for MemoryError, try running + // GC before failing outright. + gc_collect(); + sock->fd = lwip_socket(sock->domain, sock->type, sock->proto); + } if (sock->fd < 0) { mp_raise_OSError(errno); } diff --git a/ports/esp32/network_wlan.c b/ports/esp32/network_wlan.c index 74c6e8f9303d..e8c75c619df6 100644 --- a/ports/esp32/network_wlan.c +++ b/ports/esp32/network_wlan.c @@ -41,6 +41,7 @@ #include "esp_wifi.h" #include "esp_log.h" +#include "esp_psram.h" #ifndef NO_QSTR #include "mdns.h" @@ -206,6 +207,23 @@ void esp_initialise_wifi(void) { wlan_ap_obj.active = false; wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + #if CONFIG_SPIRAM_IGNORE_NOTFOUND + if (!esp_psram_is_initialized()) { + // If PSRAM failed to initialize, disable "Wi-Fi Cache TX Buffers" + // (default SPIRAM config ESP32_WIFI_CACHE_TX_BUFFER_NUM==32, this is 54,400 bytes of heap) + cfg.cache_tx_buf_num = 0; + cfg.feature_caps &= ~CONFIG_FEATURE_CACHE_TX_BUF_BIT; + + // Set some other options back to the non-SPIRAM default values + // to save more RAM. + // + // These can be determined from ESP-IDF components/esp_wifi/Kconfig and the + // WIFI_INIT_CONFIG_DEFAULT macro + cfg.tx_buf_type = 1; // Dynamic, this "magic number" is defined in IDF KConfig + cfg.static_tx_buf_num = 0; // Probably don't need, due to tx_buf_type + cfg.dynamic_tx_buf_num = 32; // ESP-IDF default value (maximum) + } + #endif ESP_LOGD("modnetwork", "Initializing WiFi"); esp_exceptions(esp_wifi_init(&cfg)); esp_exceptions(esp_wifi_set_storage(WIFI_STORAGE_RAM)); diff --git a/ports/esp32/uart.c b/ports/esp32/uart.c index fc69e279e33d..b82a638a5b27 100644 --- a/ports/esp32/uart.c +++ b/ports/esp32/uart.c @@ -34,12 +34,9 @@ #if MICROPY_HW_ENABLE_UART_REPL #include +#include "driver/uart.h" // For uart_get_sclk_freq() #include "hal/uart_hal.h" -// Backwards compatibility for when MICROPY_HW_UART_REPL was a ESP-IDF UART -// driver enum. Only UART_NUM_0 was supported with that version of the driver. -#define UART_NUM_0 0 - STATIC void uart_irq_handler(void *arg); // Declaring the HAL structure on the stack saves a tiny amount of static RAM @@ -53,16 +50,18 @@ STATIC void uart_irq_handler(void *arg); void uart_stdout_init(void) { uart_hal_context_t repl_hal = REPL_HAL_DEFN(); - uint32_t sclk_freq; - - #if UART_SCLK_DEFAULT == SOC_MOD_CLK_APB - sclk_freq = APB_CLK_FREQ; // Assumes no frequency scaling + #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) + uart_sclk_t sclk; #else - // ESP32-H2 and ESP32-C2, I think - #error "This SoC uses a different default UART SCLK source, code needs updating." + soc_module_clk_t sclk; #endif + uint32_t sclk_freq; + + uart_hal_get_sclk(&repl_hal, &sclk); // To restore SCLK after uart_hal_init() resets it + ESP_ERROR_CHECK(uart_get_sclk_freq(sclk, &sclk_freq)); uart_hal_init(&repl_hal, MICROPY_HW_UART_REPL); // Sets defaults: 8n1, no flow control + uart_hal_set_sclk(&repl_hal, sclk); uart_hal_set_baudrate(&repl_hal, MICROPY_HW_UART_REPL_BAUD, sclk_freq); uart_hal_rxfifo_rst(&repl_hal); uart_hal_txfifo_rst(&repl_hal); diff --git a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.h b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.h index aa1e28ab9d09..52346dedc44e 100644 --- a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.h +++ b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/mpconfigboard.h @@ -5,10 +5,14 @@ */ // MCU config -#define MICROPY_HW_BOARD_NAME "PORTENTA C33" +#define MICROPY_HW_BOARD_NAME "Arduino Portenta C33" #define MICROPY_HW_MCU_NAME "RA6M5" #define MICROPY_HW_MCU_SYSCLK 200000000 #define MICROPY_HW_MCU_PCLK 100000000 +#define MICROPY_HW_FLASH_FS_LABEL "Portenta C33" + +// Network config +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-portenta-c33" // module config #define MICROPY_EMIT_THUMB (1) @@ -59,8 +63,8 @@ void PORTENTA_C33_board_enter_bootloader(void); #endif // I2C -#define MICROPY_HW_I2C2_SCL (pin_P407) -#define MICROPY_HW_I2C2_SDA (pin_P408) +#define MICROPY_HW_I2C0_SCL (pin_P408) +#define MICROPY_HW_I2C0_SDA (pin_P407) // SPI #define MICROPY_HW_SPI1_SSL (pin_P104) diff --git a/ports/renesas-ra/mphalport.h b/ports/renesas-ra/mphalport.h index 2b25a36fbf85..e29a7fec7b81 100644 --- a/ports/renesas-ra/mphalport.h +++ b/ports/renesas-ra/mphalport.h @@ -29,6 +29,9 @@ #include "pin.h" #include "py/ringbuf.h" +#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV) +#define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state) + #define MICROPY_HW_USB_CDC_TX_TIMEOUT (500) extern const unsigned char mp_hal_status_to_errno_table[4]; diff --git a/ports/renesas-ra/usbd.c b/ports/renesas-ra/usbd.c index 7047503864f6..e7252b5f7acf 100644 --- a/ports/renesas-ra/usbd.c +++ b/ports/renesas-ra/usbd.c @@ -34,15 +34,8 @@ void mp_usbd_port_get_serial_number(char *serial_buf) { const bsp_unique_id_t *id = R_BSP_UniqueIdGet(); - // convert to hex - int hexlen = sizeof(id->unique_id_bytes) * 2; - 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->unique_id_bytes[i / 2] >> 4]; - serial_buf[i + 1] = hexdig[id->unique_id_bytes[i / 2] & 0x0f]; - } - serial_buf[hexlen] = 0; + MP_STATIC_ASSERT(sizeof(id->unique_id_bytes) * 2 <= MICROPY_HW_USB_DESC_STR_MAX); + mp_usbd_hex_str(serial_buf, id->unique_id_bytes, sizeof(id->unique_id_bytes)); } #endif diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 15f6f6ca1c28..0f8c6d8e88a9 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -102,6 +102,7 @@ set(MICROPY_SOURCE_LIB ${MICROPY_DIR}/shared/runtime/mpirq.c ${MICROPY_DIR}/shared/runtime/pyexec.c ${MICROPY_DIR}/shared/runtime/stdout_helpers.c + ${MICROPY_DIR}/shared/runtime/softtimer.c ${MICROPY_DIR}/shared/runtime/sys_stdio_mphal.c ${MICROPY_DIR}/shared/timeutils/timeutils.c ${MICROPY_DIR}/shared/tinyusb/mp_cdc_common.c diff --git a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/mpconfigboard.h b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/mpconfigboard.h index 537c6d705705..b969341a7fe1 100644 --- a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/mpconfigboard.h +++ b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/mpconfigboard.h @@ -4,6 +4,9 @@ #define MICROPY_HW_BOARD_NAME "Arduino Nano RP2040 Connect" #define MICROPY_HW_FLASH_STORAGE_BYTES (8 * 1024 * 1024) +// Network config +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-nano-rp2040-connect" + // Enable networking. #define MICROPY_PY_NETWORK (1) diff --git a/ports/rp2/cyw43_configport.h b/ports/rp2/cyw43_configport.h index 8f64762df561..96324ee5ec96 100644 --- a/ports/rp2/cyw43_configport.h +++ b/ports/rp2/cyw43_configport.h @@ -104,11 +104,9 @@ void cyw43_post_poll_hook(void); extern volatile int cyw43_has_pending; static inline void cyw43_yield(void) { - uint32_t my_interrupts = save_and_disable_interrupts(); if (!cyw43_has_pending) { - __WFI(); + best_effort_wfe_or_timeout(make_timeout_time_ms(1)); } - restore_interrupts(my_interrupts); } static inline void cyw43_delay_us(uint32_t us) { @@ -118,12 +116,7 @@ static inline void cyw43_delay_us(uint32_t us) { } static inline void cyw43_delay_ms(uint32_t ms) { - uint32_t us = ms * 1000; - int32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - cyw43_yield(); - MICROPY_EVENT_POLL_HOOK_FAST; - } + mp_hal_delay_ms(ms); } #define CYW43_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK_FAST diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 682ea14476aa..43680a46cd68 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -37,6 +37,7 @@ #include "shared/readline/readline.h" #include "shared/runtime/gchelper.h" #include "shared/runtime/pyexec.h" +#include "shared/runtime/softtimer.h" #include "tusb.h" #include "uart.h" #include "modmachine.h" @@ -212,6 +213,7 @@ int main(int argc, char **argv) { #if MICROPY_PY_THREAD mp_thread_deinit(); #endif + soft_timer_deinit(); gc_sweep_all(); mp_deinit(); } diff --git a/ports/rp2/mpbthciport.c b/ports/rp2/mpbthciport.c index 16d50ddacc3a..1aacbe881f9f 100644 --- a/ports/rp2/mpbthciport.c +++ b/ports/rp2/mpbthciport.c @@ -30,36 +30,37 @@ #include "extmod/modbluetooth.h" #include "extmod/modmachine.h" #include "extmod/mpbthci.h" +#include "shared/runtime/softtimer.h" #include "modmachine.h" #include "mpbthciport.h" -#include "pico/stdlib.h" #if MICROPY_PY_BLUETOOTH #define debug_printf(...) // mp_printf(&mp_plat_print, "mpbthciport.c: " __VA_ARGS__) #define error_printf(...) mp_printf(&mp_plat_print, "mpbthciport.c: " __VA_ARGS__) -// Poll timer ID. -static alarm_id_t poll_timer_id = 0; - uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; +// Soft timer and scheduling node for scheduling a HCI poll. +static soft_timer_entry_t mp_bluetooth_hci_soft_timer; static mp_sched_node_t mp_bluetooth_hci_sched_node; -void mp_bluetooth_hci_init(void) { +// This is called by soft_timer and executes at PendSV level. +static void mp_bluetooth_hci_soft_timer_callback(soft_timer_entry_t *self) { + mp_bluetooth_hci_poll_now(); } -static int64_t mp_bluetooth_hci_timer_callback(alarm_id_t id, void *user_data) { - poll_timer_id = 0; - mp_bluetooth_hci_poll_now(); - return 0; +void mp_bluetooth_hci_init(void) { + soft_timer_static_init( + &mp_bluetooth_hci_soft_timer, + SOFT_TIMER_MODE_ONE_SHOT, + 0, + mp_bluetooth_hci_soft_timer_callback + ); } void mp_bluetooth_hci_poll_in_ms(uint32_t ms) { - if (poll_timer_id != 0) { - cancel_alarm(poll_timer_id); - } - poll_timer_id = add_alarm_in_ms(ms, mp_bluetooth_hci_timer_callback, NULL, true); + soft_timer_reinsert(&mp_bluetooth_hci_soft_timer, ms); } // For synchronous mode, we run all BLE stack code inside a scheduled task. @@ -110,10 +111,8 @@ int mp_bluetooth_hci_uart_deinit(void) { debug_printf("mp_bluetooth_hci_uart_deinit\n"); // If a poll callback is set cancel it now. - if (poll_timer_id > 0) { - cancel_alarm(poll_timer_id); - } - poll_timer_id = 0; + soft_timer_remove(&mp_bluetooth_hci_soft_timer); + return 0; } diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index 4b1c1fa1f5bf..1fb833f2e5dc 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -29,8 +29,10 @@ #include "py/mphal.h" #include "extmod/misc.h" #include "shared/runtime/interrupt_char.h" +#include "shared/runtime/softtimer.h" #include "shared/timeutils/timeutils.h" #include "shared/tinyusb/mp_usbd.h" +#include "pendsv.h" #include "tusb.h" #include "uart.h" #include "hardware/rtc.h" @@ -44,6 +46,8 @@ // microseconds since the Epoch. STATIC uint64_t time_us_64_offset_from_epoch; +static alarm_id_t soft_timer_alarm_id = 0; + #if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_USB_CDC #ifndef MICROPY_HW_STDIN_BUFFER_LEN @@ -188,10 +192,9 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { void mp_hal_delay_ms(mp_uint_t ms) { absolute_time_t t = make_timeout_time_ms(ms); - while (!time_reached(t)) { + do { MICROPY_EVENT_POLL_HOOK_FAST; - best_effort_wfe_or_timeout(t); - } + } while (!best_effort_wfe_or_timeout(t)); } void mp_hal_time_ns_set_from_rtc(void) { @@ -260,3 +263,22 @@ void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest) { uint32_t storage_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { panic_unsupported(); } + +static int64_t soft_timer_callback(alarm_id_t id, void *user_data) { + soft_timer_alarm_id = 0; + pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler); + return 0; // don't reschedule this alarm +} + +uint32_t soft_timer_get_ms(void) { + return mp_hal_ticks_ms(); +} + +void soft_timer_schedule_at_ms(uint32_t ticks_ms) { + if (soft_timer_alarm_id != 0) { + cancel_alarm(soft_timer_alarm_id); + } + int32_t ms = soft_timer_ticks_diff(ticks_ms, mp_hal_ticks_ms()); + ms = MAX(0, ms); + soft_timer_alarm_id = add_alarm_in_ms(ms, soft_timer_callback, NULL, true); +} diff --git a/ports/rp2/mphalport.h b/ports/rp2/mphalport.h index 95e7cba2c542..b8133c783e28 100644 --- a/ports/rp2/mphalport.h +++ b/ports/rp2/mphalport.h @@ -31,10 +31,14 @@ #include "hardware/clocks.h" #include "hardware/structs/systick.h" #include "RP2040.h" // cmsis, for __WFI +#include "pendsv.h" #define SYSTICK_MAX (0xffffff) #define MICROPY_HW_USB_CDC_TX_TIMEOUT (500) +#define MICROPY_PY_PENDSV_ENTER pendsv_suspend() +#define MICROPY_PY_PENDSV_EXIT pendsv_resume() + extern int mp_interrupt_char; extern ringbuf_t stdin_ringbuf; diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c index dac04760b846..09a543c2db34 100644 --- a/ports/rp2/mpnetworkport.c +++ b/ports/rp2/mpnetworkport.c @@ -30,13 +30,14 @@ #if MICROPY_PY_LWIP +#include "shared/runtime/softtimer.h" #include "lwip/timeouts.h" -#include "pico/time.h" // Poll lwIP every 64ms by default #define LWIP_TICK_RATE_MS 64 -static alarm_id_t lwip_alarm_id = -1; +// Soft timer for running lwIP in the background. +static soft_timer_entry_t mp_network_soft_timer; #if MICROPY_PY_NETWORK_CYW43 #include "lib/cyw43-driver/src/cyw43.h" @@ -56,6 +57,7 @@ static void gpio_irq_handler(void) { // CYW43_POST_POLL_HOOK which is called at the end of cyw43_poll_func. gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, CYW43_IRQ_LEVEL, false); cyw43_has_pending = 1; + __SEV(); pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, cyw43_poll); CYW43_STAT_INC(IRQ_COUNT); } @@ -88,11 +90,6 @@ u32_t sys_now(void) { return mp_hal_ticks_ms(); } -STATIC void lwip_poll(void) { - // Run the lwIP internal updates - sys_check_timeouts(); -} - void lwip_lock_acquire(void) { // Prevent PendSV from running. pendsv_suspend(); @@ -103,22 +100,25 @@ void lwip_lock_release(void) { pendsv_resume(); } -STATIC int64_t alarm_callback(alarm_id_t id, void *user_data) { - pendsv_schedule_dispatch(PENDSV_DISPATCH_LWIP, lwip_poll); +// This is called by soft_timer and executes at PendSV level. +static void mp_network_soft_timer_callback(soft_timer_entry_t *self) { + // Run the lwIP internal updates. + sys_check_timeouts(); + #if MICROPY_PY_NETWORK_WIZNET5K - pendsv_schedule_dispatch(PENDSV_DISPATCH_WIZNET, wiznet5k_poll); + wiznet5k_poll(); #endif - return LWIP_TICK_RATE_MS * 1000; } void mod_network_lwip_init(void) { - #if MICROPY_PY_NETWORK_WIZNET5K - wiznet5k_deinit(); - #endif - if (lwip_alarm_id != -1) { - cancel_alarm(lwip_alarm_id); - } - lwip_alarm_id = add_alarm_in_us(LWIP_TICK_RATE_MS * 1000, alarm_callback, mp_const_true, true); + soft_timer_static_init( + &mp_network_soft_timer, + SOFT_TIMER_MODE_PERIODIC, + LWIP_TICK_RATE_MS, + mp_network_soft_timer_callback + ); + + soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS); } #endif // MICROPY_PY_LWIP diff --git a/ports/rp2/mpnimbleport.c b/ports/rp2/mpnimbleport.c index 74e9ecb02602..fbeaf2beca80 100644 --- a/ports/rp2/mpnimbleport.c +++ b/ports/rp2/mpnimbleport.c @@ -69,9 +69,8 @@ void mp_bluetooth_hci_poll(void) { // --- Port-specific helpers for the generic NimBLE bindings. ----------------- void mp_bluetooth_nimble_hci_uart_wfi(void) { - #if defined(__WFI) - __WFI(); - #endif + best_effort_wfe_or_timeout(make_timeout_time_ms(1)); + // This is called while NimBLE is waiting in ble_npl_sem_pend, i.e. waiting for an HCI ACK. // Do not need to run events here (it must not invoke Python code), only processing incoming HCI data. mp_bluetooth_nimble_hci_uart_process(false); diff --git a/ports/rp2/pendsv.h b/ports/rp2/pendsv.h index 294cef3c70b6..bc8e8d61c8c0 100644 --- a/ports/rp2/pendsv.h +++ b/ports/rp2/pendsv.h @@ -29,9 +29,7 @@ #include enum { - #if MICROPY_PY_LWIP - PENDSV_DISPATCH_LWIP, - #endif + PENDSV_DISPATCH_SOFT_TIMER, #if MICROPY_PY_NETWORK_CYW43 PENDSV_DISPATCH_CYW43, #endif diff --git a/ports/rp2/usbd.c b/ports/rp2/usbd.c index 568b6284169b..8724f802f074 100644 --- a/ports/rp2/usbd.c +++ b/ports/rp2/usbd.c @@ -36,15 +36,8 @@ void mp_usbd_port_get_serial_number(char *serial_buf) { pico_unique_board_id_t id; pico_get_unique_board_id(&id); - // convert to hex - int hexlen = sizeof(id.id) * 2; - 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]; - serial_buf[i + 1] = hexdig[id.id[i / 2] & 0x0f]; - } - serial_buf[hexlen] = 0; + MP_STATIC_ASSERT(sizeof(id.id) * 2 <= MICROPY_HW_USB_DESC_STR_MAX); + mp_usbd_hex_str(serial_buf, id.id, sizeof(id.id)); } #endif diff --git a/ports/samd/Makefile b/ports/samd/Makefile index 12223e6ded8f..95d0f3bd18f4 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -55,6 +55,7 @@ INC += -I$(TOP)/lib/asf4/$(MCU_SERIES_LOWER)/hpl/tc INC += -I$(TOP)/lib/asf4/$(MCU_SERIES_LOWER)/include INC += -I$(TOP)/lib/asf4/$(MCU_SERIES_LOWER)/include/pio INC += -I$(TOP)/lib/tinyusb/src +INC += -I$(TOP)/shared/tinyusb/ MAKE_PINS = boards/make-pins.py BOARD_PINS = $(BOARD_DIR)/pins.csv @@ -117,7 +118,7 @@ SRC_C += \ samd_qspiflash.c \ samd_soc.c \ samd_spiflash.c \ - tusb_port.c \ + usbd.c \ SHARED_SRC_C += \ drivers/dht/dht.c \ @@ -133,7 +134,8 @@ SHARED_SRC_C += \ shared/runtime/sys_stdio_mphal.c \ shared/timeutils/timeutils.c \ shared/tinyusb/mp_cdc_common.c \ - shared/tinyusb/mp_usbd.c + shared/tinyusb/mp_usbd.c \ + shared/tinyusb/mp_usbd_descriptor.c \ ASF4_SRC_C += $(addprefix lib/asf4/$(MCU_SERIES_LOWER)/,\ hal/src/hal_atomic.c \ diff --git a/ports/samd/mcu/samd21/mpconfigmcu.mk b/ports/samd/mcu/samd21/mpconfigmcu.mk index 35f442d86990..b3092837cbd1 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.mk +++ b/ports/samd/mcu/samd21/mpconfigmcu.mk @@ -1,5 +1,7 @@ CFLAGS_MCU += -mtune=cortex-m0plus -mcpu=cortex-m0plus -msoft-float +CFLAGS_MCU += -DCFG_TUSB_MCU=OPT_MCU_SAMD21 + MPY_CROSS_MCU_ARCH = armv6m MICROPY_HW_CODESIZE ?= 184K diff --git a/ports/samd/mcu/samd51/mpconfigmcu.mk b/ports/samd/mcu/samd51/mpconfigmcu.mk index c706331aca45..0201bacffeef 100644 --- a/ports/samd/mcu/samd51/mpconfigmcu.mk +++ b/ports/samd/mcu/samd51/mpconfigmcu.mk @@ -1,5 +1,7 @@ CFLAGS_MCU += -mtune=cortex-m4 -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard +CFLAGS_MCU += -DCFG_TUSB_MCU=OPT_MCU_SAMD51 + MPY_CROSS_MCU_ARCH = armv7m MICROPY_HW_CODESIZE ?= 368K diff --git a/ports/samd/modmachine.c b/ports/samd/modmachine.c index 7c3d96eb0136..8c4808075e7a 100644 --- a/ports/samd/modmachine.c +++ b/ports/samd/modmachine.c @@ -92,45 +92,9 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 1, machine_freq); STATIC mp_obj_t machine_unique_id(void) { - // Each device has a unique 128-bit serial number which is a concatenation of four 32-bit - // words contained at the following addresses. The uniqueness of the serial number is - // guaranteed only when using all 128 bits. - // Atmel SAM D21E / SAM D21G / SAM D21J - // SMART ARM-Based Microcontroller - // DATASHEET - // 9.6 (SAMD51) or 9.3.3 (or 10.3.3 depending on which manual)(SAMD21) Serial Number - // - // EXAMPLE (SAMD21) - // ---------------- - // OpenOCD: - // Word0: - // > at91samd21g18.cpu mdw 0x0080A00C 1 - // 0x0080a00c: 6e27f15f - // Words 1-3: - // > at91samd21g18.cpu mdw 0x0080A040 3 - // 0x0080a040: 50534b54 332e3120 ff091645 - // - // MicroPython (this code and same order as shown in Arduino IDE) - // >>> binascii.hexlify(machine.unique_id()) - // b'6e27f15f50534b54332e3120ff091645' - - #if defined(MCU_SAMD21) - uint32_t *id_addresses[4] = {(uint32_t *)0x0080A00C, (uint32_t *)0x0080A040, - (uint32_t *)0x0080A044, (uint32_t *)0x0080A048}; - #elif defined(MCU_SAMD51) - uint32_t *id_addresses[4] = {(uint32_t *)0x008061FC, (uint32_t *)0x00806010, - (uint32_t *)0x00806014, (uint32_t *)0x00806018}; - #endif - uint8_t raw_id[16]; - - for (int i = 0; i < 4; i++) { - for (int k = 0; k < 4; k++) { - // 'Reverse' the read bytes into a 32 bit word (Consistent with Arduino) - raw_id[4 * i + k] = (*(id_addresses[i]) >> (24 - k * 8)) & 0xff; - } - } - - return mp_obj_new_bytes((byte *)&raw_id, sizeof(raw_id)); + samd_unique_id_t id; + samd_get_unique_id(&id); + return mp_obj_new_bytes((byte *)&id.bytes, sizeof(id.bytes)); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id); diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index 91a7c1a10d87..3684457b48e0 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -62,6 +62,18 @@ #define MICROPY_HW_ENABLE_USBDEV (1) #define MICROPY_HW_USB_CDC_1200BPS_TOUCH (1) +#if MICROPY_HW_ENABLE_USBDEV +// Enable USB-CDC serial port +#ifndef MICROPY_HW_USB_CDC +#define MICROPY_HW_USB_CDC (1) +#endif +// SAMD unique ID is 16 bytes (hex string == 32) +#ifndef MICROPY_HW_USB_DESC_STR_MAX +#define MICROPY_HW_USB_DESC_STR_MAX (32) +#endif + +#endif + // Control over Python builtins #define MICROPY_PY_BUILTINS_BYTES_HEX (1) #define MICROPY_PY_BUILTINS_MEMORYVIEW (1) @@ -141,6 +153,15 @@ #define MP_STATE_PORT MP_STATE_VM +// Miscellaneous settings + +#ifndef MICROPY_HW_USB_VID +#define MICROPY_HW_USB_VID (0xf055) +#endif +#ifndef MICROPY_HW_USB_PID +#define MICROPY_HW_USB_PID (0x9802) +#endif + // Additional entries for use with pendsv_schedule_dispatch. #ifndef MICROPY_BOARD_PENDSV_ENTRIES #define MICROPY_BOARD_PENDSV_ENTRIES diff --git a/ports/samd/mphalport.c b/ports/samd/mphalport.c index dc38ea2964eb..3c803403f213 100644 --- a/ports/samd/mphalport.c +++ b/ports/samd/mphalport.c @@ -29,6 +29,7 @@ #include "py/mphal.h" #include "py/stream.h" #include "shared/runtime/interrupt_char.h" +#include "shared/tinyusb/mp_usbd.h" #include "extmod/misc.h" #include "samd_soc.h" #include "tusb.h" @@ -42,6 +43,14 @@ extern volatile uint32_t ticks_us64_upper; STATIC uint8_t stdin_ringbuf_array[MICROPY_HW_STDIN_BUFFER_LEN]; ringbuf_t stdin_ringbuf = { stdin_ringbuf_array, sizeof(stdin_ringbuf_array), 0, 0 }; +// Explicitly run the USB stack in case the scheduler is locked (eg we are in an +// interrupt handler) and there is in/out data pending on the USB CDC interface. +#define MICROPY_EVENT_POLL_HOOK_WITH_USB \ + do { \ + MICROPY_EVENT_POLL_HOOK; \ + mp_usbd_task(); \ + } while (0) + uint8_t cdc_itf_pending; // keep track of cdc interfaces which need attention to poll void poll_cdc_interfaces(void) { @@ -187,7 +196,7 @@ int mp_hal_stdin_rx_chr(void) { return dupterm_c; } #endif - MICROPY_EVENT_POLL_HOOK + MICROPY_EVENT_POLL_HOOK_WITH_USB; } } @@ -201,7 +210,7 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { int timeout = 0; // Wait with a max of USC_CDC_TIMEOUT ms while (n > tud_cdc_write_available() && timeout++ < MICROPY_HW_USB_CDC_TX_TIMEOUT) { - MICROPY_EVENT_POLL_HOOK + MICROPY_EVENT_POLL_HOOK_WITH_USB; } if (timeout >= MICROPY_HW_USB_CDC_TX_TIMEOUT) { break; diff --git a/ports/samd/mphalport.h b/ports/samd/mphalport.h index 83ccf44222d3..ee33e7446286 100644 --- a/ports/samd/mphalport.h +++ b/ports/samd/mphalport.h @@ -35,6 +35,9 @@ #include "hpl_time_measure.h" #include "sam.h" +#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV) +#define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state) + #define MICROPY_HW_USB_CDC_TX_TIMEOUT (500) extern int mp_interrupt_char; diff --git a/ports/samd/samd_soc.c b/ports/samd/samd_soc.c index ad5910301cfc..dcfff9dc2274 100644 --- a/ports/samd/samd_soc.c +++ b/ports/samd/samd_soc.c @@ -153,3 +153,39 @@ void sercom_deinit_all(void) { } #endif + +void samd_get_unique_id(samd_unique_id_t *id) { + // Atmel SAM D21E / SAM D21G / SAM D21J + // SMART ARM-Based Microcontroller + // DATASHEET + // 9.6 (SAMD51) or 9.3.3 (or 10.3.3 depending on which manual)(SAMD21) Serial Number + // + // EXAMPLE (SAMD21) + // ---------------- + // OpenOCD: + // Word0: + // > at91samd21g18.cpu mdw 0x0080A00C 1 + // 0x0080a00c: 6e27f15f + // Words 1-3: + // > at91samd21g18.cpu mdw 0x0080A040 3 + // 0x0080a040: 50534b54 332e3120 ff091645 + // + // MicroPython (this code and same order as shown in Arduino IDE) + // >>> binascii.hexlify(machine.unique_id()) + // b'6e27f15f50534b54332e3120ff091645' + + #if defined(MCU_SAMD21) + uint32_t *id_addresses[4] = {(uint32_t *)0x0080A00C, (uint32_t *)0x0080A040, + (uint32_t *)0x0080A044, (uint32_t *)0x0080A048}; + #elif defined(MCU_SAMD51) + uint32_t *id_addresses[4] = {(uint32_t *)0x008061FC, (uint32_t *)0x00806010, + (uint32_t *)0x00806014, (uint32_t *)0x00806018}; + #endif + + for (int i = 0; i < 4; i++) { + for (int k = 0; k < 4; k++) { + // 'Reverse' the read bytes into a 32 bit word (Consistent with Arduino) + id->bytes[4 * i + k] = (*(id_addresses[i]) >> (24 - k * 8)) & 0xff; + } + } +} diff --git a/ports/samd/samd_soc.h b/ports/samd/samd_soc.h index 90a5a57ffa14..707d2f8edfa9 100644 --- a/ports/samd/samd_soc.h +++ b/ports/samd/samd_soc.h @@ -30,6 +30,10 @@ #include "sam.h" #include "clock_config.h" +typedef struct _samd_unique_id_t { + uint8_t bytes[16]; +} samd_unique_id_t; + extern Sercom *sercom_instance[]; void samd_init(void); @@ -40,6 +44,10 @@ void USB_Handler_wrapper(void); void sercom_enable(Sercom *spi, int state); void sercom_register_irq(int sercom_id, void (*sercom_irq_handler)); +// Each device has a unique 128-bit serial number. The uniqueness of the serial number is +// guaranteed only when using all 128 bits. +void samd_get_unique_id(samd_unique_id_t *id); + #define SERCOM_IRQ_TYPE_UART (0) #define SERCOM_IRQ_TYPE_SPI (1) #define SERCOM_IRQ_TYPE_I2C (2) diff --git a/ports/samd/tusb_port.c b/ports/samd/tusb_port.c deleted file mode 100644 index e56ef0fd6af0..000000000000 --- a/ports/samd/tusb_port.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Damien P. George - * - * 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 "samd_soc.h" -#include "tusb.h" - -#ifndef MICROPY_HW_USB_VID -#define MICROPY_HW_USB_VID (0xf055) -#define MICROPY_HW_USB_PID (0x9802) -#endif - -#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) -#define USBD_MAX_POWER_MA (250) - -#define USBD_ITF_CDC (0) // needs 2 interfaces -#define USBD_ITF_MAX (2) - -#define USBD_CDC_EP_CMD (0x81) -#define USBD_CDC_EP_OUT (0x02) -#define USBD_CDC_EP_IN (0x82) -#define USBD_CDC_CMD_MAX_SIZE (8) -#define USBD_CDC_IN_OUT_MAX_SIZE (64) - -#define USBD_STR_0 (0x00) -#define USBD_STR_MANUF (0x01) -#define USBD_STR_PRODUCT (0x02) -#define USBD_STR_SERIAL (0x03) -#define USBD_STR_CDC (0x04) - -// Note: descriptors returned from callbacks must exist long enough for transfer to complete - -static const tusb_desc_device_t usbd_desc_device = { - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0200, - .bDeviceClass = TUSB_CLASS_MISC, - .bDeviceSubClass = MISC_SUBCLASS_COMMON, - .bDeviceProtocol = MISC_PROTOCOL_IAD, - .bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE, - .idVendor = MICROPY_HW_USB_VID, - .idProduct = MICROPY_HW_USB_PID, - .bcdDevice = 0x0100, - .iManufacturer = USBD_STR_MANUF, - .iProduct = USBD_STR_PRODUCT, - .iSerialNumber = USBD_STR_SERIAL, - .bNumConfigurations = 1, -}; - -static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { - TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, - TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA), - - TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, - USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), -}; - -static const char *const usbd_desc_str[] = { - [USBD_STR_MANUF] = "MicroPython", - [USBD_STR_PRODUCT] = "Board in FS mode", - [USBD_STR_SERIAL] = "000000000000", // TODO - [USBD_STR_CDC] = "Board CDC", -}; - -const uint8_t *tud_descriptor_device_cb(void) { - return (const uint8_t *)&usbd_desc_device; -} - -const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { - (void)index; - return usbd_desc_cfg; -} - -const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { - #define DESC_STR_MAX (20) - static uint16_t desc_str[DESC_STR_MAX]; - - uint8_t len; - if (index == 0) { - desc_str[1] = 0x0409; // supported language is English - len = 1; - } else { - if (index >= sizeof(usbd_desc_str) / sizeof(usbd_desc_str[0])) { - return NULL; - } - const char *str = usbd_desc_str[index]; - for (len = 0; len < DESC_STR_MAX - 1 && str[len]; ++len) { - desc_str[1 + len] = str[len]; - } - } - - // first byte is length (including header), second byte is string type - desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * len + 2); - - return desc_str; -} - -void USB_Handler_wrapper(void) { - tud_int_handler(0); -} diff --git a/ports/samd/tusb_config.h b/ports/samd/usbd.c similarity index 64% rename from ports/samd/tusb_config.h rename to ports/samd/usbd.c index 07fa680cbbb6..2e3200ad7d2b 100644 --- a/ports/samd/tusb_config.h +++ b/ports/samd/usbd.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Damien P. George + * Copyright (c) 2023 Angus Gratton * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,25 +23,25 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_SAMD_TUSB_CONFIG_H -#define MICROPY_INCLUDED_SAMD_TUSB_CONFIG_H -// Common configuration +#include "py/mpconfig.h" -#if defined(MCU_SAMD21) -#define CFG_TUSB_MCU OPT_MCU_SAMD21 -#elif defined(MCU_SAMD51) -#define CFG_TUSB_MCU OPT_MCU_SAMD51 -#endif -#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE -#define CFG_TUSB_MEM_SECTION -#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4) +#if MICROPY_HW_ENABLE_USBDEV + +#include "mp_usbd.h" +#include "string.h" +#include "tusb.h" +#include "samd_soc.h" -// Device configuration +void mp_usbd_port_get_serial_number(char *serial_buf) { + samd_unique_id_t id; + samd_get_unique_id(&id); + MP_STATIC_ASSERT(sizeof(id.bytes) * 2 <= MICROPY_HW_USB_DESC_STR_MAX); + mp_usbd_hex_str(serial_buf, id.bytes, sizeof(id.bytes)); +} -#define CFG_TUD_ENDOINT0_SIZE (64) -#define CFG_TUD_CDC (1) -#define CFG_TUD_CDC_RX_BUFSIZE (64) -#define CFG_TUD_CDC_TX_BUFSIZE (64) +void USB_Handler_wrapper(void) { + tud_int_handler(0); +} -#endif // MICROPY_INCLUDED_SAMD_TUSB_CONFIG_H +#endif diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index edebd2db9d8f..56b5a1020c5e 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -408,7 +408,7 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ ) endif -ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h7 l4)) +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h5 h7 l4)) HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_sd.c \ ll_sdmmc.c \ diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h index ec9bbed5ea6e..b278fa005b75 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h @@ -4,10 +4,12 @@ * Copyright (c) 2023 Arduino SA */ -#define MICROPY_HW_BOARD_NAME "GIGA" +#define MICROPY_HW_BOARD_NAME "Arduino GIGA R1 WiFi" #define MICROPY_HW_MCU_NAME "STM32H747" -#define MICROPY_PY_SYS_PLATFORM "Giga" -#define MICROPY_HW_FLASH_FS_LABEL "Giga" +#define MICROPY_HW_FLASH_FS_LABEL "GIGA R1 WiFi" + +// Network config +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-giga-r1-wifi" #define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C) #define UINT_FMT "%u" diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h index 3cae9b25fa34..3a2def4d4044 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h @@ -4,10 +4,12 @@ * Copyright (c) 2023 Arduino SA */ -#define MICROPY_HW_BOARD_NAME "NICLAVISION" +#define MICROPY_HW_BOARD_NAME "Arduino Nicla Vision" #define MICROPY_HW_MCU_NAME "STM32H747" -#define MICROPY_PY_SYS_PLATFORM "Nicla Vision" -#define MICROPY_HW_FLASH_FS_LABEL "niclavision" +#define MICROPY_HW_FLASH_FS_LABEL "Nicla Vision" + +// Network config +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-nicla-vision" #define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C) #define UINT_FMT "%u" diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h index 133158c60add..e2e6956a47d6 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h @@ -4,12 +4,12 @@ * Copyright (c) 2022 Arduino SA */ -#define MICROPY_HW_BOARD_NAME "PORTENTA" +#define MICROPY_HW_BOARD_NAME "Arduino Portenta H7" #define MICROPY_HW_MCU_NAME "STM32H747" -#define MICROPY_PY_SYS_PLATFORM "Portenta" -#define MICROPY_HW_FLASH_FS_LABEL "portenta" +#define MICROPY_HW_FLASH_FS_LABEL "Portenta H7" -#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "Portenta" +// Network config +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-portenta-h7" #define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C) #define UINT_FMT "%u" diff --git a/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h b/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h index 88bfd39b4166..a0a6dbf8c51e 100644 --- a/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_F446RE/mpconfigboard.h @@ -16,10 +16,20 @@ #define MICROPY_HW_CLK_PLLQ (7) // UART config -#define MICROPY_HW_UART2_TX (pin_A2) -#define MICROPY_HW_UART2_RX (pin_A3) -#define MICROPY_HW_UART6_TX (pin_C6) -#define MICROPY_HW_UART6_RX (pin_C7) +#define MICROPY_HW_UART1_TX (pin_B6) // Arduino D10, pin 17 on CN10 +#define MICROPY_HW_UART1_RX (pin_B7) // pin 21 on CN7 +#define MICROPY_HW_UART2_TX (pin_A2) // Arduino D1, pin 35 on CN10 +#define MICROPY_HW_UART2_RX (pin_A3) // Arduino D0, pin 37 on CN10 +#define MICROPY_HW_UART2_RTS (pin_A1) // Arduino A1, pin 30 on CN7 +#define MICROPY_HW_UART2_CTS (pin_A0) // Arduino A0, pin 28 on CN7 +#define MICROPY_HW_UART3_TX (pin_B10) // Arduino D6, pin 25 on CN10 +#define MICROPY_HW_UART3_RX (pin_C5) // pin 6 on CN10 +#define MICROPY_HW_UART3_RTS (pin_B14) // pin 28 on CN10 +#define MICROPY_HW_UART3_CTS (pin_B13) // pin 30 on CN10 +#define MICROPY_HW_UART4_TX (pin_A0) // Arduino A0, pin 28 on CN7 +#define MICROPY_HW_UART4_RX (pin_A1) // Arduino A1, pin 30 on CN7 +#define MICROPY_HW_UART6_TX (pin_C6) // pin 4 on CN10 +#define MICROPY_HW_UART6_RX (pin_C7) // Arduino D9, pin 19 on CN10 // UART 2 connects to the STM32F103 (STLINK) on the Nucleo board // and this is exposed as a USB Serial port. #define MICROPY_HW_UART_REPL PYB_UART_2 diff --git a/ports/stm32/boards/NUCLEO_H563ZI/board.json b/ports/stm32/boards/NUCLEO_H563ZI/board.json new file mode 100644 index 000000000000..b9c4e41b3eb3 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H563ZI/board.json @@ -0,0 +1,15 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [], + "images": [ + "nucleo_h563zi.jpg" + ], + "mcu": "stm32h5", + "product": "Nucleo H563ZI", + "thumbnail": "", + "url": "", + "vendor": "ST Microelectronics" +} diff --git a/ports/stm32/boards/NUCLEO_H563ZI/mpconfigboard.h b/ports/stm32/boards/NUCLEO_H563ZI/mpconfigboard.h new file mode 100644 index 000000000000..1e802eda5b04 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H563ZI/mpconfigboard.h @@ -0,0 +1,91 @@ +#define MICROPY_HW_BOARD_NAME "NUCLEO_H563ZI" +#define MICROPY_HW_MCU_NAME "STM32H563ZI" + +#define MICROPY_PY_PYB_LEGACY (0) +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_ADC (1) +#define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_HW_ENABLE_USB (1) +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_HAS_FLASH (1) + +// The board has a 8MHz oscillator, the following gives 250MHz CPU speed +#define MICROPY_HW_CLK_USE_BYPASS (1) +#define MICROPY_HW_CLK_PLLM (2) +#define MICROPY_HW_CLK_PLLN (125) +#define MICROPY_HW_CLK_PLLP (2) +#define MICROPY_HW_CLK_PLLQ (2) +#define MICROPY_HW_CLK_PLLR (2) +#define MICROPY_HW_CLK_PLLVCI_LL (LL_RCC_PLLINPUTRANGE_4_8) +#define MICROPY_HW_CLK_PLLVCO_LL (LL_RCC_PLLVCORANGE_WIDE) +#define MICROPY_HW_CLK_PLLFRAC (0) + +// PLL3 with Q output at 48MHz for USB +// #define MICROPY_HW_CLK_USE_PLL3_FOR_USB +#define MICROPY_HW_CLK_PLL3M (8) +#define MICROPY_HW_CLK_PLL3N (192) +#define MICROPY_HW_CLK_PLL3P (2) +#define MICROPY_HW_CLK_PLL3Q (4) +#define MICROPY_HW_CLK_PLL3R (2) +#define MICROPY_HW_CLK_PLL3FRAC (0) +#define MICROPY_HW_CLK_PLL3VCI_LL (LL_RCC_PLLINPUTRANGE_1_2) +#define MICROPY_HW_CLK_PLL3VCO_LL (LL_RCC_PLLVCORANGE_MEDIUM) + +// 5 wait states, according to Table 37, Reference Manual (RM0481 Rev 1) +#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_5 + +// There is an external 32kHz oscillator +#define MICROPY_HW_RTC_USE_LSE (1) + +// UART config +#define MICROPY_HW_UART1_TX (pin_B6) // SB14: Arduino Connector CN10-Pin14 (D1) +#define MICROPY_HW_UART1_RX (pin_B7) // SB63: Arduino Connector CN10-Pin16 (D0) +#define MICROPY_HW_UART3_TX (pin_D8) // SB23: ST-Link +#define MICROPY_HW_UART3_RX (pin_D9) // SB18: ST-Link + +// Connect REPL to UART3 which is provided on ST-Link USB interface +#define MICROPY_HW_UART_REPL PYB_UART_3 +#define MICROPY_HW_UART_REPL_BAUD 115200 + +// I2C buses +#define MICROPY_HW_I2C1_SCL (pin_B8) // Arduino Connector CN7-Pin2 (D15) +#define MICROPY_HW_I2C1_SDA (pin_B9) // Arduino Connector CN7-Pin4 (D14) +#define MICROPY_HW_I2C2_SCL (pin_F1) // Connector CN9-Pin19 +#define MICROPY_HW_I2C2_SDA (pin_F0) // Connector CN9-Pin21 + +// SPI buses +// PD14 according to datasheet not working as SPI1_NSS, have to use as GPIO, not as AF +#define MICROPY_HW_SPI1_NSS (pin_D14) // Arduino Connector CN7-Pin16 (D10) +#define MICROPY_HW_SPI1_SCK (pin_A5) // Arduino Connector CN7-Pin10 (D13) +#define MICROPY_HW_SPI1_MISO (pin_G9) // Arduino Connector CN7-Pin12 (D12) +#define MICROPY_HW_SPI1_MOSI (pin_B5) // Arduino Connector CN7-Pin14 (D11) + +// 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_F4) // Orange +#define MICROPY_HW_LED3 (pin_G4) // 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_FS (1) +#define MICROPY_HW_USB_MAIN_DEV (USB_PHY_FS_ID) + +// 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_B15) diff --git a/ports/stm32/boards/NUCLEO_H563ZI/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_H563ZI/mpconfigboard.mk new file mode 100644 index 000000000000..a55a54d1c76d --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H563ZI/mpconfigboard.mk @@ -0,0 +1,23 @@ +USE_MBOOT ?= 0 + +# MCU settings +MCU_SERIES = h5 +CMSIS_MCU = STM32H573xx +MICROPY_FLOAT_IMPL = single +AF_FILE = boards/stm32h573_af.csv + +ifeq ($(USE_MBOOT),1) +# When using Mboot everything goes after the bootloader +# TODO: not tested +LD_FILES = boards/stm32h573xi.ld boards/common_bl.ld +TEXT0_ADDR = 0x08008000 +else +# When not using Mboot everything goes at the start of flash +LD_FILES = boards/stm32h573xi.ld boards/common_basic.ld +TEXT0_ADDR = 0x08000000 +endif + +# MicroPython settings +MICROPY_PY_LWIP = 1 +MICROPY_PY_SSL = 1 +MICROPY_SSL_MBEDTLS = 1 diff --git a/ports/stm32/boards/NUCLEO_H563ZI/pins.csv b/ports/stm32/boards/NUCLEO_H563ZI/pins.csv new file mode 100644 index 000000000000..1f83e8a505b8 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H563ZI/pins.csv @@ -0,0 +1,130 @@ +,PA0 +,PA1 +,PA2 +,PA3 +,PA4 +,PA5 +,PA7 +,PA8 +,PA9 +,PA10 +,PA11 +,PA12 +,PA13 +,PA14 +,PA15 +,PB0 +,PB2 +,PB3 +,PB4 +,PB5 +,PB6 +,PB7 +,PB8 +,PB9 +,PB10 +,PB11 +,PB12 +,PB13 +,PB14 +,PB15 +,PC1 +,PC4 +,PC5 +,PC6 +,PC7 +,PC8 +,PC9 +,PC10 +,PC11 +,PC12 +,PC13 +,PC14 +,PC15 +,PD0 +,PD1 +,PD2 +,PD3 +,PD4 +,PD5 +,PD6 +,PD7 +,PD8 +,PD9 +,PD10 +,PD11 +,PD12 +,PD13 +,PD14 +,PE0 +,PE1 +,PE2 +,PE3 +,PE4 +,PE5 +,PE6 +,PE7 +,PE8 +,PE10 +,PE12 +,PE15 +,PF0 +,PF1 +,PF2 +,PF4 +,PF5 +,PF6 +,PF7 +,PF8 +,PF9 +,PF10 +,PF12 +,PF13 +,PF14 +,PF15 +,PG0 +,PG1 +,PG2 +,PG3 +,PG4 +,PG5 +,PG6 +,PG7 +,PG8 +,PG9 +,PG10 +,PG11 +,PG13 +,PG15 +A0,PA6 +A1,PC0 +A2,PC3 +A3,PB1 +A4,PC2 +A5,PF11 +D2,PG14 +D3,PE13 +D4,PE14 +D5,PE11 +D6,PE9 +D7,PG12 +D8,PF3 +D9,PD15 +UART1_TX,PB6 +UART1_RX,PB7 +UART3_TX,PD8 +UART3_RX,PD9 +LED_GREEN,PB0 +LED_ORANGE,PF4 +LED_RED,PG4 +SW,PC13 +I2C1_SDA,PB9 +I2C1_SCL,PB8 +I2C2_SDA,PF1 +I2C2_SCL,PF0 +USB_DM,PA11 +USB_DP,PA12 +SPI_A_CS,PD14 +SPI_A_SCK,PA5 +SPI_A_MOSI,PB5 +SPI_A_MISO,PG9 diff --git a/ports/stm32/boards/NUCLEO_H563ZI/stm32h5xx_hal_conf.h b/ports/stm32/boards/NUCLEO_H563ZI/stm32h5xx_hal_conf.h new file mode 100644 index 000000000000..f86c9300e387 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_H563ZI/stm32h5xx_hal_conf.h @@ -0,0 +1,19 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2023 Damien P. George + */ +#ifndef MICROPY_INCLUDED_STM32H5XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32H5XX_HAL_CONF_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 (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#include "boards/stm32h5xx_hal_conf_base.h" + +#endif // MICROPY_INCLUDED_STM32H5XX_HAL_CONF_H diff --git a/ports/stm32/boards/STM32H573I_DK/mpconfigboard.h b/ports/stm32/boards/STM32H573I_DK/mpconfigboard.h index fba14329a5ce..c8e0340af1ec 100644 --- a/ports/stm32/boards/STM32H573I_DK/mpconfigboard.h +++ b/ports/stm32/boards/STM32H573I_DK/mpconfigboard.h @@ -8,6 +8,7 @@ #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) @@ -106,6 +107,11 @@ #define MICROPY_HW_USB_FS (1) #define MICROPY_HW_USB_MAIN_DEV (USB_PHY_FS_ID) +// SD card +#define MICROPY_HW_SDCARD_DETECT_PIN (pin_H14) +#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) diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index 0a5d25c16cc7..af56d812367c 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -156,7 +156,7 @@ static const DMA_InitTypeDef dma_init_struct_i2s = { }; #endif -#if ENABLE_SDIO && !defined(STM32H7) +#if ENABLE_SDIO && !defined(STM32H5) && !defined(STM32H7) // Parameters to dma_init() for SDIO tx and rx. static const DMA_InitTypeDef dma_init_struct_sdio = { #if defined(STM32F4) || defined(STM32F7) diff --git a/ports/stm32/i2c.c b/ports/stm32/i2c.c index 42aba2eccbe5..52f227a950eb 100644 --- a/ports/stm32/i2c.c +++ b/ports/stm32/i2c.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include #include "py/mperrno.h" #include "py/mphal.h" #include "py/runtime.h" diff --git a/ports/stm32/lwip_inc/lwipopts.h b/ports/stm32/lwip_inc/lwipopts.h index 7b2460fa80aa..f641cf515add 100644 --- a/ports/stm32/lwip_inc/lwipopts.h +++ b/ports/stm32/lwip_inc/lwipopts.h @@ -24,6 +24,9 @@ #define LWIP_NETIF_HOSTNAME 1 #define LWIP_NETIF_EXT_STATUS_CALLBACK 1 +#define LWIP_LOOPIF_MULTICAST 1 +#define LWIP_LOOPBACK_MAX_PBUFS 8 + #define LWIP_IPV6 0 #define LWIP_DHCP 1 #define LWIP_DHCP_CHECK_LINK_UP 1 diff --git a/ports/stm32/machine_i2s.c b/ports/stm32/machine_i2s.c index dbcd9352d5bd..b3e1cee34f32 100644 --- a/ports/stm32/machine_i2s.c +++ b/ports/stm32/machine_i2s.c @@ -29,6 +29,7 @@ // extmod/machine_i2s.c via MICROPY_PY_MACHINE_I2S_INCLUDEFILE. #include +#include #include "py/mphal.h" #include "pin.h" #include "dma.h" diff --git a/ports/stm32/mpnetworkport.c b/ports/stm32/mpnetworkport.c index 62f780a35a04..3b9591213a8d 100644 --- a/ports/stm32/mpnetworkport.c +++ b/ports/stm32/mpnetworkport.c @@ -72,6 +72,10 @@ STATIC void pyb_lwip_poll(void) { // Run the lwIP internal updates sys_check_timeouts(); + + #if LWIP_NETIF_LOOPBACK + netif_poll_all(); + #endif } void mod_network_lwip_poll_wrapper(uint32_t ticks_ms) { diff --git a/ports/stm32/mpu.h b/ports/stm32/mpu.h index e95a07da91a8..b3e2aec2d807 100644 --- a/ports/stm32/mpu.h +++ b/ports/stm32/mpu.h @@ -26,7 +26,7 @@ #ifndef MICROPY_INCLUDED_STM32_MPU_H #define MICROPY_INCLUDED_STM32_MPU_H -#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F7) || defined(STM32H7) +#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F7) || defined(STM32H7) || defined(STM32WB) #define MPU_REGION_ETH (MPU_REGION_NUMBER0) #define MPU_REGION_QSPI1 (MPU_REGION_NUMBER1) diff --git a/ports/stm32/pin_static_af.h b/ports/stm32/pin_static_af.h index 49580a278746..86ae90ba4c6c 100644 --- a/ports/stm32/pin_static_af.h +++ b/ports/stm32/pin_static_af.h @@ -26,6 +26,9 @@ #ifndef MICROPY_INCLUDED_STM32_PIN_STATIC_AF_H #define MICROPY_INCLUDED_STM32_PIN_STATIC_AF_H +// For debug builds some of the macros expand to use strcmp. +#include + #include "py/mphal.h" #include "genhdr/pins.h" #include "genhdr/pins_af_defs.h" diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index d09ee21d09bf..a903f79ed216 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -40,7 +40,7 @@ #if MICROPY_HW_ENABLE_SDCARD || MICROPY_HW_ENABLE_MMCARD -#if defined(STM32F7) || defined(STM32H7) || defined(STM32L4) +#if defined(STM32F7) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) // The H7/F7/L4 have 2 SDMMC peripherals, but at the moment this driver only supports // using one of them in a given build, selected by MICROPY_HW_SDCARD_SDMMC. @@ -104,7 +104,7 @@ #define SDIO_HARDWARE_FLOW_CONTROL_DISABLE SDMMC_HARDWARE_FLOW_CONTROL_DISABLE #define SDIO_HARDWARE_FLOW_CONTROL_ENABLE SDMMC_HARDWARE_FLOW_CONTROL_ENABLE -#if defined(STM32H7) +#if defined(STM32H5) || defined(STM32H7) #define SDIO_TRANSFER_CLK_DIV SDMMC_NSpeed_CLK_DIV #define SDIO_USE_GPDMA 0 #else @@ -268,7 +268,7 @@ STATIC HAL_StatusTypeDef sdmmc_init_sd(void) { // SD device interface configuration sdmmc_handle.sd.Instance = SDIO; sdmmc_handle.sd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING; - #ifndef STM32H7 + #if !defined(STM32H5) && !defined(STM32H7) sdmmc_handle.sd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE; #endif sdmmc_handle.sd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_ENABLE; diff --git a/ports/stm32/spi.c b/ports/stm32/spi.c index 47714a28725b..ee2301c803b8 100644 --- a/ports/stm32/spi.c +++ b/ports/stm32/spi.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include #include "py/runtime.h" #include "py/mperrno.h" #include "py/mphal.h" diff --git a/py/misc.h b/py/misc.h index 352a9f34c451..e19a535720fb 100644 --- a/py/misc.h +++ b/py/misc.h @@ -52,10 +52,15 @@ typedef unsigned int uint; // Static assertion macro #define MP_STATIC_ASSERT(cond) ((void)sizeof(char[1 - 2 * !(cond)])) -#if defined(_MSC_VER) -#define MP_STATIC_ASSERT_NOT_MSC(cond) (1) +// In C++ things like comparing extern const pointers are not constant-expressions so cannot be used +// in MP_STATIC_ASSERT. Note that not all possible compiler versions will reject this. Some gcc versions +// do, others only with -Werror=vla, msvc always does. +// The (void) is needed to avoid "left operand of comma operator has no effect [-Werror=unused-value]" +// when using this macro on the left-hand side of a comma. +#if defined(_MSC_VER) || defined(__cplusplus) +#define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)1) #else -#define MP_STATIC_ASSERT_NOT_MSC(cond) MP_STATIC_ASSERT(cond) +#define MP_STATIC_ASSERT_NONCONSTEXPR(cond) MP_STATIC_ASSERT(cond) #endif // Round-up integer division diff --git a/py/mkrules.cmake b/py/mkrules.cmake index 7622cd903a88..743302aea98e 100644 --- a/py/mkrules.cmake +++ b/py/mkrules.cmake @@ -218,10 +218,27 @@ if(MICROPY_FROZEN_MANIFEST) set(MICROPY_CROSS_FLAGS "-f${MICROPY_CROSS_FLAGS}") endif() + # Set default path variables to be passed to makemanifest.py. These will + # be available in path substitutions. Additional variables can be set + # per-board in mpconfigboard.cmake. + set(MICROPY_MANIFEST_PORT_DIR ${MICROPY_PORT_DIR}) + set(MICROPY_MANIFEST_BOARD_DIR ${MICROPY_BOARD_DIR}) + set(MICROPY_MANIFEST_MPY_DIR ${MICROPY_DIR}) + set(MICROPY_MANIFEST_MPY_LIB_DIR ${MICROPY_LIB_DIR}) + + # Find all MICROPY_MANIFEST_* variables and turn them into command line arguments. + get_cmake_property(_manifest_vars VARIABLES) + list(FILTER _manifest_vars INCLUDE REGEX "MICROPY_MANIFEST_.*") + foreach(_manifest_var IN LISTS _manifest_vars) + list(APPEND _manifest_var_args "-v") + string(REGEX REPLACE "MICROPY_MANIFEST_(.*)" "\\1" _manifest_var_name ${_manifest_var}) + list(APPEND _manifest_var_args "${_manifest_var_name}=${${_manifest_var}}") + endforeach() + add_custom_target( BUILD_FROZEN_CONTENT ALL BYPRODUCTS ${MICROPY_FROZEN_CONTENT} - COMMAND ${Python3_EXECUTABLE} ${MICROPY_DIR}/tools/makemanifest.py -o ${MICROPY_FROZEN_CONTENT} -v "MPY_DIR=${MICROPY_DIR}" -v "MPY_LIB_DIR=${MICROPY_LIB_DIR}" -v "PORT_DIR=${MICROPY_PORT_DIR}" -v "BOARD_DIR=${MICROPY_BOARD_DIR}" -b "${CMAKE_BINARY_DIR}" ${MICROPY_CROSS_FLAGS} --mpy-tool-flags=${MICROPY_MPY_TOOL_FLAGS} ${MICROPY_FROZEN_MANIFEST} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_DIR}/tools/makemanifest.py -o ${MICROPY_FROZEN_CONTENT} ${_manifest_var_args} -b "${CMAKE_BINARY_DIR}" ${MICROPY_CROSS_FLAGS} --mpy-tool-flags=${MICROPY_MPY_TOOL_FLAGS} ${MICROPY_FROZEN_MANIFEST} DEPENDS ${MICROPY_QSTRDEFS_GENERATED} ${MICROPY_ROOT_POINTERS} diff --git a/py/mkrules.mk b/py/mkrules.mk index 507655932834..7ecb2a5f66b3 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -199,10 +199,21 @@ CFLAGS += -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool CFLAGS += -DMICROPY_MODULE_FROZEN_MPY CFLAGS += -DMICROPY_MODULE_FROZEN_STR +# Set default path variables to be passed to makemanifest.py. These will be +# available in path substitutions. Additional variables can be set per-board +# in mpconfigboard.mk or on the make command line. +MICROPY_MANIFEST_MPY_LIB_DIR = $(MPY_LIB_DIR) +MICROPY_MANIFEST_PORT_DIR = $(shell pwd) +MICROPY_MANIFEST_BOARD_DIR = $(BOARD_DIR) +MICROPY_MANIFEST_MPY_DIR = $(TOP) + +# Find all MICROPY_MANIFEST_* variables and turn them into command line arguments. +MANIFEST_VARIABLES = $(foreach var,$(filter MICROPY_MANIFEST_%, $(.VARIABLES)),-v "$(subst MICROPY_MANIFEST_,,$(var))=$($(var))") + # to build frozen_content.c from a manifest $(BUILD)/frozen_content.c: FORCE $(BUILD)/genhdr/qstrdefs.generated.h $(BUILD)/genhdr/root_pointers.h | $(MICROPY_MPYCROSS_DEPENDENCY) $(Q)test -e "$(MPY_LIB_DIR)/README.md" || (echo -e $(HELP_MPY_LIB_SUBMODULE); false) - $(Q)$(MAKE_MANIFEST) -o $@ -v "MPY_DIR=$(TOP)" -v "MPY_LIB_DIR=$(MPY_LIB_DIR)" -v "PORT_DIR=$(shell pwd)" -v "BOARD_DIR=$(BOARD_DIR)" -b "$(BUILD)" $(if $(MPY_CROSS_FLAGS),-f"$(MPY_CROSS_FLAGS)",) --mpy-tool-flags="$(MPY_TOOL_FLAGS)" $(FROZEN_MANIFEST) + $(Q)$(MAKE_MANIFEST) -o $@ $(MANIFEST_VARIABLES) -b "$(BUILD)" $(if $(MPY_CROSS_FLAGS),-f"$(MPY_CROSS_FLAGS)",) --mpy-tool-flags="$(MPY_TOOL_FLAGS)" $(FROZEN_MANIFEST) endif ifneq ($(PROG),) diff --git a/py/modbuiltins.c b/py/modbuiltins.c index da29e5b67da8..4ff7d445097d 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -137,30 +137,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_callable_obj, mp_builtin_callable); STATIC mp_obj_t mp_builtin_chr(mp_obj_t o_in) { #if MICROPY_PY_BUILTINS_STR_UNICODE mp_uint_t c = mp_obj_get_int(o_in); - uint8_t str[4]; - int len = 0; - if (c < 0x80) { - *str = c; - len = 1; - } else if (c < 0x800) { - str[0] = (c >> 6) | 0xC0; - str[1] = (c & 0x3F) | 0x80; - len = 2; - } else if (c < 0x10000) { - str[0] = (c >> 12) | 0xE0; - str[1] = ((c >> 6) & 0x3F) | 0x80; - str[2] = (c & 0x3F) | 0x80; - len = 3; - } else if (c < 0x110000) { - str[0] = (c >> 18) | 0xF0; - str[1] = ((c >> 12) & 0x3F) | 0x80; - str[2] = ((c >> 6) & 0x3F) | 0x80; - str[3] = (c & 0x3F) | 0x80; - len = 4; - } else { + if (c >= 0x110000) { mp_raise_ValueError(MP_ERROR_TEXT("chr() arg not in range(0x110000)")); } - return mp_obj_new_str_via_qstr((char *)str, len); + VSTR_FIXED(buf, 4); + vstr_add_char(&buf, c); + return mp_obj_new_str_via_qstr(buf.buf, buf.len); #else mp_int_t ord = mp_obj_get_int(o_in); if (0 <= ord && ord <= 0xff) { diff --git a/py/obj.h b/py/obj.h index 56ae62172fce..c7b7db0c3834 100644 --- a/py/obj.h +++ b/py/obj.h @@ -928,10 +928,10 @@ void *mp_obj_malloc_helper(size_t num_bytes, const mp_obj_type_t *type); // optimizations (other tricks like using ({ expr; exper; }) or (exp, expr, expr) in mp_obj_is_type() result // in missed optimizations) #define mp_type_assert_not_bool_int_str_nonetype(t) ( \ - MP_STATIC_ASSERT_NOT_MSC((t) != &mp_type_bool), assert((t) != &mp_type_bool), \ - MP_STATIC_ASSERT_NOT_MSC((t) != &mp_type_int), assert((t) != &mp_type_int), \ - MP_STATIC_ASSERT_NOT_MSC((t) != &mp_type_str), assert((t) != &mp_type_str), \ - MP_STATIC_ASSERT_NOT_MSC((t) != &mp_type_NoneType), assert((t) != &mp_type_NoneType), \ + MP_STATIC_ASSERT_NONCONSTEXPR((t) != &mp_type_bool), assert((t) != &mp_type_bool), \ + MP_STATIC_ASSERT_NONCONSTEXPR((t) != &mp_type_int), assert((t) != &mp_type_int), \ + MP_STATIC_ASSERT_NONCONSTEXPR((t) != &mp_type_str), assert((t) != &mp_type_str), \ + MP_STATIC_ASSERT_NONCONSTEXPR((t) != &mp_type_NoneType), assert((t) != &mp_type_NoneType), \ 1) #define mp_obj_is_type(o, t) (mp_type_assert_not_bool_int_str_nonetype(t) && mp_obj_is_exact_type(o, t)) diff --git a/py/objslice.c b/py/objslice.c index 75fa3bc3f55e..dcd6af8b2856 100644 --- a/py/objslice.c +++ b/py/objslice.c @@ -54,7 +54,7 @@ STATIC mp_obj_t slice_unary_op(mp_unary_op_t op, mp_obj_t o_in) { #if MICROPY_PY_BUILTINS_SLICE_INDICES STATIC mp_obj_t slice_indices(mp_obj_t self_in, mp_obj_t length_obj) { - mp_int_t length = mp_obj_int_get_checked(length_obj); + mp_int_t length = mp_obj_get_int(length_obj); mp_bound_slice_t bound_indices; mp_obj_slice_indices(self_in, length, &bound_indices); diff --git a/shared/runtime/softtimer.c b/shared/runtime/softtimer.c index 267610b2694d..468fa95b7208 100644 --- a/shared/runtime/softtimer.c +++ b/shared/runtime/softtimer.c @@ -30,42 +30,43 @@ #include "py/runtime.h" #include "softtimer.h" +#ifdef MICROPY_SOFT_TIMER_TICKS_MS + extern __IO uint32_t MICROPY_SOFT_TIMER_TICKS_MS; volatile uint32_t soft_timer_next; +static inline uint32_t soft_timer_get_ms(void) { + return MICROPY_SOFT_TIMER_TICKS_MS; +} + +static void soft_timer_schedule_at_ms(uint32_t ticks_ms) { + uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + uint32_t uw_tick = MICROPY_SOFT_TIMER_TICKS_MS; + if (soft_timer_ticks_diff(ticks_ms, uw_tick) <= 0) { + soft_timer_next = uw_tick + 1; + } else { + soft_timer_next = ticks_ms; + } + MICROPY_END_ATOMIC_SECTION(atomic_state); +} + +#endif + // Pointer to the pairheap of soft timer objects. // This may contain bss/data pointers as well as GC-heap pointers, // and is explicitly GC traced by soft_timer_gc_mark_all(). STATIC soft_timer_entry_t *soft_timer_heap; -static inline int32_t ticks_diff(uint32_t t1, uint32_t t0) { - // t1 is after t0 (i.e. positive result) if there exists a uint32_t X <= INT_MAX - // such that t0 + X = t1. Otherwise t1 is interpreted to be earlier than - // t0 (negative result). - return t1 - t0; -} - STATIC int soft_timer_lt(mp_pairheap_t *n1, mp_pairheap_t *n2) { soft_timer_entry_t *e1 = (soft_timer_entry_t *)n1; soft_timer_entry_t *e2 = (soft_timer_entry_t *)n2; - return ticks_diff(e1->expiry_ms, e2->expiry_ms) < 0; -} - -STATIC void soft_timer_schedule_systick(uint32_t ticks_ms) { - uint32_t irq_state = disable_irq(); - uint32_t uw_tick = MICROPY_SOFT_TIMER_TICKS_MS; - if (ticks_diff(ticks_ms, uw_tick) <= 0) { - soft_timer_next = uw_tick + 1; - } else { - soft_timer_next = ticks_ms; - } - enable_irq(irq_state); + return soft_timer_ticks_diff(e1->expiry_ms, e2->expiry_ms) < 0; } void soft_timer_deinit(void) { // Pop off all the nodes which are allocated on the GC-heap. - uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV); + MICROPY_PY_PENDSV_ENTER; soft_timer_entry_t *heap_from = soft_timer_heap; soft_timer_entry_t *heap_to = (soft_timer_entry_t *)mp_pairheap_new(soft_timer_lt); while (heap_from != NULL) { @@ -76,14 +77,14 @@ void soft_timer_deinit(void) { } } soft_timer_heap = heap_to; - restore_irq_pri(irq_state); + MICROPY_PY_PENDSV_EXIT; } // Must be executed at IRQ_PRI_PENDSV void soft_timer_handler(void) { - uint32_t ticks_ms = MICROPY_SOFT_TIMER_TICKS_MS; + uint32_t ticks_ms = soft_timer_get_ms(); soft_timer_entry_t *heap = soft_timer_heap; - while (heap != NULL && ticks_diff(heap->expiry_ms, ticks_ms) <= 0) { + while (heap != NULL && soft_timer_ticks_diff(heap->expiry_ms, ticks_ms) <= 0) { soft_timer_entry_t *entry = heap; heap = (soft_timer_entry_t *)mp_pairheap_pop(soft_timer_lt, &heap->pairheap); if (entry->flags & SOFT_TIMER_FLAG_PY_CALLBACK) { @@ -97,19 +98,17 @@ void soft_timer_handler(void) { } } soft_timer_heap = heap; - if (heap == NULL) { - // No more timers left, set largest delay possible - soft_timer_next = MICROPY_SOFT_TIMER_TICKS_MS; - } else { - // Set soft_timer_next so SysTick calls us back at the correct time - soft_timer_schedule_systick(heap->expiry_ms); + + // Schedule the port's timer to call us back at the correct time. + if (heap != NULL) { + soft_timer_schedule_at_ms(heap->expiry_ms); } } void soft_timer_gc_mark_all(void) { // Mark all soft timer nodes that are allocated on the GC-heap. // To avoid deep C recursion, pop and recreate the pairheap as nodes are marked. - uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV); + MICROPY_PY_PENDSV_ENTER; soft_timer_entry_t *heap_from = soft_timer_heap; soft_timer_entry_t *heap_to = (soft_timer_entry_t *)mp_pairheap_new(soft_timer_lt); while (heap_from != NULL) { @@ -121,7 +120,7 @@ void soft_timer_gc_mark_all(void) { heap_to = (soft_timer_entry_t *)mp_pairheap_push(soft_timer_lt, &heap_to->pairheap, &entry->pairheap); } soft_timer_heap = heap_to; - restore_irq_pri(irq_state); + MICROPY_PY_PENDSV_EXIT; } void soft_timer_static_init(soft_timer_entry_t *entry, uint16_t mode, uint32_t delta_ms, void (*cb)(soft_timer_entry_t *)) { @@ -134,18 +133,18 @@ void soft_timer_static_init(soft_timer_entry_t *entry, uint16_t mode, uint32_t d void soft_timer_insert(soft_timer_entry_t *entry, uint32_t initial_delta_ms) { mp_pairheap_init_node(soft_timer_lt, &entry->pairheap); - entry->expiry_ms = MICROPY_SOFT_TIMER_TICKS_MS + initial_delta_ms; - uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV); + entry->expiry_ms = soft_timer_get_ms() + initial_delta_ms; + MICROPY_PY_PENDSV_ENTER; soft_timer_heap = (soft_timer_entry_t *)mp_pairheap_push(soft_timer_lt, &soft_timer_heap->pairheap, &entry->pairheap); if (entry == soft_timer_heap) { - // This new timer became the earliest one so set soft_timer_next - soft_timer_schedule_systick(entry->expiry_ms); + // This new timer became the earliest one so schedule a callback. + soft_timer_schedule_at_ms(entry->expiry_ms); } - restore_irq_pri(irq_state); + MICROPY_PY_PENDSV_EXIT; } void soft_timer_remove(soft_timer_entry_t *entry) { - uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV); + MICROPY_PY_PENDSV_ENTER; soft_timer_heap = (soft_timer_entry_t *)mp_pairheap_delete(soft_timer_lt, &soft_timer_heap->pairheap, &entry->pairheap); - restore_irq_pri(irq_state); + MICROPY_PY_PENDSV_EXIT; } diff --git a/shared/runtime/softtimer.h b/shared/runtime/softtimer.h index 8603ea13f070..fe5d02b90787 100644 --- a/shared/runtime/softtimer.h +++ b/shared/runtime/softtimer.h @@ -48,6 +48,13 @@ typedef struct _soft_timer_entry_t { extern volatile uint32_t soft_timer_next; +static inline int32_t soft_timer_ticks_diff(uint32_t t1, uint32_t t0) { + // t1 is after t0 (i.e. positive result) if there exists a uint32_t X <= INT_MAX + // such that t0 + X = t1. Otherwise t1 is interpreted to be earlier than + // t0 (negative result). + return t1 - t0; +} + void soft_timer_deinit(void); void soft_timer_handler(void); void soft_timer_gc_mark_all(void); @@ -63,4 +70,17 @@ static inline void soft_timer_reinsert(soft_timer_entry_t *entry, uint32_t initi soft_timer_insert(entry, initial_delta_ms); } +#if !defined(MICROPY_SOFT_TIMER_TICKS_MS) +// IF MICROPY_SOFT_TIMER_TICKS_MS is not defined then the port must provide the +// following timer functions: +// - soft_timer_get_ms() must return a 32-bit millisecond counter that wraps around. +// - soft_timer_schedule_at_ms(ticks_ms) must schedule a callback of soft_timer_handler() +// when the above millisecond counter reaches the given ticks_ms value. If ticks_ms +// is behind the current counter (using int32_t arithmetic) then the callback should +// be scheduled immediately. The callback of soft_timer_handler() should be made at +// pend-SV IRQ level, or equivalent. +uint32_t soft_timer_get_ms(void); +void soft_timer_schedule_at_ms(uint32_t ticks_ms); +#endif + #endif // MICROPY_INCLUDED_SHARED_RUNTIME_SOFTTIMER_H diff --git a/shared/tinyusb/mp_usbd.c b/shared/tinyusb/mp_usbd.c index 55af3d4fb479..74b3f074927b 100644 --- a/shared/tinyusb/mp_usbd.c +++ b/shared/tinyusb/mp_usbd.c @@ -62,4 +62,14 @@ static void mp_usbd_task_callback(mp_sched_node_t *node) { mp_usbd_task(); } +void mp_usbd_hex_str(char *out_str, const uint8_t *bytes, size_t bytes_len) { + size_t hex_len = bytes_len * 2; + for (int i = 0; i < hex_len; i += 2) { + static const char *hexdig = "0123456789abcdef"; + out_str[i] = hexdig[bytes[i / 2] >> 4]; + out_str[i + 1] = hexdig[bytes[i / 2] & 0x0f]; + } + out_str[hex_len] = 0; +} + #endif diff --git a/shared/tinyusb/mp_usbd.h b/shared/tinyusb/mp_usbd.h index 340637c95f9c..83a8f8617c08 100644 --- a/shared/tinyusb/mp_usbd.h +++ b/shared/tinyusb/mp_usbd.h @@ -36,4 +36,9 @@ void mp_usbd_task(void); // 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); +// Most ports need to write a hexadecimal serial number from a byte array, this +// is a helper function for this. out_str must be long enough to hold a string of total +// length (2 * bytes_len + 1) (including NUL terminator). +void mp_usbd_hex_str(char *out_str, const uint8_t *bytes, size_t bytes_len); + #endif // MICROPY_INCLUDED_SHARED_TINYUSB_USBD_H diff --git a/tests/basics/slice_indices.py b/tests/basics/slice_indices.py index b7f439ccca26..ccd7667e9b36 100644 --- a/tests/basics/slice_indices.py +++ b/tests/basics/slice_indices.py @@ -25,3 +25,8 @@ def __getitem__(self, idx): print(A()[2:7:-2].indices(5)) print(A()[7:2:2].indices(5)) print(A()[7:2:-2].indices(5)) + +try: + print(A()[::].indices(None)) +except TypeError: + print("TypeError") diff --git a/tests/net_hosted/asyncio_loopback.py b/tests/net_hosted/asyncio_loopback.py new file mode 100644 index 000000000000..fd4674544ce7 --- /dev/null +++ b/tests/net_hosted/asyncio_loopback.py @@ -0,0 +1,64 @@ +# Test network loopback behaviour + +try: + import asyncio +except ImportError: + print("SKIP") + raise SystemExit + + +async def client(host, port): + print(f"client open_connection to {host}:{port}") + reader, writer = await asyncio.open_connection(host, port) + + data_in = b"A" * 100 + + print("client writing") + writer.write(data_in) + await writer.drain() + + await asyncio.sleep(0.1) + + print("client reading") + data = await reader.readexactly(100) + print(f"client got {len(data)} bytes") + + assert data_in == data + + print("client closing") + + writer.close() + await writer.wait_closed() + + print("client closed") + + +async def echo_handler(reader, writer): + print("handler reading") + await asyncio.sleep(0.1) + data = await reader.readexactly(100) + print(f"handler got {len(data)} bytes") + + print("handler writing") + writer.write(data) + await writer.drain() + + print("handler closing") + + writer.close() + await writer.wait_closed() + + print("handler closed") + + +async def test(host, port): + print(f"create server on {host}:{port}") + server = await asyncio.start_server(echo_handler, host, port) + + async with server: + print("server started") + await client("127.0.0.1", 8080) + print("server closed") + + +asyncio.run(test("0.0.0.0", 8080)) diff --git a/tests/net_hosted/asyncio_loopback.py.exp b/tests/net_hosted/asyncio_loopback.py.exp new file mode 100644 index 000000000000..7110b35e12ca --- /dev/null +++ b/tests/net_hosted/asyncio_loopback.py.exp @@ -0,0 +1,14 @@ +create server on 0.0.0.0:8080 +server started +client open_connection to 127.0.0.1:8080 +client writing +handler reading +client reading +handler got 100 bytes +handler writing +handler closing +handler closed +client got 100 bytes +client closing +client closed +server closed