summaryrefslogtreecommitdiffstatshomepage
path: root/ports
diff options
context:
space:
mode:
Diffstat (limited to 'ports')
-rw-r--r--ports/alif/Makefile15
-rw-r--r--ports/alif/README.md58
-rw-r--r--ports/alif/alif.mk5
-rw-r--r--ports/alif/boards/ALIF_ENSEMBLE/board.json17
-rw-r--r--ports/alif/boards/ALIF_ENSEMBLE/deploy.md37
-rw-r--r--ports/alif/cyw43_configport.h54
-rw-r--r--ports/alif/irq.h1
-rw-r--r--ports/alif/machine_pin.c197
-rw-r--r--ports/alif/machine_spi.c60
-rw-r--r--ports/alif/main.c4
-rw-r--r--ports/alif/modmachine.c6
-rw-r--r--ports/alif/mpconfigport.h8
-rw-r--r--ports/alif/mphalport.h3
-rw-r--r--ports/cc3200/misc/mperror.c2
-rw-r--r--ports/cc3200/misc/mperror.h2
-rw-r--r--ports/cc3200/mods/modmachine.c4
-rw-r--r--ports/cc3200/mods/modtime.c2
-rw-r--r--ports/cc3200/mods/pybsleep.c4
-rw-r--r--ports/esp32/README.md32
-rw-r--r--ports/esp32/adc.c94
-rw-r--r--ports/esp32/adc.h47
-rw-r--r--ports/esp32/boards/ARDUINO_NANO_ESP32/board.json4
-rw-r--r--ports/esp32/boards/ESP32_GENERIC/board.md2
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_C3/board.md4
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S2/board.md2
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/board.json3
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/board.md4
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake4
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board6
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m4
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json23
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake7
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h10
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv13
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board4
-rw-r--r--ports/esp32/boards/LILYGO_TTGO_LORA32/board.json4
-rw-r--r--ports/esp32/boards/M5STACK_ATOM/board.json6
-rw-r--r--ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json2
-rw-r--r--ports/esp32/boards/M5STACK_NANOC6/board.json2
-rw-r--r--ports/esp32/boards/OLIMEX_ESP32_EVB/board.json6
-rw-r--r--ports/esp32/boards/OLIMEX_ESP32_POE/board.json6
-rw-r--r--ports/esp32/boards/SIL_WESP32/board.json2
-rw-r--r--ports/esp32/boards/SIL_WESP32/sdkconfig.board9
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json23
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py2
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake7
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h14
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv22
-rw-r--r--ports/esp32/boards/UM_FEATHERS2/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_FEATHERS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_NANOS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_OMGS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_PROS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_TINYC6/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_TINYS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/sdkconfig.base4
-rw-r--r--ports/esp32/esp32_common.cmake19
-rw-r--r--ports/esp32/machine_adc.c95
-rw-r--r--ports/esp32/machine_adc_block.c14
-rw-r--r--ports/esp32/machine_i2c.c5
-rw-r--r--ports/esp32/machine_pin.c6
-rw-r--r--ports/esp32/machine_pwm.c974
-rw-r--r--ports/esp32/machine_rtc.c4
-rw-r--r--ports/esp32/machine_rtc.h12
-rw-r--r--ports/esp32/machine_timer.c49
-rw-r--r--ports/esp32/machine_timer.h3
-rw-r--r--ports/esp32/machine_uart.c74
-rw-r--r--ports/esp32/main.c34
-rw-r--r--ports/esp32/main/idf_component.yml5
-rw-r--r--ports/esp32/modesp32.c70
-rw-r--r--ports/esp32/modmachine.c8
-rw-r--r--ports/esp32/modnetwork.h31
-rw-r--r--ports/esp32/modnetwork_globals.h8
-rw-r--r--ports/esp32/modsocket.c31
-rw-r--r--ports/esp32/modtime.c2
-rw-r--r--ports/esp32/mpconfigport.h9
-rw-r--r--ports/esp32/mpnimbleport.c4
-rw-r--r--ports/esp32/mpthreadport.c96
-rw-r--r--ports/esp32/network_common.c4
-rw-r--r--ports/esp32/network_lan.c20
-rw-r--r--ports/esp32/network_ppp.c511
-rw-r--r--ports/esp32/panichandler.c11
-rw-r--r--ports/esp32/partitions-16MiB-ota.csv10
-rw-r--r--ports/esp32/partitions-16MiB.csv7
-rw-r--r--ports/esp32/partitions-2MiB.csv1
-rw-r--r--ports/esp32/partitions-32MiB.csv7
-rw-r--r--ports/esp32/partitions-4MiB-ota.csv1
-rw-r--r--ports/esp32/partitions-4MiB-romfs.csv1
-rw-r--r--ports/esp32/partitions-4MiBplus.csv (renamed from ports/esp32/partitions-4MiB.csv)5
-rw-r--r--ports/esp32/partitions-8MiB.csv7
-rw-r--r--ports/esp32/partitions-8MiBplus-ota.csv (renamed from ports/esp32/partitions-32MiB-ota.csv)6
-rwxr-xr-xports/esp32/tools/metrics_esp32.py2
-rw-r--r--ports/esp8266/esp_init_data.c2
-rw-r--r--ports/esp8266/esp_mphal.h9
-rw-r--r--ports/esp8266/main.c62
-rw-r--r--ports/esp8266/modmachine.c34
-rw-r--r--ports/esp8266/modtime.c4
-rw-r--r--ports/esp8266/mpconfigport.h3
-rw-r--r--ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json2
-rw-r--r--ports/mimxrt/boards/OLIMEX_RT1010/board.json6
-rw-r--r--ports/mimxrt/cyw43_configport.h50
-rw-r--r--ports/mimxrt/modmachine.c6
-rw-r--r--ports/mimxrt/modtime.c9
-rw-r--r--ports/mimxrt/mpconfigport.h8
-rw-r--r--ports/minimal/main.c2
-rw-r--r--ports/nrf/Makefile8
-rw-r--r--ports/nrf/boards/ACTINIUS_ICARUS/board.json2
-rw-r--r--ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json2
-rw-r--r--ports/nrf/boards/ARDUINO_PRIMO/board.json4
-rw-r--r--ports/nrf/boards/BLUEIO_TAG_EVIM/board.json2
-rw-r--r--ports/nrf/boards/DVK_BL652/board.json6
-rw-r--r--ports/nrf/boards/EVK_NINA_B1/board.json2
-rw-r--r--ports/nrf/boards/EVK_NINA_B3/board.json2
-rw-r--r--ports/nrf/boards/IBK_BLYST_NANO/board.json2
-rw-r--r--ports/nrf/boards/IDK_BLYST_NANO/board.json2
-rw-r--r--ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json2
-rw-r--r--ports/nrf/boards/PCA10028/mpconfigboard.h5
-rw-r--r--ports/nrf/boards/PCA10040/mpconfigboard.h5
-rw-r--r--ports/nrf/boards/SEEED_XIAO_NRF52/board.json4
-rw-r--r--ports/nrf/boards/WT51822_S4AT/board.json4
-rw-r--r--ports/nrf/main.c22
-rw-r--r--ports/nrf/modules/machine/modmachine.c29
-rw-r--r--ports/nrf/modules/machine/uart.c57
-rw-r--r--ports/nrf/modules/machine/uart.h3
-rw-r--r--ports/nrf/modules/os/modos.c14
-rw-r--r--ports/nrf/mpconfigport.h13
-rw-r--r--ports/nrf/mphalport.c2
-rw-r--r--ports/nrf/mphalport.h18
-rw-r--r--ports/pic16bit/main.c2
-rw-r--r--ports/powerpc/main.c2
-rw-r--r--ports/qemu/mcu/arm/errorhandler.c20
-rw-r--r--ports/qemu/mcu/arm/startup.c2
-rw-r--r--ports/renesas-ra/Makefile3
-rw-r--r--ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json2
-rw-r--r--ports/renesas-ra/machine_uart.c2
-rw-r--r--ports/renesas-ra/main.c2
-rw-r--r--ports/renesas-ra/modmachine.c6
-rw-r--r--ports/renesas-ra/modtime.c2
-rw-r--r--ports/renesas-ra/mpconfigport.h21
-rw-r--r--ports/renesas-ra/mphalport.c4
-rw-r--r--ports/renesas-ra/mphalport.h10
-rw-r--r--ports/renesas-ra/powerctrl.c6
-rw-r--r--ports/renesas-ra/powerctrl.h6
-rw-r--r--ports/renesas-ra/systick.c10
-rw-r--r--ports/renesas-ra/uart.c6
-rw-r--r--ports/rp2/CMakeLists.txt38
-rw-r--r--ports/rp2/Makefile3
-rw-r--r--ports/rp2/README.md6
-rw-r--r--ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json2
-rw-r--r--ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json2
-rw-r--r--ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json2
-rw-r--r--ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json2
-rw-r--r--ports/rp2/boards/SPARKFUN_PROMICRO/board.json2
-rw-r--r--ports/rp2/boards/SPARKFUN_THINGPLUS/board.json2
-rw-r--r--ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h4
-rw-r--r--ports/rp2/boards/W5100S_EVB_PICO/board.json6
-rw-r--r--ports/rp2/boards/W5500_EVB_PICO/board.json4
-rw-r--r--ports/rp2/boards/WEACTSTUDIO/board.json2
-rw-r--r--ports/rp2/cyw43_configport.h108
-rw-r--r--ports/rp2/machine_pin.c25
-rw-r--r--ports/rp2/machine_timer.c30
-rw-r--r--ports/rp2/memmap_mp_rp2040.ld2
-rw-r--r--ports/rp2/memmap_mp_rp2350.ld2
-rw-r--r--ports/rp2/modmachine.c88
-rw-r--r--ports/rp2/mpconfigport.h38
-rw-r--r--ports/rp2/mphalport.c12
-rw-r--r--ports/rp2/mphalport.h17
-rw-r--r--ports/rp2/mpnetworkport.c39
-rw-r--r--ports/rp2/rp2_dma.c2
-rw-r--r--ports/rp2/rp2_flash.c18
-rw-r--r--ports/rp2/tools_patch/Findpioasm.cmake58
-rw-r--r--ports/samd/Makefile2
-rw-r--r--ports/samd/boards/MINISAM_M4/mpconfigboard.h2
-rw-r--r--ports/samd/boards/SAMD_GENERIC_D21X18/board.json1
-rw-r--r--ports/samd/boards/SAMD_GENERIC_D51X19/board.json1
-rw-r--r--ports/samd/boards/SAMD_GENERIC_D51X20/board.json1
-rw-r--r--ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk6
-rw-r--r--ports/samd/boards/SEEED_XIAO_SAMD21/board.json2
-rw-r--r--ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json21
-rw-r--r--ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h11
-rw-r--r--ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk8
-rw-r--r--ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv47
-rw-r--r--ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json19
-rw-r--r--ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h8
-rw-r--r--ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk4
-rw-r--r--ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv45
-rw-r--r--ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json4
-rw-r--r--ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h2
-rw-r--r--ports/samd/machine_i2c.c35
-rw-r--r--ports/samd/machine_rtc.c1
-rw-r--r--ports/samd/main.c2
-rw-r--r--ports/samd/mcu/samd51/mpconfigmcu.h2
-rw-r--r--ports/samd/modmachine.c8
-rw-r--r--ports/samd/modtime.c14
-rw-r--r--ports/samd/mphalport.h5
-rw-r--r--ports/samd/samd_qspiflash.c32
-rw-r--r--ports/samd/samd_spiflash.c65
-rw-r--r--ports/stm32/Makefile64
-rw-r--r--ports/stm32/adc.c93
-rw-r--r--ports/stm32/adc.h2
-rw-r--r--ports/stm32/boardctrl.c2
-rw-r--r--ports/stm32/boardctrl.h3
-rw-r--r--ports/stm32/boards/ARDUINO_GIGA/board.json2
-rw-r--r--ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h1
-rw-r--r--ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld25
-rw-r--r--ports/stm32/boards/ARDUINO_NICLA_VISION/board.json2
-rw-r--r--ports/stm32/boards/ARDUINO_OPTA/board.json2
-rw-r--r--ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json2
-rw-r--r--ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h1
-rw-r--r--ports/stm32/boards/HYDRABUS/board.json2
-rw-r--r--ports/stm32/boards/LIMIFROG/board.json2
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/bdev.c42
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/board.c122
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/board.md17
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h103
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk26
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/partition_stm32n657xx.h5
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/pins.csv62
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/stm32n6xx_hal_conf.h18
-rw-r--r--ports/stm32/boards/OLIMEX_E407/board.json6
-rw-r--r--ports/stm32/boards/OLIMEX_H407/board.json6
-rw-r--r--ports/stm32/boards/OPENMV_N6/bdev.c41
-rw-r--r--ports/stm32/boards/OPENMV_N6/board.c131
-rw-r--r--ports/stm32/boards/OPENMV_N6/board.ld39
-rw-r--r--ports/stm32/boards/OPENMV_N6/manifest.py3
-rw-r--r--ports/stm32/boards/OPENMV_N6/mpconfigboard.h167
-rw-r--r--ports/stm32/boards/OPENMV_N6/mpconfigboard.mk30
-rw-r--r--ports/stm32/boards/OPENMV_N6/partition_stm32n657xx.h5
-rw-r--r--ports/stm32/boards/OPENMV_N6/pins.csv142
-rw-r--r--ports/stm32/boards/OPENMV_N6/stm32n6xx_hal_conf.h18
-rw-r--r--ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json6
-rw-r--r--ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h2
-rw-r--r--ports/stm32/boards/common_n6_flash.ld57
-rw-r--r--ports/stm32/boards/common_text.ld8
-rwxr-xr-xports/stm32/boards/make-pins.py2
-rw-r--r--ports/stm32/boards/pllvalues.py2
-rw-r--r--ports/stm32/boards/stm32g474_af.csv58
-rw-r--r--ports/stm32/boards/stm32h7xx_hal_conf_base.h1
-rw-r--r--ports/stm32/boards/stm32n657_af.csv42
-rw-r--r--ports/stm32/boards/stm32n657x0.ld34
-rw-r--r--ports/stm32/boards/stm32n6xx_hal_conf_base.h215
-rw-r--r--ports/stm32/cyw43_configport.h67
-rw-r--r--ports/stm32/dma.c91
-rw-r--r--ports/stm32/dma.h13
-rw-r--r--ports/stm32/extint.c39
-rw-r--r--ports/stm32/extint.h4
-rw-r--r--ports/stm32/flash.c4
-rw-r--r--ports/stm32/i2cslave.h10
-rw-r--r--ports/stm32/irq.h5
-rw-r--r--ports/stm32/lwip_inc/lwipopts.h9
-rw-r--r--ports/stm32/machine_adc.c79
-rw-r--r--ports/stm32/machine_uart.c2
-rw-r--r--ports/stm32/main.c68
-rwxr-xr-xports/stm32/mboot/Makefile43
-rw-r--r--ports/stm32/mboot/adc.c2
-rw-r--r--ports/stm32/mboot/main.c41
-rw-r--r--ports/stm32/mboot/mboot.h2
-rw-r--r--ports/stm32/mboot/mphalport.h17
-rw-r--r--ports/stm32/mboot/stm32_memory_n6.ld18
-rw-r--r--ports/stm32/mboot/stm32_sections.ld8
-rw-r--r--ports/stm32/modmachine.c24
-rw-r--r--ports/stm32/modtime.c2
-rw-r--r--ports/stm32/mpconfigboard_common.h31
-rw-r--r--ports/stm32/mpconfigport.h19
-rw-r--r--ports/stm32/mphalport.c4
-rw-r--r--ports/stm32/mphalport.h2
-rw-r--r--ports/stm32/mpu.h22
-rw-r--r--ports/stm32/pin_defs_stm32.h4
-rw-r--r--ports/stm32/powerctrl.c88
-rw-r--r--ports/stm32/powerctrl.h12
-rw-r--r--ports/stm32/powerctrlboot.c126
-rw-r--r--ports/stm32/qspi.c2
-rw-r--r--ports/stm32/resethandler_iram.s82
-rw-r--r--ports/stm32/rtc.c55
-rw-r--r--ports/stm32/sdcard.c8
-rw-r--r--ports/stm32/sdio.c30
-rw-r--r--ports/stm32/spi.c37
-rw-r--r--ports/stm32/spibdev.c39
-rw-r--r--ports/stm32/stm32.mk22
-rw-r--r--ports/stm32/stm32_it.c27
-rw-r--r--ports/stm32/storage.c15
-rw-r--r--ports/stm32/storage.h4
-rw-r--r--ports/stm32/timer.c32
-rw-r--r--ports/stm32/uart.c77
-rw-r--r--ports/stm32/uart.h4
-rw-r--r--ports/stm32/usb.c2
-rw-r--r--ports/stm32/usbd_conf.c35
-rw-r--r--ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h6
-rw-r--r--ports/stm32/vfs_rom_ioctl.c21
-rw-r--r--ports/stm32/xspi.c599
-rw-r--r--ports/stm32/xspi.h43
-rw-r--r--ports/unix/Makefile15
-rw-r--r--ports/unix/README.md18
-rw-r--r--ports/unix/coverage.c90
-rw-r--r--ports/unix/main.c2
-rw-r--r--ports/unix/modsocket.c1
-rw-r--r--ports/unix/modtime.c10
-rw-r--r--ports/unix/mpconfigport.h3
-rw-r--r--ports/unix/variants/coverage/mpconfigvariant.h2
-rw-r--r--ports/webassembly/main.c2
-rw-r--r--ports/webassembly/objpyproxy.js6
-rw-r--r--ports/windows/Makefile4
-rw-r--r--ports/windows/mpconfigport.h7
-rw-r--r--ports/zephyr/CMakeLists.txt22
-rw-r--r--ports/zephyr/Kconfig16
-rw-r--r--ports/zephyr/README.md2
-rw-r--r--ports/zephyr/boards/bbc_microbit_v2.conf1
-rw-r--r--ports/zephyr/boards/beagleconnect_freedom.conf12
-rw-r--r--ports/zephyr/boards/beagleplay_cc1352p7.conf13
-rw-r--r--ports/zephyr/boards/frdm_k64f.conf4
-rw-r--r--ports/zephyr/boards/manifest.py7
-rw-r--r--ports/zephyr/boards/nrf52840dk_nrf52840.conf5
-rw-r--r--ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.conf2
-rw-r--r--ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.overlay16
-rw-r--r--ports/zephyr/boards/nrf9151dk_nrf9151.conf7
-rw-r--r--ports/zephyr/boards/nrf9151dk_nrf9151.overlay22
-rw-r--r--ports/zephyr/boards/nucleo_h743zi.conf3
-rw-r--r--ports/zephyr/boards/nucleo_wb55rg.conf17
-rw-r--r--ports/zephyr/boards/nucleo_wb55rg.overlay23
-rw-r--r--ports/zephyr/boards/qemu_cortex_m3.conf11
-rw-r--r--ports/zephyr/boards/qemu_x86.conf8
-rw-r--r--ports/zephyr/boards/rpi_pico.conf19
-rw-r--r--ports/zephyr/boards/rpi_pico.overlay48
-rw-r--r--ports/zephyr/machine_pin.c37
-rw-r--r--ports/zephyr/machine_timer.c15
-rw-r--r--ports/zephyr/machine_uart.c194
-rw-r--r--ports/zephyr/main.c32
-rw-r--r--ports/zephyr/modbluetooth_zephyr.c616
-rw-r--r--ports/zephyr/modsocket.c2
-rw-r--r--ports/zephyr/mpconfigport.h21
-rw-r--r--ports/zephyr/mpconfigport_minimal.h2
-rw-r--r--ports/zephyr/mphalport.c8
-rw-r--r--ports/zephyr/mphalport.h46
-rw-r--r--ports/zephyr/prj.conf12
-rw-r--r--ports/zephyr/prj_minimal.conf5
-rw-r--r--ports/zephyr/qstrdefsport.h4
-rw-r--r--ports/zephyr/src/usbd.c193
-rw-r--r--ports/zephyr/src/zephyr_getchar.c17
-rw-r--r--ports/zephyr/src/zephyr_getchar.h1
-rw-r--r--ports/zephyr/uart_core.c19
-rw-r--r--ports/zephyr/zephyr_storage.c19
344 files changed, 7562 insertions, 2130 deletions
diff --git a/ports/alif/Makefile b/ports/alif/Makefile
index 6870667436..d258b27b1d 100644
--- a/ports/alif/Makefile
+++ b/ports/alif/Makefile
@@ -21,6 +21,7 @@ Reset\n\
Exit
ALIF_TOC_CONFIG = alif_cfg.json
+ALIF_TOC_BIN = $(BUILD)/firmware.toc.bin
ALIF_TOC_APPS = $(BUILD)/$(ALIF_TOC_CONFIG)
ALIF_TOC_CFLAGS += -DTOC_CFG_FILE=$(ALIF_TOOLKIT_CFG_FILE)
@@ -69,7 +70,7 @@ include $(TOP)/extmod/extmod.mk
# Main targets
.PHONY: all
-all: $(BUILD)/firmware.toc.bin
+all: $(BUILD)/firmware.zip
# Force make commands to run the targets every time
# regardless of whether firmware.toc.bin already exists
@@ -81,24 +82,28 @@ $(BUILD):
$(MKDIR) -p $@
$(BUILD)/M55_HP/firmware.bin:
- make -f alif.mk MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0
+ make -f alif.mk BUILD=$(BUILD)/M55_HP MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0
$(BUILD)/M55_HE/firmware.bin:
- make -f alif.mk MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1
+ make -f alif.mk BUILD=$(BUILD)/M55_HE MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1
$(BUILD)/$(ALIF_TOC_CONFIG): mcu/$(ALIF_TOC_CONFIG).in | $(BUILD)
$(ECHO) "Preprocess toc config $@"
$(Q)$(CPP) -P -E $(ALIF_TOC_CFLAGS) - < mcu/$(ALIF_TOC_CONFIG).in > $@
-$(BUILD)/firmware.toc.bin: $(ALIF_TOC_APPS)
+$(ALIF_TOC_BIN): $(ALIF_TOC_APPS)
$(Q)python $(ALIF_TOOLS)/app-gen-toc.py \
--filename $(abspath $(BUILD)/$(ALIF_TOC_CONFIG)) \
--output-dir $(BUILD) \
--firmware-dir $(BUILD) \
--output $@
+$(BUILD)/firmware.zip: $(ALIF_TOC_BIN) $(ALIF_TOC_APPS)
+ $(ECHO) "Create $@"
+ $(Q)$(ZIP) -q - $(BUILD)/application_package.ds $^ > $@
+
.PHONY: deploy
-deploy: $(BUILD)/firmware.toc.bin
+deploy: $(ALIF_TOC_BIN)
$(ECHO) "Writing $< to the board"
$(Q)python $(ALIF_TOOLS)/app-write-mram.py \
--cfg-part $(ALIF_TOOLKIT_CFG_PART) \
diff --git a/ports/alif/README.md b/ports/alif/README.md
index 824a63da18..dcf327060f 100644
--- a/ports/alif/README.md
+++ b/ports/alif/README.md
@@ -19,3 +19,61 @@ The following more advanced features will follow later:
- Ethernet support.
- SDRAM support.
- Other machine modules.
+
+Build instructions
+------------------
+
+Before building the firmware for a given board the MicroPython cross-compiler
+must be built; it will be used to pre-compile some of the built-in scripts to
+bytecode. The cross-compiler is built and run on the host machine, using:
+```bash
+$ make -C mpy-cross
+```
+
+This command should be executed from the root directory of this repository.
+All other commands below should be executed from the ports/alif/ directory.
+
+An ARM compiler is required for the build, along with the associated binary
+utilities. The recommended toolchain version to use with this port is
+Arm GNU toolchain version 13.3.Rel1. The compiler can be changed using the
+`CROSS_COMPILE` variable when invoking `make`.
+
+Next, the board to build must be selected. The default board is `ALIF_ENSEMBLE`
+but any of the names of the subdirectories in the `boards/` directory is valid.
+The board name must be passed as the argument to `BOARD=` when invoking `make`.
+
+All boards require certain submodules to be obtained before they can be built.
+The correct set of submodules can be initialised using (with `ALIF_ENSEMBLE`
+as an example of the selected board):
+```bash
+make BOARD=ALIF_ENSEMBLE submodules
+```
+
+Then to build the board's firmware run:
+```bash
+make BOARD=ALIF_ENSEMBLE
+```
+
+The above command should produce binary images in the `build-ALIF_ENSEMBLE/`
+subdirectory (or the equivalent directory for the board specified).
+
+### Update the SE Firmware
+
+The SE firmware must be updated **before** flashing the main firmware to match
+the version used by MicroPython. This step only needs to be performed once.
+Connect the board to your PC via the **SE UART USB** port (on the Ensemble kit,
+this is labeled **PRG USB**), then run:
+
+```bash
+make update-system-package
+```
+
+**Note:** The board must be power-cycled after this step.
+
+### Deploy MicroPython
+
+To flash the firmware, run:
+
+```bash
+make BOARD=ALIF_ENSEMBLE deploy
+```
diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk
index eee27b3b7d..265418aa07 100644
--- a/ports/alif/alif.mk
+++ b/ports/alif/alif.mk
@@ -3,7 +3,6 @@
BOARD ?= ALIF_ENSEMBLE
BOARD_DIR ?= boards/$(BOARD)
-BUILD ?= build-$(BOARD)/$(MCU_CORE)
ifeq ($(wildcard $(BOARD_DIR)/.),)
$(error Invalid BOARD specified: $(BOARD_DIR))
@@ -103,10 +102,6 @@ CFLAGS += -Wl,-T$(BUILD)/ensemble.ld \
-Wl,--print-memory-usage \
-Wl,--no-warn-rwx-segment
-ifeq ($(MCU_CORE),M55_HP)
-CFLAGS += -Wl,--wrap=dcd_event_handler
-endif
-
################################################################################
# Source files and libraries
diff --git a/ports/alif/boards/ALIF_ENSEMBLE/board.json b/ports/alif/boards/ALIF_ENSEMBLE/board.json
new file mode 100644
index 0000000000..0c63494f7a
--- /dev/null
+++ b/ports/alif/boards/ALIF_ENSEMBLE/board.json
@@ -0,0 +1,17 @@
+{
+ "deploy": [
+ "./deploy.md"
+ ],
+ "docs": "",
+ "features": [
+ "Ethernet"
+ ],
+ "images": [
+ "ensemble-devkit-gen-2.jpg"
+ ],
+ "mcu": "AE722F80F55D5XX",
+ "product": "Ensemble E7 DevKit",
+ "thumbnail": "",
+ "url": "https://alifsemi.com/support/kits/ensemble-devkit/",
+ "vendor": "Alif Semiconductor"
+}
diff --git a/ports/alif/boards/ALIF_ENSEMBLE/deploy.md b/ports/alif/boards/ALIF_ENSEMBLE/deploy.md
new file mode 100644
index 0000000000..acbc85617e
--- /dev/null
+++ b/ports/alif/boards/ALIF_ENSEMBLE/deploy.md
@@ -0,0 +1,37 @@
+### Alif Security Toolkit
+
+This board can be programmed via the SE UART interface using the Alif Security
+Toolkit. MicroPython uses a custom version of the toolkit, which can be downloaded
+from [here](https://github.com/micropython/alif-security-toolkit/) or cloned:
+
+```bash
+git clone https://github.com/micropython/alif-security-toolkit/
+```
+
+---
+
+### Update the SE Firmware (Optional)
+
+If needed, update the SE firmware to match the version used by MicroPython. Ensure
+you have the correct port, part number, and chip revision.
+
+```bash
+python alif-security-toolkit/toolkit/updateSystemPackage.py --port /dev/ttyACM0 --cfg-part AE722F80F55D5LS --cfg-rev B4
+```
+
+**Note:** The board must be power-cycled after this step.
+
+---
+
+### Deploy MicroPython
+
+Download (or build) the firmware package, unzip it, then deploy it:
+
+```bash
+
+python alif-security-toolkit/toolkit/app-write-mram.py --port /dev/ttyACM0 --cfg-part AE722F80F55D5LS --cfg-rev B4 --pad --images file:build-ALIF_ENSEMBLE/application_package.ds
+```
+
+The MicroPython REPL is available on the second USB serial port, eg `/dev/ttyACM1`.
+
+---
diff --git a/ports/alif/cyw43_configport.h b/ports/alif/cyw43_configport.h
index 4b5cce6713..bfd4364ab9 100644
--- a/ports/alif/cyw43_configport.h
+++ b/ports/alif/cyw43_configport.h
@@ -27,13 +27,8 @@
#define MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H
// The board-level config will be included here, so it can set some CYW43 values.
-#include "py/mpconfig.h"
-#include "py/mperrno.h"
-#include "py/mphal.h"
-#include "py/runtime.h"
-#include "extmod/modnetwork.h"
#include "extmod/mpbthci.h"
-#include "pendsv.h"
+#include "extmod/cyw43_config_common.h"
#ifndef static_assert
#define static_assert(expr, msg) typedef int static_assert_##__LINE__[(expr) ? 1 : -1]
@@ -53,51 +48,15 @@
#define CYW43_WARN(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__)
#endif
-#define CYW43_IOCTL_TIMEOUT_US (1000000)
-#define CYW43_SLEEP_MAX (50)
-#define CYW43_NETUTILS (1)
#define CYW43_CLEAR_SDIO_INT (1)
-#define CYW43_EPERM MP_EPERM // Operation not permitted
-#define CYW43_EIO MP_EIO // I/O error
-#define CYW43_EINVAL MP_EINVAL // Invalid argument
-#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out
-
-#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER
-#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT
-#define CYW43_THREAD_LOCK_CHECK
-
-#define CYW43_HOST_NAME mod_network_hostname_data
-
#define CYW43_SDPCM_SEND_COMMON_WAIT __WFE()
#define CYW43_DO_IOCTL_WAIT // __WFE()
-#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait()
-
-#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a)
-
-#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT
-#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT
-#define CYW43_HAL_PIN_PULL_NONE 0
-
-#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0
-#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR
-
-#define cyw43_hal_ticks_us mp_hal_ticks_us
-#define cyw43_hal_ticks_ms mp_hal_ticks_ms
-
-#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t
-#define cyw43_hal_pin_read mp_hal_pin_read
-#define cyw43_hal_pin_low mp_hal_pin_low
-#define cyw43_hal_pin_high mp_hal_pin_high
#define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate
#define cyw43_hal_uart_write mp_bluetooth_hci_uart_write
#define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar
-#define cyw43_hal_get_mac mp_hal_get_mac
-#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii
-#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac
-
#define CYW43_PIN_WL_REG_ON pin_WL_REG_ON
#define CYW43_PIN_WL_IRQ pin_WL_IRQ
@@ -110,15 +69,7 @@
void cyw43_post_poll_hook(void);
-static inline void cyw43_delay_us(uint32_t us) {
- uint32_t start = mp_hal_ticks_us();
- while (mp_hal_ticks_us() - start < us) {
- }
-}
-
-static inline void cyw43_delay_ms(uint32_t ms) {
- mp_hal_delay_ms(ms);
-}
+#undef cyw43_hal_pin_config // mp_hal_pin_config on alif port is not API compatible
static inline void cyw43_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uint32_t pull, uint32_t alt) {
if (mode == MP_HAL_PIN_MODE_INPUT) {
@@ -127,7 +78,6 @@ static inline void cyw43_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uin
mp_hal_pin_output(pin);
}
}
-
static inline void cyw43_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool enable) {
mp_hal_pin_config_irq_falling(pin, enable);
}
diff --git a/ports/alif/irq.h b/ports/alif/irq.h
index 08b8ef8086..02df524a49 100644
--- a/ports/alif/irq.h
+++ b/ports/alif/irq.h
@@ -48,6 +48,7 @@
#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 7, 0)
#define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0)
#define IRQ_PRI_GPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0)
+#define IRQ_PRI_GPIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 50, 0)
#define IRQ_PRI_RTC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 100, 0)
#define IRQ_PRI_CYW43 NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 126, 0)
#define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0)
diff --git a/ports/alif/machine_pin.c b/ports/alif/machine_pin.c
index 02c5e482ad..b8773f103f 100644
--- a/ports/alif/machine_pin.c
+++ b/ports/alif/machine_pin.c
@@ -30,8 +30,76 @@
#include "extmod/virtpin.h"
#include "shared/runtime/mpirq.h"
+#ifndef MACHINE_PIN_NUM_VECTORS
+#define MACHINE_PIN_NUM_PORT_IO (8)
+#define MACHINE_PIN_NUM_VECTORS (16 * MACHINE_PIN_NUM_PORT_IO)
+#endif
+
+typedef struct _machine_pin_irq_obj_t {
+ mp_irq_obj_t base;
+ uint32_t flags;
+ uint32_t trigger;
+ IRQn_Type irq_num;
+ bool reserved; // for use by other drivers
+} machine_pin_irq_obj_t;
+
+#define MACHINE_PIN_IRQ_INDEX(port, pin) \
+ ((port) * MACHINE_PIN_NUM_PORT_IO + (pin))
+
+#define MACHINE_PIN_IRQ_OBJECT(port, pin) \
+ (MP_STATE_PORT(machine_pin_irq_obj[MACHINE_PIN_IRQ_INDEX((port), (pin))]))
+
+// Defines a single GPIO IRQ handler
+#define DEFINE_GPIO_IRQ_HANDLER(pname, port, pin) \
+ void pname##_IRQ##pin##Handler(void) { \
+ machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(port, pin); \
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(irq->base.parent); \
+ gpio_interrupt_eoi(self->gpio, pin); \
+ irq->flags = irq->trigger; \
+ mp_irq_handler(&irq->base); \
+ }
+
+// Defines all 8 pin IRQ handlers for a port
+#define DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(gpio, port) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 0) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 1) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 2) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 3) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 4) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 5) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 6) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 7)
+
+// Generate handlers for GPIO ports 0 to 14 + LPGPIO
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO0, 0)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO1, 1)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO2, 2)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO3, 3)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO4, 4)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO5, 5)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO6, 6)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO7, 7)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO8, 8)
+
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 0)
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 1)
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 2)
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 3)
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 4)
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 5)
+// DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 6) // Reserved for WiFi
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 7)
+
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO10, 10)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO11, 11)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO12, 12)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO13, 13)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO14, 14)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(LPGPIO, 15)
+
extern const mp_obj_dict_t machine_pin_cpu_pins_locals_dict;
extern const mp_obj_dict_t machine_pin_board_pins_locals_dict;
+static const mp_irq_methods_t machine_pin_irq_methods;
static const machine_pin_obj_t *machine_pin_find_named(const mp_obj_dict_t *named_pins, mp_obj_t name) {
const mp_map_t *named_map = &named_pins->map;
@@ -171,6 +239,7 @@ mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args);
}
+
return MP_OBJ_FROM_PTR(self);
}
@@ -225,6 +294,129 @@ static mp_obj_t machine_pin_toggle(mp_obj_t self_in) {
}
static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle);
+static mp_uint_t machine_pin_irq_trigger(mp_obj_t self_in, mp_uint_t trigger) {
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(self->port, self->pin);
+
+ irq->flags = 0;
+ irq->trigger = trigger;
+
+ // Disable IRQs.
+ gpio_disable_interrupt(self->gpio, self->pin);
+ gpio_mask_interrupt(self->gpio, self->pin);
+
+ NVIC_ClearPendingIRQ(irq->irq_num);
+ NVIC_DisableIRQ(irq->irq_num);
+
+ // Return if the trigger is disabled.
+ if (trigger == 0) {
+ return 0;
+ }
+
+ // Clear and enable GPIO IRQ.
+ gpio_enable_interrupt(self->gpio, self->pin);
+ gpio_unmask_interrupt(self->gpio, self->pin);
+
+ // Clear GPIO config.
+ self->gpio->GPIO_INT_BOTHEDGE &= ~(1 << self->pin);
+ self->gpio->GPIO_INT_POLARITY &= ~(1 << self->pin);
+ self->gpio->GPIO_INTTYPE_LEVEL &= ~(1 << self->pin);
+
+ // Configure GPIO IRQ trigger
+ if (trigger == MP_HAL_PIN_TRIGGER_FALL) {
+ gpio_interrupt_set_edge_trigger(self->gpio, self->pin);
+ gpio_interrupt_set_polarity_low(self->gpio, self->pin);
+ } else if (trigger == MP_HAL_PIN_TRIGGER_RISE) {
+ gpio_interrupt_set_edge_trigger(self->gpio, self->pin);
+ gpio_interrupt_set_polarity_high(self->gpio, self->pin);
+ } else if (trigger == (MP_HAL_PIN_TRIGGER_FALL | MP_HAL_PIN_TRIGGER_RISE)) {
+ gpio_interrupt_set_both_edge_trigger(self->gpio, self->pin);
+ } else {
+ mp_raise_ValueError(MP_ERROR_TEXT("Invalid IRQ trigger"));
+ }
+
+ // Clear GPIO IRQ (must be done after configuring trigger)
+ gpio_interrupt_eoi(self->gpio, self->pin);
+
+ // Clear and enable NVIC GPIO IRQ.
+ NVIC_ClearPendingIRQ(irq->irq_num);
+ NVIC_SetPriority(irq->irq_num, IRQ_PRI_GPIO);
+ NVIC_EnableIRQ(irq->irq_num);
+
+ return 0;
+}
+
+static mp_uint_t machine_pin_irq_info(mp_obj_t self_in, mp_uint_t info_type) {
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(self->port, self->pin);
+
+ if (info_type == MP_IRQ_INFO_FLAGS) {
+ return irq->flags;
+ } else if (info_type == MP_IRQ_INFO_TRIGGERS) {
+ return irq->trigger;
+ }
+ return 0;
+}
+
+static const mp_irq_methods_t machine_pin_irq_methods = {
+ .trigger = machine_pin_irq_trigger,
+ .info = machine_pin_irq_info,
+};
+
+// pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING, hard=False)
+static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_handler, ARG_trigger, ARG_hard };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
+ { MP_QSTR_trigger, MP_ARG_INT, {.u_int = MP_HAL_PIN_TRIGGER_FALL | MP_HAL_PIN_TRIGGER_RISE} },
+ { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} },
+ };
+
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(self->port, self->pin);
+
+ // Allocate a new IRQ object if it doesn't exist.
+ if (irq == NULL) {
+ irq = m_new_obj(machine_pin_irq_obj_t);
+ uint32_t idx = MACHINE_PIN_IRQ_INDEX(self->port, self->pin);
+
+ irq->base.base.type = &mp_irq_type;
+ irq->base.methods = (mp_irq_methods_t *)&machine_pin_irq_methods;
+ irq->base.parent = MP_OBJ_FROM_PTR(self);
+ irq->base.handler = mp_const_none;
+ irq->base.ishard = false;
+ irq->reserved = false;
+ irq->irq_num = (self->port < 15) ? (GPIO0_IRQ0_IRQn + idx) : (LPGPIO_IRQ0_IRQn + self->pin);
+ MP_STATE_PORT(machine_pin_irq_obj[idx]) = irq;
+ }
+
+ if (n_args > 1 || kw_args->used != 0) {
+ if (irq->reserved) {
+ mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Pin IRQ is reserved"));
+ }
+ irq->base.handler = args[ARG_handler].u_obj;
+ irq->base.ishard = args[ARG_hard].u_bool;
+ machine_pin_irq_trigger(self, args[ARG_trigger].u_int);
+ }
+
+ return MP_OBJ_FROM_PTR(irq);
+}
+static MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq);
+
+void machine_pin_irq_deinit(void) {
+ for (size_t i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(machine_pin_irq_obj)); i++) {
+ machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_obj[i]);
+ if (irq != NULL) {
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(irq->base.parent);
+ machine_pin_irq_trigger(self, 0);
+ MP_STATE_PORT(machine_pin_irq_obj[i]) = NULL;
+ }
+ }
+}
+
static MP_DEFINE_CONST_OBJ_TYPE(
pin_cpu_pins_obj_type,
MP_QSTR_cpu,
@@ -248,6 +440,7 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_low_obj) },
{ MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&machine_pin_high_obj) },
{ MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) },
+ { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_pin_irq_obj) },
// class attributes
{ MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&pin_board_pins_obj_type) },
@@ -259,6 +452,8 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(MP_HAL_PIN_MODE_OPEN_DRAIN) },
{ MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(MP_HAL_PIN_PULL_UP) },
{ MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(MP_HAL_PIN_PULL_DOWN) },
+ { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(MP_HAL_PIN_TRIGGER_FALL) },
+ { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(MP_HAL_PIN_TRIGGER_RISE) },
};
static MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table);
@@ -296,3 +491,5 @@ MP_DEFINE_CONST_OBJ_TYPE(
mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t obj) {
return machine_pin_find(obj);
}
+
+MP_REGISTER_ROOT_POINTER(void *machine_pin_irq_obj[MACHINE_PIN_NUM_VECTORS]);
diff --git a/ports/alif/machine_spi.c b/ports/alif/machine_spi.c
index abb60bc4e8..b2c14745cf 100644
--- a/ports/alif/machine_spi.c
+++ b/ports/alif/machine_spi.c
@@ -39,6 +39,7 @@ typedef struct _machine_spi_obj_t {
uint8_t id;
SPI_Type *inst;
bool is_lp;
+ uint32_t bits;
} machine_spi_obj_t;
static machine_spi_obj_t machine_spi_obj[] = {
@@ -246,6 +247,9 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
// Get static peripheral object.
machine_spi_obj_t *self = &machine_spi_obj[spi_id];
+ // Set args
+ self->bits = args[ARG_bits].u_int;
+
// here we would check the sck/mosi/miso pins and configure them, but it's not implemented
if (args[ARG_sck].u_obj != MP_OBJ_NULL ||
args[ARG_mosi].u_obj != MP_OBJ_NULL ||
@@ -294,22 +298,50 @@ static void machine_spi_deinit(mp_obj_base_t *self_in) {
}
}
+static void machine_spi_poll_flag(SPI_Type *spi, uint32_t flag, uint32_t timeout) {
+ mp_uint_t tick_start = mp_hal_ticks_ms();
+ while (!(spi->SPI_SR & flag)) {
+ if (mp_hal_ticks_ms() - tick_start >= timeout) {
+ mp_raise_OSError(MP_ETIMEDOUT);
+ }
+ mp_event_handle_nowait();
+ }
+}
+
static void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
machine_spi_obj_t *self = (machine_spi_obj_t *)self_in;
- spi_transfer_t spi_xfer = {
- .tx_buff = src,
- .tx_total_cnt = len,
- .rx_buff = dest,
- .rx_total_cnt = len,
- .tx_default_val = 0xFF,
- .tx_default_enable = true,
- .mode = SPI_TMOD_TX_AND_RX,
- };
- // TODO redo transfer_blocking to timeout and poll events.
- if (!self->is_lp) {
- spi_transfer_blocking(self->inst, &spi_xfer);
- } else {
- lpspi_transfer_blocking(self->inst, &spi_xfer);
+ volatile uint32_t *dr = self->inst->SPI_DR;
+
+ spi_set_tmod(self->inst, SPI_TMOD_TX_AND_RX);
+
+ for (size_t i = 0; i < len; i++) {
+ // Wait for space in the TX FIFO
+ machine_spi_poll_flag(self->inst, SPI_SR_TFNF, 100);
+
+ // Send data
+ if (src == NULL) {
+ *dr = 0xFFFFFFFFU;
+ } else if (self->bits > 16) {
+ *dr = ((uint32_t *)src)[i];
+ } else if (self->bits > 8) {
+ *dr = ((uint16_t *)src)[i];
+ } else {
+ *dr = ((uint8_t *)src)[i];
+ }
+
+ // Wait for data in the RX FIFO
+ machine_spi_poll_flag(self->inst, SPI_SR_RFNE, 100);
+
+ // Recv data
+ if (dest == NULL) {
+ (void)*dr;
+ } else if (self->bits > 16) {
+ ((uint32_t *)dest)[i] = *dr;
+ } else if (self->bits > 8) {
+ ((uint16_t *)dest)[i] = *dr;
+ } else {
+ ((uint8_t *)dest)[i] = *dr;
+ }
}
}
diff --git a/ports/alif/main.c b/ports/alif/main.c
index fd35604cbc..ab5e85d5b9 100644
--- a/ports/alif/main.c
+++ b/ports/alif/main.c
@@ -55,8 +55,9 @@
extern uint8_t __StackTop, __StackLimit;
extern uint8_t __GcHeapStart, __GcHeapEnd;
+extern void machine_pin_irq_deinit(void);
-NORETURN void panic(const char *msg) {
+MP_NORETURN void panic(const char *msg) {
mp_hal_stdout_tx_strn("\nFATAL ERROR:\n", 14);
mp_hal_stdout_tx_strn(msg, strlen(msg));
for (;;) {
@@ -164,6 +165,7 @@ int main(void) {
mp_bluetooth_deinit();
#endif
soft_timer_deinit();
+ machine_pin_irq_deinit();
gc_sweep_all();
mp_deinit();
}
diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c
index 9868abbeea..71b5be5193 100644
--- a/ports/alif/modmachine.c
+++ b/ports/alif/modmachine.c
@@ -47,11 +47,11 @@ static mp_obj_t mp_machine_unique_id(void) {
return mp_obj_new_bytes(id, sizeof(id));
}
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
se_services_reset_soc();
}
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
__disable_irq();
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
@@ -112,7 +112,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
#endif
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
#if MICROPY_HW_ENABLE_USBDEV
mp_machine_enable_usb(false);
#endif
diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h
index 6c9e6af635..ddfc551bf8 100644
--- a/ports/alif/mpconfigport.h
+++ b/ports/alif/mpconfigport.h
@@ -158,19 +158,11 @@
#define MICROPY_FATFS_MAX_SS (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES)
#endif
-#if MICROPY_PY_NETWORK_CYW43
-extern const struct _mp_obj_type_t mp_network_cyw43_type;
-#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) },
-#else
-#define MICROPY_HW_NIC_CYW43
-#endif
-
#ifndef MICROPY_BOARD_NETWORK_INTERFACES
#define MICROPY_BOARD_NETWORK_INTERFACES
#endif
#define MICROPY_PORT_NETWORK_INTERFACES \
- MICROPY_HW_NIC_CYW43 \
MICROPY_BOARD_NETWORK_INTERFACES \
// Bluetooth code only runs in the scheduler, no locking/mutex required.
diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h
index 4f81439b54..f03dc7c1c1 100644
--- a/ports/alif/mphalport.h
+++ b/ports/alif/mphalport.h
@@ -90,6 +90,9 @@ extern ringbuf_t stdin_ringbuf;
#define MP_HAL_PIN_SPEED_LOW (0)
#define MP_HAL_PIN_SPEED_HIGH (PADCTRL_SLEW_RATE_FAST)
+#define MP_HAL_PIN_TRIGGER_FALL (1)
+#define MP_HAL_PIN_TRIGGER_RISE (2)
+
#define mp_hal_pin_obj_t const machine_pin_obj_t *
#define MP_HAL_PIN_ALT(function, unit) (MP_HAL_PIN_ALT_MAKE((MP_HAL_PIN_ALT_##function), (unit)))
diff --git a/ports/cc3200/misc/mperror.c b/ports/cc3200/misc/mperror.c
index 6d6c0ff0ba..69f0a25371 100644
--- a/ports/cc3200/misc/mperror.c
+++ b/ports/cc3200/misc/mperror.c
@@ -159,7 +159,7 @@ void mperror_heartbeat_signal (void) {
}
}
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
#ifdef DEBUG
if (msg != NULL) {
// wait for 20ms
diff --git a/ports/cc3200/misc/mperror.h b/ports/cc3200/misc/mperror.h
index 1c3eb62697..30effbbf91 100644
--- a/ports/cc3200/misc/mperror.h
+++ b/ports/cc3200/misc/mperror.h
@@ -27,7 +27,7 @@
#ifndef MICROPY_INCLUDED_CC3200_MISC_MPERROR_H
#define MICROPY_INCLUDED_CC3200_MISC_MPERROR_H
-extern void NORETURN __fatal_error(const char *msg);
+extern void MP_NORETURN __fatal_error(const char *msg);
void mperror_init0 (void);
void mperror_bootloader_check_reset_cause (void);
diff --git a/ports/cc3200/mods/modmachine.c b/ports/cc3200/mods/modmachine.c
index f7184df442..8986635ac6 100644
--- a/ports/cc3200/mods/modmachine.c
+++ b/ports/cc3200/mods/modmachine.c
@@ -93,7 +93,7 @@ extern OsiTaskHandle xSimpleLinkSpawnTaskHndl;
/******************************************************************************/
// MicroPython bindings;
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
// disable wlan
wlan_stop(SL_STOP_TIMEOUT_LONG);
// reset the cpu and it's peripherals
@@ -161,7 +161,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
pyb_sleep_sleep();
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
pyb_sleep_deepsleep();
for (;;) {
}
diff --git a/ports/cc3200/mods/modtime.c b/ports/cc3200/mods/modtime.c
index 21388568ab..254678fb2d 100644
--- a/ports/cc3200/mods/modtime.c
+++ b/ports/cc3200/mods/modtime.c
@@ -50,5 +50,5 @@ static mp_obj_t mp_time_localtime_get(void) {
// Returns the number of seconds, as an integer, since the Epoch.
static mp_obj_t mp_time_time_get(void) {
- return mp_obj_new_int(pyb_rtc_get_seconds());
+ return timeutils_obj_from_timestamp(pyb_rtc_get_seconds());
}
diff --git a/ports/cc3200/mods/pybsleep.c b/ports/cc3200/mods/pybsleep.c
index 9d04dd411d..291860de8d 100644
--- a/ports/cc3200/mods/pybsleep.c
+++ b/ports/cc3200/mods/pybsleep.c
@@ -136,7 +136,7 @@ static MP_DEFINE_CONST_OBJ_TYPE(
******************************************************************************/
static pyb_sleep_obj_t *pyb_sleep_find (mp_obj_t obj);
static void pyb_sleep_flash_powerdown (void);
-static NORETURN void pyb_sleep_suspend_enter (void);
+static MP_NORETURN void pyb_sleep_suspend_enter (void);
void pyb_sleep_suspend_exit (void);
static void pyb_sleep_obj_wakeup (void);
static void PRCMInterruptHandler (void);
@@ -360,7 +360,7 @@ static void pyb_sleep_flash_powerdown (void) {
MAP_SPICSDisable(SSPI_BASE);
}
-static NORETURN void pyb_sleep_suspend_enter (void) {
+static MP_NORETURN void pyb_sleep_suspend_enter (void) {
// enable full RAM retention
MAP_PRCMSRAMRetentionEnable(PRCM_SRAM_COL_1 | PRCM_SRAM_COL_2 | PRCM_SRAM_COL_3 | PRCM_SRAM_COL_4, PRCM_SRAM_LPDS_RET);
diff --git a/ports/esp32/README.md b/ports/esp32/README.md
index d9115072a4..d8b55e45f3 100644
--- a/ports/esp32/README.md
+++ b/ports/esp32/README.md
@@ -31,7 +31,7 @@ manage the ESP32 microcontroller, as well as a way to manage the required
build environment and toolchains needed to build the firmware.
The ESP-IDF changes quickly and MicroPython only supports certain versions.
-Currently MicroPython supports v5.2, v5.2.2, v5.3 and v5.4.
+Currently MicroPython supports v5.2, v5.2.2, v5.3, v5.4 and v5.4.1.
To install the ESP-IDF the full instructions can be found at the
[Espressif Getting Started guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#installation-step-by-step).
@@ -49,10 +49,10 @@ The steps to take are summarised below.
To check out a copy of the IDF use git clone:
```bash
-$ git clone -b v5.2.2 --recursive https://github.com/espressif/esp-idf.git
+$ git clone -b v5.4.1 --recursive https://github.com/espressif/esp-idf.git
```
-You can replace `v5.2.2` with any other supported version.
+You can replace `v5.4.1` with any other supported version.
(You don't need a full recursive clone; see the `ci_esp32_setup` function in
`tools/ci.sh` in this repository for more detailed set-up commands.)
@@ -61,7 +61,7 @@ MicroPython and update the submodules using:
```bash
$ cd esp-idf
-$ git checkout v5.2.2
+$ git checkout v5.4.1
$ git submodule update --init --recursive
```
@@ -221,6 +221,30 @@ import machine
antenna = machine.Pin(16, machine.Pin.OUT, value=0)
```
+Partition table and filesystem size
+-----------------------------------
+
+ESP32 firmware contains a bootloader, partition table and main application
+firmware, which are all stored in (external) SPI flash. The user filesystem
+is also stored in the same SPI flash. By default, MicroPython does not have
+a fixed entry in the ESP32 partition table for the filesystem. Instead it
+will automatically determine the size of the SPI flash upon boot, and then --
+so long as there is no existing partition called "vfs" or "ffat" -- it will
+create a partition for the filesystem called "vfs" which takes all of the
+remaining flash between the end of the last defined partition up until the
+end of flash.
+
+This means that firmware built for, say, a 4MiB flash will work on any
+device that has at least 4MiB flash. The user "vfs" filesystem will then
+take up as much space as possible.
+
+This auto-detection behaviour can be overridden: if the ESP32 partition
+table does contain an entry called "vfs" or "ffat" then these are used for
+the user filesystem and no automatic "vfs" partition is added. This is
+useful in cases where only the MicroPython ESP32 application is flashed to
+the device (and not the bootloader or partition table), eg when deploying
+.uf2 files.
+
Defining a custom ESP32 board
-----------------------------
diff --git a/ports/esp32/adc.c b/ports/esp32/adc.c
index 83af6dce0b..d0e5e81330 100644
--- a/ports/esp32/adc.c
+++ b/ports/esp32/adc.c
@@ -26,83 +26,73 @@
*/
#include "py/mphal.h"
+#include "py/mperrno.h"
#include "adc.h"
-#include "driver/adc.h"
+#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali_scheme.h"
-#define DEFAULT_VREF 1100
+static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten);
-void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits) {
- if (bits < SOC_ADC_RTC_MIN_BITWIDTH && bits > SOC_ADC_RTC_MAX_BITWIDTH) {
- // Invalid value for the current chip, raise exception in the switch below.
- bits = -1;
+void adc_is_init_guard(machine_adc_block_obj_t *self) {
+ if (!self->handle) {
+ mp_raise_OSError(MP_EPERM);
}
- switch (bits) {
- case 9:
- self->width = ADC_BITWIDTH_9;
- break;
- case 10:
- self->width = ADC_BITWIDTH_10;
- break;
- case 11:
- self->width = ADC_BITWIDTH_11;
- break;
- case 12:
- self->width = ADC_BITWIDTH_12;
- break;
- case 13:
- self->width = ADC_BITWIDTH_13;
- break;
- default:
- mp_raise_ValueError(MP_ERROR_TEXT("invalid bits"));
- }
- self->bits = bits;
+}
- if (self->unit_id == ADC_UNIT_1) {
- adc1_config_width(self->width);
- }
+esp_err_t apply_self_adc_channel_atten(const machine_adc_obj_t *self, uint8_t atten) {
+ adc_is_init_guard(self->block);
+
+ adc_oneshot_chan_cfg_t config = {
+ .atten = atten,
+ .bitwidth = self->block->bitwidth,
+ };
+ esp_err_t ret = adc_oneshot_config_channel(self->block->handle, self->channel_id, &config);
+ return ret;
}
mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id) {
- int raw = 0;
- if (self->unit_id == ADC_UNIT_1) {
- raw = adc1_get_raw(channel_id);
- } else {
- #if (SOC_ADC_PERIPH_NUM >= 2)
- check_esp_err(adc2_get_raw(channel_id, self->width, &raw));
- #endif
- }
- return raw;
+ adc_is_init_guard(self);
+
+ int reading = 0;
+ adc_oneshot_read(self->handle, channel_id, &reading);
+ return reading;
+}
+
+/*
+During testing, it turned out that the function `adc_cali_raw_to_voltage` does not account for the lower resolution,
+instead it expects the full resolution value as an argument, hence the scaling applied here
+*/
+mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) {
+ int raw = madcblock_read_helper(self, channel_id);
+ int uv = 0;
+
+ check_esp_err(ensure_adc_calibration(self, atten));
+ check_esp_err(adc_cali_raw_to_voltage(self->calib[atten], (raw << (ADC_WIDTH_MAX - self->bitwidth)), &uv));
+ return (mp_int_t)uv * 1000;
}
static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten) {
- if (self->handle[atten] != NULL) {
+ if (self->calib[atten] != NULL) {
return ESP_OK;
}
+ esp_err_t ret = ESP_OK;
+
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = self->unit_id,
.atten = atten,
- .bitwidth = self->width,
+ .bitwidth = self->bitwidth,
};
- return adc_cali_create_scheme_curve_fitting(&cali_config, &self->handle[atten]);
+ ret = adc_cali_create_scheme_curve_fitting(&cali_config, &self->calib[atten]);
#else
adc_cali_line_fitting_config_t cali_config = {
.unit_id = self->unit_id,
.atten = atten,
- .bitwidth = self->width,
+ .bitwidth = self->bitwidth,
};
- return adc_cali_create_scheme_line_fitting(&cali_config, &self->handle[atten]);
+ ret = adc_cali_create_scheme_line_fitting(&cali_config, &self->calib[atten]);
#endif
-}
-mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) {
- int raw = madcblock_read_helper(self, channel_id);
- int uv;
-
- check_esp_err(ensure_adc_calibration(self, atten));
- check_esp_err(adc_cali_raw_to_voltage(self->handle[atten], raw, &uv));
-
- return (mp_int_t)uv * 1000;
+ return ret;
}
diff --git a/ports/esp32/adc.h b/ports/esp32/adc.h
index 5688e0a29a..ebf7fcc21a 100644
--- a/ports/esp32/adc.h
+++ b/ports/esp32/adc.h
@@ -29,17 +29,45 @@
#define MICROPY_INCLUDED_ESP32_ADC_H
#include "py/runtime.h"
-#include "esp_adc_cal.h"
+#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali_scheme.h"
-#define ADC_ATTEN_MAX SOC_ADC_ATTEN_NUM
+#define ADC_ATTEN_COUNT SOC_ADC_ATTEN_NUM
+#define ADC_ATTEN_MIN ADC_ATTEN_DB_0
+#define ADC_ATTEN_MAX ADC_ATTEN_DB_11
+
+/*
+https://github.com/espressif/esp-idf/issues/13128
+https://github.com/espressif/esp-idf/blob/release/v5.2/components/soc/esp32s3/include/soc/soc_caps.h
+https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32s2/03-errata-description/index.html
+https://docs.espressif.com/projects/esp-idf/en/v4.2/esp32/api-reference/peripherals/adc.html
+
+Looks like only the original esp32 is capable of bitwidth adjustment, all others are stuck at 12 bits,
+except the S2, which is locked at 13 bits, otherwise attenuation doesn't work.
+*/
+#if CONFIG_IDF_TARGET_ESP32S2
+
+#define ADC_WIDTH_MIN ADC_BITWIDTH_13
+#define ADC_WIDTH_MAX ADC_BITWIDTH_13
+
+#elif CONFIG_IDF_TARGET_ESP32
+
+#define ADC_WIDTH_MIN ADC_BITWIDTH_9
+#define ADC_WIDTH_MAX ADC_BITWIDTH_12
+
+#else
+
+#define ADC_WIDTH_MIN ADC_BITWIDTH_12
+#define ADC_WIDTH_MAX ADC_BITWIDTH_12
+
+#endif
typedef struct _machine_adc_block_obj_t {
mp_obj_base_t base;
adc_unit_t unit_id;
- mp_int_t bits;
- adc_bits_width_t width;
- adc_cali_handle_t handle[ADC_ATTEN_MAX];
+ adc_oneshot_unit_handle_t handle;
+ adc_bitwidth_t bitwidth;
+ adc_cali_handle_t calib[ADC_ATTEN_COUNT];
} machine_adc_block_obj_t;
typedef struct _machine_adc_obj_t {
@@ -51,11 +79,18 @@ typedef struct _machine_adc_obj_t {
extern machine_adc_block_obj_t madcblock_obj[];
-void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits);
+esp_err_t apply_self_adc_channel_atten(const machine_adc_obj_t *self, uint8_t atten);
+
mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id);
mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten);
const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id);
void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
+mp_int_t mp_machine_adc_atten_get_helper(const machine_adc_obj_t *self);
+void mp_machine_adc_atten_set_helper(const machine_adc_obj_t *self, mp_int_t atten);
+
+mp_int_t mp_machine_adc_width_get_helper(const machine_adc_obj_t *self);
+void mp_machine_adc_block_width_set_helper(machine_adc_block_obj_t *self, mp_int_t width);
+
#endif // MICROPY_INCLUDED_ESP32_ADC_H
diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json
index 9d0016017f..936a498b5e 100644
--- a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json
+++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json
@@ -17,8 +17,8 @@
"ABX00092_01.iso_1000x750.jpg"
],
"mcu": "esp32s3",
- "product": "Arduino Nano ESP32",
+ "product": "Nano ESP32",
"thumbnail": "",
- "url": "https://store.arduino.cc/products/arduino-nano-esp32",
+ "url": "https://docs.arduino.cc/hardware/nano-esp32/",
"vendor": "Arduino"
}
diff --git a/ports/esp32/boards/ESP32_GENERIC/board.md b/ports/esp32/boards/ESP32_GENERIC/board.md
index 9346d18d84..06972e436f 100644
--- a/ports/esp32/boards/ESP32_GENERIC/board.md
+++ b/ports/esp32/boards/ESP32_GENERIC/board.md
@@ -1,5 +1,5 @@
The following files are firmware that should work on most ESP32-based boards
-with 4MiB of flash, including WROOM WROVER, SOLO, PICO, and MINI modules.
+with 4MiB or more of flash, including WROOM WROVER, SOLO, PICO, and MINI modules.
This board has multiple variants available:
diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/board.md b/ports/esp32/boards/ESP32_GENERIC_C3/board.md
index 1604b879c5..6097eb85df 100644
--- a/ports/esp32/boards/ESP32_GENERIC_C3/board.md
+++ b/ports/esp32/boards/ESP32_GENERIC_C3/board.md
@@ -1,6 +1,6 @@
The following files are firmware images that should work on most
-ESP32-C3-based boards with 4MiB of flash, including WROOM and MINI modules,
-that use the revision 3 silicon (or newer).
+ESP32-C3-based boards with 4MiB or more of flash, including WROOM and MINI
+modules, that use the revision 3 silicon (or newer).
USB serial/JTAG support is enabled on pin 18 and 19. Note that this
is not a full USB stack, the C3 just provides a CDC/ACM class serial
diff --git a/ports/esp32/boards/ESP32_GENERIC_S2/board.md b/ports/esp32/boards/ESP32_GENERIC_S2/board.md
index 3e46c02467..d065ecbbb9 100644
--- a/ports/esp32/boards/ESP32_GENERIC_S2/board.md
+++ b/ports/esp32/boards/ESP32_GENERIC_S2/board.md
@@ -1,5 +1,5 @@
The following files are firmware that should work on most ESP32-S2-based
-boards with 4MiB of flash, including WROOM, WROVER, and MINI modules.
+boards with 4MiB or more of flash, including WROOM, WROVER, and MINI modules.
This firmware supports configurations with and without SPIRAM (also known as
PSRAM) and will auto-detect a connected SPIRAM chip at startup and allocate
diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/board.json b/ports/esp32/boards/ESP32_GENERIC_S3/board.json
index fd0c9edce0..7a546d35fc 100644
--- a/ports/esp32/boards/ESP32_GENERIC_S3/board.json
+++ b/ports/esp32/boards/ESP32_GENERIC_S3/board.json
@@ -21,7 +21,6 @@
"url": "https://www.espressif.com/en/products/modules",
"vendor": "Espressif",
"variants": {
- "SPIRAM_OCT": "Support for Octal-SPIRAM",
- "FLASH_4M": "4MiB flash"
+ "SPIRAM_OCT": "Support for Octal-SPIRAM"
}
}
diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/board.md b/ports/esp32/boards/ESP32_GENERIC_S3/board.md
index 16930c9193..68ce0917e0 100644
--- a/ports/esp32/boards/ESP32_GENERIC_S3/board.md
+++ b/ports/esp32/boards/ESP32_GENERIC_S3/board.md
@@ -1,9 +1,7 @@
The following files are firmware that should work on most ESP32-S3-based
-boards with 4/8MiB of flash, including WROOM and MINI modules.
+boards with 4MiB or more of flash, including WROOM and MINI modules.
This firmware supports configurations with and without SPIRAM (also known as
PSRAM) and will auto-detect a connected SPIRAM chip at startup and allocate
the MicroPython heap accordingly. However if your board has Octal SPIRAM, then
use the "spiram-oct" variant.
-
-If your board has 4MiB flash (including ESP32-S3FH4R2 based ones with embedded flash), then use the "flash-4m" build.
diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake
deleted file mode 100644
index e832cdb60f..0000000000
--- a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake
+++ /dev/null
@@ -1,4 +0,0 @@
-set(SDKCONFIG_DEFAULTS
- ${SDKCONFIG_DEFAULTS}
- boards/ESP32_GENERIC_S3/sdkconfig.flash_4m
-)
diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board
index 9839b0d300..369330682f 100644
--- a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board
+++ b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board
@@ -1,8 +1,2 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
-
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m
deleted file mode 100644
index c967c46ae6..0000000000
--- a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=
-
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB.csv"
diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json
new file mode 100644
index 0000000000..d2ada7cccd
--- /dev/null
+++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json
@@ -0,0 +1,23 @@
+{
+ "deploy": [
+ "../deploy_nativeusb.md"
+ ],
+ "deploy_options": {
+ "flash_offset": "0"
+ },
+ "docs": "",
+ "features": [
+ "BLE",
+ "External Flash",
+ "RGB LED",
+ "WiFi"
+ ],
+ "images": [
+ "GAR-PYBSTICK26-C3-mUSB-00.JPG"
+ ],
+ "mcu": "esp32c3",
+ "product": "PYBSTICK26_ESP32C3",
+ "thumbnail": "",
+ "url": "https://shop.mchobby.be/fr/pybstick/2505-pybstick26-esp32-c3-micropython-et-arduino-3232100025059.html",
+ "vendor": "McHobby"
+}
diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake
new file mode 100644
index 0000000000..81cfff1d77
--- /dev/null
+++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake
@@ -0,0 +1,7 @@
+set(IDF_TARGET esp32c3)
+
+set(SDKCONFIG_DEFAULTS
+ boards/sdkconfig.base
+ boards/sdkconfig.ble
+ boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board
+)
diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h
new file mode 100644
index 0000000000..c9796d1f65
--- /dev/null
+++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h
@@ -0,0 +1,10 @@
+#define MICROPY_HW_BOARD_NAME "PYBSTICK26_ESP32C3"
+#define MICROPY_HW_MCU_NAME "ESP32C3"
+#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "pybstick26_esp32c3"
+
+#define MICROPY_HW_I2C0_SCL (1)
+#define MICROPY_HW_I2C0_SDA (0)
+
+#define MICROPY_HW_SPI1_MOSI (7)
+#define MICROPY_HW_SPI1_MISO (2)
+#define MICROPY_HW_SPI1_SCK (6)
diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv
new file mode 100644
index 0000000000..d786654903
--- /dev/null
+++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv
@@ -0,0 +1,13 @@
+GP0,GPIO0
+GP1,GPIO1
+GP2,GPIO2
+GP3,GPIO3
+GP4,GPIO4
+GP5,GPIO5
+GP6,GPIO6
+GP7,GPIO7
+GP8,GPIO8
+GP9,GPIO9
+GP10,GPIO10
+GP20,GPIO20
+GP21,GPIO21
diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board
new file mode 100644
index 0000000000..70e5f1f0d1
--- /dev/null
+++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board
@@ -0,0 +1,4 @@
+CONFIG_ESP32C3_REV_MIN_3=y
+
+# Workaround for https://github.com/espressif/esp-idf/issues/14456
+CONFIG_ESP_SYSTEM_HW_STACK_GUARD=n
diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json
index d68a9baadd..7521c01a6b 100644
--- a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json
+++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json
@@ -19,8 +19,8 @@
"lilygo-ttgo-lora-32-v1-6.jpg"
],
"mcu": "esp32",
- "product": "LILYGO TTGO LoRa32",
+ "product": "TTGO LoRa32",
"thumbnail": "",
- "url": "http://www.lilygo.cn/prod_view.aspx?TypeId=50060&Id=1270&FId=t3:50060:3",
+ "url": "https://www.lilygo.cc/products/lora3",
"vendor": "LILYGO"
}
diff --git a/ports/esp32/boards/M5STACK_ATOM/board.json b/ports/esp32/boards/M5STACK_ATOM/board.json
index 3a1e7ce359..c3908a4779 100644
--- a/ports/esp32/boards/M5STACK_ATOM/board.json
+++ b/ports/esp32/boards/M5STACK_ATOM/board.json
@@ -18,8 +18,8 @@
"m5stack_atom.jpg"
],
"mcu": "esp32",
- "product": "M5 Stack Atom",
+ "product": "Atom",
"thumbnail": "",
- "url": "https://m5stack.com/",
- "vendor": "M5 Stack"
+ "url": "https://shop.m5stack.com/products/atom-matrix-esp32-development-kit",
+ "vendor": "M5Stack"
}
diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json
index fe0e97f9f9..26b1a4d54a 100644
--- a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json
+++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json
@@ -20,5 +20,5 @@
"product": "AtomS3 Lite",
"thumbnail": "",
"url": "https://shop.m5stack.com/products/atoms3-lite-esp32s3-dev-kit",
- "vendor": "M5 Stack"
+ "vendor": "M5Stack"
}
diff --git a/ports/esp32/boards/M5STACK_NANOC6/board.json b/ports/esp32/boards/M5STACK_NANOC6/board.json
index 087851ae5e..ddf3406c83 100644
--- a/ports/esp32/boards/M5STACK_NANOC6/board.json
+++ b/ports/esp32/boards/M5STACK_NANOC6/board.json
@@ -18,7 +18,7 @@
"m5stack_nanoc6.jpg"
],
"mcu": "esp32c6",
- "product": "M5Stack NanoC6",
+ "product": "NanoC6",
"url": "https://shop.m5stack.com/products/m5stack-nanoc6-dev-kit",
"vendor": "M5Stack"
}
diff --git a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json
index 3eb9a5e0a7..198f1d4b6d 100644
--- a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json
+++ b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json
@@ -18,8 +18,8 @@
"ESP32-EVB_Rev_K1.png"
],
"mcu": "esp32",
- "product": "Olimex ESP32 EVB",
+ "product": "ESP32 EVB",
"thumbnail": "",
- "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB/open-source-hardware",
- "vendor": "OLIMEX"
+ "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB",
+ "vendor": "Olimex"
}
diff --git a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json
index cda26178d7..1b00d213c7 100644
--- a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json
+++ b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json
@@ -19,8 +19,8 @@
"ESP32-POE-ISO-1.jpg"
],
"mcu": "esp32",
- "product": "Olimex ESP32 POE",
+ "product": "ESP32 POE",
"thumbnail": "",
- "url": "https://www.olimex.com/",
- "vendor": "OLIMEX"
+ "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE",
+ "vendor": "Olimex"
}
diff --git a/ports/esp32/boards/SIL_WESP32/board.json b/ports/esp32/boards/SIL_WESP32/board.json
index 53a50f3286..7126fa7ea1 100644
--- a/ports/esp32/boards/SIL_WESP32/board.json
+++ b/ports/esp32/boards/SIL_WESP32/board.json
@@ -18,7 +18,7 @@
"wesp32-top.jpg"
],
"mcu": "esp32",
- "product": "SIL WESP32",
+ "product": "wESP32",
"thumbnail": "",
"url": "https://wesp32.com/",
"vendor": "Silicognition"
diff --git a/ports/esp32/boards/SIL_WESP32/sdkconfig.board b/ports/esp32/boards/SIL_WESP32/sdkconfig.board
index 741a4db439..36155cd5e3 100644
--- a/ports/esp32/boards/SIL_WESP32/sdkconfig.board
+++ b/ports/esp32/boards/SIL_WESP32/sdkconfig.board
@@ -1,9 +1,6 @@
-# 16 MB flash
+# 8 MB+ flash
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE="16MB"
+CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
# Fast flash
@@ -14,7 +11,7 @@ CONFIG_ESP32_REV_MIN_1=y
# OTA
CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB-ota.csv"
+CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiBplus-ota.csv"
# Network name
diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json
new file mode 100644
index 0000000000..8bce1a0013
--- /dev/null
+++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json
@@ -0,0 +1,23 @@
+{
+ "deploy": [
+ "../deploy.md"
+ ],
+ "deploy_options": {
+ "flash_offset": "0x1000"
+ },
+ "docs": "",
+ "features": [
+ "BLE",
+ "External Flash",
+ "WiFi",
+ "USB-C"
+ ],
+ "images": [
+ "19177-Sparkfun_IoT_Redboard-ESP32.jpg"
+ ],
+ "mcu": "esp32",
+ "product": "ESP32 / WROOM",
+ "thumbnail": "",
+ "url": "https://www.sparkfun.com/sparkfun-iot-redboard-esp32-development-board.html",
+ "vendor": "SparkFun"
+}
diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py
new file mode 100644
index 0000000000..73446ecac9
--- /dev/null
+++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py
@@ -0,0 +1,2 @@
+include("$(PORT_DIR)/boards/manifest.py")
+require("sdcard")
diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake
new file mode 100644
index 0000000000..0ffcc38c87
--- /dev/null
+++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake
@@ -0,0 +1,7 @@
+set(SDKCONFIG_DEFAULTS
+ boards/sdkconfig.base
+ boards/sdkconfig.ble
+ boards/sdkconfig.240mhz
+)
+
+set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)
diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h
new file mode 100644
index 0000000000..4fe888ac17
--- /dev/null
+++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h
@@ -0,0 +1,14 @@
+// Board and hardware specific configuration
+
+#define MICROPY_HW_BOARD_NAME "SparkFun IoT RedBoard ESP32"
+#define MICROPY_HW_MCU_NAME "ESP32"
+
+// Enable UART REPL for modules that have an external USB-UART and don't use native USB.
+#define MICROPY_HW_ENABLE_UART_REPL (1)
+
+#define MICROPY_HW_I2C0_SCL (22)
+#define MICROPY_HW_I2C0_SDA (21)
+
+#define MICROPY_HW_SPI1_SCK (18)
+#define MICROPY_HW_SPI1_MOSI (23)
+#define MICROPY_HW_SPI1_MISO (19)
diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv
new file mode 100644
index 0000000000..d37c51af8f
--- /dev/null
+++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv
@@ -0,0 +1,22 @@
+TX,GPIO1
+RX,GPIO3
+ALERT,GPIO4
+TCK,GPIO13
+TMS,GPIO14
+CS,GPIO5
+PICO,GPIO23
+POCI,GPIO19
+SCK,GPIO18
+SDA,GPIO21
+SCL,GPIO22
+A0,GPIO36
+A3,GPIO39
+A4,GPIO32
+A5,GPIO33
+A6,GPIO34
+A7,GPIO35
+LED,GPIO18
+LED_BLUE,GPIO18
+BLUE_LED,GPIO18
+RGB_LED,GPIO2
+NEOPIXEL,GPIO2
diff --git a/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board
index 9ab58f215f..162de0da14 100644
--- a/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board
+++ b/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board
@@ -3,11 +3,6 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_SPIRAM_MEMTEST=
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv"
-
# LWIP
CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS2"
# end of LWIP
diff --git a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board
index 3ca0c4b243..3092e35598 100644
--- a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board
+++ b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board
@@ -1,12 +1,7 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3"
diff --git a/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board
index d0355a94dc..25b8d7689d 100644
--- a/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board
+++ b/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board
@@ -3,11 +3,6 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_AFTER_NORESET=y
CONFIG_SPIRAM_MEMTEST=
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3Neo"
diff --git a/ports/esp32/boards/UM_NANOS3/sdkconfig.board b/ports/esp32/boards/UM_NANOS3/sdkconfig.board
index 2a39c64337..e06f7a4245 100644
--- a/ports/esp32/boards/UM_NANOS3/sdkconfig.board
+++ b/ports/esp32/boards/UM_NANOS3/sdkconfig.board
@@ -1,12 +1,7 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMNanoS3"
diff --git a/ports/esp32/boards/UM_OMGS3/sdkconfig.board b/ports/esp32/boards/UM_OMGS3/sdkconfig.board
index 84a8ce449e..8a0bf0b13a 100644
--- a/ports/esp32/boards/UM_OMGS3/sdkconfig.board
+++ b/ports/esp32/boards/UM_OMGS3/sdkconfig.board
@@ -2,12 +2,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_AFTER_NORESET=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMOMGS3"
diff --git a/ports/esp32/boards/UM_PROS3/sdkconfig.board b/ports/esp32/boards/UM_PROS3/sdkconfig.board
index 5752e03e60..75ca58d80b 100644
--- a/ports/esp32/boards/UM_PROS3/sdkconfig.board
+++ b/ports/esp32/boards/UM_PROS3/sdkconfig.board
@@ -1,12 +1,7 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMProS3"
diff --git a/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board b/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board
index ef3f38af47..7d244fdc16 100644
--- a/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board
+++ b/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board
@@ -2,12 +2,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_AFTER_NORESET=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMRGBTouchMini"
diff --git a/ports/esp32/boards/UM_TINYC6/sdkconfig.board b/ports/esp32/boards/UM_TINYC6/sdkconfig.board
index 7917467b12..213d28d8b4 100644
--- a/ports/esp32/boards/UM_TINYC6/sdkconfig.board
+++ b/ports/esp32/boards/UM_TINYC6/sdkconfig.board
@@ -2,9 +2,4 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_AFTER_NORESET=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
diff --git a/ports/esp32/boards/UM_TINYS3/sdkconfig.board b/ports/esp32/boards/UM_TINYS3/sdkconfig.board
index d1d19761a0..2474c5fb27 100644
--- a/ports/esp32/boards/UM_TINYS3/sdkconfig.board
+++ b/ports/esp32/boards/UM_TINYS3/sdkconfig.board
@@ -1,12 +1,7 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyS3"
diff --git a/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board b/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board
index 1380e15ce8..10121d235d 100644
--- a/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board
+++ b/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board
@@ -1,12 +1,7 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyWATCHS3"
diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base
index 530db42711..30740af434 100644
--- a/ports/esp32/boards/sdkconfig.base
+++ b/ports/esp32/boards/sdkconfig.base
@@ -50,7 +50,6 @@ CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n
CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=2
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=n
-CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK=y
# UDP
CONFIG_LWIP_PPP_SUPPORT=y
@@ -98,7 +97,7 @@ CONFIG_ULP_COPROC_RESERVE_MEM=2040
# For cmake build
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB.csv"
+CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiBplus.csv"
# To reduce iRAM usage
CONFIG_ESP32_WIFI_IRAM_OPT=n
@@ -118,7 +117,6 @@ CONFIG_ADC_CAL_LUT_ENABLE=y
CONFIG_UART_ISR_IN_IRAM=y
# IDF 5 deprecated
-CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y
CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
CONFIG_ETH_USE_SPI_ETHERNET=y
diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake
index c54f5a5403..09b1203913 100644
--- a/ports/esp32/esp32_common.cmake
+++ b/ports/esp32/esp32_common.cmake
@@ -80,9 +80,7 @@ list(APPEND MICROPY_SOURCE_DRIVERS
${MICROPY_DIR}/drivers/dht/dht.c
)
-list(APPEND GIT_SUBMODULES lib/tinyusb)
if(MICROPY_PY_TINYUSB)
- set(TINYUSB_SRC "${MICROPY_DIR}/lib/tinyusb/src")
string(TOUPPER OPT_MCU_${IDF_TARGET} tusb_mcu)
list(APPEND MICROPY_DEF_TINYUSB
@@ -90,12 +88,6 @@ if(MICROPY_PY_TINYUSB)
)
list(APPEND MICROPY_SOURCE_TINYUSB
- ${TINYUSB_SRC}/tusb.c
- ${TINYUSB_SRC}/common/tusb_fifo.c
- ${TINYUSB_SRC}/device/usbd.c
- ${TINYUSB_SRC}/device/usbd_control.c
- ${TINYUSB_SRC}/class/cdc/cdc_device.c
- ${TINYUSB_SRC}/portable/synopsys/dwc2/dcd_dwc2.c
${MICROPY_DIR}/shared/tinyusb/mp_usbd.c
${MICROPY_DIR}/shared/tinyusb/mp_usbd_cdc.c
${MICROPY_DIR}/shared/tinyusb/mp_usbd_descriptor.c
@@ -103,13 +95,8 @@ if(MICROPY_PY_TINYUSB)
)
list(APPEND MICROPY_INC_TINYUSB
- ${TINYUSB_SRC}
${MICROPY_DIR}/shared/tinyusb/
)
-
- list(APPEND MICROPY_LINK_TINYUSB
- -Wl,--wrap=dcd_event_handler
- )
endif()
list(APPEND MICROPY_SOURCE_PORT
@@ -251,6 +238,7 @@ endif()
# Set compile options for this port.
target_compile_definitions(${MICROPY_TARGET} PUBLIC
+ ${MICROPY_DEF_COMPONENT}
${MICROPY_DEF_CORE}
${MICROPY_DEF_BOARD}
${MICROPY_DEF_TINYUSB}
@@ -263,15 +251,12 @@ target_compile_definitions(${MICROPY_TARGET} PUBLIC
# Disable some warnings to keep the build output clean.
target_compile_options(${MICROPY_TARGET} PUBLIC
+ ${MICROPY_COMPILE_COMPONENT}
-Wno-clobbered
-Wno-deprecated-declarations
-Wno-missing-field-initializers
)
-target_link_options(${MICROPY_TARGET} PUBLIC
- ${MICROPY_LINK_TINYUSB}
-)
-
# Additional include directories needed for private NimBLE headers.
target_include_directories(${MICROPY_TARGET} PUBLIC
${IDF_PATH}/components/bt/host/nimble/nimble
diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c
index be1725c370..02acaa22da 100644
--- a/ports/esp32/machine_adc.c
+++ b/ports/esp32/machine_adc.c
@@ -30,7 +30,7 @@
#include "py/mphal.h"
#include "adc.h"
-#include "driver/adc.h"
+#include "esp_adc/adc_oneshot.h"
#define ADCBLOCK1 (&madcblock_obj[0])
#define ADCBLOCK2 (&madcblock_obj[1])
@@ -126,20 +126,8 @@ static const machine_adc_obj_t madc_obj[] = {
#endif
};
-// These values are initialised to 0, which means the corresponding ADC channel is not initialised.
-// The madc_atten_get/madc_atten_set functions store (atten+1) here so that the uninitialised state
-// can be distinguished from the initialised state.
static uint8_t madc_obj_atten[MP_ARRAY_SIZE(madc_obj)];
-static inline adc_atten_t madc_atten_get(const machine_adc_obj_t *self) {
- uint8_t value = madc_obj_atten[self - &madc_obj[0]];
- return value == 0 ? ADC_ATTEN_MAX : value - 1;
-}
-
-static inline void madc_atten_set(const machine_adc_obj_t *self, adc_atten_t atten) {
- madc_obj_atten[self - &madc_obj[0]] = atten + 1;
-}
-
const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id) {
for (int i = 0; i < MP_ARRAY_SIZE(madc_obj); i++) {
const machine_adc_obj_t *adc = &madc_obj[i];
@@ -152,22 +140,7 @@ const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_
static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
const machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in);
- mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, madc_atten_get(self));
-}
-
-static void madc_atten_helper(const machine_adc_obj_t *self, mp_int_t atten) {
- esp_err_t err = ESP_FAIL;
- if (self->block->unit_id == ADC_UNIT_1) {
- err = adc1_config_channel_atten(self->channel_id, atten);
- } else {
- #if SOC_ADC_PERIPH_NUM >= 2
- err = adc2_config_channel_atten(self->channel_id, atten);
- #endif
- }
- if (err != ESP_OK) {
- mp_raise_ValueError(MP_ERROR_TEXT("invalid atten"));
- }
- madc_atten_set(self, atten);
+ mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, mp_machine_adc_atten_get_helper(self));
}
void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
@@ -182,18 +155,32 @@ void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_pos_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
- mp_int_t atten = args[ARG_atten].u_int;
- if (atten != -1) {
- madc_atten_helper(self, atten);
- } else if (madc_atten_get(self) == ADC_ATTEN_MAX) {
- madc_atten_helper(self, ADC_ATTEN_DB_0);
+
+ if (!self->block->handle) {
+ adc_oneshot_unit_init_cfg_t init_config = {
+ .unit_id = self->block->unit_id
+ };
+ check_esp_err(adc_oneshot_new_unit(&init_config, &self->block->handle));
}
+
+ mp_int_t atten = args[ARG_atten].u_int;
+ mp_machine_adc_atten_set_helper(self, atten != -1 ? atten : ADC_ATTEN_MAX);
+ mp_machine_adc_block_width_set_helper(self->block, ADC_WIDTH_MAX);
+ apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self));
+
}
static void mp_machine_adc_init_helper(machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
madc_init_helper(self, n_pos_args, pos_args, kw_args);
}
+static void mp_machine_adc_deinit(machine_adc_obj_t *self) {
+ if (self->block->handle) {
+ check_esp_err(adc_oneshot_del_unit(self->block->handle));
+ self->block->handle = NULL;
+ }
+}
+
static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_pos_args, size_t n_kw_args, const mp_obj_t *args) {
mp_arg_check_num(n_pos_args, n_kw_args, 1, MP_OBJ_FUN_ARGS_MAX, true);
gpio_num_t gpio_id = machine_pin_get_id(args[0]);
@@ -202,10 +189,6 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_pos_
mp_raise_ValueError(MP_ERROR_TEXT("invalid pin"));
}
- if (self->block->width == -1) {
- madcblock_bits_helper(self->block, self->block->bits);
- }
-
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw_args, args + n_pos_args);
madc_init_helper(self, n_pos_args - 1, args + 1, &kw_args);
@@ -225,20 +208,46 @@ static mp_int_t mp_machine_adc_read(machine_adc_obj_t *self) {
static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) {
mp_uint_t raw = madcblock_read_helper(self->block, self->channel_id);
// Scale raw reading to 16 bit value using a Taylor expansion (for 8 <= bits <= 16)
- mp_int_t bits = self->block->bits;
+ mp_int_t bits = mp_machine_adc_width_get_helper(self);
mp_uint_t u16 = raw << (16 - bits) | raw >> (2 * bits - 16);
return u16;
}
static mp_int_t mp_machine_adc_read_uv(machine_adc_obj_t *self) {
- adc_atten_t atten = madc_atten_get(self);
- return madcblock_read_uv_helper(self->block, self->channel_id, atten);
+ return madcblock_read_uv_helper(self->block, self->channel_id, mp_machine_adc_atten_get_helper(self));
+}
+
+mp_int_t mp_machine_adc_atten_get_helper(const machine_adc_obj_t *self) {
+ uint8_t value = madc_obj_atten[self - &madc_obj[0]];
+ return value == 0 ? ADC_ATTEN_MAX : value - 1;
+}
+
+void mp_machine_adc_atten_set_helper(const machine_adc_obj_t *self, mp_int_t atten) {
+ if (atten < ADC_ATTEN_MIN || atten > ADC_ATTEN_MAX) {
+ mp_raise_ValueError(MP_ERROR_TEXT("invalid attenuation"));
+ }
+
+ madc_obj_atten[self - &madc_obj[0]] = atten + 1;
}
static void mp_machine_adc_atten_set(machine_adc_obj_t *self, mp_int_t atten) {
- madc_atten_helper(self, atten);
+ mp_machine_adc_atten_set_helper(self, atten);
+ apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self));
+}
+
+mp_int_t mp_machine_adc_width_get_helper(const machine_adc_obj_t *self) {
+ return self->block->bitwidth;
+}
+
+void mp_machine_adc_block_width_set_helper(machine_adc_block_obj_t *self, mp_int_t width) {
+ if (width < ADC_WIDTH_MIN || width > ADC_WIDTH_MAX) {
+ mp_raise_ValueError(MP_ERROR_TEXT("invalid bit-width"));
+ }
+
+ self->bitwidth = width;
}
static void mp_machine_adc_width_set(machine_adc_obj_t *self, mp_int_t width) {
- madcblock_bits_helper(self->block, width);
+ mp_machine_adc_block_width_set_helper(self->block, width);
+ apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self));
}
diff --git a/ports/esp32/machine_adc_block.c b/ports/esp32/machine_adc_block.c
index a373603b7e..6b06b432c2 100644
--- a/ports/esp32/machine_adc_block.c
+++ b/ports/esp32/machine_adc_block.c
@@ -29,25 +29,21 @@
#include "py/mphal.h"
#include "adc.h"
-#include "driver/adc.h"
+#include "esp_adc/adc_oneshot.h"
machine_adc_block_obj_t madcblock_obj[] = {
- {{&machine_adc_block_type}, ADC_UNIT_1, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}},
+ {{&machine_adc_block_type}, ADC_UNIT_1, NULL, ADC_WIDTH_MAX, {0}},
#if SOC_ADC_PERIPH_NUM > 1
- {{&machine_adc_block_type}, ADC_UNIT_2, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}},
+ {{&machine_adc_block_type}, ADC_UNIT_2, NULL, ADC_WIDTH_MAX, {0}},
#endif
};
static void mp_machine_adc_block_print(const mp_print_t *print, machine_adc_block_obj_t *self) {
- mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bits);
+ mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bitwidth);
}
static void mp_machine_adc_block_bits_set(machine_adc_block_obj_t *self, mp_int_t bits) {
- if (bits != -1) {
- madcblock_bits_helper(self, bits);
- } else if (self->width == -1) {
- madcblock_bits_helper(self, self->bits);
- }
+ mp_machine_adc_block_width_set_helper(self, bits);
}
static machine_adc_block_obj_t *mp_machine_adc_block_get(mp_int_t unit) {
diff --git a/ports/esp32/machine_i2c.c b/ports/esp32/machine_i2c.c
index 12b86e4aa0..259101ee7e 100644
--- a/ports/esp32/machine_i2c.c
+++ b/ports/esp32/machine_i2c.c
@@ -35,9 +35,14 @@
#if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C
#ifndef MICROPY_HW_I2C0_SCL
+#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
+#define MICROPY_HW_I2C0_SCL (GPIO_NUM_9)
+#define MICROPY_HW_I2C0_SDA (GPIO_NUM_8)
+#else
#define MICROPY_HW_I2C0_SCL (GPIO_NUM_18)
#define MICROPY_HW_I2C0_SDA (GPIO_NUM_19)
#endif
+#endif
#ifndef MICROPY_HW_I2C1_SCL
#if CONFIG_IDF_TARGET_ESP32
diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c
index d0c4ee1a7e..4ab79f0a26 100644
--- a/ports/esp32/machine_pin.c
+++ b/ports/esp32/machine_pin.c
@@ -325,14 +325,17 @@ static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_
mp_raise_ValueError(MP_ERROR_TEXT("bad wake value"));
}
+ #if SOC_TOUCH_SENSOR_SUPPORTED
if (machine_rtc_config.wake_on_touch) { // not compatible
mp_raise_ValueError(MP_ERROR_TEXT("no resources"));
}
+ #endif
if (!RTC_IS_VALID_EXT_PIN(index)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid pin for wake"));
}
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
if (machine_rtc_config.ext0_pin == -1) {
machine_rtc_config.ext0_pin = index;
} else if (machine_rtc_config.ext0_pin != index) {
@@ -341,10 +344,13 @@ static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_
machine_rtc_config.ext0_level = trigger == GPIO_INTR_LOW_LEVEL ? 0 : 1;
machine_rtc_config.ext0_wake_types = wake;
+ #endif
} else {
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
if (machine_rtc_config.ext0_pin == index) {
machine_rtc_config.ext0_pin = -1;
}
+ #endif
if (handler == mp_const_none) {
handler = MP_OBJ_NULL;
diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c
index 7826769556..7cb84640ee 100644
--- a/ports/esp32/machine_pwm.c
+++ b/ports/esp32/machine_pwm.c
@@ -6,7 +6,8 @@
* Copyright (c) 2016-2021 Damien P. George
* Copyright (c) 2018 Alan Dragomirecky
* Copyright (c) 2020 Antoine Aubert
- * Copyright (c) 2021 Ihor Nehrutsa
+ * Copyright (c) 2021, 2023-2025 Ihor Nehrutsa
+ * Copyright (c) 2024 Yoann Darche
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -32,79 +33,41 @@
#include <math.h>
#include "py/mphal.h"
-#include "driver/ledc.h"
#include "esp_err.h"
+#include "driver/ledc.h"
+#include "soc/ledc_periph.h"
#include "soc/gpio_sig_map.h"
-
-#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
#include "esp_clk_tree.h"
-#endif
-
-#define PWM_DBG(...)
-// #define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n");
-
-// Total number of channels
-#define PWM_CHANNEL_MAX (LEDC_SPEED_MODE_MAX * LEDC_CHANNEL_MAX)
-
-typedef struct _chan_t {
- // Which channel has which GPIO pin assigned?
- // (-1 if not assigned)
- gpio_num_t pin;
- // Which channel has which timer assigned?
- // (-1 if not assigned)
- int timer_idx;
-} chan_t;
+#include "py/mpprint.h"
-// List of PWM channels
-static chan_t chans[PWM_CHANNEL_MAX];
+#define debug_printf(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, " | %d at %s\n", __LINE__, __FILE__);
-// channel_idx is an index (end-to-end sequential numbering) for all channels
-// available on the chip and described in chans[]
-#define CHANNEL_IDX(mode, channel) (mode * LEDC_CHANNEL_MAX + channel)
-#define CHANNEL_IDX_TO_MODE(channel_idx) (channel_idx / LEDC_CHANNEL_MAX)
-#define CHANNEL_IDX_TO_CHANNEL(channel_idx) (channel_idx % LEDC_CHANNEL_MAX)
+#define FADE 0
-// Total number of timers
-#define PWM_TIMER_MAX (LEDC_SPEED_MODE_MAX * LEDC_TIMER_MAX)
+// 10-bit user interface resolution compatible with esp8266 PWM.duty()
+#define UI_RES_10_BIT (10)
+#define DUTY_10 UI_RES_10_BIT
+// Maximum duty value on 10-bit resolution is 1024 but reduced to 1023 in UI
+#define MAX_10_DUTY (1U << UI_RES_10_BIT)
-// List of timer configs
-static ledc_timer_config_t timers[PWM_TIMER_MAX];
+// 16-bit user interface resolution used in PWM.duty_u16()
+#define UI_RES_16_BIT (16)
+#define DUTY_16 UI_RES_16_BIT
+// Maximum duty value on 16-bit resolution is 65536 but reduced to 65535 in UI
+#define MAX_16_DUTY (1U << UI_RES_16_BIT)
-// timer_idx is an index (end-to-end sequential numbering) for all timers
-// available on the chip and configured in timers[]
-#define TIMER_IDX(mode, timer) (mode * LEDC_TIMER_MAX + timer)
-#define TIMER_IDX_TO_MODE(timer_idx) (timer_idx / LEDC_TIMER_MAX)
-#define TIMER_IDX_TO_TIMER(timer_idx) (timer_idx % LEDC_TIMER_MAX)
+// ns user interface used in PWM.duty_ns()
+#define DUTY_NS (1)
-// Params for PWM operation
// 5khz is default frequency
-#define PWM_FREQ (5000)
-
-// 10-bit resolution (compatible with esp8266 PWM)
-#define PWM_RES_10_BIT (LEDC_TIMER_10_BIT)
-
-// Maximum duty value on 10-bit resolution
-#define MAX_DUTY_U10 ((1 << PWM_RES_10_BIT) - 1)
-// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html#supported-range-of-frequency-and-duty-resolutions
-// duty() uses 10-bit resolution or less
-// duty_u16() and duty_ns() use 16-bit resolution or less
-
-// Possible highest resolution in device
-#if (LEDC_TIMER_BIT_MAX - 1) < LEDC_TIMER_16_BIT
-#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1)
-#else
-#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used
-#endif
-// Duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer
-#define UI_RES_16_BIT (16)
-// Maximum duty value on highest user interface resolution
-#define UI_MAX_DUTY ((1 << UI_RES_16_BIT) - 1)
-// How much to shift from the HIGHEST_PWM_RES duty resolution to the user interface duty resolution UI_RES_16_BIT
-#define UI_RES_SHIFT (UI_RES_16_BIT - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3
-
-#if SOC_LEDC_SUPPORT_REF_TICK
+#define PWM_FREQ (5000)
+// default duty 50%
+#define PWM_DUTY ((1U << UI_RES_16_BIT) / 2)
+
+// All chips except esp32 and esp32s2 do not have timer-specific clock sources, which means clock source for all timers must be the same one.
+#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
// If the PWM frequency is less than EMPIRIC_FREQ, then LEDC_REF_CLK_HZ(1 MHz) source is used, else LEDC_APB_CLK_HZ(80 MHz) source is used
-#define EMPIRIC_FREQ (10) // Hz
+#define EMPIRIC_FREQ (10) // Hz
#endif
// Config of timer upon which we run all PWM'ed GPIO pins
@@ -113,490 +76,675 @@ static bool pwm_inited = false;
// MicroPython PWM object struct
typedef struct _machine_pwm_obj_t {
mp_obj_base_t base;
- gpio_num_t pin;
- bool active;
- int mode;
- int channel;
- int timer;
- int duty_x; // PWM_RES_10_BIT if duty(), HIGHEST_PWM_RES if duty_u16(), -HIGHEST_PWM_RES if duty_ns()
- int duty_u10; // stored values from previous duty setters
- int duty_u16; // - / -
- int duty_ns; // - / -
+ int8_t pin;
+ int8_t mode;
+ int8_t channel;
+ int8_t timer;
+ bool lightsleep;
+ int32_t freq;
+ int8_t duty_scale; // DUTY_10 if duty(), DUTY_16 if duty_u16(), DUTY_NS if duty_ns()
+ int duty_ui; // saved values of UI duty
+ int channel_duty; // saved values of UI duty, calculated to raw channel->duty
+ bool output_invert;
+ bool output_invert_prev;
} machine_pwm_obj_t;
-static bool is_timer_in_use(int current_channel_idx, int timer_idx);
-static void set_duty_u16(machine_pwm_obj_t *self, int duty);
-static void set_duty_u10(machine_pwm_obj_t *self, int duty);
-static void set_duty_ns(machine_pwm_obj_t *self, int ns);
+typedef struct _chans_t {
+ int8_t pin; // Which channel has which GPIO pin assigned? (-1 if not assigned)
+ int8_t timer; // Which channel has which timer assigned? (-1 if not assigned)
+ bool lightsleep; // Is light sleep enable has been set for this pin
+} chans_t;
+
+// List of PWM channels
+static chans_t chans[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX];
+
+typedef struct _timers_t {
+ int32_t freq;
+ int8_t duty_resolution;
+ ledc_clk_cfg_t clk_cfg;
+} timers_t;
+
+// List of PWM timers
+static timers_t timers[LEDC_SPEED_MODE_MAX][LEDC_TIMER_MAX];
+
+// register-unregister channel
+static void register_channel(int mode, int channel, int pin, int timer) {
+ chans[mode][channel].pin = pin;
+ chans[mode][channel].timer = timer;
+}
+
+static void unregister_channel(int mode, int channel) {
+ register_channel(mode, channel, -1, -1);
+ chans[mode][channel].lightsleep = false;
+}
+
+static void unregister_timer(int mode, int timer) {
+ timers[mode][timer].freq = -1; // unused timer freq is -1
+ timers[mode][timer].duty_resolution = 0;
+ timers[mode][timer].clk_cfg = LEDC_AUTO_CLK;
+}
static void pwm_init(void) {
- // Initial condition: no channels assigned
- for (int i = 0; i < PWM_CHANNEL_MAX; ++i) {
- chans[i].pin = -1;
- chans[i].timer_idx = -1;
+ for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
+ // Initial condition: no channels assigned
+ for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) {
+ unregister_channel(mode, channel);
+ }
+ // Initial condition: no timers assigned
+ for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) {
+ unregister_timer(mode, timer);
+ }
}
+}
- // Prepare all timers config
- // Initial condition: no timers assigned
- for (int i = 0; i < PWM_TIMER_MAX; ++i) {
- timers[i].duty_resolution = HIGHEST_PWM_RES;
- // unset timer is -1
- timers[i].freq_hz = -1;
- timers[i].speed_mode = TIMER_IDX_TO_MODE(i);
- timers[i].timer_num = TIMER_IDX_TO_TIMER(i);
- timers[i].clk_cfg = LEDC_AUTO_CLK; // will reinstall later according to the EMPIRIC_FREQ
+// Returns true if the timer is in use in addition to current channel
+static bool is_timer_in_use(int mode, int current_channel, int timer) {
+ for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) {
+ if ((channel != current_channel) && (chans[mode][channel].timer == timer) && (chans[mode][channel].pin >= 0)) {
+ return true;
+ }
}
+ return false;
}
// Deinit channel and timer if the timer is unused
-static void pwm_deinit(int channel_idx) {
- // Valid channel?
- if ((channel_idx >= 0) && (channel_idx < PWM_CHANNEL_MAX)) {
+static void pwm_deinit(int mode, int channel, int level) {
+ // Is valid channel?
+ if ((mode >= 0) && (mode < LEDC_SPEED_MODE_MAX) && (channel >= 0) && (channel < LEDC_CHANNEL_MAX)) {
// Clean up timer if necessary
- int timer_idx = chans[channel_idx].timer_idx;
- if (timer_idx != -1) {
- if (!is_timer_in_use(channel_idx, timer_idx)) {
- check_esp_err(ledc_timer_pause(TIMER_IDX_TO_MODE(timer_idx), TIMER_IDX_TO_TIMER(timer_idx)));
- ledc_timer_config_t timer_config = {
+ int timer = chans[mode][channel].timer;
+ if (timer >= 0) {
+ if (!is_timer_in_use(mode, channel, timer)) {
+ check_esp_err(ledc_timer_pause(mode, timer));
+ ledc_timer_config_t ledc_timer = {
.deconfigure = true,
- .speed_mode = TIMER_IDX_TO_MODE(timer_idx),
- .timer_num = TIMER_IDX_TO_TIMER(timer_idx),
+ .speed_mode = mode,
+ .timer_num = timer,
};
- check_esp_err(ledc_timer_config(&timer_config));
- // Flag it unused
- timers[chans[channel_idx].timer_idx].freq_hz = -1;
+ ledc_timer_config(&ledc_timer);
+ unregister_timer(mode, timer);
}
}
- int pin = chans[channel_idx].pin;
- if (pin != -1) {
- int mode = CHANNEL_IDX_TO_MODE(channel_idx);
- int channel = CHANNEL_IDX_TO_CHANNEL(channel_idx);
- // Mark it unused, and tell the hardware to stop routing
- check_esp_err(ledc_stop(mode, channel, 0));
- // Disable ledc signal for the pin
- // esp_rom_gpio_connect_out_signal(pin, SIG_GPIO_OUT_IDX, false, false);
- if (mode == LEDC_LOW_SPEED_MODE) {
- esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + channel, false, true);
- } else {
- #if LEDC_SPEED_MODE_MAX > 1
- #if CONFIG_IDF_TARGET_ESP32
- esp_rom_gpio_connect_out_signal(pin, LEDC_HS_SIG_OUT0_IDX + channel, false, true);
- #else
- #error Add supported CONFIG_IDF_TARGET_ESP32_xxx
- #endif
- #endif
+ int pin = chans[mode][channel].pin;
+ if (pin >= 0) {
+ // Disable LEDC output, and set idle level
+ check_esp_err(ledc_stop(mode, channel, level));
+ if (chans[mode][channel].lightsleep) {
+ // Enable SLP_SEL to change GPIO status automantically in lightsleep.
+ check_esp_err(gpio_sleep_sel_en(pin));
+ chans[mode][channel].lightsleep = false;
}
}
- chans[channel_idx].pin = -1;
- chans[channel_idx].timer_idx = -1;
+ unregister_channel(mode, channel);
}
}
// This called from Ctrl-D soft reboot
void machine_pwm_deinit_all(void) {
if (pwm_inited) {
- for (int channel_idx = 0; channel_idx < PWM_CHANNEL_MAX; ++channel_idx) {
- pwm_deinit(channel_idx);
+ for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
+ for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) {
+ pwm_deinit(mode, channel, 0);
+ }
}
+ #if FADE
+ ledc_fade_func_uninstall();
+ #endif
pwm_inited = false;
}
}
-static void configure_channel(machine_pwm_obj_t *self) {
- ledc_channel_config_t cfg = {
- .channel = self->channel,
- .duty = (1 << (timers[TIMER_IDX(self->mode, self->timer)].duty_resolution)) / 2,
- .gpio_num = self->pin,
- .intr_type = LEDC_INTR_DISABLE,
- .speed_mode = self->mode,
- .timer_sel = self->timer,
- };
- if (ledc_channel_config(&cfg) != ESP_OK) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("PWM not supported on Pin(%d)"), self->pin);
- }
-}
-
-static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) {
- esp_err_t err;
- if (freq != timer->freq_hz) {
- // Configure the new frequency and resolution
- timer->freq_hz = freq;
-
- #if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK
- timer->clk_cfg = LEDC_USE_PLL_DIV_CLK;
- #elif SOC_LEDC_SUPPORT_APB_CLOCK
- timer->clk_cfg = LEDC_USE_APB_CLK;
- #elif SOC_LEDC_SUPPORT_XTAL_CLOCK
- timer->clk_cfg = LEDC_USE_XTAL_CLK;
- #else
- #error No supported PWM / LEDC clocks.
- #endif
- #if SOC_LEDC_SUPPORT_REF_TICK
- if (freq < EMPIRIC_FREQ) {
- timer->clk_cfg = LEDC_USE_REF_TICK;
- }
- #endif
- uint32_t src_clk_freq = 0;
- err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_freq);
- if (err != ESP_OK) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unable to query source clock frequency %d"), (int)timer->clk_cfg);
- }
-
- timer->duty_resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer->freq_hz);
-
- // Set frequency
- err = ledc_timer_config(timer);
- if (err != ESP_OK) {
- if (err == ESP_FAIL) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unreachable frequency %d"), freq);
- } else {
- check_esp_err(err);
- }
- }
- // Reset the timer if low speed
- if (self->mode == LEDC_LOW_SPEED_MODE) {
- check_esp_err(ledc_timer_rst(self->mode, self->timer));
- }
- }
-
- // Save the same duty cycle when frequency is changed
- if (self->duty_x == HIGHEST_PWM_RES) {
- set_duty_u16(self, self->duty_u16);
- } else if (self->duty_x == PWM_RES_10_BIT) {
- set_duty_u10(self, self->duty_u10);
- } else if (self->duty_x == -HIGHEST_PWM_RES) {
- set_duty_ns(self, self->duty_ns);
+static void pwm_is_active(machine_pwm_obj_t *self) {
+ if (self->timer < 0) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("PWM is inactive"));
}
}
// Calculate the duty parameters based on an ns value
static int ns_to_duty(machine_pwm_obj_t *self, int ns) {
- ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)];
- int64_t duty = ((int64_t)ns * UI_MAX_DUTY * timer.freq_hz + 500000000LL) / 1000000000LL;
+ pwm_is_active(self);
+ int64_t duty = ((int64_t)ns * (int64_t)MAX_16_DUTY * self->freq + 500000000LL) / 1000000000LL;
if ((ns > 0) && (duty == 0)) {
duty = 1;
- } else if (duty > UI_MAX_DUTY) {
- duty = UI_MAX_DUTY;
+ } else if (duty > MAX_16_DUTY) {
+ duty = MAX_16_DUTY;
}
return duty;
}
static int duty_to_ns(machine_pwm_obj_t *self, int duty) {
- ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)];
- int64_t ns = ((int64_t)duty * 1000000000LL + (int64_t)timer.freq_hz * UI_MAX_DUTY / 2) / ((int64_t)timer.freq_hz * UI_MAX_DUTY);
- return ns;
+ pwm_is_active(self);
+ return ((int64_t)duty * 1000000000LL + (int64_t)self->freq * (int64_t)(MAX_16_DUTY / 2)) / ((int64_t)self->freq * (int64_t)MAX_16_DUTY);
}
-#define get_duty_raw(self) ledc_get_duty(self->mode, self->channel)
+// Reconfigure PWM pin output as input/output.
+static void reconfigure_pin(machine_pwm_obj_t *self) {
+ #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0)
+ // This allows to read the pin level.
+ gpio_set_direction(self->pin, GPIO_MODE_INPUT_OUTPUT);
+ #endif
+ esp_rom_gpio_connect_out_signal(self->pin, ledc_periph_signal[self->mode].sig_out0_idx + self->channel, self->output_invert, 0);
+}
-static void pwm_is_active(machine_pwm_obj_t *self) {
- if (self->active == false) {
- mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("PWM inactive"));
+static void apply_duty(machine_pwm_obj_t *self) {
+ pwm_is_active(self);
+
+ int duty = 0;
+ if (self->duty_scale == DUTY_16) {
+ duty = self->duty_ui;
+ } else if (self->duty_scale == DUTY_10) {
+ duty = self->duty_ui << (UI_RES_16_BIT - UI_RES_10_BIT);
+ } else if (self->duty_scale == DUTY_NS) {
+ duty = ns_to_duty(self, self->duty_ui);
+ }
+ self->channel_duty = duty >> (UI_RES_16_BIT - timers[self->mode][self->timer].duty_resolution);
+ if ((chans[self->mode][self->channel].pin == -1) || (self->output_invert_prev != self->output_invert)) {
+ self->output_invert_prev = self->output_invert;
+ // New PWM assignment
+ ledc_channel_config_t cfg = {
+ .channel = self->channel,
+ .duty = self->channel_duty,
+ .gpio_num = self->pin,
+ .intr_type = LEDC_INTR_DISABLE,
+ .speed_mode = self->mode,
+ .timer_sel = self->timer,
+ .hpoint = 0,
+ .flags.output_invert = self->output_invert,
+ };
+ check_esp_err(ledc_channel_config(&cfg));
+ reconfigure_pin(self);
+ } else {
+ #if FADE
+ check_esp_err(ledc_set_duty_and_update(self->mode, self->channel, self->channel_duty, 0));
+ #else
+ check_esp_err(ledc_set_duty(self->mode, self->channel, self->channel_duty));
+ check_esp_err(ledc_update_duty(self->mode, self->channel));
+ #endif
+ }
+ if (self->lightsleep) {
+ // Disable SLP_SEL to change GPIO status automantically in lightsleep.
+ check_esp_err(gpio_sleep_sel_dis(self->pin));
+ chans[self->mode][self->channel].lightsleep = true;
}
+ register_channel(self->mode, self->channel, self->pin, self->timer);
+}
+
+static uint32_t find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) {
+ unsigned int resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer_freq);
+ if (resolution > UI_RES_16_BIT) {
+ // limit resolution to user interface
+ resolution = UI_RES_16_BIT;
+ }
+ // Note: On ESP32, ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32C6, ESP32H2, ESP32P4, due to a hardware bug,
+ // 100% duty cycle (i.e. 2**duty_res) is not reachable when the binded timer selects the maximum duty
+ // resolution. For example, the max duty resolution on ESP32C3 is 14-bit width, then set duty to (2**14)
+ // will mess up the duty calculation in hardware.
+ // Reduce the resolution from 14 to 13 bits to resolve the hardware bug.
+ if (resolution >= SOC_LEDC_TIMER_BIT_WIDTH) {
+ resolution -= 1;
+ }
+ return resolution;
}
static uint32_t get_duty_u16(machine_pwm_obj_t *self) {
pwm_is_active(self);
- int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution;
- int duty = ledc_get_duty(self->mode, self->channel);
- if (resolution <= UI_RES_16_BIT) {
- duty <<= (UI_RES_16_BIT - resolution);
+ int duty = ledc_get_duty(self->mode, self->channel) << (UI_RES_16_BIT - timers[self->mode][self->timer].duty_resolution);
+ if (duty != MAX_16_DUTY) {
+ return duty;
} else {
- duty >>= (resolution - UI_RES_16_BIT);
+ return MAX_16_DUTY - 1;
}
- return duty;
}
static uint32_t get_duty_u10(machine_pwm_obj_t *self) {
- pwm_is_active(self);
- return get_duty_u16(self) >> 6; // Scale down from 16 bit to 10 bit resolution
+ // Scale down from 16 bit to 10 bit resolution
+ return get_duty_u16(self) >> (UI_RES_16_BIT - UI_RES_10_BIT);
}
static uint32_t get_duty_ns(machine_pwm_obj_t *self) {
- pwm_is_active(self);
return duty_to_ns(self, get_duty_u16(self));
}
-static void set_duty_u16(machine_pwm_obj_t *self, int duty) {
- pwm_is_active(self);
- if ((duty < 0) || (duty > UI_MAX_DUTY)) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), UI_MAX_DUTY);
+static void check_duty_u16(machine_pwm_obj_t *self, int duty) {
+ if ((duty < 0) || (duty > MAX_16_DUTY - 1)) {
+ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), MAX_16_DUTY);
}
- ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)];
- int channel_duty;
- if (timer.duty_resolution <= UI_RES_16_BIT) {
- channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution);
- } else {
- channel_duty = duty << (timer.duty_resolution - UI_RES_16_BIT);
+ if (duty == MAX_16_DUTY - 1) {
+ duty = MAX_16_DUTY;
}
- int max_duty = (1 << timer.duty_resolution) - 1;
- if (channel_duty < 0) {
- channel_duty = 0;
- } else if (channel_duty > max_duty) {
- channel_duty = max_duty;
- }
- check_esp_err(ledc_set_duty(self->mode, self->channel, channel_duty));
- check_esp_err(ledc_update_duty(self->mode, self->channel));
+ self->duty_scale = DUTY_16;
+ self->duty_ui = duty;
+}
- /*
- // Bug: Sometimes duty is not set right now.
- // Not a bug. It's a feature. The duty is applied at the beginning of the next signal period.
- // Bug: It has been experimentally established that the duty is set during 2 signal periods, but 1 period is expected.
- // See https://github.com/espressif/esp-idf/issues/7288
- if (duty != get_duty_u16(self)) {
- PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz);
- esp_rom_delay_us(2 * 1000000 / timer.freq_hz);
- if (duty != get_duty_u16(self)) {
- PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz);
- }
- }
- */
+static void set_duty_u16(machine_pwm_obj_t *self, int duty) {
+ check_duty_u16(self, duty);
+ apply_duty(self);
+}
- self->duty_x = HIGHEST_PWM_RES;
- self->duty_u16 = duty;
+static void check_duty_u10(machine_pwm_obj_t *self, int duty) {
+ if ((duty < 0) || (duty > MAX_10_DUTY - 1)) {
+ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_10_DUTY - 1);
+ }
+ if (duty == MAX_10_DUTY - 1) {
+ duty = MAX_10_DUTY;
+ }
+ self->duty_scale = DUTY_10;
+ self->duty_ui = duty;
}
static void set_duty_u10(machine_pwm_obj_t *self, int duty) {
- pwm_is_active(self);
- if ((duty < 0) || (duty > MAX_DUTY_U10)) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_DUTY_U10);
+ check_duty_u10(self, duty);
+ apply_duty(self);
+}
+
+static void check_duty_ns(machine_pwm_obj_t *self, int ns) {
+ if ((ns < 0) || (ns > duty_to_ns(self, MAX_16_DUTY))) {
+ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, MAX_16_DUTY));
}
- set_duty_u16(self, duty << (UI_RES_16_BIT - PWM_RES_10_BIT));
- self->duty_x = PWM_RES_10_BIT;
- self->duty_u10 = duty;
+ self->duty_scale = DUTY_NS;
+ self->duty_ui = ns;
}
static void set_duty_ns(machine_pwm_obj_t *self, int ns) {
- pwm_is_active(self);
- if ((ns < 0) || (ns > duty_to_ns(self, UI_MAX_DUTY))) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, UI_MAX_DUTY));
+ check_duty_ns(self, ns);
+ apply_duty(self);
+}
+
+#if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2)
+// This check if a clock is already set in the timer list, if yes, return the LEDC_XXX value
+static ledc_clk_cfg_t find_clock_in_use() {
+ for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
+ for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) {
+ if (timers[mode][timer].clk_cfg != LEDC_AUTO_CLK) {
+ return timers[mode][timer].clk_cfg;
+ }
+ }
}
- set_duty_u16(self, ns_to_duty(self, ns));
- self->duty_x = -HIGHEST_PWM_RES;
- self->duty_ns = ns;
+ return LEDC_AUTO_CLK;
}
-/******************************************************************************/
+// Check if a timer is already set with a different clock source
+static bool is_timer_with_different_clock(int mode, int current_timer, ledc_clk_cfg_t clk_cfg) {
+ for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) {
+ if ((timer != current_timer) && (clk_cfg != LEDC_AUTO_CLK) && (timers[mode][timer].clk_cfg != LEDC_AUTO_CLK) && (timers[mode][timer].clk_cfg != clk_cfg)) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
-#define SAME_FREQ_ONLY (true)
-#define SAME_FREQ_OR_FREE (false)
-#define ANY_MODE (-1)
+static void check_freq_ranges(machine_pwm_obj_t *self, int freq, int upper_freq) {
+ if ((freq <= 0) || (freq > upper_freq)) {
+ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("frequency must be from 1Hz to %dMHz"), upper_freq / 1000000);
+ }
+}
-// Return timer_idx. Use TIMER_IDX_TO_MODE(timer_idx) and TIMER_IDX_TO_TIMER(timer_idx) to get mode and timer
-static int find_timer(unsigned int freq, bool same_freq_only, int mode) {
- int free_timer_idx_found = -1;
- // Find a free PWM Timer using the same freq
- for (int timer_idx = 0; timer_idx < PWM_TIMER_MAX; ++timer_idx) {
- if ((mode == ANY_MODE) || (mode == TIMER_IDX_TO_MODE(timer_idx))) {
- if (timers[timer_idx].freq_hz == freq) {
- // A timer already uses the same freq. Use it now.
- return timer_idx;
- }
- if (!same_freq_only && (free_timer_idx_found == -1) && (timers[timer_idx].freq_hz == -1)) {
- free_timer_idx_found = timer_idx;
- // Continue to check if a channel with the same freq is in use.
+// Set timer frequency
+static void set_freq(machine_pwm_obj_t *self, unsigned int freq) {
+ self->freq = freq;
+ if ((timers[self->mode][self->timer].freq != freq) || (self->lightsleep)) {
+ ledc_timer_config_t timer = {};
+ timer.speed_mode = self->mode;
+ timer.timer_num = self->timer;
+ timer.freq_hz = freq;
+ timer.deconfigure = false;
+
+ timer.clk_cfg = LEDC_AUTO_CLK;
+ #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2)
+ ledc_clk_cfg_t clk_cfg = find_clock_in_use();
+ if (clk_cfg != LEDC_AUTO_CLK) {
+ timer.clk_cfg = clk_cfg;
+ } else
+ #endif
+ if (self->lightsleep) {
+ timer.clk_cfg = LEDC_USE_RC_FAST_CLK; // 8 or 20 MHz
+ } else {
+ #if SOC_LEDC_SUPPORT_APB_CLOCK
+ timer.clk_cfg = LEDC_USE_APB_CLK; // 80 MHz
+ #elif SOC_LEDC_SUPPORT_PLL_DIV_CLOCK
+ timer.clk_cfg = LEDC_USE_PLL_DIV_CLK; // 60 or 80 or 96 MHz
+ #elif SOC_LEDC_SUPPORT_XTAL_CLOCK
+ timer.clk_cfg = LEDC_USE_XTAL_CLK; // 40 MHz
+ #else
+ #error No supported PWM / LEDC clocks.
+ #endif
+
+ #ifdef EMPIRIC_FREQ // ESP32 and ESP32S2 only
+ if (freq < EMPIRIC_FREQ) {
+ timer.clk_cfg = LEDC_USE_REF_TICK; // 1 MHz
}
+ #endif
}
- }
+ #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2)
+ // Check for clock source conflict
+ clk_cfg = find_clock_in_use();
+ if ((clk_cfg != LEDC_AUTO_CLK) && (clk_cfg != timer.clk_cfg)) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("one or more active timers use a different clock source, not supported by the current SoC."));
+ }
+ #endif
- return free_timer_idx_found;
+ uint32_t src_clk_freq = 0;
+ check_esp_err(esp_clk_tree_src_get_freq_hz(timer.clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &src_clk_freq));
+ // machine.freq(20_000_000) reduces APB_CLK_FREQ to 20MHz and the highest PWM frequency to 10MHz
+ check_freq_ranges(self, freq, src_clk_freq / 2);
+
+ // Configure the new resolution
+ timer.duty_resolution = find_suitable_duty_resolution(src_clk_freq, self->freq);
+ // Configure timer - Set frequency
+ if ((timers[self->mode][self->timer].duty_resolution == timer.duty_resolution) && (timers[self->mode][self->timer].clk_cfg == timer.clk_cfg)) {
+ check_esp_err(ledc_set_freq(self->mode, self->timer, freq));
+ } else {
+ check_esp_err(ledc_timer_config(&timer));
+ }
+ // Reset the timer if low speed
+ if (self->mode == LEDC_LOW_SPEED_MODE) {
+ check_esp_err(ledc_timer_rst(self->mode, self->timer));
+ }
+ timers[self->mode][self->timer].freq = freq;
+ timers[self->mode][self->timer].duty_resolution = timer.duty_resolution;
+ timers[self->mode][self->timer].clk_cfg = timer.clk_cfg;
+ }
}
-// Return true if the timer is in use in addition to current channel
-static bool is_timer_in_use(int current_channel_idx, int timer_idx) {
- for (int i = 0; i < PWM_CHANNEL_MAX; ++i) {
- if ((i != current_channel_idx) && (chans[i].timer_idx == timer_idx)) {
+static bool is_free_channels(int mode, int pin) {
+ for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) {
+ if ((chans[mode][channel].pin < 0) || (chans[mode][channel].pin == pin)) {
return true;
}
}
+ return false;
+}
+static bool is_free_timers(int mode, int32_t freq) {
+ for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) {
+ if ((timers[mode][timer].freq < 0) || (timers[mode][timer].freq == freq)) {
+ return true;
+ }
+ }
return false;
}
-// Find a free PWM channel, also spot if our pin is already mentioned.
-// Return channel_idx. Use CHANNEL_IDX_TO_MODE(channel_idx) and CHANNEL_IDX_TO_CHANNEL(channel_idx) to get mode and channel
-static int find_channel(int pin, int mode) {
- int avail_idx = -1;
- int channel_idx;
- for (channel_idx = 0; channel_idx < PWM_CHANNEL_MAX; ++channel_idx) {
- if ((mode == ANY_MODE) || (mode == CHANNEL_IDX_TO_MODE(channel_idx))) {
- if (chans[channel_idx].pin == pin) {
- break;
+// Find self channel or free channel
+static void find_channel(machine_pwm_obj_t *self, int *ret_mode, int *ret_channel, int32_t freq) {
+ // Try to find self channel first
+ for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
+ #if SOC_LEDC_SUPPORT_HS_MODE
+ if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) {
+ continue;
+ }
+ #endif
+ for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) {
+ if (chans[mode][channel].pin == self->pin) {
+ *ret_mode = mode;
+ *ret_channel = channel;
+ return;
}
- if ((avail_idx == -1) && (chans[channel_idx].pin == -1)) {
- avail_idx = channel_idx;
+ }
+ }
+ // Find free channel
+ for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
+ #if SOC_LEDC_SUPPORT_HS_MODE
+ if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) {
+ continue;
+ }
+ #endif
+ for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) {
+ if ((chans[mode][channel].pin < 0) && is_free_timers(mode, freq)) {
+ *ret_mode = mode;
+ *ret_channel = channel;
+ return;
}
}
}
- if (channel_idx >= PWM_CHANNEL_MAX) {
- channel_idx = avail_idx;
+}
+
+// Returns timer with the same frequency, freq == -1 means free timer
+static void find_timer(machine_pwm_obj_t *self, int freq, int *ret_mode, int *ret_timer) {
+ for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
+ #if SOC_LEDC_SUPPORT_HS_MODE
+ if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) {
+ continue;
+ }
+ #endif
+ if (is_free_channels(mode, self->pin)) {
+ for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) {
+ if (timers[mode][timer].freq == freq) {
+ *ret_mode = mode;
+ *ret_timer = timer;
+ return;
+ }
+ }
+ }
}
- return channel_idx;
}
-/******************************************************************************/
+// Try to find a timer with the same frequency in the current mode, otherwise in the next mode.
+// If no existing timer and channel was found, then try to find free timer in any mode.
+// If the mode or channel is changed, release the channel and register a new channel in the next mode.
+static void select_timer(machine_pwm_obj_t *self, int freq) {
+ // mode, channel, timer may be -1(not defined) or actual values
+ int mode = -1;
+ int timer = -1;
+ // Check if an already running timer with the required frequency is running
+ find_timer(self, freq, &mode, &timer);
+ if (timer < 0) {
+ // Try to reuse self timer
+ if ((self->mode >= 0) && (self->channel >= 0)) {
+ if (!is_timer_in_use(self->mode, self->channel, self->timer)) {
+ mode = self->mode;
+ timer = self->timer;
+ }
+ }
+ // If no existing timer and channel was found, then try to find free timer in any mode
+ if (timer < 0) {
+ find_timer(self, -1, &mode, &timer);
+ }
+ }
+ if (timer < 0) {
+ mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("out of %sPWM timers:%d"), self->lightsleep ? "light sleep capable " : "", self->lightsleep ? LEDC_TIMER_MAX : LEDC_SPEED_MODE_MAX *LEDC_TIMER_MAX);
+ }
+ // If the timer is found, then register
+ if (self->timer != timer) {
+ unregister_channel(self->mode, self->channel);
+ // Rregister the channel to the timer
+ self->mode = mode;
+ self->timer = timer;
+ register_channel(self->mode, self->channel, -1, self->timer);
+ }
+ #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2)
+ if (is_timer_with_different_clock(self->mode, self->timer, timers[self->mode][self->timer].clk_cfg)) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("one or more active timers use a different clock source, not supported by the current SoC."));
+ }
+ #endif
+}
+
+static void set_freq_duty(machine_pwm_obj_t *self, unsigned int freq) {
+ select_timer(self, freq);
+ set_freq(self, freq);
+ apply_duty(self);
+}
+
+// ******************************************************************************
// MicroPython bindings for PWM
static void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "PWM(Pin(%u)", self->pin);
- if (self->active) {
+ if (self->timer >= 0) {
mp_printf(print, ", freq=%u", ledc_get_freq(self->mode, self->timer));
-
- if (self->duty_x == PWM_RES_10_BIT) {
+ if (self->duty_scale == DUTY_10) {
mp_printf(print, ", duty=%d", get_duty_u10(self));
- } else if (self->duty_x == -HIGHEST_PWM_RES) {
+ } else if (self->duty_scale == DUTY_NS) {
mp_printf(print, ", duty_ns=%d", get_duty_ns(self));
} else {
mp_printf(print, ", duty_u16=%d", get_duty_u16(self));
}
- int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution;
- mp_printf(print, ", resolution=%d", resolution);
-
- mp_printf(print, ", (duty=%.2f%%, resolution=%.3f%%)", 100.0 * get_duty_raw(self) / (1 << resolution), 100.0 * 1 / (1 << resolution)); // percents
-
- mp_printf(print, ", mode=%d, channel=%d, timer=%d", self->mode, self->channel, self->timer);
+ if (self->output_invert) {
+ mp_printf(print, ", invert=True");
+ }
+ if (self->lightsleep) {
+ mp_printf(print, ", lightsleep=True");
+ }
+ mp_printf(print, ")");
+
+ #if MICROPY_ERROR_REPORTING > MICROPY_ERROR_REPORTING_NORMAL
+ mp_printf(print, " # duty=%.2f%%", 100.0 * get_duty_u16(self) / MAX_16_DUTY);
+ mp_printf(print, ", raw_duty=%d, resolution=%d", ledc_get_duty(self->mode, self->channel), timers[self->mode][self->timer].duty_resolution);
+ mp_printf(print, ", mode=%d, timer=%d, channel=%d", self->mode, self->timer, self->channel);
+ int clk_cfg = timers[self->mode][self->timer].clk_cfg;
+ mp_printf(print, ", clk_cfg=%d=", clk_cfg);
+ if (clk_cfg == LEDC_USE_RC_FAST_CLK) {
+ mp_printf(print, "RC_FAST_CLK");
+ }
+ #if SOC_LEDC_SUPPORT_APB_CLOCK
+ else if (clk_cfg == LEDC_USE_APB_CLK) {
+ mp_printf(print, "APB_CLK");
+ }
+ #endif
+ #if SOC_LEDC_SUPPORT_XTAL_CLOCK
+ else if (clk_cfg == LEDC_USE_XTAL_CLK) {
+ mp_printf(print, "XTAL_CLK");
+ }
+ #endif
+ #if SOC_LEDC_SUPPORT_REF_TICK
+ else if (clk_cfg == LEDC_USE_REF_TICK) {
+ mp_printf(print, "REF_TICK");
+ }
+ #endif
+ #if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK
+ else if (clk_cfg == LEDC_USE_PLL_DIV_CLK) {
+ mp_printf(print, "PLL_CLK");
+ }
+ #endif
+ else if (clk_cfg == LEDC_AUTO_CLK) {
+ mp_printf(print, "AUTO_CLK");
+ } else {
+ mp_printf(print, "UNKNOWN");
+ }
+ #endif
+ } else {
+ mp_printf(print, ")");
}
- mp_printf(print, ")");
}
// This called from pwm.init() method
+//
+// Check the current mode.
+// If the frequency is changed, try to find a timer with the same frequency
+// in the current mode, otherwise in the new mode.
+// If the mode is changed, release the channel and select a new channel in the new mode.
+// Then set the frequency with the same duty.
static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self,
size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
- enum { ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns };
- static const mp_arg_t allowed_args[] = {
+
+ enum { ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_lightsleep };
+ mp_arg_t allowed_args[] = {
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_duty, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_duty_u16, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_duty_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
+ { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = self->output_invert} },
+ { MP_QSTR_lightsleep, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = self->lightsleep} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args,
MP_ARRAY_SIZE(allowed_args), allowed_args, args);
- int channel_idx = find_channel(self->pin, ANY_MODE);
- if (channel_idx == -1) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), PWM_CHANNEL_MAX); // in all modes
+ self->lightsleep = args[ARG_lightsleep].u_bool;
+
+ int freq = args[ARG_freq].u_int;
+ if (freq != -1) {
+ check_freq_ranges(self, freq, 40000000);
}
int duty = args[ARG_duty].u_int;
int duty_u16 = args[ARG_duty_u16].u_int;
int duty_ns = args[ARG_duty_ns].u_int;
- if (((duty != -1) && (duty_u16 != -1)) || ((duty != -1) && (duty_ns != -1)) || ((duty_u16 != -1) && (duty_ns != -1))) {
- mp_raise_ValueError(MP_ERROR_TEXT("only one of parameters 'duty', 'duty_u16' or 'duty_ns' is allowed"));
+ if (duty_u16 >= 0) {
+ check_duty_u16(self, duty_u16);
+ } else if (duty_ns >= 0) {
+ check_duty_ns(self, duty_ns);
+ } else if (duty >= 0) {
+ check_duty_u10(self, duty);
+ } else if (self->duty_scale == 0) {
+ self->duty_scale = DUTY_16;
+ self->duty_ui = PWM_DUTY;
+ }
+
+ self->output_invert = args[ARG_invert].u_bool;
+
+ // Check the current mode and channel
+ int mode = -1;
+ int channel = -1;
+ find_channel(self, &mode, &channel, freq);
+ if (channel < 0) {
+ mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("out of %sPWM channels:%d"), self->lightsleep ? "light sleep capable " : "", self->lightsleep ? LEDC_CHANNEL_MAX : LEDC_SPEED_MODE_MAX *LEDC_CHANNEL_MAX);
}
+ self->mode = mode;
+ self->channel = channel;
- int freq = args[ARG_freq].u_int;
// Check if freq wasn't passed as an argument
- if (freq == -1) {
+ if ((freq == -1) && (mode >= 0) && (channel >= 0)) {
// Check if already set, otherwise use the default freq.
// It is possible in case:
// pwm = PWM(pin, freq=1000, duty=256)
// pwm = PWM(pin, duty=128)
- if (chans[channel_idx].timer_idx != -1) {
- freq = timers[chans[channel_idx].timer_idx].freq_hz;
+ if (chans[mode][channel].timer >= 0) {
+ freq = timers[mode][chans[mode][channel].timer].freq;
}
if (freq <= 0) {
freq = PWM_FREQ;
}
}
- if ((freq <= 0) || (freq > 40000000)) {
- mp_raise_ValueError(MP_ERROR_TEXT("frequency must be from 1Hz to 40MHz"));
- }
-
- int timer_idx;
- int current_timer_idx = chans[channel_idx].timer_idx;
- bool current_in_use = is_timer_in_use(channel_idx, current_timer_idx);
- if (current_in_use) {
- timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, CHANNEL_IDX_TO_MODE(channel_idx));
- } else {
- timer_idx = chans[channel_idx].timer_idx;
- }
-
- if (timer_idx == -1) {
- timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, ANY_MODE);
- }
- if (timer_idx == -1) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), PWM_TIMER_MAX); // in all modes
- }
-
- int mode = TIMER_IDX_TO_MODE(timer_idx);
- if (CHANNEL_IDX_TO_MODE(channel_idx) != mode) {
- // unregister old channel
- chans[channel_idx].pin = -1;
- chans[channel_idx].timer_idx = -1;
- // find new channel
- channel_idx = find_channel(self->pin, mode);
- if (CHANNEL_IDX_TO_MODE(channel_idx) != mode) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), PWM_CHANNEL_MAX); // in current mode
- }
- }
- self->mode = mode;
- self->timer = TIMER_IDX_TO_TIMER(timer_idx);
- self->channel = CHANNEL_IDX_TO_CHANNEL(channel_idx);
-
- // New PWM assignment
- if ((chans[channel_idx].pin == -1) || (chans[channel_idx].timer_idx != timer_idx)) {
- configure_channel(self);
- chans[channel_idx].pin = self->pin;
- }
- chans[channel_idx].timer_idx = timer_idx;
- self->active = true;
-
- // Set timer frequency
- set_freq(self, freq, &timers[timer_idx]);
+ set_freq_duty(self, freq);
+}
- // Set duty cycle?
- if (duty_u16 != -1) {
- set_duty_u16(self, duty_u16);
- } else if (duty_ns != -1) {
- set_duty_ns(self, duty_ns);
- } else if (duty != -1) {
- set_duty_u10(self, duty);
- } else if (self->duty_x == 0) {
- set_duty_u10(self, (1 << PWM_RES_10_BIT) / 2); // 50%
- }
+static void self_reset(machine_pwm_obj_t *self) {
+ self->mode = -1;
+ self->channel = -1;
+ self->timer = -1;
+ self->freq = -1;
+ self->duty_scale = 0;
+ self->duty_ui = 0;
+ self->channel_duty = -1;
+ self->output_invert = false;
+ self->output_invert_prev = false;
+ self->lightsleep = false;
}
// This called from PWM() constructor
static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type,
size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 2, true);
- gpio_num_t pin_id = machine_pin_get_id(args[0]);
-
- // create PWM object from the given pin
- machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type);
- self->pin = pin_id;
- self->active = false;
- self->mode = -1;
- self->channel = -1;
- self->timer = -1;
- self->duty_x = 0;
// start the PWM subsystem if it's not already running
if (!pwm_inited) {
pwm_init();
+ #if FADE
+ ledc_fade_func_install(0);
+ #endif
pwm_inited = true;
}
- // start the PWM running for this channel
+ // create PWM object from the given pin
+ machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type);
+ self_reset(self);
+
+ self->pin = machine_pin_get_id(args[0]);
+
+ // Process the remaining parameters.
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
mp_machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args);
-
return MP_OBJ_FROM_PTR(self);
}
// This called from pwm.deinit() method
static void mp_machine_pwm_deinit(machine_pwm_obj_t *self) {
- int channel_idx = CHANNEL_IDX(self->mode, self->channel);
- pwm_deinit(channel_idx);
- self->active = false;
- self->mode = -1;
- self->channel = -1;
- self->timer = -1;
- self->duty_x = 0;
+ pwm_deinit(self->mode, self->channel, self->output_invert);
+ self_reset(self);
}
// Set and get methods of PWM class
@@ -608,57 +756,11 @@ static mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) {
static void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) {
pwm_is_active(self);
- if ((freq <= 0) || (freq > 40000000)) {
- mp_raise_ValueError(MP_ERROR_TEXT("frequency must be from 1Hz to 40MHz"));
- }
- if (freq == timers[TIMER_IDX(self->mode, self->timer)].freq_hz) {
+ check_freq_ranges(self, freq, 40000000);
+ if (freq == timers[self->mode][self->timer].freq) {
return;
}
-
- int current_timer_idx = chans[CHANNEL_IDX(self->mode, self->channel)].timer_idx;
- bool current_in_use = is_timer_in_use(CHANNEL_IDX(self->mode, self->channel), current_timer_idx);
-
- // Check if an already running timer with the same freq is running
- int new_timer_idx = find_timer(freq, SAME_FREQ_ONLY, self->mode);
-
- // If no existing timer was found, and the current one is in use, then find a new one
- if ((new_timer_idx == -1) && current_in_use) {
- // Have to find a new timer
- new_timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, self->mode);
-
- if (new_timer_idx == -1) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), PWM_TIMER_MAX); // in current mode
- }
- }
-
- if ((new_timer_idx != -1) && (new_timer_idx != current_timer_idx)) {
- // Bind the channel to the new timer
- chans[self->channel].timer_idx = new_timer_idx;
-
- if (ledc_bind_channel_timer(self->mode, self->channel, TIMER_IDX_TO_TIMER(new_timer_idx)) != ESP_OK) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("failed to bind timer to channel"));
- }
-
- if (!current_in_use) {
- // Free the old timer
- check_esp_err(ledc_timer_pause(self->mode, self->timer));
- ledc_timer_config_t timer_config = {
- .deconfigure = true,
- .speed_mode = self->mode,
- .timer_num = self->timer,
- };
- check_esp_err(ledc_timer_config(&timer_config));
- // Flag it unused
- timers[current_timer_idx].freq_hz = -1;
- }
-
- current_timer_idx = new_timer_idx;
- }
- self->mode = TIMER_IDX_TO_MODE(current_timer_idx);
- self->timer = TIMER_IDX_TO_TIMER(current_timer_idx);
-
- // Set the frequency
- set_freq(self, freq, &timers[current_timer_idx]);
+ set_freq_duty(self, freq);
}
static mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) {
diff --git a/ports/esp32/machine_rtc.c b/ports/esp32/machine_rtc.c
index a2f4bb132d..ee6b1ad5cb 100644
--- a/ports/esp32/machine_rtc.c
+++ b/ports/esp32/machine_rtc.c
@@ -82,8 +82,12 @@ _USER_MEM_ATTR uint8_t rtc_user_mem_data[MICROPY_HW_RTC_USER_MEM_MAX];
static const machine_rtc_obj_t machine_rtc_obj = {{&machine_rtc_type}};
machine_rtc_config_t machine_rtc_config = {
+ #if SOC_PM_SUPPORT_EXT1_WAKEUP
.ext1_pins = 0,
+ #endif
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
.ext0_pin = -1
+ #endif
};
static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
diff --git a/ports/esp32/machine_rtc.h b/ports/esp32/machine_rtc.h
index ce2a5482a2..72717c5936 100644
--- a/ports/esp32/machine_rtc.h
+++ b/ports/esp32/machine_rtc.h
@@ -31,13 +31,25 @@
#include "modmachine.h"
typedef struct {
+ #if SOC_PM_SUPPORT_EXT1_WAKEUP
uint64_t ext1_pins; // set bit == pin#
+ #endif
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
int8_t ext0_pin; // just the pin#, -1 == None
+ #endif
+ #if SOC_TOUCH_SENSOR_SUPPORTED
bool wake_on_touch : 1;
+ #endif
+ #if SOC_ULP_SUPPORTED
bool wake_on_ulp : 1;
+ #endif
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
bool ext0_level : 1;
wake_type_t ext0_wake_types;
+ #endif
+ #if SOC_PM_SUPPORT_EXT1_WAKEUP
bool ext1_level : 1;
+ #endif
} machine_rtc_config_t;
extern machine_rtc_config_t machine_rtc_config;
diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c
index 82b432bbac..a104288f6e 100644
--- a/ports/esp32/machine_timer.c
+++ b/ports/esp32/machine_timer.c
@@ -50,12 +50,13 @@
const mp_obj_type_t machine_timer_type;
static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
+static mp_obj_t machine_timer_deinit(mp_obj_t self_in);
void machine_timer_deinit_all(void) {
// Disable, deallocate and remove all timers from list
machine_timer_obj_t **t = &MP_STATE_PORT(machine_timer_obj_head);
while (*t != NULL) {
- machine_timer_disable(*t);
+ machine_timer_deinit(*t);
machine_timer_obj_t *next = (*t)->next;
m_del_obj(machine_timer_obj_t, *t);
*t = next;
@@ -96,6 +97,7 @@ machine_timer_obj_t *machine_timer_create(mp_uint_t timer) {
self = mp_obj_malloc(machine_timer_obj_t, &machine_timer_type);
self->group = group;
self->index = index;
+ self->handle = NULL;
// Add the timer to the linked-list of timers
self->next = MP_STATE_PORT(machine_timer_obj_head);
@@ -131,9 +133,8 @@ void machine_timer_disable(machine_timer_obj_t *self) {
}
if (self->handle) {
- // Free the interrupt handler.
- esp_intr_free(self->handle);
- self->handle = NULL;
+ // Disable the interrupt
+ ESP_ERROR_CHECK(esp_intr_disable(self->handle));
}
// We let the disabled timer stay in the list, as it might be
@@ -150,12 +151,16 @@ static void machine_timer_isr(void *self_in) {
if (self->repeat) {
timer_ll_enable_alarm(self->hal_context.dev, self->index, true);
}
- mp_sched_schedule(self->callback, self);
- mp_hal_wake_main_task_from_isr();
+ self->handler(self);
}
}
-void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) {
+static void machine_timer_isr_handler(machine_timer_obj_t *self) {
+ mp_sched_schedule(self->callback, self);
+ mp_hal_wake_main_task_from_isr();
+}
+
+void machine_timer_enable(machine_timer_obj_t *self) {
// Initialise the timer.
timer_hal_init(&self->hal_context, self->group, self->index);
timer_ll_enable_counter(self->hal_context.dev, self->index, false);
@@ -167,10 +172,17 @@ void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) {
// Allocate and enable the alarm interrupt.
timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), false);
timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index));
- ESP_ERROR_CHECK(
- esp_intr_alloc(timer_group_periph_signals.groups[self->group].timer_irq_id[self->index],
- TIMER_FLAGS, timer_isr, self, &self->handle)
- );
+ if (self->handle) {
+ ESP_ERROR_CHECK(esp_intr_enable(self->handle));
+ } else {
+ ESP_ERROR_CHECK(esp_intr_alloc(
+ timer_group_periph_signals.groups[self->group].timer_irq_id[self->index],
+ TIMER_FLAGS,
+ machine_timer_isr,
+ self,
+ &self->handle
+ ));
+ }
timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), true);
// Enable the alarm to trigger at the given period.
@@ -224,16 +236,22 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n
}
self->repeat = args[ARG_mode].u_int;
+ self->handler = machine_timer_isr_handler;
self->callback = args[ARG_callback].u_obj;
- self->handle = NULL;
- machine_timer_enable(self, machine_timer_isr);
+ machine_timer_enable(self);
return mp_const_none;
}
static mp_obj_t machine_timer_deinit(mp_obj_t self_in) {
- machine_timer_disable(self_in);
+ machine_timer_obj_t *self = self_in;
+
+ machine_timer_disable(self);
+ if (self->handle) {
+ ESP_ERROR_CHECK(esp_intr_free(self->handle));
+ self->handle = NULL;
+ }
return mp_const_none;
}
@@ -246,6 +264,9 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init)
static mp_obj_t machine_timer_value(mp_obj_t self_in) {
machine_timer_obj_t *self = self_in;
+ if (self->handle == NULL) {
+ mp_raise_ValueError(MP_ERROR_TEXT("timer not set"));
+ }
uint64_t result = timer_ll_get_counter_value(self->hal_context.dev, self->index);
return MP_OBJ_NEW_SMALL_INT((mp_uint_t)(result / (TIMER_SCALE / 1000))); // value in ms
}
diff --git a/ports/esp32/machine_timer.h b/ports/esp32/machine_timer.h
index 914bedd86b..10fe2f39c9 100644
--- a/ports/esp32/machine_timer.h
+++ b/ports/esp32/machine_timer.h
@@ -55,12 +55,13 @@ typedef struct _machine_timer_obj_t {
mp_obj_t callback;
intr_handle_t handle;
+ void (*handler)(struct _machine_timer_obj_t *timer);
struct _machine_timer_obj_t *next;
} machine_timer_obj_t;
machine_timer_obj_t *machine_timer_create(mp_uint_t timer);
-void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr));
+void machine_timer_enable(machine_timer_obj_t *self);
void machine_timer_disable(machine_timer_obj_t *self);
#endif // MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H
diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c
index 73089ef463..982d9a7e27 100644
--- a/ports/esp32/machine_uart.c
+++ b/ports/esp32/machine_uart.c
@@ -59,6 +59,7 @@
#define UART_IRQ_BREAK (1 << UART_BREAK)
#define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_RXIDLE | UART_IRQ_BREAK)
#define RXIDLE_TIMER_MIN (5000) // 500 us
+#define UART_QUEUE_SIZE (3)
enum {
RXIDLE_INACTIVE,
@@ -109,30 +110,19 @@ static const char *_parity_name[] = {"None", "1", "0"};
{ MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \
{ MP_ROM_QSTR(MP_QSTR_IRQ_BREAK), MP_ROM_INT(UART_IRQ_BREAK) }, \
-static void uart_timer_callback(void *self_in) {
- machine_timer_obj_t *self = self_in;
-
- uint32_t intr_status = timer_ll_get_intr_status(self->hal_context.dev);
-
- if (intr_status & TIMER_LL_EVENT_ALARM(self->index)) {
- timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index));
- if (self->repeat) {
- timer_ll_enable_alarm(self->hal_context.dev, self->index, true);
- }
- }
-
+static void uart_timer_callback(machine_timer_obj_t *timer) {
// The UART object is referred here by the callback field.
- machine_uart_obj_t *uart = (machine_uart_obj_t *)self->callback;
- if (uart->rxidle_state == RXIDLE_ALERT) {
+ machine_uart_obj_t *self = (machine_uart_obj_t *)timer->callback;
+ if (self->rxidle_state == RXIDLE_ALERT) {
// At the first call, just switch the state
- uart->rxidle_state = RXIDLE_ARMED;
- } else if (uart->rxidle_state == RXIDLE_ARMED) {
+ self->rxidle_state = RXIDLE_ARMED;
+ } else if (self->rxidle_state == RXIDLE_ARMED) {
// At the second call, run the irq callback and stop the timer
- uart->rxidle_state = RXIDLE_STANDBY;
- uart->mp_irq_flags = UART_IRQ_RXIDLE;
- mp_irq_handler(uart->mp_irq_obj);
+ self->rxidle_state = RXIDLE_STANDBY;
+ self->mp_irq_flags = UART_IRQ_RXIDLE;
+ mp_irq_handler(self->mp_irq_obj);
mp_hal_wake_main_task_from_isr();
- machine_timer_disable(uart->rxidle_timer);
+ machine_timer_disable(self->rxidle_timer);
}
}
@@ -149,9 +139,7 @@ static void uart_event_task(void *self_in) {
if (self->mp_irq_trigger & UART_IRQ_RXIDLE) {
if (self->rxidle_state != RXIDLE_INACTIVE) {
if (self->rxidle_state == RXIDLE_STANDBY) {
- self->rxidle_timer->repeat = true;
- self->rxidle_timer->handle = NULL;
- machine_timer_enable(self->rxidle_timer, uart_timer_callback);
+ machine_timer_enable(self->rxidle_timer);
}
}
self->rxidle_state = RXIDLE_ALERT;
@@ -174,6 +162,13 @@ static void uart_event_task(void *self_in) {
}
}
+static inline void uart_event_task_create(machine_uart_obj_t *self) {
+ if (xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self,
+ ESP_TASKD_EVENT_PRIO, (TaskHandle_t *)&self->uart_event_task, MP_TASK_COREID) != pdPASS) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("failed to create UART event task"));
+ }
+}
+
static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint32_t baudrate;
@@ -250,7 +245,7 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args,
// wait for all data to be transmitted before changing settings
uart_wait_tx_done(self->uart_num, pdMS_TO_TICKS(1000));
- if (args[ARG_txbuf].u_int >= 0 || args[ARG_rxbuf].u_int >= 0) {
+ if ((args[ARG_txbuf].u_int >= 0 && args[ARG_txbuf].u_int != self->txbuf) || (args[ARG_rxbuf].u_int >= 0 && args[ARG_rxbuf].u_int != self->rxbuf)) {
// must reinitialise driver to change the tx/rx buffer size
#if MICROPY_HW_ENABLE_UART_REPL
if (self->uart_num == MICROPY_HW_UART_REPL) {
@@ -275,9 +270,12 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args,
check_esp_err(uart_get_word_length(self->uart_num, &uartcfg.data_bits));
check_esp_err(uart_get_parity(self->uart_num, &uartcfg.parity));
check_esp_err(uart_get_stop_bits(self->uart_num, &uartcfg.stop_bits));
- check_esp_err(uart_driver_delete(self->uart_num));
+ mp_machine_uart_deinit(self);
check_esp_err(uart_param_config(self->uart_num, &uartcfg));
- check_esp_err(uart_driver_install(self->uart_num, self->rxbuf, self->txbuf, 0, NULL, 0));
+ check_esp_err(uart_driver_install(self->uart_num, self->rxbuf, self->txbuf, UART_QUEUE_SIZE, &self->uart_queue, 0));
+ if (self->mp_irq_obj != NULL && self->mp_irq_obj->handler != mp_const_none) {
+ uart_event_task_create(self);
+ }
}
// set baudrate
@@ -437,7 +435,8 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
self->timeout_char = 0;
self->invert = 0;
self->flowcontrol = 0;
- self->uart_event_task = 0;
+ self->uart_event_task = NULL;
+ self->uart_queue = NULL;
self->rxidle_state = RXIDLE_INACTIVE;
switch (uart_num) {
@@ -470,12 +469,13 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
{
// Remove any existing configuration
check_esp_err(uart_driver_delete(self->uart_num));
+ self->uart_queue = NULL;
// init the peripheral
// Setup
check_esp_err(uart_param_config(self->uart_num, &uartcfg));
- check_esp_err(uart_driver_install(uart_num, self->rxbuf, self->txbuf, 3, &self->uart_queue, 0));
+ check_esp_err(uart_driver_install(uart_num, self->rxbuf, self->txbuf, UART_QUEUE_SIZE, &self->uart_queue, 0));
}
mp_map_t kw_args;
@@ -489,7 +489,12 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
}
static void mp_machine_uart_deinit(machine_uart_obj_t *self) {
+ if (self->uart_event_task != NULL) {
+ vTaskDelete(self->uart_event_task);
+ self->uart_event_task = NULL;
+ }
check_esp_err(uart_driver_delete(self->uart_num));
+ self->uart_queue = NULL;
}
static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) {
@@ -535,11 +540,11 @@ static void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger
}
self->rxidle_period = period;
self->rxidle_timer->period = period;
+ self->rxidle_timer->handler = uart_timer_callback;
// The Python callback is not used. So use this
// data field to hold a reference to the UART object.
self->rxidle_timer->callback = self;
self->rxidle_timer->repeat = true;
- self->rxidle_timer->handle = NULL;
self->rxidle_state = RXIDLE_STANDBY;
}
}
@@ -568,6 +573,12 @@ static const mp_irq_methods_t uart_irq_methods = {
};
static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) {
+ #if MICROPY_HW_ENABLE_UART_REPL
+ if (self->uart_num == MICROPY_HW_UART_REPL) {
+ mp_raise_ValueError(MP_ERROR_TEXT("UART does not support IRQs"));
+ }
+ #endif
+
if (self->mp_irq_obj == NULL) {
self->mp_irq_trigger = 0;
self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self));
@@ -597,9 +608,8 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args
uart_irq_configure_timer(self, trigger);
// Start a task for handling events
- if (handler != mp_const_none && self->uart_event_task == NULL) {
- xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self,
- ESP_TASKD_EVENT_PRIO, (TaskHandle_t *)&self->uart_event_task, MP_TASK_COREID);
+ if (handler != mp_const_none && self->uart_event_task == NULL && self->uart_queue != NULL) {
+ uart_event_task_create(self);
} else if (handler == mp_const_none && self->uart_event_task != NULL) {
vTaskDelete(self->uart_event_task);
self->uart_event_task = NULL;
diff --git a/ports/esp32/main.c b/ports/esp32/main.c
index 4f0c27ee07..f048aa85f5 100644
--- a/ports/esp32/main.c
+++ b/ports/esp32/main.c
@@ -38,6 +38,7 @@
#include "nvs_flash.h"
#include "esp_task.h"
#include "esp_event.h"
+#include "esp_flash.h"
#include "esp_log.h"
#include "esp_memory_utils.h"
#include "esp_psram.h"
@@ -214,18 +215,45 @@ void boardctrl_startup(void) {
nvs_flash_erase();
nvs_flash_init();
}
+
+ // Query the physical size of the SPI flash and store it in the size
+ // variable of the global, default SPI flash handle.
+ esp_flash_get_physical_size(NULL, &esp_flash_default_chip->size);
+
+ // If there is no filesystem partition (no "vfs" or "ffat"), add a "vfs" partition
+ // that extends from the end of the application partition up to the end of flash.
+ if (esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "vfs") == NULL
+ && esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "ffat") == NULL) {
+ // No "vfs" or "ffat" partition, so try to create one.
+
+ // Find the end of the last partition that exists in the partition table.
+ size_t offset = 0;
+ esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
+ while (iter != NULL) {
+ const esp_partition_t *part = esp_partition_get(iter);
+ offset = MAX(offset, part->address + part->size);
+ iter = esp_partition_next(iter);
+ }
+
+ // If we found the application partition and there is some space between the end of
+ // that and the end of flash, create a "vfs" partition taking up all of that space.
+ if (offset > 0 && esp_flash_default_chip->size > offset) {
+ size_t size = esp_flash_default_chip->size - offset;
+ esp_partition_register_external(esp_flash_default_chip, offset, size, "vfs", ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
+ }
+ }
}
-void app_main(void) {
+void MICROPY_ESP_IDF_ENTRY(void) {
// Hook for a board to run code at start up.
- // This defaults to initialising NVS.
+ // This defaults to initialising NVS and detecting the flash size.
MICROPY_BOARD_STARTUP();
// Create and transfer control to the MicroPython task.
xTaskCreatePinnedToCore(mp_task, "mp_task", MICROPY_TASK_STACK_SIZE / sizeof(StackType_t), NULL, MP_TASK_PRIORITY, &mp_main_task_handle, MP_TASK_COREID);
}
-void nlr_jump_fail(void *val) {
+MP_WEAK void nlr_jump_fail(void *val) {
printf("NLR jump failed, val=%p\n", val);
esp_restart();
}
diff --git a/ports/esp32/main/idf_component.yml b/ports/esp32/main/idf_component.yml
index ccbde1f27e..f7773f4f4e 100644
--- a/ports/esp32/main/idf_component.yml
+++ b/ports/esp32/main/idf_component.yml
@@ -5,5 +5,10 @@ dependencies:
rules:
- if: "target in [esp32s2, esp32s3]"
version: "~1.0.0"
+ espressif/lan867x:
+ version: "~1.0.0"
+ rules:
+ - if: "target == esp32"
+ - if: "idf_version >=5.3"
idf:
version: ">=5.2.0"
diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c
index 164b191826..fcd6ed9fa8 100644
--- a/ports/esp32/modesp32.c
+++ b/ports/esp32/modesp32.c
@@ -30,7 +30,6 @@
#include <time.h>
#include <sys/time.h>
#include "driver/gpio.h"
-#include "driver/adc.h"
#include "esp_heap_caps.h"
#include "multi_heap.h"
@@ -48,22 +47,30 @@
#include "../multi_heap_platform.h"
#include "../heap_private.h"
+#if SOC_TOUCH_SENSOR_SUPPORTED
static mp_obj_t esp32_wake_on_touch(const mp_obj_t wake) {
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
if (machine_rtc_config.ext0_pin != -1) {
mp_raise_ValueError(MP_ERROR_TEXT("no resources"));
}
+ #endif
machine_rtc_config.wake_on_touch = mp_obj_is_true(wake);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_wake_on_touch_obj, esp32_wake_on_touch);
+#endif
+#if SOC_PM_SUPPORT_EXT0_WAKEUP
static mp_obj_t esp32_wake_on_ext0(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ #if SOC_TOUCH_SENSOR_SUPPORTED
if (machine_rtc_config.wake_on_touch) {
mp_raise_ValueError(MP_ERROR_TEXT("no resources"));
}
+ #endif
+
enum {ARG_pin, ARG_level};
const mp_arg_t allowed_args[] = {
{ MP_QSTR_pin, MP_ARG_OBJ, {.u_obj = mp_obj_new_int(machine_rtc_config.ext0_pin)} },
@@ -90,7 +97,9 @@ static mp_obj_t esp32_wake_on_ext0(size_t n_args, const mp_obj_t *pos_args, mp_m
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext0_obj, 0, esp32_wake_on_ext0);
+#endif
+#if SOC_PM_SUPPORT_EXT1_WAKEUP
static mp_obj_t esp32_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum {ARG_pins, ARG_level};
const mp_arg_t allowed_args[] = {
@@ -126,15 +135,20 @@ static mp_obj_t esp32_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_m
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext1_obj, 0, esp32_wake_on_ext1);
+#endif
+#if SOC_ULP_SUPPORTED
static mp_obj_t esp32_wake_on_ulp(const mp_obj_t wake) {
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
if (machine_rtc_config.ext0_pin != -1) {
mp_raise_ValueError(MP_ERROR_TEXT("no resources"));
}
+ #endif
machine_rtc_config.wake_on_ulp = mp_obj_is_true(wake);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_wake_on_ulp_obj, esp32_wake_on_ulp);
+#endif
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
static mp_obj_t esp32_gpio_deep_sleep_hold(const mp_obj_t enable) {
@@ -212,13 +226,64 @@ static mp_obj_t esp32_idf_heap_info(const mp_obj_t cap_in) {
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_idf_heap_info_obj, esp32_idf_heap_info);
+#if CONFIG_FREERTOS_USE_TRACE_FACILITY
+static mp_obj_t esp32_idf_task_info(void) {
+ const size_t task_count_max = uxTaskGetNumberOfTasks();
+ TaskStatus_t *task_array = m_new(TaskStatus_t, task_count_max);
+ uint32_t total_time;
+ const size_t task_count = uxTaskGetSystemState(task_array, task_count_max, &total_time);
+
+ mp_obj_list_t *task_list = MP_OBJ_TO_PTR(mp_obj_new_list(task_count, NULL));
+ for (size_t i = 0; i < task_count; i++) {
+ mp_obj_t task_data[] = {
+ mp_obj_new_int_from_uint((mp_uint_t)task_array[i].xHandle),
+ mp_obj_new_str(task_array[i].pcTaskName, strlen(task_array[i].pcTaskName)),
+ MP_OBJ_NEW_SMALL_INT(task_array[i].eCurrentState),
+ MP_OBJ_NEW_SMALL_INT(task_array[i].uxCurrentPriority),
+ #if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
+ mp_obj_new_int_from_uint(task_array[i].ulRunTimeCounter),
+ #else
+ mp_const_none,
+ #endif
+ mp_obj_new_int_from_uint(task_array[i].usStackHighWaterMark),
+ #if CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID
+ MP_OBJ_NEW_SMALL_INT(task_array[i].xCoreID),
+ #else
+ mp_const_none,
+ #endif
+ };
+ task_list->items[i] = mp_obj_new_tuple(7, task_data);
+ }
+
+ m_del(TaskStatus_t, task_array, task_count_max);
+ mp_obj_t task_stats[] = {
+ #if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
+ MP_OBJ_NEW_SMALL_INT(total_time),
+ #else
+ mp_const_none,
+ #endif
+ task_list
+ };
+ return mp_obj_new_tuple(2, task_stats);
+}
+static MP_DEFINE_CONST_FUN_OBJ_0(esp32_idf_task_info_obj, esp32_idf_task_info);
+#endif
+
static const mp_rom_map_elem_t esp32_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_esp32) },
+ #if SOC_TOUCH_SENSOR_SUPPORTED
{ MP_ROM_QSTR(MP_QSTR_wake_on_touch), MP_ROM_PTR(&esp32_wake_on_touch_obj) },
+ #endif
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
{ MP_ROM_QSTR(MP_QSTR_wake_on_ext0), MP_ROM_PTR(&esp32_wake_on_ext0_obj) },
+ #endif
+ #if SOC_PM_SUPPORT_EXT1_WAKEUP
{ MP_ROM_QSTR(MP_QSTR_wake_on_ext1), MP_ROM_PTR(&esp32_wake_on_ext1_obj) },
+ #endif
+ #if SOC_ULP_SUPPORTED
{ MP_ROM_QSTR(MP_QSTR_wake_on_ulp), MP_ROM_PTR(&esp32_wake_on_ulp_obj) },
+ #endif
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
{ MP_ROM_QSTR(MP_QSTR_gpio_deep_sleep_hold), MP_ROM_PTR(&esp32_gpio_deep_sleep_hold_obj) },
#endif
@@ -228,6 +293,9 @@ static const mp_rom_map_elem_t esp32_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_mcu_temperature), MP_ROM_PTR(&esp32_mcu_temperature_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_idf_heap_info), MP_ROM_PTR(&esp32_idf_heap_info_obj) },
+ #if CONFIG_FREERTOS_USE_TRACE_FACILITY
+ { MP_ROM_QSTR(MP_QSTR_idf_task_info), MP_ROM_PTR(&esp32_idf_task_info_obj) },
+ #endif
{ MP_ROM_QSTR(MP_QSTR_NVS), MP_ROM_PTR(&esp32_nvs_type) },
{ MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) },
diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c
index 0c1b94d02d..0d7ea44c66 100644
--- a/ports/esp32/modmachine.c
+++ b/ports/esp32/modmachine.c
@@ -178,7 +178,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
machine_sleep_helper(MACHINE_WAKE_SLEEP, n_args, args);
};
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
machine_sleep_helper(MACHINE_WAKE_DEEPSLEEP, n_args, args);
mp_machine_reset();
};
@@ -221,7 +221,7 @@ static mp_int_t mp_machine_reset_cause(void) {
#include "esp32s3/rom/usb/chip_usb_dw_wrapper.h"
#endif
-NORETURN static void machine_bootloader_rtc(void) {
+MP_NORETURN static void machine_bootloader_rtc(void) {
#if CONFIG_IDF_TARGET_ESP32S3 && MICROPY_HW_USB_CDC
usb_usj_mode();
usb_dc_prepare_persist();
@@ -233,7 +233,7 @@ NORETURN static void machine_bootloader_rtc(void) {
#endif
#ifdef MICROPY_BOARD_ENTER_BOOTLOADER
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
for (;;) {
}
@@ -254,7 +254,7 @@ static mp_obj_t machine_wake_reason(size_t n_args, const mp_obj_t *pos_args, mp_
}
static MP_DEFINE_CONST_FUN_OBJ_KW(machine_wake_reason_obj, 0, machine_wake_reason);
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
esp_restart();
}
diff --git a/ports/esp32/modnetwork.h b/ports/esp32/modnetwork.h
index 387f961976..ba69d5cc0f 100644
--- a/ports/esp32/modnetwork.h
+++ b/ports/esp32/modnetwork.h
@@ -28,7 +28,32 @@
#include "esp_netif.h"
-enum { PHY_LAN8710, PHY_LAN8720, PHY_IP101, PHY_RTL8201, PHY_DP83848, PHY_KSZ8041, PHY_KSZ8081, PHY_KSZ8851SNL = 100, PHY_DM9051, PHY_W5500 };
+// lan867x component requires newer IDF version
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && CONFIG_IDF_TARGET_ESP32
+#define PHY_LAN867X_ENABLED (1)
+#else
+#define PHY_LAN867X_ENABLED (0)
+#endif
+
+// PHY_GENERIC support requires newer IDF version
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) && CONFIG_IDF_TARGET_ESP32
+#define PHY_GENERIC_ENABLED (1)
+#else
+#define PHY_GENERIC_ENABLED (0)
+#endif
+
+enum {
+ // PHYs supported by the internal Ethernet MAC:
+ PHY_LAN8710, PHY_LAN8720, PHY_IP101, PHY_RTL8201, PHY_DP83848, PHY_KSZ8041, PHY_KSZ8081,
+ #if PHY_LAN867X_ENABLED
+ PHY_LAN8670,
+ #endif
+ #if PHY_GENERIC_ENABLED
+ PHY_GENERIC,
+ #endif
+ // PHYs which are actually SPI Ethernet MAC+PHY chips:
+ PHY_KSZ8851SNL = 100, PHY_DM9051, PHY_W5500
+};
#define IS_SPI_PHY(NUM) (NUM >= 100)
enum { ETH_INITIALIZED, ETH_STARTED, ETH_STOPPED, ETH_CONNECTED, ETH_DISCONNECTED, ETH_GOT_IP };
@@ -52,7 +77,7 @@ extern const mp_obj_type_t esp_network_wlan_type;
MP_DECLARE_CONST_FUN_OBJ_0(esp_network_initialize_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_get_wlan_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_get_lan_obj);
-MP_DECLARE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj);
+extern const struct _mp_obj_type_t esp_network_ppp_lwip_type;
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_ifconfig_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(esp_nic_ipconfig_obj);
@@ -61,7 +86,7 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_phy_mode_obj);
mp_obj_t esp_ifname(esp_netif_t *netif);
-NORETURN void esp_exceptions_helper(esp_err_t e);
+MP_NORETURN void esp_exceptions_helper(esp_err_t e);
static inline void esp_exceptions(esp_err_t e) {
if (e != ESP_OK) {
diff --git a/ports/esp32/modnetwork_globals.h b/ports/esp32/modnetwork_globals.h
index 1aad785ee3..12252ddbc5 100644
--- a/ports/esp32/modnetwork_globals.h
+++ b/ports/esp32/modnetwork_globals.h
@@ -8,7 +8,7 @@
{ MP_ROM_QSTR(MP_QSTR_LAN), MP_ROM_PTR(&esp_network_get_lan_obj) },
#endif
#if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT)
-{ MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&esp_network_ppp_make_new_obj) },
+{ MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&esp_network_ppp_lwip_type) },
#endif
{ MP_ROM_QSTR(MP_QSTR_phy_mode), MP_ROM_PTR(&esp_network_phy_mode_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_network_ipconfig_obj) },
@@ -47,6 +47,12 @@
{ MP_ROM_QSTR(MP_QSTR_PHY_DP83848), MP_ROM_INT(PHY_DP83848) },
{ MP_ROM_QSTR(MP_QSTR_PHY_KSZ8041), MP_ROM_INT(PHY_KSZ8041) },
{ MP_ROM_QSTR(MP_QSTR_PHY_KSZ8081), MP_ROM_INT(PHY_KSZ8081) },
+#if PHY_LAN867X_ENABLED
+{ MP_ROM_QSTR(MP_QSTR_PHY_LAN8670), MP_ROM_INT(PHY_LAN8670) },
+#endif
+#if PHY_GENERIC_ENABLED
+{ MP_ROM_QSTR(MP_QSTR_PHY_GENERIC), MP_ROM_INT(PHY_GENERIC) },
+#endif
#if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL
{ MP_ROM_QSTR(MP_QSTR_PHY_KSZ8851SNL), MP_ROM_INT(PHY_KSZ8851SNL) },
diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c
index 7ea5e855d3..2050d1d04d 100644
--- a/ports/esp32/modsocket.c
+++ b/ports/esp32/modsocket.c
@@ -611,7 +611,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking);
// XXX this can end up waiting a very long time if the content is dribbled in one character
// at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not
// good behaviour.
-static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size,
+static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, mp_int_t flags,
struct sockaddr *from, socklen_t *from_len, int *errcode) {
socket_obj_t *sock = MP_OBJ_TO_PTR(self_in);
@@ -645,7 +645,7 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size,
if (release_gil) {
MP_THREAD_GIL_EXIT();
}
- int r = lwip_recvfrom(sock->fd, buf, size, 0, from, from_len);
+ int r = lwip_recvfrom(sock->fd, buf, size, flags, from, from_len);
if (release_gil) {
MP_THREAD_GIL_ENTER();
}
@@ -655,7 +655,7 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size,
if (r >= 0) {
return r;
}
- if (errno != EWOULDBLOCK) {
+ if (errno != EWOULDBLOCK || (flags & MSG_DONTWAIT)) {
*errcode = errno;
return MP_STREAM_ERROR;
}
@@ -666,14 +666,17 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size,
return MP_STREAM_ERROR;
}
-mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in,
+mp_obj_t _socket_recvfrom(size_t n_args, const mp_obj_t *args,
struct sockaddr *from, socklen_t *from_len) {
- size_t len = mp_obj_get_int(len_in);
+ mp_obj_t self_in = args[0];
+ size_t len = mp_obj_get_int(args[1]);
+ int flags = n_args > 2 ? mp_obj_get_int(args[2]) : 0;
+
vstr_t vstr;
vstr_init_len(&vstr, len);
int errcode;
- mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, from, from_len, &errcode);
+ mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, flags, from, from_len, &errcode);
if (ret == MP_STREAM_ERROR) {
mp_raise_OSError(errcode);
}
@@ -682,17 +685,17 @@ mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in,
return mp_obj_new_bytes_from_vstr(&vstr);
}
-static mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) {
- return _socket_recvfrom(self_in, len_in, NULL, NULL);
+static mp_obj_t socket_recv(size_t n_args, const mp_obj_t *args) {
+ return _socket_recvfrom(n_args, args, NULL, NULL);
}
-static MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv);
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recv_obj, 2, 3, socket_recv);
-static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
+static mp_obj_t socket_recvfrom(size_t n_args, const mp_obj_t *args) {
struct sockaddr from;
socklen_t fromlen = sizeof(from);
mp_obj_t tuple[2];
- tuple[0] = _socket_recvfrom(self_in, len_in, &from, &fromlen);
+ tuple[0] = _socket_recvfrom(n_args, args, &from, &fromlen);
uint8_t *ip = (uint8_t *)&((struct sockaddr_in *)&from)->sin_addr;
mp_uint_t port = lwip_ntohs(((struct sockaddr_in *)&from)->sin_port);
@@ -700,7 +703,7 @@ static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
return mp_obj_new_tuple(2, tuple);
}
-static MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom);
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recvfrom_obj, 2, 3, socket_recvfrom);
int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) {
int sentlen = 0;
@@ -789,7 +792,7 @@ static mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) {
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile);
static mp_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
- return _socket_read_data(self_in, buf, size, NULL, NULL, errcode);
+ return _socket_read_data(self_in, buf, size, 0, NULL, NULL, errcode);
}
static mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
@@ -1010,6 +1013,8 @@ static const mp_rom_map_elem_t mp_module_socket_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_SO_BINDTODEVICE), MP_ROM_INT(SO_BINDTODEVICE) },
{ MP_ROM_QSTR(MP_QSTR_IP_ADD_MEMBERSHIP), MP_ROM_INT(IP_ADD_MEMBERSHIP) },
{ MP_ROM_QSTR(MP_QSTR_TCP_NODELAY), MP_ROM_INT(TCP_NODELAY) },
+ { MP_ROM_QSTR(MP_QSTR_MSG_PEEK), MP_ROM_INT(MSG_PEEK) },
+ { MP_ROM_QSTR(MP_QSTR_MSG_DONTWAIT), MP_ROM_INT(MSG_DONTWAIT) },
};
static MP_DEFINE_CONST_DICT(mp_module_socket_globals, mp_module_socket_globals_table);
diff --git a/ports/esp32/modtime.c b/ports/esp32/modtime.c
index 4695dd23e7..991f2cf578 100644
--- a/ports/esp32/modtime.c
+++ b/ports/esp32/modtime.c
@@ -54,5 +54,5 @@ static mp_obj_t mp_time_localtime_get(void) {
static mp_obj_t mp_time_time_get(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
- return mp_obj_new_int(tv.tv_sec);
+ return timeutils_obj_from_timestamp(tv.tv_sec);
}
diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h
index b5b7d63a56..a6f103cdef 100644
--- a/ports/esp32/mpconfigport.h
+++ b/ports/esp32/mpconfigport.h
@@ -62,7 +62,8 @@
#define MICROPY_STACK_CHECK_MARGIN (1024)
#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
-#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL)
+#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) // Debugging Note: Increase the error reporting level to view
+ // __FUNCTION__, __LINE__, __FILE__ in check_esp_err() exceptions
#define MICROPY_WARNINGS (1)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#define MICROPY_STREAMS_POSIX_API (1)
@@ -123,6 +124,7 @@
#define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/esp32/machine_adc.c"
#define MICROPY_PY_MACHINE_ADC_ATTEN_WIDTH (1)
#define MICROPY_PY_MACHINE_ADC_INIT (1)
+#define MICROPY_PY_MACHINE_ADC_DEINIT (1)
#define MICROPY_PY_MACHINE_ADC_READ (1)
#define MICROPY_PY_MACHINE_ADC_READ_UV (1)
#define MICROPY_PY_MACHINE_ADC_BLOCK (1)
@@ -391,3 +393,8 @@ void boardctrl_startup(void);
#ifndef MICROPY_PY_STRING_TX_GIL_THRESHOLD
#define MICROPY_PY_STRING_TX_GIL_THRESHOLD (20)
#endif
+
+// Code can override this to provide a custom ESP-IDF entry point.
+#ifndef MICROPY_ESP_IDF_ENTRY
+#define MICROPY_ESP_IDF_ENTRY app_main
+#endif
diff --git a/ports/esp32/mpnimbleport.c b/ports/esp32/mpnimbleport.c
index ce4b77727a..77185883fd 100644
--- a/ports/esp32/mpnimbleport.c
+++ b/ports/esp32/mpnimbleport.c
@@ -58,7 +58,7 @@ void mp_bluetooth_nimble_port_start(void) {
nimble_port_freertos_init(ble_host_task);
}
-void mp_bluetooth_nimble_port_shutdown(void) {
+int mp_bluetooth_nimble_port_shutdown(void) {
DEBUG_printf("mp_bluetooth_nimble_port_shutdown\n");
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK
@@ -79,6 +79,8 @@ void mp_bluetooth_nimble_port_shutdown(void) {
// Mark stack as shutdown.
mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF;
+
+ return 0;
}
#endif
diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c
index 962b5780d0..67917e33e5 100644
--- a/ports/esp32/mpthreadport.c
+++ b/ports/esp32/mpthreadport.c
@@ -41,10 +41,16 @@
#define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + MICROPY_STACK_CHECK_MARGIN)
#define MP_THREAD_PRIORITY (ESP_TASK_PRIO_MIN + 1)
+typedef enum {
+ MP_THREAD_RUN_STATE_NEW,
+ MP_THREAD_RUN_STATE_RUNNING,
+ MP_THREAD_RUN_STATE_FINISHED,
+} mp_thread_run_state_t;
+
// this structure forms a linked list, one node per active thread
typedef struct _mp_thread_t {
TaskHandle_t id; // system id of thread
- int ready; // whether the thread is ready and running
+ mp_thread_run_state_t run_state; // current run state of the thread
void *arg; // thread Python args, a GC root pointer
void *stack; // pointer to the stack
size_t stack_len; // number of words in the stack
@@ -60,18 +66,16 @@ void mp_thread_init(void *stack, uint32_t stack_len) {
mp_thread_set_state(&mp_state_ctx.thread);
// create the first entry in the linked list of all threads
thread_entry0.id = xTaskGetCurrentTaskHandle();
- thread_entry0.ready = 1;
+ thread_entry0.run_state = MP_THREAD_RUN_STATE_RUNNING;
thread_entry0.arg = NULL;
thread_entry0.stack = stack;
thread_entry0.stack_len = stack_len;
thread_entry0.next = NULL;
+ thread = &thread_entry0;
mp_thread_mutex_init(&thread_mutex);
// memory barrier to ensure above data is committed
__sync_synchronize();
-
- // vTaskPreDeletionHook needs the thread ready after thread_mutex is ready
- thread = &thread_entry0;
}
void mp_thread_gc_others(void) {
@@ -82,7 +86,7 @@ void mp_thread_gc_others(void) {
if (th->id == xTaskGetCurrentTaskHandle()) {
continue;
}
- if (!th->ready) {
+ if (th->run_state != MP_THREAD_RUN_STATE_RUNNING) {
continue;
}
gc_collect_root(th->stack, th->stack_len);
@@ -106,7 +110,7 @@ void mp_thread_start(void) {
mp_thread_mutex_lock(&thread_mutex, 1);
for (mp_thread_t *th = thread; th != NULL; th = th->next) {
if (th->id == xTaskGetCurrentTaskHandle()) {
- th->ready = 1;
+ th->run_state = MP_THREAD_RUN_STATE_RUNNING;
break;
}
}
@@ -116,12 +120,22 @@ void mp_thread_start(void) {
static void *(*ext_thread_entry)(void *) = NULL;
static void freertos_entry(void *arg) {
+ // Run the Python code.
if (ext_thread_entry) {
ext_thread_entry(arg);
}
- vTaskDelete(NULL);
- for (;;) {;
+
+ // Remove the thread from the linked-list of active threads.
+ mp_thread_mutex_lock(&thread_mutex, 1);
+ for (mp_thread_t **th = &thread; *th != NULL; th = &(*th)->next) {
+ if ((*th)->id == xTaskGetCurrentTaskHandle()) {
+ *th = (*th)->next;
+ }
}
+ mp_thread_mutex_unlock(&thread_mutex);
+
+ // Delete this FreeRTOS task (this call to vTaskDelete will not return).
+ vTaskDelete(NULL);
}
mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_size, int priority, char *name) {
@@ -147,7 +161,7 @@ mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_s
}
// add thread to linked list of all threads
- th->ready = 0;
+ th->run_state = MP_THREAD_RUN_STATE_NEW;
th->arg = arg;
th->stack = pxTaskGetStackStart(th->id);
th->stack_len = *stack_size / sizeof(uintptr_t);
@@ -167,33 +181,7 @@ void mp_thread_finish(void) {
mp_thread_mutex_lock(&thread_mutex, 1);
for (mp_thread_t *th = thread; th != NULL; th = th->next) {
if (th->id == xTaskGetCurrentTaskHandle()) {
- th->ready = 0;
- break;
- }
- }
- mp_thread_mutex_unlock(&thread_mutex);
-}
-
-// This is called either from vTaskDelete() or from the FreeRTOS idle task, so
-// may not be within Python context. Therefore MP_STATE_THREAD may not be valid
-// and it does not have the GIL.
-void vTaskPreDeletionHook(void *tcb) {
- if (thread == NULL) {
- // threading not yet initialised
- return;
- }
- mp_thread_t *prev = NULL;
- mp_thread_mutex_lock(&thread_mutex, 1);
- for (mp_thread_t *th = thread; th != NULL; prev = th, th = th->next) {
- // unlink the node from the list
- if ((void *)th->id == tcb) {
- if (prev != NULL) {
- prev->next = th->next;
- } else {
- // move the start pointer
- thread = th->next;
- }
- // The "th" memory will eventually be reclaimed by the GC.
+ th->run_state = MP_THREAD_RUN_STATE_FINISHED;
break;
}
}
@@ -221,32 +209,22 @@ void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
}
void mp_thread_deinit(void) {
- for (;;) {
- // Find a task to delete
- TaskHandle_t id = NULL;
- mp_thread_mutex_lock(&thread_mutex, 1);
- for (mp_thread_t *th = thread; th != NULL; th = th->next) {
- // Don't delete the current task
- if (th->id != xTaskGetCurrentTaskHandle()) {
- id = th->id;
- break;
- }
- }
- mp_thread_mutex_unlock(&thread_mutex);
+ // The current task should be thread_entry0 and should be the last in the linked list.
+ assert(thread_entry0.id == xTaskGetCurrentTaskHandle());
+ assert(thread_entry0.next == NULL);
- if (id == NULL) {
- // No tasks left to delete
- break;
- } else {
- // Call FreeRTOS to delete the task (it will call vTaskPreDeletionHook)
- vTaskDelete(id);
+ // Delete all tasks except the main one.
+ mp_thread_mutex_lock(&thread_mutex, 1);
+ for (mp_thread_t *th = thread; th != NULL; th = th->next) {
+ if (th != &thread_entry0) {
+ vTaskDelete(th->id);
}
}
-}
-
-#else
+ thread = &thread_entry0;
+ mp_thread_mutex_unlock(&thread_mutex);
-void vTaskPreDeletionHook(void *tcb) {
+ // Give the idle task a chance to run, to clean up any deleted tasks.
+ vTaskDelay(1);
}
#endif // MICROPY_PY_THREAD
diff --git a/ports/esp32/network_common.c b/ports/esp32/network_common.c
index fce8a0304c..bd34f1b41f 100644
--- a/ports/esp32/network_common.c
+++ b/ports/esp32/network_common.c
@@ -45,7 +45,7 @@
#include "lwip/sockets.h"
#include "lwip/dns.h"
-NORETURN void esp_exceptions_helper(esp_err_t e) {
+MP_NORETURN void esp_exceptions_helper(esp_err_t e) {
switch (e) {
case ESP_ERR_WIFI_NOT_INIT:
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Not Initialized"));
@@ -77,6 +77,8 @@ NORETURN void esp_exceptions_helper(esp_err_t e) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Would Block"));
case ESP_ERR_WIFI_NOT_CONNECT:
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Not Connected"));
+ case ESP_ERR_NO_MEM:
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("WiFi Out of Memory"));
default:
mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("Wifi Unknown Error 0x%04x"), e);
}
diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c
index e1a8c95785..309ee0b14a 100644
--- a/ports/esp32/network_lan.c
+++ b/ports/esp32/network_lan.c
@@ -45,6 +45,10 @@
#include "modnetwork.h"
#include "extmod/modnetwork.h"
+#if PHY_LAN867X_ENABLED
+#include "esp_eth_phy_lan867x.h"
+#endif
+
typedef struct _lan_if_obj_t {
base_if_obj_t base;
bool initialized;
@@ -156,6 +160,12 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
args[ARG_phy_type].u_int != PHY_RTL8201 &&
args[ARG_phy_type].u_int != PHY_KSZ8041 &&
args[ARG_phy_type].u_int != PHY_KSZ8081 &&
+ #if PHY_LAN867X_ENABLED
+ args[ARG_phy_type].u_int != PHY_LAN8670 &&
+ #endif
+ #if PHY_GENERIC_ENABLED
+ args[ARG_phy_type].u_int != PHY_GENERIC &&
+ #endif
#if CONFIG_ETH_USE_SPI_ETHERNET
#if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL
args[ARG_phy_type].u_int != PHY_KSZ8851SNL &&
@@ -231,7 +241,17 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
case PHY_KSZ8081:
self->phy = esp_eth_phy_new_ksz80xx(&phy_config);
break;
+ #if PHY_LAN867X_ENABLED
+ case PHY_LAN8670:
+ self->phy = esp_eth_phy_new_lan867x(&phy_config);
+ break;
+ #endif
+ #if PHY_GENERIC_ENABLED
+ case PHY_GENERIC:
+ self->phy = esp_eth_phy_new_generic(&phy_config);
+ break;
#endif
+ #endif // CONFIG_IDF_TARGET_ESP32
#if CONFIG_ETH_USE_SPI_ETHERNET
#if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL
case PHY_KSZ8851SNL: {
diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c
index 4dd5a3718c..8b700c98ef 100644
--- a/ports/esp32/network_ppp.c
+++ b/ports/esp32/network_ppp.c
@@ -4,6 +4,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2018 "Eric Poulsen" <eric@zyxod.com>
+ * Copyright (c) 2024 Damien P. George
*
* Based on the ESP IDF example code which is Public Domain / CC0
*
@@ -26,172 +27,265 @@
* THE SOFTWARE.
*/
+// This file is intended to closely match extmod/network_ppp_lwip.c. Changes can
+// and should probably be applied to both files. Compare them directly by using:
+// git diff --no-index extmod/network_ppp_lwip.c ports/esp32/network_ppp.c
+
#include "py/runtime.h"
#include "py/mphal.h"
-#include "py/objtype.h"
#include "py/stream.h"
-#include "shared/netutils/netutils.h"
-#include "modmachine.h"
-#include "ppp_set_auth.h"
-#include "netif/ppp/ppp.h"
-#include "netif/ppp/pppos.h"
-#include "lwip/err.h"
-#include "lwip/sockets.h"
-#include "lwip/sys.h"
-#include "lwip/netdb.h"
+#if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT)
+
#include "lwip/dns.h"
+#include "netif/ppp/ppp.h"
#include "netif/ppp/pppapi.h"
+#include "netif/ppp/pppos.h"
-#if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT)
+// Includes for port-specific changes compared to network_ppp_lwip.c
+#include "shared/netutils/netutils.h"
+#include "ppp_set_auth.h"
-#define PPP_CLOSE_TIMEOUT_MS (4000)
+// Enable this to see the serial data going between the PPP layer.
+#define PPP_TRACE_IN_OUT (0)
-typedef struct _ppp_if_obj_t {
+typedef enum {
+ STATE_INACTIVE,
+ STATE_ACTIVE,
+ STATE_ERROR,
+ STATE_CONNECTING,
+ STATE_CONNECTED,
+} network_ppp_state_t;
+
+typedef struct _network_ppp_obj_t {
mp_obj_base_t base;
- bool active;
- bool connected;
- volatile bool clean_close;
- ppp_pcb *pcb;
+ network_ppp_state_t state;
+ int error_code;
mp_obj_t stream;
- SemaphoreHandle_t inactiveWaitSem;
- volatile TaskHandle_t client_task_handle;
- struct netif pppif;
-} ppp_if_obj_t;
+ ppp_pcb *pcb;
+ struct netif netif;
+} network_ppp_obj_t;
-const mp_obj_type_t ppp_if_type;
+const mp_obj_type_t esp_network_ppp_lwip_type;
+
+static mp_obj_t network_ppp___del__(mp_obj_t self_in);
+
+static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) {
+ if (self->stream == mp_const_none) {
+ return;
+ }
-static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
- ppp_if_obj_t *self = ctx;
- struct netif *pppif = ppp_netif(self->pcb);
+ // Disable UART IRQ.
+ mp_obj_t dest[3];
+ mp_load_method(self->stream, MP_QSTR_irq, dest);
+ dest[2] = mp_const_none;
+ mp_call_method_n_kw(1, 0, dest);
+}
+static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
+ network_ppp_obj_t *self = ctx;
switch (err_code) {
case PPPERR_NONE:
- #if CONFIG_LWIP_IPV6
- self->connected = (pppif->ip_addr.u_addr.ip4.addr != 0);
- #else
- self->connected = (pppif->ip_addr.addr != 0);
- #endif // CONFIG_LWIP_IPV6
+ self->state = STATE_CONNECTED;
break;
case PPPERR_USER:
- self->clean_close = true;
- break;
- case PPPERR_CONNECT:
- self->connected = false;
+ if (self->state >= STATE_ERROR) {
+ // Indicate that we are no longer connected and thus
+ // only need to free the PPP PCB, not close it.
+ self->state = STATE_ACTIVE;
+ }
+ // Clean up the PPP PCB.
+ network_ppp___del__(MP_OBJ_FROM_PTR(self));
break;
default:
+ self->state = STATE_ERROR;
+ self->error_code = err_code;
break;
}
}
-static mp_obj_t ppp_make_new(mp_obj_t stream) {
+static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
+ mp_arg_check_num(n_args, n_kw, 1, 1, false);
+
+ mp_obj_t stream = all_args[0];
+
if (stream != mp_const_none) {
mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
}
- ppp_if_obj_t *self = mp_obj_malloc_with_finaliser(ppp_if_obj_t, &ppp_if_type);
+ network_ppp_obj_t *self = mp_obj_malloc_with_finaliser(network_ppp_obj_t, type);
+ self->state = STATE_INACTIVE;
self->stream = stream;
- self->active = false;
- self->connected = false;
- self->clean_close = false;
- self->client_task_handle = NULL;
+ self->pcb = NULL;
return MP_OBJ_FROM_PTR(self);
}
-MP_DEFINE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj, ppp_make_new);
-#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
-static u32_t ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx)
-#else
-static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx)
-#endif
-{
- ppp_if_obj_t *self = ctx;
-
- mp_obj_t stream = self->stream;
- if (stream == mp_const_none) {
- return 0;
+static mp_obj_t network_ppp___del__(mp_obj_t self_in) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ if (self->state >= STATE_ACTIVE) {
+ if (self->state >= STATE_ERROR) {
+ // Still connected over the stream.
+ // Force the connection to close, with nocarrier=1.
+ self->state = STATE_INACTIVE;
+ pppapi_close(self->pcb, 1);
+ }
+ network_ppp_stream_uart_irq_disable(self);
+ // Free PPP PCB and reset state.
+ self->state = STATE_INACTIVE;
+ pppapi_free(self->pcb);
+ self->pcb = NULL;
}
-
- int err;
- return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE);
+ return mp_const_none;
}
+static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp___del___obj, network_ppp___del__);
-static void pppos_client_task(void *self_in) {
- ppp_if_obj_t *self = (ppp_if_obj_t *)self_in;
- uint8_t buf[256];
+static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
- int len = 0;
- while (ulTaskNotifyTake(pdTRUE, len <= 0) == 0) {
- mp_obj_t stream = self->stream;
- if (stream == mp_const_none) {
- len = 0;
- } else {
- int err;
- len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0);
- if (len > 0) {
- pppos_input_tcpip(self->pcb, (u8_t *)buf, len);
- }
- }
+ if (self->state <= STATE_ERROR) {
+ return MP_OBJ_NEW_SMALL_INT(-MP_EPERM);
}
- self->client_task_handle = NULL;
- vTaskDelete(NULL);
- for (;;) {
+ mp_int_t total_len = 0;
+ mp_obj_t stream = self->stream;
+ while (stream != mp_const_none) {
+ uint8_t buf[256];
+ int err;
+ mp_uint_t len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0);
+ if (len == 0) {
+ break;
+ }
+ #if PPP_TRACE_IN_OUT
+ mp_printf(&mp_plat_print, "ppp_in(n=%u,data=", len);
+ for (size_t i = 0; i < len; ++i) {
+ mp_printf(&mp_plat_print, "%02x:", buf[i]);
+ }
+ mp_printf(&mp_plat_print, ")\n");
+ #endif
+ pppos_input(self->pcb, (u8_t *)buf, len);
+ total_len += len;
}
+
+ return MP_OBJ_NEW_SMALL_INT(total_len);
}
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_poll_obj, 1, 2, network_ppp_poll);
-static mp_obj_t ppp_active(size_t n_args, const mp_obj_t *args) {
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+static void network_ppp_stream_uart_irq_enable(network_ppp_obj_t *self) {
+ if (self->stream == mp_const_none) {
+ return;
+ }
- if (n_args > 1) {
- if (mp_obj_is_true(args[1])) {
- if (self->active) {
- return mp_const_true;
- }
+ // Enable UART IRQ to call PPP.poll() when incoming data is ready.
+ mp_obj_t dest[4];
+ mp_load_method(self->stream, MP_QSTR_irq, dest);
+ dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self));
+ dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE);
+ mp_call_method_n_kw(2, 0, dest);
+}
- self->pcb = pppapi_pppos_create(&self->pppif, ppp_output_callback, ppp_status_cb, self);
+static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
+ if (n_args != 1 && kwargs->used != 0) {
+ mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed"));
+ }
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
- if (self->pcb == NULL) {
- mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("init failed"));
- }
- self->active = true;
- } else {
- if (!self->active) {
- return mp_const_false;
+ if (kwargs->used != 0) {
+ for (size_t i = 0; i < kwargs->alloc; i++) {
+ if (mp_map_slot_is_filled(kwargs, i)) {
+ switch (mp_obj_str_get_qstr(kwargs->table[i].key)) {
+ case MP_QSTR_stream: {
+ if (kwargs->table[i].value != mp_const_none) {
+ mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
+ }
+ if (self->state >= STATE_ACTIVE) {
+ network_ppp_stream_uart_irq_disable(self);
+ }
+ self->stream = kwargs->table[i].value;
+ if (self->state >= STATE_ACTIVE) {
+ network_ppp_stream_uart_irq_enable(self);
+ }
+ break;
+ }
+ default:
+ break;
+ }
}
+ }
+ return mp_const_none;
+ }
- if (self->client_task_handle != NULL) { // is connecting or connected?
- // Wait for PPPERR_USER, with timeout
- pppapi_close(self->pcb, 0);
- uint32_t t0 = mp_hal_ticks_ms();
- while (!self->clean_close && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) {
- mp_hal_delay_ms(10);
- }
+ if (n_args != 2) {
+ mp_raise_TypeError(MP_ERROR_TEXT("can query only one param"));
+ }
- // Shutdown task
- xTaskNotifyGive(self->client_task_handle);
- t0 = mp_hal_ticks_ms();
- while (self->client_task_handle != NULL && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) {
- mp_hal_delay_ms(10);
+ mp_obj_t val = mp_const_none;
+
+ switch (mp_obj_str_get_qstr(args[1])) {
+ case MP_QSTR_stream: {
+ val = self->stream;
+ break;
+ }
+ case MP_QSTR_ifname: {
+ if (self->pcb != NULL) {
+ struct netif *pppif = ppp_netif(self->pcb);
+ char ifname[NETIF_NAMESIZE + 1] = {0};
+ netif_index_to_name(netif_get_index(pppif), ifname);
+ if (ifname[0] != 0) {
+ val = mp_obj_new_str_from_cstr((char *)ifname);
}
}
-
- // Release PPP
- pppapi_free(self->pcb);
- self->pcb = NULL;
- self->active = false;
- self->connected = false;
- self->clean_close = false;
+ break;
}
+ default:
+ mp_raise_ValueError(MP_ERROR_TEXT("unknown config param"));
+ }
+
+ return val;
+}
+static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_config_obj, 1, network_ppp_config);
+
+static mp_obj_t network_ppp_status(mp_obj_t self_in) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ if (self->state == STATE_ERROR) {
+ return MP_OBJ_NEW_SMALL_INT(-self->error_code);
+ } else {
+ return MP_OBJ_NEW_SMALL_INT(self->state);
+ }
+}
+static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_status_obj, network_ppp_status);
+
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
+static u32_t network_ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx)
+#else
+static u32_t network_ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx)
+#endif
+{
+ network_ppp_obj_t *self = ctx;
+ #if PPP_TRACE_IN_OUT
+ mp_printf(&mp_plat_print, "ppp_out(n=%u,data=", len);
+ for (size_t i = 0; i < len; ++i) {
+ mp_printf(&mp_plat_print, "%02x:", ((const uint8_t *)data)[i]);
+ }
+ mp_printf(&mp_plat_print, ")\n");
+ #endif
+ mp_obj_t stream = self->stream;
+ if (stream == mp_const_none) {
+ return 0;
}
- return mp_obj_new_bool(self->active);
+ int err;
+ // The return value from this output callback is the number of bytes written out.
+ // If it's less than the requested number of bytes then lwIP will propagate out an error.
+ return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE);
}
-static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_active_obj, 1, 2, ppp_active);
-static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
- enum { ARG_authmode, ARG_username, ARG_password };
+static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
+ enum { ARG_security, ARG_user, ARG_key, ARG_authmode, ARG_username, ARG_password };
static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
+ { MP_QSTR_user, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
+ { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
+ // Deprecated arguments for backwards compatibility
{ MP_QSTR_authmode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PPPAUTHTYPE_NONE} },
{ MP_QSTR_username, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_password, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
@@ -200,17 +294,34 @@ static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw
mp_arg_val_t parsed_args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, parsed_args);
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ // Use deprecated arguments as defaults
+ if (parsed_args[ARG_security].u_int == -1) {
+ parsed_args[ARG_security].u_int = parsed_args[ARG_authmode].u_int;
+ }
+ if (parsed_args[ARG_user].u_obj == mp_const_none) {
+ parsed_args[ARG_user].u_obj = parsed_args[ARG_username].u_obj;
+ }
+ if (parsed_args[ARG_key].u_obj == mp_const_none) {
+ parsed_args[ARG_key].u_obj = parsed_args[ARG_password].u_obj;
+ }
+
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+
+ if (self->state == STATE_INACTIVE) {
+ self->pcb = pppapi_pppos_create(&self->netif, network_ppp_output_callback, network_ppp_status_cb, self);
+ if (self->pcb == NULL) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppos_create failed"));
+ }
+ self->state = STATE_ACTIVE;
- if (!self->active) {
- mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("must be active"));
+ network_ppp_stream_uart_irq_enable(self);
}
- if (self->client_task_handle != NULL) {
+ if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) {
mp_raise_OSError(MP_EALREADY);
}
- switch (parsed_args[ARG_authmode].u_int) {
+ switch (parsed_args[ARG_security].u_int) {
case PPPAUTHTYPE_NONE:
case PPPAUTHTYPE_PAP:
case PPPAUTHTYPE_CHAP:
@@ -219,39 +330,49 @@ static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw
mp_raise_ValueError(MP_ERROR_TEXT("invalid auth"));
}
- if (parsed_args[ARG_authmode].u_int != PPPAUTHTYPE_NONE) {
- const char *username_str = mp_obj_str_get_str(parsed_args[ARG_username].u_obj);
- const char *password_str = mp_obj_str_get_str(parsed_args[ARG_password].u_obj);
- pppapi_set_auth(self->pcb, parsed_args[ARG_authmode].u_int, username_str, password_str);
+ if (parsed_args[ARG_security].u_int != PPPAUTHTYPE_NONE) {
+ const char *user_str = mp_obj_str_get_str(parsed_args[ARG_user].u_obj);
+ const char *key_str = mp_obj_str_get_str(parsed_args[ARG_key].u_obj);
+ pppapi_set_auth(self->pcb, parsed_args[ARG_security].u_int, user_str, key_str);
}
- if (pppapi_set_default(self->pcb) != ESP_OK) {
- mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("set default failed"));
+
+ if (pppapi_set_default(self->pcb) != ERR_OK) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_set_default failed"));
}
ppp_set_usepeerdns(self->pcb, true);
- if (pppapi_connect(self->pcb, 0) != ESP_OK) {
- mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("connect failed"));
+ if (pppapi_connect(self->pcb, 0) != ERR_OK) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_connect failed"));
}
- if (xTaskCreatePinnedToCore(pppos_client_task, "ppp", 2048, self, 1, (TaskHandle_t *)&self->client_task_handle, MP_TASK_COREID) != pdPASS) {
- mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("failed to create worker task"));
- }
+ self->state = STATE_CONNECTING;
+
+ // Do a poll in case there is data waiting on the input stream.
+ network_ppp_poll(1, args);
return mp_const_none;
}
-MP_DEFINE_CONST_FUN_OBJ_KW(ppp_connect_obj, 1, ppp_connect_py);
+static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_connect_obj, 1, network_ppp_connect);
-static mp_obj_t ppp_delete(mp_obj_t self_in) {
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(self_in);
- mp_obj_t args[] = {self, mp_const_false};
- ppp_active(2, args);
+static mp_obj_t network_ppp_disconnect(mp_obj_t self_in) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) {
+ // Initiate close and wait for PPPERR_USER callback.
+ pppapi_close(self->pcb, 0);
+ }
return mp_const_none;
}
-MP_DEFINE_CONST_FUN_OBJ_1(ppp_delete_obj, ppp_delete);
+static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_disconnect_obj, network_ppp_disconnect);
+
+static mp_obj_t network_ppp_isconnected(mp_obj_t self_in) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ return mp_obj_new_bool(self->state == STATE_CONNECTED);
+}
+static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_isconnected_obj, network_ppp_isconnected);
-static mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) {
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+static mp_obj_t network_ppp_ifconfig(size_t n_args, const mp_obj_t *args) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
if (n_args == 1) {
// get
const ip_addr_t *dns;
@@ -282,11 +403,11 @@ static mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) {
return mp_const_none;
}
}
-MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_ifconfig_obj, 1, 2, ppp_ifconfig);
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_ifconfig_obj, 1, 2, network_ppp_ifconfig);
-static mp_obj_t ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
+static mp_obj_t network_ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
if (kwargs->used == 0) {
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
if (self->pcb == NULL) {
mp_raise_ValueError(MP_ERROR_TEXT("PPP not active"));
}
@@ -318,94 +439,64 @@ static mp_obj_t ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwar
}
return mp_const_none;
}
-static MP_DEFINE_CONST_FUN_OBJ_KW(ppp_ipconfig_obj, 1, ppp_ipconfig);
+static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_ipconfig_obj, 1, network_ppp_ipconfig);
-static mp_obj_t ppp_status(mp_obj_t self_in) {
- return mp_const_none;
-}
-static MP_DEFINE_CONST_FUN_OBJ_1(ppp_status_obj, ppp_status);
-
-static mp_obj_t ppp_isconnected(mp_obj_t self_in) {
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(self_in);
- return mp_obj_new_bool(self->connected);
-}
-static MP_DEFINE_CONST_FUN_OBJ_1(ppp_isconnected_obj, ppp_isconnected);
-
-static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
- if (n_args != 1 && kwargs->used != 0) {
- mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed"));
- }
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
-
- if (kwargs->used != 0) {
- for (size_t i = 0; i < kwargs->alloc; i++) {
- if (mp_map_slot_is_filled(kwargs, i)) {
- switch (mp_obj_str_get_qstr(kwargs->table[i].key)) {
- case MP_QSTR_stream: {
- if (kwargs->table[i].value != mp_const_none) {
- mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
- }
- self->stream = kwargs->table[i].value;
- break;
- }
- default:
- break;
- }
+static mp_obj_t network_ppp_active(size_t n_args, const mp_obj_t *args) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ if (n_args > 1) {
+ if (mp_obj_is_true(args[1])) {
+ if (self->state >= STATE_ACTIVE) {
+ return mp_const_true;
}
- }
- return mp_const_none;
- }
- if (n_args != 2) {
- mp_raise_TypeError(MP_ERROR_TEXT("can query only one param"));
- }
-
- mp_obj_t val = mp_const_none;
+ self->pcb = pppapi_pppos_create(&self->netif, network_ppp_output_callback, network_ppp_status_cb, self);
+ if (self->pcb == NULL) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppos_create failed"));
+ }
+ self->state = STATE_ACTIVE;
- switch (mp_obj_str_get_qstr(args[1])) {
- case MP_QSTR_stream: {
- val = self->stream;
- break;
- }
- case MP_QSTR_ifname: {
- if (self->pcb != NULL) {
- struct netif *pppif = ppp_netif(self->pcb);
- char ifname[NETIF_NAMESIZE + 1] = {0};
- netif_index_to_name(netif_get_index(pppif), ifname);
- if (ifname[0] != 0) {
- val = mp_obj_new_str_from_cstr((char *)ifname);
- }
+ network_ppp_stream_uart_irq_enable(self);
+ } else {
+ if (self->state < STATE_ACTIVE) {
+ return mp_const_false;
}
- break;
+
+ network_ppp___del__(MP_OBJ_FROM_PTR(self));
}
- default:
- mp_raise_ValueError(MP_ERROR_TEXT("unknown config param"));
}
-
- return val;
+ return mp_obj_new_bool(self->state >= STATE_ACTIVE);
}
-static MP_DEFINE_CONST_FUN_OBJ_KW(ppp_config_obj, 1, ppp_config);
-
-static const mp_rom_map_elem_t ppp_if_locals_dict_table[] = {
- { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&ppp_active_obj) },
- { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&ppp_connect_obj) },
- { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&ppp_isconnected_obj) },
- { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&ppp_status_obj) },
- { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&ppp_config_obj) },
- { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&ppp_ifconfig_obj) },
- { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&ppp_ipconfig_obj) },
- { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ppp_delete_obj) },
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_active_obj, 1, 2, network_ppp_active);
+
+static const mp_rom_map_elem_t network_ppp_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&network_ppp___del___obj) },
+ { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_ppp_config_obj) },
+ { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_ppp_status_obj) },
+ { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&network_ppp_connect_obj) },
+ { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&network_ppp_disconnect_obj) },
+ { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_ppp_isconnected_obj) },
+ { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_ppp_ifconfig_obj) },
+ { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_ppp_ipconfig_obj) },
+ { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&network_ppp_poll_obj) },
+
+ { MP_ROM_QSTR(MP_QSTR_SEC_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) },
+ { MP_ROM_QSTR(MP_QSTR_SEC_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) },
+ { MP_ROM_QSTR(MP_QSTR_SEC_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) },
+
+ // Deprecated interface for backwards compatibility
+ { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&network_ppp_active_obj) },
{ MP_ROM_QSTR(MP_QSTR_AUTH_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) },
{ MP_ROM_QSTR(MP_QSTR_AUTH_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) },
{ MP_ROM_QSTR(MP_QSTR_AUTH_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) },
};
-static MP_DEFINE_CONST_DICT(ppp_if_locals_dict, ppp_if_locals_dict_table);
+static MP_DEFINE_CONST_DICT(network_ppp_locals_dict, network_ppp_locals_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
- ppp_if_type,
+ esp_network_ppp_lwip_type,
MP_QSTR_PPP,
MP_TYPE_FLAG_NONE,
- locals_dict, &ppp_if_locals_dict
+ make_new, network_ppp_make_new,
+ locals_dict, &network_ppp_locals_dict
);
#endif
diff --git a/ports/esp32/panichandler.c b/ports/esp32/panichandler.c
index e6ff98ded2..5211286f84 100644
--- a/ports/esp32/panichandler.c
+++ b/ports/esp32/panichandler.c
@@ -42,11 +42,20 @@
#endif
void __real_esp_panic_handler(void *);
-void esp_panic_handler_reconfigure_wdts(uint32_t timeout_ms);
void panic_print_str(const char *str);
+#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2)
+void esp_panic_handler_reconfigure_wdts(uint32_t timeout_ms);
+#else
+void esp_panic_handler_feed_wdts(void);
+#endif
+
void MICROPY_WRAP_PANICHANDLER_FUN(__wrap_esp_panic_handler)(void *info) {
+ #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2)
esp_panic_handler_reconfigure_wdts(1000);
+ #else
+ esp_panic_handler_feed_wdts();
+ #endif
const static char *msg = MICROPY_WRAP_PANICHANDLER_STR(
"\r\n"
diff --git a/ports/esp32/partitions-16MiB-ota.csv b/ports/esp32/partitions-16MiB-ota.csv
deleted file mode 100644
index a6f83bc46b..0000000000
--- a/ports/esp32/partitions-16MiB-ota.csv
+++ /dev/null
@@ -1,10 +0,0 @@
-# Partition table for MicroPython with OTA support using 16MB flash
-# Notes: the offset of the partition table itself is set in
-# $IDF_PATH/components/partition_table/Kconfig.projbuild.
-# Name, Type, SubType, Offset, Size, Flags
-nvs, data, nvs, 0x9000, 0x4000,
-otadata, data, ota, 0xd000, 0x2000,
-phy_init, data, phy, 0xf000, 0x1000,
-ota_0, app, ota_0, 0x10000, 0x270000,
-ota_1, app, ota_1, 0x280000, 0x270000,
-vfs, data, fat, 0x4f0000, 0xb10000,
diff --git a/ports/esp32/partitions-16MiB.csv b/ports/esp32/partitions-16MiB.csv
deleted file mode 100644
index ae926c7b94..0000000000
--- a/ports/esp32/partitions-16MiB.csv
+++ /dev/null
@@ -1,7 +0,0 @@
-# Notes: the offset of the partition table itself is set in
-# $IDF_PATH/components/partition_table/Kconfig.projbuild.
-# Name, Type, SubType, Offset, Size, Flags
-nvs, data, nvs, 0x9000, 0x6000,
-phy_init, data, phy, 0xf000, 0x1000,
-factory, app, factory, 0x10000, 0x1F0000,
-vfs, data, fat, 0x200000, 0xE00000,
diff --git a/ports/esp32/partitions-2MiB.csv b/ports/esp32/partitions-2MiB.csv
index ea6626825c..5449201a7a 100644
--- a/ports/esp32/partitions-2MiB.csv
+++ b/ports/esp32/partitions-2MiB.csv
@@ -4,4 +4,3 @@
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x160000,
-vfs, data, fat, 0x170000, 0x50000,
diff --git a/ports/esp32/partitions-32MiB.csv b/ports/esp32/partitions-32MiB.csv
deleted file mode 100644
index 31591c9949..0000000000
--- a/ports/esp32/partitions-32MiB.csv
+++ /dev/null
@@ -1,7 +0,0 @@
-# Notes: the offset of the partition table itself is set in
-# $IDF_PATH/components/partition_table/Kconfig.projbuild.
-# Name, Type, SubType, Offset, Size, Flags
-nvs, data, nvs, 0x9000, 0x6000,
-phy_init, data, phy, 0xf000, 0x1000,
-factory, app, factory, 0x10000, 0x1F0000,
-vfs, data, fat, 0x200000, 0x1E00000,
diff --git a/ports/esp32/partitions-4MiB-ota.csv b/ports/esp32/partitions-4MiB-ota.csv
index 094ad76660..9cbb422799 100644
--- a/ports/esp32/partitions-4MiB-ota.csv
+++ b/ports/esp32/partitions-4MiB-ota.csv
@@ -7,4 +7,3 @@ otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
ota_0, app, ota_0, 0x10000, 0x180000,
ota_1, app, ota_1, 0x190000, 0x180000,
-vfs, data, fat, 0x310000, 0x0f0000,
diff --git a/ports/esp32/partitions-4MiB-romfs.csv b/ports/esp32/partitions-4MiB-romfs.csv
index dd02506e54..29033ff3ab 100644
--- a/ports/esp32/partitions-4MiB-romfs.csv
+++ b/ports/esp32/partitions-4MiB-romfs.csv
@@ -5,4 +5,3 @@ nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x1D0000,
romfs, data, 0x8f, 0x1E0000, 0x20000,
-vfs, data, fat, 0x200000, 0x200000,
diff --git a/ports/esp32/partitions-4MiB.csv b/ports/esp32/partitions-4MiBplus.csv
index 53f0f16744..460e5cc0e9 100644
--- a/ports/esp32/partitions-4MiB.csv
+++ b/ports/esp32/partitions-4MiBplus.csv
@@ -1,7 +1,10 @@
+# This partition table is for devices with 4MiB or more of flash.
+# The first 2MiB is used for bootloader, nvs, phy_init and firmware.
+# The remaining flash is for the user filesystem(s).
+
# Notes: the offset of the partition table itself is set in
# $IDF_PATH/components/partition_table/Kconfig.projbuild.
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x1F0000,
-vfs, data, fat, 0x200000, 0x200000,
diff --git a/ports/esp32/partitions-8MiB.csv b/ports/esp32/partitions-8MiB.csv
deleted file mode 100644
index 582d3b50e5..0000000000
--- a/ports/esp32/partitions-8MiB.csv
+++ /dev/null
@@ -1,7 +0,0 @@
-# Notes: the offset of the partition table itself is set in
-# $IDF_PATH/components/partition_table/Kconfig.projbuild.
-# Name, Type, SubType, Offset, Size, Flags
-nvs, data, nvs, 0x9000, 0x6000,
-phy_init, data, phy, 0xf000, 0x1000,
-factory, app, factory, 0x10000, 0x1F0000,
-vfs, data, fat, 0x200000, 0x600000,
diff --git a/ports/esp32/partitions-32MiB-ota.csv b/ports/esp32/partitions-8MiBplus-ota.csv
index 7366a2ad8d..09a8e6d702 100644
--- a/ports/esp32/partitions-32MiB-ota.csv
+++ b/ports/esp32/partitions-8MiBplus-ota.csv
@@ -1,4 +1,7 @@
-# Partition table for MicroPython with OTA support using 32MB flash
+# This partition table is for devices with 8MiB or more of flash and OTA support.
+# The first 5056kiB is used for bootloader, nvs, phy_init and firmware.
+# The remaining flash is for the user filesystem(s).
+
# Notes: the offset of the partition table itself is set in
# $IDF_PATH/components/partition_table/Kconfig.projbuild.
# Name, Type, SubType, Offset, Size, Flags
@@ -7,4 +10,3 @@ otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
ota_0, app, ota_0, 0x10000, 0x270000,
ota_1, app, ota_1, 0x280000, 0x270000,
-vfs, data, fat, 0x4f0000, 0x1B10000,
diff --git a/ports/esp32/tools/metrics_esp32.py b/ports/esp32/tools/metrics_esp32.py
index 5e65a78c97..9efaae63a9 100755
--- a/ports/esp32/tools/metrics_esp32.py
+++ b/ports/esp32/tools/metrics_esp32.py
@@ -37,7 +37,7 @@ import sys
import subprocess
from dataclasses import dataclass
-IDF_VERS = ("v5.2.2",)
+IDF_VERS = ("v5.4.1",)
BUILDS = (
("ESP32_GENERIC", ""),
diff --git a/ports/esp8266/esp_init_data.c b/ports/esp8266/esp_init_data.c
index c369ed58f5..a3dd6ffed1 100644
--- a/ports/esp8266/esp_init_data.c
+++ b/ports/esp8266/esp_init_data.c
@@ -30,7 +30,7 @@
#include "user_interface.h"
#include "extmod/misc.h"
-NORETURN void call_user_start(void);
+MP_NORETURN void call_user_start(void);
void ets_printf(const char *fmt, ...);
extern char flashchip;
diff --git a/ports/esp8266/esp_mphal.h b/ports/esp8266/esp_mphal.h
index 0a4f92ac0b..fb2e441c97 100644
--- a/ports/esp8266/esp_mphal.h
+++ b/ports/esp8266/esp_mphal.h
@@ -27,11 +27,20 @@
#include "user_interface.h"
#include "py/ringbuf.h"
#include "shared/runtime/interrupt_char.h"
+#include "ets_alt_task.h"
#include "xtirq.h"
#define MICROPY_BEGIN_ATOMIC_SECTION() esp_disable_irq()
#define MICROPY_END_ATOMIC_SECTION(state) esp_enable_irq(state)
+// During machine.time_pulse_us, feed WDT every now and then.
+#define MICROPY_PY_MACHINE_TIME_PULSE_US_HOOK(dt) \
+ do { \
+ if ((dt & 0xffff) == 0xffff && !ets_loop_dont_feed_sw_wdt) { \
+ system_soft_wdt_feed(); \
+ } \
+ } while (0)
+
void mp_sched_keyboard_interrupt(void);
struct _mp_print_t;
diff --git a/ports/esp8266/main.c b/ports/esp8266/main.c
index 2dd7c1dece..da712fce9b 100644
--- a/ports/esp8266/main.c
+++ b/ports/esp8266/main.c
@@ -49,6 +49,64 @@
static char heap[38 * 1024];
+#if MICROPY_HW_HARD_FAULT_DEBUG
+
+static void format_hex(uint32_t hex, char *buffer) {
+ static const char table[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ int offset = 7;
+ uint32_t value = hex;
+ while (offset >= 0) {
+ buffer[offset--] = table[value & 0x0F];
+ value >>= 4;
+ }
+}
+
+static void print_reset_info(void) {
+ struct rst_info *rst_info = system_get_rst_info();
+ if ((rst_info->reason == REASON_WDT_RST) || (rst_info->reason == REASON_EXCEPTION_RST) || (rst_info->reason == REASON_SOFT_WDT_RST)) {
+ char buffer[8];
+ mp_hal_stdout_tx_str("\r\n\r\nThe system restarted due to an error.\r\n\r\nReason: ");
+ switch (rst_info->reason) {
+ case REASON_WDT_RST:
+ mp_hal_stdout_tx_str("WDT");
+ break;
+
+ case REASON_EXCEPTION_RST:
+ mp_hal_stdout_tx_str("EXCEPTION");
+ break;
+
+ case REASON_SOFT_WDT_RST:
+ mp_hal_stdout_tx_str("SOFT_WDT");
+ break;
+
+ default:
+ assert(!"Should not ever get here.");
+ break;
+ }
+ mp_hal_stdout_tx_str(" Cause: ");
+ format_hex(rst_info->exccause, buffer);
+ mp_hal_stdout_tx_strn(buffer, sizeof(buffer));
+ mp_hal_stdout_tx_str(" EPC1: ");
+ format_hex(rst_info->epc1, buffer);
+ mp_hal_stdout_tx_strn(buffer, sizeof(buffer));
+ mp_hal_stdout_tx_str(" EPC2: ");
+ format_hex(rst_info->epc2, buffer);
+ mp_hal_stdout_tx_strn(buffer, sizeof(buffer));
+ mp_hal_stdout_tx_str(" EPC3: ");
+ format_hex(rst_info->epc3, buffer);
+ mp_hal_stdout_tx_strn(buffer, sizeof(buffer));
+ mp_hal_stdout_tx_str(" Exception Vector address: ");
+ format_hex(rst_info->excvaddr, buffer);
+ mp_hal_stdout_tx_strn(buffer, sizeof(buffer));
+ mp_hal_stdout_tx_str(" DEPC: ");
+ format_hex(rst_info->depc, buffer);
+ mp_hal_stdout_tx_strn(buffer, sizeof(buffer));
+ mp_hal_stdout_tx_str("\r\n\r\n");
+ }
+}
+
+#endif
+
static void mp_reset(void) {
mp_stack_set_top((void *)0x40000000);
mp_stack_set_limit(8192);
@@ -114,6 +172,10 @@ void init_done(void) {
pyexec_event_repl_init();
#endif
+ #if MICROPY_HW_HARD_FAULT_DEBUG
+ print_reset_info();
+ #endif
+
#if !MICROPY_REPL_EVENT_DRIVEN
soft_reset:
for (;;) {
diff --git a/ports/esp8266/modmachine.c b/ports/esp8266/modmachine.c
index 23ccf8cebf..d43fe38245 100644
--- a/ports/esp8266/modmachine.c
+++ b/ports/esp8266/modmachine.c
@@ -33,7 +33,6 @@
#include "os_type.h"
#include "osapi.h"
#include "etshal.h"
-#include "ets_alt_task.h"
#include "user_interface.h"
// #define MACHINE_WAKE_IDLE (0x01)
@@ -71,7 +70,7 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
system_update_cpu_freq(freq);
}
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
system_restart();
// we must not return
@@ -114,7 +113,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
}
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
// default to sleep forever
uint32_t sleep_us = 0;
@@ -327,32 +326,3 @@ MP_DEFINE_CONST_OBJ_TYPE(
print, esp_timer_print,
locals_dict, &esp_timer_locals_dict
);
-
-// Custom version of this function that feeds system WDT if necessary
-mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us) {
- int nchanges = 2;
- uint32_t start = system_get_time(); // in microseconds
- for (;;) {
- uint32_t dt = system_get_time() - start;
-
- // Check if pin changed to wanted value
- if (mp_hal_pin_read(pin) == pulse_level) {
- if (--nchanges == 0) {
- return dt;
- }
- pulse_level = 1 - pulse_level;
- start = system_get_time();
- continue;
- }
-
- // Check for timeout
- if (dt >= timeout_us) {
- return (mp_uint_t)-nchanges;
- }
-
- // Only feed WDT every now and then, to make sure edge timing is accurate
- if ((dt & 0xffff) == 0xffff && !ets_loop_dont_feed_sw_wdt) {
- system_soft_wdt_feed();
- }
- }
-}
diff --git a/ports/esp8266/modtime.c b/ports/esp8266/modtime.c
index 0903005597..e99d920fde 100644
--- a/ports/esp8266/modtime.c
+++ b/ports/esp8266/modtime.c
@@ -31,7 +31,7 @@
// Return the localtime as an 8-tuple.
static mp_obj_t mp_time_localtime_get(void) {
- mp_int_t seconds = pyb_rtc_get_us_since_epoch() / 1000 / 1000;
+ mp_uint_t seconds = pyb_rtc_get_us_since_epoch() / 1000u / 1000u;
timeutils_struct_time_t tm;
timeutils_seconds_since_epoch_to_struct_time(seconds, &tm);
mp_obj_t tuple[8] = {
@@ -50,5 +50,5 @@ static mp_obj_t mp_time_localtime_get(void) {
// Returns the number of seconds, as an integer, since the Epoch.
static mp_obj_t mp_time_time_get(void) {
// get date and time
- return mp_obj_new_int(pyb_rtc_get_us_since_epoch() / 1000 / 1000);
+ return timeutils_obj_from_timestamp(pyb_rtc_get_us_since_epoch() / 1000 / 1000);
}
diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h
index 83d80a7c96..03f3bb643d 100644
--- a/ports/esp8266/mpconfigport.h
+++ b/ports/esp8266/mpconfigport.h
@@ -117,6 +117,9 @@
#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */
#define MICROPY_ESP8266_APA102 (1)
+// Print error information at reboot time if the board crashed.
+#define MICROPY_HW_HARD_FAULT_DEBUG (0)
+
// No blocking wait-for-event on ESP8266, only non-blocking pump of the "OS" event
// loop
//
diff --git a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json
index 9f260b3a00..804b4fd734 100644
--- a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json
+++ b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json
@@ -15,7 +15,7 @@
"Metro_M7.jpg"
],
"mcu": "mimxrt",
- "product": "Adafruit Metro M7",
+ "product": "Metro M7",
"thumbnail": "",
"url": "https://www.adafruit.com/product/4950",
"vendor": "Adafruit"
diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/board.json b/ports/mimxrt/boards/OLIMEX_RT1010/board.json
index 882b931f8b..a6f0316bc8 100644
--- a/ports/mimxrt/boards/OLIMEX_RT1010/board.json
+++ b/ports/mimxrt/boards/OLIMEX_RT1010/board.json
@@ -12,8 +12,8 @@
"OLIMEX_RT1010Py.jpg"
],
"mcu": "mimxrt",
- "product": "Olimex_RT1010Py",
+ "product": "RT1010-Py",
"thumbnail": "",
- "url": "https://www.olimex.com/Products/ARM/NXP",
- "vendor": "OLIMEX"
+ "url": "https://www.olimex.com/Products/MicroPython/RT1010-Py",
+ "vendor": "Olimex"
}
diff --git a/ports/mimxrt/cyw43_configport.h b/ports/mimxrt/cyw43_configport.h
index cf2f5d4b9f..b1dd6fbe6a 100644
--- a/ports/mimxrt/cyw43_configport.h
+++ b/ports/mimxrt/cyw43_configport.h
@@ -28,12 +28,8 @@
#define MICROPY_INCLUDED_MIMXRT_CYW43_CONFIGPORT_H
// The board-level config will be included here, so it can set some CYW43 values.
-#include "py/mpconfig.h"
-#include "py/mperrno.h"
-#include "py/mphal.h"
-#include "extmod/modnetwork.h"
#include "extmod/mpbthci.h"
-#include "pendsv.h"
+#include "extmod/cyw43_config_common.h"
#include "sdio.h"
#define CYW43_USE_SPI (0)
@@ -61,55 +57,15 @@
#define CYW43_BT_UART_BAUDRATE_DOWNLOAD_FIRMWARE MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE
#endif
-#define CYW43_IOCTL_TIMEOUT_US (1000000)
-#define CYW43_SLEEP_MAX (50)
-#define CYW43_NETUTILS (1)
#define CYW43_CLEAR_SDIO_INT (1)
-#define CYW43_EPERM MP_EPERM // Operation not permitted
-#define CYW43_EIO MP_EIO // I/O error
-#define CYW43_EINVAL MP_EINVAL // Invalid argument
-#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out
-
-#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER
-#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT
-#define CYW43_THREAD_LOCK_CHECK
-
-#define CYW43_HOST_NAME mod_network_hostname_data
-
#define CYW43_SDPCM_SEND_COMMON_WAIT __WFI();
#define CYW43_DO_IOCTL_WAIT __WFI();
-#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a)
-
-#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT
-#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT
-#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE
-#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP
-#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN
-
-#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0
-#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR
-
-#define cyw43_hal_ticks_us mp_hal_ticks_us
-#define cyw43_hal_ticks_ms mp_hal_ticks_ms
-
-#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t
-#define cyw43_hal_pin_read mp_hal_pin_read
-#define cyw43_hal_pin_low mp_hal_pin_low
-#define cyw43_hal_pin_high mp_hal_pin_high
-
-#define cyw43_hal_get_mac mp_hal_get_mac
-#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii
-#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac
-
#define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate
#define cyw43_hal_uart_write mp_bluetooth_hci_uart_write
#define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar
-#define cyw43_delay_us mp_hal_delay_us
-#define cyw43_delay_ms mp_hal_delay_ms
-
#define cyw43_bluetooth_controller_init mp_bluetooth_hci_controller_init
#define cyw43_bluetooth_controller_deinit mp_bluetooth_hci_controller_deinit
#define cyw43_bluetooth_controller_woken mp_bluetooth_hci_controller_woken
@@ -135,7 +91,7 @@
#define CYW43_PIN_RFSW_VDD pin_WL_RFSW_VDD
#endif
-#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func)
+#undef cyw43_hal_pin_config // mp_hal_pin_config not yet implemented on this port
static inline void cyw43_hal_pin_config(cyw43_hal_pin_obj_t pin, uint8_t mode, uint8_t pull, uint8_t alt) {
machine_pin_set_mode(pin, mode);
@@ -175,6 +131,4 @@ static inline int cyw43_sdio_transfer_cmd53(bool write, uint32_t block_size, uin
return sdio_transfer_cmd53(write, block_size, arg, len, buf);
}
-#define CYW43_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK
-
#endif // MICROPY_INCLUDED_MIMXRT_CYW43_CONFIGPORT_H
diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c
index 204699bcc4..ad078ff3ac 100644
--- a/ports/mimxrt/modmachine.c
+++ b/ports/mimxrt/modmachine.c
@@ -87,7 +87,7 @@ static mp_obj_t mp_machine_unique_id(void) {
return mp_obj_new_bytes(id, sizeof(id));
}
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
WDOG_TriggerSystemSoftwareReset(WDOG1);
while (true) {
;
@@ -131,7 +131,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
mp_raise_NotImplementedError(NULL);
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
if (n_args != 0) {
mp_int_t seconds = mp_obj_get_int(args[0]) / 1000;
if (seconds > 0) {
@@ -159,7 +159,7 @@ NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
}
}
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
#if defined(MICROPY_BOARD_ENTER_BOOTLOADER)
// If a board has a custom bootloader, call it first.
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
diff --git a/ports/mimxrt/modtime.c b/ports/mimxrt/modtime.c
index a2ccf1b273..3bcfac411c 100644
--- a/ports/mimxrt/modtime.c
+++ b/ports/mimxrt/modtime.c
@@ -51,14 +51,7 @@ static mp_obj_t mp_time_localtime_get(void) {
static mp_obj_t mp_time_time_get(void) {
snvs_lp_srtc_datetime_t t;
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
- // EPOCH is 1970 for this port, which leads to the following trouble:
- // timeutils_seconds_since_epoch() calls timeutils_seconds_since_2000(), and
- // timeutils_seconds_since_2000() subtracts 2000 from year, but uses
- // an unsigned number for seconds, That causes an underrun, which is not
- // fixed by adding the TIMEUTILS_SECONDS_1970_TO_2000.
- // Masking it to 32 bit for year < 2000 fixes it.
- return mp_obj_new_int_from_ull(
+ return timeutils_obj_from_timestamp(
timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second)
- & (t.year < 2000 ? 0xffffffff : 0xffffffffffff)
);
}
diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h
index 9a7dfc630f..e1c605f452 100644
--- a/ports/mimxrt/mpconfigport.h
+++ b/ports/mimxrt/mpconfigport.h
@@ -178,20 +178,12 @@ extern const struct _mp_obj_type_t network_lan_type;
#define MICROPY_HW_NIC_ETH
#endif
-#if MICROPY_PY_NETWORK_CYW43
-extern const struct _mp_obj_type_t mp_network_cyw43_type;
-#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) },
-#else
-#define MICROPY_HW_NIC_CYW43
-#endif
-
#ifndef MICROPY_BOARD_NETWORK_INTERFACES
#define MICROPY_BOARD_NETWORK_INTERFACES
#endif
#define MICROPY_PORT_NETWORK_INTERFACES \
MICROPY_HW_NIC_ETH \
- MICROPY_HW_NIC_CYW43 \
MICROPY_BOARD_NETWORK_INTERFACES \
#ifndef MICROPY_BOARD_ROOT_POINTERS
diff --git a/ports/minimal/main.c b/ports/minimal/main.c
index 5f472c1afd..b9e9034bf5 100644
--- a/ports/minimal/main.c
+++ b/ports/minimal/main.c
@@ -87,7 +87,7 @@ void nlr_jump_fail(void *val) {
}
}
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
while (1) {
;
}
diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile
index 59e74dce42..7b16974f97 100644
--- a/ports/nrf/Makefile
+++ b/ports/nrf/Makefile
@@ -129,14 +129,7 @@ CFLAGS_MCU_m4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 -mfpu=fpv4-s
CFLAGS_MCU_m0 = $(CFLAGS_CORTEX_M) -fshort-enums -mtune=cortex-m0 -mcpu=cortex-m0 -mfloat-abi=soft
-# linker wrap does not work with lto on older gcc/binutils: https://sourceware.org/bugzilla/show_bug.cgi?id=24406
-GCC_VERSION = $(shell arm-none-eabi-gcc -dumpversion)
-GCC_MAJOR_VERS = $(word 1,$(subst ., ,$(GCC_VERSION)))
-ifeq ($(shell test $(GCC_MAJOR_VERS) -ge 10; echo $$?),0)
LTO ?= 1
-else
-LTO ?= 0
-endif
ifeq ($(LTO),1)
CFLAGS += -flto
@@ -268,7 +261,6 @@ SRC_C += $(addprefix lib/tinyusb/src/,\
portable/nordic/nrf5x/dcd_nrf5x.c \
)
-LDFLAGS += -Wl,--wrap=dcd_event_handler
endif
DRIVERS_SRC_C += $(addprefix modules/,\
diff --git a/ports/nrf/boards/ACTINIUS_ICARUS/board.json b/ports/nrf/boards/ACTINIUS_ICARUS/board.json
index c1d9f1c4ef..09ed3fcba3 100644
--- a/ports/nrf/boards/ACTINIUS_ICARUS/board.json
+++ b/ports/nrf/boards/ACTINIUS_ICARUS/board.json
@@ -8,7 +8,7 @@
"icarus-v1.4-front-shadow-p-800.jpg"
],
"mcu": "nrf91",
- "product": "actinius_icarus",
+ "product": "Icarus",
"thumbnail": "",
"url": "https://www.actinius.com/icarus",
"vendor": "Actinius"
diff --git a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json
index 9079fbd666..72b5135611 100644
--- a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json
+++ b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json
@@ -15,7 +15,7 @@
"ABX00031_01.iso_998x749.jpg"
],
"mcu": "nrf52",
- "product": "Arduino Nano 33 BLE Sense",
+ "product": "Nano 33 BLE Sense",
"thumbnail": "",
"url": "https://store.arduino.cc/products/arduino-nano-33-ble-sense",
"vendor": "Arduino"
diff --git a/ports/nrf/boards/ARDUINO_PRIMO/board.json b/ports/nrf/boards/ARDUINO_PRIMO/board.json
index f7afed0ced..236acfd448 100644
--- a/ports/nrf/boards/ARDUINO_PRIMO/board.json
+++ b/ports/nrf/boards/ARDUINO_PRIMO/board.json
@@ -8,8 +8,8 @@
"arduino_primo.jpg"
],
"mcu": "nrf52",
- "product": "arduino_primo",
+ "product": "Primo",
"thumbnail": "",
- "url": "",
+ "url": "https://docs.arduino.cc/retired/boards/arduino-primo/",
"vendor": "Arduino"
}
diff --git a/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json b/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json
index 5b6e5747d5..e910ea8db2 100644
--- a/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json
+++ b/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json
@@ -8,7 +8,7 @@
"blyst-nano-mod-4_jpg_project-body.jpg"
],
"mcu": "nrf52",
- "product": "blueio_tag_evim",
+ "product": "BLUEIO Tag EVIM",
"thumbnail": "",
"url": "https://www.i-syst.com/index.php/products/blyst-nano",
"vendor": "I-SYST"
diff --git a/ports/nrf/boards/DVK_BL652/board.json b/ports/nrf/boards/DVK_BL652/board.json
index 5c9cfad26e..d0942ca256 100644
--- a/ports/nrf/boards/DVK_BL652/board.json
+++ b/ports/nrf/boards/DVK_BL652/board.json
@@ -8,8 +8,8 @@
"BL652-SA_JPG-500.jpg"
],
"mcu": "nrf52",
- "product": "dvk_bl652",
+ "product": "DVK-BL652",
"thumbnail": "",
- "url": "https://www.lairdconnect.com/wireless-modules/bluetooth-modules/bluetooth-5-modules/bl652-series-bluetooth-v5-nfc-module",
- "vendor": "Laird Connectivity"
+ "url": "https://www.ezurio.com/wireless-modules/bluetooth-modules/bluetooth-5-modules/bl652-series-bluetooth-v5-nfc-module",
+ "vendor": "Ezurio"
}
diff --git a/ports/nrf/boards/EVK_NINA_B1/board.json b/ports/nrf/boards/EVK_NINA_B1/board.json
index 657b0fa06d..ef9c6ae5be 100644
--- a/ports/nrf/boards/EVK_NINA_B1/board.json
+++ b/ports/nrf/boards/EVK_NINA_B1/board.json
@@ -8,7 +8,7 @@
"EVK-NINA-B1_.jpg"
],
"mcu": "nrf52",
- "product": "evk_nina_b1",
+ "product": "EVK-NINA-B1",
"thumbnail": "",
"url": "https://www.u-blox.com/en/product/evk-nina-b1",
"vendor": "u-blox"
diff --git a/ports/nrf/boards/EVK_NINA_B3/board.json b/ports/nrf/boards/EVK_NINA_B3/board.json
index 54e3dc3585..6311b17338 100644
--- a/ports/nrf/boards/EVK_NINA_B3/board.json
+++ b/ports/nrf/boards/EVK_NINA_B3/board.json
@@ -8,7 +8,7 @@
"EVK-NINA-B3-top.jpg"
],
"mcu": "nrf52",
- "product": "evk_nina_b3",
+ "product": "EVK-NINA-B3",
"thumbnail": "",
"url": "https://www.u-blox.com/en/product/evk-nina-b3",
"vendor": "u-blox"
diff --git a/ports/nrf/boards/IBK_BLYST_NANO/board.json b/ports/nrf/boards/IBK_BLYST_NANO/board.json
index 562c33607b..ddbbef66f5 100644
--- a/ports/nrf/boards/IBK_BLYST_NANO/board.json
+++ b/ports/nrf/boards/IBK_BLYST_NANO/board.json
@@ -8,7 +8,7 @@
"blyst-nano-fingertip-close_jpg_content-body-gallery.jpg"
],
"mcu": "nrf52",
- "product": "ibk_blyst_nano",
+ "product": "IBK BLYST Nano",
"thumbnail": "",
"url": "https://www.i-syst.com/products/blyst-nano",
"vendor": "I-SYST"
diff --git a/ports/nrf/boards/IDK_BLYST_NANO/board.json b/ports/nrf/boards/IDK_BLYST_NANO/board.json
index 199721698d..cb692f44cd 100644
--- a/ports/nrf/boards/IDK_BLYST_NANO/board.json
+++ b/ports/nrf/boards/IDK_BLYST_NANO/board.json
@@ -8,7 +8,7 @@
"blyst-nano-fingertip-close_jpg_content-body-gallery.jpg"
],
"mcu": "nrf52",
- "product": "idk_blyst_nano",
+ "product": "IDK BLYST Nano",
"thumbnail": "",
"url": "https://www.i-syst.com/products/blyst-nano",
"vendor": "I-SYST"
diff --git a/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json b/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json
index 99d0caad23..102102b6c2 100644
--- a/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json
+++ b/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json
@@ -8,7 +8,7 @@
"dongle_pcba_case.jpg"
],
"mcu": "nrf52",
- "product": "nrf52840-mdk-usb-dongle",
+ "product": "nrf52840 MDK USB Dongle",
"thumbnail": "",
"url": "https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle",
"vendor": "Makerdiary"
diff --git a/ports/nrf/boards/PCA10028/mpconfigboard.h b/ports/nrf/boards/PCA10028/mpconfigboard.h
index 3dc8ed3459..df2e4e85d9 100644
--- a/ports/nrf/boards/PCA10028/mpconfigboard.h
+++ b/ports/nrf/boards/PCA10028/mpconfigboard.h
@@ -60,3 +60,8 @@
#define MICROPY_HW_SPI0_MISO (28)
#define HELP_TEXT_BOARD_LED "1,2,3,4"
+
+// The JLink CDC on the PCA10028 cannot accept more than 64 incoming bytes at a time.
+// That makes the UART REPL unreliable in general. But it can be improved to some
+// extent by setting the raw-paste buffer size to that limit of 64.
+#define MICROPY_REPL_STDIN_BUFFER_MAX (64)
diff --git a/ports/nrf/boards/PCA10040/mpconfigboard.h b/ports/nrf/boards/PCA10040/mpconfigboard.h
index b965bf319d..2b1c4c7ef5 100644
--- a/ports/nrf/boards/PCA10040/mpconfigboard.h
+++ b/ports/nrf/boards/PCA10040/mpconfigboard.h
@@ -64,3 +64,8 @@
#define MICROPY_HW_PWM2_NAME "PWM2"
#define HELP_TEXT_BOARD_LED "1,2,3,4"
+
+// The JLink CDC on the PCA10040 cannot accept more than 64 incoming bytes at a time.
+// That makes the UART REPL unreliable in general. But it can be improved to some
+// extent by setting the raw-paste buffer size to that limit of 64.
+#define MICROPY_REPL_STDIN_BUFFER_MAX (64)
diff --git a/ports/nrf/boards/SEEED_XIAO_NRF52/board.json b/ports/nrf/boards/SEEED_XIAO_NRF52/board.json
index d40355bb2e..b00044a56a 100644
--- a/ports/nrf/boards/SEEED_XIAO_NRF52/board.json
+++ b/ports/nrf/boards/SEEED_XIAO_NRF52/board.json
@@ -16,8 +16,8 @@
"XIAO_nrf52840_front.jpg"
],
"mcu": "nrf52",
- "product": "SEEED XIAO nRF52840 Sense",
+ "product": "XIAO nRF52840 Sense",
"thumbnail": "",
- "url": "https://www.seeedstudio.com",
+ "url": "https://www.seeedstudio.com/Seeed-XIAO-BLE-Sense-nRF52840-p-5253.html",
"vendor": "Seeed Studio"
}
diff --git a/ports/nrf/boards/WT51822_S4AT/board.json b/ports/nrf/boards/WT51822_S4AT/board.json
index 89d008c6f5..2ec42ac414 100644
--- a/ports/nrf/boards/WT51822_S4AT/board.json
+++ b/ports/nrf/boards/WT51822_S4AT/board.json
@@ -8,8 +8,8 @@
"WT51822-S4AT.jpg"
],
"mcu": "nrf51",
- "product": "wt51822_s4at",
+ "product": "WT51822-S4AT",
"thumbnail": "",
- "url": "http://www.wireless-tag.com/portfolio/wt51822-s4at-2/",
+ "url": "https://shop.wireless-tag.com/products/esp32-c3-mini-1-10pcs-espressif-esp32-c3-mini-1-4mb-flash-pcb-antenna-15-gpios-wifi-ble-5-module-esp32-c3-module-on-esp32-c3-chip",
"vendor": "Wireless-Tag"
}
diff --git a/ports/nrf/main.c b/ports/nrf/main.c
index 29550bd77a..21a71c7c9e 100644
--- a/ports/nrf/main.c
+++ b/ports/nrf/main.c
@@ -39,6 +39,8 @@
#include "py/stackctrl.h"
#include "py/gc.h"
#include "py/compile.h"
+#include "py/persistentcode.h"
+#include "extmod/misc.h"
#include "extmod/modmachine.h"
#include "shared/runtime/pyexec.h"
#include "readline.h"
@@ -107,7 +109,7 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) {
extern uint32_t _heap_start;
extern uint32_t _heap_end;
-void NORETURN _start(void) {
+void MP_NORETURN _start(void) {
// Hook for a board to run code at start up, for example check if a
// bootloader should be entered instead of the main application.
MICROPY_BOARD_STARTUP();
@@ -171,7 +173,8 @@ soft_reset:
MP_OBJ_NEW_SMALL_INT(MICROPY_HW_UART_REPL),
MP_OBJ_NEW_SMALL_INT(MICROPY_HW_UART_REPL_BAUD),
};
- MP_STATE_VM(dupterm_objs[0]) = MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, make_new)((mp_obj_t)&machine_uart_type, MP_ARRAY_SIZE(args), 0, args);
+ mp_obj_t uart = MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, make_new)((mp_obj_t)&machine_uart_type, MP_ARRAY_SIZE(args), 0, args);
+ mp_os_dupterm_obj.fun.var(1, &uart);
}
#endif
@@ -353,7 +356,7 @@ void HardFault_Handler(void) {
#endif
}
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
while (1) {
;
}
@@ -369,3 +372,16 @@ void MP_WEAK __assert_func(const char *file, int line, const char *func, const c
printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line);
__fatal_error("Assertion failed");
}
+
+#if MICROPY_EMIT_MACHINE_CODE
+void *nrf_native_code_commit(void *buf, unsigned int len, void *reloc) {
+ (void)len;
+ if (reloc) {
+ // Native code in RAM must execute from the IRAM region at 0x00800000, and so relocations
+ // to text must also point to this region. The MICROPY_MAKE_POINTER_CALLABLE macro will
+ // adjust the `buf` address from RAM to IRAM.
+ mp_native_relocate(reloc, buf, (uintptr_t)MICROPY_MAKE_POINTER_CALLABLE(buf) & ~1);
+ }
+ return buf;
+}
+#endif
diff --git a/ports/nrf/modules/machine/modmachine.c b/ports/nrf/modules/machine/modmachine.c
index de1d0e3124..f543265479 100644
--- a/ports/nrf/modules/machine/modmachine.c
+++ b/ports/nrf/modules/machine/modmachine.c
@@ -85,8 +85,6 @@
#define MICROPY_PY_MACHINE_EXTRA_GLOBALS \
{ MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&machine_info_obj) }, \
- { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, \
- { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, \
{ MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&machine_lightsleep_obj) }, \
{ MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pin_type) }, \
\
@@ -181,11 +179,11 @@ static mp_obj_t mp_machine_unique_id(void) {
}
// Resets the board in a manner similar to pushing the external RESET button.
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
NVIC_SystemReset();
}
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
for (;;) {
}
@@ -199,7 +197,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
__WFE();
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
mp_machine_reset();
}
@@ -214,24 +212,3 @@ static mp_obj_t mp_machine_get_freq(void) {
static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
mp_raise_NotImplementedError(NULL);
}
-
-static mp_obj_t machine_enable_irq(void) {
- #ifndef BLUETOOTH_SD
- __enable_irq();
- #else
-
- #endif
- return mp_const_none;
-}
-MP_DEFINE_CONST_FUN_OBJ_0(machine_enable_irq_obj, machine_enable_irq);
-
-// Resets the board in a manner similar to pushing the external RESET button.
-static mp_obj_t machine_disable_irq(void) {
- #ifndef BLUETOOTH_SD
- __disable_irq();
- #else
-
- #endif
- return mp_const_none;
-}
-MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq);
diff --git a/ports/nrf/modules/machine/uart.c b/ports/nrf/modules/machine/uart.c
index 8d5a73e095..4c75bf8209 100644
--- a/ports/nrf/modules/machine/uart.c
+++ b/ports/nrf/modules/machine/uart.c
@@ -46,6 +46,11 @@
#include "nrfx_uarte.h"
#endif
+#if defined(NRF52832)
+// The nRF52832 cannot write more than 255 bytes at a time.
+#define UART_MAX_TX_CHUNK (255)
+#endif
+
typedef struct _machine_uart_buf_t {
uint8_t tx_buf[1];
uint8_t rx_buf[1];
@@ -104,6 +109,7 @@ typedef struct _machine_uart_obj_t {
uint16_t timeout_char; // timeout waiting between chars (in ms)
uint8_t uart_id;
bool initialized; // static flag. Initialized to False
+ bool attached_to_repl;
#if MICROPY_PY_MACHINE_UART_IRQ
uint16_t mp_irq_trigger; // user IRQ trigger mask
uint16_t mp_irq_flags; // user IRQ active IRQ flags
@@ -118,6 +124,13 @@ static machine_uart_obj_t machine_uart_obj[] = {
};
void uart_init0(void) {
+ for (int i = 0; i < MP_ARRAY_SIZE(machine_uart_obj); i++) {
+ machine_uart_obj[i].attached_to_repl = false;
+ }
+}
+
+void uart_attach_to_repl(machine_uart_obj_t *self, bool attached) {
+ self->attached_to_repl = attached;
}
static int uart_find(mp_obj_t id) {
@@ -137,14 +150,16 @@ static void uart_event_handler(nrfx_uart_event_t const *p_event, void *p_context
if (p_event->type == NRFX_UART_EVT_RX_DONE) {
nrfx_uart_rx(self->p_uart, &self->buf.rx_buf[0], 1);
int chr = self->buf.rx_buf[0];
- #if !MICROPY_PY_BLE_NUS && MICROPY_KBD_EXCEPTION
- if (chr == mp_interrupt_char) {
- self->buf.rx_ringbuf.iget = 0;
- self->buf.rx_ringbuf.iput = 0;
- mp_sched_keyboard_interrupt();
- } else
- #endif
- {
+ if (self->attached_to_repl) {
+ #if MICROPY_KBD_EXCEPTION
+ if (chr == mp_interrupt_char) {
+ mp_sched_keyboard_interrupt();
+ } else
+ #endif
+ {
+ ringbuf_put((ringbuf_t *)&stdin_ringbuf, chr);
+ }
+ } else {
ringbuf_put((ringbuf_t *)&self->buf.rx_ringbuf, chr);
}
#if MICROPY_PY_MACHINE_UART_IRQ
@@ -446,17 +461,29 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf, mp_uin
#endif
machine_uart_obj_t *self = self_in;
- nrfx_err_t err = nrfx_uart_tx(self->p_uart, buf, size);
- if (err == NRFX_SUCCESS) {
+
+ // Send data out, in chunks if needed.
+ mp_uint_t remaining = size;
+ while (remaining) {
+ #ifdef UART_MAX_TX_CHUNK
+ mp_uint_t chunk = MIN(UART_MAX_TX_CHUNK, remaining);
+ #else
+ mp_uint_t chunk = remaining;
+ #endif
+ nrfx_err_t err = nrfx_uart_tx(self->p_uart, buf, chunk);
+ if (err != NRFX_SUCCESS) {
+ *errcode = mp_hal_status_to_errno_table[err];
+ return MP_STREAM_ERROR;
+ }
while (nrfx_uart_tx_in_progress(self->p_uart)) {
MICROPY_EVENT_POLL_HOOK;
}
- // return number of bytes written
- return size;
- } else {
- *errcode = mp_hal_status_to_errno_table[err];
- return MP_STREAM_ERROR;
+ buf += chunk;
+ remaining -= chunk;
}
+
+ // return number of bytes written
+ return size;
}
static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
diff --git a/ports/nrf/modules/machine/uart.h b/ports/nrf/modules/machine/uart.h
index 741473ab7a..85c2092458 100644
--- a/ports/nrf/modules/machine/uart.h
+++ b/ports/nrf/modules/machine/uart.h
@@ -34,8 +34,7 @@
typedef struct _machine_uart_obj_t machine_uart_obj_t;
void uart_init0(void);
-void uart_deinit(void);
-void uart_irq_handler(mp_uint_t uart_id);
+void uart_attach_to_repl(machine_uart_obj_t *self, bool attached);
bool uart_rx_any(machine_uart_obj_t *uart_obj);
int uart_rx_char(machine_uart_obj_t *uart_obj);
diff --git a/ports/nrf/modules/os/modos.c b/ports/nrf/modules/os/modos.c
index f000e1eeb6..97ed1e1bad 100644
--- a/ports/nrf/modules/os/modos.c
+++ b/ports/nrf/modules/os/modos.c
@@ -30,6 +30,7 @@
#include "py/runtime.h"
#include "extmod/modmachine.h"
#include "drivers/rng.h"
+#include "modules/machine/uart.h"
#if MICROPY_PY_OS_URANDOM
// Return a bytes object with n random bytes, generated by the hardware random number generator.
@@ -46,10 +47,17 @@ static MP_DEFINE_CONST_FUN_OBJ_1(mp_os_urandom_obj, mp_os_urandom);
#endif
#if MICROPY_PY_OS_DUPTERM
-// TODO should accept any object with read/write methods.
void mp_os_dupterm_stream_detached_attached(mp_obj_t stream_detached, mp_obj_t stream_attached) {
- if (!(stream_attached == mp_const_none || mp_obj_get_type(stream_attached) == &machine_uart_type)) {
- mp_raise_ValueError(MP_ERROR_TEXT("need a UART object"));
+ #if MICROPY_PY_MACHINE_UART
+ if (mp_obj_get_type(stream_detached) == &machine_uart_type) {
+ uart_attach_to_repl(MP_OBJ_TO_PTR(stream_detached), false);
}
+ #endif
+
+ #if MICROPY_PY_MACHINE_UART
+ if (mp_obj_get_type(stream_attached) == &machine_uart_type) {
+ uart_attach_to_repl(MP_OBJ_TO_PTR(stream_attached), true);
+ }
+ #endif
}
#endif // MICROPY_PY_OS_DUPTERM
diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h
index 7cc8a66d98..d52b5745d4 100644
--- a/ports/nrf/mpconfigport.h
+++ b/ports/nrf/mpconfigport.h
@@ -181,6 +181,7 @@
#define MICROPY_PY_MACHINE_RESET (1)
#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
#define MICROPY_PY_MACHINE_BOOTLOADER (1)
+#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1)
#define MICROPY_PY_MACHINE_PULSE (0)
#define MICROPY_PY_MACHINE_SOFTI2C (MICROPY_PY_MACHINE_I2C)
@@ -320,7 +321,17 @@
// type definitions for the specific machine
-#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1))
+#if defined(NRF52832) || defined(NRF52840)
+// On nRF52, the physical SRAM is mapped to 0x20000000 for data access and 0x00800000
+// for instruction access. So convert addresses to make them executable.
+#define MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA (1)
+#define MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA (0)
+#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)(((uintptr_t)(p) - 0x20000000 + 0x00800000) | 1))
+void *nrf_native_code_commit(void *, unsigned int, void *);
+#define MP_PLAT_COMMIT_EXEC(buf, len, reloc) nrf_native_code_commit(buf, len, reloc)
+#else
+#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((uintptr_t)(p) | 1))
+#endif
#define MP_SSIZE_MAX (0x7fffffff)
diff --git a/ports/nrf/mphalport.c b/ports/nrf/mphalport.c
index 9bb51deb12..3a3d3ad7a6 100644
--- a/ports/nrf/mphalport.c
+++ b/ports/nrf/mphalport.c
@@ -202,7 +202,7 @@ const byte mp_hal_status_to_errno_table[4] = {
[HAL_TIMEOUT] = MP_ETIMEDOUT,
};
-NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
+MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
mp_raise_OSError(mp_hal_status_to_errno_table[status]);
}
diff --git a/ports/nrf/mphalport.h b/ports/nrf/mphalport.h
index a0bca58a62..12e881d7b6 100644
--- a/ports/nrf/mphalport.h
+++ b/ports/nrf/mphalport.h
@@ -35,6 +35,22 @@
#include "nrfx_config.h"
#include "shared/runtime/interrupt_char.h"
+// Entering a critical section.
+#ifndef BLUETOOTH_SD
+#define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq()
+#define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state)
+#endif
+
+static inline void enable_irq(mp_uint_t state) {
+ __set_PRIMASK(state);
+}
+
+static inline mp_uint_t disable_irq(void) {
+ mp_uint_t state = __get_PRIMASK();
+ __disable_irq();
+ return state;
+}
+
typedef enum
{
HAL_OK = 0x00,
@@ -47,7 +63,7 @@ extern const unsigned char mp_hal_status_to_errno_table[4];
extern ringbuf_t stdin_ringbuf;
-NORETURN void mp_hal_raise(HAL_StatusTypeDef status);
+MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status);
void mp_hal_set_interrupt_char(int c); // -1 to disable
int mp_hal_stdin_rx_chr(void);
diff --git a/ports/pic16bit/main.c b/ports/pic16bit/main.c
index b1fe7321f0..437f91ca7c 100644
--- a/ports/pic16bit/main.c
+++ b/ports/pic16bit/main.c
@@ -121,7 +121,7 @@ void nlr_jump_fail(void *val) {
}
}
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
while (1) {
;
}
diff --git a/ports/powerpc/main.c b/ports/powerpc/main.c
index 4b668c861c..bbd2082b42 100644
--- a/ports/powerpc/main.c
+++ b/ports/powerpc/main.c
@@ -130,7 +130,7 @@ void nlr_jump_fail(void *val) {
}
}
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
while (1) {
;
}
diff --git a/ports/qemu/mcu/arm/errorhandler.c b/ports/qemu/mcu/arm/errorhandler.c
index a0e7ef930f..a647c0e153 100644
--- a/ports/qemu/mcu/arm/errorhandler.c
+++ b/ports/qemu/mcu/arm/errorhandler.c
@@ -63,7 +63,7 @@ static const char *EXCEPTION_NAMES_TABLE[] = {
// R0-R15, PSR, Kind
uintptr_t registers_copy[18] = { 0 };
-__attribute__((naked)) NORETURN void exception_handler(uintptr_t kind) {
+__attribute__((naked)) MP_NORETURN void exception_handler(uintptr_t kind) {
// Save registers
__asm volatile (
"ldr r1, =registers_copy \n"
@@ -137,39 +137,39 @@ __attribute__((naked)) NORETURN void exception_handler(uintptr_t kind) {
for (;;) {}
}
-__attribute__((naked)) NORETURN void NMI_Handler(void) {
+__attribute__((naked)) MP_NORETURN void NMI_Handler(void) {
exception_handler(NMI);
}
-__attribute__((naked)) NORETURN void HardFault_Handler(void) {
+__attribute__((naked)) MP_NORETURN void HardFault_Handler(void) {
exception_handler(HARD_FAULT);
}
-__attribute__((naked)) NORETURN void MemManage_Handler(void) {
+__attribute__((naked)) MP_NORETURN void MemManage_Handler(void) {
exception_handler(MEM_MANAGE);
}
-__attribute__((naked)) NORETURN void BusFault_Handler(void) {
+__attribute__((naked)) MP_NORETURN void BusFault_Handler(void) {
exception_handler(BUS_FAULT);
}
-__attribute__((naked)) NORETURN void UsageFault_Handler(void) {
+__attribute__((naked)) MP_NORETURN void UsageFault_Handler(void) {
exception_handler(USAGE_FAULT);
}
-__attribute__((naked)) NORETURN void SVC_Handler(void) {
+__attribute__((naked)) MP_NORETURN void SVC_Handler(void) {
exception_handler(SV_CALL);
}
-__attribute__((naked)) NORETURN void DebugMon_Handler(void) {
+__attribute__((naked)) MP_NORETURN void DebugMon_Handler(void) {
exception_handler(DEBUG_MONITOR);
}
-__attribute__((naked)) NORETURN void PendSV_Handler(void) {
+__attribute__((naked)) MP_NORETURN void PendSV_Handler(void) {
exception_handler(PENDING_SV);
}
-__attribute__((naked)) NORETURN void SysTick_Handler(void) {
+__attribute__((naked)) MP_NORETURN void SysTick_Handler(void) {
exception_handler(SYSTEM_TICK);
}
diff --git a/ports/qemu/mcu/arm/startup.c b/ports/qemu/mcu/arm/startup.c
index dbb75e3392..a7e9efb9fb 100644
--- a/ports/qemu/mcu/arm/startup.c
+++ b/ports/qemu/mcu/arm/startup.c
@@ -52,7 +52,7 @@ __attribute__((naked)) void Reset_Handler(void) {
_start();
}
-NORETURN void Default_Handler(void) {
+MP_NORETURN void Default_Handler(void) {
for (;;) {
}
}
diff --git a/ports/renesas-ra/Makefile b/ports/renesas-ra/Makefile
index ec47510d98..fd74c60a85 100644
--- a/ports/renesas-ra/Makefile
+++ b/ports/renesas-ra/Makefile
@@ -157,9 +157,6 @@ LIBSTDCPP_FILE_NAME = "$(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a)"
LDFLAGS += -L"$(shell dirname $(LIBSTDCPP_FILE_NAME))"
endif
-# Hook tinyusb USB interrupt if used to service usb task.
-LDFLAGS += --wrap=dcd_event_handler
-
# Options for mpy-cross
MPY_CROSS_FLAGS += -march=armv7m
diff --git a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json
index 4c4d4e4326..70a33b3515 100644
--- a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json
+++ b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json
@@ -15,7 +15,7 @@
"ABX00074_01.iso_1000x750.jpg"
],
"mcu": "RA6M5",
- "product": "Arduino Portenta C33",
+ "product": "Portenta C33",
"thumbnail": "",
"url": "https://store.arduino.cc/pages/portenta-c33",
"vendor": "Arduino"
diff --git a/ports/renesas-ra/machine_uart.c b/ports/renesas-ra/machine_uart.c
index b70978ad7a..196e1ccd6d 100644
--- a/ports/renesas-ra/machine_uart.c
+++ b/ports/renesas-ra/machine_uart.c
@@ -501,7 +501,7 @@ static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uint
if (!uart_tx_busy(self)) {
return 0;
}
- MICROPY_EVENT_POLL_HOOK
+ mp_event_wait_indefinite();
} while (mp_hal_ticks_ms() < timeout);
*errcode = MP_ETIMEDOUT;
ret = MP_STREAM_ERROR;
diff --git a/ports/renesas-ra/main.c b/ports/renesas-ra/main.c
index febb7b6d7a..cfc4611ecb 100644
--- a/ports/renesas-ra/main.c
+++ b/ports/renesas-ra/main.c
@@ -88,7 +88,7 @@ static machine_uart_obj_t machine_uart_repl_obj;
static uint8_t machine_uart_repl_rxbuf[MICROPY_HW_UART_REPL_RXBUF];
#endif
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
for (volatile uint delay = 0; delay < 1000000; delay++) {
}
led_state(1, 1);
diff --git a/ports/renesas-ra/modmachine.c b/ports/renesas-ra/modmachine.c
index dc38d809dd..c23ce7e469 100644
--- a/ports/renesas-ra/modmachine.c
+++ b/ports/renesas-ra/modmachine.c
@@ -184,12 +184,12 @@ static mp_obj_t mp_machine_unique_id(void) {
}
// Resets the pyboard in a manner similar to pushing the external RESET button.
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
powerctrl_mcu_reset();
}
// Activate the bootloader without BOOT* pins.
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
#if MICROPY_HW_ENABLE_STORAGE
storage_flush();
#endif
@@ -232,7 +232,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
powerctrl_enter_stop_mode();
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
if (n_args != 0) {
mp_obj_t args2[2] = {MP_OBJ_NULL, args[0]};
machine_rtc_wakeup(2, args2);
diff --git a/ports/renesas-ra/modtime.c b/ports/renesas-ra/modtime.c
index cbd639721f..e1358f82bc 100644
--- a/ports/renesas-ra/modtime.c
+++ b/ports/renesas-ra/modtime.c
@@ -53,5 +53,5 @@ static mp_obj_t mp_time_time_get(void) {
rtc_init_finalise();
ra_rtc_t time;
ra_rtc_get_time(&time);
- return mp_obj_new_int(timeutils_seconds_since_epoch(time.year, time.month, time.date, time.hour, time.minute, time.second));
+ return timeutils_obj_from_timestamp(timeutils_seconds_since_epoch(time.year, time.month, time.date, time.hour, time.minute, time.second));
}
diff --git a/ports/renesas-ra/mpconfigport.h b/ports/renesas-ra/mpconfigport.h
index bd936061fa..8a116269bb 100644
--- a/ports/renesas-ra/mpconfigport.h
+++ b/ports/renesas-ra/mpconfigport.h
@@ -210,19 +210,11 @@
#define MP_STATE_PORT MP_STATE_VM
-#if MICROPY_PY_NETWORK_ESP_HOSTED
-extern const struct _mp_obj_type_t mod_network_esp_hosted_type;
-#define MICROPY_HW_NIC_ESP_HOSTED { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_esp_hosted_type) },
-#else
-#define MICROPY_HW_NIC_ESP_HOSTED
-#endif
-
#ifndef MICROPY_BOARD_NETWORK_INTERFACES
#define MICROPY_BOARD_NETWORK_INTERFACES
#endif
#define MICROPY_PORT_NETWORK_INTERFACES \
- MICROPY_HW_NIC_ESP_HOSTED \
MICROPY_BOARD_NETWORK_INTERFACES \
// Miscellaneous settings
@@ -251,28 +243,17 @@ typedef unsigned int mp_uint_t; // must be pointer size
typedef long mp_off_t;
#if MICROPY_PY_THREAD
-#define MICROPY_EVENT_POLL_HOOK \
+#define MICROPY_INTERNAL_EVENT_HOOK \
do { \
- extern void mp_handle_pending(bool); \
- mp_handle_pending(true); \
if (pyb_thread_enabled) { \
MP_THREAD_GIL_EXIT(); \
pyb_thread_yield(); \
MP_THREAD_GIL_ENTER(); \
- } else { \
- __WFI(); \
} \
} while (0);
#define MICROPY_THREAD_YIELD() pyb_thread_yield()
#else
-#define MICROPY_EVENT_POLL_HOOK \
- do { \
- extern void mp_handle_pending(bool); \
- mp_handle_pending(true); \
- __WFI(); \
- } while (0);
-
#define MICROPY_THREAD_YIELD()
#endif
diff --git a/ports/renesas-ra/mphalport.c b/ports/renesas-ra/mphalport.c
index 1c62c23dd7..455b28af19 100644
--- a/ports/renesas-ra/mphalport.c
+++ b/ports/renesas-ra/mphalport.c
@@ -61,7 +61,7 @@ const byte mp_hal_status_to_errno_table[4] = {
[HAL_TIMEOUT] = MP_ETIMEDOUT,
};
-NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
+MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
mp_raise_OSError(mp_hal_status_to_errno_table[status]);
}
@@ -104,7 +104,7 @@ int mp_hal_stdin_rx_chr(void) {
return dupterm_c;
}
#endif
- MICROPY_EVENT_POLL_HOOK
+ mp_event_wait_indefinite();
}
}
diff --git a/ports/renesas-ra/mphalport.h b/ports/renesas-ra/mphalport.h
index 0819abeaf6..4f8be705d1 100644
--- a/ports/renesas-ra/mphalport.h
+++ b/ports/renesas-ra/mphalport.h
@@ -35,6 +35,14 @@
#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV)
#define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state)
+// Port level Wait-for-Event macro
+//
+// Do not use this macro directly, include py/runtime.h and
+// call mp_event_wait_indefinite() or mp_event_wait_ms(timeout).
+// Uses WFI which will wake up from regular systick interrupt if not
+// before from any other source.
+#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) __WFI()
+
#define MICROPY_PY_LWIP_ENTER
#define MICROPY_PY_LWIP_REENTER
#define MICROPY_PY_LWIP_EXIT
@@ -49,7 +57,7 @@ static inline int mp_hal_status_to_neg_errno(HAL_StatusTypeDef status) {
return -mp_hal_status_to_errno_table[status];
}
-NORETURN void mp_hal_raise(HAL_StatusTypeDef status);
+MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status);
void mp_hal_set_interrupt_char(int c); // -1 to disable
static inline void mp_hal_wake_main_task_from_isr(void) {
diff --git a/ports/renesas-ra/powerctrl.c b/ports/renesas-ra/powerctrl.c
index 548d31679a..04c39b3511 100644
--- a/ports/renesas-ra/powerctrl.c
+++ b/ports/renesas-ra/powerctrl.c
@@ -174,7 +174,7 @@ const lpm_instance_t g_lpm_standby = {
#endif
-NORETURN void powerctrl_mcu_reset(void) {
+MP_NORETURN void powerctrl_mcu_reset(void) {
#if BSP_TZ_SECURE_BUILD
R_BSP_NonSecureEnter();
#else
@@ -185,7 +185,7 @@ NORETURN void powerctrl_mcu_reset(void) {
}
}
-NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) {
+MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) {
while (1) {
;
}
@@ -245,7 +245,7 @@ void powerctrl_enter_stop_mode(void) {
enable_irq(irq_state);
}
-NORETURN void powerctrl_enter_standby_mode(void) {
+MP_NORETURN void powerctrl_enter_standby_mode(void) {
rtc_init_finalise();
#if defined(MICROPY_BOARD_ENTER_STANDBY)
diff --git a/ports/renesas-ra/powerctrl.h b/ports/renesas-ra/powerctrl.h
index 34c40ea1ad..37932b1002 100644
--- a/ports/renesas-ra/powerctrl.h
+++ b/ports/renesas-ra/powerctrl.h
@@ -32,11 +32,11 @@
void SystemClock_Config(void);
-NORETURN void powerctrl_mcu_reset(void);
-NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr);
+MP_NORETURN void powerctrl_mcu_reset(void);
+MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr);
void powerctrl_check_enter_bootloader(void);
void powerctrl_enter_stop_mode(void);
-NORETURN void powerctrl_enter_standby_mode(void);
+MP_NORETURN void powerctrl_enter_standby_mode(void);
#endif // MICROPY_INCLUDED_RA_POWERCTRL_H
diff --git a/ports/renesas-ra/systick.c b/ports/renesas-ra/systick.c
index 6fa02a2b6e..cb528d91d2 100644
--- a/ports/renesas-ra/systick.c
+++ b/ports/renesas-ra/systick.c
@@ -97,22 +97,24 @@ void HAL_Delay(uint32_t Delay) {
// Core delay function that does an efficient sleep and may switch thread context.
// If IRQs are enabled then we must have the GIL.
-void mp_hal_delay_ms(mp_uint_t Delay) {
+void mp_hal_delay_ms(mp_uint_t ms) {
if (query_irq() == IRQ_STATE_ENABLED) {
// IRQs enabled, so can use systick counter to do the delay
uint32_t start = uwTick;
+ mp_uint_t elapsed = 0;
// Wraparound of tick is taken care of by 2's complement arithmetic.
do {
// This macro will execute the necessary idle behaviour. It may
// raise an exception, switch threads or enter sleep mode (waiting for
// (at least) the SysTick interrupt).
- MICROPY_EVENT_POLL_HOOK
- } while (uwTick - start < Delay);
+ mp_event_wait_ms(ms - elapsed);
+ elapsed = uwTick - start;
+ } while (elapsed < ms);
} else {
// IRQs disabled, so need to use a busy loop for the delay.
// To prevent possible overflow of the counter we use a double loop.
volatile uint32_t count_1ms;
- while (Delay-- > 0) {
+ while (ms-- > 0) {
count_1ms = (MICROPY_HW_MCU_PCLK / 1000 / 10);
while (count_1ms-- > 0) {
__asm__ __volatile__ ("nop");
diff --git a/ports/renesas-ra/uart.c b/ports/renesas-ra/uart.c
index c319b4c0a8..91714bf05b 100644
--- a/ports/renesas-ra/uart.c
+++ b/ports/renesas-ra/uart.c
@@ -42,7 +42,7 @@
typedef int (*KEYEX_CB)(uint32_t d);
-extern void NORETURN __fatal_error(const char *msg);
+extern void MP_NORETURN __fatal_error(const char *msg);
#if MICROPY_KBD_EXCEPTION
extern int mp_interrupt_char;
static KEYEX_CB keyex_cb[MICROPY_HW_MAX_UART] = {(KEYEX_CB)NULL};
@@ -477,7 +477,7 @@ bool uart_rx_wait(machine_uart_obj_t *self, uint32_t timeout) {
if (HAL_GetTick() - start >= timeout) {
return false; // timeout
}
- MICROPY_EVENT_POLL_HOOK
+ mp_event_wait_ms(1);
}
}
@@ -498,7 +498,7 @@ bool uart_tx_wait(machine_uart_obj_t *self, uint32_t timeout) {
if (HAL_GetTick() - start >= timeout) {
return false; // timeout
}
- MICROPY_EVENT_POLL_HOOK
+ mp_event_wait_ms(1);
}
}
diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt
index 53d00c7dfd..49ba8d77d6 100644
--- a/ports/rp2/CMakeLists.txt
+++ b/ports/rp2/CMakeLists.txt
@@ -84,6 +84,9 @@ endif()
list(APPEND GIT_SUBMODULES lib/mbedtls)
list(APPEND GIT_SUBMODULES lib/tinyusb)
+# Workaround for pico-sdk host toolchain issue, see directory for details
+list(APPEND CMAKE_MODULE_PATH "${MICROPY_PORT_DIR}/tools_patch")
+
# Include component cmake fragments
include(${MICROPY_DIR}/py/py.cmake)
include(${MICROPY_DIR}/extmod/extmod.cmake)
@@ -522,10 +525,23 @@ target_compile_options(${MICROPY_TARGET} PRIVATE
target_link_options(${MICROPY_TARGET} PRIVATE
-Wl,--defsym=__micropy_c_heap_size__=${MICROPY_C_HEAP_SIZE}
- -Wl,--wrap=dcd_event_handler
-Wl,--wrap=runtime_init_clocks
)
+if(PICO_FLASH_SIZE_BYTES GREATER 0)
+ target_link_options(${MICROPY_TARGET} PRIVATE
+ -Wl,--defsym=__micropy_flash_size__=${PICO_FLASH_SIZE_BYTES}
+ )
+elseif(PICO_RP2040)
+ target_link_options(${MICROPY_TARGET} PRIVATE
+ -Wl,--defsym=__micropy_flash_size__=2048k # Default to 2MB
+ )
+elseif(PICO_RP2350)
+ target_link_options(${MICROPY_TARGET} PRIVATE
+ -Wl,--defsym=__micropy_flash_size__=4096k # Default to 4MB
+ )
+endif()
+
if(PICO_RP2350)
target_link_options(${MICROPY_TARGET} PRIVATE
-Wl,--defsym=__micropy_extra_stack__=4096
@@ -594,14 +610,18 @@ endif()
# todo this is a bit brittle, but we want to move a few source files into RAM (which requires
# a linker script modification) until we explicitly add macro calls around the function
# defs to move them into RAM.
-if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM)
+if (NOT MICROPY_BOARD_LINKER_SCRIPT)
if(PICO_RP2040)
- pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2040.ld)
+ set(MICROPY_BOARD_LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2040.ld)
elseif(PICO_RP2350)
- pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2350.ld)
+ set(MICROPY_BOARD_LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2350.ld)
endif()
endif()
+if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM)
+ pico_set_linker_script(${MICROPY_TARGET} ${MICROPY_BOARD_LINKER_SCRIPT})
+endif()
+
pico_add_extra_outputs(${MICROPY_TARGET})
pico_find_compiler_with_triples(PICO_COMPILER_SIZE "${PICO_GCC_TRIPLE}" size)
@@ -638,9 +658,13 @@ if(NOT PICO_NUM_EXT_GPIOS)
set(PICO_NUM_EXT_GPIOS 10)
endif()
-if(EXISTS "${MICROPY_BOARD_DIR}/pins.csv")
- set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_DIR}/pins.csv")
- set(GEN_PINS_CSV_ARG --board-csv "${GEN_PINS_BOARD_CSV}")
+if(NOT MICROPY_BOARD_PINS)
+ set(MICROPY_BOARD_PINS "${MICROPY_BOARD_DIR}/pins.csv")
+endif()
+
+if(EXISTS "${MICROPY_BOARD_PINS}")
+ set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_PINS}")
+ set(GEN_PINS_CSV_ARG --board-csv "${MICROPY_BOARD_PINS}")
endif()
target_sources(${MICROPY_TARGET} PRIVATE
diff --git a/ports/rp2/Makefile b/ports/rp2/Makefile
index 76b5698d2c..2895faaca6 100644
--- a/ports/rp2/Makefile
+++ b/ports/rp2/Makefile
@@ -67,6 +67,9 @@ all:
clean:
$(RM) -rf $(BUILD)
+deploy: all
+ $(Q)picotool load -x $(BUILD)/firmware.elf
+
# First ensure that pico-sdk is initialised, then run CMake with the
# UPDATE_SUBMODULES flag to update necessary submodules for this board.
#
diff --git a/ports/rp2/README.md b/ports/rp2/README.md
index 911d797fe0..41fca97f95 100644
--- a/ports/rp2/README.md
+++ b/ports/rp2/README.md
@@ -47,8 +47,10 @@ pass the board name to the build; e.g. for Raspberry Pi Pico W:
## Deploying firmware to the device
Firmware can be deployed to the device by putting it into bootloader mode
-(hold down BOOTSEL while powering on or resetting) and then copying
-`firmware.uf2` to the USB mass storage device that appears.
+(hold down BOOTSEL while powering on or resetting) and then either copying
+`firmware.uf2` to the USB mass storage device that appears, or using
+`picotool load -x firmware.elf`. The latter command can be accessed
+conveniently via `make deploy`.
If MicroPython is already installed then the bootloader can be entered by
executing `import machine; machine.bootloader()` at the REPL.
diff --git a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json
index 5639aaa2a4..5914445ed8 100644
--- a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json
+++ b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json
@@ -17,7 +17,7 @@
"ABX00052_01.iso_999x750.jpg"
],
"mcu": "rp2040",
- "product": "Arduino Nano RP2040 Connect",
+ "product": "Nano RP2040 Connect",
"thumbnail": "",
"url": "https://store-usa.arduino.cc/products/arduino-nano-rp2040-connect",
"vendor": "Arduino"
diff --git a/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json b/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json
index df9dd5d0de..108d632678 100644
--- a/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json
+++ b/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json
@@ -13,7 +13,7 @@
"pybstick-rp2040-26-broches-micropython-c.jpg"
],
"mcu": "rp2040",
- "product": "PYBSTICK26 RP2040",
+ "product": "RP2040 PYBStick",
"thumbnail": "",
"url": "https://shop.mchobby.be/product.php?id_product=2331",
"vendor": "McHobby"
diff --git a/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json b/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json
index 5358db627e..4b12cfda65 100644
--- a/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json
+++ b/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json
@@ -15,7 +15,7 @@
"pololu_3pi_2040_robot.jpg"
],
"mcu": "rp2040",
- "product": "Pololu 3pi+ 2040 Robot",
+ "product": "3pi+ 2040 Robot",
"thumbnail": "",
"url": "https://www.pololu.com/3pi",
"vendor": "Pololu"
diff --git a/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json b/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json
index 7ef383a9c4..1ec15ad591 100644
--- a/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json
+++ b/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json
@@ -16,7 +16,7 @@
"pololu_zumo_2040_robot.jpg"
],
"mcu": "rp2040",
- "product": "Pololu Zumo 2040 Robot",
+ "product": "Zumo 2040 Robot",
"thumbnail": "",
"url": "https://www.pololu.com/zumo",
"vendor": "Pololu"
diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json
index 66cea79dde..5ee6bcb754 100644
--- a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json
+++ b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json
@@ -14,7 +14,7 @@
"18288-SparkFun_Pro_Micro_-_RP2040-01.jpg"
],
"mcu": "rp2040",
- "product": "Pro Micro RP2040",
+ "product": "Pro Micro - RP2040",
"thumbnail": "",
"url": "https://www.sparkfun.com/products/18288",
"vendor": "SparkFun"
diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json b/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json
index e756c9bff4..ebd68bc5d9 100644
--- a/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json
+++ b/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json
@@ -17,7 +17,7 @@
"17745-SparkFun_Thing_Plus_-_RP2040-01a.jpg"
],
"mcu": "rp2040",
- "product": "Thing Plus RP2040",
+ "product": "Thing Plus - RP2040",
"thumbnail": "",
"url": "https://www.sparkfun.com/products/17745",
"vendor": "SparkFun"
diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h
index 56071e1873..931391d972 100644
--- a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h
+++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h
@@ -24,5 +24,9 @@
int mp_hal_is_pin_reserved(int n);
#define MICROPY_HW_PIN_RESERVED(i) mp_hal_is_pin_reserved(i)
+// Set the default I2C to I2C1 on pins 18 and 19 which route to the qwiic connector
+#undef PICO_DEFAULT_I2C
+#define PICO_DEFAULT_I2C (1)
+
#define MICROPY_HW_I2C1_SDA (18)
#define MICROPY_HW_I2C1_SCL (19)
diff --git a/ports/rp2/boards/W5100S_EVB_PICO/board.json b/ports/rp2/boards/W5100S_EVB_PICO/board.json
index daaf1cf8ac..3d052de420 100644
--- a/ports/rp2/boards/W5100S_EVB_PICO/board.json
+++ b/ports/rp2/boards/W5100S_EVB_PICO/board.json
@@ -13,8 +13,8 @@
"W5100S-EVB-Pico.jpg"
],
"mcu": "rp2040",
- "product": "Wiznet W5100S-EVB-Pico",
+ "product": "W5100S-EVB-Pico",
"thumbnail": "",
- "url": "https://www.wiznet.io/product-item/w5100s-evb-pico/",
- "vendor": "Wiznet"
+ "url": "https://docs.wiznet.io/Product/iEthernet/W5100S/w5100s-evb-pico",
+ "vendor": "WIZnet"
}
diff --git a/ports/rp2/boards/W5500_EVB_PICO/board.json b/ports/rp2/boards/W5500_EVB_PICO/board.json
index e7625c3436..d4298014c0 100644
--- a/ports/rp2/boards/W5500_EVB_PICO/board.json
+++ b/ports/rp2/boards/W5500_EVB_PICO/board.json
@@ -13,8 +13,8 @@
"W5500-EVB-Pico.jpg"
],
"mcu": "rp2040",
- "product": "Wiznet W5500-EVB-Pico",
+ "product": "W5500-EVB-Pico",
"thumbnail": "",
"url": "https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico",
- "vendor": "Wiznet"
+ "vendor": "WIZnet"
}
diff --git a/ports/rp2/boards/WEACTSTUDIO/board.json b/ports/rp2/boards/WEACTSTUDIO/board.json
index 8881b40e73..58d89ddf2e 100644
--- a/ports/rp2/boards/WEACTSTUDIO/board.json
+++ b/ports/rp2/boards/WEACTSTUDIO/board.json
@@ -12,7 +12,7 @@
"weact_rp2040.jpg"
],
"mcu": "rp2040",
- "product": "WeAct Studio RP2040",
+ "product": "Studio RP2040",
"url": "https://github.com/WeActTC/WeActStudio.RP2040CoreBoard",
"variants": {
"FLASH_2M": "2 MiB Flash",
diff --git a/ports/rp2/cyw43_configport.h b/ports/rp2/cyw43_configport.h
index 5523305742..2da0b1f8a4 100644
--- a/ports/rp2/cyw43_configport.h
+++ b/ports/rp2/cyw43_configport.h
@@ -26,34 +26,27 @@
#ifndef MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H
#define MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H
-// The board-level config will be included here, so it can set some CYW43 values.
-#include <stdio.h>
-#include "py/mpconfig.h"
-#include "py/mperrno.h"
-#include "py/mphal.h"
-#include "py/runtime.h"
-#include "extmod/modnetwork.h"
-#include "lwip/apps/mdns.h"
-#include "pendsv.h"
+#include "extmod/cyw43_config_common.h"
#define CYW43_INCLUDE_LEGACY_F1_OVERFLOW_WORKAROUND_VARIABLES (1)
#define CYW43_WIFI_NVRAM_INCLUDE_FILE "wifi_nvram_43439.h"
-#define CYW43_IOCTL_TIMEOUT_US (1000000)
-#define CYW43_SLEEP_MAX (10)
-#define CYW43_NETUTILS (1)
+#define CYW43_SLEEP_MAX (10) // Unclear why rp2 port overrides the default here
#define CYW43_USE_OTP_MAC (1)
-#define CYW43_PRINTF(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__)
-#define CYW43_EPERM MP_EPERM // Operation not permitted
-#define CYW43_EIO MP_EIO // I/O error
-#define CYW43_EINVAL MP_EINVAL // Invalid argument
-#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out
+static inline bool cyw43_poll_is_pending(void) {
+ return pendsv_is_pending(PENDSV_DISPATCH_CYW43);
+}
+
+static inline void cyw43_yield(void) {
+ if (!cyw43_poll_is_pending()) {
+ best_effort_wfe_or_timeout(make_timeout_time_ms(1));
+ }
+}
-#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER
-#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT
-#define CYW43_THREAD_LOCK_CHECK
+#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook();
-#define CYW43_HOST_NAME mod_network_hostname_data
+// set in SDK board header
+#define CYW43_NUM_GPIOS CYW43_WL_GPIO_COUNT
#if CYW43_PIN_WL_DYNAMIC
@@ -100,36 +93,6 @@ uint cyw43_get_pin_wl(cyw43_pin_index_t pin_id);
cyw43_yield(); \
} \
-#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a)
-
-#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT
-#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT
-#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE
-#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP
-#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN
-
-#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0
-
-// set in SDK board header
-#define CYW43_NUM_GPIOS CYW43_WL_GPIO_COUNT
-
-#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook();
-
-#define cyw43_hal_ticks_us mp_hal_ticks_us
-#define cyw43_hal_ticks_ms mp_hal_ticks_ms
-
-#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t
-#define cyw43_hal_pin_config mp_hal_pin_config
-#define cyw43_hal_pin_read mp_hal_pin_read
-#define cyw43_hal_pin_low mp_hal_pin_low
-#define cyw43_hal_pin_high mp_hal_pin_high
-
-#define cyw43_hal_get_mac mp_hal_get_mac
-#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii
-#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac
-
-#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func)
-
// Bluetooth requires dynamic memory allocation to load its firmware (the allocation
// call is made from pico-sdk). This allocation is always done at thread-level, not
// from an IRQ, so is safe to delegate to the MicroPython GC heap.
@@ -140,47 +103,4 @@ uint cyw43_get_pin_wl(cyw43_pin_index_t pin_id);
#define cyw43_free m_tracked_free
#endif
-void cyw43_post_poll_hook(void);
-static inline bool cyw43_poll_is_pending(void) {
- return pendsv_is_pending(PENDSV_DISPATCH_CYW43);
-}
-
-static inline void cyw43_yield(void) {
- if (!cyw43_poll_is_pending()) {
- best_effort_wfe_or_timeout(make_timeout_time_ms(1));
- }
-}
-
-static inline void cyw43_delay_us(uint32_t us) {
- uint32_t start = mp_hal_ticks_us();
- while (mp_hal_ticks_us() - start < us) {
- }
-}
-
-static inline void cyw43_delay_ms(uint32_t ms) {
- // PendSV may be disabled via CYW43_THREAD_ENTER, so this delay is a busy loop.
- uint32_t us = ms * 1000;
- uint32_t start = mp_hal_ticks_us();
- while (mp_hal_ticks_us() - start < us) {
- mp_event_handle_nowait();
- }
-}
-
-#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait()
-
-#if LWIP_MDNS_RESPONDER == 1
-
-// Hook for any additional TCP/IP initialization than needs to be done.
-// Called after the netif specified by `itf` has been set up.
-#ifndef CYW43_CB_TCPIP_INIT_EXTRA
-#define CYW43_CB_TCPIP_INIT_EXTRA(self, itf) mdns_resp_add_netif(&self->netif[itf], mod_network_hostname_data)
-#endif
-
-// Hook for any additional TCP/IP deinitialization than needs to be done.
-// Called before the netif specified by `itf` is removed.
-#ifndef CYW43_CB_TCPIP_DEINIT_EXTRA
-#define CYW43_CB_TCPIP_DEINIT_EXTRA(self, itf) mdns_resp_remove_netif(&self->netif[itf])
-#endif
-
-#endif
#endif // MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H
diff --git a/ports/rp2/machine_pin.c b/ports/rp2/machine_pin.c
index d9ca3f8ce3..7a2de0c0bc 100644
--- a/ports/rp2/machine_pin.c
+++ b/ports/rp2/machine_pin.c
@@ -46,15 +46,6 @@
#define GPIO_IRQ_ALL (0xf)
-// Macros to access the state of the hardware.
-#define GPIO_GET_FUNCSEL(id) ((iobank0_hw->io[(id)].ctrl & IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS) >> IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB)
-#define GPIO_IS_OUT(id) (sio_hw->gpio_oe & (1 << (id)))
-#define GPIO_IS_PULL_UP(id) (padsbank0_hw->io[(id)] & PADS_BANK0_GPIO0_PUE_BITS)
-#define GPIO_IS_PULL_DOWN(id) (padsbank0_hw->io[(id)] & PADS_BANK0_GPIO0_PDE_BITS)
-
-// Open drain behaviour is simulated.
-#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1 << (id)))
-
#ifndef MICROPY_HW_PIN_RESERVED
#define MICROPY_HW_PIN_RESERVED(i) (0)
#endif
@@ -89,7 +80,11 @@ static const mp_irq_methods_t machine_pin_irq_methods;
static const int num_intr_regs = sizeof(iobank0_hw->intr) / sizeof(iobank0_hw->intr[0]);
// Mask with "1" indicating that the corresponding pin is in simulated open-drain mode.
+#if NUM_BANK0_GPIOS > 32
uint64_t machine_pin_open_drain_mask;
+#else
+uint32_t machine_pin_open_drain_mask;
+#endif
#if MICROPY_HW_PIN_EXT_COUNT
static inline bool is_ext_pin(__unused const machine_pin_obj_t *self) {
@@ -198,13 +193,13 @@ const machine_pin_obj_t *machine_pin_find(mp_obj_t pin) {
static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_pin_obj_t *self = self_in;
- uint funcsel = GPIO_GET_FUNCSEL(self->id);
+ uint funcsel = gpio_get_function(self->id);
qstr mode_qst;
if (!is_ext_pin(self)) {
if (funcsel == GPIO_FUNC_SIO) {
if (GPIO_IS_OPEN_DRAIN(self->id)) {
mode_qst = MP_QSTR_OPEN_DRAIN;
- } else if (GPIO_IS_OUT(self->id)) {
+ } else if (gpio_is_dir_out(self->id)) {
mode_qst = MP_QSTR_OUT;
} else {
mode_qst = MP_QSTR_IN;
@@ -214,11 +209,11 @@ static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_prin
}
mp_printf(print, "Pin(%q, mode=%q", self->name, mode_qst);
bool pull_up = false;
- if (GPIO_IS_PULL_UP(self->id)) {
+ if (gpio_is_pulled_up(self->id)) {
mp_printf(print, ", pull=%q", MP_QSTR_PULL_UP);
pull_up = true;
}
- if (GPIO_IS_PULL_DOWN(self->id)) {
+ if (gpio_is_pulled_down(self->id)) {
if (pull_up) {
mp_printf(print, "|%q", MP_QSTR_PULL_DOWN);
} else {
@@ -298,7 +293,7 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid pin af: %d"), af);
}
gpio_set_function(self->id, af);
- machine_pin_open_drain_mask &= ~(1ULL << self->id);
+ GPIO_DISABLE_OPEN_DRAIN(self->id);
}
}
@@ -411,7 +406,7 @@ static mp_obj_t machine_pin_toggle(mp_obj_t self_in) {
machine_pin_ext_set(self, self->last_output_value ^ 1);
#endif
} else if (GPIO_IS_OPEN_DRAIN(self->id)) {
- if (GPIO_IS_OUT(self->id)) {
+ if (gpio_is_dir_out(self->id)) {
gpio_set_dir(self->id, GPIO_IN);
} else {
gpio_set_dir(self->id, GPIO_OUT);
diff --git a/ports/rp2/machine_timer.c b/ports/rp2/machine_timer.c
index 6f8b04f10e..ffb4c70243 100644
--- a/ports/rp2/machine_timer.c
+++ b/ports/rp2/machine_timer.c
@@ -27,6 +27,7 @@
#include "py/runtime.h"
#include "py/mperrno.h"
#include "py/mphal.h"
+#include "py/gc.h"
#include "pico/time.h"
#define ALARM_ID_INVALID (-1)
@@ -40,13 +41,36 @@ typedef struct _machine_timer_obj_t {
uint32_t mode;
uint64_t delta_us; // for periodic mode
mp_obj_t callback;
+ bool ishard;
} machine_timer_obj_t;
const mp_obj_type_t machine_timer_type;
static int64_t alarm_callback(alarm_id_t id, void *user_data) {
machine_timer_obj_t *self = user_data;
- mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self));
+
+ if (self->ishard) {
+ // When executing code within a handler we must lock the scheduler to
+ // prevent any scheduled callbacks from running, and lock the GC to
+ // prevent any memory allocations.
+ mp_sched_lock();
+ gc_lock();
+ nlr_buf_t nlr;
+ if (nlr_push(&nlr) == 0) {
+ mp_call_function_1(self->callback, MP_OBJ_FROM_PTR(self));
+ nlr_pop();
+ } else {
+ // Uncaught exception; disable the callback so it doesn't run again.
+ self->mode = TIMER_MODE_ONE_SHOT;
+ mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in timer callback\n");
+ mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val));
+ }
+ gc_unlock();
+ mp_sched_unlock();
+ } else {
+ mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self));
+ }
+
if (self->mode == TIMER_MODE_ONE_SHOT) {
return 0;
} else {
@@ -66,13 +90,14 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr
}
static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
- enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, };
+ enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, ARG_hard, };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIMER_MODE_PERIODIC} },
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
{ MP_QSTR_tick_hz, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} },
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
+ { MP_QSTR_hard, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
};
// Parse args
@@ -96,6 +121,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_ar
}
self->callback = args[ARG_callback].u_obj;
+ self->ishard = args[ARG_hard].u_bool;
self->alarm_id = alarm_pool_add_alarm_in_us(self->pool, self->delta_us, alarm_callback, self, true);
if (self->alarm_id == -1) {
mp_raise_OSError(MP_ENOMEM);
diff --git a/ports/rp2/memmap_mp_rp2040.ld b/ports/rp2/memmap_mp_rp2040.ld
index a5799cd88b..5c8d9f4718 100644
--- a/ports/rp2/memmap_mp_rp2040.ld
+++ b/ports/rp2/memmap_mp_rp2040.ld
@@ -23,7 +23,7 @@
MEMORY
{
- FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k
+ FLASH(rx) : ORIGIN = 0x10000000, LENGTH = __micropy_flash_size__
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
diff --git a/ports/rp2/memmap_mp_rp2350.ld b/ports/rp2/memmap_mp_rp2350.ld
index 1e1cbbfd70..1c4770efe1 100644
--- a/ports/rp2/memmap_mp_rp2350.ld
+++ b/ports/rp2/memmap_mp_rp2350.ld
@@ -23,7 +23,7 @@
MEMORY
{
- FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 4096k
+ FLASH(rx) : ORIGIN = 0x10000000, LENGTH = __micropy_flash_size__
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k
SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k
diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c
index 58a3a8ae4d..1f1b0e2f59 100644
--- a/ports/rp2/modmachine.c
+++ b/ports/rp2/modmachine.c
@@ -65,7 +65,7 @@ static mp_obj_t mp_machine_unique_id(void) {
return mp_obj_new_bytes(id.id, sizeof(id.id));
}
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
watchdog_reboot(0, SRAM_END, 0);
for (;;) {
__wfi();
@@ -82,7 +82,7 @@ static mp_int_t mp_machine_reset_cause(void) {
return reset_cause;
}
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB;
reset_usb_boot(0, 0);
@@ -137,6 +137,12 @@ static void mp_machine_idle(void) {
MICROPY_INTERNAL_WFE(1);
}
+static void alarm_sleep_callback(uint alarm_id) {
+}
+
+// Set this to 1 to enable some debug of the interrupt that woke the device
+#define DEBUG_LIGHTSLEEP 0
+
static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
mp_int_t delay_ms = 0;
bool use_timer_alarm = false;
@@ -167,6 +173,16 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
}
#endif
+ #if MICROPY_PY_THREAD
+ static bool in_lightsleep;
+ if (in_lightsleep) {
+ // The other CPU is also in machine.lightsleep()
+ MICROPY_END_ATOMIC_SECTION(my_interrupts);
+ return;
+ }
+ in_lightsleep = true;
+ #endif
+
#if MICROPY_HW_ENABLE_USBDEV
// Only disable the USB clock if a USB host has not configured the device
// or if going to DORMANT mode.
@@ -206,6 +222,15 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
// Disable ROSC.
rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB;
+ #if DEBUG_LIGHTSLEEP
+ #if PICO_RP2040
+ uint32_t pending_intr = 0;
+ #else
+ uint32_t pending_intr[2] = { 0 };
+ #endif
+ #endif
+
+ bool alarm_armed = false;
if (n_args == 0) {
#if MICROPY_PY_NETWORK_CYW43
gpio_set_dormant_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
@@ -214,16 +239,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
} else {
uint32_t save_sleep_en0 = clocks_hw->sleep_en0;
uint32_t save_sleep_en1 = clocks_hw->sleep_en1;
- bool timer3_enabled = irq_is_enabled(3);
-
- const uint32_t alarm_num = 3;
- const uint32_t irq_num = TIMER_ALARM_IRQ_NUM(timer_hw, alarm_num);
if (use_timer_alarm) {
- // Make sure ALARM3/IRQ3 is enabled on _this_ core
- if (!timer3_enabled) {
- irq_set_enabled(irq_num, true);
- }
- hw_set_bits(&timer_hw->inte, 1u << alarm_num);
// Use timer alarm to wake.
clocks_hw->sleep_en0 = 0x0;
#if PICO_RP2040
@@ -233,8 +249,11 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
#else
#error Unknown processor
#endif
- timer_hw->intr = 1u << alarm_num; // clear any IRQ
- timer_hw->alarm[alarm_num] = timer_hw->timerawl + delay_ms * 1000;
+ hardware_alarm_claim(MICROPY_HW_LIGHTSLEEP_ALARM_NUM);
+ hardware_alarm_set_callback(MICROPY_HW_LIGHTSLEEP_ALARM_NUM, alarm_sleep_callback);
+ if (hardware_alarm_set_target(MICROPY_HW_LIGHTSLEEP_ALARM_NUM, make_timeout_time_ms(delay_ms)) == PICO_OK) {
+ alarm_armed = true;
+ }
} else {
// TODO: Use RTC alarm to wake.
clocks_hw->sleep_en0 = 0x0;
@@ -264,10 +283,17 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
#endif
// Go into low-power mode.
- __wfi();
+ if (alarm_armed) {
+ __wfi();
- if (!timer3_enabled) {
- irq_set_enabled(irq_num, false);
+ #if DEBUG_LIGHTSLEEP
+ #if PICO_RP2040
+ pending_intr = nvic_hw->ispr;
+ #else
+ pending_intr[0] = nvic_hw->ispr[0];
+ pending_intr[1] = nvic_hw->ispr[1];
+ #endif
+ #endif
}
clocks_hw->sleep_en0 = save_sleep_en0;
clocks_hw->sleep_en1 = save_sleep_en1;
@@ -282,9 +308,37 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
// Re-sync mp_hal_time_ns() counter with aon timer.
mp_hal_time_ns_set_from_rtc();
+
+ // Note: This must be done after MICROPY_END_ATOMIC_SECTION
+ if (use_timer_alarm) {
+ if (alarm_armed) {
+ hardware_alarm_cancel(MICROPY_HW_LIGHTSLEEP_ALARM_NUM);
+ }
+ hardware_alarm_set_callback(MICROPY_HW_LIGHTSLEEP_ALARM_NUM, NULL);
+ hardware_alarm_unclaim(MICROPY_HW_LIGHTSLEEP_ALARM_NUM);
+
+ #if DEBUG_LIGHTSLEEP
+ // Check irq.h for the list of IRQ's
+ // for rp2040 00000042: TIMER_IRQ_1 woke the device as expected
+ // 00000020: USBCTRL_IRQ woke the device (probably early)
+ // For rp2350 00000000:00000002: TIMER0_IRQ_1 woke the device as expected
+ // 00000000:00004000: USBCTRL_IRQ woke the device (probably early)
+ #if PICO_RP2040
+ mp_printf(MP_PYTHON_PRINTER, "lightsleep: pending_intr=%08lx\n", pending_intr);
+ #else
+ mp_printf(MP_PYTHON_PRINTER, "lightsleep: pending_intr=%08lx:%08lx\n", pending_intr[1], pending_intr[0]);
+ #endif
+ #endif
+ }
+
+ #if MICROPY_PY_THREAD
+ // Clearing the flag here is atomic, and we know we're the ones who set it
+ // (higher up, inside the critical section)
+ in_lightsleep = false;
+ #endif
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
mp_machine_lightsleep(n_args, args);
mp_machine_reset();
}
diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h
index 3d65737266..35afea4fac 100644
--- a/ports/rp2/mpconfigport.h
+++ b/ports/rp2/mpconfigport.h
@@ -198,8 +198,9 @@
#define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP)
// Hardware timer alarm index. Available range 0-3.
-// Number 3 is currently used by pico-sdk (PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM)
+// Number 3 is currently used by pico-sdk alarm pool (PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM)
#define MICROPY_HW_SOFT_TIMER_ALARM_NUM (2)
+#define MICROPY_HW_LIGHTSLEEP_ALARM_NUM (1)
// fatfs configuration
#define MICROPY_FATFS_ENABLE_LFN (2)
@@ -241,46 +242,11 @@
#endif
#endif
-#if MICROPY_PY_NETWORK_CYW43
-extern const struct _mp_obj_type_t mp_network_cyw43_type;
-#define MICROPY_HW_NIC_CYW43 \
- { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, \
- { MP_ROM_QSTR(MP_QSTR_STAT_IDLE), MP_ROM_INT(CYW43_LINK_DOWN) }, \
- { MP_ROM_QSTR(MP_QSTR_STAT_CONNECTING), MP_ROM_INT(CYW43_LINK_JOIN) }, \
- { MP_ROM_QSTR(MP_QSTR_STAT_WRONG_PASSWORD), MP_ROM_INT(CYW43_LINK_BADAUTH) }, \
- { MP_ROM_QSTR(MP_QSTR_STAT_NO_AP_FOUND), MP_ROM_INT(CYW43_LINK_NONET) }, \
- { MP_ROM_QSTR(MP_QSTR_STAT_CONNECT_FAIL), MP_ROM_INT(CYW43_LINK_FAIL) }, \
- { MP_ROM_QSTR(MP_QSTR_STAT_GOT_IP), MP_ROM_INT(CYW43_LINK_UP) },
-#else
-#define MICROPY_HW_NIC_CYW43
-#endif
-
-#if MICROPY_PY_NETWORK_NINAW10
-// This Network interface requires the extended socket state.
-#ifndef MICROPY_PY_SOCKET_EXTENDED_STATE
-#define MICROPY_PY_SOCKET_EXTENDED_STATE (1)
-#endif
-extern const struct _mp_obj_type_t mod_network_nic_type_nina;
-#define MICROPY_HW_NIC_NINAW10 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_nic_type_nina) },
-#else
-#define MICROPY_HW_NIC_NINAW10
-#endif
-
-#if MICROPY_PY_NETWORK_WIZNET5K
-extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k;
-#define MICROPY_HW_NIC_WIZNET5K { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) },
-#else
-#define MICROPY_HW_NIC_WIZNET5K
-#endif
-
#ifndef MICROPY_BOARD_NETWORK_INTERFACES
#define MICROPY_BOARD_NETWORK_INTERFACES
#endif
#define MICROPY_PORT_NETWORK_INTERFACES \
- MICROPY_HW_NIC_CYW43 \
- MICROPY_HW_NIC_NINAW10 \
- MICROPY_HW_NIC_WIZNET5K \
MICROPY_BOARD_NETWORK_INTERFACES \
// Additional entries for use with pendsv_schedule_dispatch.
diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c
index caecb69509..b581b3b59f 100644
--- a/ports/rp2/mphalport.c
+++ b/ports/rp2/mphalport.c
@@ -266,17 +266,7 @@ void soft_timer_init(void) {
}
void mp_wfe_or_timeout(uint32_t timeout_ms) {
- soft_timer_entry_t timer;
-
- // Note the timer doesn't have an associated callback, it just exists to create a
- // hardware interrupt to wake the CPU
- soft_timer_static_init(&timer, SOFT_TIMER_MODE_ONE_SHOT, 0, NULL);
- soft_timer_insert(&timer, timeout_ms);
-
- __wfe();
-
- // Clean up the timer node if it's not already
- soft_timer_remove(&timer);
+ best_effort_wfe_or_timeout(delayed_by_ms(get_absolute_time(), timeout_ms));
}
int mp_hal_is_pin_reserved(int n) {
diff --git a/ports/rp2/mphalport.h b/ports/rp2/mphalport.h
index 956db3ec70..5012b682b9 100644
--- a/ports/rp2/mphalport.h
+++ b/ports/rp2/mphalport.h
@@ -123,7 +123,18 @@ static inline mp_uint_t mp_hal_get_cpu_freq(void) {
#define MP_HAL_PIN_PULL_UP (1)
#define MP_HAL_PIN_PULL_DOWN (2)
+// Open drain behaviour is simulated.
+#if NUM_BANK0_GPIOS > 32
extern uint64_t machine_pin_open_drain_mask;
+#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1ULL << id))
+#define GPIO_ENABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask |= (1ULL << id))
+#define GPIO_DISABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask &= ~(1ULL << id))
+#else
+extern uint32_t machine_pin_open_drain_mask;
+#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1U << id))
+#define GPIO_ENABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask |= (1U << id))
+#define GPIO_DISABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask &= ~(1U << id))
+#endif
mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in);
@@ -133,13 +144,13 @@ static inline unsigned int mp_hal_pin_name(mp_hal_pin_obj_t pin) {
static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) {
gpio_set_dir(pin, GPIO_IN);
- machine_pin_open_drain_mask &= ~(1 << pin);
+ GPIO_DISABLE_OPEN_DRAIN(pin);
gpio_set_function(pin, GPIO_FUNC_SIO);
}
static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) {
gpio_set_dir(pin, GPIO_OUT);
- machine_pin_open_drain_mask &= ~(1 << pin);
+ GPIO_DISABLE_OPEN_DRAIN(pin);
gpio_set_function(pin, GPIO_FUNC_SIO);
}
@@ -151,7 +162,7 @@ static inline void mp_hal_pin_open_drain_with_value(mp_hal_pin_obj_t pin, int v)
gpio_put(pin, 0);
gpio_set_dir(pin, GPIO_OUT);
}
- machine_pin_open_drain_mask |= 1 << pin;
+ GPIO_ENABLE_OPEN_DRAIN(pin);
gpio_set_function(pin, GPIO_FUNC_SIO);
}
diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c
index e1e1567828..fed34be380 100644
--- a/ports/rp2/mpnetworkport.c
+++ b/ports/rp2/mpnetworkport.c
@@ -31,6 +31,7 @@
#if MICROPY_PY_LWIP
#include "shared/runtime/softtimer.h"
+#include "lwip/netif.h"
#include "lwip/timeouts.h"
// Poll lwIP every 64ms by default
@@ -39,6 +40,9 @@
// Soft timer for running lwIP in the background.
static soft_timer_entry_t mp_network_soft_timer;
+// Callback for change of netif state
+NETIF_DECLARE_EXT_CALLBACK(netif_callback)
+
#if MICROPY_PY_NETWORK_CYW43
#include "lib/cyw43-driver/src/cyw43.h"
#include "lib/cyw43-driver/src/cyw43_stats.h"
@@ -137,17 +141,48 @@ static void mp_network_soft_timer_callback(soft_timer_entry_t *self) {
#if MICROPY_PY_NETWORK_WIZNET5K
wiznet5k_poll();
#endif
+
+ // Only keep the timer running if any TCP sockets are active, or any netif is up
+ struct netif *netif;
+ extern void *tcp_active_pcbs;
+ bool keep_running = (tcp_active_pcbs != NULL);
+ if (!keep_running) {
+ NETIF_FOREACH(netif) {
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+ keep_running = true;
+ break;
+ }
+ }
+ }
+
+ // Periodic timer will re-queue as soon as this handler exits,
+ // one shot timer will not
+ mp_network_soft_timer.mode = keep_running ? SOFT_TIMER_MODE_PERIODIC : SOFT_TIMER_MODE_ONE_SHOT;
}
+static void mp_network_netif_status_cb(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args);
+
void mod_network_lwip_init(void) {
soft_timer_static_init(
&mp_network_soft_timer,
- SOFT_TIMER_MODE_PERIODIC,
+ SOFT_TIMER_MODE_ONE_SHOT,
LWIP_TICK_RATE_MS,
mp_network_soft_timer_callback
);
- soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS);
+ if (netif_callback.callback_fn == NULL) {
+ netif_add_ext_callback(&netif_callback, mp_network_netif_status_cb);
+ }
+}
+
+static void mp_network_netif_status_cb(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args) {
+ // Start the network soft timer any time an interface comes up, unless
+ // it's already running
+ if (reason == LWIP_NSC_LINK_CHANGED && args->link_changed.state
+ && mp_network_soft_timer.mode == SOFT_TIMER_MODE_ONE_SHOT) {
+ mp_network_soft_timer.mode = SOFT_TIMER_MODE_PERIODIC;
+ soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS);
+ }
}
#endif // MICROPY_PY_LWIP
diff --git a/ports/rp2/rp2_dma.c b/ports/rp2/rp2_dma.c
index 78f69e6452..94c61e226e 100644
--- a/ports/rp2/rp2_dma.c
+++ b/ports/rp2/rp2_dma.c
@@ -315,7 +315,7 @@ static mp_obj_t rp2_dma_pack_ctrl(size_t n_pos_args, const mp_obj_t *pos_args, m
// Pack keyword settings into a control register value, using either the default for this
// DMA channel or the provided defaults
rp2_dma_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
- mp_uint_t value = DEFAULT_DMA_CONFIG | ((self->channel & 0xf) << 11);
+ mp_uint_t value = DEFAULT_DMA_CONFIG | ((self->channel & 0xf) << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB);
if (n_pos_args > 1) {
mp_raise_TypeError(MP_ERROR_TEXT("pack_ctrl only takes keyword arguments"));
diff --git a/ports/rp2/rp2_flash.c b/ports/rp2/rp2_flash.c
index a9beabf051..d6b9e13653 100644
--- a/ports/rp2/rp2_flash.c
+++ b/ports/rp2/rp2_flash.c
@@ -49,6 +49,19 @@
static_assert(MICROPY_HW_ROMFS_BYTES % 4096 == 0, "ROMFS size must be a multiple of 4K");
static_assert(MICROPY_HW_FLASH_STORAGE_BYTES % 4096 == 0, "Flash storage size must be a multiple of 4K");
+#ifndef MICROPY_HW_FLASH_MAX_FREQ
+// Emulate Pico SDK's SYS_CLK_HZ / PICO_FLASH_SPI_CLKDIV behaviour by default.
+// On RP2040 if PICO_USE_FASTEST_SUPPORTED_CLOCK is set then SYS_CLK_HZ can be
+// 200MHz, potentially putting timings derived from PICO_FLASH_SPI_CLKDIV
+// out of range.
+#ifdef PICO_FLASH_SPI_CLKDIV
+#define MICROPY_HW_FLASH_MAX_FREQ (SYS_CLK_HZ / PICO_FLASH_SPI_CLKDIV)
+#else
+// A default PICO_FLASH_SPI_CLKDIV of 4 is set in boot2_generic_03h.S
+#define MICROPY_HW_FLASH_MAX_FREQ (SYS_CLK_HZ / 4)
+#endif
+#endif
+
#ifndef MICROPY_HW_FLASH_STORAGE_BASE
#define MICROPY_HW_FLASH_STORAGE_BASE (PICO_FLASH_SIZE_BYTES - MICROPY_HW_FLASH_STORAGE_BYTES)
#endif
@@ -104,9 +117,8 @@ static bool use_multicore_lockout(void) {
// and core1 locked out if relevant.
static void __no_inline_not_in_flash_func(rp2_flash_set_timing_internal)(int clock_hz) {
- // Use the minimum divisor assuming a 133MHz flash.
- const int max_flash_freq = 133000000;
- int divisor = (clock_hz + max_flash_freq - 1) / max_flash_freq;
+ // Use the minimum divisor based upon our target MICROPY_HW_FLASH_MAX_FREQ
+ int divisor = (clock_hz + MICROPY_HW_FLASH_MAX_FREQ - 1) / MICROPY_HW_FLASH_MAX_FREQ;
#if PICO_RP2350
// Make sure flash is deselected - QMI doesn't appear to have a busy flag(!)
diff --git a/ports/rp2/tools_patch/Findpioasm.cmake b/ports/rp2/tools_patch/Findpioasm.cmake
new file mode 100644
index 0000000000..72e6bf6fb6
--- /dev/null
+++ b/ports/rp2/tools_patch/Findpioasm.cmake
@@ -0,0 +1,58 @@
+# Finds (or builds) the pioasm executable
+#
+# This will define the following imported targets
+#
+# pioasm
+#
+
+# This is a temporary patched copy of pico-sdk file Findpioasm.cmake to work around
+# a host toolchain issue with GCC 15.1:
+# https://github.com/raspberrypi/pico-sdk/issues/2448
+
+if (NOT TARGET pioasm)
+ # todo we would like to use pckgconfig to look for it first
+ # see https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/
+
+ include(ExternalProject)
+
+ set(PIOASM_SOURCE_DIR ${PICO_SDK_PATH}/tools/pioasm)
+ set(PIOASM_BINARY_DIR ${CMAKE_BINARY_DIR}/pioasm)
+ set(PIOASM_INSTALL_DIR ${CMAKE_BINARY_DIR}/pioasm-install CACHE PATH "Directory where pioasm has been installed" FORCE)
+
+ set(pioasmBuild_TARGET pioasmBuild)
+ set(pioasm_TARGET pioasm)
+
+ if (NOT TARGET ${pioasmBuild_TARGET})
+ pico_message_debug("PIOASM will need to be built")
+# message("Adding external project ${pioasmBuild_Target} in ${CMAKE_CURRENT_LIST_DIR}}")
+ ExternalProject_Add(${pioasmBuild_TARGET}
+ PREFIX pioasm
+ SOURCE_DIR ${PIOASM_SOURCE_DIR}
+ BINARY_DIR ${PIOASM_BINARY_DIR}
+ INSTALL_DIR ${PIOASM_INSTALL_DIR}
+ CMAKE_ARGS
+ "--no-warn-unused-cli"
+ "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}"
+ "-DPIOASM_FLAT_INSTALL=1"
+ "-DCMAKE_INSTALL_PREFIX=${PIOASM_INSTALL_DIR}"
+ "-DCMAKE_RULE_MESSAGES=OFF" # quieten the build
+ "-DCMAKE_INSTALL_MESSAGE=NEVER" # quieten the install
+ # Toolchain workaround follows
+ "-DCMAKE_CXX_FLAGS=-include cstdint"
+ CMAKE_CACHE_ARGS "-DPIOASM_EXTRA_SOURCE_FILES:STRING=${PIOASM_EXTRA_SOURCE_FILES}"
+ BUILD_ALWAYS 1 # force dependency checking
+ EXCLUDE_FROM_ALL TRUE
+ )
+ endif()
+
+ if (CMAKE_HOST_WIN32)
+ set(pioasm_EXECUTABLE ${PIOASM_INSTALL_DIR}/pioasm/pioasm.exe)
+ else()
+ set(pioasm_EXECUTABLE ${PIOASM_INSTALL_DIR}/pioasm/pioasm)
+ endif()
+ add_executable(${pioasm_TARGET} IMPORTED GLOBAL)
+ set_property(TARGET ${pioasm_TARGET} PROPERTY IMPORTED_LOCATION
+ ${pioasm_EXECUTABLE})
+
+ add_dependencies(${pioasm_TARGET} ${pioasmBuild_TARGET})
+endif()
diff --git a/ports/samd/Makefile b/ports/samd/Makefile
index 005664f178..bec530d803 100644
--- a/ports/samd/Makefile
+++ b/ports/samd/Makefile
@@ -113,8 +113,6 @@ LIBSTDCPP_FILE_NAME = "$(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a)"
LDFLAGS += -L"$(shell dirname $(LIBSTDCPP_FILE_NAME))"
endif
-LDFLAGS += --wrap=dcd_event_handler
-
MPY_CROSS_FLAGS += -march=$(MPY_CROSS_MCU_ARCH)
SRC_C += \
diff --git a/ports/samd/boards/MINISAM_M4/mpconfigboard.h b/ports/samd/boards/MINISAM_M4/mpconfigboard.h
index 6d908bdcb8..30a7a80454 100644
--- a/ports/samd/boards/MINISAM_M4/mpconfigboard.h
+++ b/ports/samd/boards/MINISAM_M4/mpconfigboard.h
@@ -7,4 +7,4 @@
#define MICROPY_HW_DEFAULT_I2C_ID (2)
#define MICROPY_HW_DEFAULT_SPI_ID (1)
-#define MICROPY_HW_QSPIFLASH GD25Q16C
+#define MICROPY_HW_QSPIFLASH W25Q16JV_IQ
diff --git a/ports/samd/boards/SAMD_GENERIC_D21X18/board.json b/ports/samd/boards/SAMD_GENERIC_D21X18/board.json
index a14730bd1b..c00d78addd 100644
--- a/ports/samd/boards/SAMD_GENERIC_D21X18/board.json
+++ b/ports/samd/boards/SAMD_GENERIC_D21X18/board.json
@@ -11,6 +11,7 @@
],
"mcu": "samd21",
"vendor": "Microchip",
+ "url": "https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/32-bit-mcus/sam-32-bit-mcus/sam-d",
"product": "Generic SAMD21J18",
"thumbnail": ""
}
diff --git a/ports/samd/boards/SAMD_GENERIC_D51X19/board.json b/ports/samd/boards/SAMD_GENERIC_D51X19/board.json
index 21cb114bf6..2be5f20851 100644
--- a/ports/samd/boards/SAMD_GENERIC_D51X19/board.json
+++ b/ports/samd/boards/SAMD_GENERIC_D51X19/board.json
@@ -11,6 +11,7 @@
],
"mcu": "samd51",
"vendor": "Microchip",
+ "url": "https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/32-bit-mcus/sam-32-bit-mcus/sam-d",
"product": "Generic SAMD51P19",
"thumbnail": ""
}
diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/board.json b/ports/samd/boards/SAMD_GENERIC_D51X20/board.json
index ae4f83472e..8384484327 100644
--- a/ports/samd/boards/SAMD_GENERIC_D51X20/board.json
+++ b/ports/samd/boards/SAMD_GENERIC_D51X20/board.json
@@ -11,6 +11,7 @@
],
"mcu": "samd51",
"vendor": "Microchip",
+ "url": "https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/32-bit-mcus/sam-32-bit-mcus/sam-d",
"product": "Generic SAMD51P20",
"thumbnail": ""
}
diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk
index ddba3dcbba..b240c2587f 100644
--- a/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk
+++ b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk
@@ -1,13 +1,11 @@
MCU_SERIES = SAMD51
CMSIS_MCU = SAMD51P20A
-LD_FILES = boards/samd51x19a.ld sections.ld
+LD_FILES = boards/samd51x20a.ld sections.ld
TEXT0 = 0x4000
-
# The ?='s allow overriding in mpconfigboard.mk.
# MicroPython settings
# The size of a MCU flash filesystem will be
-# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_VFSROMSIZE
+# 1008k - MICROPY_HW_CODESIZE
# The default for MICROPY_HW_VFSROMSIZE is 64K
MICROPY_HW_CODESIZE ?= 752K
-MICROPY_HW_VFSROMSIZE ?= 128K
diff --git a/ports/samd/boards/SEEED_XIAO_SAMD21/board.json b/ports/samd/boards/SEEED_XIAO_SAMD21/board.json
index 4d19f6b945..0d6b2fc30d 100644
--- a/ports/samd/boards/SEEED_XIAO_SAMD21/board.json
+++ b/ports/samd/boards/SEEED_XIAO_SAMD21/board.json
@@ -10,7 +10,7 @@
"seeeduino-xiao.jpg"
],
"mcu": "samd21",
- "product": "Seeeduino XIAO (SAMD21)",
+ "product": "XIAO SAMD21",
"thumbnail": "",
"url": "https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html",
"vendor": "Seeed Studio"
diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json
new file mode 100644
index 0000000000..789bd97730
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json
@@ -0,0 +1,21 @@
+{
+ "deploy": [
+ "../deploy.md"
+ ],
+ "docs": "",
+ "features": [
+ "Battery Charging",
+ "DAC",
+ "External Flash",
+ "USB",
+ "RGB LED"
+ ],
+ "images": [
+ "sparkfun_readboard_turbo.jpg"
+ ],
+ "mcu": "samd21",
+ "product": "SparkFun RedBoard Turbo",
+ "thumbnail": "",
+ "url": "https://www.sparkfun.com/products/14812",
+ "vendor": "SparkFun"
+}
diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h
new file mode 100644
index 0000000000..cd0ec51acc
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h
@@ -0,0 +1,11 @@
+#define MICROPY_HW_BOARD_NAME "SparkFun RedBoard Turbo"
+#define MICROPY_HW_MCU_NAME "SAMD21G18A"
+
+#define MICROPY_HW_XOSC32K (1)
+
+#define MICROPY_HW_SPIFLASH (1)
+#define MICROPY_HW_SPIFLASH_ID (5)
+
+#define MICROPY_HW_DEFAULT_UART_ID (0)
+#define MICROPY_HW_DEFAULT_I2C_ID (3)
+#define MICROPY_HW_DEFAULT_SPI_ID (4)
diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk
new file mode 100644
index 0000000000..6ea327a650
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk
@@ -0,0 +1,8 @@
+MCU_SERIES = SAMD21
+CMSIS_MCU = SAMD21G18A
+LD_FILES = boards/samd21x18a.ld sections.ld
+TEXT0 = 0x2000
+
+# The ?='s allow overriding in mpconfigboard.mk.
+# MicroPython settings
+MICROPY_HW_CODESIZE ?= 232K
diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv
new file mode 100644
index 0000000000..f065fc8917
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv
@@ -0,0 +1,47 @@
+# The lines contain pairs of Pin name and Pin number.
+# Pin names must be valid Python identifiers.
+# Pin numbers have the form Pxnn, with x being A, B, C or D.
+# Lines starting with # or empty lines are ignored.
+
+USB_DM,PA24
+USB_DP,PA25
+
+SWCLK,PA30
+SWDIO,PA31
+
+D0,PA11
+D1,PA10
+D2,PA14
+D3,PA09
+D4,PA08
+D5,PA15
+D6,PA20
+D7,PA21
+D8,PA06
+D9,PA07
+D10,PA18
+D11,PA16
+D12,PA19
+D13,PA17
+A0,PA02
+A1,PB08
+A2,PB09
+A3,PA04
+A4,PA05
+A5,PB02
+AREF,PA03
+RX,PA11
+TX,PA10
+SCL,PA23
+SDA,PA22
+MOSI,PB10
+MISO,PA12
+SCK,PB11
+FLASH_MOSI,PB22
+FLASH_MISO,PB03
+FLASH_SCK,PB23
+FLASH_CS,PA13
+NEOPIXEL,PA30
+
+LED_TX,PA27
+LED_RX,PA31
diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json
new file mode 100644
index 0000000000..e5c5411a0c
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json
@@ -0,0 +1,19 @@
+{
+ "deploy": [
+ "../deploy.md"
+ ],
+ "docs": "",
+ "features": [
+ "Battery Charging",
+ "DAC",
+ "USB"
+ ],
+ "images": [
+ "sparkfun_sam21_dev_breakout.jpg"
+ ],
+ "mcu": "samd21",
+ "product": "SparkFun SAMD21 Dev Breakout",
+ "thumbnail": "",
+ "url": "https://www.sparkfun.com/products/13672",
+ "vendor": "SparkFun"
+}
diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h
new file mode 100644
index 0000000000..0679a9f4e4
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h
@@ -0,0 +1,8 @@
+#define MICROPY_HW_BOARD_NAME "SparkFun SAMD21 Dev Breakout"
+#define MICROPY_HW_MCU_NAME "SAMD21G18A"
+
+#define MICROPY_HW_XOSC32K (1)
+
+#define MICROPY_HW_DEFAULT_UART_ID (0)
+#define MICROPY_HW_DEFAULT_I2C_ID (3)
+#define MICROPY_HW_DEFAULT_SPI_ID (4)
diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk
new file mode 100644
index 0000000000..8696c966bc
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk
@@ -0,0 +1,4 @@
+MCU_SERIES = SAMD21
+CMSIS_MCU = SAMD21G18A
+LD_FILES = boards/samd21x18a.ld sections.ld
+TEXT0 = 0x2000
diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv
new file mode 100644
index 0000000000..794be31800
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv
@@ -0,0 +1,45 @@
+# The lines contain pairs of Pin name and Pin number.
+# Pin names must be valid Python identifiers.
+# Pin numbers have the form Pxnn, with x being A, B, C or D.
+# Lines starting with # or empty lines are ignored.
+
+USB_DM,PA24
+USB_DP,PA25
+
+SWCLK,PA30
+SWDIO,PA31
+
+D0,PA11
+D1,PA10
+D2,PA14
+D3,PA09
+D4,PA08
+D5,PA15
+D6,PA20
+D7,PA21
+D8,PA06
+D9,PA07
+D10,PA18
+D11,PA16
+D12,PA19
+D13,PA17
+A0,PA02
+A1,PB08
+A2,PB09
+A3,PA04
+A4,PA05
+A5,PB02
+AREF,PA03
+RX,PA11
+TX,PA10
+SCL,PA23
+SDA,PA22
+MOSI,PB10
+MISO,PA12
+SCK,PB11
+D38,PA13
+D30,PB22
+D31,PB23
+
+LED_TX,PA27
+LED_RX,PB03
diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json
index ee9ca9d368..6e89cebadd 100644
--- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json
+++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json
@@ -13,8 +13,8 @@
"sparkfun_samd51_thing_plus.jpg"
],
"mcu": "samd51",
- "product": "Sparkfun SAMD51 Thing Plus",
+ "product": "SAMD51 Thing Plus",
"thumbnail": "",
"url": "https://www.sparkfun.com/products/14713",
- "vendor": "Sparkfun"
+ "vendor": "SparkFun"
}
diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h
index fe2226a59e..9e0db7875f 100644
--- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h
+++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h
@@ -1,4 +1,4 @@
-#define MICROPY_HW_BOARD_NAME "Sparkfun SAMD51 Thing Plus"
+#define MICROPY_HW_BOARD_NAME "SparkFun SAMD51 Thing Plus"
#define MICROPY_HW_MCU_NAME "SAMD51J20A"
#define MICROPY_HW_XOSC32K (1)
diff --git a/ports/samd/machine_i2c.c b/ports/samd/machine_i2c.c
index 03af3d29ed..172518523d 100644
--- a/ports/samd/machine_i2c.c
+++ b/ports/samd/machine_i2c.c
@@ -37,9 +37,9 @@
#include "genhdr/pins.h"
#include "clock_config.h"
-#define DEFAULT_I2C_FREQ (400000)
-#define RISETIME_NS (200)
-#define I2C_TIMEOUT (100)
+#define DEFAULT_I2C_FREQ (400000)
+#define RISETIME_NS (200)
+#define DEFAULT_I2C_TIMEOUT (50000)
#define IS_BUS_BUSY (i2c->I2CM.STATUS.bit.BUSSTATE == 3)
#define NACK_RECVD (i2c->I2CM.STATUS.bit.RXNACK == 1)
@@ -67,6 +67,7 @@ typedef struct _machine_i2c_obj_t {
uint8_t state;
uint32_t freq;
uint32_t timeout;
+ uint32_t timer;
size_t len;
uint8_t *buf;
} machine_i2c_obj_t;
@@ -88,7 +89,7 @@ void common_i2c_irq_handler(int i2c_id) {
if (self->len > 0) {
*(self->buf)++ = i2c->I2CM.DATA.reg;
self->len--;
- self->timeout = I2C_TIMEOUT;
+ self->timer = self->timeout;
}
if (self->len > 0) { // no ACK at the last byte
PREPARE_ACK; // Send ACK
@@ -105,7 +106,7 @@ void common_i2c_irq_handler(int i2c_id) {
} else if (self->len > 0) { // data to be sent
i2c->I2CM.DATA.bit.DATA = *(self->buf)++;
self->len--;
- self->timeout = I2C_TIMEOUT;
+ self->timer = self->timeout;
} else { // No data left, if there was any.
self->state = state_done;
i2c->I2CM.INTFLAG.reg |= SERCOM_I2CM_INTFLAG_MB;
@@ -120,12 +121,13 @@ void common_i2c_irq_handler(int i2c_id) {
static void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
- mp_printf(print, "I2C(%u, freq=%u, scl=\"%q\", sda=\"%q\")",
- self->id, self->freq, pin_find_by_id(self->scl)->name, pin_find_by_id(self->sda)->name);
+ mp_printf(print, "I2C(%u, freq=%u, scl=\"%q\", sda=\"%q\", timeout=%u)",
+ self->id, self->freq, pin_find_by_id(self->scl)->name, pin_find_by_id(self->sda)->name,
+ self->timeout * 1000);
}
mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
- enum { ARG_id, ARG_freq, ARG_scl, ARG_sda };
+ enum { ARG_id, ARG_freq, ARG_scl, ARG_sda, ARG_timeout };
static const mp_arg_t allowed_args[] = {
#if MICROPY_HW_DEFAULT_I2C_ID < 0
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} },
@@ -140,6 +142,7 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
{ MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
#endif
+ { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_I2C_TIMEOUT} },
};
// Parse args.
@@ -168,6 +171,8 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
}
MP_STATE_PORT(sercom_table[self->id]) = self;
self->freq = args[ARG_freq].u_int;
+ // The unit for ARG_timeout is us, but the code uses ms.
+ self->timeout = args[ARG_timeout].u_int / 1000;
// Configure the Pin mux.
mp_hal_set_pin_mux(self->scl, scl_pad_config.alt_fct);
@@ -224,13 +229,13 @@ static int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si
machine_i2c_obj_t *self = (machine_i2c_obj_t *)self_in;
Sercom *i2c = self->instance;
- self->timeout = I2C_TIMEOUT;
+ self->timer = self->timeout;
self->len = len;
self->buf = buf;
// Wait a while if the bus is busy
- while (IS_BUS_BUSY && self->timeout) {
+ while (IS_BUS_BUSY && self->timer) {
MICROPY_EVENT_POLL_HOOK
- if (--self->timeout == 0) {
+ if (--self->timer == 0) {
return -MP_ETIMEDOUT;
}
}
@@ -242,9 +247,9 @@ static int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si
i2c->I2CM.ADDR.bit.ADDR = (addr << 1) | READ_MODE;
// Transfer the data
- self->timeout = I2C_TIMEOUT;
- while (self->state == state_busy && self->timeout) {
- self->timeout--;
+ self->timer = self->timeout;
+ while (self->state == state_busy && self->timer) {
+ self->timer--;
MICROPY_EVENT_POLL_HOOK
}
i2c->I2CM.INTENCLR.reg = SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB | SERCOM_I2CM_INTENSET_ERROR;
@@ -256,7 +261,7 @@ static int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si
} else if (self->state == state_buserr) {
SET_STOP_STATE;
return -MP_EIO;
- } else if (self->timeout == 0) {
+ } else if (self->timer == 0) {
SET_STOP_STATE;
return -MP_ETIMEDOUT;
}
diff --git a/ports/samd/machine_rtc.c b/ports/samd/machine_rtc.c
index 74c2266d60..4b03488b71 100644
--- a/ports/samd/machine_rtc.c
+++ b/ports/samd/machine_rtc.c
@@ -133,6 +133,7 @@ static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args,
}
#endif
+ mp_hal_time_ns_set_from_rtc();
return mp_const_none;
}
}
diff --git a/ports/samd/main.c b/ports/samd/main.c
index 2bbaf63e6e..a7da95582f 100644
--- a/ports/samd/main.c
+++ b/ports/samd/main.c
@@ -28,6 +28,7 @@
#include "py/runtime.h"
#include "py/gc.h"
#include "py/mperrno.h"
+#include "py/mphal.h"
#include "py/stackctrl.h"
#include "shared/readline/readline.h"
#include "shared/runtime/gchelper.h"
@@ -45,6 +46,7 @@ extern void sercom_deinit_all(void);
void samd_main(void) {
mp_stack_set_top(&_estack);
mp_stack_set_limit(&_estack - &_sstack - 1024);
+ mp_hal_time_ns_set_from_rtc();
for (;;) {
gc_init(&_sheap, &_eheap);
diff --git a/ports/samd/mcu/samd51/mpconfigmcu.h b/ports/samd/mcu/samd51/mpconfigmcu.h
index 831949bab5..8cce90b886 100644
--- a/ports/samd/mcu/samd51/mpconfigmcu.h
+++ b/ports/samd/mcu/samd51/mpconfigmcu.h
@@ -23,7 +23,7 @@ unsigned long trng_random_u32(void);
#define MICROPY_FATFS_MAX_SS (4096)
#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */
-#define VFS_BLOCK_SIZE_BYTES (1536) //
+#define VFS_BLOCK_SIZE_BYTES (2048) //
#ifndef MICROPY_HW_UART_TXBUF
#define MICROPY_HW_UART_TXBUF (1)
diff --git a/ports/samd/modmachine.c b/ports/samd/modmachine.c
index 65dbf8a7f5..c20ce8d641 100644
--- a/ports/samd/modmachine.c
+++ b/ports/samd/modmachine.c
@@ -42,7 +42,7 @@
#define DBL_TAP_ADDR ((volatile uint32_t *)(HSRAM_ADDR + HSRAM_SIZE - 4))
#endif
// A board may define a DPL_TAP_ADDR_ALT, which will be set as well
-// Needed at the moment for Sparkfun SAMD51 Thing Plus
+// Needed at the moment for SparkFun SAMD51 Thing Plus
#define DBL_TAP_MAGIC_LOADER 0xf01669ef
#define DBL_TAP_MAGIC_RESET 0xf02669ef
@@ -65,7 +65,7 @@
extern bool EIC_occured;
extern uint32_t _dbl_tap_addr;
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
*DBL_TAP_ADDR = DBL_TAP_MAGIC_RESET;
#ifdef DBL_TAP_ADDR_ALT
*DBL_TAP_ADDR_ALT = DBL_TAP_MAGIC_RESET;
@@ -73,7 +73,7 @@ NORETURN static void mp_machine_reset(void) {
NVIC_SystemReset();
}
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
*DBL_TAP_ADDR = DBL_TAP_MAGIC_LOADER;
#ifdef DBL_TAP_ADDR_ALT
*DBL_TAP_ADDR_ALT = DBL_TAP_MAGIC_LOADER;
@@ -164,7 +164,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
set_cpu_freq(freq);
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
mp_machine_lightsleep(n_args, args);
mp_machine_reset();
}
diff --git a/ports/samd/modtime.c b/ports/samd/modtime.c
index 83072dd16f..0bed3cb83a 100644
--- a/ports/samd/modtime.c
+++ b/ports/samd/modtime.c
@@ -28,6 +28,8 @@
#include "shared/timeutils/timeutils.h"
#include "modmachine.h"
+static uint64_t time_us_64_offset_from_epoch;
+
// Return the localtime as an 8-tuple.
static mp_obj_t mp_time_localtime_get(void) {
timeutils_struct_time_t tm;
@@ -54,3 +56,15 @@ static mp_obj_t mp_time_time_get(void) {
return mp_obj_new_int_from_uint(timeutils_mktime(
tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec));
}
+
+void mp_hal_time_ns_set_from_rtc(void) {
+ timeutils_struct_time_t tm;
+ rtc_gettime(&tm);
+ uint64_t time_us = (uint64_t)timeutils_mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
+ tm.tm_min, tm.tm_sec) * 1000000ULL;
+ time_us_64_offset_from_epoch = time_us - mp_hal_ticks_us_64();
+}
+
+uint64_t mp_hal_time_ns(void) {
+ return (time_us_64_offset_from_epoch + mp_hal_ticks_us_64()) * 1000ULL;
+}
diff --git a/ports/samd/mphalport.h b/ports/samd/mphalport.h
index 6c9e525bb9..69e2b60640 100644
--- a/ports/samd/mphalport.h
+++ b/ports/samd/mphalport.h
@@ -47,6 +47,7 @@ extern int mp_interrupt_char;
extern ringbuf_t stdin_ringbuf;
extern volatile uint32_t systick_ms;
uint64_t mp_hal_ticks_us_64(void);
+void mp_hal_time_ns_set_from_rtc(void);
void mp_hal_set_interrupt_char(int c);
@@ -94,10 +95,6 @@ static inline mp_uint_t mp_hal_ticks_cpu(void) {
}
#endif
-static inline uint64_t mp_hal_time_ns(void) {
- return mp_hal_ticks_us_64() * 1000;
-}
-
// C-level pin HAL
#include "py/obj.h"
diff --git a/ports/samd/samd_qspiflash.c b/ports/samd/samd_qspiflash.c
index e25ddf21ef..f314a95519 100644
--- a/ports/samd/samd_qspiflash.c
+++ b/ports/samd/samd_qspiflash.c
@@ -105,7 +105,6 @@ static const external_flash_device possible_devices[] = {
#define EXTERNAL_FLASH_DEVICE_COUNT MP_ARRAY_SIZE(possible_devices)
static external_flash_device const *flash_device;
-static external_flash_device generic_config = GENERIC;
extern const mp_obj_type_t samd_qspiflash_type;
// The QSPIflash object is a singleton
@@ -248,15 +247,6 @@ static uint8_t get_baud(int32_t freq_mhz) {
return baud;
}
-int get_sfdp_table(uint8_t *table, int maxlen) {
- uint8_t header[16];
- read_memory_single(QSPI_CMD_READ_SFDP_PARAMETER, 0, header, sizeof(header));
- int len = MIN(header[11] * 4, maxlen);
- int addr = header[12] + (header[13] << 8) + (header[14] << 16);
- read_memory_single(QSPI_CMD_READ_SFDP_PARAMETER, addr, table, len);
- return len;
-}
-
static mp_obj_t samd_qspiflash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
mp_arg_check_num(n_args, n_kw, 0, 0, false);
@@ -297,19 +287,6 @@ static mp_obj_t samd_qspiflash_make_new(const mp_obj_type_t *type, size_t n_args
uint8_t jedec_ids[3];
read_command(QSPI_CMD_READ_JEDEC_ID, jedec_ids, sizeof(jedec_ids));
- // Read the common sfdp table
- // Check the device addr length, support of 1-1-4 mode and get the sector size
- uint8_t sfdp_table[128];
- int len = get_sfdp_table(sfdp_table, sizeof(sfdp_table));
- if (len >= 29) {
- self->sectorsize = 1 << sfdp_table[28];
- bool addr4b = ((sfdp_table[2] >> 1) & 0x03) == 0x02;
- bool supports_qspi_114 = (sfdp_table[2] & 0x40) != 0;
- if (addr4b || !supports_qspi_114) {
- mp_raise_ValueError(MP_ERROR_TEXT("QSPI mode not supported"));
- }
- }
-
// Check, if the flash device is known and get it's properties.
flash_device = NULL;
for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) {
@@ -321,15 +298,8 @@ static mp_obj_t samd_qspiflash_make_new(const mp_obj_type_t *type, size_t n_args
break;
}
}
-
- // If the flash device is not known, try generic config options
if (flash_device == NULL) {
- if (jedec_ids[0] == 0xc2) { // Macronix devices
- generic_config.quad_enable_bit_mask = 0x04;
- generic_config.single_status_byte = true;
- }
- generic_config.total_size = 1 << jedec_ids[2];
- flash_device = &generic_config;
+ mp_raise_ValueError(MP_ERROR_TEXT("QSPI device not supported"));
}
self->size = flash_device->total_size;
diff --git a/ports/samd/samd_spiflash.c b/ports/samd/samd_spiflash.c
index 8ada7e9609..63dc0a8304 100644
--- a/ports/samd/samd_spiflash.c
+++ b/ports/samd/samd_spiflash.c
@@ -67,6 +67,19 @@ typedef struct _spiflash_obj_t {
extern const mp_obj_type_t samd_spiflash_type;
+typedef struct _spiflash_jedec_id_t {
+ uint32_t jedec_id;
+ uint32_t mask;
+ uint32_t size;
+} spiflash_jedec_id_t;
+
+spiflash_jedec_id_t jedec_id_table[] = {
+ { 0x1f8401, 0xffff00, 512 * 1024 }, // Adesto/Renesas 4 MBit
+ { 0x1f2400, 0xffff00, 512 * 1024 }, // Adesto 4 MBit
+ { 0x1f4501, 0xffff00, 1024 * 1024 }, // Adesto/Renesas/Atmel 8 MBit
+ { 0xc84013, 0xffffff, 512 * 1024 }, // Gigadevices 4 MBit
+};
+
// The SPIflash object is a singleton
static spiflash_obj_t spiflash_obj = {
{ &samd_spiflash_type }, NULL, 0, false, PAGE_SIZE, SECTOR_SIZE, NULL, 0
@@ -124,6 +137,7 @@ static void write_enable(spiflash_obj_t *self) {
mp_hal_pin_write(self->cs, 1);
}
+#if !defined(MICROPY_HW_SPIFLASH_SIZE)
// Write status register 1
static void write_sr1(spiflash_obj_t *self, uint8_t value) {
uint8_t msg[2];
@@ -134,6 +148,7 @@ static void write_sr1(spiflash_obj_t *self, uint8_t value) {
spi_transfer(self->spi, 2, msg, NULL);
mp_hal_pin_write(self->cs, 1);
}
+#endif
static void get_sfdp(spiflash_obj_t *self, uint32_t addr, uint8_t *buffer, int size) {
uint8_t dummy[1];
@@ -170,32 +185,46 @@ static mp_obj_t spiflash_make_new(const mp_obj_type_t *type, size_t n_args, size
// Get the flash size from the device ID (default)
uint8_t id[3];
get_id(self, id);
- bool read_sfdp = true;
-
- if (id[1] == 0x84 && id[2] == 1) { // Adesto
- self->size = 512 * 1024;
- } else if (id[0] == 0x1f && id[1] == 0x45 && id[2] == 1) { // Adesto/Renesas 8 MBit
- self->size = 1024 * 1024;
- read_sfdp = false;
- self->sectorsize = 4096;
- self->addr_is_32bit = false;
- // Globally unlock the sectors, which are locked after power on.
- write_enable(self);
- write_sr1(self, 0);
- } else {
- self->size = 1 << id[2];
+
+ #if defined(MICROPY_HW_SPIFLASH_SIZE)
+ self->size = MICROPY_HW_SPIFLASH_SIZE;
+ #else
+ // Assume as default that the size is coded into the last JEDEC ID byte.
+ self->size = 1 << id[2];
+ // Look for specific flash devices with different encoding.
+ uint32_t jedec_id = (id[0] << 16) | (id[1] << 8) | id[2];
+ for (int i = 0; i < MP_ARRAY_SIZE(jedec_id_table); i++) {
+ if (jedec_id_table[i].jedec_id == (jedec_id & jedec_id_table[i].mask)) {
+ self->size = jedec_id_table[i].size;
+ // Globally unlock the sectors, which may be locked after power on.
+ write_enable(self);
+ write_sr1(self, 0);
+ break;
+ }
}
+ #endif
- // Get the addr_is_32bit flag and the sector size
- if (read_sfdp) {
- uint8_t buffer[128];
- get_sfdp(self, 0, buffer, 16); // get the header
+ // Get the flash size, addr_is_32bit flag and sector size from SFDP, if present.
+ uint8_t buffer[128];
+ get_sfdp(self, 0, buffer, 16); // get the header
+ if (*(uint32_t *)buffer == 0x50444653) { // Header signature "SFDP"
int len = MIN(buffer[11] * 4, sizeof(buffer));
if (len >= 29) {
int addr = buffer[12] + (buffer[13] << 8) + (buffer[14] << 16);
get_sfdp(self, addr, buffer, len); // Get the JEDEC mandatory table
self->sectorsize = 1 << buffer[28];
self->addr_is_32bit = ((buffer[2] >> 1) & 0x03) != 0;
+ #if !defined(MICROPY_HW_SPIFLASH_SIZE)
+ // Get the bit size from the SFDP data
+ uint32_t size = *(uint32_t *)(buffer + 4);
+ if (size & 0x8000000) {
+ // Byte size is 2 ** lower_31_bits / 8
+ self->size = 1 << ((size & 0x7fffffff) >> 3);
+ } else {
+ // Byte size is lower_31_bits / 8 + 1
+ self->size = ((size & 0x7fffffff) >> 3) + 1;
+ }
+ #endif
}
}
self->commands = self->addr_is_32bit ? _COMMANDS_32BIT : _COMMANDS_24BIT;
diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile
index 8ac9a8af03..37d70dcdbf 100644
--- a/ports/stm32/Makefile
+++ b/ports/stm32/Makefile
@@ -60,6 +60,7 @@ include $(TOP)/extmod/extmod.mk
GIT_SUBMODULES += lib/libhydrogen lib/stm32lib
+CROSS_COMPILE ?= arm-none-eabi-
LD_DIR=boards
USBDEV_DIR=usbdev
#USBHOST_DIR=usbhost
@@ -101,9 +102,6 @@ GEN_STMCONST_HDR = $(HEADER_BUILD)/modstm_const.h
GEN_STMCONST_MPZ = $(HEADER_BUILD)/modstm_mpz.h
CMSIS_MCU_HDR = $(STM32LIB_CMSIS_ABS)/Include/$(CMSIS_MCU_LOWER).h
-# Select the cross compile prefix
-CROSS_COMPILE ?= arm-none-eabi-
-
INC += -I.
INC += -I$(TOP)
INC += -I$(BUILD)
@@ -123,7 +121,15 @@ CFLAGS += -DSTM32_HAL_H='<stm32$(MCU_SERIES)xx_hal.h>'
CFLAGS += -DMBOOT_VTOR=$(MBOOT_TEXT0_ADDR)
CFLAGS += -DMICROPY_HW_VTOR=$(TEXT0_ADDR)
+ifeq ($(MCU_SERIES),n6)
+ifeq ($(USE_MBOOT),1)
+CFLAGS += -DMICROPY_HW_RUNS_FROM_EXT_FLASH=1
+endif
+# as doesn't recognise -mcpu=cortex-m55
+AFLAGS += -march=armv8.1-m.main
+else
AFLAGS += $(filter -mcpu=%,$(CFLAGS_MCU_$(MCU_SERIES)))
+endif
# Configure for nan-boxing object model if requested
ifeq ($(NANBOX),1)
@@ -300,6 +306,7 @@ SRC_C += \
adc.c \
sdio.c \
subghz.c \
+ xspi.c \
$(wildcard $(BOARD_DIR)/*.c)
SRC_O += \
@@ -316,6 +323,13 @@ CFLAGS += -DUSE_HAL_DRIVER
SRC_O += \
resethandler_m3.o \
shared/runtime/gchelper_thumb2.o
+else ifeq ($(MCU_SERIES),n6)
+SRC_O += shared/runtime/gchelper_thumb2.o
+ifeq ($(USE_MBOOT),1)
+SRC_O += resethandler_iram.o
+else
+SRC_O += resethandler.o
+endif
else
SRC_O += \
system_stm32.o \
@@ -329,8 +343,6 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal_adc_ex.c \
hal_cortex.c \
hal_dma.c \
- hal_flash.c \
- hal_flash_ex.c \
hal_gpio.c \
hal_i2c.c \
hal_pwr.c \
@@ -347,7 +359,14 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
ll_utils.c \
)
-ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g0 g4 h5 h7 l0 l1 l4 wb))
+ifneq ($(MCU_SERIES),n6)
+HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
+ hal_flash.c \
+ hal_flash_ex.c \
+ )
+endif
+
+ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g0 g4 h5 h7 l0 l1 l4 n6 wb))
HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal_pcd.c \
hal_pcd_ex.c \
@@ -355,7 +374,15 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
)
endif
-ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h5 h7 l4))
+ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),n6))
+HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
+ hal_bsec.c \
+ hal_rif.c \
+ hal_xspi.c \
+ )
+endif
+
+ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h5 h7 l4 n6))
HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal_sd.c \
ll_sdmmc.c \
@@ -380,7 +407,7 @@ $(BUILD)/$(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_hal_mmc.o: CFLAGS += -Wno
endif
endif
-ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g0 g4 h5 h7))
+ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g0 g4 h5 h7 n6))
HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal_dma_ex.c \
)
@@ -417,6 +444,12 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
)
endif
+ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),h7))
+HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
+ hal_uart_ex.c \
+ )
+endif
+
USBDEV_SRC_C += $(addprefix $(USBDEV_DIR)/,\
core/src/usbd_core.c \
core/src/usbd_ctlreq.c \
@@ -490,6 +523,12 @@ all: $(TOP)/lib/stm32lib/README.md all_main $(BUILD)/firmware.hex
ifeq ($(MBOOT_ENABLE_PACKING),1)
all_main: $(BUILD)/firmware.pack.dfu
+else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),n6))
+ifeq ($(USE_MBOOT),1)
+all_main: $(BUILD)/firmware.dfu
+else
+all_main: $(BUILD)/firmware-trusted.bin
+endif
else
all_main: $(BUILD)/firmware.dfu
endif
@@ -550,7 +589,7 @@ define GENERATE_HEX
$(Q)$(OBJCOPY) -O ihex $(2) $(1)
endef
-.PHONY: deploy deploy-stlink deploy-openocd
+.PHONY: deploy deploy-stlink deploy-openocd deploy-trusted
ifeq ($(MBOOT_ENABLE_PACKING),1)
deploy: $(BUILD)/firmware.pack.dfu
@@ -560,6 +599,9 @@ deploy: $(BUILD)/firmware.dfu
$(call RUN_DFU,$^)
endif
+deploy-trusted: $(BUILD)/firmware-trusted.bin
+ $(STM32_CUBE_PROGRAMMER)/bin/STM32_Programmer.sh -c port=SWD mode=HOTPLUG ap=1 -el $(DKEL) -w $^ 0x70000000 -hardRst
+
# A board should specify TEXT0_ADDR if to use a different location than the
# default for the firmware memory location. A board can also optionally define
# TEXT1_ADDR to split the firmware into two sections; see below for details.
@@ -614,6 +656,10 @@ $(BUILD)/firmware.hex: $(BUILD)/firmware.elf
$(BUILD)/firmware.elf: $(OBJ)
$(call GENERATE_ELF,$@,$^)
+$(BUILD)/firmware-trusted.bin: $(BUILD)/firmware.bin
+ /bin/rm -f $@
+ $(STM32_CUBE_PROGRAMMER)/bin/STM32_SigningTool_CLI -bin $^ -nk -of 0x80000000 -t fsbl -o $@ -hv $(STM32_N6_HEADER_VERSION)
+
# List of sources for qstr extraction
SRC_QSTR += $(SRC_C) $(SRC_CXX) $(SHARED_SRC_C) $(GEN_PINS_SRC)
diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c
index 3549fc29a9..f47e9eaad7 100644
--- a/ports/stm32/adc.c
+++ b/ports/stm32/adc.c
@@ -51,7 +51,7 @@
/// val = adc.read_core_vref() # read MCU VREF
/* ADC definitions */
-#if defined(STM32H5)
+#if defined(STM32H5) || defined(STM32N6)
// STM32H5 features two ADC instances, ADCx and pin_adc_table are set dynamically
#define PIN_ADC_MASK (PIN_ADC1 | PIN_ADC2)
#else
@@ -107,12 +107,12 @@
#define ADC_CAL2 ((uint16_t *)(ADC_CAL_ADDRESS + 4))
#define ADC_CAL_BITS (12)
-#elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5)
+#elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L1) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
#define ADC_SCALE_V (((float)VREFINT_CAL_VREF) / 1000.0f)
-#define ADC_CAL_ADDRESS VREFINT_CAL_ADDR
-#define ADC_CAL1 TEMPSENSOR_CAL1_ADDR
-#define ADC_CAL2 TEMPSENSOR_CAL2_ADDR
+#define ADC_CAL_ADDRESS (VREFINT_CAL_ADDR)
+#define ADC_CAL1 (TEMPSENSOR_CAL1_ADDR)
+#define ADC_CAL2 (TEMPSENSOR_CAL2_ADDR)
#define ADC_CAL_BITS (12) // UM2319/UM2570, __HAL_ADC_CALC_TEMPERATURE: 'corresponds to a resolution of 12 bits'
#elif defined(STM32H7)
@@ -123,22 +123,6 @@
#define ADC_CAL2 ((uint16_t *)(0x1FF1E840))
#define ADC_CAL_BITS (16)
-#elif defined(STM32L1)
-
-#define ADC_SCALE_V (VREFINT_CAL_VREF / 1000.0f)
-#define ADC_CAL_ADDRESS (VREFINT_CAL_ADDR)
-#define ADC_CAL1 (TEMPSENSOR_CAL1_ADDR)
-#define ADC_CAL2 (TEMPSENSOR_CAL2_ADDR)
-#define ADC_CAL_BITS (12)
-
-#elif defined(STM32L4) || defined(STM32WB)
-
-#define ADC_SCALE_V (VREFINT_CAL_VREF / 1000.0f)
-#define ADC_CAL_ADDRESS (VREFINT_CAL_ADDR)
-#define ADC_CAL1 (TEMPSENSOR_CAL1_ADDR)
-#define ADC_CAL2 (TEMPSENSOR_CAL2_ADDR)
-#define ADC_CAL_BITS (12)
-
#else
#error Unsupported processor
@@ -182,6 +166,9 @@
#define VBAT_DIV (3)
#elif defined(STM32L152xE)
// STM32L152xE does not have vbat.
+#elif defined(STM32N6)
+// ADC2 VINP 16
+#define VBAT_DIV (4)
#else
#error Unsupported processor
#endif
@@ -263,7 +250,7 @@ static bool is_adcx_channel(int channel) {
handle.Instance = ADCx;
return __HAL_ADC_IS_CHANNEL_INTERNAL(channel)
|| IS_ADC_CHANNEL(&handle, __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel));
- #elif defined(STM32H5)
+ #elif defined(STM32H5) || defined(STM32N6)
// The first argument to the IS_ADC_CHANNEL macro is unused.
return __HAL_ADC_IS_CHANNEL_INTERNAL(channel)
|| IS_ADC_CHANNEL(NULL, __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel));
@@ -276,7 +263,7 @@ static void adc_wait_for_eoc_or_timeout(ADC_HandleTypeDef *adcHandle, int32_t ti
uint32_t tickstart = HAL_GetTick();
#if defined(STM32F4) || defined(STM32F7) || defined(STM32L1)
while ((adcHandle->Instance->SR & ADC_FLAG_EOC) != ADC_FLAG_EOC) {
- #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)
+ #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
while (READ_BIT(adcHandle->Instance->ISR, ADC_FLAG_EOC) != ADC_FLAG_EOC) {
#else
#error Unsupported processor
@@ -295,7 +282,7 @@ static void adcx_clock_enable(ADC_HandleTypeDef *adch) {
__HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_CLKP);
#elif defined(STM32G0)
__HAL_RCC_ADC_CLK_ENABLE();
- #elif defined(STM32G4)
+ #elif defined(STM32G4) || defined(STM32N6)
__HAL_RCC_ADC12_CLK_ENABLE();
#elif defined(STM32H5)
__HAL_RCC_ADC_CLK_ENABLE();
@@ -368,6 +355,15 @@ static void adcx_init_periph(ADC_HandleTypeDef *adch, uint32_t resolution) {
adch->Init.OversamplingMode = DISABLE;
adch->Init.DataAlign = ADC_DATAALIGN_RIGHT;
adch->Init.DMAContinuousRequests = DISABLE;
+ #elif defined(STM32N6)
+ adch->Init.GainCompensation = 0;
+ adch->Init.ScanConvMode = ADC_SCAN_DISABLE;
+ adch->Init.LowPowerAutoWait = DISABLE;
+ adch->Init.SamplingMode = ADC_SAMPLING_MODE_NORMAL;
+ adch->Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
+ adch->Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
+ adch->Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
+ adch->Init.OversamplingMode = DISABLE;
#else
#error Unsupported processor
#endif
@@ -400,7 +396,7 @@ static void adc_init_single(pyb_obj_adc_t *adc_obj, ADC_TypeDef *adc) {
static void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) {
ADC_ChannelConfTypeDef sConfig;
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
sConfig.Rank = ADC_REGULAR_RANK_1;
if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel) == 0) {
channel = __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel);
@@ -432,7 +428,7 @@ static void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel)
if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) {
sConfig.SamplingTime = ADC_SAMPLETIME_384CYCLES;
} else {
- sConfig.SamplingTime = ADC_SAMPLETIME_384CYCLES;
+ sConfig.SamplingTime = ADC_SAMPLETIME_16CYCLES;
}
#elif defined(STM32G0)
if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) {
@@ -449,6 +445,18 @@ static void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel)
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
+ #elif defined(STM32N6)
+ if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) {
+ sConfig.SamplingTime = ADC_SAMPLETIME_246CYCLES_5;
+ } else {
+ sConfig.SamplingTime = ADC_SAMPLETIME_11CYCLES_5;
+ }
+ sConfig.SingleDiff = ADC_SINGLE_ENDED;
+ sConfig.OffsetNumber = ADC_OFFSET_NONE;
+ sConfig.Offset = 0;
+ sConfig.OffsetSignedSaturation = DISABLE;
+ sConfig.OffsetSaturation = DISABLE;
+ sConfig.OffsetSign = ADC_OFFSET_SIGN_POSITIVE;
#else
#error Unsupported processor
#endif
@@ -464,10 +472,13 @@ static void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel)
static uint32_t adc_read_channel(ADC_HandleTypeDef *adcHandle) {
uint32_t value;
- #if defined(STM32G4)
- // For STM32G4 there is errata 2.7.7, "Wrong ADC result if conversion done late after
- // calibration or previous conversion". According to the errata, this can be avoided
- // by performing two consecutive ADC conversions and keeping the second result.
+ #if defined(STM32G4) || defined(STM32WB)
+ // For STM32G4 errata 2.7.7 / STM32WB errata 2.7.1:
+ // "Wrong ADC result if conversion done late after calibration or previous conversion"
+ // states an incorrect reading is returned if more than 1ms has elapsed since the last
+ // reading or calibration. According to the errata, this can be avoided by performing
+ // two consecutive ADC conversions and keeping the second result.
+ // Note: On STM32WB55 @ 64Mhz each ADC read takes ~ 3us.
for (uint8_t i = 0; i < 2; i++)
#endif
{
@@ -523,7 +534,7 @@ static mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
// 1st argument is the pin name
mp_obj_t pin_obj = args[0];
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
// STM32H5 has two ADC instances where some pins are only available on ADC1 or ADC2 (but not both).
// Assume we're using a channel of ADC1. Can be overridden for ADC2 later in this function.
ADC_TypeDef *adc = ADC1;
@@ -540,7 +551,7 @@ static mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
// No ADC function on the given pin.
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Pin(%q) doesn't have ADC capabilities"), pin->name);
}
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
if ((pin->adc_num & PIN_ADC2) == PIN_ADC2) {
adc = ADC2;
pin_adc_table = pin_adc2;
@@ -555,7 +566,7 @@ static mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
}
// If this channel corresponds to a pin then configure the pin in ADC mode.
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
if (channel < num_adc_pins) {
const machine_pin_obj_t *pin = pin_adc_table[channel];
if (pin != NULL) {
@@ -576,7 +587,7 @@ static mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
o->base.type = &pyb_adc_type;
o->pin_name = pin_obj;
o->channel = channel;
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
adc_init_single(o, adc);
#else
adc_init_single(o, ADCx);
@@ -667,7 +678,7 @@ static mp_obj_t adc_read_timed(mp_obj_t self_in, mp_obj_t buf_in, mp_obj_t freq_
// for subsequent samples we can just set the "start sample" bit
#if defined(STM32F4) || defined(STM32F7) || defined(STM32L1)
self->handle.Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART;
- #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)
+ #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
SET_BIT(self->handle.Instance->CR, ADC_CR_ADSTART);
#else
#error Unsupported processor
@@ -777,7 +788,7 @@ static mp_obj_t adc_read_timed_multi(mp_obj_t adc_array_in, mp_obj_t buf_array_i
// ADC is started: set the "start sample" bit
#if defined(STM32F4) || defined(STM32F7) || defined(STM32L1)
adc->handle.Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART;
- #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)
+ #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
SET_BIT(adc->handle.Instance->CR, ADC_CR_ADSTART);
#else
#error Unsupported processor
@@ -911,6 +922,8 @@ int adc_read_core_temp(ADC_HandleTypeDef *adcHandle) {
} else {
return 0;
}
+ #elif defined(STM32N6)
+ int32_t raw_value = 0; // TODO
#else
int32_t raw_value = adc_config_and_read_ref(adcHandle, ADC_CHANNEL_TEMPSENSOR);
#endif
@@ -922,6 +935,10 @@ int adc_read_core_temp(ADC_HandleTypeDef *adcHandle) {
static volatile float adc_refcor = 1.0f;
float adc_read_core_temp_float(ADC_HandleTypeDef *adcHandle) {
+ #if defined(STM32N6)
+ return 0.0f; // TODO
+ #else
+
#if defined(STM32G4) || defined(STM32L1) || defined(STM32L4)
// Update the reference correction factor before reading tempsensor
// because TS_CAL1 and TS_CAL2 of STM32G4,L1/L4 are at VDDA=3.0V
@@ -936,7 +953,7 @@ float adc_read_core_temp_float(ADC_HandleTypeDef *adcHandle) {
return 0;
}
float core_temp_avg_slope = (*ADC_CAL2 - *ADC_CAL1) / 100.0f;
- #elif defined(STM32H5)
+ #elif defined(STM32H5) || defined(STM32WB)
int32_t raw_value = adc_config_and_read_ref(adcHandle, ADC_CHANNEL_TEMPSENSOR);
float core_temp_avg_slope = (*ADC_CAL2 - *ADC_CAL1) / 100.0f;
#else
@@ -944,6 +961,8 @@ float adc_read_core_temp_float(ADC_HandleTypeDef *adcHandle) {
float core_temp_avg_slope = (*ADC_CAL2 - *ADC_CAL1) / 80.0f;
#endif
return (((float)raw_value * adc_refcor - *ADC_CAL1) / core_temp_avg_slope) + 30.0f;
+
+ #endif
}
float adc_read_core_vbat(ADC_HandleTypeDef *adcHandle) {
diff --git a/ports/stm32/adc.h b/ports/stm32/adc.h
index 0518cdcd9a..0adc9ac2be 100644
--- a/ports/stm32/adc.h
+++ b/ports/stm32/adc.h
@@ -48,7 +48,7 @@ static inline void adc_deselect_vbat(ADC_TypeDef *adc, uint32_t channel) {
adc_common = ADC_COMMON_REGISTER(0);
#elif defined(STM32F7)
adc_common = ADC123_COMMON;
- #elif defined(STM32G4) || defined(STM32H5)
+ #elif defined(STM32G4) || defined(STM32H5) || defined(STM32N6)
adc_common = ADC12_COMMON;
#elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ)
adc_common = ADC12_COMMON;
diff --git a/ports/stm32/boardctrl.c b/ports/stm32/boardctrl.c
index 8f0d066f37..ea95c8d2d5 100644
--- a/ports/stm32/boardctrl.c
+++ b/ports/stm32/boardctrl.c
@@ -35,7 +35,7 @@
#include "led.h"
#include "usrsw.h"
-NORETURN void boardctrl_fatal_error(const char *msg) {
+MP_NORETURN void boardctrl_fatal_error(const char *msg) {
for (volatile uint delay = 0; delay < 10000000; delay++) {
}
led_state(1, 1);
diff --git a/ports/stm32/boardctrl.h b/ports/stm32/boardctrl.h
index 8f4ce30eff..cb5380c298 100644
--- a/ports/stm32/boardctrl.h
+++ b/ports/stm32/boardctrl.h
@@ -114,7 +114,7 @@ typedef struct _boardctrl_state_t {
bool log_soft_reset;
} boardctrl_state_t;
-NORETURN void boardctrl_fatal_error(const char *msg);
+MP_NORETURN void boardctrl_fatal_error(const char *msg);
void boardctrl_maybe_enter_mboot(size_t n_args, const void *args);
void boardctrl_before_soft_reset_loop(boardctrl_state_t *state);
void boardctrl_top_soft_reset_loop(boardctrl_state_t *state);
@@ -122,5 +122,6 @@ int boardctrl_run_boot_py(boardctrl_state_t *state);
int boardctrl_run_main_py(boardctrl_state_t *state);
void boardctrl_start_soft_reset(boardctrl_state_t *state);
void boardctrl_end_soft_reset(boardctrl_state_t *state);
+void boardctrl_enter_standby(void);
#endif // MICROPY_INCLUDED_STM32_BOARDCTRL_H
diff --git a/ports/stm32/boards/ARDUINO_GIGA/board.json b/ports/stm32/boards/ARDUINO_GIGA/board.json
index 53c636c774..a99e747017 100644
--- a/ports/stm32/boards/ARDUINO_GIGA/board.json
+++ b/ports/stm32/boards/ARDUINO_GIGA/board.json
@@ -15,7 +15,7 @@
"ABX00063_01.front_1000x750.jpg"
],
"mcu": "stm32h7",
- "product": "Arduino Giga",
+ "product": "Giga",
"thumbnail": "",
"url": "https://store.arduino.cc/products/giga-r1-wifi",
"vendor": "Arduino"
diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h
index cef45d730c..44f6ce66bc 100644
--- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h
+++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h
@@ -31,6 +31,7 @@ typedef unsigned int mp_uint_t; // must be pointer size
#define MICROPY_HW_ENABLE_MMCARD (0)
#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0)
#define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1)
+#define MICROPY_GC_SPLIT_HEAP (1)
// ROMFS config
#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1)
diff --git a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld
index e7bb950dbe..dceb1a7489 100644
--- a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld
+++ b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld
@@ -12,6 +12,7 @@ MEMORY
SRAM2 (xrw) : ORIGIN = 0x30020000, LENGTH = 128K /* SRAM2 D2 */
SRAM3 (xrw) : ORIGIN = 0x30040000, LENGTH = 32K /* SRAM3 D2 */
SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */
+ SDRAM (xrw) : ORIGIN = 0x60000000, LENGTH = 8M /* SDRAM */
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */
FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */
FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */
@@ -53,3 +54,27 @@ _openamp_shm_region_start = ORIGIN(SRAM4);
_openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4);
INCLUDE common_blifs.ld
+
+SECTIONS
+{
+ /* GC blocks addresses and sizes */
+ .gc.blocks.table (READONLY) : {
+ . = ALIGN(4);
+ _gc_blocks_table_start = .;
+
+ LONG (ORIGIN(SRAM1));
+ LONG (128K);
+
+ LONG (ORIGIN(SDRAM) + 0M);
+ LONG (2M);
+
+ LONG (ORIGIN(SDRAM) + 2M);
+ LONG (2M);
+
+ LONG (ORIGIN(SDRAM) + 4M);
+ LONG (4M);
+
+ _gc_blocks_table_end = .;
+ . = ALIGN(4);
+ } > FLASH_TEXT
+}
diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json b/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json
index a4c81d69d4..129d9f2fc7 100644
--- a/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json
+++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json
@@ -15,7 +15,7 @@
"ABX00051_01.iso_1000x750.jpg"
],
"mcu": "stm32h7",
- "product": "Arduino Nicla Vision",
+ "product": "Nicla Vision",
"thumbnail": "",
"url": "https://store.arduino.cc/products/nicla-vision",
"vendor": "Arduino"
diff --git a/ports/stm32/boards/ARDUINO_OPTA/board.json b/ports/stm32/boards/ARDUINO_OPTA/board.json
index 3955d7df31..dd6c39a191 100644
--- a/ports/stm32/boards/ARDUINO_OPTA/board.json
+++ b/ports/stm32/boards/ARDUINO_OPTA/board.json
@@ -14,7 +14,7 @@
"AFX00002_01.iso_1000x750.jpg"
],
"mcu": "stm32h7",
- "product": "Arduino Opta WiFi",
+ "product": "Opta WiFi",
"thumbnail": "",
"url": "https://store.arduino.cc/products/opta-wifi",
"vendor": "Arduino"
diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json b/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json
index f39d7d4c47..c97b9f165c 100644
--- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json
+++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json
@@ -16,7 +16,7 @@
"ABX00042_01.iso_1000x750.jpg"
],
"mcu": "stm32h7",
- "product": "Arduino Portenta H7",
+ "product": "Portenta H7",
"thumbnail": "",
"url": "https://store.arduino.cc/products/portenta-h7",
"vendor": "Arduino"
diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h
index 90a1469056..a9ecf38fbf 100644
--- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h
+++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h
@@ -30,7 +30,6 @@ typedef unsigned int mp_uint_t; // must be pointer size
#define MICROPY_HW_ENABLE_SDCARD (1)
#define MICROPY_HW_ENABLE_MMCARD (0)
#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0)
-#define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1)
// ROMFS config
#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1)
diff --git a/ports/stm32/boards/HYDRABUS/board.json b/ports/stm32/boards/HYDRABUS/board.json
index 20a2ac5eae..24df6ac99f 100644
--- a/ports/stm32/boards/HYDRABUS/board.json
+++ b/ports/stm32/boards/HYDRABUS/board.json
@@ -10,6 +10,6 @@
"mcu": "stm32f4",
"product": "HydraBus v1.0",
"thumbnail": "",
- "url": "",
+ "url": "https://github.com/hydrabus/hydrabus",
"vendor": "HydraBus"
}
diff --git a/ports/stm32/boards/LIMIFROG/board.json b/ports/stm32/boards/LIMIFROG/board.json
index f516963a5b..36c80f8f72 100644
--- a/ports/stm32/boards/LIMIFROG/board.json
+++ b/ports/stm32/boards/LIMIFROG/board.json
@@ -10,6 +10,6 @@
"mcu": "stm32l4",
"product": "LimiFrog",
"thumbnail": "",
- "url": "",
+ "url": "https://github.com/LimiFrog",
"vendor": "LimiFrog"
}
diff --git a/ports/stm32/boards/NUCLEO_N657X0/bdev.c b/ports/stm32/boards/NUCLEO_N657X0/bdev.c
new file mode 100644
index 0000000000..2180d46174
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/bdev.c
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2025 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 "py/obj.h"
+#include "storage.h"
+#include "xspi.h"
+
+#if MICROPY_HW_SPIFLASH_ENABLE_CACHE
+#error "Cannot enable MICROPY_HW_SPIFLASH_ENABLE_CACHE"
+#endif
+
+// External SPI flash uses hardware XSPI interface.
+const mp_spiflash_config_t spiflash_config = {
+ .bus_kind = MP_SPIFLASH_BUS_QSPI,
+ .bus.u_qspi.data = (void *)&xspi_flash2,
+ .bus.u_qspi.proto = &xspi_proto,
+};
+
+spi_bdev_t spi_bdev;
diff --git a/ports/stm32/boards/NUCLEO_N657X0/board.c b/ports/stm32/boards/NUCLEO_N657X0/board.c
new file mode 100644
index 0000000000..fe5f2f1cc8
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/board.c
@@ -0,0 +1,122 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024-2025 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 "py/mphal.h"
+#include "boardctrl.h"
+#include "xspi.h"
+
+// Values for OTP fuses for VDDIO3, to select low voltage mode (<2.5V).
+// See RM0486, Section 5, Table 18.
+#define BSEC_HW_CONFIG_ID (124U)
+#define BSEC_HWS_HSLV_VDDIO3 (1U << 15)
+
+static void board_config_vdd(void) {
+ // TODO: move some of the below code to a common location for all N6 boards?
+
+ // Enable PWR, BSEC and SYSCFG clocks.
+ LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG);
+
+ // Program high speed IO optimization fuses if they aren't already set.
+ uint32_t fuse;
+ BSEC_HandleTypeDef hbsec = { .Instance = BSEC };
+ const uint32_t mask = BSEC_HWS_HSLV_VDDIO3;
+ if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) {
+ fuse = 0;
+ } else if ((fuse & mask) != mask) {
+ // Program the fuse, and read back the set value.
+ if (HAL_BSEC_OTP_Program(&hbsec, BSEC_HW_CONFIG_ID, fuse | mask, HAL_BSEC_NORMAL_PROG) != HAL_OK) {
+ fuse = 0;
+ } else if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) {
+ fuse = 0;
+ }
+ }
+
+ // Enable Vdd ADC, needed for the ADC to work.
+ LL_PWR_EnableVddADC();
+
+ // Configure VDDIO2.
+ LL_PWR_EnableVddIO2();
+ LL_PWR_SetVddIO2VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3);
+ SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO3. Only enable 1.8V mode if the fuse is set.
+ LL_PWR_EnableVddIO3();
+ if (fuse & BSEC_HWS_HSLV_VDDIO3) {
+ LL_PWR_SetVddIO3VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_1V8);
+ }
+ SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO4.
+ LL_PWR_EnableVddIO4();
+ LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3);
+ SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation
+
+ // Enable VDD for ADC and USB.
+ LL_PWR_EnableVddADC();
+ LL_PWR_EnableVddUSB();
+}
+
+void mboot_board_early_init(void) {
+ board_config_vdd();
+ xspi_init();
+}
+
+void board_early_init(void) {
+ #if !MICROPY_HW_RUNS_FROM_EXT_FLASH
+ // Firmware runs directly from SRAM, so configure VDD and enable XSPI flash.
+ board_config_vdd();
+ xspi_init();
+ #endif
+}
+
+void board_leave_standby(void) {
+ // TODO: move some of the below code to a common location for all N6 boards?
+
+ // Enable PWR, BSEC and SYSCFG clocks.
+ LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG);
+
+ // Configure VDDIO2.
+ LL_PWR_EnableVddIO2();
+ LL_PWR_SetVddIO2VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3);
+ SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO3 (1.8V mode selection is retained).
+ LL_PWR_EnableVddIO3();
+ SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO4.
+ LL_PWR_EnableVddIO4();
+ LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3);
+ SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation
+
+ // Enable VDD for ADC and USB.
+ LL_PWR_EnableVddADC();
+ LL_PWR_EnableVddUSB();
+}
diff --git a/ports/stm32/boards/NUCLEO_N657X0/board.md b/ports/stm32/boards/NUCLEO_N657X0/board.md
new file mode 100644
index 0000000000..3360c5db6c
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/board.md
@@ -0,0 +1,17 @@
+The mboot bootloader must first be built and deployed to this board. Make sure that
+CN9 is in position 1-2 to select STLK as the 5V power source, that JP1 is in position
+1-2 (lower position) and JP2 is in position 2-3 (upper position). Then plug in a USB
+cable into the ST-LINK port CN10. This will allow mboot firmware to be programmed to
+the external SPI flash via ST's tools, eg:
+
+ make -C ports/stm32/mboot BOARD=NUCLEO_N657X0 deploy-trusted
+
+Once mboot is installed, change CN9 to position 3-4 to select USB as the 5V power
+source, change JP2 back to position 1-2 (lower position) and change the USB cable to
+CN8. mboot will present a USB DFU device on this USB port, and the red LED2 should be
+blinking at 1Hz to indicate that mboot is active. If it's not active then hold the
+USER button and press NRST, and wait until all three LEDs are on, then release USER.
+Now mboot will be active.
+
+Once the USB DFU port can be seen, the firmware below can be programmed as usual with
+any DFU loader.
diff --git a/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h
new file mode 100644
index 0000000000..ccc3fa051f
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h
@@ -0,0 +1,103 @@
+#define MICROPY_HW_BOARD_NAME "NUCLEO-N657X0"
+#define MICROPY_HW_MCU_NAME "STM32N657X0"
+
+#define MICROPY_GC_STACK_ENTRY_TYPE uint32_t
+#define MICROPY_ALLOC_GC_STACK_SIZE (128)
+#define MICROPY_FATFS_EXFAT (1)
+
+#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0)
+#define MICROPY_HW_HAS_SWITCH (1)
+#define MICROPY_HW_HAS_FLASH (1)
+#define MICROPY_HW_ENABLE_RNG (1)
+#define MICROPY_HW_ENABLE_RTC (1)
+#define MICROPY_HW_ENABLE_DAC (0)
+#define MICROPY_HW_ENABLE_USB (1)
+#define MICROPY_PY_PYB_LEGACY (0)
+
+#define MICROPY_BOARD_EARLY_INIT board_early_init
+#define MICROPY_BOARD_LEAVE_STANDBY board_leave_standby()
+
+// HSE is 48MHz, this gives a CPU frequency of 800MHz.
+#define MICROPY_HW_CLK_PLLM (6)
+#define MICROPY_HW_CLK_PLLN (100)
+#define MICROPY_HW_CLK_PLLP1 (1)
+#define MICROPY_HW_CLK_PLLP2 (1)
+#define MICROPY_HW_CLK_PLLFRAC (0)
+
+// The LSE is a 32kHz crystal.
+#define MICROPY_HW_RTC_USE_LSE (1)
+#define MICROPY_HW_RTC_USE_US (1)
+
+// External SPI flash, MX25UM51245GXDI00.
+#define MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2 (29)
+
+// SPI flash, block device config.
+#define MICROPY_HW_BDEV_SPIFLASH (&spi_bdev)
+#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev)
+#define MICROPY_HW_BDEV_SPIFLASH_CONFIG (&spiflash_config)
+#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES (4 * 1024 * 1024)
+#define MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES (60 * 1024 * 1024)
+
+// UART buses
+#define MICROPY_HW_UART1_TX (pyb_pin_UART1_TX)
+#define MICROPY_HW_UART1_RX (pyb_pin_UART1_RX)
+#define MICROPY_HW_UART3_TX (pyb_pin_UART3_TX)
+#define MICROPY_HW_UART3_RX (pyb_pin_UART3_RX)
+#define MICROPY_HW_UART_REPL (PYB_UART_1)
+#define MICROPY_HW_UART_REPL_BAUD (115200)
+
+// I2C buses
+#define MICROPY_HW_I2C1_SCL (pyb_pin_I2C1_SCL)
+#define MICROPY_HW_I2C1_SDA (pyb_pin_I2C1_SDA)
+
+// SPI buses
+#define MICROPY_HW_SPI5_NSS (pyb_pin_SPI5_CS)
+#define MICROPY_HW_SPI5_SCK (pyb_pin_SPI5_SCK)
+#define MICROPY_HW_SPI5_MISO (pyb_pin_SPI5_MISO)
+#define MICROPY_HW_SPI5_MOSI (pyb_pin_SPI5_MOSI)
+
+// USER2 is floating, and pressing the button makes the input go high.
+#define MICROPY_HW_USRSW_PIN (pyb_pin_BUTTON)
+#define MICROPY_HW_USRSW_PULL (GPIO_PULLDOWN)
+#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_RISING)
+#define MICROPY_HW_USRSW_PRESSED (1)
+
+// LEDs
+#define MICROPY_HW_LED1 (pyb_pin_LED_RED)
+#define MICROPY_HW_LED2 (pyb_pin_LED_GREEN)
+#define MICROPY_HW_LED3 (pyb_pin_LED_BLUE)
+#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin))
+#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin))
+
+// USB config
+#define MICROPY_HW_USB_HS (1)
+#define MICROPY_HW_USB_HS_IN_FS (1)
+#define MICROPY_HW_USB_MAIN_DEV (USB_PHY_HS_ID)
+
+/******************************************************************************/
+// Bootloader configuration
+
+#define MBOOT_BOARD_EARLY_INIT(initial_r0) mboot_board_early_init()
+
+#define MBOOT_SPIFLASH_CS (pyb_pin_XSPIM_P2_CS)
+#define MBOOT_SPIFLASH_SCK (pyb_pin_XSPIM_P2_SCK)
+#define MBOOT_SPIFLASH_MOSI (pyb_pin_XSPIM_P2_IO0)
+#define MBOOT_SPIFLASH_MISO (pyb_pin_XSPIM_P2_IO1)
+#define MBOOT_SPIFLASH_ADDR (0x70000000)
+#define MBOOT_SPIFLASH_BYTE_SIZE (64 * 1024 * 1024)
+#define MBOOT_SPIFLASH_LAYOUT "/0x70000000/16384*4Kg"
+#define MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE (1)
+#define MBOOT_SPIFLASH_SPIFLASH (&spi_bdev.spiflash)
+#define MBOOT_SPIFLASH_CONFIG (&spiflash_config)
+
+/******************************************************************************/
+// Function and variable declarations
+
+extern const struct _mp_spiflash_config_t spiflash_config;
+extern struct _spi_bdev_t spi_bdev;
+
+void mboot_board_early_init(void);
+void mboot_board_entry_init(void);
+
+void board_early_init(void);
+void board_leave_standby(void);
diff --git a/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk
new file mode 100644
index 0000000000..fa64cb1706
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk
@@ -0,0 +1,26 @@
+# Without mboot, the main firmware must fit in 512k flash, will be copied to SRAM by
+# the hardware bootloader, and will run from SRAM. With mboot, the main firmware can
+# be much larger and will run from flash via XSPI in memory-mapped mode.
+USE_MBOOT ?= 1
+
+MCU_SERIES = n6
+CMSIS_MCU = STM32N657xx
+AF_FILE = boards/stm32n657_af.csv
+ifeq ($(BUILDING_MBOOT),1)
+SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_fsbl.o
+else
+SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_s.o
+endif
+STM32_N6_HEADER_VERSION = 2.1
+DKEL = $(STM32_CUBE_PROGRAMMER)/bin/ExternalLoader/MX25UM51245G_STM32N6570-NUCLEO.stldr
+
+ifeq ($(USE_MBOOT),1)
+LD_FILES = boards/stm32n657x0.ld boards/common_n6_flash.ld
+TEXT0_ADDR = 0x70080000
+else
+LD_FILES = boards/stm32n657x0.ld boards/common_basic.ld
+TEXT0_ADDR = 0x34180400
+endif
+
+# MicroPython settings
+MICROPY_FLOAT_IMPL = double
diff --git a/ports/stm32/boards/NUCLEO_N657X0/partition_stm32n657xx.h b/ports/stm32/boards/NUCLEO_N657X0/partition_stm32n657xx.h
new file mode 100644
index 0000000000..ac38dac748
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/partition_stm32n657xx.h
@@ -0,0 +1,5 @@
+// This board does not use any security settings, so can just stay in secure
+// mode without configuring the SAU.
+
+static inline void TZ_SAU_Setup(void) {
+}
diff --git a/ports/stm32/boards/NUCLEO_N657X0/pins.csv b/ports/stm32/boards/NUCLEO_N657X0/pins.csv
new file mode 100644
index 0000000000..033f0a552e
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/pins.csv
@@ -0,0 +1,62 @@
+D0,PD9
+D1,PD8
+D2,PD0
+D3,PE9
+D4,PE0
+D5,PE10
+D6,PD5
+D7,PE11
+D8,PD12
+D9,PD7
+D10,PA3
+D11,PG2
+D12,PG1
+D13,PE15
+D14,PC1
+D15,PH9
+
+# Ax header pins are connected directly to the following digital IO
+A0D,PF5
+A1D,PC10
+A2D,PF6
+A3D,PA2
+A4D,PC12
+A5D,PH2
+
+# Ax header pins are connected to the following analog IO via an op-amp in voltage-follower mode running at 1.8V
+A0,PA8
+A1,PA9
+A2,PA10
+A3,PA12
+A4,PF3
+A5,PG15
+
+-UART1_TX,PE5
+-UART1_RX,PE6
+-UART3_TX,PD8
+-UART3_RX,PD9
+
+-I2C1_SCL,PH9
+-I2C1_SDA,PC1
+
+-SPI5_CS,PA3
+-SPI5_SCK,PE15
+-SPI5_MISO,PG1
+-SPI5_MOSI,PG2
+
+-BUTTON,PC13
+LED_BLUE,PG8
+LED_RED,PG10
+LED_GREEN,PG0
+
+-XSPIM_P2_DQS,PN0
+-XSPIM_P2_CS,PN1
+-XSPIM_P2_IO0,PN2
+-XSPIM_P2_IO1,PN3
+-XSPIM_P2_IO2,PN4
+-XSPIM_P2_IO3,PN5
+-XSPIM_P2_SCK,PN6
+-XSPIM_P2_IO4,PN8
+-XSPIM_P2_IO5,PN9
+-XSPIM_P2_IO6,PN10
+-XSPIM_P2_IO7,PN11
diff --git a/ports/stm32/boards/NUCLEO_N657X0/stm32n6xx_hal_conf.h b/ports/stm32/boards/NUCLEO_N657X0/stm32n6xx_hal_conf.h
new file mode 100644
index 0000000000..4012d56e5a
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/stm32n6xx_hal_conf.h
@@ -0,0 +1,18 @@
+/* This file is part of the MicroPython project, http://micropython.org/
+ * The MIT License (MIT)
+ * Copyright (c) 2019 Damien P. George
+ */
+#ifndef MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H
+#define MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H
+
+// Oscillator values in Hz
+#define HSE_VALUE (48000000)
+#define LSE_VALUE (32768)
+
+// Oscillator timeouts in ms
+#define HSE_STARTUP_TIMEOUT (100)
+#define LSE_STARTUP_TIMEOUT (5000)
+
+#include "boards/stm32n6xx_hal_conf_base.h"
+
+#endif // MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H
diff --git a/ports/stm32/boards/OLIMEX_E407/board.json b/ports/stm32/boards/OLIMEX_E407/board.json
index c14755c564..408df5a320 100644
--- a/ports/stm32/boards/OLIMEX_E407/board.json
+++ b/ports/stm32/boards/OLIMEX_E407/board.json
@@ -8,8 +8,8 @@
"olimex_e407.jpg"
],
"mcu": "stm32f4",
- "product": "E407",
+ "product": "STM32-E407",
"thumbnail": "",
- "url": "",
- "vendor": "OLIMEX"
+ "url": "https://www.olimex.com/Products/ARM/ST/STM32-E407",
+ "vendor": "Olimex"
}
diff --git a/ports/stm32/boards/OLIMEX_H407/board.json b/ports/stm32/boards/OLIMEX_H407/board.json
index 9ecc860ddc..27d2940513 100644
--- a/ports/stm32/boards/OLIMEX_H407/board.json
+++ b/ports/stm32/boards/OLIMEX_H407/board.json
@@ -8,8 +8,8 @@
"olimex_h407.jpg"
],
"mcu": "stm32f4",
- "product": "H407",
+ "product": "STM32-H407",
"thumbnail": "",
- "url": "",
- "vendor": "OLIMEX"
+ "url": "https://www.olimex.com/Products/ARM/ST/STM32-H407",
+ "vendor": "Olimex"
}
diff --git a/ports/stm32/boards/OPENMV_N6/bdev.c b/ports/stm32/boards/OPENMV_N6/bdev.c
new file mode 100644
index 0000000000..c6c918bd67
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/bdev.c
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2025 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 "storage.h"
+#include "xspi.h"
+
+#if MICROPY_HW_SPIFLASH_ENABLE_CACHE
+#error "Cannot enable MICROPY_HW_SPIFLASH_ENABLE_CACHE"
+#endif
+
+// External SPI flash uses hardware XSPI interface.
+const mp_spiflash_config_t spiflash_config = {
+ .bus_kind = MP_SPIFLASH_BUS_QSPI,
+ .bus.u_qspi.data = (void *)&xspi_flash2,
+ .bus.u_qspi.proto = &xspi_proto,
+};
+
+spi_bdev_t spi_bdev;
diff --git a/ports/stm32/boards/OPENMV_N6/board.c b/ports/stm32/boards/OPENMV_N6/board.c
new file mode 100644
index 0000000000..1f82d10bac
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/board.c
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024-2025 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 "py/mphal.h"
+#include "boardctrl.h"
+#include "xspi.h"
+
+// Values for OTP fuses for VDDIO2/3, to select low voltage mode (<2.5V).
+// See RM0486, Section 5, Table 18.
+#define BSEC_HW_CONFIG_ID (124U)
+#define BSEC_HWS_HSLV_VDDIO3 (1U << 15)
+#define BSEC_HWS_HSLV_VDDIO2 (1U << 16)
+
+#define OMV_BOOT_MAGIC_ADDR (0x3401FFFCU)
+#define OMV_BOOT_MAGIC_VALUE (0xB00710ADU)
+
+void mboot_board_early_init(void) {
+ // TODO: move some of the below code to a common location for all N6 boards?
+
+ // Enable PWR, BSEC and SYSCFG clocks.
+ LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG);
+
+ // Program high speed IO optimization fuses if they aren't already set.
+ uint32_t fuse;
+ BSEC_HandleTypeDef hbsec = { .Instance = BSEC };
+ const uint32_t mask = BSEC_HWS_HSLV_VDDIO2 | BSEC_HWS_HSLV_VDDIO3;
+ if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) {
+ fuse = 0;
+ } else if ((fuse & mask) != mask) {
+ // Program the fuse, and read back the set value.
+ if (HAL_BSEC_OTP_Program(&hbsec, BSEC_HW_CONFIG_ID, fuse | mask, HAL_BSEC_NORMAL_PROG) != HAL_OK) {
+ fuse = 0;
+ } else if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) {
+ fuse = 0;
+ }
+ }
+
+ // Enable Vdd ADC, needed for the ADC to work.
+ LL_PWR_EnableVddADC();
+
+ // Configure VDDIO2. Only enable 1.8V mode if the fuse is set.
+ LL_PWR_EnableVddIO2();
+ if (fuse & BSEC_HWS_HSLV_VDDIO2) {
+ LL_PWR_SetVddIO2VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_1V8);
+ }
+ SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO3. Only enable 1.8V mode if the fuse is set.
+ LL_PWR_EnableVddIO3();
+ if (fuse & BSEC_HWS_HSLV_VDDIO3) {
+ LL_PWR_SetVddIO3VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_1V8);
+ }
+ SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO4.
+ LL_PWR_EnableVddIO4();
+ LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3);
+ SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation
+
+ // Enable VDD for ADC and USB.
+ LL_PWR_EnableVddADC();
+ LL_PWR_EnableVddUSB();
+
+ // Enable XSPI in memory-mapped mode.
+ xspi_init();
+}
+
+void board_enter_bootloader(unsigned int n_args, const void *args) {
+ // Support both OpenMV bootloader and mboot.
+ *((uint32_t *)OMV_BOOT_MAGIC_ADDR) = OMV_BOOT_MAGIC_VALUE;
+ SCB_CleanDCache();
+ boardctrl_maybe_enter_mboot(n_args, args);
+}
+
+void board_early_init(void) {
+ // TODO: if (HAL_PWREx_ConfigSupply(PWR_EXTERNAL_SOURCE_SUPPLY ) != HAL_OK)
+
+ LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN3 | LL_PWR_WAKEUP_PIN2);
+ LL_PWR_SetWakeUpPinPolarityLow(LL_PWR_WAKEUP_PIN3 | LL_PWR_WAKEUP_PIN2);
+}
+
+void board_leave_standby(void) {
+ // TODO: move some of the below code to a common location for all N6 boards?
+
+ // Enable PWR, BSEC and SYSCFG clocks.
+ LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG);
+
+ // Configure VDDIO2 (1.8V mode selection is retained).
+ LL_PWR_EnableVddIO2();
+ SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO3 (1.8V mode selection is retained).
+ LL_PWR_EnableVddIO3();
+ SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO4.
+ LL_PWR_EnableVddIO4();
+ LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3);
+ SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation
+
+ // Enable VDD for ADC and USB.
+ LL_PWR_EnableVddADC();
+ LL_PWR_EnableVddUSB();
+}
diff --git a/ports/stm32/boards/OPENMV_N6/board.ld b/ports/stm32/boards/OPENMV_N6/board.ld
new file mode 100644
index 0000000000..e9ded785f2
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/board.ld
@@ -0,0 +1,39 @@
+/*
+ Linker script for OPENMV_N6.
+
+ Note: upper 512k of SRAM2 is copied from external flash upon reset.
+*/
+
+/* Specify the memory areas */
+MEMORY
+{
+ FLEXRAM_S (xrw) : ORIGIN = 0x34000000, LENGTH = 80K
+ SRAM2_S_RAM (xrw) : ORIGIN = 0x34100000, LENGTH = 1024K
+ SRAM2_S_FSBL (xrw) : ORIGIN = 0x34180400, LENGTH = 511K /* mboot firmware, not needed after mboot exits */
+ EXT_FLASH (rx) : ORIGIN = 0x70080000, LENGTH = 3584K
+ EXT_FLASH_FS (rx) : ORIGIN = 0x70400000, LENGTH = 4M
+ EXT_FLASH_ROMFS (rx) : ORIGIN = 0x70800000, LENGTH = 24M
+}
+
+REGION_ALIAS("IRAM", FLEXRAM_S);
+REGION_ALIAS("RAM", SRAM2_S_RAM);
+REGION_ALIAS("FLASH_APP", EXT_FLASH);
+
+/* 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;
+
+/* ROMFS location */
+_micropy_hw_romfs_part0_start = ORIGIN(EXT_FLASH_ROMFS);
+_micropy_hw_romfs_part0_size = LENGTH(EXT_FLASH_ROMFS);
diff --git a/ports/stm32/boards/OPENMV_N6/manifest.py b/ports/stm32/boards/OPENMV_N6/manifest.py
new file mode 100644
index 0000000000..62990220f3
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/manifest.py
@@ -0,0 +1,3 @@
+include("$(PORT_DIR)/boards/manifest.py")
+require("bundle-networking")
+require("aioble")
diff --git a/ports/stm32/boards/OPENMV_N6/mpconfigboard.h b/ports/stm32/boards/OPENMV_N6/mpconfigboard.h
new file mode 100644
index 0000000000..ed7bb548a1
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/mpconfigboard.h
@@ -0,0 +1,167 @@
+#define MICROPY_HW_BOARD_NAME "OpenMV N6"
+#define MICROPY_HW_MCU_NAME "STM32N657X0"
+
+#define MICROPY_GC_STACK_ENTRY_TYPE uint32_t
+#define MICROPY_ALLOC_GC_STACK_SIZE (128)
+#define MICROPY_FATFS_EXFAT (1)
+
+#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0)
+#define MICROPY_HW_HAS_SWITCH (0)
+#define MICROPY_HW_HAS_FLASH (1)
+#define MICROPY_HW_SDCARD_MOUNT_AT_BOOT (0)
+#define MICROPY_HW_ENABLE_RNG (1)
+#define MICROPY_HW_ENABLE_RTC (1)
+#define MICROPY_HW_ENABLE_DAC (0)
+#define MICROPY_HW_ENABLE_USB (1)
+#define MICROPY_HW_ENABLE_SDCARD (1)
+#define MICROPY_PY_PYB_LEGACY (0)
+
+#define MICROPY_BOARD_ENTER_BOOTLOADER board_enter_bootloader
+#define MICROPY_BOARD_EARLY_INIT board_early_init
+#define MICROPY_BOARD_LEAVE_STANDBY board_leave_standby()
+
+// HSE is 48MHz, this gives a CPU frequency of 800MHz.
+#define MICROPY_HW_CLK_PLLM (6)
+#define MICROPY_HW_CLK_PLLN (100)
+#define MICROPY_HW_CLK_PLLP1 (1)
+#define MICROPY_HW_CLK_PLLP2 (1)
+#define MICROPY_HW_CLK_PLLFRAC (0)
+
+// The LSE is a 32kHz crystal.
+#define MICROPY_HW_RTC_USE_LSE (1)
+#define MICROPY_HW_RTC_USE_US (1)
+
+// External SPI flash.
+#define MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2 (28) // 256Mbit
+
+// ROMFS config
+#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI (1)
+#define MICROPY_HW_ROMFS_XSPI_SPIBDEV_OBJ (&spi_bdev)
+#define MICROPY_HW_ROMFS_ENABLE_PART0 (1)
+
+// SPI flash, block device config.
+#define MICROPY_HW_BDEV_SPIFLASH (&spi_bdev)
+#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev)
+#define MICROPY_HW_BDEV_SPIFLASH_CONFIG (&spiflash_config)
+#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES (4 * 1024 * 1024)
+#define MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES (4 * 1024 * 1024)
+
+// UART buses
+#define MICROPY_HW_UART2_TX (pyb_pin_BT_TXD)
+#define MICROPY_HW_UART2_RX (pyb_pin_BT_RXD)
+#define MICROPY_HW_UART2_RTS (pyb_pin_BT_RTS)
+#define MICROPY_HW_UART2_CTS (pyb_pin_BT_CTS)
+#define MICROPY_HW_UART3_TX (pyb_pin_UART3_TX)
+#define MICROPY_HW_UART3_RX (pyb_pin_UART3_RX)
+#define MICROPY_HW_UART4_TX (pyb_pin_UART4_TX)
+#define MICROPY_HW_UART4_RX (pyb_pin_UART4_RX)
+#define MICROPY_HW_UART7_TX (pyb_pin_UART7_TX)
+#define MICROPY_HW_UART7_RX (pyb_pin_UART7_RX)
+
+// I2C buses
+#define MICROPY_HW_I2C2_SCL (pyb_pin_I2C2_SCL)
+#define MICROPY_HW_I2C2_SDA (pyb_pin_I2C2_SDA)
+#define MICROPY_HW_I2C4_SCL (pyb_pin_I2C4_SCL)
+#define MICROPY_HW_I2C4_SDA (pyb_pin_I2C4_SDA)
+
+// SPI buses
+#define MICROPY_HW_SPI2_NSS (pyb_pin_SPI2_CS)
+#define MICROPY_HW_SPI2_SCK (pyb_pin_SPI2_SCK)
+#define MICROPY_HW_SPI2_MISO (pyb_pin_SPI2_MISO)
+#define MICROPY_HW_SPI2_MOSI (pyb_pin_SPI2_MOSI)
+#define MICROPY_HW_SPI4_NSS (pyb_pin_SPI4_CS)
+#define MICROPY_HW_SPI4_SCK (pyb_pin_SPI4_SCK)
+#define MICROPY_HW_SPI4_MISO (pyb_pin_SPI4_MISO)
+#define MICROPY_HW_SPI4_MOSI (pyb_pin_SPI4_MOSI)
+
+// USER is pulled high, and pressing the button makes the input go low.
+#define MICROPY_HW_USRSW_PIN (pyb_pin_BUTTON)
+#define MICROPY_HW_USRSW_PULL (GPIO_NOPULL)
+#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_FALLING)
+#define MICROPY_HW_USRSW_PRESSED (0)
+
+// LEDs
+#define MICROPY_HW_LED1 (pyb_pin_LED_RED)
+#define MICROPY_HW_LED2 (pyb_pin_LED_GREEN)
+#define MICROPY_HW_LED3 (pyb_pin_LED_BLUE)
+#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin))
+#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin))
+
+// SD Card SDMMC
+// SD_VSELECT: low(default)=3.3V IO, high=1.8V IO
+// SD_RESET: drive low to turn off SD VCC (pulled high by default)
+// SD_DETECT: pulled high in hardware, goes low when SD inserted
+#define MICROPY_HW_SDCARD_SDMMC (1)
+#define MICROPY_HW_SDCARD_CK (pyb_pin_SD_SDIO_CK)
+#define MICROPY_HW_SDCARD_CMD (pyb_pin_SD_SDIO_CMD)
+#define MICROPY_HW_SDCARD_D0 (pyb_pin_SD_SDIO_D0)
+#define MICROPY_HW_SDCARD_D1 (pyb_pin_SD_SDIO_D1)
+#define MICROPY_HW_SDCARD_D2 (pyb_pin_SD_SDIO_D2)
+#define MICROPY_HW_SDCARD_D3 (pyb_pin_SD_SDIO_D3)
+#define MICROPY_HW_SDCARD_DETECT_PIN (pyb_pin_SD_DETECT)
+#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_NOPULL)
+#define MICROPY_HW_SDCARD_DETECT_PRESENT (GPIO_PIN_RESET)
+
+// WiFi SDMMC
+#define MICROPY_HW_SDIO_SDMMC (2)
+#define MICROPY_HW_SDIO_CK (pyb_pin_WL_SDIO_CK)
+#define MICROPY_HW_SDIO_CMD (pyb_pin_WL_SDIO_CMD)
+#define MICROPY_HW_SDIO_D0 (pyb_pin_WL_SDIO_D0)
+#define MICROPY_HW_SDIO_D1 (pyb_pin_WL_SDIO_D1)
+#define MICROPY_HW_SDIO_D2 (pyb_pin_WL_SDIO_D2)
+#define MICROPY_HW_SDIO_D3 (pyb_pin_WL_SDIO_D3)
+
+// USB config
+#define MICROPY_HW_USB_HS (1)
+#define MICROPY_HW_USB_HS_IN_FS (1)
+#define MICROPY_HW_USB_MAIN_DEV (USB_PHY_HS_ID)
+#define MICROPY_HW_USB_VID 0x37C5
+#define MICROPY_HW_USB_PID 0x1206
+#define MICROPY_HW_USB_PID_CDC (MICROPY_HW_USB_PID)
+#define MICROPY_HW_USB_PID_MSC (MICROPY_HW_USB_PID)
+#define MICROPY_HW_USB_PID_CDC_MSC (MICROPY_HW_USB_PID)
+#define MICROPY_HW_USB_PID_CDC_HID (MICROPY_HW_USB_PID)
+#define MICROPY_HW_USB_PID_CDC_MSC_HID (MICROPY_HW_USB_PID)
+
+// Murata 1YN configuration
+#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/w43439_sdio_1yn_7_95_59_combined.h"
+#define CYW43_WIFI_NVRAM_INCLUDE_FILE "lib/cyw43-driver/firmware/wifi_nvram_1yn.h"
+#define CYW43_BT_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/cyw43_btfw_1yn.h"
+
+// Bluetooth config
+#define MICROPY_HW_BLE_UART_ID (PYB_UART_2)
+#define MICROPY_HW_BLE_UART_BAUDRATE (115200)
+#define MICROPY_HW_BLE_UART_BAUDRATE_SECONDARY (3000000)
+#define MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE (3000000)
+
+/******************************************************************************/
+// Bootloader configuration
+
+#define MBOOT_BOARD_EARLY_INIT(initial_r0) mboot_board_early_init()
+
+#define MBOOT_FSLOAD (1)
+#define MBOOT_VFS_FAT (1)
+
+#define MBOOT_SPIFLASH_CS (pyb_pin_XSPIM_P2_CS)
+#define MBOOT_SPIFLASH_SCK (pyb_pin_XSPIM_P2_SCK)
+#define MBOOT_SPIFLASH_MOSI (pyb_pin_XSPIM_P2_IO0)
+#define MBOOT_SPIFLASH_MISO (pyb_pin_XSPIM_P2_IO1)
+#define MBOOT_SPIFLASH_ADDR (0x70000000)
+#define MBOOT_SPIFLASH_BYTE_SIZE (32 * 1024 * 1024)
+#define MBOOT_SPIFLASH_LAYOUT "/0x70000000/8192*4Kg"
+#define MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE (1)
+#define MBOOT_SPIFLASH_SPIFLASH (&spi_bdev.spiflash)
+#define MBOOT_SPIFLASH_CONFIG (&spiflash_config)
+
+/******************************************************************************/
+// Function and variable declarations
+
+extern const struct _mp_spiflash_config_t spiflash_config;
+extern struct _spi_bdev_t spi_bdev;
+
+void mboot_board_early_init(void);
+void mboot_board_entry_init(void);
+
+void board_enter_bootloader(unsigned int n_args, const void *args);
+void board_early_init(void);
+void board_leave_standby(void);
diff --git a/ports/stm32/boards/OPENMV_N6/mpconfigboard.mk b/ports/stm32/boards/OPENMV_N6/mpconfigboard.mk
new file mode 100644
index 0000000000..0283a486c1
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/mpconfigboard.mk
@@ -0,0 +1,30 @@
+# This board requires a bootloader, either mboot or OpenMV's bootloader.
+USE_MBOOT = 1
+
+MCU_SERIES = n6
+CMSIS_MCU = STM32N657xx
+AF_FILE = boards/stm32n657_af.csv
+ifeq ($(BUILDING_MBOOT),1)
+SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_fsbl.o
+else
+SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_s.o
+endif
+STM32_N6_HEADER_VERSION = 2.3
+DKEL = $(STM32_CUBE_PROGRAMMER)/bin/ExternalLoader/MX25UM51245G_STM32N6570-NUCLEO.stldr
+
+LD_FILES = boards/OPENMV_N6/board.ld boards/common_n6_flash.ld
+TEXT0_ADDR = 0x70080000
+
+# MicroPython settings
+MICROPY_FLOAT_IMPL = double
+MICROPY_PY_BLUETOOTH ?= 1
+MICROPY_BLUETOOTH_NIMBLE ?= 1
+MICROPY_BLUETOOTH_BTSTACK ?= 0
+MICROPY_PY_LWIP ?= 1
+MICROPY_PY_NETWORK_CYW43 ?= 1
+MICROPY_PY_SSL ?= 1
+MICROPY_SSL_MBEDTLS ?= 1
+MICROPY_VFS_LFS2 ?= 1
+
+# Board specific frozen modules
+FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py
diff --git a/ports/stm32/boards/OPENMV_N6/partition_stm32n657xx.h b/ports/stm32/boards/OPENMV_N6/partition_stm32n657xx.h
new file mode 100644
index 0000000000..ac38dac748
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/partition_stm32n657xx.h
@@ -0,0 +1,5 @@
+// This board does not use any security settings, so can just stay in secure
+// mode without configuring the SAU.
+
+static inline void TZ_SAU_Setup(void) {
+}
diff --git a/ports/stm32/boards/OPENMV_N6/pins.csv b/ports/stm32/boards/OPENMV_N6/pins.csv
new file mode 100644
index 0000000000..b05b8b57f9
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/pins.csv
@@ -0,0 +1,142 @@
+,PA0
+,PA1
+,PA2
+,PA3
+,PA4
+,PA5
+,PA6
+,PA7
+,PA8
+,PA9
+,PA10
+SPI2_CS,PA11
+SPI2_SCK,PA12
+UART4_RX,PA11
+UART4_TX,PA12
+P3,PA11
+P2,PA12
+,PA13
+,PA14
+,PA15
+,PB0
+,PB1
+,PB2
+,PB3
+,PB4
+,PB5
+SPI4_MISO,PB6
+SPI4_MOSI,PB7
+,PB8
+,PB9
+I2C2_SCL,PB10
+I2C2_SDA,PB11
+UART3_TX,PB10
+UART3_RX,PB11
+P4,PB10
+P5,PB11
+,PB12
+,PB13
+,PB14
+,PB15
+,PC0
+,PC1
+,PC2
+,PC3
+,PC4
+,PC5
+,PC6
+,PC7
+,PC8
+,PC9
+,PC10
+,PC11
+,PC12
+P11,PC13
+,PC14
+,PC15
+,PD0
+,PD1
+,PD2
+,PD3
+,PD4
+,PD5
+P10,PD6
+SPI2_MOSI,PD7
+P0,PD7
+,PD8
+,PD9
+,PD10
+SPI2_MISO,PD11
+P1,PD11
+,PD12
+P8,PD13
+,PD14
+,PD15
+,PE0
+,PE1
+,PE2
+,PE3
+,PE4
+,PE5
+,PE6
+UART7_RX,PE7
+UART7_TX,PE8
+,PE9
+,PE10
+SPI4_CS,PE11
+SPI4_SCK,PE12
+I2C4_SCL,PE13
+I2C4_SDA,PE14
+,PE15
+P6,PG0
+P9,PG12
+P7,PG13
+,PG15
+
+BUTTON,PF4
+LED_RED,PG10
+LED_GREEN,PA7
+LED_BLUE,PB1
+
+-XSPIM_P2_DQS,PN0
+-XSPIM_P2_CS,PN1
+-XSPIM_P2_IO0,PN2
+-XSPIM_P2_IO1,PN3
+-XSPIM_P2_IO2,PN4
+-XSPIM_P2_IO3,PN5
+-XSPIM_P2_SCK,PN6
+-XSPIM_P2_NCLK,PN7
+-XSPIM_P2_IO4,PN8
+-XSPIM_P2_IO5,PN9
+-XSPIM_P2_IO6,PN10
+-XSPIM_P2_IO7,PN11
+-FLASH_RESET,PN12
+
+-WL_REG_ON,PB12
+-WL_HOST_WAKE,PB14
+-WL_SDIO_D0,PB8
+-WL_SDIO_D1,PG8
+-WL_SDIO_D2,PB9
+-WL_SDIO_D3,PB4
+-WL_SDIO_CMD,PA0
+-WL_SDIO_CK,PD2
+-WL_I2S_SDO,PG14
+-WL_I2S_WS,PB15
+-WL_I2S_SCLK,PB13
+-BT_RXD,PF6
+-BT_TXD,PD5
+-BT_CTS,PG5
+-BT_RTS,PF3
+-BT_REG_ON,PD10
+-BT_HOST_WAKE,PD14
+-BT_DEV_WAKE,PD15
+
+-SD_SDIO_D0,PC8
+-SD_SDIO_D1,PC9
+-SD_SDIO_D2,PC10
+-SD_SDIO_D3,PC11
+-SD_SDIO_CK,PC12
+-SD_SDIO_CMD,PH2
+-SD_RESET,PC7
+-SD_DETECT,PC6
+-SD_VSELECT,PG6
diff --git a/ports/stm32/boards/OPENMV_N6/stm32n6xx_hal_conf.h b/ports/stm32/boards/OPENMV_N6/stm32n6xx_hal_conf.h
new file mode 100644
index 0000000000..4012d56e5a
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/stm32n6xx_hal_conf.h
@@ -0,0 +1,18 @@
+/* This file is part of the MicroPython project, http://micropython.org/
+ * The MIT License (MIT)
+ * Copyright (c) 2019 Damien P. George
+ */
+#ifndef MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H
+#define MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H
+
+// Oscillator values in Hz
+#define HSE_VALUE (48000000)
+#define LSE_VALUE (32768)
+
+// Oscillator timeouts in ms
+#define HSE_STARTUP_TIMEOUT (100)
+#define LSE_STARTUP_TIMEOUT (5000)
+
+#include "boards/stm32n6xx_hal_conf_base.h"
+
+#endif // MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H
diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json
index 0bd3573d64..5efd188618 100644
--- a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json
+++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json
@@ -8,8 +8,8 @@
"sparkfun_micromod_stm32.jpg"
],
"mcu": "stm32f4",
- "product": "Micromod STM32",
+ "product": "MicroMod STM32",
"thumbnail": "",
- "url": "",
- "vendor": "Sparkfun"
+ "url": "https://www.sparkfun.com/products/17713",
+ "vendor": "SparkFun"
}
diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h
index d2f44cf6cc..1e17905bb0 100644
--- a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h
+++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h
@@ -1,4 +1,4 @@
-// The Sparkfun MicroMod spec uses a zero-based peripheral numbering scheme.
+// The SparkFun MicroMod spec uses a zero-based peripheral numbering scheme.
// In cases where the 0th peripheral is the default, the "0" is omitted from
// the name (e.g. "I2C" instead of "I2C0").
//
diff --git a/ports/stm32/boards/common_n6_flash.ld b/ports/stm32/boards/common_n6_flash.ld
new file mode 100644
index 0000000000..a1f1fa531f
--- /dev/null
+++ b/ports/stm32/boards/common_n6_flash.ld
@@ -0,0 +1,57 @@
+/* Memory layout for N6 when the application runs from external flash in XIP mode.
+
+ FLASH_APP .isr_vector
+ FLASH_APP .text
+ FLASH_APP .data
+
+ RAM .data
+ RAM .bss
+ RAM .heap
+ RAM .stack
+*/
+
+ENTRY(Reset_Handler)
+
+REGION_ALIAS("FLASH_COMMON", FLASH_APP);
+
+/* define output sections */
+SECTIONS
+{
+ .isr_vector :
+ {
+ _siram = .;
+
+ /* This ISR is used for normal application mode. */
+ . = ALIGN(1024);
+ KEEP(*(.isr_vector));
+
+ /* This ISR is used when waking from STANDBY. */
+ . = ALIGN(1024);
+ KEEP(*(.rodata.iram_bootloader_isr_vector));
+
+ /* Need to place in RAM all the code necessary to write to
+ * flash, and to resume from STANDBY. */
+ *(.*.iram_bootloader_reset);
+ *(.*.memcpy);
+ *(.*.mp_hal_gpio_clock_enable);
+ *(.*.mp_hal_pin_config);
+ *(.*.mp_hal_pin_config_speed);
+ *drivers/memory/spiflash.o(.text.* .rodata.*)
+ *xspi.o(.text.* .rodata.*);
+ *boards*(.rodata.spiflash_config*)
+ *boards*(.*.board_leave_standby);
+ *(*.rodata.pin_N*_obj);
+ *(.text.LL_AHB4_GRP1_EnableClock);
+ *(.text.LL_APB4_GRP2_EnableClock);
+
+ . = ALIGN(4);
+ _eiram = .;
+ } >IRAM AT> FLASH_COMMON
+
+ INCLUDE common_text.ld
+ INCLUDE common_extratext_data_in_flash.ld
+ INCLUDE common_bss_heap_stack.ld
+}
+
+/* Used by the start-up code to initialise data */
+_siiram = LOADADDR(.isr_vector);
diff --git a/ports/stm32/boards/common_text.ld b/ports/stm32/boards/common_text.ld
index 16eea43bae..d95467babc 100644
--- a/ports/stm32/boards/common_text.ld
+++ b/ports/stm32/boards/common_text.ld
@@ -12,3 +12,11 @@
. = ALIGN(4);
_etext = .; /* define a global symbol at end of code */
} >FLASH_COMMON
+
+/* Secure Gateway stubs */
+.gnu.sgstubs :
+{
+ . = ALIGN(4);
+ *(.gnu.sgstubs*)
+ . = ALIGN(4);
+} >FLASH_COMMON
diff --git a/ports/stm32/boards/make-pins.py b/ports/stm32/boards/make-pins.py
index 1b89fd6415..6f8a0a659d 100755
--- a/ports/stm32/boards/make-pins.py
+++ b/ports/stm32/boards/make-pins.py
@@ -215,7 +215,7 @@ class Stm32Pin(boardgen.Pin):
def validate_cpu_pin_name(cpu_pin_name):
boardgen.Pin.validate_cpu_pin_name(cpu_pin_name)
- if not re.match("P[A-K][0-9]+(_C)?$", cpu_pin_name):
+ if not re.match("P[A-O][0-9]+(_C)?$", cpu_pin_name):
raise boardgen.PinGeneratorError("Invalid cpu pin name '{}'".format(cpu_pin_name))
diff --git a/ports/stm32/boards/pllvalues.py b/ports/stm32/boards/pllvalues.py
index d8856bfecd..ae042d999c 100644
--- a/ports/stm32/boards/pllvalues.py
+++ b/ports/stm32/boards/pllvalues.py
@@ -293,7 +293,7 @@ def main():
break
# Relax constraint on PLLQ being 48MHz on MCUs which have separate PLLs for 48MHz
- relax_pll48 = mcu_series.startswith(("stm32f413", "stm32f7", "stm32h5", "stm32h7"))
+ relax_pll48 = mcu_series.startswith(("stm32f413", "stm32f7", "stm32h5", "stm32h7", "stm32n6"))
hse_valid_plls = compute_pll_table(hse, relax_pll48)
if hsi is not None:
diff --git a/ports/stm32/boards/stm32g474_af.csv b/ports/stm32/boards/stm32g474_af.csv
index 5853f5f900..59a0e91a14 100644
--- a/ports/stm32/boards/stm32g474_af.csv
+++ b/ports/stm32/boards/stm32g474_af.csv
@@ -2,12 +2,12 @@ Port ,Pin ,AF0 ,AF1 ,AF2
, ,I2C4/SYS_AF ,LPTIM1/TIM2/5/15/16/17,I2C1/3/TIM1/2/3/4/5/8/20/15/COMP1,QUADSPI1/I2C3/4/SAI1/USB/HRTIM1/TIM8/20/15/COMP3,I2C1/2/3/4/TIM1/8/16/17,QUADSPI1/SPI1/2/3/4/I2S2/3/I2C4/UART4/5/TIM8/Infrared,QUADSPI1/SPI2/3/I2S2/3/TIM1/5/8/20/Infrared,USART1/2/3/CAN/COMP7/5/6,I2C3/4/UART4/5/LPUART1/COMP1/2/7/4/5/6/3,CAN/TIM1/8/15/CAN1/2,QUADSPI1/TIM2/3/4/8/17,LPTIM1/TIM1/8/CAN1/3,FMC/LPUART1/SAI1/HRTIM1/TIM1,SAI1SAI1/HRTIM1/OPAMP2,UART4/5/SAI1/TIM2/15/UCPD1,SYS ,ADC
PortA,PA0 , ,TIM2_CH1 ,TIM5_CH1 , , , , ,USART2_CTS ,COMP1_OUT ,TIM8_BKIN ,TIM8_ETR , , , ,TIM2_ETR ,EVENTOUT,ADC12_IN1
PortA,PA1 ,RTC_REFIN ,TIM2_CH2 ,TIM5_CH2 , , , , ,USART2_RTS_DE , ,TIM15_CH1N , , , , , ,EVENTOUT,ADC12_IN2
-PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3 , , , , ,USART2_TX ,COMP2_OUT ,TIM15_CH1 ,QUADSPI1_BK1_NCS , ,LPUART1_TX , ,UCPD1_FRSTX ,EVENTOUT,ADC1_IN3
-PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4 ,SAI1_CK1 , , , ,USART2_RX , ,TIM15_CH2 ,QUADSPI1_CLK , ,LPUART1_RX ,SAI1_MCLK_A , ,EVENTOUT,ADC1_IN4
+PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3 , , , , ,USART2_TX ,COMP2_OUT ,TIM15_CH1 ,QUADSPI_BK1_NCS , ,LPUART1_TX , ,UCPD1_FRSTX ,EVENTOUT,ADC1_IN3
+PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4 ,SAI1_CK1 , , , ,USART2_RX , ,TIM15_CH2 ,QUADSPI_CLK , ,LPUART1_RX ,SAI1_MCLK_A , ,EVENTOUT,ADC1_IN4
PortA,PA4 , , ,TIM3_CH2 , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_CK , , , , , ,SAI1_FS_B , ,EVENTOUT,ADC2_IN17
PortA,PA5 , ,TIM2_CH1 ,TIM2_ETR , , ,SPI1_SCK , , , , , , , , ,UCPD1_FRSTX ,EVENTOUT,ADC2_IN13
-PortA,PA6 , ,TIM16_CH1 ,TIM3_CH1 , ,TIM8_BKIN ,SPI1_MISO ,TIM1_BKIN , ,COMP1_OUT , ,QUADSPI1_BK1_IO3 , ,LPUART1_CTS , , ,EVENTOUT,ADC2_IN3
-PortA,PA7 , ,TIM17_CH1 ,TIM3_CH2 , ,TIM8_CH1N ,SPI1_MOSI ,TIM1_CH1N , ,COMP2_OUT , ,QUADSPI1_BK1_IO2 , , , ,UCPD1_FRSTX ,EVENTOUT,ADC2_IN4
+PortA,PA6 , ,TIM16_CH1 ,TIM3_CH1 , ,TIM8_BKIN ,SPI1_MISO ,TIM1_BKIN , ,COMP1_OUT , ,QUADSPI_BK1_IO3 , ,LPUART1_CTS , , ,EVENTOUT,ADC2_IN3
+PortA,PA7 , ,TIM17_CH1 ,TIM3_CH2 , ,TIM8_CH1N ,SPI1_MOSI ,TIM1_CH1N , ,COMP2_OUT , ,QUADSPI_BK1_IO2 , , , ,UCPD1_FRSTX ,EVENTOUT,ADC2_IN4
PortA,PA8 ,MCO , ,I2C3_SCL , ,I2C2_SDA ,I2S2_MCK ,TIM1_CH1 ,USART1_CK ,COMP7_OUT , ,TIM4_ETR ,CAN3_RX ,SAI1_CK2 ,HRTIM1_CHA1 ,SAI1_SCK_A ,EVENTOUT,ADC5_IN1
PortA,PA9 , , ,I2C3_SMBA , ,I2C2_SCL ,I2S3_MCK ,TIM1_CH2 ,USART1_TX ,COMP5_OUT ,TIM15_BKIN ,TIM2_CH3 , , ,HRTIM1_CHA2 ,SAI1_FS_A ,EVENTOUT,ADC5_IN2
PortA,PA10, ,TIM17_BKIN , ,USB_CRS_SYNC ,I2C2_SMBA ,SPI2_MISO ,TIM1_CH3 ,USART1_RX ,COMP6_OUT , ,TIM2_CH4 ,TIM8_BKIN ,SAI1_D1 ,HRTIM1_CHB1 ,SAI1_SD_A ,EVENTOUT,
@@ -16,9 +16,9 @@ PortA,PA12, ,TIM16_CH1 ,
PortA,PA13,SWDIOJTMS ,TIM16_CH1N , ,I2C4_SCL ,I2C1_SCL ,IR_OUT , ,USART3_CTS , , ,TIM4_CH3 , , ,SAI1_SD_B , ,EVENTOUT,
PortA,PA14,SWCLKJTCK ,LPTIM1_OUT , ,I2C4_SMBA ,I2C1_SDA ,TIM8_CH2 ,TIM1_BKIN ,USART2_TX , , , , , ,SAI1_FS_B , ,EVENTOUT,
PortA,PA15,JTDI ,TIM2_CH1 ,TIM8_CH1 , ,I2C1_SCL ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_RX ,UART4_RTS_DE ,TIM1_BKIN , ,CAN3_TX , ,HRTIM1_FLT2 ,TIM2_ETR ,EVENTOUT,
-PortB,PB0 , , ,TIM3_CH3 , ,TIM8_CH2N , ,TIM1_CH2N , , , ,QUADSPI1_BK1_IO1 , , ,HRTIM1_FLT5 ,UCPD1_FRSTX ,EVENTOUT,ADC3_IN12/ADC1_IN15
-PortB,PB1 , , ,TIM3_CH4 , ,TIM8_CH3N , ,TIM1_CH3N , ,COMP4_OUT , ,QUADSPI1_BK1_IO0 , ,LPUART1_RTS_DE ,HRTIM1_SCOUT , ,EVENTOUT,ADC3_IN1/ADC1_IN12
-PortB,PB2 ,RTC_OUT2 ,LPTIM1_OUT ,TIM5_CH1 ,TIM20_CH1 ,I2C3_SMBA , , , , , ,QUADSPI1_BK2_IO1 , , ,HRTIM1_SCIN , ,EVENTOUT,ADC2_IN12
+PortB,PB0 , , ,TIM3_CH3 , ,TIM8_CH2N , ,TIM1_CH2N , , , ,QUADSPI_BK1_IO1 , , ,HRTIM1_FLT5 ,UCPD1_FRSTX ,EVENTOUT,ADC3_IN12/ADC1_IN15
+PortB,PB1 , , ,TIM3_CH4 , ,TIM8_CH3N , ,TIM1_CH3N , ,COMP4_OUT , ,QUADSPI_BK1_IO0 , ,LPUART1_RTS_DE ,HRTIM1_SCOUT , ,EVENTOUT,ADC3_IN1/ADC1_IN12
+PortB,PB2 ,RTC_OUT2 ,LPTIM1_OUT ,TIM5_CH1 ,TIM20_CH1 ,I2C3_SMBA , , , , , ,QUADSPI_BK2_IO1 , , ,HRTIM1_SCIN , ,EVENTOUT,ADC2_IN12
PortB,PB3 ,JTDOTRACESWO,TIM2_CH2 ,TIM4_ETR ,USB_CRS_SYNC ,TIM8_CH1N ,SPI1_SCK ,SPI3_SCK/I2S3_CK ,USART2_TX , , ,TIM3_ETR ,CAN3_RX ,HRTIM1_SCOUT ,HRTIM1_EEV9 ,SAI1_SCK_B ,EVENTOUT,
PortB,PB4 ,JTRST ,TIM16_CH1 ,TIM3_CH1 , ,TIM8_CH2N ,SPI1_MISO ,SPI3_MISO ,USART2_RX ,UART5_RTS_DE , ,TIM17_BKIN ,CAN3_TX , ,HRTIM1_EEV7 ,SAI1_MCLK_B ,EVENTOUT,
PortB,PB5 , ,TIM16_BKIN ,TIM3_CH2 ,TIM8_CH3N ,I2C1_SMBA ,SPI1_MOSI ,SPI3_MOSI/I2S3_SD ,USART2_CK ,I2C3_SDA ,CAN2_RX ,TIM17_CH1 ,LPTIM1_IN1 ,SAI1_SD_B ,HRTIM1_EEV6 ,UART5_CTS ,EVENTOUT,
@@ -26,17 +26,17 @@ PortB,PB6 , ,TIM16_CH1N ,TIM4_CH1
PortB,PB7 , ,TIM17_CH1N ,TIM4_CH2 ,I2C4_SDA ,I2C1_SDA ,TIM8_BKIN , ,USART1_RX ,COMP3_OUT , ,TIM3_CH4 ,LPTIM1_IN2 ,FMC_NL ,HRTIM1_EEV3 ,UART4_CTS ,EVENTOUT,
PortB,PB8 , ,TIM16_CH1 ,TIM4_CH3 ,SAI1_CK1 ,I2C1_SCL , , ,USART3_RX ,COMP1_OUT ,CAN1_RX ,TIM8_CH2 , ,TIM1_BKIN ,HRTIM1_EEV8 ,SAI1_MCLK_A ,EVENTOUT,
PortB,PB9 , ,TIM17_CH1 ,TIM4_CH4 ,SAI1_D2 ,I2C1_SDA , ,IR_OUT ,USART3_TX ,COMP2_OUT ,CAN1_TX ,TIM8_CH3 , ,TIM1_CH3N ,HRTIM1_EEV5 ,SAI1_FS_A ,EVENTOUT,
-PortB,PB10, ,TIM2_CH3 , , , , , ,USART3_TX ,LPUART1_RX , ,QUADSPI1_CLK , ,TIM1_BKIN ,HRTIM1_FLT3 ,SAI1_SCK_A ,EVENTOUT,
-PortB,PB11, ,TIM2_CH4 , , , , , ,USART3_RX ,LPUART1_TX , ,QUADSPI1_BK1_NCS , , ,HRTIM1_FLT4 , ,EVENTOUT,ADC12_IN14
+PortB,PB10, ,TIM2_CH3 , , , , , ,USART3_TX ,LPUART1_RX , ,QUADSPI_CLK , ,TIM1_BKIN ,HRTIM1_FLT3 ,SAI1_SCK_A ,EVENTOUT,
+PortB,PB11, ,TIM2_CH4 , , , , , ,USART3_RX ,LPUART1_TX , ,QUADSPI_BK1_NCS , , ,HRTIM1_FLT4 , ,EVENTOUT,ADC12_IN14
PortB,PB12, , ,TIM5_ETR , ,I2C2_SMBA ,SPI2_NSS/I2S2_WS ,TIM1_BKIN ,USART3_CK ,LPUART1_RTS_DE ,CAN2_RX , , , ,HRTIM1_CHC1 , ,EVENTOUT,ADC4_IN3/ADC1_IN11
PortB,PB13, , , , , ,SPI2_SCK/I2S2_CK ,TIM1_CH1N ,USART3_CTS ,LPUART1_CTS ,CAN2_TX , , , ,HRTIM1_CHC2 , ,EVENTOUT,ADC3_IN5
PortB,PB14, ,TIM15_CH1 , , , ,SPI2_MISO ,TIM1_CH2N ,USART3_RTS_DE ,COMP4_OUT , , , , ,HRTIM1_CHD1 , ,EVENTOUT,ADC4_IN4/ADC1_IN5
PortB,PB15,RTC_REFIN ,TIM15_CH2 ,TIM15_CH1N ,COMP3_OUT ,TIM1_CH3N ,SPI2_MOSI/I2S2_SD , , , , , , , ,HRTIM1_CHD2 , ,EVENTOUT,ADC4_IN5/ADC2_IN15
PortC,PC0 , ,LPTIM1_IN1 ,TIM1_CH1 , , , , , ,LPUART1_RX , , , , , , ,EVENTOUT,ADC12_IN6
-PortC,PC1 , ,LPTIM1_OUT ,TIM1_CH2 , , , , , ,LPUART1_TX , ,QUADSPI1_BK2_IO0 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN7
-PortC,PC2 , ,LPTIM1_IN2 ,TIM1_CH3 ,COMP3_OUT , , ,TIM20_CH2 , , , ,QUADSPI1_BK2_IO1 , , , , ,EVENTOUT,ADC12_IN8
-PortC,PC3 , ,LPTIM1_ETR ,TIM1_CH4 ,SAI1_D1 , , ,TIM1_BKIN2 , , , ,QUADSPI1_BK2_IO2 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN9
-PortC,PC4 , , ,TIM1_ETR , ,I2C2_SCL , , ,USART1_TX , , ,QUADSPI1_BK2_IO3 , , , , ,EVENTOUT,ADC2_IN5
+PortC,PC1 , ,LPTIM1_OUT ,TIM1_CH2 , , , , , ,LPUART1_TX , ,QUADSPI_BK2_IO0 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN7
+PortC,PC2 , ,LPTIM1_IN2 ,TIM1_CH3 ,COMP3_OUT , , ,TIM20_CH2 , , , ,QUADSPI_BK2_IO1 , , , , ,EVENTOUT,ADC12_IN8
+PortC,PC3 , ,LPTIM1_ETR ,TIM1_CH4 ,SAI1_D1 , , ,TIM1_BKIN2 , , , ,QUADSPI_BK2_IO2 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN9
+PortC,PC4 , , ,TIM1_ETR , ,I2C2_SCL , , ,USART1_TX , , ,QUADSPI_BK2_IO3 , , , , ,EVENTOUT,ADC2_IN5
PortC,PC5 , , ,TIM15_BKIN ,SAI1_D3 , , ,TIM1_CH4N ,USART1_RX , , , , , ,HRTIM1_EEV10 , ,EVENTOUT,ADC2_IN11
PortC,PC6 , , ,TIM3_CH1 ,HRTIM1_EEV10 ,TIM8_CH1 , ,I2S2_MCK ,COMP6_OUT ,I2C4_SCL , , , , ,HRTIM1_CHF1 , ,EVENTOUT,
PortC,PC7 , , ,TIM3_CH2 ,HRTIM1_FLT5 ,TIM8_CH2 , ,I2S3_MCK ,COMP5_OUT ,I2C4_SDA , , , , ,HRTIM1_CHF2 , ,EVENTOUT,
@@ -51,11 +51,11 @@ PortC,PC15, , ,
PortD,PD0 , , , , , , ,TIM8_CH4N , , ,CAN1_RX , , ,FMC_D2 , , ,EVENTOUT,
PortD,PD1 , , , , ,TIM8_CH4 , ,TIM8_BKIN2 , , ,CAN1_TX , , ,FMC_D3 , , ,EVENTOUT,
PortD,PD2 , , ,TIM3_ETR , ,TIM8_BKIN ,UART5_RX , , , , , , , , , ,EVENTOUT,
-PortD,PD3 , , ,TIM2_CH1/TIM2_ETR , , , , ,USART2_CTS , , ,QUADSPI1_BK2_NCS , ,FMC_CLK , , ,EVENTOUT,
-PortD,PD4 , , ,TIM2_CH2 , , , , ,USART2_RTS_DE , , ,QUADSPI1_BK2_IO0 , ,FMC_NOE , , ,EVENTOUT,
-PortD,PD5 , , , , , , , ,USART2_TX , , ,QUADSPI1_BK2_IO1 , ,FMC_NWE , , ,EVENTOUT,
-PortD,PD6 , , ,TIM2_CH4 ,SAI1_D1 , , , ,USART2_RX , , ,QUADSPI1_BK2_IO2 , ,FMC_NWAIT ,SAI1_SD_A , ,EVENTOUT,
-PortD,PD7 , , ,TIM2_CH3 , , , , ,USART2_CK , , ,QUADSPI1_BK2_IO3 , ,FMC_NCE/FMC_NE1 , , ,EVENTOUT,
+PortD,PD3 , , ,TIM2_CH1/TIM2_ETR , , , , ,USART2_CTS , , ,QUADSPI_BK2_NCS , ,FMC_CLK , , ,EVENTOUT,
+PortD,PD4 , , ,TIM2_CH2 , , , , ,USART2_RTS_DE , , ,QUADSPI_BK2_IO0 , ,FMC_NOE , , ,EVENTOUT,
+PortD,PD5 , , , , , , , ,USART2_TX , , ,QUADSPI_BK2_IO1 , ,FMC_NWE , , ,EVENTOUT,
+PortD,PD6 , , ,TIM2_CH4 ,SAI1_D1 , , , ,USART2_RX , , ,QUADSPI_BK2_IO2 , ,FMC_NWAIT ,SAI1_SD_A , ,EVENTOUT,
+PortD,PD7 , , ,TIM2_CH3 , , , , ,USART2_CK , , ,QUADSPI_BK2_IO3 , ,FMC_NCE/FMC_NE1 , , ,EVENTOUT,
PortD,PD8 , , , , , , , ,USART3_TX , , , , ,FMC_D13 , , ,EVENTOUT,ADC4_IN12/ADC5_IN12
PortD,PD9 , , , , , , , ,USART3_RX , , , , ,FMC_D14 , , ,EVENTOUT,ADC4_IN13/ADC5_IN13
PortD,PD10, , , , , , , ,USART3_CK , , , , ,FMC_D15 , , ,EVENTOUT,ADC345_IN7
@@ -74,23 +74,23 @@ PortE,PE6 ,TRACED3 , ,
PortE,PE7 , , ,TIM1_ETR , , , , , , , , , ,FMC_D4 ,SAI1_SD_B , ,EVENTOUT,ADC3_IN4
PortE,PE8 , ,TIM5_CH3 ,TIM1_CH1N , , , , , , , , , ,FMC_D5 ,SAI1_SCK_B , ,EVENTOUT,ADC345_IN6
PortE,PE9 , ,TIM5_CH4 ,TIM1_CH1 , , , , , , , , , ,FMC_D6 ,SAI1_FS_B , ,EVENTOUT,ADC3_IN2
-PortE,PE10, , ,TIM1_CH2N , , , , , , , ,QUADSPI1_CLK , ,FMC_D7 ,SAI1_MCLK_B , ,EVENTOUT,ADC345_IN14
-PortE,PE11, , ,TIM1_CH2 , , ,SPI4_NSS , , , , ,QUADSPI1_BK1_NCS , ,FMC_D8 , , ,EVENTOUT,ADC345_IN15
-PortE,PE12, , ,TIM1_CH3N , , ,SPI4_SCK , , , , ,QUADSPI1_BK1_IO0 , ,FMC_D9 , , ,EVENTOUT,ADC345_IN16
-PortE,PE13, , ,TIM1_CH3 , , ,SPI4_MISO , , , , ,QUADSPI1_BK1_IO1 , ,FMC_D10 , , ,EVENTOUT,ADC3_IN3
-PortE,PE14, , ,TIM1_CH4 , , ,SPI4_MOSI ,TIM1_BKIN2 , , , ,QUADSPI1_BK1_IO2 , ,FMC_D11 , , ,EVENTOUT,ADC4_IN1
-PortE,PE15, , ,TIM1_BKIN , , , ,TIM1_CH4N ,USART3_RX , , ,QUADSPI1_BK1_IO3 , ,FMC_D12 , , ,EVENTOUT,ADC4_IN2
+PortE,PE10, , ,TIM1_CH2N , , , , , , , ,QUADSPI_CLK , ,FMC_D7 ,SAI1_MCLK_B , ,EVENTOUT,ADC345_IN14
+PortE,PE11, , ,TIM1_CH2 , , ,SPI4_NSS , , , , ,QUADSPI_BK1_NCS , ,FMC_D8 , , ,EVENTOUT,ADC345_IN15
+PortE,PE12, , ,TIM1_CH3N , , ,SPI4_SCK , , , , ,QUADSPI_BK1_IO0 , ,FMC_D9 , , ,EVENTOUT,ADC345_IN16
+PortE,PE13, , ,TIM1_CH3 , , ,SPI4_MISO , , , , ,QUADSPI_BK1_IO1 , ,FMC_D10 , , ,EVENTOUT,ADC3_IN3
+PortE,PE14, , ,TIM1_CH4 , , ,SPI4_MOSI ,TIM1_BKIN2 , , , ,QUADSPI_BK1_IO2 , ,FMC_D11 , , ,EVENTOUT,ADC4_IN1
+PortE,PE15, , ,TIM1_BKIN , , , ,TIM1_CH4N ,USART3_RX , , ,QUADSPI_BK1_IO3 , ,FMC_D12 , , ,EVENTOUT,ADC4_IN2
PortF,PF0 , , , , ,I2C2_SDA ,SPI2_NSS/I2S2_WS ,TIM1_CH3N , , , , , , , , ,EVENTOUT,ADC1_IN10
PortF,PF1 , , , , , ,SPI2_SCK/I2S2_CK , , , , , , , , , ,EVENTOUT,ADC2_IN10
PortF,PF2 , , ,TIM20_CH3 , ,I2C2_SMBA , , , , , , , ,FMC_A2 , , ,EVENTOUT,
PortF,PF3 , , ,TIM20_CH4 , ,I2C3_SCL , , , , , , , ,FMC_A3 , , ,EVENTOUT,
PortF,PF4 , , ,COMP1_OUT ,TIM20_CH1N ,I2C3_SDA , , , , , , , ,FMC_A4 , , ,EVENTOUT,
PortF,PF5 , , ,TIM20_CH2N , , , , , , , , , ,FMC_A5 , , ,EVENTOUT,
-PortF,PF6 , ,TIM5_ETR ,TIM4_CH4 ,SAI1_SD_B ,I2C2_SCL , ,TIM5_CH1 ,USART3_RTS , , ,QUADSPI1_BK1_IO3 , , , , ,EVENTOUT,
-PortF,PF7 , , ,TIM20_BKIN , , , ,TIM5_CH2 , , , ,QUADSPI1_BK1_IO2 , ,FMC_A1 ,SAI1_MCLK_B , ,EVENTOUT,
-PortF,PF8 , , ,TIM20_BKIN2 , , , ,TIM5_CH3 , , , ,QUADSPI1_BK1_IO0 , ,FMC_A24 ,SAI1_SCK_B , ,EVENTOUT,
-PortF,PF9 , , ,TIM20_BKIN ,TIM15_CH1 , ,SPI2_SCK ,TIM5_CH4 , , , ,QUADSPI1_BK1_IO1 , ,FMC_A25 ,SAI1_FS_B , ,EVENTOUT,
-PortF,PF10, , ,TIM20_BKIN2 ,TIM15_CH2 , ,SPI2_SCK , , , , ,QUADSPI1_CLK , ,FMC_A0 ,SAI1_D3 , ,EVENTOUT,
+PortF,PF6 , ,TIM5_ETR ,TIM4_CH4 ,SAI1_SD_B ,I2C2_SCL , ,TIM5_CH1 ,USART3_RTS , , ,QUADSPI_BK1_IO3 , , , , ,EVENTOUT,
+PortF,PF7 , , ,TIM20_BKIN , , , ,TIM5_CH2 , , , ,QUADSPI_BK1_IO2 , ,FMC_A1 ,SAI1_MCLK_B , ,EVENTOUT,
+PortF,PF8 , , ,TIM20_BKIN2 , , , ,TIM5_CH3 , , , ,QUADSPI_BK1_IO0 , ,FMC_A24 ,SAI1_SCK_B , ,EVENTOUT,
+PortF,PF9 , , ,TIM20_BKIN ,TIM15_CH1 , ,SPI2_SCK ,TIM5_CH4 , , , ,QUADSPI_BK1_IO1 , ,FMC_A25 ,SAI1_FS_B , ,EVENTOUT,
+PortF,PF10, , ,TIM20_BKIN2 ,TIM15_CH2 , ,SPI2_SCK , , , , ,QUADSPI_CLK , ,FMC_A0 ,SAI1_D3 , ,EVENTOUT,
PortF,PF11, , ,TIM20_ETR , , , , , , , , , ,FMC_NE4 , , ,EVENTOUT,
PortF,PF12, , ,TIM20_CH1 , , , , , , , , , ,FMC_A6 , , ,EVENTOUT,
PortF,PF13, , ,TIM20_CH2 , ,I2C4_SMBA , , , , , , , ,FMC_A7 , , ,EVENTOUT,
diff --git a/ports/stm32/boards/stm32h7xx_hal_conf_base.h b/ports/stm32/boards/stm32h7xx_hal_conf_base.h
index 670dee383f..1953ba020b 100644
--- a/ports/stm32/boards/stm32h7xx_hal_conf_base.h
+++ b/ports/stm32/boards/stm32h7xx_hal_conf_base.h
@@ -90,6 +90,7 @@
#include "stm32h7xx_hal_spi.h"
#include "stm32h7xx_hal_tim.h"
#include "stm32h7xx_hal_uart.h"
+#include "stm32h7xx_hal_uart_ex.h"
#include "stm32h7xx_hal_usart.h"
#include "stm32h7xx_hal_wwdg.h"
#include "stm32h7xx_ll_adc.h"
diff --git a/ports/stm32/boards/stm32n657_af.csv b/ports/stm32/boards/stm32n657_af.csv
new file mode 100644
index 0000000000..35e305a376
--- /dev/null
+++ b/ports/stm32/boards/stm32n657_af.csv
@@ -0,0 +1,42 @@
+Port ,Pin ,AF0 ,AF1 ,AF2 ,AF3 ,AF4 ,AF5 ,AF6 ,AF7 ,AF8 ,AF9 ,AF10 ,AF11 ,AF12 ,AF13 ,AF14 ,AF15 ,ADC
+ , ,SYS ,LPTIM1/TIM1/2/16/17,LPTIM3/PDM_SAI1/TIM3/4/5/12/15,I3C1/LPTIM2/3/LPUART1/OCTOSPI/TIM1/8,CEC/DCMI/I2C1/2/3/4/LPTIM1/2/SPI1/I2S1/TIM15/USART1,CEC/I3C1/LPTIM1/SPI1/I2S1/SPI2/I2S2/SPI3/I2S3/SPI4/5/6,I2C4/OCTOSPI/SAI1/SPI3/I2S3/SPI4/UART4/12/USART10/USB_PD,SDMMC1/SPI2/I2S2/SPI3/I2S3/SPI6/UART7/8/12/USART1/2/3/6/10/11,LPUART1/SAI2/SDMMC1/SPI6/UART4/5/8,FDCAN1/2/FMC[NAND16]/FMC[NORmux]/FMC[NOR_RAM]/OCTOSPI/SDMMC2/TIM13/14,CRS/FMC[NAND16]/OCTOSPI/SAI2/SDMMC2/TIM8/USB_,ETH[MII/RMII]/FMC[NAND16]/OCTOSPI/SDMMC2/UART7/9/USB_PD,FMC[NAND16]/FMC[NORmux]/FMC[NOR_RAM]/FMC[SDRAM_16bit]/SDMMC1,DCMI/FMC[NAND16]/FMC[NORmux]/FMC[NOR_RAM]/LPTIM5,LPTIM3/4/5/6/TIM2/UART5,SYS ,ADC
+PortA,PA0 , , , , , , , , , , , ,SDMMC2_CMD , , , , ,ADC12_INP0/ADC12_INN1
+PortA,PA3 , , , , , ,SPI5_NSS , , , , , , , , , , ,
+PortA,PA5 , , , , , , , , , , , , , , , , ,ADC2_INP18
+PortA,PA8 , , , , , , , , , , , , , , , , ,ADC12_INP5
+PortA,PA9 , , , , , , , , , , , , , , , , ,ADC12_INP10
+PortA,PA10, , , , , , , , , , , , , , , , ,ADC12_INP11/ADC12_INN10
+PortA,PA11, , , , , , , , ,UART4_RX , , , , , , , ,ADC12_INP12/ADC12_INN11
+PortA,PA12, , , , , , , , ,UART4_TX , , , , , , , ,ADC12_INP13/ADC12_INN12
+PortB,PB4 , , , , , , , , , , , ,SDMMC2_D3 , , , , ,
+PortB,PB8 , , , , , , , , , , , ,SDMMC2_D0 , , , , ,
+PortB,PB9 , , , , , , , , , , , ,SDMMC2_D2 , , , , ,
+PortB,PB10, ,TIM2_CH3 , , , , , ,USART3_TX , , , , , , , , ,
+PortB,PB11, ,TIM2_CH4 , , , , , ,USART3_RX , , , , , , , , ,
+PortC,PC8 , , , , , , , , , , ,SDMMC1_D0 , , , , , ,
+PortC,PC9 , , , , , , , , , , ,SDMMC1_D1 , , , , , ,
+PortC,PC10, , , , , , , , , , ,SDMMC1_D2 , , , , , ,
+PortC,PC11, , , , , , , , , , ,SDMMC1_D3 , , , , , ,
+PortC,PC12, , , , , , , , , , ,SDMMC1_CK , , , , , ,
+PortD,PD2 , , , , , , , , , , , ,SDMMC2_CK , , , , ,
+PortD,PD5 , , , , , , , ,USART2_TX , , , , , , , , ,
+PortD,PD6 , , , , ,TIM15_CH2 , , , , , , , , , , , ,
+PortD,PD8 , , , , , , , ,USART3_TX , , , , , , , , ,
+PortD,PD9 , , , , , , , ,USART3_RX , , , , , , , , ,
+PortD,PD13, , ,TIM4_CH2 , , , , , , , , , , , , , ,
+PortE,PE5 , , , , , , , ,USART1_TX , , , , , , , , ,
+PortE,PE6 , , , , , , , ,USART1_RX , , , , , , , , ,
+PortE,PE7 , , , , , , , , ,UART7_RX , , , , , , , ,
+PortE,PE8 , , , , , , , , ,UART7_TX , , , , , , , ,
+PortE,PE15, , , , , ,SPI5_SCK , , , , , , , , , , ,
+PortF,PF3 , , , , , , , ,USART2_RTS , , , , , , , , ,ADC1_INP16
+PortF,PF6 , , , , , , , ,USART2_RX , , , , , , , , ,
+PortG,PG0 , , ,TIM12_CH1 , , , , , , , , , , , , , ,
+PortG,PG1 , , , , , ,SPI5_MISO , , , , , , , , , , ,
+PortG,PG2 , , , , , ,SPI5_MOSI , , , , , , , , , , ,
+PortG,PG5 , , , , , , , ,USART2_CTS , , , , , , , , ,
+PortG,PG8 , , , , , , , , , , , ,SDMMC2_D1 , , , , ,
+PortG,PG12, ,TIM17_CH1 , , , , , , , , , , , , , , ,
+PortG,PG13, , ,TIM4_CH1 , , , , , , , , , , , , , ,
+PortG,PG15, , , , , , , , , , , , , , , , ,ADC12_INP7/ADC12_INN3
+PortC,PH2 , , , , , , , , , , ,SDMMC1_CMD , , , , , ,
diff --git a/ports/stm32/boards/stm32n657x0.ld b/ports/stm32/boards/stm32n657x0.ld
new file mode 100644
index 0000000000..242d113b30
--- /dev/null
+++ b/ports/stm32/boards/stm32n657x0.ld
@@ -0,0 +1,34 @@
+/*
+ GNU linker script for STM32N657x0
+
+ Note: upper 512k of SRAM2 is copied from external flash upon reset.
+*/
+
+/* Specify the memory areas */
+MEMORY
+{
+ FLEXRAM_S (xrw) : ORIGIN = 0x34000000, LENGTH = 80K
+ SRAM2_S_RAM (xrw) : ORIGIN = 0x34100000, LENGTH = 512K /* only use first half, second half may contain firmware */
+ SRAM2_S_FSBL (xrw) : ORIGIN = 0x34180400, LENGTH = 511K /* firmware loaded from SPI flash upon reset */
+ EXT_FLASH (rx) : ORIGIN = 0x70080000, LENGTH = 1536K
+}
+
+REGION_ALIAS("IRAM", FLEXRAM_S);
+REGION_ALIAS("RAM", SRAM2_S_RAM);
+REGION_ALIAS("FLASH", SRAM2_S_FSBL);
+REGION_ALIAS("FLASH_APP", EXT_FLASH);
+
+/* 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;
diff --git a/ports/stm32/boards/stm32n6xx_hal_conf_base.h b/ports/stm32/boards/stm32n6xx_hal_conf_base.h
new file mode 100644
index 0000000000..641a003d8b
--- /dev/null
+++ b/ports/stm32/boards/stm32n6xx_hal_conf_base.h
@@ -0,0 +1,215 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 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.
+ */
+#ifndef MICROPY_INCLUDED_STM32N6XX_HAL_CONF_BASE_H
+#define MICROPY_INCLUDED_STM32N6XX_HAL_CONF_BASE_H
+
+// Enable various HAL modules
+#define HAL_MODULE_ENABLED
+#define HAL_ADC_MODULE_ENABLED
+#define HAL_BSEC_MODULE_ENABLED
+#define HAL_CACHEAXI_MODULE_ENABLED
+#define HAL_CORTEX_MODULE_ENABLED
+#define HAL_CRC_MODULE_ENABLED
+#define HAL_CRYP_MODULE_ENABLED
+#define HAL_CSI_MODULE_ENABLED
+#define HAL_DCMI_MODULE_ENABLED
+#define HAL_DCMIPP_MODULE_ENABLED
+#define HAL_DMA_MODULE_ENABLED
+#define HAL_DMA2D_MODULE_ENABLED
+#define HAL_DTS_MODULE_ENABLED
+#define HAL_ETH_MODULE_ENABLED
+#define HAL_EXTI_MODULE_ENABLED
+#define HAL_FDCAN_MODULE_ENABLED
+#define HAL_GFXMMU_MODULE_ENABLED
+#define HAL_GFXTIM_MODULE_ENABLED
+#define HAL_GPIO_MODULE_ENABLED
+#define HAL_GPU2D_MODULE_ENABLED
+#define HAL_HASH_MODULE_ENABLED
+#define HAL_HCD_MODULE_ENABLED
+#define HAL_I2C_MODULE_ENABLED
+#define HAL_I3C_MODULE_ENABLED
+#define HAL_ICACHE_MODULE_ENABLED
+#define HAL_IRDA_MODULE_ENABLED
+#define HAL_IWDG_MODULE_ENABLED
+#define HAL_JPEG_MODULE_ENABLED
+#define HAL_LPTIM_MODULE_ENABLED
+#define HAL_LTDC_MODULE_ENABLED
+#define HAL_MCE_MODULE_ENABLED
+#define HAL_MDF_MODULE_ENABLED
+#define HAL_MDIOS_MODULE_ENABLED
+#define HAL_MMC_MODULE_ENABLED
+#define HAL_NAND_MODULE_ENABLED
+#define HAL_NOR_MODULE_ENABLED
+#define HAL_PCD_MODULE_ENABLED
+#define HAL_PKA_MODULE_ENABLED
+#define HAL_PSSI_MODULE_ENABLED
+#define HAL_PWR_MODULE_ENABLED
+#define HAL_RAMCFG_MODULE_ENABLED
+#define HAL_RCC_MODULE_ENABLED
+#define HAL_RIF_MODULE_ENABLED
+#define HAL_RNG_MODULE_ENABLED
+#define HAL_RTC_MODULE_ENABLED
+#define HAL_SAI_MODULE_ENABLED
+#define HAL_SD_MODULE_ENABLED
+#define HAL_SDRAM_MODULE_ENABLED
+#define HAL_SMARTCARD_MODULE_ENABLED
+#define HAL_SMBUS_MODULE_ENABLED
+#define HAL_SPDIFRX_MODULE_ENABLED
+#define HAL_SPI_MODULE_ENABLED
+#define HAL_SRAM_MODULE_ENABLED
+#define HAL_TIM_MODULE_ENABLED
+#define HAL_UART_MODULE_ENABLED
+#define HAL_USART_MODULE_ENABLED
+#define HAL_WWDG_MODULE_ENABLED
+#define HAL_XSPI_MODULE_ENABLED
+
+// Oscillator values in Hz
+#define HSI_VALUE (64000000UL)
+#define LSI_VALUE (32000UL)
+#define MSI_VALUE (4000000UL)
+
+// SysTick has the highest priority
+#define TICK_INT_PRIORITY (0x00)
+
+// Miscellaneous HAL settings
+#define VDD_VALUE 3300UL
+#define USE_RTOS 0
+#define USE_SD_TRANSCEIVER 0
+#define USE_SPI_CRC 1
+
+// Disable dynamic callback registration
+#define USE_HAL_ADC_REGISTER_CALLBACKS 0U /* ADC register callback disabled */
+#define USE_HAL_CACHEAXI_REGISTER_CALLBACKS 0U /* CACHEAXI register callback disabled */
+#define USE_HAL_CRYP_REGISTER_CALLBACKS 0U /* CRYP register callback disabled */
+#define USE_HAL_DCMI_REGISTER_CALLBACKS 0U /* DCMI register callback disabled */
+#define USE_HAL_DCMIPP_REGISTER_CALLBACKS 0U /* DCMIPP register callback disabled */
+#define USE_HAL_DMA2D_REGISTER_CALLBACKS 0U /* DMA2D register callback disabled */
+#define USE_HAL_DTS_REGISTER_CALLBACKS 0U /* DTS register callback disabled */
+#define USE_HAL_ETH_REGISTER_CALLBACKS 0U /* ETH register callback disabled */
+#define USE_HAL_FDCAN_REGISTER_CALLBACKS 0U /* FDCAN register callback disabled */
+#define USE_HAL_GFXMMU_REGISTER_CALLBACKS 0U /* GFXMMU register callback disabled */
+#define USE_HAL_GFXTIM_REGISTER_CALLBACKS 0U /* GFXTIM register callback disabled */
+#define USE_HAL_HASH_REGISTER_CALLBACKS 0U /* HASH register callback disabled */
+#define USE_HAL_HCD_REGISTER_CALLBACKS 0U /* HCD register callback disabled */
+#define USE_HAL_I2C_REGISTER_CALLBACKS 0U /* I2C register callback disabled */
+#define USE_HAL_I3C_REGISTER_CALLBACKS 0U /* I3C register callback disabled */
+#define USE_HAL_IWDG_REGISTER_CALLBACKS 0U /* IWDG register callback disabled */
+#define USE_HAL_IRDA_REGISTER_CALLBACKS 0U /* IRDA register callback disabled */
+#define USE_HAL_LPTIM_REGISTER_CALLBACKS 0U /* LPTIM register callback disabled */
+#define USE_HAL_LTDC_REGISTER_CALLBACKS 0U /* LTDC register callback disabled */
+#define USE_HAL_MCE_REGISTER_CALLBACKS 0U /* MCE register callback disabled */
+#define USE_HAL_MDF_REGISTER_CALLBACKS 0U /* MDF register callback disabled */
+#define USE_HAL_MMC_REGISTER_CALLBACKS 0U /* MMC register callback disabled */
+#define USE_HAL_NAND_REGISTER_CALLBACKS 0U /* NAND register callback disabled */
+#define USE_HAL_NOR_REGISTER_CALLBACKS 0U /* NOR register callback disabled */
+#define USE_HAL_PCD_REGISTER_CALLBACKS 0U /* PCD register callback disabled */
+#define USE_HAL_PKA_REGISTER_CALLBACKS 0U /* PKA register callback disabled */
+#define USE_HAL_PSSI_REGISTER_CALLBACKS 0U /* PSSI register callback disabled */
+#define USE_HAL_RAMCFG_REGISTER_CALLBACKS 0U /* RAMCFG register callback disabled */
+#define USE_HAL_RNG_REGISTER_CALLBACKS 0U /* RNG register callback disabled */
+#define USE_HAL_RTC_REGISTER_CALLBACKS 0U /* RTC register callback disabled */
+#define USE_HAL_SAI_REGISTER_CALLBACKS 0U /* SAI register callback disabled */
+#define USE_HAL_SD_REGISTER_CALLBACKS 0U /* SD register callback disabled */
+#define USE_HAL_SDRAM_REGISTER_CALLBACKS 0U /* SDRAM register callback disabled */
+#define USE_HAL_SMARTCARD_REGISTER_CALLBACKS 0U /* SMARTCARD register callback disabled */
+#define USE_HAL_SMBUS_REGISTER_CALLBACKS 0U /* SMBUS register callback disabled */
+#define USE_HAL_SPDIFRX_REGISTER_CALLBACKS 0U /* SPDIFRX register callback disabled */
+#define USE_HAL_SPI_REGISTER_CALLBACKS 0U /* SPI register callback disabled */
+#define USE_HAL_SRAM_REGISTER_CALLBACKS 0U /* SRAM register callback disabled */
+#define USE_HAL_TIM_REGISTER_CALLBACKS 0U /* TIM register callback disabled */
+#define USE_HAL_UART_REGISTER_CALLBACKS 0U /* UART register callback disabled */
+#define USE_HAL_USART_REGISTER_CALLBACKS 0U /* USART register callback disabled */
+#define USE_HAL_WWDG_REGISTER_CALLBACKS 0U /* WWDG register callback disabled */
+#define USE_HAL_XSPI_REGISTER_CALLBACKS 0U /* XSPI register callback disabled */
+
+// Include various HAL modules for convenience
+#include "stm32n6xx_hal_rcc.h"
+#include "stm32n6xx_hal_gpio.h"
+#include "stm32n6xx_hal_rif.h"
+#include "stm32n6xx_hal_dma.h"
+#include "stm32n6xx_hal_cacheaxi.h"
+#include "stm32n6xx_hal_cortex.h"
+#include "stm32n6xx_hal_adc.h"
+#include "stm32n6xx_hal_bsec.h"
+#include "stm32n6xx_hal_crc.h"
+#include "stm32n6xx_hal_cryp.h"
+#include "stm32n6xx_hal_dcmi.h"
+#include "stm32n6xx_hal_dcmipp.h"
+#include "stm32n6xx_hal_dma2d.h"
+#include "stm32n6xx_hal_dts.h"
+#include "stm32n6xx_hal_eth.h"
+#include "stm32n6xx_hal_exti.h"
+#include "stm32n6xx_hal_fdcan.h"
+#include "stm32n6xx_hal_gfxmmu.h"
+#include "stm32n6xx_hal_gfxtim.h"
+#include "stm32n6xx_hal_gpio.h"
+#include "stm32n6xx_hal_gpu2d.h"
+#include "stm32n6xx_hal_hash.h"
+#include "stm32n6xx_hal_hcd.h"
+#include "stm32n6xx_hal_i2c.h"
+#include "stm32n6xx_hal_i3c.h"
+#include "stm32n6xx_hal_icache.h"
+#include "stm32n6xx_hal_irda.h"
+#include "stm32n6xx_hal_iwdg.h"
+#include "stm32n6xx_hal_jpeg.h"
+#include "stm32n6xx_hal_lptim.h"
+#include "stm32n6xx_hal_ltdc.h"
+#include "stm32n6xx_hal_mce.h"
+#include "stm32n6xx_hal_mdf.h"
+#include "stm32n6xx_hal_mdios.h"
+#include "stm32n6xx_hal_mmc.h"
+#include "stm32n6xx_hal_nand.h"
+#include "stm32n6xx_hal_nor.h"
+#include "stm32n6xx_hal_nand.h"
+#include "stm32n6xx_hal_pcd.h"
+#include "stm32n6xx_hal_pka.h"
+#include "stm32n6xx_hal_pssi.h"
+#include "stm32n6xx_hal_pwr.h"
+#include "stm32n6xx_hal_ramcfg.h"
+#include "stm32n6xx_hal_rng.h"
+#include "stm32n6xx_hal_rtc.h"
+#include "stm32n6xx_hal_sai.h"
+#include "stm32n6xx_hal_sd.h"
+#include "stm32n6xx_hal_sdram.h"
+#include "stm32n6xx_hal_smartcard.h"
+#include "stm32n6xx_hal_smbus.h"
+#include "stm32n6xx_hal_spdifrx.h"
+#include "stm32n6xx_hal_spi.h"
+#include "stm32n6xx_hal_sram.h"
+#include "stm32n6xx_hal_tim.h"
+#include "stm32n6xx_hal_uart.h"
+#include "stm32n6xx_hal_usart.h"
+#include "stm32n6xx_hal_wwdg.h"
+#include "stm32n6xx_hal_xspi.h"
+#include "stm32n6xx_ll_lpuart.h"
+#include "stm32n6xx_ll_pwr.h"
+#include "stm32n6xx_ll_rtc.h"
+#include "stm32n6xx_ll_usart.h"
+
+// HAL parameter assertions are disabled
+#define assert_param(expr) ((void)0)
+
+#endif // MICROPY_INCLUDED_STM32N6XX_HAL_CONF_BASE_H
diff --git a/ports/stm32/cyw43_configport.h b/ports/stm32/cyw43_configport.h
index c26c554761..6528dbc625 100644
--- a/ports/stm32/cyw43_configport.h
+++ b/ports/stm32/cyw43_configport.h
@@ -27,14 +27,9 @@
#ifndef MICROPY_INCLUDED_STM32_CYW43_CONFIGPORT_H
#define MICROPY_INCLUDED_STM32_CYW43_CONFIGPORT_H
-// The board-level config will be included here, so it can set some CYW43 values.
-#include "py/mpconfig.h"
-#include "py/mperrno.h"
-#include "py/mphal.h"
-#include "extmod/modnetwork.h"
-#include "extmod/mpbthci.h"
#include "extint.h"
-#include "pendsv.h"
+#include "extmod/mpbthci.h"
+#include "extmod/cyw43_config_common.h"
#include "sdio.h"
#define CYW43_USE_SPI (0)
@@ -62,50 +57,12 @@
#define CYW43_BT_UART_BAUDRATE_DOWNLOAD_FIRMWARE MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE
#endif
-#define CYW43_IOCTL_TIMEOUT_US (1000000)
-#define CYW43_SLEEP_MAX (50)
-#define CYW43_NETUTILS (1)
#define CYW43_CLEAR_SDIO_INT (1)
-#define CYW43_EPERM MP_EPERM // Operation not permitted
-#define CYW43_EIO MP_EIO // I/O error
-#define CYW43_EINVAL MP_EINVAL // Invalid argument
-#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out
-
-#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER
-#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT
-#define CYW43_THREAD_LOCK_CHECK
-
-#define CYW43_HOST_NAME mod_network_hostname_data
-
#define CYW43_SDPCM_SEND_COMMON_WAIT __WFI();
#define CYW43_DO_IOCTL_WAIT __WFI();
#define CYW43_HAL_UART_READCHAR_BLOCKING_WAIT __WFI()
-#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a)
-
-#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT
-#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT
-#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE
-#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP
-#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN
-
-#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0
-#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR
-
-#define cyw43_hal_ticks_us mp_hal_ticks_us
-#define cyw43_hal_ticks_ms mp_hal_ticks_ms
-
-#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t
-#define cyw43_hal_pin_config mp_hal_pin_config
-#define cyw43_hal_pin_read mp_hal_pin_read
-#define cyw43_hal_pin_low mp_hal_pin_low
-#define cyw43_hal_pin_high mp_hal_pin_high
-
-#define cyw43_hal_get_mac mp_hal_get_mac
-#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii
-#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac
-
#define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate
#define cyw43_hal_uart_write mp_bluetooth_hci_uart_write
#define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar
@@ -132,24 +89,6 @@
#define CYW43_PIN_RFSW_SELECT pyb_pin_WL_GPIO_1
#endif
-#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func)
-
-void cyw43_post_poll_hook(void);
-
-static inline void cyw43_delay_us(uint32_t us) {
- uint32_t start = mp_hal_ticks_us();
- while (mp_hal_ticks_us() - start < us) {
- }
-}
-
-static inline void cyw43_delay_ms(uint32_t ms) {
- uint32_t us = ms * 1000;
- uint32_t start = mp_hal_ticks_us();
- while (mp_hal_ticks_us() - start < us) {
- MICROPY_EVENT_POLL_HOOK;
- }
-}
-
static inline void cyw43_hal_pin_config_irq_falling(cyw43_hal_pin_obj_t pin, int enable) {
if (enable) {
extint_set(pin, GPIO_MODE_IT_FALLING);
@@ -184,6 +123,4 @@ static inline int cyw43_sdio_transfer_cmd53(bool write, uint32_t block_size, uin
return sdio_transfer_cmd53(write, block_size, arg, len, buf);
}
-#define CYW43_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK
-
#endif // MICROPY_INCLUDED_STM32_CYW43_CONFIGPORT_H
diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c
index df8a408cbf..c252770740 100644
--- a/ports/stm32/dma.c
+++ b/ports/stm32/dma.c
@@ -81,7 +81,7 @@ typedef union {
struct _dma_descr_t {
#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
DMA_Stream_TypeDef *instance;
- #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
DMA_Channel_TypeDef *instance;
#else
#error "Unsupported Processor"
@@ -93,7 +93,7 @@ struct _dma_descr_t {
// Default parameters to dma_init() shared by spi and i2c; Channel and Direction
// vary depending on the peripheral instance so they get passed separately
-#if defined(STM32H5)
+#if defined(STM32H5) || defined(STM32N6)
static const DMA_InitTypeDef dma_init_struct_spi_i2c = {
.Request = 0, // set by dma_init_handle
.BlkHWRequest = DMA_BREQ_SINGLE_BURST,
@@ -157,7 +157,7 @@ static const DMA_InitTypeDef dma_init_struct_i2s = {
};
#endif
-#if ENABLE_SDIO && !defined(STM32H5) && !defined(STM32H7)
+#if ENABLE_SDIO && !defined(STM32H5) && !defined(STM32H7) && !defined(STM32N6)
// Parameters to dma_init() for SDIO tx and rx.
static const DMA_InitTypeDef dma_init_struct_sdio = {
#if defined(STM32F4) || defined(STM32F7)
@@ -735,9 +735,13 @@ const dma_descr_t dma_SPI_1_RX = { GPDMA1_Channel0, GPDMA1_REQUEST_SPI1_RX, dma_
const dma_descr_t dma_SPI_1_TX = { GPDMA1_Channel1, GPDMA1_REQUEST_SPI1_TX, dma_id_1, &dma_init_struct_spi_i2c };
const dma_descr_t dma_SPI_2_RX = { GPDMA1_Channel2, GPDMA1_REQUEST_SPI2_RX, dma_id_2, &dma_init_struct_spi_i2c };
const dma_descr_t dma_SPI_2_TX = { GPDMA1_Channel3, GPDMA1_REQUEST_SPI2_TX, dma_id_3, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_3_RX = { GPDMA1_Channel4, GPDMA1_REQUEST_SPI3_RX, dma_id_4, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_3_TX = { GPDMA1_Channel5, GPDMA1_REQUEST_SPI3_TX, dma_id_5, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_4_RX = { GPDMA1_Channel6, GPDMA1_REQUEST_SPI4_RX, dma_id_6, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_4_TX = { GPDMA1_Channel7, GPDMA1_REQUEST_SPI4_TX, dma_id_7, &dma_init_struct_spi_i2c };
#if MICROPY_HW_ENABLE_DAC
-const dma_descr_t dma_DAC_1_TX = { GPDMA1_Channel4, GPDMA1_REQUEST_DAC1_CH1, dma_id_4, &dma_init_struct_dac };
-const dma_descr_t dma_DAC_2_TX = { GPDMA1_Channel5, GPDMA1_REQUEST_DAC1_CH2, dma_id_5, &dma_init_struct_dac };
+const dma_descr_t dma_DAC_1_TX = { GPDMA2_Channel0, GPDMA1_REQUEST_DAC1_CH1, dma_id_8, &dma_init_struct_dac };
+const dma_descr_t dma_DAC_2_TX = { GPDMA2_Channel1, GPDMA1_REQUEST_DAC1_CH2, dma_id_9, &dma_init_struct_dac };
#endif
static const uint8_t dma_irqn[NSTREAM] = {
@@ -826,6 +830,46 @@ static const uint8_t dma_irqn[NSTREAM] = {
DMA2_Stream7_IRQn,
};
+#elif defined(STM32N6)
+
+#define NCONTROLLERS (1)
+#define NSTREAMS_PER_CONTROLLER (16)
+#define NSTREAM (NCONTROLLERS * NSTREAMS_PER_CONTROLLER)
+
+#define DMA_SUB_INSTANCE_AS_UINT8(dma_channel) (dma_channel)
+
+#define DMA1_ENABLE_MASK (0xffff) // Bits in dma_enable_mask corresponding to GPDMA1
+
+const dma_descr_t dma_SPI_1_RX = { GPDMA1_Channel0, GPDMA1_REQUEST_SPI1_RX, dma_id_0, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_1_TX = { GPDMA1_Channel1, GPDMA1_REQUEST_SPI1_TX, dma_id_1, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_2_RX = { GPDMA1_Channel2, GPDMA1_REQUEST_SPI2_RX, dma_id_2, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_2_TX = { GPDMA1_Channel3, GPDMA1_REQUEST_SPI2_TX, dma_id_3, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_3_RX = { GPDMA1_Channel4, GPDMA1_REQUEST_SPI3_RX, dma_id_4, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_3_TX = { GPDMA1_Channel5, GPDMA1_REQUEST_SPI3_TX, dma_id_5, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_4_RX = { GPDMA1_Channel6, GPDMA1_REQUEST_SPI4_RX, dma_id_6, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_4_TX = { GPDMA1_Channel7, GPDMA1_REQUEST_SPI4_TX, dma_id_7, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_5_RX = { GPDMA1_Channel8, GPDMA1_REQUEST_SPI5_RX, dma_id_8, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_5_TX = { GPDMA1_Channel9, GPDMA1_REQUEST_SPI5_TX, dma_id_9, &dma_init_struct_spi_i2c };
+
+static const uint8_t dma_irqn[NSTREAM] = {
+ GPDMA1_Channel0_IRQn,
+ GPDMA1_Channel1_IRQn,
+ GPDMA1_Channel2_IRQn,
+ GPDMA1_Channel3_IRQn,
+ GPDMA1_Channel4_IRQn,
+ GPDMA1_Channel5_IRQn,
+ GPDMA1_Channel6_IRQn,
+ GPDMA1_Channel7_IRQn,
+ GPDMA1_Channel8_IRQn,
+ GPDMA1_Channel9_IRQn,
+ GPDMA1_Channel10_IRQn,
+ GPDMA1_Channel11_IRQn,
+ GPDMA1_Channel12_IRQn,
+ GPDMA1_Channel13_IRQn,
+ GPDMA1_Channel14_IRQn,
+ GPDMA1_Channel15_IRQn,
+};
+
#endif
static DMA_HandleTypeDef *dma_handle[NSTREAM] = {NULL};
@@ -849,6 +893,10 @@ volatile dma_idle_count_t dma_idle;
#define __HAL_RCC_DMA2_CLK_ENABLE __HAL_RCC_GPDMA2_CLK_ENABLE
#define __HAL_RCC_DMA1_CLK_DISABLE __HAL_RCC_GPDMA1_CLK_DISABLE
#define __HAL_RCC_DMA2_CLK_DISABLE __HAL_RCC_GPDMA2_CLK_DISABLE
+#elif defined(STM32N6)
+#define DMA1_IS_CLK_ENABLED() (__HAL_RCC_GPDMA1_IS_CLK_ENABLED())
+#define __HAL_RCC_DMA1_CLK_ENABLE __HAL_RCC_GPDMA1_CLK_ENABLE
+#define __HAL_RCC_DMA1_CLK_DISABLE __HAL_RCC_GPDMA1_CLK_DISABLE
#else
#define DMA1_IS_CLK_ENABLED() ((RCC->AHB1ENR & RCC_AHB1ENR_DMA1EN) != 0)
#define DMA2_IS_CLK_ENABLED() ((RCC->AHB1ENR & RCC_AHB1ENR_DMA2EN) != 0)
@@ -1181,10 +1229,11 @@ void DMA2_Channel8_IRQHandler(void) {
}
#endif
-#elif defined(STM32H5)
+#elif defined(STM32H5) || defined(STM32N6)
#define DEFINE_IRQ_HANDLER(periph, channel, id) \
void GPDMA##periph##_Channel##channel##_IRQHandler(void) { \
+ MP_STATIC_ASSERT(GPDMA##periph##_Channel##channel##_IRQn > 0); \
IRQ_ENTER(GPDMA##periph##_Channel##channel##_IRQn); \
if (dma_handle[id] != NULL) { \
HAL_DMA_IRQHandler(dma_handle[id]); \
@@ -1200,6 +1249,7 @@ DEFINE_IRQ_HANDLER(1, 4, dma_id_4)
DEFINE_IRQ_HANDLER(1, 5, dma_id_5)
DEFINE_IRQ_HANDLER(1, 6, dma_id_6)
DEFINE_IRQ_HANDLER(1, 7, dma_id_7)
+#if defined(STM32H5)
DEFINE_IRQ_HANDLER(2, 0, dma_id_8)
DEFINE_IRQ_HANDLER(2, 1, dma_id_9)
DEFINE_IRQ_HANDLER(2, 2, dma_id_10)
@@ -1208,6 +1258,16 @@ DEFINE_IRQ_HANDLER(2, 4, dma_id_12)
DEFINE_IRQ_HANDLER(2, 5, dma_id_13)
DEFINE_IRQ_HANDLER(2, 6, dma_id_14)
DEFINE_IRQ_HANDLER(2, 7, dma_id_15)
+#else
+DEFINE_IRQ_HANDLER(1, 8, dma_id_8)
+DEFINE_IRQ_HANDLER(1, 9, dma_id_9)
+DEFINE_IRQ_HANDLER(1, 10, dma_id_10)
+DEFINE_IRQ_HANDLER(1, 11, dma_id_11)
+DEFINE_IRQ_HANDLER(1, 12, dma_id_12)
+DEFINE_IRQ_HANDLER(1, 13, dma_id_13)
+DEFINE_IRQ_HANDLER(1, 14, dma_id_14)
+DEFINE_IRQ_HANDLER(1, 15, dma_id_15)
+#endif
#elif defined(STM32L0)
@@ -1420,7 +1480,7 @@ void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint3
dma->Instance = dma_descr->instance;
dma->Init = *dma_descr->init;
dma->Init.Direction = dir;
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
dma->Init.Request = dma_descr->sub_instance;
#else
#if !defined(STM32F0) && !defined(STM32L1)
@@ -1428,7 +1488,7 @@ void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint3
#endif
#endif
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
// Configure src/dest settings based on the DMA direction.
if (dir == DMA_MEMORY_TO_PERIPH) {
dma->Init.SrcInc = DMA_SINC_INCREMENTED;
@@ -1459,13 +1519,24 @@ void dma_init(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir
dma_enable_clock(dma_id);
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
// Always reset and configure the H7 and G0/G4/H7/L0/L4/WB/WL DMA peripheral
// (dma->State is set to HAL_DMA_STATE_RESET by memset above)
// TODO: understand how L0/L4 DMA works so this is not needed
HAL_DMA_DeInit(dma);
dma->Parent = data; // HAL_DMA_DeInit may clear Parent, so set it again
HAL_DMA_Init(dma);
+
+ #if defined(STM32N6)
+ // Configure security attributes.
+ HAL_DMA_ConfigChannelAttributes(dma, DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC | DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC);
+ // Configure data handling.
+ DMA_DataHandlingConfTypeDef config;
+ config.DataExchange = DMA_EXCHANGE_NONE;
+ config.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
+ HAL_DMAEx_ConfigDataHandling(dma, &config);
+ #endif
+
NVIC_SetPriority(IRQn_NONNEG(dma_irqn[dma_id]), IRQ_PRI_DMA);
#else
// if this stream was previously configured for this channel/request and direction then we
@@ -1736,7 +1807,7 @@ void dma_nohal_start(const dma_descr_t *descr, uint32_t src_addr, uint32_t dst_a
dma->CCR |= DMA_CCR_EN;
}
-#elif defined(STM32G0) || defined(STM32WB) || defined(STM32WL)
+#elif defined(STM32G0) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
// These functions are currently not implemented or needed for this MCU.
diff --git a/ports/stm32/dma.h b/ports/stm32/dma.h
index 7ba04baf52..f05b22b5d0 100644
--- a/ports/stm32/dma.h
+++ b/ports/stm32/dma.h
@@ -147,6 +147,19 @@ extern const dma_descr_t dma_I2C_4_RX;
extern const dma_descr_t dma_SPI_SUBGHZ_TX;
extern const dma_descr_t dma_SPI_SUBGHZ_RX;
+#elif defined(STM32N6)
+
+extern const dma_descr_t dma_SPI_1_RX;
+extern const dma_descr_t dma_SPI_1_TX;
+extern const dma_descr_t dma_SPI_2_RX;
+extern const dma_descr_t dma_SPI_2_TX;
+extern const dma_descr_t dma_SPI_3_RX;
+extern const dma_descr_t dma_SPI_3_TX;
+extern const dma_descr_t dma_SPI_4_RX;
+extern const dma_descr_t dma_SPI_4_TX;
+extern const dma_descr_t dma_SPI_5_RX;
+extern const dma_descr_t dma_SPI_5_TX;
+
#endif
// API that configures the DMA via the HAL.
diff --git a/ports/stm32/extint.c b/ports/stm32/extint.c
index 5b5658809b..9c3c243253 100644
--- a/ports/stm32/extint.c
+++ b/ports/stm32/extint.c
@@ -92,7 +92,7 @@
#define EXTI_SWIER_BB(line) (*(__IO uint32_t *)(PERIPH_BB_BASE + ((EXTI_OFFSET + offsetof(EXTI_TypeDef, SWIER)) * 32) + ((line) * 4)))
#endif
-#if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+#if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
// The L4 MCU supports 40 Events/IRQs lines of the type configurable and direct.
// Here we only support configurable line types. Details, see page 330 of RM0351, Rev 1.
// The USB_FS_WAKUP event is a direct type and there is no support for it.
@@ -170,7 +170,7 @@ static const uint8_t nvic_irq_channel[EXTI_NUM_VECTORS] = {
ADC1_COMP_IRQn,
#endif
- #elif defined(STM32H5)
+ #elif defined(STM32N6) || defined(STM32H5)
EXTI0_IRQn,
EXTI1_IRQn,
@@ -189,6 +189,13 @@ static const uint8_t nvic_irq_channel[EXTI_NUM_VECTORS] = {
EXTI14_IRQn,
EXTI15_IRQn,
+ #if defined(STM32N6)
+ 0,
+ RTC_S_IRQn,
+ RTC_IRQn,
+ TAMP_IRQn,
+ #endif
+
#else
EXTI0_IRQn, EXTI1_IRQn, EXTI2_IRQn, EXTI3_IRQn, EXTI4_IRQn,
@@ -307,7 +314,7 @@ void EXTI15_10_IRQHandler(void) {
IRQ_EXIT(EXTI15_10_IRQn);
}
-#elif defined(STM32H5)
+#elif defined(STM32H5) || defined(STM32N6)
DEFINE_EXTI_IRQ_HANDLER(0)
DEFINE_EXTI_IRQ_HANDLER(1)
@@ -440,10 +447,10 @@ void extint_register_pin(const machine_pin_obj_t *pin, uint32_t mode, bool hard_
#if !defined(STM32H5) && !defined(STM32WB) && !defined(STM32WL)
__HAL_RCC_SYSCFG_CLK_ENABLE();
#endif
- #if defined(STM32G0) || defined(STM32H5)
+ #if defined(STM32G0) || defined(STM32H5) || defined(STM32N6)
EXTI->EXTICR[line >> 2] =
- (EXTI->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03))))
- | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (4 * (line & 0x03)));
+ (EXTI->EXTICR[line >> 2] & ~(0xff << (8 * (line & 0x03))))
+ | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (8 * (line & 0x03)));
#else
SYSCFG->EXTICR[line >> 2] =
(SYSCFG->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03))))
@@ -480,13 +487,13 @@ void extint_set(const machine_pin_obj_t *pin, uint32_t mode) {
pyb_extint_callback_arg[line] = MP_OBJ_FROM_PTR(pin);
// Route the GPIO to EXTI
- #if !defined(STM32H5) && !defined(STM32WB) && !defined(STM32WL)
+ #if !defined(STM32H5) && !defined(STM32N6) && !defined(STM32WB) && !defined(STM32WL)
__HAL_RCC_SYSCFG_CLK_ENABLE();
#endif
- #if defined(STM32G0) || defined(STM32H5)
+ #if defined(STM32G0) || defined(STM32H5) || defined(STM32N6)
EXTI->EXTICR[line >> 2] =
- (EXTI->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03))))
- | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (4 * (line & 0x03)));
+ (EXTI->EXTICR[line >> 2] & ~(0xff << (8 * (line & 0x03))))
+ | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (8 * (line & 0x03)));
#else
SYSCFG->EXTICR[line >> 2] =
(SYSCFG->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03))))
@@ -526,7 +533,7 @@ void extint_enable(uint line) {
if (pyb_extint_mode[line] == EXTI_Mode_Interrupt) {
#if defined(STM32H7)
EXTI_D1->IMR1 |= (1 << line);
- #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WB) || defined(STM32WL)
+ #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
EXTI->IMR1 |= (1 << line);
#else
EXTI->IMR |= (1 << line);
@@ -534,7 +541,7 @@ void extint_enable(uint line) {
} else {
#if defined(STM32H7)
EXTI_D1->EMR1 |= (1 << line);
- #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WB) || defined(STM32WL)
+ #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
EXTI->EMR1 |= (1 << line);
#else
EXTI->EMR |= (1 << line);
@@ -560,7 +567,7 @@ void extint_disable(uint line) {
#if defined(STM32H7)
EXTI_D1->IMR1 &= ~(1 << line);
EXTI_D1->EMR1 &= ~(1 << line);
- #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WB) || defined(STM32WL)
+ #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
EXTI->IMR1 &= ~(1 << line);
EXTI->EMR1 &= ~(1 << line);
#else
@@ -582,7 +589,7 @@ void extint_swint(uint line) {
return;
}
// we need 0 to 1 transition to trigger the interrupt
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
EXTI->SWIER1 &= ~(1 << line);
EXTI->SWIER1 |= (1 << line);
#else
@@ -662,7 +669,7 @@ static MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_swint_obj, extint_obj_swint);
static mp_obj_t extint_regs(void) {
const mp_print_t *print = &mp_plat_print;
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
mp_printf(print, "EXTI_IMR1 %08x\n", (unsigned int)EXTI->IMR1);
mp_printf(print, "EXTI_IMR2 %08x\n", (unsigned int)EXTI->IMR2);
mp_printf(print, "EXTI_EMR1 %08x\n", (unsigned int)EXTI->EMR1);
@@ -673,7 +680,7 @@ static mp_obj_t extint_regs(void) {
mp_printf(print, "EXTI_FTSR2 %08x\n", (unsigned int)EXTI->FTSR2);
mp_printf(print, "EXTI_SWIER1 %08x\n", (unsigned int)EXTI->SWIER1);
mp_printf(print, "EXTI_SWIER2 %08x\n", (unsigned int)EXTI->SWIER2);
- #if defined(STM32G0) || defined(STM32H5)
+ #if defined(STM32G0) || defined(STM32N6) || defined(STM32H5)
mp_printf(print, "EXTI_RPR1 %08x\n", (unsigned int)EXTI->RPR1);
mp_printf(print, "EXTI_FPR1 %08x\n", (unsigned int)EXTI->FPR1);
mp_printf(print, "EXTI_RPR2 %08x\n", (unsigned int)EXTI->RPR2);
diff --git a/ports/stm32/extint.h b/ports/stm32/extint.h
index d5abb04d65..801dcf62b5 100644
--- a/ports/stm32/extint.h
+++ b/ports/stm32/extint.h
@@ -46,7 +46,7 @@
#if defined(STM32F0) || defined(STM32G4) || defined(STM32L1) || defined(STM32L4) || defined(STM32WL)
#define EXTI_RTC_TIMESTAMP (19)
#define EXTI_RTC_WAKEUP (20)
-#elif defined(STM32H5)
+#elif defined(STM32H5) || defined(STM32N6)
#define EXTI_RTC_WAKEUP (17)
#define EXTI_RTC_TAMP (19)
#elif defined(STM32H7) || defined(STM32WB)
@@ -55,8 +55,6 @@
#elif defined(STM32G0)
#define EXTI_RTC_WAKEUP (19)
#define EXTI_RTC_TIMESTAMP (21)
-#elif defined(STM32H5)
-#define EXTI_RTC_WAKEUP (17)
#else
#define EXTI_RTC_TIMESTAMP (21)
#define EXTI_RTC_WAKEUP (22)
diff --git a/ports/stm32/flash.c b/ports/stm32/flash.c
index 7668b53a43..85bcee5a97 100644
--- a/ports/stm32/flash.c
+++ b/ports/stm32/flash.c
@@ -30,6 +30,8 @@
#include "py/mphal.h"
#include "flash.h"
+#if !defined(STM32N6)
+
#if defined(STM32F0)
#define FLASH_FLAG_ALL_ERRORS (FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGERR)
@@ -509,3 +511,5 @@ int flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) {
return mp_hal_status_to_neg_errno(status);
}
+
+#endif
diff --git a/ports/stm32/i2cslave.h b/ports/stm32/i2cslave.h
index a4b2957de3..cc4e7f9be9 100644
--- a/ports/stm32/i2cslave.h
+++ b/ports/stm32/i2cslave.h
@@ -51,6 +51,16 @@ static inline void i2c_slave_init(i2c_slave_t *i2c, int irqn, int irq_pri, int a
RCC->APB1LENR |= 1 << (RCC_APB1LENR_I2C1EN_Pos + i2c_idx);
volatile uint32_t tmp = RCC->APB1LENR; // Delay after enabling clock
(void)tmp;
+ #elif defined(STM32N6)
+ if (i2c_idx == 3) {
+ RCC->APB4ENR1 |= RCC_APB4ENR1_I2C4EN;
+ volatile uint32_t tmp = RCC->APB4ENR1; // Delay after enabling clock
+ (void)tmp;
+ } else {
+ RCC->APB1ENR1 |= 1 << (RCC_APB1ENR1_I2C1EN_Pos + i2c_idx);
+ volatile uint32_t tmp = RCC->APB1ENR1; // Delay after enabling clock
+ (void)tmp;
+ }
#elif defined(STM32WB)
RCC->APB1ENR1 |= 1 << (RCC_APB1ENR1_I2C1EN_Pos + i2c_idx);
volatile uint32_t tmp = RCC->APB1ENR1; // Delay after enabling clock
diff --git a/ports/stm32/irq.h b/ports/stm32/irq.h
index 58e6d0a804..dfe901ff74 100644
--- a/ports/stm32/irq.h
+++ b/ports/stm32/irq.h
@@ -155,6 +155,9 @@ static inline void restore_irq_pri(uint32_t state) {
// SDIO must be higher priority than DMA for SDIO DMA transfers to work.
#define IRQ_PRI_SDIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 4, 0)
+// SPI must be higher priority than DMA for SPI DMA transfers to work.
+#define IRQ_PRI_SPI NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 4, 0)
+
// DMA should be higher priority than USB, since USB Mass Storage calls
// into the sdcard driver which waits for the DMA to complete.
#define IRQ_PRI_DMA NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 5, 0)
@@ -172,8 +175,6 @@ static inline void restore_irq_pri(uint32_t state) {
#define IRQ_PRI_SUBGHZ_RADIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0)
-#define IRQ_PRI_SPI NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0)
-
#define IRQ_PRI_HSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 10, 0)
// Interrupt priority for non-special timers.
diff --git a/ports/stm32/lwip_inc/lwipopts.h b/ports/stm32/lwip_inc/lwipopts.h
index 9e2402c8dc..ad1143845f 100644
--- a/ports/stm32/lwip_inc/lwipopts.h
+++ b/ports/stm32/lwip_inc/lwipopts.h
@@ -10,6 +10,15 @@
#define LWIP_RAND() rng_get()
+// Increase memory for lwIP to get better performance.
+#if defined(STM32N6)
+#define MEM_SIZE (16 * 1024)
+#define TCP_MSS (1460)
+#define TCP_WND (8 * TCP_MSS)
+#define TCP_SND_BUF (8 * TCP_MSS)
+#define MEMP_NUM_TCP_SEG (32)
+#endif
+
// Include common lwIP configuration.
#include "extmod/lwip-include/lwipopts_common.h"
diff --git a/ports/stm32/machine_adc.c b/ports/stm32/machine_adc.c
index 8f07075914..63cd4e089d 100644
--- a/ports/stm32/machine_adc.c
+++ b/ports/stm32/machine_adc.c
@@ -30,7 +30,7 @@
#include "py/mphal.h"
#include "adc.h"
-#if defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+#if defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
#define ADC_V2 (1)
#else
#define ADC_V2 (0)
@@ -80,11 +80,14 @@
#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_12CYCLES_5
#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_160CYCLES_5
#elif defined(STM32L1)
-#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_384CYCLES
+#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_16CYCLES
#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_384CYCLES
#elif defined(STM32L4) || defined(STM32WB)
#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_12CYCLES_5
#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_247CYCLES_5
+#elif defined(STM32N6)
+#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_11CYCLES_5
+#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_246CYCLES_5
#endif
// Timeout for waiting for end-of-conversion
@@ -127,6 +130,8 @@ static uint32_t adc_ll_channel(uint32_t channel_id) {
case MACHINE_ADC_INT_CH_TEMPSENSOR:
#if defined(STM32G4)
adc_ll_ch = ADC_CHANNEL_TEMPSENSOR_ADC1;
+ #elif defined(STM32N6)
+ adc_ll_ch = ADC_CHANNEL_0; // TODO
#else
adc_ll_ch = ADC_CHANNEL_TEMPSENSOR;
#endif
@@ -183,7 +188,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) {
if (adc == ADC1) {
#if defined(STM32H5)
__HAL_RCC_ADC_CLK_ENABLE();
- #elif defined(STM32G4) || defined(STM32H7)
+ #elif defined(STM32G4) || defined(STM32H7) || defined(STM32N6)
__HAL_RCC_ADC12_CLK_ENABLE();
#else
__HAL_RCC_ADC1_CLK_ENABLE();
@@ -193,7 +198,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) {
if (adc == ADC2) {
#if defined(STM32H5)
__HAL_RCC_ADC_CLK_ENABLE();
- #elif defined(STM32G4) || defined(STM32H7)
+ #elif defined(STM32G4) || defined(STM32H7) || defined(STM32N6)
__HAL_RCC_ADC12_CLK_ENABLE();
#else
__HAL_RCC_ADC2_CLK_ENABLE();
@@ -225,13 +230,15 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) {
ADC3_COMMON->CCR = 3 << ADC_CCR_CKMODE_Pos;
#elif defined(STM32L0)
ADC1_COMMON->CCR = 0; // ADCPR=PCLK/2
+ #elif defined(STM32L1)
+ ADC1_COMMON->CCR = 1 << ADC_CCR_ADCPRE_Pos; // ADCPRE=2
#elif defined(STM32WB)
ADC1_COMMON->CCR = 0 << ADC_CCR_PRESC_Pos | 0 << ADC_CCR_CKMODE_Pos; // PRESC=1, MODE=ASYNC
#elif defined(STM32WL)
ADC_COMMON->CCR = 0 << ADC_CCR_PRESC_Pos; // PRESC=1
#endif
- #if defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)
+ #if defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
if (adc->CR & ADC_CR_DEEPPWD) {
adc->CR = 0; // disable deep powerdown
}
@@ -255,6 +262,11 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) {
LL_ADC_StartCalibration(adc);
#elif defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB)
LL_ADC_StartCalibration(adc, LL_ADC_SINGLE_ENDED);
+ #elif defined(STM32N6)
+ ADC_HandleTypeDef hadc;
+ hadc.Instance = adc;
+ hadc.State = 0;
+ HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED);
#else
LL_ADC_StartCalibration(adc, LL_ADC_CALIB_OFFSET_LINEARITY, LL_ADC_SINGLE_ENDED);
#endif
@@ -310,11 +322,17 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) {
uint32_t cfgr = res << ADC_CFGR_RES_Pos;
adc->CFGR = (adc->CFGR & ~cfgr_clr) | cfgr;
+ #elif defined(STM32N6)
+
+ uint32_t cfgr1_clr = ADC_CFGR1_CONT | ADC_CFGR1_EXTEN;
+ uint32_t cfgr1 = res << ADC_CFGR1_RES_Pos;
+ adc->CFGR1 = (adc->CFGR1 & ~cfgr1_clr) | cfgr1;
+
#endif
}
static int adc_get_bits(ADC_TypeDef *adc) {
- #if defined(STM32F0) || defined(STM32G0) || defined(STM32L0) || defined(STM32WL)
+ #if defined(STM32F0) || defined(STM32G0) || defined(STM32L0) || defined(STM32N6) || defined(STM32WL)
uint32_t res = (adc->CFGR1 & ADC_CFGR1_RES) >> ADC_CFGR1_RES_Pos;
#elif defined(STM32F4) || defined(STM32F7) || defined(STM32L1)
uint32_t res = (adc->CR1 & ADC_CR1_RES) >> ADC_CR1_RES_Pos;
@@ -385,8 +403,34 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp
}
*smpr = (*smpr & ~(7 << (channel * 3))) | sample_time << (channel * 3); // select sample time
- #elif defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)
- #if defined(STM32G4) || defined(STM32H5) || defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ)
+ #elif defined(STM32L1)
+
+ ADC_Common_TypeDef *adc_common = ADC1_COMMON;
+ if (channel == ADC_CHANNEL_VREFINT || channel == ADC_CHANNEL_TEMPSENSOR) {
+ adc_common->CCR |= ADC_CCR_TSVREFE;
+ if (channel == ADC_CHANNEL_TEMPSENSOR) {
+ adc_stabilisation_delay_us(ADC_TEMPSENSOR_DELAY_US);
+ }
+ }
+
+ adc->SQR1 = (1 - 1) << ADC_SQR1_L_Pos;
+ adc->SQR5 = (channel & 0x1f) << ADC_SQR5_SQ1_Pos;
+
+ __IO uint32_t *smpr;
+ if (channel >= 20) {
+ smpr = &adc->SMPR1;
+ channel -= 20;
+ } else if (channel >= 10) {
+ smpr = &adc->SMPR2;
+ channel -= 10;
+ } else {
+ smpr = &adc->SMPR3;
+ }
+ *smpr = (*smpr & ~(7 << (channel * 3))) | sample_time << (channel * 3); // select sample time
+
+ #elif defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
+
+ #if defined(STM32G4) || defined(STM32H5) || defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) || defined(STM32N6)
ADC_Common_TypeDef *adc_common = ADC12_COMMON;
#elif defined(STM32H7)
#if defined(ADC_VER_V5_V90)
@@ -404,6 +448,7 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp
#endif
if (channel == ADC_CHANNEL_VREFINT) {
adc_common->CCR |= ADC_CCR_VREFEN;
+ #if !defined(STM32N6)
#if defined(STM32G4)
} else if (channel == ADC_CHANNEL_TEMPSENSOR_ADC1) {
adc_common->CCR |= ADC_CCR_VSENSESEL;
@@ -412,19 +457,20 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp
adc_common->CCR |= ADC_CCR_TSEN;
#endif
adc_stabilisation_delay_us(ADC_TEMPSENSOR_DELAY_US);
+ #endif
} else if (channel == ADC_CHANNEL_VBAT) {
#if defined(STM32G4)
adc_common->CCR |= ADC_CCR_VBATSEL;
#else
adc_common->CCR |= ADC_CCR_VBATEN;
#endif
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
} else if (channel == ADC_CHANNEL_VDDCORE) {
adc->OR |= ADC_OR_OP0; // Enable Vddcore channel on ADC2
#endif
}
- #if defined(STM32G4) || defined(STM32H5)
- // G4 and H5 use encoded literals for internal channels -> extract ADC channel for following code
+ #if defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WB)
+ // MCU uses encoded literals for internal channels -> extract ADC channel for following code
if (__LL_ADC_IS_CHANNEL_INTERNAL(channel)) {
channel = __LL_ADC_CHANNEL_TO_DECIMAL_NB(channel);
}
@@ -445,10 +491,13 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp
static uint32_t adc_read_channel(ADC_TypeDef *adc) {
uint32_t value;
- #if defined(STM32G4)
- // For STM32G4 there is errata 2.7.7, "Wrong ADC result if conversion done late after
- // calibration or previous conversion". According to the errata, this can be avoided
- // by performing two consecutive ADC conversions and keeping the second result.
+ #if defined(STM32G4) || defined(STM32WB)
+ // For STM32G4 errata 2.7.7 / STM32WB errata 2.7.1:
+ // "Wrong ADC result if conversion done late after calibration or previous conversion"
+ // states an incorrect reading is returned if more than 1ms has elapsed since the last
+ // reading or calibration. According to the errata, this can be avoided by performing
+ // two consecutive ADC conversions and keeping the second result.
+ // Note: On STM32WB55 @ 64Mhz each ADC read takes ~ 3us.
for (uint8_t i = 0; i < 2; i++)
#endif
{
diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c
index 8444b29989..8f1faea4b6 100644
--- a/ports/stm32/machine_uart.c
+++ b/ports/stm32/machine_uart.c
@@ -399,7 +399,7 @@ static bool mp_machine_uart_txdone(machine_uart_obj_t *self) {
// Send a break condition.
static void mp_machine_uart_sendbreak(machine_uart_obj_t *self) {
- #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
self->uartx->RQR = USART_RQR_SBKRQ; // write-only register
#else
self->uartx->CR1 |= USART_CR1_SBK;
diff --git a/ports/stm32/main.c b/ports/stm32/main.c
index e8395013b9..137e132817 100644
--- a/ports/stm32/main.c
+++ b/ports/stm32/main.c
@@ -306,11 +306,30 @@ static bool init_sdcard_fs(void) {
}
#endif
+#if defined(STM32N6)
+static void risaf_init(void) {
+ RIMC_MasterConfig_t rimc_master = {0};
+
+ __HAL_RCC_RIFSC_CLK_ENABLE();
+ LL_AHB3_GRP1_EnableClockLowPower(LL_AHB3_GRP1_PERIPH_RIFSC | LL_AHB3_GRP1_PERIPH_RISAF);
+
+ rimc_master.MasterCID = RIF_CID_1;
+ rimc_master.SecPriv = RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV;
+
+ HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_SDMMC1, &rimc_master);
+ HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_SDMMC1, RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV);
+ HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_SDMMC2, &rimc_master);
+ HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_SDMMC2, RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV);
+}
+#endif
+
void stm32_main(uint32_t reset_mode) {
// Low-level MCU initialisation.
stm32_system_init();
- #if !defined(STM32F0)
+ // Set VTOR, the location of the interrupt vector table.
+ // On N6, SystemInit does this, setting VTOR to &g_pfnVectors.
+ #if !defined(STM32F0) && !defined(STM32N6)
#if MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM
// Copy IRQ vector table to RAM and point VTOR there
extern uint32_t __isr_vector_flash_addr, __isr_vector_ram_start, __isr_vector_ram_end;
@@ -325,8 +344,7 @@ void stm32_main(uint32_t reset_mode) {
#endif
#endif
-
- #if __CORTEX_M != 33
+ #if __CORTEX_M != 33 && __CORTEX_M != 55
// Enable 8-byte stack alignment for IRQ handlers, in accord with EABI
SCB->CCR |= SCB_CCR_STKALIGN_Msk;
#endif
@@ -349,14 +367,18 @@ void stm32_main(uint32_t reset_mode) {
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
- #elif defined(STM32F7) || defined(STM32H7)
+ #elif defined(STM32F7) || defined(STM32H7) || defined(STM32N6)
#if ART_ACCLERATOR_ENABLE
__HAL_FLASH_ART_ENABLE();
#endif
SCB_EnableICache();
+ #if defined(STM32N6) && !defined(NDEBUG)
+ // Don't enable D-cache on N6 when debugging; see ST Errata ES0620 - Rev 0.2 section 2.1.2.
+ #else
SCB_EnableDCache();
+ #endif
#elif defined(STM32H5)
@@ -376,6 +398,23 @@ void stm32_main(uint32_t reset_mode) {
#endif
+ #if defined(STM32N6)
+ // SRAM, XSPI needs to remain awake during sleep, eg so DMA from flash works.
+ LL_MEM_EnableClockLowPower(0xffffffff);
+ LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_XSPI2 | LL_AHB5_GRP1_PERIPH_XSPIM);
+ LL_APB4_GRP1_EnableClock(LL_APB4_GRP1_PERIPH_RTC | LL_APB4_GRP1_PERIPH_RTCAPB);
+ LL_APB4_GRP1_EnableClockLowPower(LL_APB4_GRP1_PERIPH_RTC | LL_APB4_GRP1_PERIPH_RTCAPB);
+
+ // Enable some AHB peripherals during sleep.
+ LL_AHB1_GRP1_EnableClockLowPower(0xffffffff); // GPDMA1, ADC12
+ LL_AHB4_GRP1_EnableClockLowPower(0xffffffff); // GPIOA-Q, PWR, CRC
+
+ // Enable some APB peripherals during sleep.
+ LL_APB1_GRP1_EnableClockLowPower(0xffffffff); // I2C, I3C, LPTIM, SPI, TIM, UART, WWDG
+ LL_APB2_GRP1_EnableClockLowPower(0xffffffff); // SAI, SPI, TIM, UART
+ LL_APB4_GRP1_EnableClockLowPower(0xffffffff); // I2C, LPTIM, LPUART, RTC, SPI
+ #endif
+
mpu_init();
#if __CORTEX_M >= 0x03
@@ -389,6 +428,10 @@ void stm32_main(uint32_t reset_mode) {
// set the system clock to be HSE
SystemClock_Config();
+ #if defined(STM32N6)
+ risaf_init();
+ #endif
+
#if defined(STM32F4) || defined(STM32F7)
#if defined(__HAL_RCC_DTCMRAMEN_CLK_ENABLE)
// The STM32F746 doesn't really have CCM memory, but it does have DTCM,
@@ -521,6 +564,23 @@ soft_reset:
// GC init
gc_init(MICROPY_HEAP_START, MICROPY_HEAP_END);
+ // Add additional GC blocks (if enabled).
+ #if MICROPY_GC_SPLIT_HEAP
+ typedef struct {
+ uint8_t *addr;
+ uint32_t size;
+ } gc_blocks_table_t;
+
+ extern const gc_blocks_table_t _gc_blocks_table_start;
+ extern const gc_blocks_table_t _gc_blocks_table_end;
+
+ for (gc_blocks_table_t const *block = &_gc_blocks_table_start; block < &_gc_blocks_table_end; block++) {
+ if (block->size) {
+ gc_add(block->addr, block->addr + block->size);
+ }
+ }
+ #endif
+
#if MICROPY_ENABLE_PYSTACK
static mp_obj_t pystack[384];
mp_pystack_init(pystack, &pystack[384]);
diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile
index 87bced1aee..7226dd353f 100755
--- a/ports/stm32/mboot/Makefile
+++ b/ports/stm32/mboot/Makefile
@@ -34,7 +34,13 @@ include ../../../py/mkenv.mk
include $(BOARD_DIR)/mpconfigboard.mk
# A board can set MBOOT_TEXT0_ADDR to a custom location where mboot should reside.
+ifeq ($(MCU_SERIES),n6)
+MBOOT_TEXT0_ADDR ?= 0x34180400
+MBOOT_LD_FILES ?= stm32_memory_n6.ld stm32_sections.ld
+else
MBOOT_TEXT0_ADDR ?= 0x08000000
+MBOOT_LD_FILES ?= stm32_memory.ld stm32_sections.ld
+endif
# The string in MBOOT_VERSION (default defined in version.c if not defined by a
# board) will be stored in the final MBOOT_VERSION_ALLOCATED_BYTES bytes of mboot flash.
@@ -42,6 +48,7 @@ MBOOT_TEXT0_ADDR ?= 0x08000000
MBOOT_VERSION_ALLOCATED_BYTES ?= 64
MBOOT_VERSION_INCLUDE_OPTIONS ?= 1 # if set to 1, this will append build options to version string (see version.c)
+CROSS_COMPILE ?= arm-none-eabi-
USBDEV_DIR=usbdev
DFU=$(TOP)/tools/dfu.py
PYDFU ?= $(TOP)/tools/pydfu.py
@@ -53,8 +60,6 @@ OPENOCD_CONFIG ?= boards/openocd_stm32f4.cfg
include ../stm32.mk
-CROSS_COMPILE ?= arm-none-eabi-
-
INC += -I.
INC += -I..
INC += -I$(TOP)
@@ -89,7 +94,6 @@ CFLAGS += -DMBOOT_VERSION=\"$(MBOOT_VERSION)\"
endif
CFLAGS += -DMBOOT_VERSION_ALLOCATED_BYTES=$(MBOOT_VERSION_ALLOCATED_BYTES) -DMBOOT_VERSION_INCLUDE_OPTIONS=$(MBOOT_VERSION_INCLUDE_OPTIONS)
-MBOOT_LD_FILES ?= stm32_memory.ld stm32_sections.ld
LDFLAGS += -nostdlib -L . $(addprefix -T,$(MBOOT_LD_FILES)) -Map=$(@:.elf=.map) --cref
LDFLAGS += --defsym mboot_version_len=$(MBOOT_VERSION_ALLOCATED_BYTES)
LIBS += $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
@@ -137,12 +141,11 @@ SRC_C += \
drivers/bus/softqspi.c \
drivers/memory/spiflash.c \
ports/stm32/flash.c \
- ports/stm32/flashbdev.c \
ports/stm32/i2cslave.c \
ports/stm32/powerctrlboot.c \
ports/stm32/qspi.c \
- ports/stm32/spibdev.c \
ports/stm32/usbd_conf.c \
+ ports/stm32/xspi.c \
$(wildcard $(BOARD_DIR)/*.c)
SRC_O += \
@@ -169,16 +172,22 @@ SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal.c \
hal_cortex.c \
hal_dma.c \
- hal_flash.c \
- hal_flash_ex.c \
hal_pcd.c \
hal_pcd_ex.c \
hal_pwr_ex.c \
hal_rcc.c \
hal_rcc_ex.c \
+ ll_rcc.c \
ll_usb.c \
)
+ifneq ($(MCU_SERIES),n6)
+SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
+ hal_flash.c \
+ hal_flash_ex.c \
+ )
+endif
+
ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h7))
SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal_mmc.c \
@@ -187,6 +196,12 @@ SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
)
endif
+ifeq ($(MCU_SERIES),n6)
+SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
+ hal_bsec.c \
+ )
+endif
+
SRC_USBDEV += $(addprefix ports/stm32/$(USBDEV_DIR)/,\
core/src/usbd_core.c \
core/src/usbd_ctlreq.c \
@@ -206,7 +221,7 @@ $(TOP)/lib/stm32lib/README.md:
$(ECHO) "stm32lib submodule not found, fetching it now..."
(cd $(TOP) && git submodule update --init lib/stm32lib)
-.PHONY: deploy deploy-stlink
+.PHONY: deploy deploy-stlink deploy-trusted
deploy: $(BUILD)/firmware.dfu
$(ECHO) "Writing $< to the board"
@@ -216,9 +231,15 @@ deploy-stlink: $(BUILD)/firmware.dfu
$(ECHO) "Writing $< to the board via ST-LINK"
$(Q)$(STFLASH) write $(BUILD)/firmware.bin $(MBOOT_TEXT0_ADDR)
-$(BUILD)/firmware.dfu: $(BUILD)/firmware.elf
+deploy-trusted: $(BUILD)/firmware-trusted.bin
+ $(STM32_CUBE_PROGRAMMER)/bin/STM32_Programmer.sh -c port=SWD mode=HOTPLUG ap=1 -el $(DKEL) -w $^ 0x70000000 -hardRst
+
+$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
$(ECHO) "Create $@"
$(Q)$(OBJCOPY) -O binary -j .isr_vector -j .text -j .data -j .mboot_version_text $^ $(BUILD)/firmware.bin
+
+$(BUILD)/firmware.dfu: $(BUILD)/firmware.bin
+ $(ECHO) "Create $@"
$(Q)$(PYTHON) $(DFU) -b $(MBOOT_TEXT0_ADDR):$(BUILD)/firmware.bin $@
$(BUILD)/firmware.hex: $(BUILD)/firmware.elf
@@ -230,6 +251,10 @@ $(BUILD)/firmware.elf: $(OBJ)
$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
$(Q)$(SIZE) $@
+$(BUILD)/firmware-trusted.bin: $(BUILD)/firmware.bin
+ /bin/rm -f $@
+ $(STM32_CUBE_PROGRAMMER)/bin/STM32_SigningTool_CLI -bin $^ -nk -of 0x80000000 -t fsbl -o $@ -hv $(STM32_N6_HEADER_VERSION)
+
#########################################
# Rules to generate header files
diff --git a/ports/stm32/mboot/adc.c b/ports/stm32/mboot/adc.c
index c7b9749244..06db0b59b7 100644
--- a/ports/stm32/mboot/adc.c
+++ b/ports/stm32/mboot/adc.c
@@ -1,3 +1,5 @@
// Include the main ADC driver, so mboot can use adc_config() and adc_config_and_read_u16().
#include "py/obj.h"
+#if MICROPY_PY_MACHINE_ADC
#include "../machine_adc.c"
+#endif
diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c
index 01f8892a51..2be8793351 100644
--- a/ports/stm32/mboot/main.c
+++ b/ports/stm32/mboot/main.c
@@ -41,6 +41,7 @@
#include "sdcard.h"
#include "dfu.h"
#include "pack.h"
+#include "xspi.h"
// Whether the bootloader will leave via reset, or direct jump to the application.
#ifndef MBOOT_LEAVE_BOOTLOADER_VIA_RESET
@@ -176,7 +177,7 @@ void HAL_Delay(uint32_t ms) {
mp_hal_delay_ms(ms);
}
-NORETURN static void __fatal_error(const char *msg) {
+MP_NORETURN static void __fatal_error(const char *msg) {
NVIC_SystemReset();
for (;;) {
}
@@ -373,7 +374,7 @@ void SystemClock_Config(void) {
#elif defined(STM32G0)
#define AHBxENR IOPENR
#define AHBxENR_GPIOAEN_Pos RCC_IOPENR_GPIOAEN_Pos
-#elif defined(STM32H7)
+#elif defined(STM32H7) || defined(STM32N6)
#define AHBxENR AHB4ENR
#define AHBxENR_GPIOAEN_Pos RCC_AHB4ENR_GPIOAEN_Pos
#elif defined(STM32H5) || defined(STM32WB)
@@ -424,6 +425,10 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed) {
#define MBOOT_SPIFLASH2_LAYOUT ""
#endif
+#if defined(STM32N6)
+#define FLASH_LAYOUT_STR "@Internal Flash " MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT
+#else
+
#if defined(STM32F4) \
|| defined(STM32F722xx) \
|| defined(STM32F723xx) \
@@ -584,12 +589,18 @@ static int mboot_flash_write(uint32_t addr, const uint8_t *src8, size_t len) {
return 0;
}
+#endif
+
/******************************************************************************/
// Writable address space interface
static int do_mass_erase(void) {
+ #if defined(STM32N6)
+ return -1;
+ #else
// TODO spiflash erase ?
return mboot_flash_mass_erase();
+ #endif
}
#if defined(MBOOT_SPIFLASH_ADDR) || defined(MBOOT_SPIFLASH2_ADDR)
@@ -625,7 +636,12 @@ int hw_page_erase(uint32_t addr, uint32_t *next_addr) {
} else
#endif
{
+ #if defined(STM32N6)
+ dfu_context.status = DFU_STATUS_ERROR_ADDRESS;
+ dfu_context.error = MBOOT_ERROR_STR_INVALID_ADDRESS_IDX;
+ #else
ret = mboot_flash_page_erase(addr, next_addr);
+ #endif
}
mboot_state_change(MBOOT_STATE_ERASE_END, ret);
@@ -678,9 +694,12 @@ int hw_write(uint32_t addr, const uint8_t *src8, size_t len) {
ret = mp_spiflash_write(MBOOT_SPIFLASH2_SPIFLASH, addr - MBOOT_SPIFLASH2_ADDR, len, src8);
} else
#endif
+ #if !defined(STM32N6)
if (flash_is_valid_addr(addr)) {
ret = mboot_flash_write(addr, src8, len);
- } else {
+ } else
+ #endif
+ {
dfu_context.status = DFU_STATUS_ERROR_ADDRESS;
dfu_context.error = MBOOT_ERROR_STR_INVALID_ADDRESS_IDX;
}
@@ -1443,7 +1462,7 @@ static int pyb_usbdd_shutdown(void) {
/******************************************************************************/
// main
-NORETURN static __attribute__((naked)) void branch_to_application(uint32_t r0, uint32_t bl_addr) {
+MP_NORETURN static __attribute__((naked)) void branch_to_application(uint32_t r0, uint32_t bl_addr) {
__asm volatile (
"ldr r2, [r1, #0]\n" // get address of stack pointer
"msr msp, r2\n" // set stack pointer
@@ -1509,7 +1528,7 @@ void stm32_main(uint32_t initial_r0) {
// Make sure IRQ vector table points to flash where this bootloader lives.
SCB->VTOR = MBOOT_VTOR;
- #if __CORTEX_M != 33
+ #if __CORTEX_M != 33 && __CORTEX_M != 55
// Enable 8-byte stack alignment for IRQ handlers, in accord with EABI
SCB->CCR |= SCB_CCR_STKALIGN_Msk;
#endif
@@ -1539,6 +1558,12 @@ void stm32_main(uint32_t initial_r0) {
SCB_EnableDCache();
#endif
+ #if defined(STM32N6)
+ LL_PWR_EnableBkUpAccess();
+ initial_r0 = TAMP_S->BKP31R;
+ TAMP_S->BKP31R = 0;
+ #endif
+
MBOOT_BOARD_EARLY_INIT(&initial_r0);
#ifdef MBOOT_BOOTPIN_PIN
@@ -1748,6 +1773,12 @@ void USB_DRD_FS_IRQHandler(void) {
HAL_PCD_IRQHandler(&pcd_fs_handle);
}
+#elif defined(STM32N6)
+
+void USB1_OTG_HS_IRQHandler(void) {
+ HAL_PCD_IRQHandler(&pcd_hs_handle);
+}
+
#elif defined(STM32WB)
void USB_LP_IRQHandler(void) {
diff --git a/ports/stm32/mboot/mboot.h b/ports/stm32/mboot/mboot.h
index 1fabff0080..bc1320c5cd 100644
--- a/ports/stm32/mboot/mboot.h
+++ b/ports/stm32/mboot/mboot.h
@@ -36,7 +36,7 @@
#define ELEM_DATA_START (&_estack[0])
#define ELEM_DATA_MAX (&_estack[ELEM_DATA_SIZE])
-#define NORETURN __attribute__((noreturn))
+#define MP_NORETURN __attribute__((noreturn))
#define MP_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
// The default UI code in ui.c only works if there is at least one LED configured.
diff --git a/ports/stm32/mboot/mphalport.h b/ports/stm32/mboot/mphalport.h
index 45bf11d42b..9cac0f70c4 100644
--- a/ports/stm32/mboot/mphalport.h
+++ b/ports/stm32/mboot/mphalport.h
@@ -239,3 +239,20 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed);
#define pin_J13 (GPIOJ_BASE | 13)
#define pin_J14 (GPIOJ_BASE | 14)
#define pin_J15 (GPIOJ_BASE | 15)
+
+#define pin_N0 (GPION_BASE | 0)
+#define pin_N1 (GPION_BASE | 1)
+#define pin_N2 (GPION_BASE | 2)
+#define pin_N3 (GPION_BASE | 3)
+#define pin_N4 (GPION_BASE | 4)
+#define pin_N5 (GPION_BASE | 5)
+#define pin_N6 (GPION_BASE | 6)
+#define pin_N7 (GPION_BASE | 7)
+#define pin_N8 (GPION_BASE | 8)
+#define pin_N9 (GPION_BASE | 9)
+#define pin_N10 (GPION_BASE | 10)
+#define pin_N11 (GPION_BASE | 11)
+#define pin_N12 (GPION_BASE | 12)
+#define pin_N13 (GPION_BASE | 13)
+#define pin_N14 (GPION_BASE | 14)
+#define pin_N15 (GPION_BASE | 15)
diff --git a/ports/stm32/mboot/stm32_memory_n6.ld b/ports/stm32/mboot/stm32_memory_n6.ld
new file mode 100644
index 0000000000..bd2471dbfa
--- /dev/null
+++ b/ports/stm32/mboot/stm32_memory_n6.ld
@@ -0,0 +1,18 @@
+/*
+ Linker script fragment for mboot on an STM32N6xx MCU.
+ This defines the memory sections for the bootloader to use.
+
+ On N6, the hardware bootloader loads the first 512k of external flash into
+ the upper part of SRAM2 AXI S, starting at 0x34180000. The first 1024 bytes
+ is a header. Then comes the actual code, starting with the vector table.
+*/
+
+MEMORY
+{
+ FLASH_BL (rx) : ORIGIN = 0x34180400, LENGTH = 31744 /* AXISRAM2_S */
+ RAM (xrw) : ORIGIN = 0x341e0000, LENGTH = 128K /* AXISRAM2_S */
+}
+
+/* Location of protected flash area which must not be modified, because mboot lives there. */
+_mboot_protected_flash_start = ORIGIN(FLASH_BL);
+_mboot_protected_flash_end_exclusive = ORIGIN(FLASH_BL) + LENGTH(FLASH_BL);
diff --git a/ports/stm32/mboot/stm32_sections.ld b/ports/stm32/mboot/stm32_sections.ld
index 43511f0839..4a6fd44b2b 100644
--- a/ports/stm32/mboot/stm32_sections.ld
+++ b/ports/stm32/mboot/stm32_sections.ld
@@ -33,6 +33,14 @@ SECTIONS
_etext = .;
} >FLASH_BL
+ /* Secure Gateway stubs */
+ .gnu.sgstubs :
+ {
+ . = ALIGN(4);
+ *(.gnu.sgstubs*)
+ . = ALIGN(4);
+ } >FLASH_BL
+
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c
index 620ae468cb..8123cd8011 100644
--- a/ports/stm32/modmachine.c
+++ b/ports/stm32/modmachine.c
@@ -54,7 +54,7 @@
#define RCC_CSR_PORRSTF RCC_CSR_PWRRSTF
#endif
-#if defined(STM32H5)
+#if defined(STM32H5) || defined(STM32N6)
#define RCC_SR RSR
#define RCC_SR_IWDGRSTF RCC_RSR_IWDGRSTF
#define RCC_SR_WWDGRSTF RCC_RSR_WWDGRSTF
@@ -135,7 +135,7 @@ void machine_init(void) {
reset_cause = PYB_RESET_DEEPSLEEP;
PWR->PMCR |= PWR_PMCR_CSSF;
} else
- #elif defined(STM32H7)
+ #elif defined(STM32H7) || defined(STM32N6)
if (PWR->CPUCR & PWR_CPUCR_SBF || PWR->CPUCR & PWR_CPUCR_STOPF) {
// came out of standby or stop mode
reset_cause = PYB_RESET_DEEPSLEEP;
@@ -291,12 +291,12 @@ static mp_obj_t mp_machine_unique_id(void) {
}
// Resets the pyboard in a manner similar to pushing the external RESET button.
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
powerctrl_mcu_reset();
}
// Activate the bootloader without BOOT* pins.
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
#if MICROPY_HW_ENABLE_USB
pyb_usb_dev_deinit();
#endif
@@ -323,6 +323,19 @@ NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
// get or set the MCU frequencies
static mp_obj_t mp_machine_get_freq(void) {
+ #if defined(STM32N6)
+ LL_RCC_ClocksTypeDef clocks;
+ LL_RCC_GetSystemClocksFreq(&clocks);
+ mp_obj_t tuple[] = {
+ mp_obj_new_int(clocks.CPUCLK_Frequency),
+ mp_obj_new_int(clocks.SYSCLK_Frequency),
+ mp_obj_new_int(clocks.HCLK_Frequency),
+ mp_obj_new_int(clocks.PCLK1_Frequency),
+ mp_obj_new_int(clocks.PCLK2_Frequency),
+ mp_obj_new_int(clocks.PCLK4_Frequency),
+ mp_obj_new_int(clocks.PCLK5_Frequency),
+ };
+ #else
mp_obj_t tuple[] = {
mp_obj_new_int(HAL_RCC_GetSysClockFreq()),
mp_obj_new_int(HAL_RCC_GetHCLKFreq()),
@@ -331,11 +344,12 @@ static mp_obj_t mp_machine_get_freq(void) {
mp_obj_new_int(HAL_RCC_GetPCLK2Freq()),
#endif
};
+ #endif
return mp_obj_new_tuple(MP_ARRAY_SIZE(tuple), tuple);
}
static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
- #if defined(STM32F0) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32G0)
+ #if defined(STM32F0) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32G0) || defined(STM32N6)
mp_raise_NotImplementedError(MP_ERROR_TEXT("machine.freq set not supported yet"));
#else
mp_int_t sysclk = mp_obj_get_int(args[0]);
diff --git a/ports/stm32/modtime.c b/ports/stm32/modtime.c
index ff1495a5d9..87a4536b04 100644
--- a/ports/stm32/modtime.c
+++ b/ports/stm32/modtime.c
@@ -59,5 +59,5 @@ static mp_obj_t mp_time_time_get(void) {
RTC_TimeTypeDef time;
HAL_RTC_GetTime(&RTCHandle, &time, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&RTCHandle, &date, RTC_FORMAT_BIN);
- return mp_obj_new_int(timeutils_seconds_since_epoch(2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds));
+ return timeutils_obj_from_timestamp(timeutils_seconds_since_epoch(2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds));
}
diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h
index e01a4d4b87..9fa9bf7714 100644
--- a/ports/stm32/mpconfigboard_common.h
+++ b/ports/stm32/mpconfigboard_common.h
@@ -64,8 +64,12 @@
// Whether machine.bootloader() will enter the bootloader via reset, or direct jump.
#ifndef MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET
+#if defined(STM32N6)
+#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0)
+#else
#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (1)
#endif
+#endif
// Whether to enable ROMFS on the internal flash.
#ifndef MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH
@@ -77,6 +81,11 @@
#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (0)
#endif
+// Whether to enable ROMFS on external XSPI flash.
+#ifndef MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI
+#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI (0)
+#endif
+
// Whether to enable ROMFS partition 0.
#ifndef MICROPY_HW_ROMFS_ENABLE_PART0
#define MICROPY_HW_ROMFS_ENABLE_PART0 (0)
@@ -465,6 +474,16 @@
#define MICROPY_HW_MAX_UART (5)
#define MICROPY_HW_MAX_LPUART (1)
+// Configuration for STM32N6 series
+#elif defined(STM32N6)
+
+#define MP_HAL_UNIQUE_ID_ADDRESS (UID_BASE)
+#define PYB_EXTI_NUM_VECTORS (20) // only EXTI[15:0], RTC and TAMP currently supported
+#define MICROPY_HW_MAX_I2C (4)
+#define MICROPY_HW_MAX_TIMER (18)
+#define MICROPY_HW_MAX_UART (10)
+#define MICROPY_HW_MAX_LPUART (1)
+
// Configuration for STM32WB series
#elif defined(STM32WB)
@@ -589,8 +608,16 @@
(op) == BDEV_IOCTL_INIT ? spi_bdev_ioctl(MICROPY_HW_BDEV_SPIFLASH, (op), (uint32_t)MICROPY_HW_BDEV_SPIFLASH_CONFIG) : \
spi_bdev_ioctl(MICROPY_HW_BDEV_SPIFLASH, (op), (arg)) \
)
-#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) spi_bdev_readblocks(MICROPY_HW_BDEV_SPIFLASH, (dest), (bl), (n))
-#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(MICROPY_HW_BDEV_SPIFLASH, (src), (bl), (n))
+#ifndef MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES
+#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES (0)
+#endif
+#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BLOCKS (MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES / FLASH_BLOCK_SIZE)
+#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) spi_bdev_readblocks(MICROPY_HW_BDEV_SPIFLASH, (dest), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BLOCKS + (bl), (n))
+#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(MICROPY_HW_BDEV_SPIFLASH, (src), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BLOCKS + (bl), (n))
+#endif
+
+#if defined(STM32N6)
+#define MICROPY_FATFS_MAX_SS (4096)
#endif
// Whether to enable caching for external SPI flash, to allow block writes that are
diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h
index d1d6fe249b..b910188c5a 100644
--- a/ports/stm32/mpconfigport.h
+++ b/ports/stm32/mpconfigport.h
@@ -76,12 +76,13 @@
#ifndef MICROPY_FLOAT_IMPL // can be configured by each board via mpconfigboard.mk
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#endif
+#define MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE (1)
#define MICROPY_USE_INTERNAL_ERRNO (1)
#define MICROPY_SCHEDULER_STATIC_NODES (1)
#define MICROPY_SCHEDULER_DEPTH (8)
#define MICROPY_VFS (1)
#ifndef MICROPY_VFS_ROM
-#define MICROPY_VFS_ROM (MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH || MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI)
+#define MICROPY_VFS_ROM (MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH || MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI || MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI)
#endif
// control over Python builtins
@@ -205,20 +206,6 @@ extern const struct _mp_obj_type_t network_lan_type;
#define MICROPY_HW_NIC_ETH
#endif
-#if MICROPY_PY_NETWORK_CYW43
-extern const struct _mp_obj_type_t mp_network_cyw43_type;
-#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) },
-#else
-#define MICROPY_HW_NIC_CYW43
-#endif
-
-#if MICROPY_PY_NETWORK_WIZNET5K
-extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k;
-#define MICROPY_HW_NIC_WIZNET5K { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) },
-#else
-#define MICROPY_HW_NIC_WIZNET5K
-#endif
-
// extra constants
#define MICROPY_PORT_CONSTANTS \
MACHINE_BUILTIN_MODULE_CONSTANTS \
@@ -231,8 +218,6 @@ extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k;
#define MICROPY_PORT_NETWORK_INTERFACES \
MICROPY_HW_NIC_ETH \
- MICROPY_HW_NIC_CYW43 \
- MICROPY_HW_NIC_WIZNET5K \
MICROPY_BOARD_NETWORK_INTERFACES \
#define MP_STATE_PORT MP_STATE_VM
diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c
index dfd50cebd3..fcd08cbd84 100644
--- a/ports/stm32/mphalport.c
+++ b/ports/stm32/mphalport.c
@@ -20,7 +20,7 @@ const byte mp_hal_status_to_errno_table[4] = {
uint8_t mp_hal_unique_id_address[12];
#endif
-NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
+MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
mp_raise_OSError(mp_hal_status_to_errno_table[status]);
}
@@ -105,7 +105,7 @@ void mp_hal_gpio_clock_enable(GPIO_TypeDef *gpio) {
#elif defined(STM32F4) || defined(STM32F7)
#define AHBxENR AHB1ENR
#define AHBxENR_GPIOAEN_Pos RCC_AHB1ENR_GPIOAEN_Pos
- #elif defined(STM32H7)
+ #elif defined(STM32H7) || defined(STM32N6)
#define AHBxENR AHB4ENR
#define AHBxENR_GPIOAEN_Pos RCC_AHB4ENR_GPIOAEN_Pos
#elif defined(STM32L0)
diff --git a/ports/stm32/mphalport.h b/ports/stm32/mphalport.h
index e520bc54ae..03b0f8e772 100644
--- a/ports/stm32/mphalport.h
+++ b/ports/stm32/mphalport.h
@@ -37,7 +37,7 @@ static inline int mp_hal_status_to_neg_errno(HAL_StatusTypeDef status) {
return -mp_hal_status_to_errno_table[status];
}
-NORETURN void mp_hal_raise(HAL_StatusTypeDef status);
+MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status);
void mp_hal_set_interrupt_char(int c); // -1 to disable
// Atomic section helpers.
diff --git a/ports/stm32/mpu.h b/ports/stm32/mpu.h
index a87c04a58d..8713fe8370 100644
--- a/ports/stm32/mpu.h
+++ b/ports/stm32/mpu.h
@@ -28,7 +28,7 @@
#include "irq.h"
-#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F7) || defined(STM32H7) || defined(STM32WB)
+#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) || defined(STM32WB)
#define MPU_REGION_ETH (MPU_REGION_NUMBER0)
#define MPU_REGION_QSPI1 (MPU_REGION_NUMBER1)
@@ -137,18 +137,26 @@ static inline void mpu_config_end(uint32_t irq_state) {
enable_irq(irq_state);
}
-#elif defined(STM32H5)
+#elif defined(STM32H5) || defined(STM32N6)
#define MPU_REGION_SIG (MPU_REGION_NUMBER0)
#define MPU_REGION_ETH (MPU_REGION_NUMBER1)
-#define MPU_REGION_LAST_USED (MPU_REGION_NUMBER1)
+#define MPU_REGION_DMA_UNCACHED_1 (MPU_REGION_NUMBER2)
+#define MPU_REGION_DMA_UNCACHED_2 (MPU_REGION_NUMBER3)
+#define MPU_REGION_LAST_USED (MPU_REGION_NUMBER3)
#define ST_DEVICE_SIGNATURE_BASE (0x08fff800)
#define ST_DEVICE_SIGNATURE_LIMIT (0x08ffffff)
// STM32H5 Cortex-M33 MPU works differently from older cores.
// Macro only takes region size in bytes, Attributes are coded in mpu_config_region().
+#define MPU_CONFIG_DISABLE (0)
#define MPU_CONFIG_ETH(size) (size)
+#define MPU_CONFIG_UNCACHED(size) (size)
+
+#if defined(STM32N6)
+#define MPU_REGION_SIZE_32B (32)
+#endif
static inline void mpu_init(void) {
// Configure attribute 0, inner-outer non-cacheable (=0x44).
@@ -180,8 +188,12 @@ static inline uint32_t mpu_config_start(void) {
}
static inline void mpu_config_region(uint32_t region, uint32_t base_addr, uint32_t size) {
- if (region == MPU_REGION_ETH) {
- // Configure region 1 to make DMA memory non-cacheable.
+ if (size == 0) {
+ // Disable MPU for this region.
+ MPU->RNR = region;
+ MPU->RLAR &= ~MPU_RLAR_EN_Msk;
+ } else if (region == MPU_REGION_ETH || region == MPU_REGION_DMA_UNCACHED_1 || region == MPU_REGION_DMA_UNCACHED_2) {
+ // Configure region to make DMA memory non-cacheable.
__DMB();
// Configure attribute 1, inner-outer non-cacheable (=0x44).
diff --git a/ports/stm32/pin_defs_stm32.h b/ports/stm32/pin_defs_stm32.h
index 6c67b64924..645ec5b2df 100644
--- a/ports/stm32/pin_defs_stm32.h
+++ b/ports/stm32/pin_defs_stm32.h
@@ -39,6 +39,10 @@ enum {
PORT_I,
PORT_J,
PORT_K,
+ PORT_L,
+ PORT_M,
+ PORT_N,
+ PORT_O,
};
// Must have matching entries in SUPPORTED_FN in boards/make-pins.py
diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c
index eea009e2d7..a750e8f5be 100644
--- a/ports/stm32/powerctrl.c
+++ b/ports/stm32/powerctrl.c
@@ -26,10 +26,10 @@
#include "py/mperrno.h"
#include "py/mphal.h"
+#include "boardctrl.h"
#include "powerctrl.h"
#include "rtc.h"
#include "extmod/modbluetooth.h"
-#include "py/mpconfig.h"
#ifndef NO_QSTR
#include "genhdr/pllfreqtable.h"
#endif
@@ -46,7 +46,7 @@ static uint32_t __attribute__((unused)) micropy_hw_hse_value = HSE_VALUE;
static uint32_t __attribute__((unused)) micropy_hw_clk_pllm = MICROPY_HW_CLK_PLLM;
#endif
-#if defined(STM32H5) || defined(STM32H7)
+#if defined(STM32H5) || defined(STM32H7) || defined(STM32N6)
#define RCC_SR RSR
#if defined(STM32H747xx)
#define RCC_SR_SFTRSTF RCC_RSR_SFT2RSTF
@@ -63,7 +63,7 @@ static uint32_t __attribute__((unused)) micropy_hw_clk_pllm = MICROPY_HW_CLK_PLL
#define POWERCTRL_GET_VOLTAGE_SCALING() PWR_REGULATOR_VOLTAGE_SCALE0
#elif defined(STM32H723xx)
#define POWERCTRL_GET_VOLTAGE_SCALING() LL_PWR_GetRegulVoltageScaling()
-#elif defined(STM32H5)
+#elif defined(STM32H5) || defined(STM32N6)
#define POWERCTRL_GET_VOLTAGE_SCALING() LL_PWR_GetRegulVoltageScaling()
#else
#define POWERCTRL_GET_VOLTAGE_SCALING() \
@@ -115,7 +115,7 @@ static inline void powerctrl_disable_hsi_if_unused(void) {
#endif
}
-NORETURN void powerctrl_mcu_reset(void) {
+MP_NORETURN void powerctrl_mcu_reset(void) {
#if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET
*BL_STATE_PTR = BL_STATE_INVALID;
#if __DCACHE_PRESENT == 1
@@ -125,7 +125,7 @@ NORETURN void powerctrl_mcu_reset(void) {
NVIC_SystemReset();
}
-NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, uint32_t bl_addr) {
+MP_NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, uint32_t bl_addr) {
__asm volatile (
"ldr r2, [r1, #0]\n" // get address of stack pointer
"msr msp, r2\n" // get stack pointer
@@ -135,7 +135,13 @@ NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, ui
MP_UNREACHABLE;
}
-NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) {
+MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) {
+ #if defined(STM32N6)
+ LL_PWR_EnableBkUpAccess();
+ TAMP_S->BKP31R = r0;
+ NVIC_SystemReset();
+ #endif
+
#if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET
// Enter the bootloader via a reset, so everything is reset (including WDT).
@@ -169,6 +175,8 @@ void powerctrl_check_enter_bootloader(void) {
#endif
}
+#if !defined(STM32N6)
+
#if !defined(STM32F0) && !defined(STM32L0) && !defined(STM32WB) && !defined(STM32WL)
typedef struct _sysclk_scaling_table_entry_t {
@@ -781,6 +789,8 @@ static void powerctrl_low_power_exit_wb55() {
#endif // !defined(STM32F0) && !defined(STM32G0) && !defined(STM32L0) && !defined(STM32L1) && !defined(STM32L4)
+#endif
+
void powerctrl_enter_stop_mode(void) {
// Disable IRQs so that the IRQ that wakes the device from stop mode is not
// executed until after the clocks are reconfigured
@@ -809,7 +819,7 @@ void powerctrl_enter_stop_mode(void) {
__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI);
#endif
- #if !defined(STM32F0) && !defined(STM32G0) && !defined(STM32G4) && !defined(STM32L0) && !defined(STM32L1) && !defined(STM32L4) && !defined(STM32WB) && !defined(STM32WL)
+ #if !defined(STM32F0) && !defined(STM32G0) && !defined(STM32G4) && !defined(STM32L0) && !defined(STM32L1) && !defined(STM32L4) && !defined(STM32N6) && !defined(STM32WB) && !defined(STM32WL)
// takes longer to wake but reduces stop current
HAL_PWREx_EnableFlashPowerDown();
#endif
@@ -848,6 +858,8 @@ void powerctrl_enter_stop_mode(void) {
#if defined(STM32F7)
HAL_PWR_EnterSTOPMode((PWR_CR1_LPDS | PWR_CR1_LPUDS | PWR_CR1_FPDS | PWR_CR1_UDEN), PWR_STOPENTRY_WFI);
+ #elif defined(STM32N6)
+ HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI);
#else
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
#endif
@@ -912,6 +924,19 @@ void powerctrl_enter_stop_mode(void) {
while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL1) {
}
+ #elif defined(STM32N6)
+
+ // Enable PLL1, and switch the CPU and system clock source to use PLL1.
+ LL_RCC_PLL1_Enable();
+ while (!LL_RCC_PLL1_IsReady()) {
+ }
+ LL_RCC_SetCpuClkSource(LL_RCC_CPU_CLKSOURCE_IC1);
+ while (LL_RCC_GetCpuClkSource() != LL_RCC_CPU_CLKSOURCE_STATUS_IC1) {
+ }
+ LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_IC2_IC6_IC11);
+ while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_IC2_IC6_IC11) {
+ }
+
#else // defined(STM32H5)
// enable PLL
@@ -1016,9 +1041,47 @@ void powerctrl_enter_stop_mode(void) {
enable_irq(irq_state);
}
-NORETURN void powerctrl_enter_standby_mode(void) {
+#if defined(STM32N6)
+
+// Upon wake from standby, STM32N6 can resume execution from retained SRAM1.
+// Place a small bootloader there which initialises XSPI in memory-mapped mode
+// and jumps to the main application entry point.
+
+#include "xspi.h"
+
+extern uint32_t _estack;
+
+void Reset_Handler(void);
+
+void iram_bootloader_reset(void) {
+ #if defined(MICROPY_BOARD_LEAVE_STANDBY)
+ MICROPY_BOARD_LEAVE_STANDBY;
+ #endif
+ xspi_init();
+ Reset_Handler();
+}
+
+// Very simple ARM vector table.
+const uint32_t iram_bootloader_isr_vector[] = {
+ (uint32_t)&_estack,
+ (uint32_t)&iram_bootloader_reset,
+};
+
+#endif
+
+MP_NORETURN void powerctrl_enter_standby_mode(void) {
rtc_init_finalise();
+ #if defined(STM32N6)
+ // Upon wake from standby, jump to the code at SRAM1.
+ // A board can reconfigure this in MICROPY_BOARD_ENTER_STANDBY if needed.
+ LL_PWR_EnableTCMSBRetention();
+ LL_PWR_EnableTCMFLXSBRetention();
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG);
+ SCB_CleanDCache();
+ SYSCFG->INITSVTORCR = (uint32_t)&iram_bootloader_isr_vector[0];
+ #endif
+
#if defined(MICROPY_BOARD_ENTER_STANDBY)
MICROPY_BOARD_ENTER_STANDBY
#endif
@@ -1039,6 +1102,13 @@ NORETURN void powerctrl_enter_standby_mode(void) {
mp_bluetooth_deinit();
#endif
+ #if defined(STM32N6)
+
+ // Clear all WKUPx flags.
+ LL_PWR_ClearFlag_WU();
+
+ #else
+
// We need to clear the PWR wake-up-flag before entering standby, since
// the flag may have been set by a previous wake-up event. Furthermore,
// we need to disable the wake-up sources while clearing this flag, so
@@ -1135,6 +1205,8 @@ NORETURN void powerctrl_enter_standby_mode(void) {
powerctrl_low_power_prep_wb55();
#endif
+ #endif
+
// enter standby mode
HAL_PWR_EnterSTANDBYMode();
diff --git a/ports/stm32/powerctrl.h b/ports/stm32/powerctrl.h
index 5b92405611..724ab58366 100644
--- a/ports/stm32/powerctrl.h
+++ b/ports/stm32/powerctrl.h
@@ -34,19 +34,25 @@ void stm32_system_init(void);
#else
static inline void stm32_system_init(void) {
SystemInit();
+
+ #if defined(STM32N6)
+ // The ROM bootloader uses PLL1 to set the CPU to 400MHz, so update
+ // the value of SystemCoreClock to reflect the hardware state.
+ SystemCoreClockUpdate();
+ #endif
}
#endif
void SystemClock_Config(void);
-NORETURN void powerctrl_mcu_reset(void);
-NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr);
+MP_NORETURN void powerctrl_mcu_reset(void);
+MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr);
void powerctrl_check_enter_bootloader(void);
void powerctrl_config_systick(void);
int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk_mhz, bool need_pllsai);
int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t apb2);
void powerctrl_enter_stop_mode(void);
-NORETURN void powerctrl_enter_standby_mode(void);
+MP_NORETURN void powerctrl_enter_standby_mode(void);
#endif // MICROPY_INCLUDED_STM32_POWERCTRL_H
diff --git a/ports/stm32/powerctrlboot.c b/ports/stm32/powerctrlboot.c
index 31dae527c1..059d2a45da 100644
--- a/ports/stm32/powerctrlboot.c
+++ b/ports/stm32/powerctrlboot.c
@@ -47,9 +47,15 @@ void stm32_system_init(void) {
#endif
void powerctrl_config_systick(void) {
+ #if defined(STM32N6)
+ uint32_t systick_source_freq = HAL_RCC_GetCpuClockFreq();
+ #else
+ uint32_t systick_source_freq = HAL_RCC_GetHCLKFreq();
+ #endif
+
// Configure SYSTICK to run at 1kHz (1ms interval)
SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK;
- SysTick_Config(HAL_RCC_GetHCLKFreq() / 1000);
+ SysTick_Config(systick_source_freq / 1000);
NVIC_SetPriority(SysTick_IRQn, IRQ_PRI_SYSTICK);
#if !BUILDING_MBOOT && (defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB))
@@ -410,6 +416,124 @@ void SystemClock_Config(void) {
DBGMCU->CR &= ~(DBGMCU_CR_DBG_SLEEP | DBGMCU_CR_DBG_STOP | DBGMCU_CR_DBG_STANDBY);
#endif
}
+
+#elif defined(STM32N6)
+
+void SystemClock_Config(void) {
+ // Enable HSI.
+ LL_RCC_HSI_Enable();
+ while (!LL_RCC_HSI_IsReady()) {
+ }
+
+ // Switch the CPU clock source to HSI.
+ LL_RCC_SetCpuClkSource(LL_RCC_CPU_CLKSOURCE_HSI);
+ while (LL_RCC_GetCpuClkSource() != LL_RCC_CPU_CLKSOURCE_STATUS_HSI) {
+ }
+
+ // Switch the system clock source to HSI.
+ LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);
+ while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) {
+ }
+
+ // Disable all ICx clocks.
+ RCC->DIVENCR = 0x000fffff;
+
+ // This doesn't work, VOSRDY never becomes active.
+ #if 0
+ LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE0);
+ while (!LL_PWR_IsActiveFlag_VOSRDY()) {
+ }
+ #endif
+
+ // Enable HSE.
+ LL_RCC_HSE_Enable();
+ while (!LL_RCC_HSE_IsReady()) {
+ }
+
+ // Disable PLL1.
+ LL_RCC_PLL1_Disable();
+ while (LL_RCC_PLL1_IsReady()) {
+ }
+
+ // Configure PLL1 for use as system clock.
+ LL_RCC_PLL1_SetSource(LL_RCC_PLLSOURCE_HSE);
+ LL_RCC_PLL1_DisableBypass();
+ LL_RCC_PLL1_DisableFractionalModulationSpreadSpectrum();
+ LL_RCC_PLL1_SetM(MICROPY_HW_CLK_PLLM);
+ LL_RCC_PLL1_SetN(MICROPY_HW_CLK_PLLN);
+ LL_RCC_PLL1_SetP1(MICROPY_HW_CLK_PLLP1);
+ LL_RCC_PLL1_SetP2(MICROPY_HW_CLK_PLLP2);
+ LL_RCC_PLL1_SetFRACN(MICROPY_HW_CLK_PLLFRAC);
+ LL_RCC_PLL1P_Enable();
+
+ // Enable PLL1.
+ LL_RCC_PLL1_Enable();
+ while (!LL_RCC_PLL1_IsReady()) {
+ }
+
+ // Configure IC1, IC2, IC6, IC11.
+ LL_RCC_IC1_SetSource(LL_RCC_ICCLKSOURCE_PLL1);
+ LL_RCC_IC1_SetDivider(1);
+ LL_RCC_IC1_Enable();
+ LL_RCC_IC2_SetSource(LL_RCC_ICCLKSOURCE_PLL1);
+ LL_RCC_IC2_SetDivider(2);
+ LL_RCC_IC2_Enable();
+ LL_RCC_IC6_SetSource(LL_RCC_ICCLKSOURCE_PLL1);
+ LL_RCC_IC6_SetDivider(1);
+ LL_RCC_IC6_Enable();
+ LL_RCC_IC11_SetSource(LL_RCC_ICCLKSOURCE_PLL1);
+ LL_RCC_IC11_SetDivider(1);
+ LL_RCC_IC11_Enable();
+
+ // Configure IC14 at 100MHz for slower peripherals.
+ LL_RCC_IC14_SetSource(LL_RCC_ICCLKSOURCE_PLL1);
+ LL_RCC_IC14_SetDivider(8);
+ LL_RCC_IC14_Enable();
+
+ // Enable buses.
+ LL_BUS_EnableClock(LL_APB5 | LL_APB4 | LL_APB3 | LL_APB2 | LL_APB1 | LL_AHB5 | LL_AHB4 | LL_AHB3 | LL_AHB2 | LL_AHB1);
+ LL_MISC_EnableClock(LL_PER);
+
+ // Configure bus dividers.
+ LL_RCC_SetAHBPrescaler(LL_RCC_AHB_DIV_2);
+ LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
+ LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
+ LL_RCC_SetAPB4Prescaler(LL_RCC_APB4_DIV_1);
+ LL_RCC_SetAPB5Prescaler(LL_RCC_APB5_DIV_1);
+
+ // Switch the CPU clock source to IC1 (connected to PLL1).
+ LL_RCC_SetCpuClkSource(LL_RCC_CPU_CLKSOURCE_IC1);
+ while (LL_RCC_GetCpuClkSource() != LL_RCC_CPU_CLKSOURCE_STATUS_IC1) {
+ }
+
+ // Switch the system clock source to IC2/IC6/IC11 (connected to PLL1).
+ LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_IC2_IC6_IC11);
+ while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_IC2_IC6_IC11) {
+ }
+
+ // ADC clock configuration, HCLK/2.
+ LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_HCLK);
+ LL_RCC_SetADCPrescaler(2 - 1);
+
+ // USB clock configuration.
+ #if MICROPY_HW_ENABLE_USB
+
+ // Select HSE/2 as output of direct HSE signal.
+ LL_RCC_HSE_SelectHSEDiv2AsDiv2Clock();
+
+ // Select HSE/2 for OTG1 clock source.
+ LL_RCC_SetClockSource(LL_RCC_OTGPHY1_CLKSOURCE_HSE_DIV_2_OSC);
+ LL_RCC_SetClockSource(LL_RCC_OTGPHY1CKREF_CLKSOURCE_HSE_DIV_2_OSC);
+ LL_RCC_SetOTGPHYClockSource(LL_RCC_OTGPHY1_CLKSOURCE_HSE_DIV_2_OSC);
+ LL_RCC_SetOTGPHYCKREFClockSource(LL_RCC_OTGPHY1CKREF_CLKSOURCE_HSE_DIV_2_OSC);
+
+ #endif
+
+ // Reconfigure clock state and SysTick.
+ SystemCoreClockUpdate();
+ powerctrl_config_systick();
+}
+
#elif defined(STM32WB)
void SystemClock_Config(void) {
diff --git a/ports/stm32/qspi.c b/ports/stm32/qspi.c
index 781aae803e..2ef9a4d018 100644
--- a/ports/stm32/qspi.c
+++ b/ports/stm32/qspi.c
@@ -213,11 +213,13 @@ static int qspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) {
qspi_memory_map();
break;
case MP_QSPI_IOCTL_MEMORY_MODIFIED: {
+ #if defined(__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)
uintptr_t *addr_len = (uintptr_t *)arg;
volatile void *addr = (volatile void *)(QSPI_MAP_ADDR + addr_len[0]);
size_t len = addr_len[1];
SCB_InvalidateICache_by_Addr(addr, len);
SCB_InvalidateDCache_by_Addr(addr, len);
+ #endif
break;
}
}
diff --git a/ports/stm32/resethandler_iram.s b/ports/stm32/resethandler_iram.s
new file mode 100644
index 0000000000..49a8b40068
--- /dev/null
+++ b/ports/stm32/resethandler_iram.s
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018-2025 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.
+ */
+
+ .syntax unified
+ .cpu cortex-m4
+ .thumb
+
+ .section .text.Reset_Handler
+ .global Reset_Handler
+ .type Reset_Handler, %function
+
+Reset_Handler:
+ /* Save the first argument to pass through to stm32_main */
+ mov r4, r0
+
+ /* Load the stack pointer */
+ ldr sp, =_estack
+
+ /* Initialise the iram section */
+ ldr r1, =_siiram
+ ldr r2, =_siram
+ ldr r3, =_eiram
+ b .iram_copy_entry
+ nop
+.iram_copy_loop:
+ ldr r0, [r1], #4 /* Should be 4-aligned to be as fast as possible */
+ str r0, [r2], #4
+.iram_copy_entry:
+ cmp r2, r3
+ bcc .iram_copy_loop
+
+ /* Initialise the data section */
+ ldr r1, =_sidata
+ ldr r2, =_sdata
+ ldr r3, =_edata
+ b .data_copy_entry
+.data_copy_loop:
+ ldr r0, [r1], #4 /* Should be 4-aligned to be as fast as possible */
+ str r0, [r2], #4
+.data_copy_entry:
+ cmp r2, r3
+ bcc .data_copy_loop
+
+ /* Zero out the BSS section */
+ movs r0, #0
+ ldr r1, =_sbss
+ ldr r2, =_ebss
+ b .bss_zero_entry
+.bss_zero_loop:
+ str r0, [r1], #4 /* Should be 4-aligned to be as fast as possible */
+.bss_zero_entry:
+ cmp r1, r2
+ bcc .bss_zero_loop
+
+ /* Jump to the main code */
+ mov r0, r4
+ b stm32_main
+
+ .size Reset_Handler, .-Reset_Handler
diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c
index 8dadc4a88d..b90d17149b 100644
--- a/ports/stm32/rtc.c
+++ b/ports/stm32/rtc.c
@@ -100,6 +100,10 @@ static bool rtc_need_init_finalise = false;
#define RCC_BDCR_LSEBYP RCC_CSR_LSEBYP
#endif
+#if defined(STM32N6)
+#define RCC_DBP_TIMEOUT_VALUE (5)
+#endif
+
void rtc_init_start(bool force_init) {
// Enable the RTC APB bus clock, to communicate with the RTC.
#if defined(STM32H5)
@@ -129,6 +133,32 @@ void rtc_init_start(bool force_init) {
if (!force_init) {
bool rtc_running = false;
+ #if defined(STM32N6)
+ if (LL_RCC_IsEnabledRTC()
+ && LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSE
+ && LL_RCC_LSE_IsReady()) {
+ // LSE is enabled & ready --> no need to (re-)init RTC
+ rtc_running = true;
+ // remove Backup Domain write protection
+ HAL_PWR_EnableBkUpAccess();
+ // Clear source Reset Flag
+ __HAL_RCC_CLEAR_RESET_FLAGS();
+ // provide some status information
+ rtc_info |= 0x40000;
+ } else if (LL_RCC_IsEnabledRTC()
+ && LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSI) {
+ // LSI configured as the RTC clock source --> no need to (re-)init RTC
+ rtc_running = true;
+ // remove Backup Domain write protection
+ HAL_PWR_EnableBkUpAccess();
+ // Clear source Reset Flag
+ __HAL_RCC_CLEAR_RESET_FLAGS();
+ // Turn the LSI on (it may need this even if the RTC is running)
+ LL_RCC_LSI_Enable();
+ // provide some status information
+ rtc_info |= 0x80000;
+ }
+ #else
uint32_t bdcr = RCC->BDCR;
if ((bdcr & (RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL | RCC_BDCR_LSEON | RCC_BDCR_LSERDY))
== (RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL_0 | RCC_BDCR_LSEON | RCC_BDCR_LSERDY)) {
@@ -157,6 +187,7 @@ void rtc_init_start(bool force_init) {
// provide some status information
rtc_info |= 0x80000;
}
+ #endif
if (rtc_running) {
// Provide information about the registers that indicated the RTC is running.
@@ -296,7 +327,7 @@ static HAL_StatusTypeDef PYB_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct
return HAL_TIMEOUT;
}
}
- #elif defined(STM32H5)
+ #elif defined(STM32H5) || defined(STM32N6)
// Wait for Backup domain Write protection disable
while (!LL_PWR_IsEnabledBkUpAccess()) {
if (HAL_GetTick() - tickstart > RCC_DBP_TIMEOUT_VALUE) {
@@ -381,7 +412,7 @@ static HAL_StatusTypeDef PYB_RTC_Init(RTC_HandleTypeDef *hrtc) {
#elif defined(STM32F7)
hrtc->Instance->OR &= (uint32_t) ~RTC_OR_ALARMTYPE;
hrtc->Instance->OR |= (uint32_t)(hrtc->Init.OutPutType);
- #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WL)
+ #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WL)
hrtc->Instance->CR &= (uint32_t) ~RTC_CR_TAMPALRM_TYPE_Msk;
hrtc->Instance->CR |= (uint32_t)(hrtc->Init.OutPutType);
#else
@@ -413,7 +444,14 @@ static void PYB_RTC_MspInit_Kick(RTC_HandleTypeDef *hrtc, bool rtc_use_lse, bool
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE;
+ #if defined(STM32N6)
+ RCC_OscInitStruct.PLL1.PLLState = RCC_PLL_NONE;
+ RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_NONE;
+ RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_NONE;
+ RCC_OscInitStruct.PLL4.PLLState = RCC_PLL_NONE;
+ #else
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
+ #endif
#if MICROPY_HW_RTC_USE_BYPASS
if (rtc_use_byp) {
RCC_OscInitStruct.LSEState = RCC_LSE_BYPASS;
@@ -651,6 +689,8 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_rtc_datetime_obj, 1, 2, pyb_rtc_datetime
#define RTC_WKUP_IRQn RTC_IRQn
#elif defined(STM32G0)
#define RTC_WKUP_IRQn RTC_TAMP_IRQn
+#elif defined(STM32N6)
+#define RTC_WKUP_IRQn RTC_S_IRQn
#endif
// wakeup(None)
@@ -759,8 +799,9 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) {
#if defined(STM32G0) || defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
EXTI->IMR1 |= 1 << EXTI_RTC_WAKEUP;
EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP;
- #elif defined(STM32H5)
+ #elif defined(STM32H5) || defined(STM32N6)
EXTI->IMR1 |= 1 << EXTI_RTC_WAKEUP;
+ EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP;
#elif defined(STM32H7)
EXTI_D1->IMR1 |= 1 << EXTI_RTC_WAKEUP;
EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP;
@@ -772,8 +813,8 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) {
// clear interrupt flags
#if defined(STM32G0) || defined(STM32G4) || defined(STM32WL)
RTC->ICSR &= ~RTC_ICSR_WUTWF;
- #elif defined(STM32H5)
- RTC->SCR = RTC_SCR_CWUTF;
+ #elif defined(STM32H5) || defined(STM32N6)
+ LL_RTC_ClearFlag_WUT(RTC);
#elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ)
RTC->SR &= ~RTC_SR_WUTF;
#else
@@ -783,7 +824,7 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) {
EXTI->PR1 = 1 << EXTI_RTC_WAKEUP;
#elif defined(STM32H7)
EXTI_D1->PR1 = 1 << EXTI_RTC_WAKEUP;
- #elif defined(STM32G0) || defined(STM32H5)
+ #elif defined(STM32G0) || defined(STM32H5) || defined(STM32N6)
// Do nothing
#else
EXTI->PR = 1 << EXTI_RTC_WAKEUP;
@@ -799,7 +840,7 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) {
RTC->WPR = 0xff;
// disable external interrupts on line EXTI_RTC_WAKEUP
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
EXTI->IMR1 &= ~(1 << EXTI_RTC_WAKEUP);
#elif defined(STM32H7)
EXTI_D1->IMR1 |= 1 << EXTI_RTC_WAKEUP;
diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c
index 706d6315c4..b91fa3a9c2 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(STM32H5) || defined(STM32H7) || defined(STM32L4)
+#if defined(STM32F7) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6)
// 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(STM32H5) || defined(STM32H7)
+#if defined(STM32H5) || defined(STM32H7) || defined(STM32N6)
#define SDIO_TRANSFER_CLK_DIV SDMMC_NSpeed_CLK_DIV
#define SDIO_USE_GPDMA 0
#else
@@ -214,7 +214,7 @@ static void sdmmc_msp_init(void) {
// enable SDIO clock
SDMMC_CLK_ENABLE();
- #if defined(STM32H7)
+ #if defined(STM32H7) || defined(STM32N6)
// Reset SDMMC
SDMMC_FORCE_RESET();
SDMMC_RELEASE_RESET();
@@ -270,7 +270,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;
- #if !defined(STM32H5) && !defined(STM32H7)
+ #if !defined(STM32H5) && !defined(STM32H7) && !defined(STM32N6)
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/sdio.c b/ports/stm32/sdio.c
index 99d05a5155..de82ceadc5 100644
--- a/ports/stm32/sdio.c
+++ b/ports/stm32/sdio.c
@@ -77,7 +77,11 @@ static volatile uint8_t *sdmmc_buf_top;
#define SDMMC_IRQHandler SDMMC2_IRQHandler
#define SDMMC_CLK_ENABLE() __HAL_RCC_SDMMC2_CLK_ENABLE()
#define SDMMC_CLK_DISABLE() __HAL_RCC_SDMMC2_CLK_DISABLE()
+#if defined(STM32N6)
+#define SDMMC_IS_CLK_DISABLED() (!__HAL_RCC_SDMMC2_IS_CLK_ENABLED())
+#else
#define SDMMC_IS_CLK_DISABLED() __HAL_RCC_SDMMC2_IS_CLK_DISABLED()
+#endif
#define STATIC_AF_SDMMC_CK STATIC_AF_SDMMC2_CK
#define STATIC_AF_SDMMC_CMD STATIC_AF_SDMMC2_CMD
#define STATIC_AF_SDMMC_D0 STATIC_AF_SDMMC2_D0
@@ -96,9 +100,17 @@ static volatile uint8_t *sdmmc_buf_top;
#define MICROPY_HW_SDIO_CMD (pin_D2)
#endif
-#if defined(STM32H7)
+#if defined(STM32H7) || defined(STM32N6)
static uint32_t safe_divide(uint32_t denom) {
+ #if defined(STM32N6)
+ #if MICROPY_HW_SDIO_SDMMC == 1
+ uint32_t num = LL_RCC_GetSDMMCClockFreq(LL_RCC_SDMMC1_CLKSOURCE);
+ #else
+ uint32_t num = LL_RCC_GetSDMMCClockFreq(LL_RCC_SDMMC2_CLKSOURCE);
+ #endif
+ #else
uint32_t num = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC);
+ #endif
uint32_t divres;
divres = num / (2U * denom);
@@ -119,11 +131,15 @@ void sdio_init(uint32_t irq_pri) {
mp_hal_pin_config_alt_static(MICROPY_HW_SDIO_CMD, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_CMD);
SDMMC_CLK_ENABLE(); // enable SDIO peripheral
+ #if defined(STM32N6)
+ LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_SDMMC1);
+ LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_SDMMC2);
+ #endif
SDMMC_TypeDef *SDIO = SDMMC;
#if defined(STM32F7)
SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_PWRSAV | (120 - 2); // 1-bit, 400kHz
- #elif defined(STM32H7)
+ #elif defined(STM32H7) || defined(STM32N6)
SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_PWRSAV | safe_divide(400000U); // 1-bit, 400kHz
#else
SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_PWRSAV | (120 / 2); // 1-bit, 400kHz
@@ -172,7 +188,7 @@ void sdio_enable_high_speed_4bit(void) {
mp_hal_delay_us(10);
#if defined(STM32F7)
SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_WIDBUS_0 | SDMMC_CLKCR_BYPASS /*| SDMMC_CLKCR_PWRSAV*/; // 4-bit, 48MHz
- #elif defined(STM32H7)
+ #elif defined(STM32H7) || defined(STM32N6)
SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_WIDBUS_0 | safe_divide(48000000U); // 4-bit, 48MHz
#else
SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_WIDBUS_0; // 4-bit, 48MHz
@@ -199,7 +215,7 @@ void SDMMC_IRQHandler(void) {
sdmmc_irq_state = SDMMC_IRQ_STATE_DONE;
return;
}
- #if defined(STM32H7)
+ #if defined(STM32H7) || defined(STM32N6)
if (!sdmmc_dma) {
while (sdmmc_buf_cur < sdmmc_buf_top && (SDMMC->STA & SDMMC_STA_DPSMACT) && !(SDMMC->STA & SDMMC_STA_RXFIFOE)) {
*(uint32_t *)sdmmc_buf_cur = SDMMC->FIFO;
@@ -413,11 +429,15 @@ int sdio_transfer_cmd53(bool write, uint32_t block_size, uint32_t arg, size_t le
dma_nohal_init(&dma_SDIO_0, dma_config);
dma_nohal_start(&dma_SDIO_0, dma_src, dma_dest, dma_len);
#else
+ #if defined(STM32N6)
+ SDMMC->IDMABASER = (uint32_t)buf;
+ #else
SDMMC->IDMABASE0 = (uint32_t)buf;
+ #endif
SDMMC->IDMACTRL = SDMMC_IDMA_IDMAEN;
#endif
} else {
- #if defined(STM32H7)
+ #if defined(STM32H7) || defined(STM32N6)
SDMMC->IDMACTRL = 0;
#endif
}
diff --git a/ports/stm32/spi.c b/ports/stm32/spi.c
index 96dd170652..248075579a 100644
--- a/ports/stm32/spi.c
+++ b/ports/stm32/spi.c
@@ -106,7 +106,7 @@ const spi_t spi_obj[6] = {
#error "spi_obj needs updating for new value of MICROPY_HW_SUBGHZSPI_ID"
#endif
-#if defined(STM32H5) || defined(STM32H7)
+#if defined(STM32H5) || defined(STM32H7) || defined(STM32N6)
// STM32H5/H7 HAL requires SPI IRQs to be enabled and handled.
#if defined(MICROPY_HW_SPI1_SCK)
void SPI1_IRQHandler(void) {
@@ -176,6 +176,18 @@ void spi_init0(void) {
#if defined(MICROPY_HW_SUBGHZSPI_ID)
SPIHandleSubGhz.Instance = SUBGHZSPI;
#endif
+
+ #if defined(STM32N6)
+ // SPI1/2/3/6 clock configuration, PCLKx (max 200MHz).
+ LL_RCC_SetSPIClockSource(LL_RCC_SPI1_CLKSOURCE_PCLK2);
+ LL_RCC_SetSPIClockSource(LL_RCC_SPI2_CLKSOURCE_PCLK1);
+ LL_RCC_SetSPIClockSource(LL_RCC_SPI3_CLKSOURCE_PCLK1);
+ LL_RCC_SetSPIClockSource(LL_RCC_SPI6_CLKSOURCE_PCLK4);
+
+ // SPI4/5 clock configuration, IC14 (max 100MHz).
+ LL_RCC_SetSPIClockSource(LL_RCC_SPI4_CLKSOURCE_IC14);
+ LL_RCC_SetSPIClockSource(LL_RCC_SPI5_CLKSOURCE_IC14);
+ #endif
}
int spi_find_index(mp_obj_t id) {
@@ -256,6 +268,20 @@ static uint32_t spi_get_source_freq(SPI_HandleTypeDef *spi) {
} else {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI6);
}
+ #elif defined(STM32N6)
+ if (spi->Instance == SPI1) {
+ return LL_RCC_GetSPIClockFreq(LL_RCC_SPI1_CLKSOURCE);
+ } else if (spi->Instance == SPI2) {
+ return LL_RCC_GetSPIClockFreq(LL_RCC_SPI2_CLKSOURCE);
+ } else if (spi->Instance == SPI3) {
+ return LL_RCC_GetSPIClockFreq(LL_RCC_SPI3_CLKSOURCE);
+ } else if (spi->Instance == SPI4) {
+ return LL_RCC_GetSPIClockFreq(LL_RCC_SPI4_CLKSOURCE);
+ } else if (spi->Instance == SPI5) {
+ return LL_RCC_GetSPIClockFreq(LL_RCC_SPI5_CLKSOURCE);
+ } else {
+ return LL_RCC_GetSPIClockFreq(LL_RCC_SPI6_CLKSOURCE);
+ }
#else // !STM32F0, !STM32G0, !STM32H
#if defined(SPI2)
if (spi->Instance == SPI2) {
@@ -455,7 +481,10 @@ int spi_init(const spi_t *self, bool enable_nss_pin) {
if (pins[i] == NULL) {
continue;
}
- mp_hal_pin_config_alt(pins[i], mode, pull, AF_FN_SPI, (self - &spi_obj[0]) + 1);
+ if (!mp_hal_pin_config_alt(pins[i], mode, pull, AF_FN_SPI, (self - &spi_obj[0]) + 1)) {
+ // Pin does not have SPI alternate function.
+ return -MP_EINVAL;
+ }
}
// init the SPI device
@@ -470,7 +499,7 @@ int spi_init(const spi_t *self, bool enable_nss_pin) {
dma_invalidate_channel(self->tx_dma_descr);
dma_invalidate_channel(self->rx_dma_descr);
- #if defined(STM32H5) || defined(STM32H7)
+ #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6)
NVIC_SetPriority(irqn, IRQ_PRI_SPI);
HAL_NVIC_EnableIRQ(irqn);
#else
@@ -724,7 +753,7 @@ void spi_print(const mp_print_t *print, const spi_t *spi_obj, bool legacy) {
if (spi->State != HAL_SPI_STATE_RESET) {
if (spi->Init.Mode == SPI_MODE_MASTER) {
// compute baudrate
- #if defined(STM32H5) || defined(STM32H7)
+ #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6)
uint log_prescaler = (spi->Init.BaudRatePrescaler >> 28) + 1;
#else
uint log_prescaler = (spi->Init.BaudRatePrescaler >> 3) + 1;
diff --git a/ports/stm32/spibdev.c b/ports/stm32/spibdev.c
index fecd4a9915..d7a75ed240 100644
--- a/ports/stm32/spibdev.c
+++ b/ports/stm32/spibdev.c
@@ -32,6 +32,16 @@
#if MICROPY_HW_ENABLE_STORAGE
+#if MICROPY_HW_RUNS_FROM_EXT_FLASH
+// Disable all interrupts.
+#define FLASH_WRITE_ENTER uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION()
+#define FLASH_WRITE_EXIT MICROPY_END_ATOMIC_SECTION(atomic_state)
+#else
+// Prevent cache flushing and USB access.
+#define FLASH_WRITE_ENTER uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH)
+#define FLASH_WRITE_EXIT restore_irq_pri(basepri)
+#endif
+
int32_t spi_bdev_ioctl(spi_bdev_t *bdev, uint32_t op, uint32_t arg) {
switch (op) {
case BDEV_IOCTL_INIT:
@@ -68,6 +78,7 @@ int32_t spi_bdev_ioctl(spi_bdev_t *bdev, uint32_t op, uint32_t arg) {
}
#if MICROPY_HW_SPIFLASH_ENABLE_CACHE
+
int spi_bdev_readblocks(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
int ret = mp_spiflash_cached_read(&bdev->spiflash, block_num * FLASH_BLOCK_SIZE, num_blocks * FLASH_BLOCK_SIZE, dest);
@@ -87,20 +98,36 @@ int spi_bdev_writeblocks(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_nu
return ret;
}
+
+#elif FLASH_BLOCK_SIZE == MP_SPIFLASH_ERASE_BLOCK_SIZE
+
+int spi_bdev_readblocks(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
+ int ret = spi_bdev_readblocks_raw(bdev, dest, block_num, 0, num_blocks * FLASH_BLOCK_SIZE);
+ return ret;
+}
+
+int spi_bdev_writeblocks(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
+ int ret = spi_bdev_eraseblocks_raw(bdev, block_num, num_blocks * FLASH_BLOCK_SIZE);
+ if (ret == 0) {
+ ret = spi_bdev_writeblocks_raw(bdev, src, block_num, 0, num_blocks * FLASH_BLOCK_SIZE);
+ }
+ return ret;
+}
+
#endif // MICROPY_HW_SPIFLASH_ENABLE_CACHE
int spi_bdev_readblocks_raw(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t block_offset, uint32_t num_bytes) {
- uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
+ FLASH_WRITE_ENTER;
int ret = mp_spiflash_read(&bdev->spiflash, block_num * MP_SPIFLASH_ERASE_BLOCK_SIZE + block_offset, num_bytes, dest);
- restore_irq_pri(basepri);
+ FLASH_WRITE_EXIT;
return ret;
}
int spi_bdev_writeblocks_raw(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_num, uint32_t block_offset, uint32_t num_bytes) {
- uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
+ FLASH_WRITE_ENTER;
int ret = mp_spiflash_write(&bdev->spiflash, block_num * MP_SPIFLASH_ERASE_BLOCK_SIZE + block_offset, num_bytes, src);
- restore_irq_pri(basepri);
+ FLASH_WRITE_EXIT;
return ret;
}
@@ -108,9 +135,9 @@ int spi_bdev_writeblocks_raw(spi_bdev_t *bdev, const uint8_t *src, uint32_t bloc
int spi_bdev_eraseblocks_raw(spi_bdev_t *bdev, uint32_t block_num, uint32_t num_bytes) {
int ret = 0;
while (num_bytes >= MP_SPIFLASH_ERASE_BLOCK_SIZE) {
- uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
+ FLASH_WRITE_ENTER;
ret = mp_spiflash_erase_block(&bdev->spiflash, block_num * MP_SPIFLASH_ERASE_BLOCK_SIZE);
- restore_irq_pri(basepri);
+ FLASH_WRITE_EXIT;
if (ret) {
break;
}
diff --git a/ports/stm32/stm32.mk b/ports/stm32/stm32.mk
index 718fa8cf06..e6526fc6bd 100644
--- a/ports/stm32/stm32.mk
+++ b/ports/stm32/stm32.mk
@@ -43,19 +43,17 @@ ifneq ($(BUILDING_MBOOT),1)
# Select hardware floating-point support.
SUPPORTS_HARDWARE_FP_SINGLE = 0
SUPPORTS_HARDWARE_FP_DOUBLE = 0
-ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32F765xx STM32F767xx STM32F769xx STM32H743xx STM32H747xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ))
+ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32F765xx STM32F767xx STM32F769xx STM32H743xx STM32H747xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ STM32N657xx))
CFLAGS_CORTEX_M += -mfpu=fpv5-d16 -mfloat-abi=hard -mfp16-format=ieee
SUPPORTS_HARDWARE_FP_SINGLE = 1
SUPPORTS_HARDWARE_FP_DOUBLE = 1
-else
-ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 g0 l0 l1 wl))
+else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 g0 l0 l1 wl))
CFLAGS_CORTEX_M += -msoft-float
else
CFLAGS_CORTEX_M += -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mfp16-format=ieee
SUPPORTS_HARDWARE_FP_SINGLE = 1
endif
endif
-endif
# Options for particular MCU series.
CFLAGS_MCU_f0 = $(CFLAGS_CORTEX_M) -mtune=cortex-m0 -mcpu=cortex-m0
@@ -68,6 +66,7 @@ CFLAGS_MCU_l1 = $(CFLAGS_CORTEX_M) -mtune=cortex-m3 -mcpu=cortex-m3
CFLAGS_MCU_l4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4
CFLAGS_MCU_h5 = $(CFLAGS_CORTEX_M) -mtune=cortex-m33 -mcpu=cortex-m33
CFLAGS_MCU_h7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7
+CFLAGS_MCU_n6 = $(CFLAGS_CORTEX_M) -mtune=cortex-m55 -mcpu=cortex-m55 -mcmse
CFLAGS_MCU_wb = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4
CFLAGS_MCU_wl = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4
@@ -81,5 +80,20 @@ MPY_CROSS_MCU_ARCH_l1 = armv7m
MPY_CROSS_MCU_ARCH_l4 = armv7m
MPY_CROSS_MCU_ARCH_h5 = armv7m
MPY_CROSS_MCU_ARCH_h7 = armv7m
+MPY_CROSS_MCU_ARCH_n6 = armv7m # really armv8m
MPY_CROSS_MCU_ARCH_wb = armv7m
MPY_CROSS_MCU_ARCH_wl = armv7m
+
+# gcc up to 14.2.0 have a known loop-optimisation bug:
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116799
+# This bug manifests for Cortex M55 targets, so require a newer compiler on such targets.
+ifeq ($(MCU_SERIES),n6)
+# Check if GCC version is less than 14.3
+GCC_VERSION := $(shell $(CROSS_COMPILE)gcc -dumpversion | cut -d. -f1-2)
+GCC_VERSION_MAJOR := $(shell echo $(GCC_VERSION) | cut -d. -f1)
+GCC_VERSION_MINOR := $(shell echo $(GCC_VERSION) | cut -d. -f2)
+GCC_VERSION_NUM := $(shell echo $$(($(GCC_VERSION_MAJOR) * 100 + $(GCC_VERSION_MINOR))))
+ifeq ($(shell test $(GCC_VERSION_NUM) -lt 1403 && echo yes),yes)
+$(error Error: GCC $(GCC_VERSION) has known issues with Cortex-M55; upgrade to GCC 14.3+ for proper CM55 support)
+endif
+endif
diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c
index 4bf509bb94..3639e2f049 100644
--- a/ports/stm32/stm32_it.c
+++ b/ports/stm32/stm32_it.c
@@ -343,14 +343,22 @@ void OTG_FS_IRQHandler(void) {
}
#endif
#if MICROPY_HW_USB_HS
+#if defined(STM32N6)
+void USB1_OTG_HS_IRQHandler(void) {
+ IRQ_ENTER(USB1_OTG_HS_IRQn);
+ HAL_PCD_IRQHandler(&pcd_hs_handle);
+ IRQ_EXIT(USB1_OTG_HS_IRQn);
+}
+#else
void OTG_HS_IRQHandler(void) {
IRQ_ENTER(OTG_HS_IRQn);
HAL_PCD_IRQHandler(&pcd_hs_handle);
IRQ_EXIT(OTG_HS_IRQn);
}
#endif
+#endif
-#if MICROPY_HW_USB_FS || MICROPY_HW_USB_HS
+#if (MICROPY_HW_USB_FS || MICROPY_HW_USB_HS) && !defined(STM32N6)
/**
* @brief This function handles USB OTG Common FS/HS Wakeup functions.
* @param *pcd_handle for FS or HS
@@ -421,7 +429,7 @@ void OTG_FS_WKUP_IRQHandler(void) {
}
#endif
-#if MICROPY_HW_USB_HS
+#if MICROPY_HW_USB_HS && !defined(STM32N6)
/**
* @brief This function handles USB OTG HS Wakeup IRQ Handler.
* @param None
@@ -480,7 +488,7 @@ void ETH_WKUP_IRQHandler(void) {
}
#endif
-#if defined(STM32H5)
+#if defined(STM32H5) || defined(STM32N6)
void TAMP_IRQHandler(void) {
IRQ_ENTER(TAMP_IRQn);
Handle_EXTI_Irq(EXTI_RTC_TAMP);
@@ -502,6 +510,9 @@ void TAMP_STAMP_IRQHandler(void) {
#if defined(STM32H5)
void RTC_IRQHandler(void)
+#elif defined(STM32N6)
+#define RTC_WKUP_IRQn RTC_S_IRQn
+void RTC_S_IRQHandler(void)
#else
void RTC_WKUP_IRQHandler(void)
#endif
@@ -509,8 +520,8 @@ void RTC_WKUP_IRQHandler(void)
IRQ_ENTER(RTC_WKUP_IRQn);
#if defined(STM32G0) || defined(STM32G4) || defined(STM32WL)
RTC->MISR &= ~RTC_MISR_WUTMF; // clear wakeup interrupt flag
- #elif defined(STM32H5)
- RTC->SCR = RTC_SCR_CWUTF; // clear wakeup interrupt flag
+ #elif defined(STM32H5) || defined(STM32N6)
+ LL_RTC_ClearFlag_WUT(RTC);
#elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ)
RTC->SR &= ~RTC_SR_WUTF; // clear wakeup interrupt flag
#else
@@ -520,6 +531,12 @@ void RTC_WKUP_IRQHandler(void)
IRQ_EXIT(RTC_WKUP_IRQn);
}
+#if defined(STM32N6)
+void RTC_IRQHandler(void) {
+ RTC_S_IRQHandler();
+}
+#endif
+
#if defined(STM32F0) || defined(STM32G0) || defined(STM32L0)
#if defined(STM32G0)
diff --git a/ports/stm32/storage.c b/ports/stm32/storage.c
index d810261fbc..d26ac821e5 100644
--- a/ports/stm32/storage.c
+++ b/ports/stm32/storage.c
@@ -32,6 +32,7 @@
#include "led.h"
#include "storage.h"
#include "irq.h"
+#include "xspi.h"
#if MICROPY_HW_ENABLE_STORAGE
@@ -44,13 +45,17 @@
static bool storage_is_initialised = false;
+#if !defined(STM32N6)
static void storage_systick_callback(uint32_t ticks_ms);
+#endif
void storage_init(void) {
if (!storage_is_initialised) {
storage_is_initialised = true;
+ #if !defined(STM32N6)
systick_enable_dispatch(SYSTICK_DISPATCH_STORAGE, storage_systick_callback);
+ #endif
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0);
@@ -58,10 +63,12 @@ void storage_init(void) {
MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_INIT, 0);
#endif
+ #if !defined(STM32N6)
// Enable the flash IRQ, which is used to also call our storage IRQ handler
// It must go at the same priority as USB (see comment in irq.h).
NVIC_SetPriority(FLASH_IRQn, IRQ_PRI_FLASH);
HAL_NVIC_EnableIRQ(FLASH_IRQn);
+ #endif
}
}
@@ -77,6 +84,7 @@ uint32_t storage_get_block_count(void) {
#endif
}
+#if !defined(STM32N6)
static void storage_systick_callback(uint32_t ticks_ms) {
if (STORAGE_IDLE_TICK(ticks_ms)) {
// Trigger a FLASH IRQ to execute at a lower priority
@@ -96,6 +104,7 @@ void FLASH_IRQHandler(void) {
#endif
IRQ_EXIT(FLASH_IRQn);
}
+#endif
void storage_flush(void) {
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_SYNC, 0);
@@ -235,11 +244,11 @@ int storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_bl
// Board defined an external SPI flash for use with extended block protocol
#define MICROPY_HW_BDEV_BLOCKSIZE_EXT (MP_SPIFLASH_ERASE_BLOCK_SIZE)
#define MICROPY_HW_BDEV_READBLOCKS_EXT(dest, bl, off, len) \
- (spi_bdev_readblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (dest), (bl), (off), (len)))
+ (spi_bdev_readblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (dest), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES / MP_SPIFLASH_ERASE_BLOCK_SIZE + (bl), (off), (len)))
#define MICROPY_HW_BDEV_WRITEBLOCKS_EXT(src, bl, off, len) \
- (spi_bdev_writeblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (src), (bl), (off), (len)))
+ (spi_bdev_writeblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (src), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES / MP_SPIFLASH_ERASE_BLOCK_SIZE + (bl), (off), (len)))
#define MICROPY_HW_BDEV_ERASEBLOCKS_EXT(bl, len) \
- (spi_bdev_eraseblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (bl), (len)))
+ (spi_bdev_eraseblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES / MP_SPIFLASH_ERASE_BLOCK_SIZE + (bl), (len)))
#elif (MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2) && MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE
// Board uses littlefs and internal flash, so enable extended block protocol on internal flash
diff --git a/ports/stm32/storage.h b/ports/stm32/storage.h
index accf6c3904..75cb0e9c1e 100644
--- a/ports/stm32/storage.h
+++ b/ports/stm32/storage.h
@@ -28,7 +28,11 @@
#include "drivers/memory/spiflash.h"
+#if defined(STM32N6)
+#define FLASH_BLOCK_SIZE (4096)
+#else
#define FLASH_BLOCK_SIZE (512)
+#endif
#define FLASH_PART1_START_BLOCK (0x100)
// Try to match Python-level VFS block protocol where possible for these constants
diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c
index 9d65b484cd..4ec467d9db 100644
--- a/ports/stm32/timer.c
+++ b/ports/stm32/timer.c
@@ -261,6 +261,12 @@ uint32_t timer_get_source_freq(uint32_t tim_id) {
}
}
+ #elif defined(STM32N6)
+
+ // Timers are clocked either by ck_timg1 or ck_timg2.
+ // Both of those have the same frequency: sys_bus_ck / prescaler(TIMPRE)
+ return LL_RCC_GetSystemClockFreq() / (1 << LL_RCC_GetTIMPrescaler());
+
#else
uint32_t source, clk_div;
@@ -846,7 +852,9 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
TIM_ENTRY(1, TIM1_UP_TIM16_IRQn),
#endif
#endif
+
TIM_ENTRY(2, TIM2_IRQn),
+
#if defined(TIM3)
#if defined(STM32G0B1xx) || defined(STM32G0C1xx)
TIM_ENTRY(3, TIM3_TIM4_IRQn),
@@ -854,6 +862,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
TIM_ENTRY(3, TIM3_IRQn),
#endif
#endif
+
#if defined(TIM4)
#if defined(STM32G0B1xx) || defined(STM32G0C1xx)
TIM_ENTRY(3, TIM3_TIM4_IRQn),
@@ -861,20 +870,23 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
TIM_ENTRY(4, TIM4_IRQn),
#endif
#endif
+
#if defined(TIM5)
TIM_ENTRY(5, TIM5_IRQn),
#endif
+
#if defined(TIM6)
#if defined(STM32F412Zx) || defined(STM32L1)
TIM_ENTRY(6, TIM6_IRQn),
#elif defined(STM32G0)
TIM_ENTRY(6, TIM6_DAC_LPTIM1_IRQn),
- #elif defined(STM32H5)
+ #elif defined(STM32H5) || defined(STM32N6)
TIM_ENTRY(6, TIM6_IRQn),
#else
TIM_ENTRY(6, TIM6_DAC_IRQn),
#endif
#endif
+
#if defined(TIM7)
#if defined(STM32G0)
TIM_ENTRY(7, TIM7_LPTIM2_IRQn),
@@ -894,7 +906,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#endif
#if defined(TIM9)
- #if defined(STM32L1)
+ #if defined(STM32L1) || defined(STM32N6)
TIM_ENTRY(9, TIM9_IRQn),
#else
TIM_ENTRY(9, TIM1_BRK_TIM9_IRQn),
@@ -902,7 +914,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#endif
#if defined(TIM10)
- #if defined(STM32L1)
+ #if defined(STM32L1) || defined(STM32N6)
TIM_ENTRY(10, TIM10_IRQn),
#else
TIM_ENTRY(10, TIM1_UP_TIM10_IRQn),
@@ -910,7 +922,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#endif
#if defined(TIM11)
- #if defined(STM32L1)
+ #if defined(STM32L1) || defined(STM32N6)
TIM_ENTRY(11, TIM11_IRQn),
#else
TIM_ENTRY(11, TIM1_TRG_COM_TIM11_IRQn),
@@ -918,7 +930,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#endif
#if defined(TIM12)
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
TIM_ENTRY(12, TIM12_IRQn),
#else
TIM_ENTRY(12, TIM8_BRK_TIM12_IRQn),
@@ -926,21 +938,21 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#endif
#if defined(TIM13)
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
TIM_ENTRY(13, TIM13_IRQn),
#else
TIM_ENTRY(13, TIM8_UP_TIM13_IRQn),
#endif
#endif
- #if defined(STM32F0) || defined(STM32G0) || defined(STM32H5)
+ #if defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32N6)
TIM_ENTRY(14, TIM14_IRQn),
#elif defined(TIM14)
TIM_ENTRY(14, TIM8_TRG_COM_TIM14_IRQn),
#endif
#if defined(TIM15)
- #if defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7)
+ #if defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6)
TIM_ENTRY(15, TIM15_IRQn),
#else
TIM_ENTRY(15, TIM1_BRK_TIM15_IRQn),
@@ -950,7 +962,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#if defined(TIM16)
#if defined(STM32G0B1xx) || defined(STM32G0C1xx)
TIM_ENTRY(16, TIM16_FDCAN_IT0_IRQn),
- #elif defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32WL)
+ #elif defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WL)
TIM_ENTRY(16, TIM16_IRQn),
#else
TIM_ENTRY(16, TIM1_UP_TIM16_IRQn),
@@ -960,7 +972,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#if defined(TIM17)
#if defined(STM32G0B1xx) || defined(STM32G0C1xx)
TIM_ENTRY(17, TIM17_FDCAN_IT1_IRQn),
- #elif defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32WL)
+ #elif defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WL)
TIM_ENTRY(17, TIM17_IRQn),
#else
TIM_ENTRY(17, TIM1_TRG_COM_TIM17_IRQn),
diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c
index 91db91395e..9354af4a29 100644
--- a/ports/stm32/uart.c
+++ b/ports/stm32/uart.c
@@ -91,7 +91,7 @@
#define USART_CR3_IE_ALL (USART_CR3_IE_BASE | USART_CR3_WUFIE)
#endif
-#elif defined(STM32H7)
+#elif defined(STM32H7) || defined(STM32N6)
#define USART_CR1_IE_ALL (USART_CR1_IE_BASE | USART_CR1_RXFFIE | USART_CR1_TXFEIE | USART_CR1_EOBIE | USART_CR1_RTOIE | USART_CR1_CMIE)
#define USART_CR2_IE_ALL (USART_CR2_IE_BASE)
#define USART_CR3_IE_ALL (USART_CR3_IE_BASE | USART_CR3_RXFTIE | USART_CR3_TCBGTIE | USART_CR3_TXFTIE | USART_CR3_WUFIE)
@@ -157,6 +157,18 @@ void uart_init0(void) {
if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit) != HAL_OK) {
MICROPY_BOARD_FATAL_ERROR("HAL_RCCEx_PeriphCLKConfig");
}
+ #elif defined(STM32N6)
+ // UART clock configuration, IC14 (max 100MHz).
+ LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_IC14);
+ LL_RCC_SetUSARTClockSource(LL_RCC_USART2_CLKSOURCE_IC14);
+ LL_RCC_SetUSARTClockSource(LL_RCC_USART3_CLKSOURCE_IC14);
+ LL_RCC_SetUARTClockSource(LL_RCC_UART4_CLKSOURCE_IC14);
+ LL_RCC_SetUARTClockSource(LL_RCC_UART5_CLKSOURCE_IC14);
+ LL_RCC_SetUSARTClockSource(LL_RCC_USART6_CLKSOURCE_IC14);
+ LL_RCC_SetUARTClockSource(LL_RCC_UART7_CLKSOURCE_IC14);
+ LL_RCC_SetUARTClockSource(LL_RCC_UART8_CLKSOURCE_IC14);
+ LL_RCC_SetUARTClockSource(LL_RCC_UART9_CLKSOURCE_IC14);
+ LL_RCC_SetUSARTClockSource(LL_RCC_USART10_CLKSOURCE_IC14);
#endif
}
@@ -653,7 +665,7 @@ bool uart_init(machine_uart_obj_t *uart_obj,
huart.Init.HwFlowCtl = flow;
huart.Init.OverSampling = UART_OVERSAMPLING_16;
- #if defined(STM32G4) // H7 and WB also have fifo..
+ #if defined(STM32G4) || defined(STM32H7) // WB also has a fifo..
huart.FifoMode = UART_FIFOMODE_ENABLE;
#endif
@@ -661,7 +673,7 @@ bool uart_init(machine_uart_obj_t *uart_obj,
huart.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
#endif
- #if defined(STM32H7) || defined(STM32WB)
+ #if defined(STM32H7) || defined(STM32N6) || defined(STM32WB)
// Compute the smallest prescaler that will allow the given baudrate.
uint32_t presc = UART_PRESCALER_DIV1;
if (uart_obj->uart_id == PYB_LPUART_1) {
@@ -689,6 +701,10 @@ bool uart_init(machine_uart_obj_t *uart_obj,
uart_obj->is_enabled = true;
uart_obj->attached_to_repl = false;
+ #if defined(STM32F4) || defined(STM32L1)
+ uart_obj->suppress_idle_irq = true;
+ #endif
+
if (bits == UART_WORDLENGTH_9B && parity == UART_PARITY_NONE) {
uart_obj->char_mask = 0x1ff;
uart_obj->char_width = CHAR_WIDTH_9BIT;
@@ -701,6 +717,12 @@ bool uart_init(machine_uart_obj_t *uart_obj,
uart_obj->char_width = CHAR_WIDTH_8BIT;
}
+ #if defined(STM32H7)
+ HAL_UARTEx_SetTxFifoThreshold(&huart, UART_TXFIFO_THRESHOLD_1_8);
+ HAL_UARTEx_SetRxFifoThreshold(&huart, UART_RXFIFO_THRESHOLD_1_8);
+ HAL_UARTEx_EnableFifoMode(&huart);
+ #endif
+
uart_obj->mp_irq_trigger = 0;
uart_obj->mp_irq_obj = NULL;
@@ -966,6 +988,29 @@ uint32_t uart_get_source_freq(machine_uart_obj_t *self) {
default:
break;
}
+
+ #elif defined(STM32N6)
+
+ static const uint16_t is_usart = 1 << 10 | 1 << 6 | 1 << 3 | 1 << 2 | 1 << 1;
+ static const uint32_t clksource[] = {
+ LL_RCC_USART1_CLKSOURCE,
+ LL_RCC_USART2_CLKSOURCE,
+ LL_RCC_USART3_CLKSOURCE,
+ LL_RCC_UART4_CLKSOURCE,
+ LL_RCC_UART5_CLKSOURCE,
+ LL_RCC_USART6_CLKSOURCE,
+ LL_RCC_UART7_CLKSOURCE,
+ LL_RCC_UART8_CLKSOURCE,
+ LL_RCC_UART9_CLKSOURCE,
+ LL_RCC_USART10_CLKSOURCE,
+ };
+
+ if (is_usart & (1 << self->uart_id)) {
+ uart_clk = LL_RCC_GetUSARTClockFreq(clksource[self->uart_id - 1]);
+ } else {
+ uart_clk = LL_RCC_GetUARTClockFreq(clksource[self->uart_id - 1]);
+ }
+
#else
if (self->uart_id == 1
#if defined(USART6)
@@ -991,14 +1036,14 @@ uint32_t uart_get_baudrate(machine_uart_obj_t *self) {
#if defined(LPUART1)
if (self->uart_id == PYB_LPUART_1) {
return LL_LPUART_GetBaudRate(self->uartx, uart_get_source_freq(self)
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
, self->uartx->PRESC
#endif
);
}
#endif
return LL_USART_GetBaudRate(self->uartx, uart_get_source_freq(self),
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
self->uartx->PRESC,
#endif
LL_USART_OVERSAMPLING_16);
@@ -1008,7 +1053,7 @@ void uart_set_baudrate(machine_uart_obj_t *self, uint32_t baudrate) {
#if defined(LPUART1)
if (self->uart_id == PYB_LPUART_1) {
LL_LPUART_SetBaudRate(self->uartx, uart_get_source_freq(self),
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
LL_LPUART_PRESCALER_DIV1,
#endif
baudrate);
@@ -1016,7 +1061,7 @@ void uart_set_baudrate(machine_uart_obj_t *self, uint32_t baudrate) {
}
#endif
LL_USART_SetBaudRate(self->uartx, uart_get_source_freq(self),
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
LL_USART_PRESCALER_DIV1,
#endif
LL_USART_OVERSAMPLING_16, baudrate);
@@ -1067,7 +1112,7 @@ int uart_rx_char(machine_uart_obj_t *self) {
return data;
} else {
// no buffering
- #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
int data = self->uartx->RDR & self->char_mask;
self->uartx->ICR = USART_ICR_ORECF; // clear ORE if it was set
return data;
@@ -1141,6 +1186,9 @@ size_t uart_tx_data(machine_uart_obj_t *self, const void *src_in, size_t num_cha
// timeout_char by FIFO size + 1.
// STM32G4 has 8 words FIFO.
timeout = (8 + 1) * self->timeout_char;
+ #elif defined(STM32H7)
+ // STM32H7 has 16 words FIFO.
+ timeout = (16 + 1) * self->timeout_char;
#else
// The timeout specified here is for waiting for the TX data register to
// become empty (ie between chars), as well as for the final char to be
@@ -1219,7 +1267,7 @@ void uart_irq_handler(mp_uint_t uart_id) {
uint16_t next_head = (self->read_buf_head + 1) % self->read_buf_len;
if (next_head != self->read_buf_tail) {
// only read data if room in buf
- #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
int data = self->uartx->RDR; // clears UART_FLAG_RXNE
#else
self->mp_irq_flags = self->uartx->SR; // resample to get any new flags since next read of DR will clear SR
@@ -1265,6 +1313,9 @@ void uart_irq_handler(mp_uint_t uart_id) {
self->uartx->CR1 &= ~USART_CR1_RXNEIE;
}
}
+ if (self->suppress_idle_irq) {
+ self->mp_irq_flags &= ~USART_SR_IDLE;
+ }
#else
self->uartx->ICR = self->mp_irq_flags & (USART_ICR_IDLECF | USART_ICR_ORECF);
#endif
@@ -1273,6 +1324,14 @@ void uart_irq_handler(mp_uint_t uart_id) {
if (self->mp_irq_trigger & self->mp_irq_flags) {
mp_irq_handler(self->mp_irq_obj);
}
+
+ #if defined(STM32F4) || defined(STM32L1)
+ if (did_clear_sr) {
+ self->suppress_idle_irq = false;
+ } else {
+ self->suppress_idle_irq = true;
+ }
+ #endif
}
static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) {
diff --git a/ports/stm32/uart.h b/ports/stm32/uart.h
index de4b70cdea..d92434c641 100644
--- a/ports/stm32/uart.h
+++ b/ports/stm32/uart.h
@@ -26,6 +26,7 @@
#ifndef MICROPY_INCLUDED_STM32_UART_H
#define MICROPY_INCLUDED_STM32_UART_H
+#include "py/mphal.h"
#include "shared/runtime/mpirq.h"
typedef enum {
@@ -63,6 +64,9 @@ typedef struct _machine_uart_obj_t {
pyb_uart_t uart_id : 8;
bool is_static : 1;
bool is_enabled : 1;
+ #if defined(STM32F4) || defined(STM32L1)
+ bool suppress_idle_irq : 1; // whether the RX idle IRQ is suppressed (F4/L1 only)
+ #endif
bool attached_to_repl; // whether the UART is attached to REPL
byte char_width; // 0 for 7,8 bit chars, 1 for 9 bit chars
uint16_t char_mask; // 0x7f for 7 bit, 0xff for 8 bit, 0x1ff for 9 bit
diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c
index af9dd1d70e..2d70dcb261 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(STM32H7) || defined(STM32N6)
#define MAX_ENDPOINT(dev_id) (8)
#endif
diff --git a/ports/stm32/usbd_conf.c b/ports/stm32/usbd_conf.c
index 829037ba93..7a9e63c9f3 100644
--- a/ports/stm32/usbd_conf.c
+++ b/ports/stm32/usbd_conf.c
@@ -51,6 +51,11 @@ PCD_HandleTypeDef pcd_hs_handle;
#define USB_OTG_FS USB
#endif
+#if defined(STM32N6)
+#define USB_OTG_HS USB1_OTG_HS
+#define OTG_HS_IRQn USB1_OTG_HS_IRQn
+#endif
+
/*******************************************************************************
PCD BSP Routines
*******************************************************************************/
@@ -191,6 +196,10 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) {
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);
+ #elif defined(STM32N6)
+
+ // These MCUs have dedicated USB pins.
+
#else
// Other MCUs have an alternate function for GPIO's to be in USB mode.
@@ -220,6 +229,23 @@ 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(STM32N6)
+
+ __HAL_RCC_USB1_OTG_HS_FORCE_RESET();
+ __HAL_RCC_USB1_OTG_HS_PHY_FORCE_RESET();
+ __HAL_RCC_USB1_OTG_HS_PHY_RELEASE_RESET();
+ __HAL_RCC_USB1_OTG_HS_RELEASE_RESET();
+
+ LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_OTG1);
+ LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_OTGPHY1);
+ LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_OTG1);
+ LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_OTGPHY1);
+
+ // Select 24MHz clock.
+ MODIFY_REG(USB1_HS_PHYC->USBPHYC_CR, USB_USBPHYC_CR_FSEL, 2 << USB_USBPHYC_CR_FSEL_Pos);
+
+ #else
+
// Enable calling WFI and correct function of the embedded USB_FS_IN_HS phy
__HAL_RCC_USB_OTG_HS_ULPI_CLK_SLEEP_DISABLE();
__HAL_RCC_USB_OTG_HS_CLK_SLEEP_ENABLE();
@@ -235,6 +261,8 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) {
__HAL_RCC_USB_OTG_HS_CLK_ENABLE();
+ #endif
+
#else // !MICROPY_HW_USB_HS_IN_FS
// Configure USB HS GPIOs
@@ -283,7 +311,12 @@ void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd) {
#if MICROPY_HW_USB_HS
if (hpcd->Instance == USB_OTG_HS) {
/* Disable USB FS Clocks */
+ #if defined(STM32N6)
+ LL_AHB5_GRP1_DisableClock(LL_AHB5_GRP1_PERIPH_OTG1);
+ LL_AHB5_GRP1_DisableClock(LL_AHB5_GRP1_PERIPH_OTGPHY1);
+ #else
__USB_OTG_HS_CLK_DISABLE();
+ #endif
}
#endif
@@ -517,7 +550,7 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev, int high_speed, const
#if MICROPY_HW_USB_HS_IN_FS
- #if defined(STM32F723xx) || defined(STM32F733xx)
+ #if defined(STM32F723xx) || defined(STM32F733xx) || defined(STM32N6)
pcd_hs_handle.Init.phy_itface = USB_OTG_HS_EMBEDDED_PHY;
#else
pcd_hs_handle.Init.phy_itface = PCD_PHY_EMBEDDED;
diff --git a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h
index 2c90ce165e..34f0412534 100644
--- a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h
+++ b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h
@@ -11,7 +11,7 @@
// Work out if we should support USB high-speed device mode
#if MICROPY_HW_USB_HS \
- && (!MICROPY_HW_USB_HS_IN_FS || defined(STM32F723xx) || defined(STM32F733xx))
+ && (!MICROPY_HW_USB_HS_IN_FS || defined(STM32F723xx) || defined(STM32F733xx) || defined(STM32N6))
#define USBD_SUPPORT_HS_MODE (1)
#else
#define USBD_SUPPORT_HS_MODE (0)
@@ -31,7 +31,11 @@
#else
#define CDC_DATA_MAX_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE
#endif
+#if defined(STM32N6)
+#define MSC_MEDIA_PACKET (4096) // must be at least the SPI flash erase size
+#else
#define MSC_MEDIA_PACKET (2048) // was 8192; how low can it go whilst still working?
+#endif
#define HID_DATA_FS_MAX_PACKET_SIZE (64) // endpoint IN & OUT packet size
// Maximum number of LUN that can be exposed on the MSC interface
diff --git a/ports/stm32/vfs_rom_ioctl.c b/ports/stm32/vfs_rom_ioctl.c
index 7592aa22d6..5dbc855861 100644
--- a/ports/stm32/vfs_rom_ioctl.c
+++ b/ports/stm32/vfs_rom_ioctl.c
@@ -33,6 +33,7 @@
#include "flash.h"
#include "qspi.h"
#include "storage.h"
+#include "xspi.h"
#if MICROPY_VFS_ROM_IOCTL
@@ -142,6 +143,18 @@ mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) {
return MP_OBJ_NEW_SMALL_INT(4);
}
#endif
+
+ #if MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI
+ if (xspi_is_valid_addr(&xspi_flash2, dest)) {
+ dest -= xspi_get_xip_base(&xspi_flash2);
+ dest_max -= xspi_get_xip_base(&xspi_flash2);
+ int ret = spi_bdev_eraseblocks_raw(MICROPY_HW_ROMFS_XSPI_SPIBDEV_OBJ, dest / MP_SPIFLASH_ERASE_BLOCK_SIZE, dest_max - dest + MP_SPIFLASH_ERASE_BLOCK_SIZE - 1);
+ if (ret < 0) {
+ return MP_OBJ_NEW_SMALL_INT(ret);
+ }
+ return MP_OBJ_NEW_SMALL_INT(4);
+ }
+ #endif
}
if (cmd == MP_VFS_ROM_IOCTL_WRITE) {
@@ -170,6 +183,14 @@ mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) {
return MP_OBJ_NEW_SMALL_INT(ret);
}
#endif
+
+ #if MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI
+ if (xspi_is_valid_addr(&xspi_flash2, dest)) {
+ dest -= xspi_get_xip_base(&xspi_flash2);
+ int ret = spi_bdev_writeblocks_raw(MICROPY_HW_ROMFS_XSPI_SPIBDEV_OBJ, bufinfo.buf, 0, dest, bufinfo.len);
+ return MP_OBJ_NEW_SMALL_INT(ret);
+ }
+ #endif
}
return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL);
diff --git a/ports/stm32/xspi.c b/ports/stm32/xspi.c
new file mode 100644
index 0000000000..b113110c05
--- /dev/null
+++ b/ports/stm32/xspi.c
@@ -0,0 +1,599 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024-2025 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.
+ */
+
+// This XSPI driver is currently configured to run in 1-line (SPI) mode.
+// It uses the mp_qspi_proto_t QSPI protocol and translates quad-commands
+// into 1-line commands.
+
+#include <string.h>
+#include "py/mperrno.h"
+#include "py/mphal.h"
+#include "xspi.h"
+
+#if defined(MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2)
+
+#ifndef MICROPY_HW_XSPI_PRESCALER
+#define MICROPY_HW_XSPI_PRESCALER (4) // F_CLK = F_AHB/4
+#endif
+
+#ifndef MICROPY_HW_XSPI_CS_HIGH_CYCLES
+#define MICROPY_HW_XSPI_CS_HIGH_CYCLES (2) // nCS stays high for 4 cycles
+#endif
+
+// Currently hard-coded to use XSPI2 instance.
+#define XSPIx (XSPI2)
+
+// For XSPI2, PN0 through PN12.
+#define XSPI2_AF (9)
+
+typedef struct _xspi_flash_t {
+ XSPI_TypeDef *xspi;
+ uintptr_t xip_base;
+} xspi_flash_t;
+
+const xspi_flash_t xspi_flash1 = {
+ .xspi = XSPI1,
+ .xip_base = 0x90000000,
+};
+
+const xspi_flash_t xspi_flash2 = {
+ .xspi = XSPI2,
+ .xip_base = 0x70000000,
+};
+
+static bool xspi_dtr_enabled = false;
+
+#ifdef pyb_pin_FLASH_RESET
+// Can't rely on SysTick being available, so use a busy loop for delays.
+// The timing here is approximate and assumes a CPU frequency of 800MHz.
+static void xspi_delay_us(unsigned int us) {
+ while (us--) {
+ for (unsigned int i = 0; i < 800; ++i) {
+ __NOP();
+ }
+ }
+}
+#endif
+
+static inline void mp_hal_pin_config_alt_speed(mp_hal_pin_obj_t pin, uint32_t pull, uint32_t alt, uint32_t speed) {
+ mp_hal_pin_config(pin, MP_HAL_PIN_MODE_ALT, pull, alt);
+ mp_hal_pin_config_speed(pin, speed);
+}
+
+static int xspi_read_111_ext(uint8_t cmd, bool addr_enabled, uint32_t addr, size_t len, uint8_t *dest);
+static int xspi_write_111_ext(uint8_t cmd, bool addr_enabled, uint32_t addr, size_t len, const uint8_t *src);
+static int xspi_read_888_dtr_ext(uint16_t cmd, bool addr_enabled, uint32_t addr, uint32_t num_dummy, size_t len, uint8_t *dest);
+static int xspi_write_888_dtr_ext(uint16_t cmd, bool addr_enabled, uint32_t addr, size_t len, const uint8_t *src);
+static void xspi_memory_map_111(void);
+static void xspi_memory_map_888(void);
+static void xspi_memory_map_exit(void);
+
+void xspi_init(void) {
+ // Configure XSPI pins.
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_CS, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_SCK, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_DQS, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO0, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO1, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO2, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO3, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO4, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO5, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO6, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO7, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+
+ LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_XSPIM);
+ LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_XSPI1);
+ LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_XSPI2);
+ LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_XSPI3);
+
+ LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_XSPIM);
+ LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_XSPI1);
+ LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_XSPI2);
+ LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_XSPI3);
+
+ LL_RCC_SetXSPIClockSource(LL_RCC_XSPI1_CLKSOURCE_HCLK);
+ LL_RCC_SetXSPIClockSource(LL_RCC_XSPI2_CLKSOURCE_HCLK);
+
+ LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_XSPIM);
+ LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_XSPI1);
+ LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_XSPI2);
+
+ // Configure XSPIM in direct mode.
+ XSPI1->CR &= ~XSPI_CR_EN;
+ XSPI2->CR &= ~XSPI_CR_EN;
+ XSPIM->CR = 0;
+
+ // Configure the XSPIx peripheral.
+
+ XSPIx->CR =
+ 3 << XSPI_CR_FTHRES_Pos // 4 byte must be available to read/write
+ | 0 << XSPI_CR_MSEL_Pos // FLASH 0 selected
+ | 0 << XSPI_CR_CSSEL_Pos // use NCS1 as chip select
+ | 0 << XSPI_CR_DMM_Pos // dual-memory mode disabled
+ | 1 << XSPI_CR_TCEN_Pos // time-out counter enabled
+ | 0 << XSPI_CR_DMAEN_Pos // DMA disabled
+ | 0 << XSPI_CR_ABORT_Pos // no abort request
+ | 0 << XSPI_CR_EN_Pos // disabled
+ ;
+
+ XSPIx->DCR1 =
+ 1 << XSPI_DCR1_MTYP_Pos // Macronix mode
+ | (MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2 - 3 - 1) << XSPI_DCR1_DEVSIZE_Pos
+ | (MICROPY_HW_XSPI_CS_HIGH_CYCLES - 1) << XSPI_DCR1_CSHT_Pos
+ | 0 << XSPI_DCR1_FRCK_Pos // CLK is not free running
+ | 0 << XSPI_DCR1_CKMODE_Pos // CLK idles at low state
+ ;
+
+ XSPIx->DCR2 =
+ 0 << XSPI_DCR2_WRAPSIZE_Pos // separate wrap reads are not supported by the memory
+ | (MICROPY_HW_XSPI_PRESCALER - 1) << XSPI_DCR2_PRESCALER_Pos
+ ;
+
+ XSPIx->DCR3 =
+ 0
+ // 10 << XSPI_DCR3_CSBOUND_Pos // transaction boundary at 1024
+ ;
+
+ XSPIx->DCR4 =
+ 0 << XSPI_DCR4_REFRESH_Pos // refresh disabled (it's non-volatile memory)
+ ;
+
+ XSPIx->TCR = 0;
+
+ // Enable the XSPI peripheral.
+ XSPIx->CR |= XSPI_CR_EN;
+
+ // XSPIM init
+ XSPI1->CR &= ~(1 << XSPI_CR_EN_Pos);
+ XSPI2->CR &= ~(1 << XSPI_CR_EN_Pos);
+ XSPIM->CR = 0; // can also be (1 << 4) to pass through CS signal
+ XSPIx->CR |= 1 << XSPI_CR_EN_Pos;
+
+ #ifdef pyb_pin_FLASH_RESET
+ // Reset SPI flash to make sure it's in a known state (SPI mode).
+ mp_hal_pin_output(pyb_pin_FLASH_RESET);
+ mp_hal_pin_low(pyb_pin_FLASH_RESET);
+ xspi_delay_us(1000);
+ mp_hal_pin_high(pyb_pin_FLASH_RESET);
+ xspi_delay_us(10000);
+ #endif
+
+ // Enable memory-mapped mode.
+ // Can select either SPI or DTR mode.
+ if (1) {
+ xspi_switch_to_dtr();
+ xspi_memory_map_888();
+ } else {
+ xspi_memory_map_111();
+ }
+}
+
+uint32_t xspi_get_xip_base(const xspi_flash_t *self) {
+ return self->xip_base;
+}
+
+bool xspi_is_valid_addr(const xspi_flash_t *self, uint32_t addr) {
+ return self->xip_base <= addr && addr < self->xip_base + 256 * 1024 * 1024;
+}
+
+static int xspi_read_111_ext(uint8_t cmd, bool addr_enabled, uint32_t addr, size_t len, uint8_t *dest) {
+ uint32_t admode = addr_enabled ? 1 : 0;
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 1 << XSPI_CR_FMODE_Pos; // indirect read mode
+ XSPIx->CCR =
+ 1 << XSPI_CCR_DMODE_Pos // data on 1 line
+ | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address size
+ | admode << XSPI_CCR_ADMODE_Pos // address on 1 line, or disabled
+ | 1 << XSPI_CCR_IMODE_Pos // instruction on 1 line
+ ;
+ XSPIx->TCR = 0 << XSPI_TCR_DCYC_Pos; // 0 dummy cycles
+ XSPIx->DLR = len - 1; // number of bytes to read
+ XSPIx->IR = cmd; // read opcode (triggers the start of the transaction if address disabled)
+ if (addr_enabled) {
+ XSPIx->AR = addr; // triggers the start of the transaction
+ }
+
+ #if 0 // untested code
+ // Read in the data 4 bytes at a time if dest is aligned.
+ if (((uintptr_t)dest & 3) == 0) {
+ while (len >= 4) {
+ while (!(XSPIx->SR & XSPI_SR_FTF)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+ *(uint32_t *)dest = XSPIx->DR;
+ dest += 4;
+ len -= 4;
+ }
+ }
+ #endif
+
+ // Read in data 1 byte at a time.
+ while (len--) {
+ while (!((XSPIx->SR >> XSPI_SR_FLEVEL_Pos) & 0x3f)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+ *dest++ = *(volatile uint8_t *)&XSPIx->DR;
+ }
+
+ XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag
+
+ return 0;
+}
+
+static int xspi_write_111_ext(uint8_t cmd, bool addr_enabled, uint32_t addr, size_t len, const uint8_t *src) {
+ uint32_t dmode = len == 0 ? 0 : 1;
+ uint32_t admode = addr_enabled ? 1 : 0;
+
+ // Configure and start the transfer.
+ // Transfer starts with IR write if no address or data, with AR write if no data,
+ // otherwise with DR write.
+
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 0 << XSPI_CR_FMODE_Pos; // indirect write mode
+ XSPIx->CCR =
+ dmode << XSPI_CCR_DMODE_Pos // data on 1 line, or disabled
+ | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address size
+ | admode << XSPI_CCR_ADMODE_Pos // address on 1 line, or disabled
+ | 1 << XSPI_CCR_IMODE_Pos // instruction on 1 line
+ ;
+ XSPIx->TCR = 0 << XSPI_TCR_DCYC_Pos; // 0 dummy cycles
+ if (len != 0) {
+ XSPIx->DLR = len - 1;
+ }
+ XSPIx->IR = cmd; // write opcode
+ if (addr_enabled) {
+ XSPIx->AR = addr; // address
+ }
+
+ // Write out the data one byte at a time
+ while (len--) {
+ while (!(XSPIx->SR & XSPI_SR_FTF)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+ *(volatile uint8_t *)&XSPIx->DR = *src++;
+ }
+
+ // Wait for write to finish
+ while (!(XSPIx->SR & XSPI_SR_TCF)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+
+ XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag
+
+ // Wait for peripheral to return to idle.
+ while (XSPIx->SR & XSPI_SR_BUSY) {
+ }
+
+ return 0;
+}
+
+static int xspi_read_888_dtr_ext(uint16_t cmd, bool addr_enabled, uint32_t addr, uint32_t num_dummy, size_t len, uint8_t *dest) {
+ uint32_t admode = addr_enabled ? 4 : 0;
+
+ // Configure and start the transfer.
+ // Transfer starts with IR write if no address, otherwise with AR write.
+
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 1 << XSPI_CR_FMODE_Pos; // indirect read mode
+ XSPIx->CCR =
+ 1 << XSPI_CCR_DQSE_Pos // DQS enabled
+ | 1 << XSPI_CCR_DDTR_Pos // data DTR enabled
+ | 4 << XSPI_CCR_DMODE_Pos // data on 8 lines
+ | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address size
+ | 1 << XSPI_CCR_ADDTR_Pos // address DTR enabled
+ | admode << XSPI_CCR_ADMODE_Pos // address on 8 lines, or disabled
+ | 1 << XSPI_CCR_ISIZE_Pos // 16-bit instruction
+ | 1 << XSPI_CCR_IDTR_Pos // instruction DTR enabled
+ | 4 << XSPI_CCR_IMODE_Pos // instruction on 8 lines
+ ;
+ XSPIx->TCR = num_dummy << XSPI_TCR_DCYC_Pos; // N dummy cycles
+ XSPIx->DLR = len - 1;
+ XSPIx->IR = cmd; // read opcode
+ if (addr_enabled) {
+ XSPIx->AR = addr; // address
+ }
+
+ // Read in data 1 byte at a time.
+ while (len--) {
+ while (!((XSPIx->SR >> XSPI_SR_FLEVEL_Pos) & 0x3f)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+ *dest++ = *(volatile uint8_t *)&XSPIx->DR;
+ }
+
+ XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag
+
+ return 0;
+}
+
+static int xspi_write_888_dtr_ext(uint16_t cmd, bool addr_enabled, uint32_t addr, size_t len, const uint8_t *src) {
+ uint32_t dmode = len == 0 ? 0 : 4;
+ uint32_t admode = addr_enabled ? 4 : 0;
+
+ // Configure and start the transfer.
+ // Transfer starts with IR write if no address or data, with AR write if no data,
+ // otherwise with DR write.
+
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 0 << XSPI_CR_FMODE_Pos; // indirect write mode
+ XSPIx->CCR =
+ 1 << XSPI_CCR_DDTR_Pos // data DTR enabled
+ | dmode << XSPI_CCR_DMODE_Pos // data on 8 lines, or disabled
+ | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address size
+ | 1 << XSPI_CCR_ADDTR_Pos // address DTR enabled
+ | admode << XSPI_CCR_ADMODE_Pos // address on 8 lines, or disabled
+ | 1 << XSPI_CCR_ISIZE_Pos // 16-bit instruction
+ | 1 << XSPI_CCR_IDTR_Pos // instruction DTR enabled
+ | 4 << XSPI_CCR_IMODE_Pos // instruction on 8 lines
+ ;
+ XSPIx->TCR = 0 << XSPI_TCR_DCYC_Pos; // 0 dummy cycles
+ if (len != 0) {
+ XSPIx->DLR = len - 1;
+ }
+ XSPIx->IR = cmd; // write opcode
+ if (addr_enabled) {
+ XSPIx->AR = addr; // address
+ }
+
+ // Write out the data one byte at a time
+ while (len--) {
+ while (!(XSPIx->SR & XSPI_SR_FTF)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+ *(volatile uint8_t *)&XSPIx->DR = *src++;
+ }
+
+ // Wait for write to finish
+ while (!(XSPIx->SR & XSPI_SR_TCF)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+
+ XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag
+
+ // Wait for peripheral to return to idle.
+ while (XSPIx->SR & XSPI_SR_BUSY) {
+ }
+
+ return 0;
+}
+
+static void xspi_memory_map_111(void) {
+ XSPIx->CCR =
+ 1 << XSPI_CCR_DMODE_Pos // data on 1 line
+ | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address
+ | 1 << XSPI_CCR_ADMODE_Pos // address on 1 line
+ | 1 << XSPI_CCR_IMODE_Pos // instruction on 1 line
+ ;
+
+ XSPIx->TCR = 0 << XSPI_TCR_DCYC_Pos; // no dummy cycles
+ XSPIx->IR = 0x13; // READ4B
+ XSPIx->LPTR = 1024; // timeout period in number of CLK cycles
+
+ // Enable the XSPI peripheral in memory-mapped mode.
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 3 << XSPI_CR_FMODE_Pos;
+}
+
+static void xspi_memory_map_888(void) {
+ XSPIx->CCR =
+ 1 << XSPI_CCR_DQSE_Pos // DQS enabled
+ | 1 << XSPI_CCR_DDTR_Pos // data DTR enabled
+ | 4 << XSPI_CCR_DMODE_Pos // data on 8 lines
+ | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address
+ | 1 << XSPI_CCR_ADDTR_Pos // address DTR enabled
+ | 4 << XSPI_CCR_ADMODE_Pos // address on 8 lines
+ | 1 << XSPI_CCR_ISIZE_Pos // 16-bit instruction
+ | 1 << XSPI_CCR_IDTR_Pos // instruction DTR enabled
+ | 4 << XSPI_CCR_IMODE_Pos // instruction on 8 lines
+ ;
+
+ XSPIx->TCR = 20 << XSPI_TCR_DCYC_Pos; // 20 dummy cycles for reading (minimum, flash may insert more by holding DQS low)
+ XSPIx->IR = 0xee11; // octal DTR read mode (8DTRD)
+ XSPIx->LPTR = 1024; // timeout period in number of CLK cycles
+
+ // Enable the XSPI peripheral in memory-mapped mode.
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 3 << XSPI_CR_FMODE_Pos;
+}
+
+static void xspi_memory_map_exit(void) {
+ // Abort any ongoing transfer if peripheral is busy.
+ if (XSPIx->SR & XSPI_SR_BUSY) {
+ XSPIx->CR |= XSPI_CR_ABORT;
+ while (!(XSPIx->SR & XSPI_SR_TCF)) {
+ }
+ XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag
+ while (XSPIx->SR & XSPI_SR_BUSY) {
+ }
+ }
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 0 << XSPI_CR_FMODE_Pos; // indirect write mode
+}
+
+void xspi_switch_to_dtr(void) {
+ uint8_t buf[4];
+
+ // WREN.
+ xspi_write_111_ext(0x06, false, 0, 0, NULL);
+
+ // Wait WEL=1, with small timeout.
+ for (unsigned int i = 0; i < 100; ++i) {
+ xspi_read_111_ext(0x05, false, 0, 1, buf);
+ if (buf[0] & 2) {
+ break;
+ }
+ }
+
+ // Switch to DOPI DTR mode.
+ buf[0] = 2;
+ xspi_write_111_ext(0x72, true, 0x00000000, 1, buf);
+
+ xspi_dtr_enabled = true;
+}
+
+void xspi_switch_to_spi(void) {
+ uint8_t buf[4];
+
+ // WREN.
+ xspi_write_888_dtr_ext(0x06f9, false, 0, 0, NULL);
+
+ // Wait WEL=1, with small timeout.
+ for (unsigned int i = 0; i < 100; ++i) {
+ xspi_read_111_ext(0x05, false, 0, 1, buf);
+ if (buf[0] & 2) {
+ break;
+ }
+ }
+
+ // Switch to SPI mode.
+ buf[0] = 0;
+ buf[1] = 0;
+ xspi_write_888_dtr_ext(0x728d, true, 0x00000000, 2, buf);
+
+ xspi_dtr_enabled = false;
+}
+
+static int xspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) {
+ xspi_flash_t *self = self_in;
+ switch (cmd) {
+ case MP_QSPI_IOCTL_INIT:
+ // XSPI must be manually initialise by calling `xspi_init()` at boot.
+ // Here, just determine if it's in SPI or DTR mode.
+ xspi_dtr_enabled = XSPIx->IR == 0xee11;
+ break;
+ case MP_QSPI_IOCTL_BUS_ACQUIRE:
+ xspi_memory_map_exit();
+ break;
+ case MP_QSPI_IOCTL_BUS_RELEASE:
+ if (xspi_dtr_enabled) {
+ xspi_memory_map_888();
+ } else {
+ xspi_memory_map_111();
+ }
+ break;
+ case MP_QSPI_IOCTL_MEMORY_MODIFIED: {
+ uintptr_t *addr_len = (uintptr_t *)arg;
+ volatile void *addr = (volatile void *)(self->xip_base + addr_len[0]);
+ size_t len = addr_len[1];
+ SCB_InvalidateICache_by_Addr(addr, len);
+ SCB_InvalidateDCache_by_Addr(addr, len);
+ break;
+ }
+ }
+ return 0; // success
+}
+
+// These commands may be passed to this function.
+#define CMD_WREN (0x06)
+#define CMD_RSTEN (0x66)
+#define CMD_RESET (0x99)
+#define CMD_SLEEP (0xb9)
+#define CMD_AWAKE (0xab)
+static int xspi_write_cmd_data(void *self_in, uint8_t cmd, size_t len, uint32_t data) {
+ if (xspi_dtr_enabled) {
+ uint16_t cmd16 = 0;
+ if (cmd == CMD_WREN) {
+ cmd16 = 0x06f9;
+ } else if (cmd == CMD_SLEEP) {
+ cmd16 = 0xb946;
+ } else if (cmd == CMD_AWAKE) {
+ cmd16 = 0xab54;
+ }
+ return xspi_write_888_dtr_ext(cmd16, false, 0, len, (const uint8_t *)&data);
+ }
+ return xspi_write_111_ext(cmd, false, 0, len, (const uint8_t *)&data);
+}
+
+// These commands may be passed to this function.
+#define CMD_WRITE (0x02)
+#define CMD_WRITE_32 (0x12)
+#define CMD_SEC_ERASE (0x20)
+#define CMD_SEC_ERASE_32 (0x21)
+static int xspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) {
+ // Convert 24-bit address commands to 32-bit address commands.
+ if (cmd == CMD_WRITE) {
+ cmd = CMD_WRITE_32;
+ } else if (cmd == CMD_SEC_ERASE) {
+ cmd = CMD_SEC_ERASE_32;
+ }
+ if (xspi_dtr_enabled) {
+ uint16_t cmd16 = 0;
+ if (cmd == CMD_WRITE_32) {
+ cmd16 = 0x12ed;
+ } else if (cmd == CMD_SEC_ERASE_32) {
+ cmd16 = 0x21de;
+ }
+ return xspi_write_888_dtr_ext(cmd16, true, addr, len, src);
+ }
+ return xspi_write_111_ext(cmd, true, addr, len, src);
+}
+
+// These commands may be passed to this function.
+#define CMD_RDSR (0x05)
+#define CMD_RD_DEVID (0x9f)
+static int xspi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_t *dest) {
+ (void)self_in;
+ if (xspi_dtr_enabled) {
+ uint16_t cmd16 = 0;
+ uint32_t num_dummy = 0;
+ if (cmd == CMD_RDSR) {
+ cmd16 = 0x05fa;
+ num_dummy = 4;
+ len = 2;
+ } else if (cmd == CMD_RD_DEVID) {
+ // TODO this doesn't really work, because result is in STR format.
+ cmd16 = 0x9f60;
+ num_dummy = 4;
+ }
+ return xspi_read_888_dtr_ext(cmd16, true, 0, num_dummy, len, (uint8_t *)dest);
+ }
+ return xspi_read_111_ext(cmd, false, 0, len, (uint8_t *)dest);
+}
+
+static int xspi_direct_read(void *self_in, uint32_t addr, size_t len, uint8_t *dest) {
+ xspi_flash_t *self = self_in;
+ memcpy(dest, (const void *)(self->xip_base + addr), len);
+ return 0;
+}
+
+const mp_qspi_proto_t xspi_proto = {
+ .ioctl = xspi_ioctl,
+ .write_cmd_data = xspi_write_cmd_data,
+ .write_cmd_addr_data = xspi_write_cmd_addr_data,
+ .read_cmd = xspi_read_cmd,
+ .read_cmd_qaddr_qdata = NULL, // unused because .direct_read is set below, and caching is disabled
+ .direct_read = xspi_direct_read,
+};
+
+#endif // defined(MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2)
diff --git a/ports/stm32/xspi.h b/ports/stm32/xspi.h
new file mode 100644
index 0000000000..cd21662966
--- /dev/null
+++ b/ports/stm32/xspi.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024-2025 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.
+ */
+#ifndef MICROPY_INCLUDED_STM32_XSPI_H
+#define MICROPY_INCLUDED_STM32_XSPI_H
+
+#include "drivers/bus/qspi.h"
+
+typedef struct _xspi_flash_t xspi_flash_t;
+
+extern const mp_qspi_proto_t xspi_proto;
+extern const xspi_flash_t xspi_flash1;
+extern const xspi_flash_t xspi_flash2;
+
+void xspi_init(void);
+uint32_t xspi_get_xip_base(const xspi_flash_t *self);
+bool xspi_is_valid_addr(const xspi_flash_t *self, uint32_t addr);
+void xspi_switch_to_spi(void);
+void xspi_switch_to_dtr(void);
+
+#endif // MICROPY_INCLUDED_STM32_XSPI_H
diff --git a/ports/unix/Makefile b/ports/unix/Makefile
index 88fa1af045..8bd58a2542 100644
--- a/ports/unix/Makefile
+++ b/ports/unix/Makefile
@@ -52,6 +52,10 @@ CFLAGS += $(INC) $(CWARN) -std=gnu99 -DUNIX $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EX
# This option has no effect on 64-bit builds.
CFLAGS += -D_FILE_OFFSET_BITS=64
+# Force the use of 64-bits for time_t in C library functions on 32-bit platforms.
+# This option has no effect on 64-bit builds.
+CFLAGS += -D_TIME_BITS=64
+
# Debugging/Optimization
ifdef DEBUG
COPT ?= -Og
@@ -256,21 +260,24 @@ endif
include $(TOP)/py/mkrules.mk
-.PHONY: test test_full
+.PHONY: test test_full_no_native test_full
test: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py
$(eval DIRNAME=ports/$(notdir $(CURDIR)))
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py
-test_full: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py
+test_full_no_native: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py
$(eval DIRNAME=ports/$(notdir $(CURDIR)))
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py -d thread
- cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --emit native
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) -d basics float micropython
- cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) --emit native -d basics float micropython
cat $(TOP)/tests/basics/0prelim.py | ./$(BUILD)/$(PROG) | grep -q 'abc'
+test_full: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py test_full_no_native
+ $(eval DIRNAME=ports/$(notdir $(CURDIR)))
+ cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --emit native
+ cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) --emit native -d basics float micropython
+
test_gcov: test_full
gcov -o $(BUILD)/py $(TOP)/py/*.c
gcov -o $(BUILD)/extmod $(TOP)/extmod/*.c
diff --git a/ports/unix/README.md b/ports/unix/README.md
index b7aa6e3fef..656d4303d3 100644
--- a/ports/unix/README.md
+++ b/ports/unix/README.md
@@ -155,3 +155,21 @@ The default compiler optimisation level is -Os, or -Og if `DEBUG=1` is set.
Setting the variable `COPT` will explicitly set the optimisation level. For
example `make [other arguments] COPT=-O0 DEBUG=1` will build a binary with no
optimisations, assertions enabled, and debug symbols.
+
+### Sanitizers
+
+Sanitizers are extra runtime checks supported by gcc and clang. The CI process
+supports building with the "undefined behavior" (UBSan) or "address" (ASan)
+sanitizers. The script `tools/ci.sh` is the source of truth about how to build
+and run in these modes.
+
+Several classes of checks are disabled via compiler flags:
+
+* In the undefined behavior sanitizer, checks based on the presence of the
+ `non_null` attribute are disabled because the code makes technically incorrect
+ calls like `memset(NULL, 0, 0)`. A future C standard is likely to permit such
+ calls.
+* In the address sanitizer, `detect_stack_use_after_return` is disabled. This
+ check is intended to make sure locals in a "returned from" stack frame are not
+ used. However, this mode interferes with various assumptions that
+ MicroPython's stack checking, NLR, and GC rely on.
diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c
index 2b65b47fc5..0df6bf279a 100644
--- a/ports/unix/coverage.c
+++ b/ports/unix/coverage.c
@@ -184,6 +184,18 @@ static void pairheap_test(size_t nops, int *ops) {
mp_printf(&mp_plat_print, "\n");
}
+static mp_sched_node_t mp_coverage_sched_node;
+static bool coverage_sched_function_continue;
+
+static void coverage_sched_function(mp_sched_node_t *node) {
+ (void)node;
+ mp_printf(&mp_plat_print, "scheduled function\n");
+ if (coverage_sched_function_continue) {
+ // Re-scheduling node will cause it to run again next time scheduled functions are run
+ mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function);
+ }
+}
+
// function to run extra tests for things that can't be checked by scripts
static mp_obj_t extra_coverage(void) {
// mp_printf (used by ports that don't have a native printf)
@@ -208,6 +220,7 @@ static mp_obj_t extra_coverage(void) {
mp_printf(&mp_plat_print, "%X\n", 0x80000000); // should print unsigned
mp_printf(&mp_plat_print, "abc\n%"); // string ends in middle of format specifier
mp_printf(&mp_plat_print, "%%\n"); // literal % character
+ mp_printf(&mp_plat_print, ".%-3s.\n", "a"); // left adjust
}
// GC
@@ -463,6 +476,38 @@ static mp_obj_t extra_coverage(void) {
mp_int_t value_signed;
mpz_as_int_checked(&mpz, &value_signed);
mp_printf(&mp_plat_print, "%d\n", (int)value_signed);
+
+ // hash the zero mpz integer
+ mpz_set_from_int(&mpz, 0);
+ mp_printf(&mp_plat_print, "%d\n", mpz_hash(&mpz));
+
+ // convert the mpz zero integer to int
+ mp_printf(&mp_plat_print, "%d\n", mpz_as_int_checked(&mpz, &value_signed));
+ mp_printf(&mp_plat_print, "%d\n", value_signed);
+
+ // mpz_set_from_float with 0 as argument
+ mpz_set_from_float(&mpz, 0);
+ mp_printf(&mp_plat_print, "%f\n", mpz_as_float(&mpz));
+
+ // convert a large integer value (stored in a mpz) to mp_uint_t and to ll;
+ mp_obj_t obj_bigint = mp_obj_new_int_from_uint((mp_uint_t)0xdeadbeef);
+ mp_printf(&mp_plat_print, "%x\n", mp_obj_get_uint(obj_bigint));
+ obj_bigint = mp_obj_new_int_from_ll(0xc0ffee777c0ffeell);
+ long long value_ll = mp_obj_get_ll(obj_bigint);
+ mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll);
+
+ // convert a large integer value (stored via a struct object) to uint and to ll
+ // `deadbeef` global is an uctypes.struct defined by extra_coverage.py
+ obj_bigint = mp_load_global(MP_QSTR_deadbeef);
+ mp_printf(&mp_plat_print, "%x\n", mp_obj_get_uint(obj_bigint));
+ value_ll = mp_obj_get_ll(obj_bigint);
+ mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll);
+
+ // convert a smaller integer value to mp_uint_t and to ll
+ obj_bigint = mp_obj_new_int_from_uint(0xc0ffee);
+ mp_printf(&mp_plat_print, "%x\n", mp_obj_get_uint(obj_bigint));
+ value_ll = mp_obj_get_ll(obj_bigint);
+ mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll);
}
// runtime utils
@@ -480,7 +525,7 @@ static mp_obj_t extra_coverage(void) {
mp_call_function_2_protected(MP_OBJ_FROM_PTR(&mp_builtin_divmod_obj), mp_obj_new_str_from_cstr("abc"), mp_obj_new_str_from_cstr("abc"));
// mp_obj_int_get_checked with mp_obj_int_t that has a value that is a small integer
- mp_printf(&mp_plat_print, "%d\n", mp_obj_int_get_checked(mp_obj_int_new_mpz()));
+ mp_printf(&mp_plat_print, "%d\n", mp_obj_int_get_checked(MP_OBJ_FROM_PTR(mp_obj_int_new_mpz())));
// mp_obj_int_get_uint_checked with non-negative small-int
mp_printf(&mp_plat_print, "%d\n", (int)mp_obj_int_get_uint_checked(MP_OBJ_NEW_SMALL_INT(1)));
@@ -505,6 +550,22 @@ static mp_obj_t extra_coverage(void) {
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
}
+ // mp_obj_get_uint from a non-int object (should raise exception)
+ if (nlr_push(&nlr) == 0) {
+ mp_obj_get_uint(mp_const_none);
+ nlr_pop();
+ } else {
+ mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
+ }
+
+ // mp_obj_int_get_ll from a non-int object (should raise exception)
+ if (nlr_push(&nlr) == 0) {
+ mp_obj_get_ll(mp_const_none);
+ nlr_pop();
+ } else {
+ mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
+ }
+
// call mp_obj_new_exception_args (it's a part of the public C API and not used in the core)
mp_obj_print_exception(&mp_plat_print, mp_obj_new_exception_args(&mp_type_ValueError, 0, NULL));
}
@@ -557,12 +618,24 @@ static mp_obj_t extra_coverage(void) {
fun_bc.context = &context;
fun_bc.child_table = NULL;
fun_bc.bytecode = (const byte *)"\x01"; // just needed for n_state
+ #if MICROPY_PY_SYS_SETTRACE
+ struct _mp_raw_code_t rc = {};
+ fun_bc.rc = &rc;
+ #endif
mp_code_state_t *code_state = m_new_obj_var(mp_code_state_t, state, mp_obj_t, 1);
code_state->fun_bc = &fun_bc;
code_state->ip = (const byte *)"\x00"; // just needed for an invalid opcode
code_state->sp = &code_state->state[0];
code_state->exc_sp_idx = 0;
code_state->old_globals = NULL;
+ #if MICROPY_STACKLESS
+ code_state->prev = NULL;
+ #endif
+ #if MICROPY_PY_SYS_SETTRACE
+ code_state->prev_state = NULL;
+ code_state->frame = NULL;
+ #endif
+
mp_vm_return_kind_t ret = mp_execute_bytecode(code_state, MP_OBJ_NULL);
mp_printf(&mp_plat_print, "%d %d\n", ret, mp_obj_get_type(code_state->state[0]) == &mp_type_NotImplementedError);
}
@@ -621,6 +694,19 @@ static mp_obj_t extra_coverage(void) {
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
}
mp_handle_pending(true);
+
+ coverage_sched_function_continue = true;
+ mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function);
+ for (int i = 0; i < 3; ++i) {
+ mp_printf(&mp_plat_print, "loop\n");
+ mp_handle_pending(true);
+ }
+ // Clear this flag to prevent the function scheduling itself again
+ coverage_sched_function_continue = false;
+ // Will only run the first time through this loop, then not scheduled again
+ for (int i = 0; i < 3; ++i) {
+ mp_handle_pending(true);
+ }
}
// ringbuf
@@ -794,7 +880,7 @@ static mp_obj_t extra_coverage(void) {
mp_obj_streamtest_t *s2 = mp_obj_malloc(mp_obj_streamtest_t, &mp_type_stest_textio2);
// return a tuple of data for testing on the Python side
- mp_obj_t items[] = {(mp_obj_t)&str_no_hash_obj, (mp_obj_t)&bytes_no_hash_obj, MP_OBJ_FROM_PTR(s), MP_OBJ_FROM_PTR(s2)};
+ mp_obj_t items[] = {MP_OBJ_FROM_PTR(&str_no_hash_obj), MP_OBJ_FROM_PTR(&bytes_no_hash_obj), MP_OBJ_FROM_PTR(s), MP_OBJ_FROM_PTR(s2)};
return mp_obj_new_tuple(MP_ARRAY_SIZE(items), items);
}
MP_DEFINE_CONST_FUN_OBJ_0(extra_coverage_obj, extra_coverage);
diff --git a/ports/unix/main.c b/ports/unix/main.c
index 9f51573fbf..530e20a386 100644
--- a/ports/unix/main.c
+++ b/ports/unix/main.c
@@ -775,7 +775,7 @@ MP_NOINLINE int main_(int argc, char **argv) {
#endif
#if MICROPY_PY_BLUETOOTH
- void mp_bluetooth_deinit(void);
+ int mp_bluetooth_deinit(void);
mp_bluetooth_deinit();
#endif
diff --git a/ports/unix/modsocket.c b/ports/unix/modsocket.c
index 6d6059ae44..2aaa21183a 100644
--- a/ports/unix/modsocket.c
+++ b/ports/unix/modsocket.c
@@ -701,6 +701,7 @@ static const mp_rom_map_elem_t mp_module_socket_globals_table[] = {
C(MSG_DONTROUTE),
C(MSG_DONTWAIT),
+ C(MSG_PEEK),
C(SOL_SOCKET),
C(SO_BROADCAST),
diff --git a/ports/unix/modtime.c b/ports/unix/modtime.c
index fbd94b5ecd..41b7c89df4 100644
--- a/ports/unix/modtime.c
+++ b/ports/unix/modtime.c
@@ -34,6 +34,7 @@
#include "py/mphal.h"
#include "py/runtime.h"
+#include "shared/timeutils/timeutils.h"
#ifdef _WIN32
static inline int msec_sleep_tv(struct timeval *tv) {
@@ -130,12 +131,7 @@ static mp_obj_t mod_time_gm_local_time(size_t n_args, const mp_obj_t *args, stru
if (n_args == 0) {
t = time(NULL);
} else {
- #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
- mp_float_t val = mp_obj_get_float(args[0]);
- t = (time_t)MICROPY_FLOAT_C_FUN(trunc)(val);
- #else
- t = mp_obj_get_int(args[0]);
- #endif
+ t = (time_t)timeutils_obj_get_timestamp(args[0]);
}
struct tm *tm = time_func(&t);
@@ -196,7 +192,7 @@ static mp_obj_t mod_time_mktime(mp_obj_t tuple) {
if (ret == -1) {
mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("invalid mktime usage"));
}
- return mp_obj_new_int(ret);
+ return timeutils_obj_from_timestamp(ret);
}
MP_DEFINE_CONST_FUN_OBJ_1(mod_time_mktime_obj, mod_time_mktime);
diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h
index 21ce75a351..973b5e74ce 100644
--- a/ports/unix/mpconfigport.h
+++ b/ports/unix/mpconfigport.h
@@ -124,6 +124,9 @@ typedef long mp_off_t;
// VFS stat functions should return time values relative to 1970/1/1
#define MICROPY_EPOCH_IS_1970 (1)
+// port modtime functions use time_t
+#define MICROPY_TIMESTAMP_IMPL (MICROPY_TIMESTAMP_IMPL_TIME_T)
+
// Assume that select() call, interrupted with a signal, and erroring
// with EINTR, updates remaining timeout value.
#define MICROPY_SELECT_REMAINING_TIME (1)
diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h
index 04b5b8ae1a..2f5d9683b3 100644
--- a/ports/unix/variants/coverage/mpconfigvariant.h
+++ b/ports/unix/variants/coverage/mpconfigvariant.h
@@ -39,11 +39,13 @@
// Enable additional features.
#define MICROPY_DEBUG_PARSE_RULE_NAME (1)
+#define MICROPY_PY_SYS_SETTRACE (1)
#define MICROPY_TRACKED_ALLOC (1)
#define MICROPY_WARNINGS_CATEGORY (1)
#undef MICROPY_VFS_ROM_IOCTL
#define MICROPY_VFS_ROM_IOCTL (1)
#define MICROPY_PY_CRYPTOLIB_CTR (1)
+#define MICROPY_SCHEDULER_STATIC_NODES (1)
// Enable os.uname for attrtuple coverage test
#define MICROPY_PY_OS_UNAME (1)
diff --git a/ports/webassembly/main.c b/ports/webassembly/main.c
index c542f0cd72..770dfbe0ca 100644
--- a/ports/webassembly/main.c
+++ b/ports/webassembly/main.c
@@ -233,7 +233,7 @@ void nlr_jump_fail(void *val) {
}
}
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
while (1) {
;
}
diff --git a/ports/webassembly/objpyproxy.js b/ports/webassembly/objpyproxy.js
index 3b94f8aadc..0eafd0dec5 100644
--- a/ports/webassembly/objpyproxy.js
+++ b/ports/webassembly/objpyproxy.js
@@ -148,6 +148,12 @@ const py_proxy_handler = {
};
},
has(target, prop) {
+ // avoid throwing on `Symbol() in proxy` checks
+ if (typeof prop !== "string") {
+ // returns true only on iterator because other
+ // symbols are not considered in the `get` trap
+ return prop === Symbol.iterator;
+ }
return Module.ccall(
"proxy_c_to_js_has_attr",
"number",
diff --git a/ports/windows/Makefile b/ports/windows/Makefile
index 115d1a61ef..9eee98cdd4 100644
--- a/ports/windows/Makefile
+++ b/ports/windows/Makefile
@@ -46,6 +46,10 @@ LDFLAGS += -lm -lbcrypt $(LDFLAGS_EXTRA)
# This option has no effect on 64-bit builds.
CFLAGS += -D_FILE_OFFSET_BITS=64
+# Force the use of 64-bits for time_t in C library functions on 32-bit platforms.
+# This option has no effect on 64-bit builds.
+CFLAGS += -D_TIME_BITS=64
+
# Debugging/Optimization
ifdef DEBUG
CFLAGS += -g
diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h
index fabc9072d6..4e140d5edb 100644
--- a/ports/windows/mpconfigport.h
+++ b/ports/windows/mpconfigport.h
@@ -242,7 +242,7 @@ typedef long mp_off_t;
// CL specific overrides from mpconfig
-#define NORETURN __declspec(noreturn)
+#define MP_NORETURN __declspec(noreturn)
#define MP_WEAK
#define MP_NOINLINE __declspec(noinline)
#define MP_ALWAYSINLINE __forceinline
@@ -266,6 +266,11 @@ typedef long mp_off_t;
#endif
#endif
+// VC++ 2017 fixes
+#if (_MSC_VER < 1920)
+#define MICROPY_PY_MATH_COPYSIGN_FIX_NAN (1)
+#endif
+
// CL specific definitions
#ifndef __cplusplus
diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt
index b955089066..a5cc477204 100644
--- a/ports/zephyr/CMakeLists.txt
+++ b/ports/zephyr/CMakeLists.txt
@@ -34,6 +34,11 @@ set(MICROPY_TARGET micropython)
include(${MICROPY_DIR}/py/py.cmake)
include(${MICROPY_DIR}/extmod/extmod.cmake)
+if (CONFIG_MICROPY_FROZEN_MODULES)
+ cmake_path(ABSOLUTE_PATH CONFIG_MICROPY_FROZEN_MANIFEST BASE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
+ set(MICROPY_FROZEN_MANIFEST ${CONFIG_MICROPY_FROZEN_MANIFEST})
+endif()
+
set(MICROPY_SOURCE_PORT
main.c
help.c
@@ -61,10 +66,20 @@ set(MICROPY_SOURCE_SHARED
runtime/mpirq.c
runtime/pyexec.c
runtime/stdout_helpers.c
+ runtime/sys_stdio_mphal.c
timeutils/timeutils.c
)
list(TRANSFORM MICROPY_SOURCE_SHARED PREPEND ${MICROPY_DIR}/shared/)
+set(MICROPY_SOURCE_DRIVERS
+ bus/softspi.c
+)
+list(TRANSFORM MICROPY_SOURCE_DRIVERS PREPEND ${MICROPY_DIR}/drivers/)
+
+set(MICROPY_QSTRDEFS_PORT
+ ${MICROPY_PORT_DIR}/qstrdefsport.h
+)
+
set(MICROPY_SOURCE_LIB
oofatfs/ff.c
oofatfs/ffunicode.c
@@ -79,6 +94,7 @@ set(MICROPY_SOURCE_QSTR
${MICROPY_SOURCE_PY}
${MICROPY_SOURCE_EXTMOD}
${MICROPY_SOURCE_SHARED}
+ ${MICROPY_SOURCE_DRIVERS}
${MICROPY_SOURCE_LIB}
${MICROPY_SOURCE_PORT}
)
@@ -115,13 +131,15 @@ zephyr_library_compile_definitions(
zephyr_library_sources(${MICROPY_SOURCE_QSTR})
zephyr_library_link_libraries(kernel)
-add_dependencies(${MICROPY_TARGET} zephyr_generated_headers)
-
include(${MICROPY_DIR}/py/mkrules.cmake)
+add_dependencies(BUILD_VERSION_HEADER zephyr_generated_headers)
+add_dependencies(${MICROPY_TARGET} zephyr_generated_headers)
+
target_sources(app PRIVATE
src/zephyr_start.c
src/zephyr_getchar.c
+ src/usbd.c
)
target_link_libraries(app PRIVATE ${MICROPY_TARGET})
diff --git a/ports/zephyr/Kconfig b/ports/zephyr/Kconfig
index 227e943bcd..6db0133f4f 100644
--- a/ports/zephyr/Kconfig
+++ b/ports/zephyr/Kconfig
@@ -41,6 +41,22 @@ config MICROPY_VFS_LFS1
config MICROPY_VFS_LFS2
bool "LittleFs version 2 file system"
+config MICROPY_FROZEN_MODULES
+ bool "Enable Frozen Modules"
+
+config MICROPY_FROZEN_MANIFEST
+ string "Path to Frozen Modules manifest.py"
+ depends on MICROPY_FROZEN_MODULES
+ default "boards/manifest.py"
+
+config MICROPY_USB_DEVICE_VID
+ hex "USB VID"
+ default 0x2fe3
+
+config MICROPY_USB_DEVICE_PID
+ hex "USB PID"
+ default 0x0001
+
endmenu # MicroPython Options
source "Kconfig.zephyr"
diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md
index fc18d25c0a..17c1f613de 100644
--- a/ports/zephyr/README.md
+++ b/ports/zephyr/README.md
@@ -95,7 +95,7 @@ qemu_cortex_m3):
Networking is enabled with the default configuration, so you need to follow
instructions in
-https://docs.zephyrproject.org/latest/guides/networking/qemu_setup.html#networking-with-qemu
+https://docs.zephyrproject.org/latest/connectivity/networking/qemu_setup.html#networking-with-qemu
to setup the host side of TAP/SLIP networking. If you get an error like:
could not connect serial device to character backend 'unix:/tmp/slip.sock'
diff --git a/ports/zephyr/boards/bbc_microbit_v2.conf b/ports/zephyr/boards/bbc_microbit_v2.conf
index 31872244ca..43742078f5 100644
--- a/ports/zephyr/boards/bbc_microbit_v2.conf
+++ b/ports/zephyr/boards/bbc_microbit_v2.conf
@@ -1,4 +1,3 @@
-CONFIG_CONSOLE_SUBSYS=n
CONFIG_NETWORKING=n
CONFIG_BT=y
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
diff --git a/ports/zephyr/boards/beagleconnect_freedom.conf b/ports/zephyr/boards/beagleconnect_freedom.conf
index 14ce9c526e..e31e09444f 100644
--- a/ports/zephyr/boards/beagleconnect_freedom.conf
+++ b/ports/zephyr/boards/beagleconnect_freedom.conf
@@ -1,7 +1,19 @@
# Hardware features
+CONFIG_ADC=y
+CONFIG_PWM=y
CONFIG_I2C=y
CONFIG_SPI=y
# Flash drivers
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
+
+# Networking
+CONFIG_BT=n
+CONFIG_NET_IPV4=n
+CONFIG_NET_CONFIG_NEED_IPV6=y
+CONFIG_NET_CONFIG_NEED_IPV4=n
+CONFIG_NET_CONFIG_MY_IPV4_ADDR=""
+CONFIG_NET_L2_IEEE802154=y
+CONFIG_NET_DHCPV4=n
+CONFIG_NET_CONFIG_IEEE802154_CHANNEL=1
diff --git a/ports/zephyr/boards/beagleplay_cc1352p7.conf b/ports/zephyr/boards/beagleplay_cc1352p7.conf
new file mode 100644
index 0000000000..f63d184e38
--- /dev/null
+++ b/ports/zephyr/boards/beagleplay_cc1352p7.conf
@@ -0,0 +1,13 @@
+# Flash drivers
+CONFIG_FLASH=y
+CONFIG_FLASH_MAP=y
+
+# Networking
+CONFIG_BT=n
+CONFIG_NET_IPV4=n
+CONFIG_NET_CONFIG_NEED_IPV6=y
+CONFIG_NET_CONFIG_NEED_IPV4=n
+CONFIG_NET_CONFIG_MY_IPV4_ADDR=""
+CONFIG_NET_L2_IEEE802154=y
+CONFIG_NET_DHCPV4=n
+CONFIG_NET_CONFIG_IEEE802154_CHANNEL=1
diff --git a/ports/zephyr/boards/frdm_k64f.conf b/ports/zephyr/boards/frdm_k64f.conf
index c164c0c32c..71f8cc178b 100644
--- a/ports/zephyr/boards/frdm_k64f.conf
+++ b/ports/zephyr/boards/frdm_k64f.conf
@@ -3,6 +3,7 @@ CONFIG_NET_L2_ETHERNET=y
# Hardware features
CONFIG_I2C=y
+CONFIG_PWM=y
CONFIG_SPI=y
# Sensor drivers
@@ -14,3 +15,6 @@ CONFIG_FXOS8700_TEMP=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_MPU_ALLOW_FLASH_WRITE=y
+
+# MicroPython config
+CONFIG_MICROPY_HEAP_SIZE=114688
diff --git a/ports/zephyr/boards/manifest.py b/ports/zephyr/boards/manifest.py
new file mode 100644
index 0000000000..df1169c081
--- /dev/null
+++ b/ports/zephyr/boards/manifest.py
@@ -0,0 +1,7 @@
+# This is an example frozen module manifest. Enable this by configuring
+# the Zephyr project and enabling the frozen modules config feature.
+
+freeze("$(PORT_DIR)/modules")
+
+# Require a micropython-lib module.
+require("upysh")
diff --git a/ports/zephyr/boards/nrf52840dk_nrf52840.conf b/ports/zephyr/boards/nrf52840dk_nrf52840.conf
index e9ec78b64f..dc2ed1c4b4 100644
--- a/ports/zephyr/boards/nrf52840dk_nrf52840.conf
+++ b/ports/zephyr/boards/nrf52840dk_nrf52840.conf
@@ -1,8 +1,13 @@
CONFIG_NETWORKING=n
CONFIG_BT=y
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
+CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y
+CONFIG_BT_GATT_CLIENT=y
+CONFIG_BT_L2CAP_TX_MTU=252
+CONFIG_BT_BUF_ACL_RX_SIZE=256
+CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n
CONFIG_MICROPY_HEAP_SIZE=98304
CONFIG_MAIN_STACK_SIZE=8192
diff --git a/ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.conf b/ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.conf
new file mode 100644
index 0000000000..b4aed364a7
--- /dev/null
+++ b/ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.conf
@@ -0,0 +1,2 @@
+CONFIG_FLASH=y
+CONFIG_FLASH_MAP=y
diff --git a/ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.overlay b/ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.overlay
new file mode 100644
index 0000000000..7310d1e55f
--- /dev/null
+++ b/ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.overlay
@@ -0,0 +1,16 @@
+// Replace default internal storage partition with external flash
+
+/delete-node/ &storage_partition;
+
+&mx25r64 {
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ storage_partition: partition@0 {
+ reg = <0x00000000 0x800000>;
+ label = "storage";
+ };
+ };
+};
diff --git a/ports/zephyr/boards/nrf9151dk_nrf9151.conf b/ports/zephyr/boards/nrf9151dk_nrf9151.conf
new file mode 100644
index 0000000000..e89f332ba1
--- /dev/null
+++ b/ports/zephyr/boards/nrf9151dk_nrf9151.conf
@@ -0,0 +1,7 @@
+# Enable external flash
+CONFIG_SPI=y
+CONFIG_SPI_NOR=y
+CONFIG_SPI_NOR_SFDP_DEVICETREE=y
+
+CONFIG_FLASH=y
+CONFIG_FLASH_MAP=y
diff --git a/ports/zephyr/boards/nrf9151dk_nrf9151.overlay b/ports/zephyr/boards/nrf9151dk_nrf9151.overlay
new file mode 100644
index 0000000000..85cab57414
--- /dev/null
+++ b/ports/zephyr/boards/nrf9151dk_nrf9151.overlay
@@ -0,0 +1,22 @@
+/ {
+ /* Configure partition manager to use gd25wb256 as the external flash */
+ chosen {
+ nordic,pm-ext-flash = &gd25wb256;
+ };
+};
+
+/delete-node/ &storage_partition;
+
+&gd25wb256 {
+ status = "okay";
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ storage_partition: partition@0 {
+ reg = <0x00000000 0x2000000>;
+ label = "storage";
+ };
+ };
+};
diff --git a/ports/zephyr/boards/nucleo_h743zi.conf b/ports/zephyr/boards/nucleo_h743zi.conf
index 942415b796..ea6a60b352 100644
--- a/ports/zephyr/boards/nucleo_h743zi.conf
+++ b/ports/zephyr/boards/nucleo_h743zi.conf
@@ -1,6 +1,3 @@
-# disable console subsys to get REPL working
-CONFIG_CONSOLE_SUBSYS=n
-
# flash config
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
diff --git a/ports/zephyr/boards/nucleo_wb55rg.conf b/ports/zephyr/boards/nucleo_wb55rg.conf
index baf0f28075..adfab367c8 100644
--- a/ports/zephyr/boards/nucleo_wb55rg.conf
+++ b/ports/zephyr/boards/nucleo_wb55rg.conf
@@ -1,6 +1,21 @@
-CONFIG_CONSOLE_SUBSYS=n
CONFIG_NETWORKING=n
+
+# Hardware features
+CONFIG_FLASH=y
+CONFIG_FLASH_MAP=y
+CONFIG_I2C=y
+CONFIG_SPI=y
+
+# Bluetooth
CONFIG_BT=y
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
+CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y
+CONFIG_BT_GATT_CLIENT=y
+CONFIG_BT_L2CAP_TX_MTU=252
+CONFIG_BT_BUF_ACL_RX_SIZE=256
+CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n
+
+# MicroPython config
+CONFIG_MICROPY_HEAP_SIZE=131072
diff --git a/ports/zephyr/boards/nucleo_wb55rg.overlay b/ports/zephyr/boards/nucleo_wb55rg.overlay
new file mode 100644
index 0000000000..d9a4d3f24c
--- /dev/null
+++ b/ports/zephyr/boards/nucleo_wb55rg.overlay
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2025 Damien P. George
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* Delete the defined partitions and create bigger one for storage. */
+/delete-node/ &slot1_partition;
+/delete-node/ &scratch_partition;
+/delete-node/ &storage_partition;
+&flash0 {
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ /* Storage slot: 424 KiB placed after slot0_partition. */
+ storage_partition: partition@70000 {
+ label = "storage";
+ reg = <0x00070000 DT_SIZE_K(424)>;
+ };
+ };
+};
diff --git a/ports/zephyr/boards/qemu_cortex_m3.conf b/ports/zephyr/boards/qemu_cortex_m3.conf
index dac0c358dc..11eebd9606 100644
--- a/ports/zephyr/boards/qemu_cortex_m3.conf
+++ b/ports/zephyr/boards/qemu_cortex_m3.conf
@@ -1,7 +1,6 @@
-# Interrupt-driven UART console has emulation artifacts under QEMU,
-# disable it
-CONFIG_CONSOLE_SUBSYS=n
+# This QEMU target only has 256k ROM and 64k RAM, so disable networking
+CONFIG_NETWORKING=n
-# Networking drivers
-# SLIP driver for QEMU
-CONFIG_NET_SLIP_TAP=y
+# QEMU doesn't have a watchdog, so disable it
+CONFIG_WATCHDOG=n
+CONFIG_WDT_DISABLE_AT_BOOT=n
diff --git a/ports/zephyr/boards/qemu_x86.conf b/ports/zephyr/boards/qemu_x86.conf
index dac0c358dc..1d04675df4 100644
--- a/ports/zephyr/boards/qemu_x86.conf
+++ b/ports/zephyr/boards/qemu_x86.conf
@@ -1,7 +1,7 @@
-# Interrupt-driven UART console has emulation artifacts under QEMU,
-# disable it
-CONFIG_CONSOLE_SUBSYS=n
-
# Networking drivers
# SLIP driver for QEMU
CONFIG_NET_SLIP_TAP=y
+
+# QEMU doesn't have a watchdog, so disable it
+CONFIG_WATCHDOG=n
+CONFIG_WDT_DISABLE_AT_BOOT=n
diff --git a/ports/zephyr/boards/rpi_pico.conf b/ports/zephyr/boards/rpi_pico.conf
new file mode 100644
index 0000000000..6b31bc9f98
--- /dev/null
+++ b/ports/zephyr/boards/rpi_pico.conf
@@ -0,0 +1,19 @@
+# Disable floating point hardware.
+CONFIG_FPU=n
+
+# Configure serial console over USB CDC ACM.
+CONFIG_USB_DEVICE_STACK_NEXT=y
+CONFIG_USBD_CDC_ACM_CLASS=y
+CONFIG_UART_LINE_CTRL=y
+
+# Disable networking.
+CONFIG_NETWORKING=n
+
+# Hardware features.
+CONFIG_FLASH=y
+CONFIG_FLASH_MAP=y
+CONFIG_I2C=y
+CONFIG_SPI=y
+
+# MicroPython config.
+CONFIG_MICROPY_HEAP_SIZE=196608
diff --git a/ports/zephyr/boards/rpi_pico.overlay b/ports/zephyr/boards/rpi_pico.overlay
new file mode 100644
index 0000000000..d63ed73bdf
--- /dev/null
+++ b/ports/zephyr/boards/rpi_pico.overlay
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2025 Damien P. George
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/ {
+ chosen {
+ /* Use USB CDC ACM as the console. */
+ zephyr,console = &cdc_acm_uart0;
+ };
+};
+
+/* Delete defined partitions and make a layout matching the rp2 port RPI_PICO configuration. */
+/delete-node/ &code_partition;
+&flash0 {
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ /* Code slot: 640 KiB - 0x100 placed after second-stage bootloader. */
+ code_partition: partition@100 {
+ label = "code-partition";
+ reg = <0x00000100 (DT_SIZE_K(640) - 0x100)>;
+ read-only;
+ };
+
+ /* Storage slot: 1408 KiB placed after code_partition. */
+ storage_partition: partition@a0000 {
+ label = "storage";
+ reg = <0x000a0000 DT_SIZE_K(1408)>;
+ };
+ };
+};
+
+&zephyr_udc0 {
+ cdc_acm_uart0: cdc_acm_uart0 {
+ compatible = "zephyr,cdc-acm-uart";
+ };
+};
+
+&i2c1 {
+ clock-frequency = <I2C_BITRATE_STANDARD>;
+ status = "okay";
+ pinctrl-0 = <&i2c1_default>;
+ pinctrl-names = "default";
+};
diff --git a/ports/zephyr/machine_pin.c b/ports/zephyr/machine_pin.c
index 2240196690..e0718588d6 100644
--- a/ports/zephyr/machine_pin.c
+++ b/ports/zephyr/machine_pin.c
@@ -36,6 +36,7 @@
#include "py/gc.h"
#include "py/mphal.h"
#include "extmod/modmachine.h"
+#include "extmod/virtpin.h"
#include "shared/runtime/mpirq.h"
#include "modmachine.h"
#include "zephyr_device.h"
@@ -115,6 +116,10 @@ static mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_ar
}
int ret = gpio_pin_configure(self->port, self->pin, mode | pull | init);
+ if (ret == -ENOTSUP && mode == (GPIO_OUTPUT | GPIO_INPUT)) {
+ // Some targets (eg frdm_k64f) don't support GPIO_OUTPUT|GPIO_INPUT, so try again with just GPIO_OUTPUT.
+ ret = gpio_pin_configure(self->port, self->pin, GPIO_OUTPUT | pull | init);
+ }
if (ret) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid pin"));
}
@@ -126,19 +131,26 @@ static mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_ar
mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
- // get the wanted port
- if (!mp_obj_is_type(args[0], &mp_type_tuple)) {
+ machine_pin_obj_t *pin;
+ if (mp_obj_is_type(args[0], &machine_pin_type)) {
+ // Already a Pin object, reuse it.
+ pin = MP_OBJ_TO_PTR(args[0]);
+ } else if (mp_obj_is_type(args[0], &mp_type_tuple)) {
+ // Get the wanted (port, pin) values.
+ mp_obj_t *items;
+ mp_obj_get_array_fixed_n(args[0], 2, &items);
+ const struct device *wanted_port = zephyr_device_find(items[0]);
+ int wanted_pin = mp_obj_get_int(items[1]);
+
+ pin = m_new_obj(machine_pin_obj_t);
+ pin->base = machine_pin_obj_template;
+ pin->port = wanted_port;
+ pin->pin = wanted_pin;
+ pin->irq = NULL;
+ } else {
+ // Unknown Pin.
mp_raise_ValueError(MP_ERROR_TEXT("Pin id must be tuple of (\"GPIO_x\", pin#)"));
}
- mp_obj_t *items;
- mp_obj_get_array_fixed_n(args[0], 2, &items);
- const struct device *wanted_port = zephyr_device_find(items[0]);
- int wanted_pin = mp_obj_get_int(items[1]);
-
- machine_pin_obj_t *pin = m_new_obj(machine_pin_obj_t);
- pin->base = machine_pin_obj_template;
- pin->port = wanted_port;
- pin->pin = wanted_pin;
if (n_args > 1 || n_kw > 0) {
// pin mode given, so configure this GPIO
@@ -270,7 +282,8 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
// class constants
{ MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(GPIO_INPUT) },
- { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(GPIO_OUTPUT) },
+ { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(GPIO_OUTPUT | GPIO_INPUT) },
+ { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(GPIO_OUTPUT | GPIO_INPUT | GPIO_OPEN_DRAIN) },
{ MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULL_UP) },
{ MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULL_DOWN) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_INT_EDGE_RISING) },
diff --git a/ports/zephyr/machine_timer.c b/ports/zephyr/machine_timer.c
index 8ab2f62913..1bb743c2eb 100644
--- a/ports/zephyr/machine_timer.c
+++ b/ports/zephyr/machine_timer.c
@@ -77,9 +77,14 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr
}
static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
- mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
-
- if (mp_obj_get_int(args[0]) != -1) {
+ // Get timer id (only soft timer (-1) supported at the moment)
+ mp_int_t id = -1;
+ if (n_args > 0) {
+ id = mp_obj_get_int(args[0]);
+ --n_args;
+ ++args;
+ }
+ if (id != -1) {
mp_raise_ValueError(MP_ERROR_TEXT("only virtual timers are supported"));
}
@@ -90,10 +95,10 @@ static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args,
self->next = MP_STATE_PORT(machine_timer_obj_head);
MP_STATE_PORT(machine_timer_obj_head) = self;
- if (n_args > 1 || n_kw > 0) {
+ if (n_args > 0 || n_kw > 0) {
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
- machine_timer_init_helper(self, n_args - 1, args + 1, &kw_args);
+ machine_timer_init_helper(self, n_args, args, &kw_args);
}
return self;
}
diff --git a/ports/zephyr/machine_uart.c b/ports/zephyr/machine_uart.c
index 1927335ddf..60613befb1 100644
--- a/ports/zephyr/machine_uart.c
+++ b/ports/zephyr/machine_uart.c
@@ -5,6 +5,7 @@
*
* Copyright (c) 2016 Damien P. George
* Copyright (c) 2020 Yonatan Schachter
+ * Copyright (c) 2025 Daniel Campora on behalf of REMOTE TECH LTD
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -32,16 +33,32 @@
#include <zephyr/drivers/uart.h>
#include "py/mperrno.h"
+#include "py/ringbuf.h"
#include "zephyr_device.h"
-// The UART class doesn't have any constants for this port.
-#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS
+
+#define MACHINE_UART_RTS 1
+#define MACHINE_UART_CTS 2
+
+// This class needs a finalizer, so we add it here
+#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \
+ { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(MACHINE_UART_RTS) }, \
+ { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(MACHINE_UART_CTS) }, \
+ { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_uart_deinit_obj) },
+
+#define UART_RX_RING_BUF_DEF_SIZE 128
+#define UART_TX_RING_BUF_DEF_SIZE 128
+
+static void uart_interrupt_handler(const struct device *dev, void *user_data);
typedef struct _machine_uart_obj_t {
mp_obj_base_t base;
const struct device *dev;
uint16_t timeout; // timeout waiting for first char (in ms)
uint16_t timeout_char; // timeout waiting between chars (in ms)
+ ringbuf_t rx_ringbuffer;
+ ringbuf_t tx_ringbuffer;
+ bool tx_complete;
} machine_uart_obj_t;
static const char *_parity_name[] = {"None", "Odd", "Even", "Mark", "Space"};
@@ -60,23 +77,96 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_
}
static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
- enum { ARG_timeout, ARG_timeout_char };
+ enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_txbuf, ARG_rxbuf, ARG_timeout, ARG_timeout_char, ARG_flow };
static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 115200} },
+ { MP_QSTR_bits, MP_ARG_INT, {.u_int = 8} },
+ { MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_stop, MP_ARG_INT, {.u_int = 1} },
+ { MP_QSTR_txbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_RX_RING_BUF_DEF_SIZE} },
+ { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_TX_RING_BUF_DEF_SIZE} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
};
+
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->timeout = args[ARG_timeout].u_int;
self->timeout_char = args[ARG_timeout_char].u_int;
+
+ uint8_t data_bits;
+ if (args[ARG_bits].u_int == 5) {
+ data_bits = UART_CFG_DATA_BITS_5;
+ } else if (args[ARG_bits].u_int == 6) {
+ data_bits = UART_CFG_DATA_BITS_6;
+ } else if (args[ARG_bits].u_int == 7) {
+ data_bits = UART_CFG_DATA_BITS_7;
+ } else if (args[ARG_bits].u_int == 8) {
+ data_bits = UART_CFG_DATA_BITS_8;
+ } else if (args[ARG_bits].u_int == 9) {
+ data_bits = UART_CFG_DATA_BITS_9;
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid data bits"));
+ }
+
+ uint8_t parity;
+ if (args[ARG_parity].u_obj == mp_const_none) {
+ parity = UART_CFG_PARITY_NONE;
+ } else if (mp_obj_get_int(args[ARG_parity].u_obj) == 0) {
+ parity = UART_CFG_PARITY_EVEN;
+ } else if (mp_obj_get_int(args[ARG_parity].u_obj) == 1) {
+ parity = UART_CFG_PARITY_ODD;
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid parity"));
+ }
+
+ uint8_t stop_bits;
+ if (args[ARG_stop].u_int == 1) {
+ stop_bits = UART_CFG_STOP_BITS_1;
+ } else if (args[ARG_stop].u_int == 2) {
+ data_bits = UART_CFG_STOP_BITS_2;
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid stop bits"));
+ }
+
+ uint8_t flow_ctrl;
+ if (args[ARG_flow].u_int == 0) {
+ flow_ctrl = UART_CFG_FLOW_CTRL_NONE;
+ } else if (args[ARG_flow].u_int == (MACHINE_UART_RTS | MACHINE_UART_CTS)) {
+ flow_ctrl = UART_CFG_FLOW_CTRL_RTS_CTS;
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid flow control"));
+ }
+
+ const struct uart_config cfg = {
+ .baudrate = args[ARG_baudrate].u_int,
+ .parity = parity,
+ .stop_bits = args[ARG_stop].u_int,
+ .data_bits = data_bits,
+ .flow_ctrl = flow_ctrl
+ };
+
+ int ret = uart_configure(self->dev, &cfg);
+ if (ret < 0) {
+ mp_raise_OSError(-ret);
+ }
+
+ ringbuf_alloc(&self->tx_ringbuffer, args[ARG_txbuf].u_int);
+ ringbuf_alloc(&self->rx_ringbuffer, args[ARG_rxbuf].u_int);
+
+ uart_irq_callback_user_data_set(self->dev, uart_interrupt_handler, (void *)self);
+ uart_irq_rx_enable(self->dev);
}
static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
- machine_uart_obj_t *self = mp_obj_malloc(machine_uart_obj_t, &machine_uart_type);
- self->dev = zephyr_device_find(args[0]);
+ const struct device *dev = zephyr_device_find(args[0]);
+ machine_uart_obj_t *self = mp_obj_malloc_with_finaliser(machine_uart_obj_t, &machine_uart_type);
+ self->dev = dev;
+ self->tx_complete = true;
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
@@ -86,37 +176,38 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
}
static void mp_machine_uart_deinit(machine_uart_obj_t *self) {
- (void)self;
+ uart_irq_rx_disable(self->dev);
+ uart_irq_tx_disable(self->dev);
}
static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) {
- (void)self;
- mp_raise_NotImplementedError(NULL); // TODO
+ return ringbuf_avail(&self->rx_ringbuffer);
}
static bool mp_machine_uart_txdone(machine_uart_obj_t *self) {
- (void)self;
- mp_raise_NotImplementedError(NULL); // TODO
+ return self->tx_complete && !ringbuf_avail(&self->tx_ringbuffer) ? true : false;
}
static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) {
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint8_t *buffer = (uint8_t *)buf_in;
- uint8_t data;
mp_uint_t bytes_read = 0;
size_t elapsed_ms = 0;
size_t time_to_wait = self->timeout;
- while ((elapsed_ms < time_to_wait) && (bytes_read < size)) {
- if (!uart_poll_in(self->dev, &data)) {
- buffer[bytes_read++] = data;
+ do {
+ int _rx_len = MIN(ringbuf_avail(&self->rx_ringbuffer), size - bytes_read);
+ if (_rx_len > 0) {
+ ringbuf_get_bytes(&self->rx_ringbuffer, &buffer[bytes_read], _rx_len);
+ bytes_read += _rx_len;
elapsed_ms = 0;
time_to_wait = self->timeout_char;
} else {
k_msleep(1);
elapsed_ms++;
}
- }
+ } while ((elapsed_ms < time_to_wait) && (bytes_read < size));
+
return bytes_read;
}
@@ -124,27 +215,86 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint8_t *buffer = (uint8_t *)buf_in;
- for (mp_uint_t i = 0; i < size; i++) {
+ // wait for any pending transmission to complete
+ while (!mp_machine_uart_txdone(self)) {
+ MICROPY_EVENT_POLL_HOOK;
+ }
+
+ int _ex_size = 0;
+ int _free_space = ringbuf_free(&self->tx_ringbuffer);
+ if (size > _free_space) {
+ _ex_size = size - _free_space;
+ }
+
+ // do a blocking tx of what doesn't fit into the outgoing ring buffer
+ for (mp_uint_t i = 0; i < _ex_size; i++) {
uart_poll_out(self->dev, buffer[i]);
}
+ ringbuf_put_bytes(&self->tx_ringbuffer, &buffer[_ex_size], size - _ex_size);
+ self->tx_complete = false;
+ uart_irq_tx_enable(self->dev);
+
return size;
}
static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
- mp_uint_t ret;
+ machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_uint_t ret = 0;
if (request == MP_STREAM_POLL) {
- ret = 0;
- // read is always blocking
-
- if (arg & MP_STREAM_POLL_WR) {
+ uintptr_t flags = arg;
+ if ((flags & MP_STREAM_POLL_RD) && (mp_machine_uart_any(self) > 0)) {
+ ret |= MP_STREAM_POLL_RD;
+ }
+ if ((flags & MP_STREAM_POLL_WR) && mp_machine_uart_txdone(self)) {
ret |= MP_STREAM_POLL_WR;
}
- return ret;
+ } else if (request == MP_STREAM_FLUSH) {
+ while (!mp_machine_uart_txdone(self)) {
+ MICROPY_EVENT_POLL_HOOK;
+ }
} else {
*errcode = MP_EINVAL;
ret = MP_STREAM_ERROR;
}
+
return ret;
}
+
+static void uart_interrupt_handler(const struct device *dev, void *user_data) {
+ machine_uart_obj_t *self = (machine_uart_obj_t *)user_data;
+
+ while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
+ if (uart_irq_rx_ready(dev)) {
+ uint8_t _rx_buffer[32];
+ size_t _free_space = MIN(ringbuf_free(&self->rx_ringbuffer), sizeof(_rx_buffer));
+
+ // empty the uart fifo even if we can't store bytes anymore
+ // otherwise we will never exit this interrupt handler
+ int rcv_len = uart_fifo_read(dev, _rx_buffer, (_free_space > 0) ? _free_space : 1);
+ if ((rcv_len <= 0) || (_free_space == 0)) {
+ continue;
+ }
+
+ ringbuf_put_bytes(&self->rx_ringbuffer, _rx_buffer, rcv_len);
+ }
+
+ int _max_uart_tx_len = uart_irq_tx_ready(dev);
+ if (_max_uart_tx_len > 0) {
+ uint8_t _tx_buffer[32];
+ size_t _buffer_tx_len;
+
+ _max_uart_tx_len = MIN(_max_uart_tx_len, sizeof(_tx_buffer));
+ _buffer_tx_len = ringbuf_avail(&self->tx_ringbuffer);
+ if (_buffer_tx_len > 0) {
+ _buffer_tx_len = MIN(_max_uart_tx_len, _buffer_tx_len);
+ ringbuf_get_bytes(&self->tx_ringbuffer, _tx_buffer, _buffer_tx_len);
+ uart_fifo_fill(dev, _tx_buffer, _buffer_tx_len);
+ } else if (uart_irq_tx_complete(dev)) {
+ uart_irq_tx_disable(dev);
+ self->tx_complete = true;
+ }
+ }
+ }
+}
diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c
index 206b7f92d3..eaef34a786 100644
--- a/ports/zephyr/main.c
+++ b/ports/zephyr/main.c
@@ -63,6 +63,10 @@
static char heap[MICROPY_HEAP_SIZE];
+#if defined(CONFIG_USB_DEVICE_STACK_NEXT)
+extern int mp_usbd_init(void);
+#endif // defined(CONFIG_USB_DEVICE_STACK_NEXT)
+
void init_zephyr(void) {
// We now rely on CONFIG_NET_APP_SETTINGS to set up bootstrap
// network addresses.
@@ -95,6 +99,7 @@ static void vfs_init(void) {
mp_obj_t bdev = NULL;
mp_obj_t mount_point;
const char *mount_point_str = NULL;
+ qstr path_lib_qstr = MP_QSTRnull;
int ret = 0;
#ifdef CONFIG_DISK_DRIVER_SDMMC
@@ -105,15 +110,18 @@ static void vfs_init(void) {
#endif
bdev = MP_OBJ_TYPE_GET_SLOT(&zephyr_disk_access_type, make_new)(&zephyr_disk_access_type, ARRAY_SIZE(args), 0, args);
mount_point_str = "/sd";
+ path_lib_qstr = MP_QSTR__slash_sd_slash_lib;
#elif defined(CONFIG_FLASH_MAP) && FIXED_PARTITION_EXISTS(storage_partition)
mp_obj_t args[] = { MP_OBJ_NEW_SMALL_INT(FIXED_PARTITION_ID(storage_partition)), MP_OBJ_NEW_SMALL_INT(4096) };
bdev = MP_OBJ_TYPE_GET_SLOT(&zephyr_flash_area_type, make_new)(&zephyr_flash_area_type, ARRAY_SIZE(args), 0, args);
mount_point_str = "/flash";
+ path_lib_qstr = MP_QSTR__slash_flash_slash_lib;
#endif
if ((bdev != NULL)) {
mount_point = mp_obj_new_str_from_cstr(mount_point_str);
ret = mp_vfs_mount_and_chdir_protected(bdev, mount_point);
+ mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(path_lib_qstr));
// TODO: if this failed, make a new file system and try to mount again
}
}
@@ -143,12 +151,26 @@ soft_reset:
usb_enable(NULL);
#endif
+ #ifdef CONFIG_USB_DEVICE_STACK_NEXT
+ mp_usbd_init();
+ #endif
+
#if MICROPY_VFS
vfs_init();
#endif
#if MICROPY_MODULE_FROZEN || MICROPY_VFS
- pyexec_file_if_exists("main.py");
+ // Execute user scripts.
+ int ret = pyexec_file_if_exists("boot.py");
+ if (ret & PYEXEC_FORCED_EXIT) {
+ goto soft_reset_exit;
+ }
+ if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL && ret != 0) {
+ ret = pyexec_file_if_exists("main.py");
+ if (ret & PYEXEC_FORCED_EXIT) {
+ goto soft_reset_exit;
+ }
+ }
#endif
for (;;) {
@@ -163,7 +185,11 @@ soft_reset:
}
}
- printf("soft reboot\n");
+ #if MICROPY_MODULE_FROZEN || MICROPY_VFS
+soft_reset_exit:
+ #endif
+
+ mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n");
#if MICROPY_PY_BLUETOOTH
mp_bluetooth_deinit();
@@ -211,7 +237,7 @@ mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs)
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);
#endif
-NORETURN void nlr_jump_fail(void *val) {
+MP_NORETURN void nlr_jump_fail(void *val) {
while (1) {
;
}
diff --git a/ports/zephyr/modbluetooth_zephyr.c b/ports/zephyr/modbluetooth_zephyr.c
index 8947bdf535..c1b8ddfd8b 100644
--- a/ports/zephyr/modbluetooth_zephyr.c
+++ b/ports/zephyr/modbluetooth_zephyr.c
@@ -31,8 +31,12 @@
#if MICROPY_PY_BLUETOOTH
+#include <zephyr/types.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
+#include <zephyr/bluetooth/conn.h>
+#include <zephyr/bluetooth/uuid.h>
+#include <zephyr/bluetooth/gatt.h>
#include "extmod/modbluetooth.h"
#define DEBUG_printf(...) // printk("BLE: " __VA_ARGS__)
@@ -44,6 +48,23 @@
#define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV
+#define MP_BLUETOOTH_ZEPHYR_MAX_SERVICES (8)
+
+/* This masks Permission bits from GATT API */
+#define GATT_PERM_MASK (BT_GATT_PERM_READ | \
+ BT_GATT_PERM_READ_AUTHEN | \
+ BT_GATT_PERM_READ_ENCRYPT | \
+ BT_GATT_PERM_WRITE | \
+ BT_GATT_PERM_WRITE_AUTHEN | \
+ BT_GATT_PERM_WRITE_ENCRYPT | \
+ BT_GATT_PERM_PREPARE_WRITE)
+
+#define GATT_PERM_ENC_READ_MASK (BT_GATT_PERM_READ_ENCRYPT | \
+ BT_GATT_PERM_READ_AUTHEN)
+
+#define GATT_PERM_ENC_WRITE_MASK (BT_GATT_PERM_WRITE_ENCRYPT | \
+ BT_GATT_PERM_WRITE_AUTHEN)
+
enum {
MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF,
MP_BLUETOOTH_ZEPHYR_BLE_STATE_ACTIVE,
@@ -56,9 +77,42 @@ enum {
MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_ACTIVE,
};
+union uuid_u {
+ struct bt_uuid uuid;
+ struct bt_uuid_16 u16;
+ struct bt_uuid_32 u32;
+ struct bt_uuid_128 u128;
+};
+
+struct add_characteristic {
+ uint8_t properties;
+ uint8_t permissions;
+ const struct bt_uuid *uuid;
+};
+
+struct add_descriptor {
+ uint8_t permissions;
+ const struct bt_uuid *uuid;
+};
+
+typedef struct _mp_bt_zephyr_conn_t {
+ struct bt_conn *conn;
+ struct _mp_bt_zephyr_conn_t *next;
+} mp_bt_zephyr_conn_t;
+
typedef struct _mp_bluetooth_zephyr_root_pointers_t {
+ // list of objects to be tracked by the gc
+ mp_obj_t objs_list;
+
// Characteristic (and descriptor) value storage.
mp_gatts_db_t gatts_db;
+
+ // Service definitions.
+ size_t n_services;
+ struct bt_gatt_service *services[MP_BLUETOOTH_ZEPHYR_MAX_SERVICES];
+
+ // active connections
+ mp_bt_zephyr_conn_t *connections;
} mp_bluetooth_zephyr_root_pointers_t;
static int mp_bluetooth_zephyr_ble_state;
@@ -69,6 +123,98 @@ static struct k_timer mp_bluetooth_zephyr_gap_scan_timer;
static struct bt_le_scan_cb mp_bluetooth_zephyr_gap_scan_cb_struct;
#endif
+static struct bt_data bt_ad_data[8];
+static size_t bt_ad_len = 0;
+static struct bt_data bt_sd_data[8];
+static size_t bt_sd_len = 0;
+
+static mp_bt_zephyr_conn_t *mp_bt_zephyr_next_conn;
+
+static mp_bt_zephyr_conn_t *mp_bt_zephyr_find_connection(uint8_t conn_handle);
+static void mp_bt_zephyr_insert_connection(mp_bt_zephyr_conn_t *connection);
+static void mp_bt_zephyr_remove_connection(uint8_t conn_handle);
+static void mp_bt_zephyr_connected(struct bt_conn *connected, uint8_t err);
+static void mp_bt_zephyr_disconnected(struct bt_conn *disconn, uint8_t reason);
+static struct bt_uuid *create_zephyr_uuid(const mp_obj_bluetooth_uuid_t *uuid);
+static void gatt_db_add(const struct bt_gatt_attr *pattern, struct bt_gatt_attr *attr, size_t user_data_len);
+static void add_service(const struct bt_uuid *u, struct bt_gatt_attr *attr);
+static void add_characteristic(struct add_characteristic *ch, struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_value);
+static void add_ccc(struct bt_gatt_attr *attr, struct bt_gatt_attr *attr_desc);
+static void add_cep(const struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_desc);
+static void add_descriptor(struct bt_gatt_attr *chrc, struct add_descriptor *d, struct bt_gatt_attr *attr_desc);
+static void mp_bt_zephyr_gatt_indicate_done(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err);
+static struct bt_gatt_attr *mp_bt_zephyr_find_attr_by_handle(uint16_t value_handle);
+
+static struct bt_conn_cb mp_bt_zephyr_conn_callbacks = {
+ .connected = mp_bt_zephyr_connected,
+ .disconnected = mp_bt_zephyr_disconnected,
+};
+
+static mp_bt_zephyr_conn_t *mp_bt_zephyr_find_connection(uint8_t conn_handle) {
+ struct bt_conn_info info;
+ for (mp_bt_zephyr_conn_t *connection = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; connection != NULL; connection = connection->next) {
+ if (connection->conn) {
+ bt_conn_get_info(connection->conn, &info);
+ if (info.id == conn_handle) {
+ return connection;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void mp_bt_zephyr_insert_connection(mp_bt_zephyr_conn_t *connection) {
+ connection->next = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections;
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = connection;
+}
+
+static void mp_bt_zephyr_remove_connection(uint8_t conn_handle) {
+ struct bt_conn_info info;
+ mp_bt_zephyr_conn_t *prev = NULL;
+ for (mp_bt_zephyr_conn_t *connection = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; connection != NULL; connection = connection->next) {
+ if (connection->conn) {
+ bt_conn_get_info(connection->conn, &info);
+ if (info.id == conn_handle) {
+ // unlink this item and the gc will eventually collect it
+ if (prev != NULL) {
+ prev->next = connection->next;
+ } else {
+ // move the start pointer
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = connection->next;
+ }
+ break;
+ } else {
+ prev = connection;
+ }
+ }
+ }
+}
+
+static void mp_bt_zephyr_connected(struct bt_conn *conn, uint8_t err) {
+ struct bt_conn_info info;
+ bt_conn_get_info(conn, &info);
+
+ if (err) {
+ uint8_t addr[6] = {0};
+ DEBUG_printf("Connection from central failed (err %u)\n", err);
+ mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, info.id, 0xff, addr);
+ } else {
+ DEBUG_printf("Central connected with id %d\n", info.id);
+ mp_bt_zephyr_next_conn->conn = bt_conn_ref(conn);
+ mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_CONNECT, info.id, info.le.dst->type, info.le.dst->a.val);
+ mp_bt_zephyr_insert_connection(mp_bt_zephyr_next_conn);
+ }
+}
+
+static void mp_bt_zephyr_disconnected(struct bt_conn *conn, uint8_t reason) {
+ struct bt_conn_info info;
+ bt_conn_get_info(conn, &info);
+ DEBUG_printf("Central disconnected (id %d reason %u)\n", info.id, reason);
+ bt_conn_unref(conn);
+ mp_bt_zephyr_remove_connection(info.id);
+ mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, info.id, info.le.dst->type, info.le.dst->a.val);
+}
+
static int bt_err_to_errno(int err) {
// Zephyr uses errno codes directly, but they are negative.
return -err;
@@ -128,6 +274,11 @@ int mp_bluetooth_init(void) {
MP_STATE_PORT(bluetooth_zephyr_root_pointers) = m_new0(mp_bluetooth_zephyr_root_pointers_t, 1);
mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db);
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = NULL;
+ mp_bt_zephyr_next_conn = NULL;
+
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list = mp_obj_new_list(0, NULL);
+
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
mp_bluetooth_zephyr_gap_scan_state = MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_INACTIVE;
k_timer_init(&mp_bluetooth_zephyr_gap_scan_timer, gap_scan_cb_timeout, NULL);
@@ -137,6 +288,9 @@ int mp_bluetooth_init(void) {
#endif
if (mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF) {
+
+ bt_conn_cb_register(&mp_bt_zephyr_conn_callbacks);
+
// bt_enable can only be called once.
int ret = bt_enable(NULL);
if (ret) {
@@ -151,15 +305,22 @@ int mp_bluetooth_init(void) {
return 0;
}
-void mp_bluetooth_deinit(void) {
+int mp_bluetooth_deinit(void) {
DEBUG_printf("mp_bluetooth_deinit %d\n", mp_bluetooth_zephyr_ble_state);
if (mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF
|| mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED) {
- return;
+ return 0;
}
mp_bluetooth_gap_advertise_stop();
+ #if CONFIG_BT_GATT_DYNAMIC_DB
+ for (size_t i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; ++i) {
+ bt_gatt_service_unregister(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]);
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i] = NULL;
+ }
+ #endif
+
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
mp_bluetooth_gap_scan_stop();
bt_le_scan_cb_unregister(&mp_bluetooth_zephyr_gap_scan_cb_struct);
@@ -170,6 +331,8 @@ void mp_bluetooth_deinit(void) {
mp_bluetooth_zephyr_ble_state = MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED;
MP_STATE_PORT(bluetooth_zephyr_root_pointers) = NULL;
+ mp_bt_zephyr_next_conn = NULL;
+ return 0;
}
bool mp_bluetooth_is_active(void) {
@@ -191,7 +354,7 @@ void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr) {
}
void mp_bluetooth_set_address_mode(uint8_t addr_mode) {
- // TODO: implement
+ mp_raise_OSError(MP_EOPNOTSUPP);
}
size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) {
@@ -232,15 +395,11 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons
mp_bluetooth_gap_advertise_stop();
- struct bt_data bt_ad_data[8];
- size_t bt_ad_len = 0;
if (adv_data) {
bt_ad_len = MP_ARRAY_SIZE(bt_ad_data);
mp_bluetooth_prepare_bt_data(adv_data, adv_data_len, bt_ad_data, &bt_ad_len);
}
- struct bt_data bt_sd_data[8];
- size_t bt_sd_len = 0;
if (sr_data) {
bt_sd_len = MP_ARRAY_SIZE(bt_sd_data);
mp_bluetooth_prepare_bt_data(sr_data, sr_data_len, bt_sd_data, &bt_sd_len);
@@ -259,6 +418,10 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons
.peer = NULL,
};
+ // pre-allocate a new connection structure as we cannot allocate this inside the connection callback
+ mp_bt_zephyr_next_conn = m_new0(mp_bt_zephyr_conn_t, 1);
+ mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(mp_bt_zephyr_next_conn));
+
return bt_err_to_errno(bt_le_adv_start(&param, bt_ad_data, bt_ad_len, bt_sd_data, bt_sd_len));
}
@@ -271,6 +434,8 @@ void mp_bluetooth_gap_advertise_stop(void) {
}
int mp_bluetooth_gatts_register_service_begin(bool append) {
+ #if CONFIG_BT_GATT_DYNAMIC_DB
+
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
@@ -280,25 +445,190 @@ int mp_bluetooth_gatts_register_service_begin(bool append) {
return MP_EOPNOTSUPP;
}
+ // Unregister and unref any previous service definitions.
+ for (size_t i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; ++i) {
+ bt_gatt_service_unregister(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]);
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i] = NULL;
+ }
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services = 0;
+
// Reset the gatt characteristic value db.
mp_bluetooth_gatts_db_reset(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db);
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = NULL;
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list = mp_obj_new_list(0, NULL);
+ mp_bt_zephyr_next_conn = NULL;
+ return 0;
+
+ #else
return MP_EOPNOTSUPP;
+ #endif
}
int mp_bluetooth_gatts_register_service_end(void) {
- return MP_EOPNOTSUPP;
+ return 0;
}
int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, mp_obj_bluetooth_uuid_t **characteristic_uuids, uint16_t *characteristic_flags, mp_obj_bluetooth_uuid_t **descriptor_uuids, uint16_t *descriptor_flags, uint8_t *num_descriptors, uint16_t *handles, size_t num_characteristics) {
+ #if CONFIG_BT_GATT_DYNAMIC_DB
+ if (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services >= MP_BLUETOOTH_ZEPHYR_MAX_SERVICES) {
+ return MP_E2BIG;
+ }
+
+ // first of all allocate the entire memory for all the attributes that this service is composed of
+ // 1 for the service itself, 2 for each characteristic (the declaration and the value), and one for each descriptor
+ size_t total_descriptors = 0;
+ for (size_t i = 0; i < num_characteristics; ++i) {
+ total_descriptors += num_descriptors[i];
+ // we have to add the CCC manually
+ if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) {
+ total_descriptors += 1;
+ }
+ }
+ size_t total_attributes = 1 + (num_characteristics * 2) + total_descriptors;
+
+ // allocate one extra so that we can know later where the final attribute is
+ struct bt_gatt_attr *svc_attributes = m_new(struct bt_gatt_attr, total_attributes + 1);
+ mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(svc_attributes));
+
+ size_t handle_index = 0;
+ size_t descriptor_index = 0;
+ size_t attr_index = 0;
+ // bitfield of the handles we should ignore, should be more than enough for most applications
+ uint64_t attrs_to_ignore = 0;
+ uint64_t attrs_are_chrs = 0;
+ uint64_t chr_has_ccc = 0;
+
+ add_service(create_zephyr_uuid(service_uuid), &svc_attributes[attr_index]);
+ attr_index += 1;
+
+ for (size_t i = 0; i < num_characteristics; ++i) {
+
+ struct add_characteristic add_char;
+ add_char.uuid = create_zephyr_uuid(characteristic_uuids[i]);
+ add_char.permissions = 0;
+ add_char.properties = 0;
+ if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ) {
+ add_char.permissions |= BT_GATT_PERM_READ;
+ add_char.properties |= BT_GATT_CHRC_READ;
+ }
+ if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY) {
+ add_char.properties |= BT_GATT_CHRC_NOTIFY;
+ }
+ if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE) {
+ add_char.properties |= BT_GATT_CHRC_INDICATE;
+ }
+ if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE | MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE_NO_RESPONSE)) {
+ add_char.permissions |= BT_GATT_PERM_WRITE;
+ add_char.properties |= (BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP);
+ }
+
+ add_characteristic(&add_char, &svc_attributes[attr_index], &svc_attributes[attr_index + 1]);
+
+ struct bt_gatt_attr *curr_char = &svc_attributes[attr_index];
+ attrs_are_chrs |= (1 << attr_index);
+ if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) {
+ chr_has_ccc |= (1 << attr_index);
+ }
+ attr_index += 1;
+ attrs_to_ignore |= (1 << attr_index); // ignore the value handle
+ attr_index += 1;
+
+ if (num_descriptors[i] > 0) {
+ for (size_t j = 0; j < num_descriptors[i]; ++j) {
+
+ struct add_descriptor add_desc;
+ add_desc.uuid = create_zephyr_uuid(descriptor_uuids[descriptor_index]);
+ add_desc.permissions = 0;
+ if (descriptor_flags[descriptor_index] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ) {
+ add_desc.permissions |= BT_GATT_PERM_READ;
+ }
+ if (descriptor_flags[descriptor_index] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE | MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE_NO_RESPONSE)) {
+ add_desc.permissions |= BT_GATT_PERM_WRITE;
+ }
+
+ add_descriptor(curr_char, &add_desc, &svc_attributes[attr_index]);
+ attr_index += 1;
+
+ descriptor_index++;
+ }
+ }
+
+ // to support indications and notifications we must add the CCC descriptor manually
+ if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) {
+ struct add_descriptor add_desc;
+ mp_obj_bluetooth_uuid_t ccc_uuid;
+ ccc_uuid.base.type = &mp_type_bluetooth_uuid;
+ ccc_uuid.data[0] = BT_UUID_GATT_CCC_VAL & 0xff;
+ ccc_uuid.data[1] = (BT_UUID_GATT_CCC_VAL >> 8) & 0xff;
+ ccc_uuid.type = MP_BLUETOOTH_UUID_TYPE_16;
+ add_desc.uuid = create_zephyr_uuid(&ccc_uuid);
+ add_desc.permissions = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE;
+
+ attrs_to_ignore |= (1 << attr_index);
+
+ add_descriptor(curr_char, &add_desc, &svc_attributes[attr_index]);
+ attr_index += 1;
+ }
+ }
+
+ struct bt_gatt_service *service = m_new(struct bt_gatt_service, 1);
+ mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(service));
+ service->attrs = svc_attributes;
+ service->attr_count = attr_index;
+ // invalidate the last attribute uuid pointer so that we new this is the end of attributes for this service
+ svc_attributes[attr_index].uuid = NULL;
+
+ // Note: advertising must be stopped for gatts registration to work
+
+ int err = bt_gatt_service_register(service);
+ if (err) {
+ return bt_err_to_errno(err);
+ }
+
+ // now that the service has been registered, we can assign the handles for the characteristics and the descriptors
+ // we are not interested in the handle of the service itself, so we start the loop from index 1
+ for (int i = 1; i < total_attributes; i++) {
+ // store all the relevant handles (characteristics and descriptors defined in Python)
+ if (!((uint64_t)(attrs_to_ignore >> i) & (uint64_t)0x01)) {
+ if (svc_attributes[i].user_data == NULL) {
+ mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN);
+ mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle);
+ svc_attributes[i].user_data = entry->data;
+ } else if (((uint64_t)(attrs_are_chrs >> i) & (uint64_t)0x01)) {
+ if (svc_attributes[i + 1].user_data == NULL) {
+ mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN);
+ mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle);
+ svc_attributes[i + 1].user_data = entry->data;
+
+ if (((uint64_t)(chr_has_ccc >> i) & (uint64_t)0x01)) {
+ // create another database entry for the ccc of this characteristic
+ mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle + 2, 1);
+ }
+ }
+ }
+ handles[handle_index++] = svc_attributes[i].handle;
+ }
+ }
+
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services++] = service;
+
+ return 0;
+
+ #else
return MP_EOPNOTSUPP;
+ #endif
}
int mp_bluetooth_gap_disconnect(uint16_t conn_handle) {
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
- return MP_EOPNOTSUPP;
+ mp_bt_zephyr_conn_t *connection = mp_bt_zephyr_find_connection(conn_handle);
+ if (connection) {
+ return bt_conn_disconnect(connection->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
+ }
+ return MP_ENOENT;
}
int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len) {
@@ -312,24 +642,155 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
- if (send_update) {
- return MP_EOPNOTSUPP;
+
+ int err = mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len);
+
+ if ((err == 0) && send_update) {
+ struct bt_gatt_attr *attr_val = mp_bt_zephyr_find_attr_by_handle(value_handle + 1);
+ mp_bluetooth_gatts_db_entry_t *ccc_entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle + 2);
+
+ if (ccc_entry && (ccc_entry->data[0] == BT_GATT_CCC_NOTIFY)) {
+ err = bt_gatt_notify(NULL, attr_val, value, value_len);
+ } else if (ccc_entry && (ccc_entry->data[0] == BT_GATT_CCC_INDICATE)) {
+ struct bt_gatt_indicate_params params = {
+ .uuid = NULL,
+ .attr = attr_val,
+ .func = mp_bt_zephyr_gatt_indicate_done,
+ .destroy = NULL,
+ .data = value,
+ .len = value_len
+ };
+ err = bt_gatt_indicate(NULL, &params);
+ }
+ }
+ return err;
+}
+
+static void mp_bt_zephyr_gatt_indicate_done(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err) {
+ struct bt_conn_info info;
+ bt_conn_get_info(conn, &info);
+ uint16_t chr_handle = params->attr->handle - 1;
+ mp_bluetooth_gatts_on_indicate_complete(info.id, chr_handle, err);
+}
+
+static ssize_t mp_bt_zephyr_gatts_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) {
+ // we receive the value handle, but to look up in the gatts db we need the characteristic handle, and that is is the value handle minus 1
+ uint16_t _handle = attr->handle - 1;
+
+ DEBUG_printf("BLE attr read for handle %d\n", attr->handle);
+
+ mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle);
+ if (!entry) {
+ // it could be a descriptor instead
+ _handle = attr->handle;
+ entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle);
+ if (!entry) {
+ return BT_GATT_ERR(BT_ATT_ERR_INVALID_HANDLE);
+ }
+ }
+
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, entry->data, entry->data_len);
+}
+
+static ssize_t mp_bt_zephyr_gatts_attr_write(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) {
+ struct bt_conn_info info;
+ bt_conn_get_info(conn, &info);
+
+ DEBUG_printf("BLE attr write for handle %d\n", attr->handle);
+
+ // the characteristic handle is the value handle minus 1
+ uint16_t _handle = attr->handle - 1;
+
+ mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle);
+ if (!entry) {
+ // it could be a descriptor instead
+ _handle = attr->handle;
+ entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle);
+ if (!entry) {
+ return BT_GATT_ERR(BT_ATT_ERR_INVALID_HANDLE);
+ }
+ }
+
+ // Don't write anything if prepare flag is set
+ if (flags & BT_GATT_WRITE_FLAG_PREPARE) {
+ return 0;
+ }
+
+ if (offset > entry->data_alloc) {
+ return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
+ }
+
+ if ((offset + len) > entry->data_alloc) {
+ return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
+ }
+
+ if (entry->append) {
+ offset = entry->data_len;
+ }
+
+ // copy the data into the buffer in the gatts database
+ memcpy(&entry->data[offset], buf, len);
+ entry->data_len = offset + len;
+
+ mp_bluetooth_gatts_on_write(info.id, _handle);
+
+ return len;
+}
+
+static struct bt_gatt_attr *mp_bt_zephyr_find_attr_by_handle(uint16_t value_handle) {
+ for (int i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; i++) {
+ int j = 0;
+ while (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j].uuid != NULL) {
+ if (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j].handle == value_handle) {
+ return &MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j];
+ }
+ j++;
+ }
}
- return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len);
+ return NULL;
}
int mp_bluetooth_gatts_notify_indicate(uint16_t conn_handle, uint16_t value_handle, int gatts_op, const uint8_t *value, size_t value_len) {
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
- return MP_EOPNOTSUPP;
+
+ int err = MP_ENOENT;
+ mp_bt_zephyr_conn_t *connection = mp_bt_zephyr_find_connection(conn_handle);
+
+ if (connection) {
+ struct bt_gatt_attr *attr_val = mp_bt_zephyr_find_attr_by_handle(value_handle + 1);
+
+ if (attr_val) {
+ switch (gatts_op) {
+ case MP_BLUETOOTH_GATTS_OP_NOTIFY: {
+ err = bt_gatt_notify(connection->conn, attr_val, value, value_len);
+ break;
+ }
+ case MP_BLUETOOTH_GATTS_OP_INDICATE: {
+ struct bt_gatt_indicate_params params = {
+ .uuid = NULL,
+ .attr = attr_val,
+ .func = mp_bt_zephyr_gatt_indicate_done,
+ .destroy = NULL,
+ .data = value,
+ .len = value_len
+ };
+ err = bt_gatt_indicate(connection->conn, &params);
+ break;
+ }
+ }
+ }
+ }
+
+ return err;
}
int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) {
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
- return MP_EOPNOTSUPP;
+ return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, len, append);
}
int mp_bluetooth_get_preferred_mtu(void) {
@@ -404,6 +865,133 @@ int mp_bluetooth_gap_peripheral_connect_cancel(void) {
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
+// Note: modbluetooth UUIDs store their data in LE.
+static struct bt_uuid *create_zephyr_uuid(const mp_obj_bluetooth_uuid_t *uuid) {
+ struct bt_uuid *result = (struct bt_uuid *)m_new(union uuid_u, 1);
+ mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(result));
+ if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) {
+ bt_uuid_create(result, uuid->data, 2);
+ } else if (uuid->type == MP_BLUETOOTH_UUID_TYPE_32) {
+ bt_uuid_create(result, uuid->data, 4);
+ } else { // MP_BLUETOOTH_UUID_TYPE_128
+ bt_uuid_create(result, uuid->data, 16);
+ }
+ return result;
+}
+
+static void gatt_db_add(const struct bt_gatt_attr *pattern, struct bt_gatt_attr *attr, size_t user_data_len) {
+ const union uuid_u *u = CONTAINER_OF(pattern->uuid, union uuid_u, uuid);
+ size_t uuid_size = sizeof(u->u16);
+
+ if (u->uuid.type == BT_UUID_TYPE_32) {
+ uuid_size = sizeof(u->u32);
+ } else if (u->uuid.type == BT_UUID_TYPE_128) {
+ uuid_size = sizeof(u->u128);
+ }
+
+ memcpy(attr, pattern, sizeof(*attr));
+
+ // Store the UUID.
+ attr->uuid = (const struct bt_uuid *)m_new(union uuid_u, 1);
+ mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(attr->uuid));
+ memcpy((void *)attr->uuid, &u->uuid, uuid_size);
+
+ // Copy user_data to the buffer.
+ if (user_data_len) {
+ attr->user_data = m_new(uint8_t, user_data_len);
+ mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(attr->user_data));
+ memcpy(attr->user_data, pattern->user_data, user_data_len);
+ }
+}
+
+static void add_service(const struct bt_uuid *u, struct bt_gatt_attr *attr) {
+ union uuid_u *uuid = (union uuid_u *)u;
+
+ size_t uuid_size = sizeof(uuid->u16);
+
+ if (uuid->uuid.type == BT_UUID_TYPE_32) {
+ uuid_size = sizeof(uuid->u32);
+ } else if (uuid->uuid.type == BT_UUID_TYPE_128) {
+ uuid_size = sizeof(uuid->u128);
+ }
+
+ gatt_db_add(&(struct bt_gatt_attr)BT_GATT_PRIMARY_SERVICE(&uuid->uuid), attr, uuid_size);
+}
+
+static void add_characteristic(struct add_characteristic *ch, struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_value) {
+ struct bt_gatt_chrc *chrc_data;
+
+ // Add Characteristic Declaration
+ gatt_db_add(&(struct bt_gatt_attr)
+ BT_GATT_ATTRIBUTE(BT_UUID_GATT_CHRC,
+ BT_GATT_PERM_READ,
+ bt_gatt_attr_read_chrc, NULL,
+ (&(struct bt_gatt_chrc) {})), attr_chrc, sizeof(*chrc_data));
+
+ // Allow prepare writes
+ ch->permissions |= BT_GATT_PERM_PREPARE_WRITE;
+
+ // Add Characteristic Value
+ gatt_db_add(&(struct bt_gatt_attr)
+ BT_GATT_ATTRIBUTE(ch->uuid,
+ ch->permissions & GATT_PERM_MASK,
+ mp_bt_zephyr_gatts_attr_read, mp_bt_zephyr_gatts_attr_write, NULL), attr_value, 0);
+
+ chrc_data = attr_chrc->user_data;
+ chrc_data->properties = ch->properties;
+ chrc_data->uuid = attr_value->uuid;
+}
+
+static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) {
+ mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, attr->handle);
+ entry->data[0] = value;
+}
+
+static struct bt_gatt_attr ccc_definition = BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE);
+
+static void add_ccc(struct bt_gatt_attr *attr, struct bt_gatt_attr *attr_desc) {
+ struct bt_gatt_chrc *chrc = attr->user_data;
+
+ // Check characteristic properties
+ if (!(chrc->properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE))) {
+ mp_raise_OSError(MP_EINVAL);
+ }
+
+ // Add CCC descriptor to GATT database
+ gatt_db_add(&ccc_definition, attr_desc, 0);
+}
+
+static void add_cep(const struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_desc) {
+ struct bt_gatt_chrc *chrc = attr_chrc->user_data;
+ struct bt_gatt_cep cep_value;
+
+ // Extended Properties bit shall be set
+ if (!(chrc->properties & BT_GATT_CHRC_EXT_PROP)) {
+ mp_raise_OSError(MP_EINVAL);
+ }
+
+ cep_value.properties = 0x0000;
+
+ // Add CEP descriptor to GATT database
+ gatt_db_add(&(struct bt_gatt_attr)BT_GATT_CEP(&cep_value), attr_desc, sizeof(cep_value));
+}
+
+static void add_descriptor(struct bt_gatt_attr *chrc, struct add_descriptor *d, struct bt_gatt_attr *attr_desc) {
+ if (!bt_uuid_cmp(d->uuid, BT_UUID_GATT_CEP)) {
+ add_cep(chrc, attr_desc);
+ } else if (!bt_uuid_cmp(d->uuid, BT_UUID_GATT_CCC)) {
+ add_ccc(chrc, attr_desc);
+ } else {
+ // Allow prepare writes
+ d->permissions |= BT_GATT_PERM_PREPARE_WRITE;
+
+ gatt_db_add(&(struct bt_gatt_attr)
+ BT_GATT_DESCRIPTOR(d->uuid,
+ d->permissions & GATT_PERM_MASK,
+ mp_bt_zephyr_gatts_attr_read, mp_bt_zephyr_gatts_attr_write, NULL), attr_desc, 0);
+ }
+}
+
MP_REGISTER_ROOT_POINTER(struct _mp_bluetooth_zephyr_root_pointers_t *bluetooth_zephyr_root_pointers);
#endif // MICROPY_PY_BLUETOOTH
diff --git a/ports/zephyr/modsocket.c b/ports/zephyr/modsocket.c
index 1ca84edcac..d8955bffbe 100644
--- a/ports/zephyr/modsocket.c
+++ b/ports/zephyr/modsocket.c
@@ -32,8 +32,6 @@
#include <stdio.h>
#include <zephyr/kernel.h>
-// Zephyr's generated version header
-#include <version.h>
#include <zephyr/net/net_context.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/dns_resolve.h>
diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h
index e015776a4e..62226a2ded 100644
--- a/ports/zephyr/mpconfigport.h
+++ b/ports/zephyr/mpconfigport.h
@@ -26,7 +26,7 @@
#include <alloca.h>
// Include Zephyr's autoconf.h, which should be made first by Zephyr makefiles
-#include "autoconf.h"
+#include <zephyr/autoconf.h>
// Included here to get basic Zephyr environment (macros, etc.)
#include <zephyr/kernel.h>
#include <zephyr/drivers/spi.h>
@@ -36,17 +36,21 @@
#define MICROPY_HEAP_SIZE (16 * 1024)
#endif
+// We can't guarantee object layout of nlr code so use long jump by default.
+#define MICROPY_NLR_THUMB_USE_LONG_JUMP (1)
+
+#define MICROPY_PERSISTENT_CODE_LOAD (1)
#define MICROPY_ENABLE_SOURCE_LINE (1)
#define MICROPY_STACK_CHECK (1)
#define MICROPY_ENABLE_GC (1)
#define MICROPY_ENABLE_FINALISER (MICROPY_VFS)
#define MICROPY_HELPER_REPL (1)
#define MICROPY_REPL_AUTO_INDENT (1)
+#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
#define MICROPY_KBD_EXCEPTION (1)
#define MICROPY_PY_ASYNC_AWAIT (0)
#define MICROPY_PY_BUILTINS_BYTES_HEX (1)
#define MICROPY_PY_BUILTINS_FILTER (0)
-#define MICROPY_PY_BUILTINS_MIN_MAX (0)
#define MICROPY_PY_BUILTINS_PROPERTY (0)
#define MICROPY_PY_BUILTINS_RANGE_ATTRS (0)
#define MICROPY_PY_BUILTINS_REVERSED (0)
@@ -55,16 +59,18 @@
#define MICROPY_PY_BUILTINS_HELP (1)
#define MICROPY_PY_BUILTINS_HELP_TEXT zephyr_help_text
#define MICROPY_PY_ARRAY (0)
+#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1)
#define MICROPY_PY_COLLECTIONS (0)
#define MICROPY_PY_CMATH (0)
-#define MICROPY_PY_IO (0)
#define MICROPY_PY_MICROPYTHON_MEM_INFO (1)
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/zephyr/modmachine.c"
#define MICROPY_PY_MACHINE_I2C (1)
+#define MICROPY_PY_MACHINE_SOFTI2C (1)
#define MICROPY_PY_MACHINE_SPI (1)
#define MICROPY_PY_MACHINE_SPI_MSB (SPI_TRANSFER_MSB)
#define MICROPY_PY_MACHINE_SPI_LSB (SPI_TRANSFER_LSB)
+#define MICROPY_PY_MACHINE_SOFTSPI (1)
#define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new
#define MICROPY_PY_MACHINE_UART (1)
#define MICROPY_PY_MACHINE_UART_INCLUDEFILE "ports/zephyr/machine_uart.c"
@@ -95,7 +101,8 @@
#define MICROPY_PY_TIME_INCLUDEFILE "ports/zephyr/modtime.c"
#define MICROPY_PY_ZEPHYR (1)
#define MICROPY_PY_ZSENSOR (1)
-#define MICROPY_PY_SYS_MODULES (0)
+#define MICROPY_PY_SYS_MAXSIZE (1)
+#define MICROPY_PY_SYS_STDFILES (1)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#define MICROPY_PY_BUILTINS_COMPLEX (0)
@@ -139,12 +146,14 @@ void mp_hal_signal_event(void);
#define MICROPY_HW_MCU_NAME "unknown-cpu"
#endif
-typedef int mp_int_t; // must be pointer size
-typedef unsigned mp_uint_t; // must be pointer size
+typedef intptr_t mp_int_t; // must be pointer size
+typedef uintptr_t mp_uint_t; // must be pointer size
typedef long mp_off_t;
#define MP_STATE_PORT MP_STATE_VM
+#define MP_SSIZE_MAX (0x7fffffff)
+
// extra built in names to add to the global namespace
#define MICROPY_PORT_BUILTINS \
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },
diff --git a/ports/zephyr/mpconfigport_minimal.h b/ports/zephyr/mpconfigport_minimal.h
index 9aae20c72c..a0a7f97394 100644
--- a/ports/zephyr/mpconfigport_minimal.h
+++ b/ports/zephyr/mpconfigport_minimal.h
@@ -26,7 +26,7 @@
#include <alloca.h>
// Include Zephyr's autoconf.h, which should be made first by Zephyr makefiles
-#include "autoconf.h"
+#include <zephyr/autoconf.h>
// Included here to get basic Zephyr environment (macros, etc.)
#include <zephyr/kernel.h>
diff --git a/ports/zephyr/mphalport.c b/ports/zephyr/mphalport.c
index 2c95032843..db536ec085 100644
--- a/ports/zephyr/mphalport.c
+++ b/ports/zephyr/mphalport.c
@@ -26,6 +26,7 @@
#include "py/runtime.h"
#include "py/mphal.h"
+#include "extmod/modmachine.h"
static struct k_poll_signal wait_signal;
static struct k_poll_event wait_events[2] = {
@@ -75,3 +76,10 @@ void mp_hal_wait_sem(struct k_sem *sem, uint32_t timeout_ms) {
}
}
}
+
+mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in) {
+ if (mp_obj_is_type(pin_in, &machine_pin_type)) {
+ return MP_OBJ_TO_PTR(pin_in);
+ }
+ mp_raise_ValueError(MP_ERROR_TEXT("invalid pin"));
+}
diff --git a/ports/zephyr/mphalport.h b/ports/zephyr/mphalport.h
index e5414c2705..7410204621 100644
--- a/ports/zephyr/mphalport.h
+++ b/ports/zephyr/mphalport.h
@@ -1,4 +1,5 @@
#include <zephyr/kernel.h>
+#include <zephyr/drivers/gpio.h>
#include "shared/runtime/interrupt_char.h"
#define MICROPY_BEGIN_ATOMIC_SECTION irq_lock
@@ -35,3 +36,48 @@ static inline uint64_t mp_hal_time_ns(void) {
}
#define mp_hal_delay_us_fast(us) (mp_hal_delay_us(us))
+
+// C-level pin HAL
+
+#include "modmachine.h"
+
+#define MP_HAL_PIN_FMT "%u"
+#define mp_hal_pin_obj_t const machine_pin_obj_t *
+
+mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in);
+
+static inline unsigned int mp_hal_pin_name(mp_hal_pin_obj_t pin) {
+ // TODO make it include the port
+ return pin->pin;
+}
+
+static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) {
+ (void)gpio_pin_configure(pin->port, pin->pin, GPIO_INPUT);
+}
+
+static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) {
+ if (gpio_pin_configure(pin->port, pin->pin, GPIO_OUTPUT | GPIO_INPUT) == -ENOTSUP) {
+ // If GPIO_OUTPUT|GPIO_INPUT is not supported (eg frdm_k64f) then try just GPIO_OUTPUT.
+ (void)gpio_pin_configure(pin->port, pin->pin, GPIO_OUTPUT);
+ }
+}
+
+static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) {
+ (void)gpio_pin_configure(pin->port, pin->pin, GPIO_OUTPUT | GPIO_INPUT | GPIO_OPEN_DRAIN);
+}
+
+static inline int mp_hal_pin_read(mp_hal_pin_obj_t pin) {
+ return gpio_pin_get_raw(pin->port, pin->pin);
+}
+
+static inline void mp_hal_pin_write(mp_hal_pin_obj_t pin, int v) {
+ (void)gpio_pin_set_raw(pin->port, pin->pin, v);
+}
+
+static inline void mp_hal_pin_od_low(mp_hal_pin_obj_t pin) {
+ (void)gpio_pin_set_raw(pin->port, pin->pin, 0);
+}
+
+static inline void mp_hal_pin_od_high(mp_hal_pin_obj_t pin) {
+ (void)gpio_pin_set_raw(pin->port, pin->pin, 1);
+}
diff --git a/ports/zephyr/prj.conf b/ports/zephyr/prj.conf
index 0325cddd20..0939e226cf 100644
--- a/ports/zephyr/prj.conf
+++ b/ports/zephyr/prj.conf
@@ -6,11 +6,6 @@ CONFIG_STDOUT_CONSOLE=y
CONFIG_CONSOLE_HANDLER=y
CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS=y
-CONFIG_CONSOLE_SUBSYS=y
-CONFIG_CONSOLE_GETCHAR=y
-CONFIG_CONSOLE_GETCHAR_BUFSIZE=258
-CONFIG_CONSOLE_PUTCHAR_BUFSIZE=128
-
CONFIG_NEWLIB_LIBC=y
CONFIG_FPU=y
CONFIG_MAIN_STACK_SIZE=4736
@@ -81,3 +76,10 @@ CONFIG_MICROPY_VFS_LFS2=y
CONFIG_WATCHDOG=y
CONFIG_WDT_DISABLE_AT_BOOT=y
+
+CONFIG_SERIAL=y
+CONFIG_UART_INTERRUPT_DRIVEN=y
+CONFIG_UART_LINE_CTRL=y
+CONFIG_UART_USE_RUNTIME_CONFIGURE=y
+
+CONFIG_LEGACY_GENERATED_INCLUDE_PATH=n
diff --git a/ports/zephyr/prj_minimal.conf b/ports/zephyr/prj_minimal.conf
index 6c850751d7..f58c932cea 100644
--- a/ports/zephyr/prj_minimal.conf
+++ b/ports/zephyr/prj_minimal.conf
@@ -8,8 +8,7 @@ CONFIG_CONSOLE_SUBSYS=y
CONFIG_CONSOLE_GETCHAR=y
CONFIG_CONSOLE_GETCHAR_BUFSIZE=258
CONFIG_CONSOLE_PUTCHAR_BUFSIZE=32
-# TODO: Disable once https://github.com/zephyrproject-rtos/zephyr/pull/13731 is merged
-#CONFIG_CONSOLE=n
-#CONFIG_UART_CONSOLE=n
CONFIG_MICROPY_HEAP_SIZE=16384
+
+CONFIG_LEGACY_GENERATED_INCLUDE_PATH=n
diff --git a/ports/zephyr/qstrdefsport.h b/ports/zephyr/qstrdefsport.h
new file mode 100644
index 0000000000..66819e432c
--- /dev/null
+++ b/ports/zephyr/qstrdefsport.h
@@ -0,0 +1,4 @@
+// qstrs specific to this port
+// *FORMAT-OFF*
+Q(/flash/lib)
+Q(/sd/lib)
diff --git a/ports/zephyr/src/usbd.c b/ports/zephyr/src/usbd.c
new file mode 100644
index 0000000000..36b07a8638
--- /dev/null
+++ b/ports/zephyr/src/usbd.c
@@ -0,0 +1,193 @@
+/*
+* This file is part of the MicroPython project, http://micropython.org/
+*
+* The MIT License (MIT)
+*
+* Copyright (c) 2024-2025 MASSDRIVER EI (massdriver.space)
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+*/
+#include <stdint.h>
+
+#include <zephyr/device.h>
+#include <zephyr/usb/usbd.h>
+#include <zephyr/usb/bos.h>
+
+#if defined(CONFIG_USB_DEVICE_STACK_NEXT)
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(mp_usbd);
+
+#if KERNEL_VERSION_NUMBER >= ZEPHYR_VERSION(4, 1, 0)
+
+#define BLOCKLIST , blocklist
+
+/* By default, do not register the USB DFU class DFU mode instance. */
+static const char *const blocklist[] = {
+ "dfu_dfu",
+ NULL,
+};
+
+#else
+
+#define BLOCKLIST
+
+#endif
+
+USBD_DEVICE_DEFINE(mp_usbd,
+ DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
+ CONFIG_MICROPY_USB_DEVICE_VID, CONFIG_MICROPY_USB_DEVICE_PID);
+
+USBD_DESC_LANG_DEFINE(mp_lang);
+USBD_DESC_MANUFACTURER_DEFINE(mp_mfr, "Zephyr Project");
+USBD_DESC_PRODUCT_DEFINE(mp_product, "Micropython on Zephyr RTOS");
+USBD_DESC_SERIAL_NUMBER_DEFINE(mp_sn);
+
+USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration");
+USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration");
+
+/* not self-powered, no remote wakeup */
+static const uint8_t attributes = 0;
+
+/* Full speed configuration
+* power = 250 * 2 mA = 500mA
+*/
+USBD_CONFIGURATION_DEFINE(mp_fs_config,
+ attributes,
+ 250, &fs_cfg_desc);
+
+/* High speed configuration */
+USBD_CONFIGURATION_DEFINE(mp_hs_config,
+ attributes,
+ 250, &hs_cfg_desc);
+
+static void mp_fix_code_triple(struct usbd_context *uds_ctx,
+ const enum usbd_speed speed) {
+ /* Always use class code information from Interface Descriptors */
+ if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) ||
+ IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) ||
+ IS_ENABLED(CONFIG_USBD_CDC_NCM_CLASS) ||
+ IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS)) {
+ /*
+ * Class with multiple interfaces have an Interface
+ * Association Descriptor available, use an appropriate triple
+ * to indicate it.
+ */
+ usbd_device_set_code_triple(uds_ctx, speed,
+ USB_BCC_MISCELLANEOUS, 0x02, 0x01);
+ } else {
+ usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0);
+ }
+}
+
+struct usbd_context *mp_usbd_init_device(usbd_msg_cb_t msg_cb) {
+ int err;
+
+ err = usbd_add_descriptor(&mp_usbd, &mp_lang);
+ if (err) {
+ LOG_ERR("Failed to initialize language descriptor (%d)", err);
+ return NULL;
+ }
+
+ err = usbd_add_descriptor(&mp_usbd, &mp_mfr);
+ if (err) {
+ LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err);
+ return NULL;
+ }
+
+ err = usbd_add_descriptor(&mp_usbd, &mp_product);
+ if (err) {
+ LOG_ERR("Failed to initialize product descriptor (%d)", err);
+ return NULL;
+ }
+
+ err = usbd_add_descriptor(&mp_usbd, &mp_sn);
+ if (err) {
+ LOG_ERR("Failed to initialize SN descriptor (%d)", err);
+ return NULL;
+ }
+
+ if (usbd_caps_speed(&mp_usbd) == USBD_SPEED_HS) {
+ err = usbd_add_configuration(&mp_usbd, USBD_SPEED_HS,
+ &mp_hs_config);
+ if (err) {
+ LOG_ERR("Failed to add High-Speed configuration");
+ return NULL;
+ }
+
+ err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_HS, 1 BLOCKLIST);
+ if (err) {
+ LOG_ERR("Failed to add register classes");
+ return NULL;
+ }
+
+ mp_fix_code_triple(&mp_usbd, USBD_SPEED_HS);
+ }
+
+ err = usbd_add_configuration(&mp_usbd, USBD_SPEED_FS,
+ &mp_fs_config);
+ if (err) {
+ LOG_ERR("Failed to add Full-Speed configuration");
+ return NULL;
+ }
+
+ err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_FS, 1 BLOCKLIST);
+ if (err) {
+ LOG_ERR("Failed to add register classes");
+ return NULL;
+ }
+
+ mp_fix_code_triple(&mp_usbd, USBD_SPEED_FS);
+
+ if (msg_cb != NULL) {
+ err = usbd_msg_register_cb(&mp_usbd, msg_cb);
+ if (err) {
+ LOG_ERR("Failed to register message callback");
+ return NULL;
+ }
+ }
+
+ err = usbd_init(&mp_usbd);
+ if (err) {
+ LOG_ERR("Failed to initialize device support");
+ return NULL;
+ }
+
+ return &mp_usbd;
+}
+
+static struct usbd_context *mp_usbd_context;
+
+int mp_usbd_init(void) {
+ int err;
+
+ mp_usbd_context = mp_usbd_init_device(NULL);
+ if (mp_usbd_context == NULL) {
+ return -ENODEV;
+ }
+
+ err = usbd_enable(mp_usbd_context);
+ if (err) {
+ return err;
+ }
+
+ return 0;
+}
+
+#endif // defined(CONFIG_USB_DEVICE_STACK_NEXT)
diff --git a/ports/zephyr/src/zephyr_getchar.c b/ports/zephyr/src/zephyr_getchar.c
index 94e35e2e84..bf504a97c9 100644
--- a/ports/zephyr/src/zephyr_getchar.c
+++ b/ports/zephyr/src/zephyr_getchar.c
@@ -23,12 +23,10 @@
extern int mp_interrupt_char;
void mp_sched_keyboard_interrupt(void);
void mp_hal_signal_event(void);
-void mp_hal_wait_sem(struct k_sem *sem, uint32_t timeout_ms);
-static struct k_sem uart_sem;
-#define UART_BUFSIZE 256
+#define UART_BUFSIZE (512)
static uint8_t uart_ringbuf[UART_BUFSIZE];
-static uint8_t i_get, i_put;
+static uint16_t i_get, i_put;
static int console_irq_input_hook(uint8_t ch) {
int i_next = (i_put + 1) & (UART_BUFSIZE - 1);
@@ -44,14 +42,16 @@ static int console_irq_input_hook(uint8_t ch) {
uart_ringbuf[i_put] = ch;
i_put = i_next;
}
- // printk("%x\n", ch);
- k_sem_give(&uart_sem);
return 1;
}
+// Returns true if a char is available for reading.
+int zephyr_getchar_check(void) {
+ return i_get != i_put;
+}
+
int zephyr_getchar(void) {
- mp_hal_wait_sem(&uart_sem, 0);
- if (k_sem_take(&uart_sem, K_MSEC(0)) == 0) {
+ if (i_get != i_put) {
unsigned int key = irq_lock();
int c = (int)uart_ringbuf[i_get++];
i_get &= UART_BUFSIZE - 1;
@@ -62,7 +62,6 @@ int zephyr_getchar(void) {
}
void zephyr_getchar_init(void) {
- k_sem_init(&uart_sem, 0, UINT_MAX);
uart_console_in_debug_hook_install(console_irq_input_hook);
// All NULLs because we're interested only in the callback above
uart_register_input(NULL, NULL, NULL);
diff --git a/ports/zephyr/src/zephyr_getchar.h b/ports/zephyr/src/zephyr_getchar.h
index fee899e1b4..3f31c4317f 100644
--- a/ports/zephyr/src/zephyr_getchar.h
+++ b/ports/zephyr/src/zephyr_getchar.h
@@ -17,4 +17,5 @@
#include <stdint.h>
void zephyr_getchar_init(void);
+int zephyr_getchar_check(void);
int zephyr_getchar(void);
diff --git a/ports/zephyr/uart_core.c b/ports/zephyr/uart_core.c
index ee525c33f7..fe8a2a51db 100644
--- a/ports/zephyr/uart_core.c
+++ b/ports/zephyr/uart_core.c
@@ -26,6 +26,7 @@
#include <unistd.h>
#include "py/mpconfig.h"
#include "py/runtime.h"
+#include "py/stream.h"
#include "src/zephyr_getchar.h"
// Zephyr headers
#include <zephyr/kernel.h>
@@ -52,6 +53,24 @@ static uint8_t mp_console_txbuf[CONFIG_CONSOLE_PUTCHAR_BUFSIZE];
* Core UART functions to implement for a port
*/
+uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
+ uintptr_t ret = 0;
+ if (poll_flags & MP_STREAM_POLL_RD) {
+ #ifdef CONFIG_CONSOLE_SUBSYS
+ // It's not easy to test if tty is readable, so just unconditionally set it for now.
+ ret |= MP_STREAM_POLL_RD;
+ #else
+ if (zephyr_getchar_check()) {
+ ret |= MP_STREAM_POLL_RD;
+ }
+ #endif
+ }
+ if (poll_flags & MP_STREAM_POLL_WR) {
+ ret |= MP_STREAM_POLL_WR;
+ }
+ return ret;
+}
+
// Receive single character
int mp_hal_stdin_rx_chr(void) {
for (;;) {
diff --git a/ports/zephyr/zephyr_storage.c b/ports/zephyr/zephyr_storage.c
index 484feb1130..40bcef7338 100644
--- a/ports/zephyr/zephyr_storage.c
+++ b/ports/zephyr/zephyr_storage.c
@@ -139,6 +139,20 @@ MP_DEFINE_CONST_OBJ_TYPE(
#endif // CONFIG_DISK_ACCESS
#ifdef CONFIG_FLASH_MAP
+
+#define FLASH_AREA_DEFINE_LABEL(part) CONCAT(MP_QSTR_ID_, DT_STRING_TOKEN(part, label))
+#define FLASH_AREA_DEFINE_NB(part) CONCAT(MP_QSTR_ID_, DT_FIXED_PARTITION_ID(part))
+
+#define FLASH_AREA_DEFINE_GETNAME(part) COND_CODE_1(DT_NODE_HAS_PROP(part, label), \
+ (FLASH_AREA_DEFINE_LABEL(part)), (FLASH_AREA_DEFINE_NB(part)))
+
+#define FLASH_AREA_DEFINE_DEFINE(part) { MP_ROM_QSTR(FLASH_AREA_DEFINE_GETNAME(part)), MP_ROM_INT(DT_FIXED_PARTITION_ID(part)) },
+
+#define FLASH_AREA_DEFINE(part) COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_MTD_FROM_FIXED_PARTITION(part)), \
+ (FLASH_AREA_DEFINE_DEFINE(part)), ())
+
+#define FOREACH_PARTITION(n) DT_FOREACH_CHILD(n, FLASH_AREA_DEFINE)
+
const mp_obj_type_t zephyr_flash_area_type;
typedef struct _zephyr_flash_area_obj_t {
@@ -244,9 +258,8 @@ static const mp_rom_map_elem_t zephyr_flash_area_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&zephyr_flash_area_readblocks_obj) },
{ MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&zephyr_flash_area_writeblocks_obj) },
{ MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&zephyr_flash_area_ioctl_obj) },
- #if FIXED_PARTITION_EXISTS(storage_partition)
- { MP_ROM_QSTR(MP_QSTR_STORAGE), MP_ROM_INT(FIXED_PARTITION_ID(storage_partition)) },
- #endif
+ /* Generate list of partition IDs from Zephyr Devicetree */
+ DT_FOREACH_STATUS_OKAY(fixed_partitions, FOREACH_PARTITION)
};
static MP_DEFINE_CONST_DICT(zephyr_flash_area_locals_dict, zephyr_flash_area_locals_dict_table);