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