diff options
Diffstat (limited to 'ports')
105 files changed, 2220 insertions, 597 deletions
diff --git a/ports/alif/tinyusb_port/alif_dcd_reg.h b/ports/alif/tinyusb_port/alif_dcd_reg.h index 84220f6be..c5a707656 100644 --- a/ports/alif/tinyusb_port/alif_dcd_reg.h +++ b/ports/alif/tinyusb_port/alif_dcd_reg.h @@ -1,11 +1,13 @@ // *FORMAT-OFF* -///------------------------------------------------------------------------------------------------- -/// @file alif_dcd_reg.h -/// @author karol.saja@alifsemi.com -/// @version 0.0.1 -/// @date 2023-09-08 -/// @brief Low Level SPI driver -///------------------------------------------------------------------------------------------------- +/* + * Copyright (C) 2024 Alif Semiconductor - All Rights Reserved. + * Use, distribution and modification of this code is permitted under the + * terms stated in the Alif Semiconductor Software License Agreement + * + * You should have received a copy of the Alif Semiconductor Software + * License Agreement with this file. If not, please write to: + * contact@alifsemi.com, or visit: https://alifsemi.com/license + */ #ifndef __ALIF_DCD_REG_H__ #define __ALIF_DCD_REG_H__ diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake index a38888234..06d3c27a6 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake @@ -6,7 +6,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz boards/sdkconfig.spiram_sx diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h index 31b2a49bf..3ca587ae4 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h @@ -22,6 +22,11 @@ #define MICROPY_HW_USB_CDC_1200BPS_TOUCH (1) #define MICROPY_SCHEDULER_STATIC_NODES (1) +#define MICROPY_HW_USB_VID 0x2341 +#define MICROPY_HW_USB_PID 0x056B +#define MICROPY_HW_USB_MANUFACTURER_STRING "Arduino" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "Nano ESP32" + #define MICROPY_BOARD_STARTUP NANO_ESP32_board_startup void NANO_ESP32_board_startup(void); diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/sdkconfig.board b/ports/esp32/boards/ARDUINO_NANO_ESP32/sdkconfig.board index d586dd4ce..242cff84d 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/sdkconfig.board +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/sdkconfig.board @@ -11,13 +11,6 @@ CONFIG_SPIRAM_IGNORE_NOTFOUND= CONFIG_LWIP_LOCAL_HOSTNAME="nano-esp32" -CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID=n -CONFIG_TINYUSB_DESC_USE_DEFAULT_PID=n -CONFIG_TINYUSB_DESC_CUSTOM_VID=0x2341 -CONFIG_TINYUSB_DESC_CUSTOM_PID=0x056B -CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Arduino" -CONFIG_TINYUSB_DESC_PRODUCT_STRING="Nano ESP32" - # compatibility with Espressif Arduino core CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y CONFIG_ESP_ENABLE_COREDUMP_TO_FLASH=y diff --git a/ports/esp32/boards/ESP32_GENERIC_C5/board.json b/ports/esp32/boards/ESP32_GENERIC_C5/board.json new file mode 100644 index 000000000..371da3929 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_C5/board.json @@ -0,0 +1,22 @@ +{ + "deploy": [ + "../deploy.md" + ], + "deploy_options": { + "flash_offset": "0x2000" + }, + "docs": "", + "features": [ + "BLE", + "External Flash", + "WiFi" + ], + "images": [ + "esp32c5_devkitmini.jpg" + ], + "mcu": "esp32c5", + "product": "ESP32-C5", + "thumbnail": "", + "url": "https://www.espressif.com/en/products/modules", + "vendor": "Espressif" +} diff --git a/ports/esp32/boards/ESP32_GENERIC_C5/board.md b/ports/esp32/boards/ESP32_GENERIC_C5/board.md new file mode 100644 index 000000000..82bac44b9 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_C5/board.md @@ -0,0 +1,2 @@ +The following files are firmware images that should work on most ESP32-C5-based +boards with at least 4MiB of flash and 40MHz/48MHz crystal frequency. diff --git a/ports/esp32/boards/ESP32_GENERIC_C5/mpconfigboard.cmake b/ports/esp32/boards/ESP32_GENERIC_C5/mpconfigboard.cmake new file mode 100644 index 000000000..79aba7d47 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_C5/mpconfigboard.cmake @@ -0,0 +1,10 @@ +set(IDF_TARGET esp32c5) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.riscv + boards/sdkconfig.ble + boards/sdkconfig.240mhz + boards/sdkconfig.free_ram + boards/ESP32_GENERIC_C5/sdkconfig.board +) diff --git a/ports/esp32/boards/ESP32_GENERIC_C5/mpconfigboard.h b/ports/esp32/boards/ESP32_GENERIC_C5/mpconfigboard.h new file mode 100644 index 000000000..552468497 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_C5/mpconfigboard.h @@ -0,0 +1,7 @@ +// This configuration is for a generic ESP32C5 board with 4MiB (or more) of flash. + +#define MICROPY_HW_BOARD_NAME "ESP32C5 module" +#define MICROPY_HW_MCU_NAME "ESP32C5" + +#define MICROPY_PY_MACHINE_I2S (0) +#define MICROPY_HW_ENABLE_UART_REPL (1) diff --git a/ports/esp32/boards/ESP32_GENERIC_C5/sdkconfig.board b/ports/esp32/boards/ESP32_GENERIC_C5/sdkconfig.board new file mode 100644 index 000000000..369330682 --- /dev/null +++ b/ports/esp32/boards/ESP32_GENERIC_C5/sdkconfig.board @@ -0,0 +1,2 @@ +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y diff --git a/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.cmake b/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.cmake index 76d185d90..6907adb81 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.cmake +++ b/ports/esp32/boards/ESP32_GENERIC_S2/mpconfigboard.cmake @@ -2,6 +2,5 @@ set(IDF_TARGET esp32s2) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - boards/sdkconfig.usb boards/sdkconfig.spiram_sx ) diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake index 9b0df3b37..cdcefed84 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake +++ b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.spiram_sx boards/ESP32_GENERIC_S3/sdkconfig.board diff --git a/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.cmake b/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.cmake index dc9abd747..8d6d1f359 100644 --- a/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.cmake +++ b/ports/esp32/boards/LOLIN_S2_MINI/mpconfigboard.cmake @@ -3,7 +3,6 @@ set(IDF_TARGET esp32s2) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base boards/sdkconfig.spiram_sx - boards/sdkconfig.usb ) set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.cmake b/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.cmake index dc9abd747..8d6d1f359 100644 --- a/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.cmake +++ b/ports/esp32/boards/LOLIN_S2_PICO/mpconfigboard.cmake @@ -3,7 +3,6 @@ set(IDF_TARGET esp32s2) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base boards/sdkconfig.spiram_sx - boards/sdkconfig.usb ) set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/mpconfigboard.cmake b/ports/esp32/boards/M5STACK_ATOMS3_LITE/mpconfigboard.cmake index 10608fcec..9157c6b93 100644 --- a/ports/esp32/boards/M5STACK_ATOMS3_LITE/mpconfigboard.cmake +++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - boards/sdkconfig.usb boards/sdkconfig.ble boards/ESP32_GENERIC_S3/sdkconfig.board ) diff --git a/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.cmake b/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.cmake index 5e570d513..2d8f70135 100644 --- a/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_FEATHERS2/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s2) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base boards/sdkconfig.spiram_sx - boards/sdkconfig.usb boards/UM_FEATHERS2/sdkconfig.board ) diff --git a/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.cmake b/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.cmake index c98ef6917..defb597c9 100644 --- a/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_FEATHERS2NEO/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s2) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base boards/sdkconfig.spiram_sx - boards/sdkconfig.usb boards/UM_FEATHERS2NEO/sdkconfig.board ) diff --git a/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake b/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake index 63d1af1da..5a41be186 100644 --- a/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz boards/sdkconfig.spiram_sx diff --git a/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.h b/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.h index f4abfb21b..cb6269ff9 100644 --- a/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.h +++ b/ports/esp32/boards/UM_FEATHERS3/mpconfigboard.h @@ -11,3 +11,8 @@ #define MICROPY_HW_SPI1_MOSI (35) #define MICROPY_HW_SPI1_MISO (37) #define MICROPY_HW_SPI1_SCK (36) + +#define MICROPY_HW_USB_VID 0x303A +#define MICROPY_HW_USB_PID 0x80D7 +#define MICROPY_HW_USB_MANUFACTURER_STRING "Unexpected Maker" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "FeatherS3" diff --git a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board index 3092e3559..2962cb44c 100644 --- a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board +++ b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board @@ -4,12 +4,3 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_SPIRAM_MEMTEST= CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3" - -CONFIG_TINYUSB_DESC_CUSTOM_VID=0x303A -CONFIG_TINYUSB_DESC_CUSTOM_PID=0x80D7 -CONFIG_TINYUSB_DESC_BCD_DEVICE=0x0100 -CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID=n -CONFIG_TINYUSB_DESC_USE_DEFAULT_PID=n -CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Unexpected Maker" -CONFIG_TINYUSB_DESC_PRODUCT_STRING="FeatherS3" -CONFIG_TINYUSB_DESC_SERIAL_STRING="_fs3_" diff --git a/ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.cmake b/ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.cmake index 1d2b887d2..698aa76f0 100644 --- a/ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz boards/sdkconfig.spiram_sx diff --git a/ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.h b/ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.h index c08206f7b..bfe38bc03 100644 --- a/ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.h +++ b/ports/esp32/boards/UM_FEATHERS3NEO/mpconfigboard.h @@ -8,3 +8,8 @@ #define MICROPY_HW_SPI1_MOSI (35) #define MICROPY_HW_SPI1_MISO (37) #define MICROPY_HW_SPI1_SCK (36) + +#define MICROPY_HW_USB_VID 0x303A +#define MICROPY_HW_USB_PID 0x81FC +#define MICROPY_HW_USB_MANUFACTURER_STRING "Unexpected Maker" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "FeatherS3 Neo" diff --git a/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board index 25b8d7689..8d44c65c3 100644 --- a/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board +++ b/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board @@ -5,12 +5,3 @@ CONFIG_ESPTOOLPY_AFTER_NORESET=y CONFIG_SPIRAM_MEMTEST= CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3Neo" - -CONFIG_TINYUSB_DESC_CUSTOM_VID=0x303A -CONFIG_TINYUSB_DESC_CUSTOM_PID=0x81FC -CONFIG_TINYUSB_DESC_BCD_DEVICE=0x0100 -CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID=n -CONFIG_TINYUSB_DESC_USE_DEFAULT_PID=n -CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Unexpected Maker" -CONFIG_TINYUSB_DESC_PRODUCT_STRING="FeatherS3 Neo" -CONFIG_TINYUSB_DESC_SERIAL_STRING="_fs3neo_" diff --git a/ports/esp32/boards/UM_NANOS3/mpconfigboard.cmake b/ports/esp32/boards/UM_NANOS3/mpconfigboard.cmake index 6c7f34009..3935a66f8 100644 --- a/ports/esp32/boards/UM_NANOS3/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_NANOS3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz boards/sdkconfig.spiram_sx diff --git a/ports/esp32/boards/UM_NANOS3/mpconfigboard.h b/ports/esp32/boards/UM_NANOS3/mpconfigboard.h index 44197be2a..44434d26e 100644 --- a/ports/esp32/boards/UM_NANOS3/mpconfigboard.h +++ b/ports/esp32/boards/UM_NANOS3/mpconfigboard.h @@ -8,3 +8,8 @@ #define MICROPY_HW_SPI1_MOSI (35) #define MICROPY_HW_SPI1_MISO (37) #define MICROPY_HW_SPI1_SCK (36) + +#define MICROPY_HW_USB_VID 0x303A +#define MICROPY_HW_USB_PID 0x817A +#define MICROPY_HW_USB_MANUFACTURER_STRING "Unexpected Maker" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "NanoS3" diff --git a/ports/esp32/boards/UM_NANOS3/sdkconfig.board b/ports/esp32/boards/UM_NANOS3/sdkconfig.board index e06f7a424..0a1361b9b 100644 --- a/ports/esp32/boards/UM_NANOS3/sdkconfig.board +++ b/ports/esp32/boards/UM_NANOS3/sdkconfig.board @@ -4,12 +4,3 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_SPIRAM_MEMTEST= CONFIG_LWIP_LOCAL_HOSTNAME="UMNanoS3" - -CONFIG_TINYUSB_DESC_CUSTOM_VID=0x303A -CONFIG_TINYUSB_DESC_CUSTOM_PID=0x817A -CONFIG_TINYUSB_DESC_BCD_DEVICE=0x0100 -CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID=n -CONFIG_TINYUSB_DESC_USE_DEFAULT_PID=n -CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Unexpected Maker" -CONFIG_TINYUSB_DESC_PRODUCT_STRING="NanoS3" -CONFIG_TINYUSB_DESC_SERIAL_STRING="_ns3_" diff --git a/ports/esp32/boards/UM_OMGS3/mpconfigboard.cmake b/ports/esp32/boards/UM_OMGS3/mpconfigboard.cmake index aa82111d7..85c3d71e8 100644 --- a/ports/esp32/boards/UM_OMGS3/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_OMGS3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz boards/sdkconfig.spiram_sx diff --git a/ports/esp32/boards/UM_OMGS3/mpconfigboard.h b/ports/esp32/boards/UM_OMGS3/mpconfigboard.h index e3955a6b8..451225b6f 100644 --- a/ports/esp32/boards/UM_OMGS3/mpconfigboard.h +++ b/ports/esp32/boards/UM_OMGS3/mpconfigboard.h @@ -8,3 +8,8 @@ #define MICROPY_HW_SPI1_MOSI (6) #define MICROPY_HW_SPI1_MISO (5) #define MICROPY_HW_SPI1_SCK (4) + +#define MICROPY_HW_USB_VID 0x303A +#define MICROPY_HW_USB_PID 0x8225 +#define MICROPY_HW_USB_MANUFACTURER_STRING "Unexpected Maker" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "OMGS3" diff --git a/ports/esp32/boards/UM_OMGS3/sdkconfig.board b/ports/esp32/boards/UM_OMGS3/sdkconfig.board index 8a0bf0b13..794bc70b1 100644 --- a/ports/esp32/boards/UM_OMGS3/sdkconfig.board +++ b/ports/esp32/boards/UM_OMGS3/sdkconfig.board @@ -5,12 +5,3 @@ CONFIG_ESPTOOLPY_AFTER_NORESET=y CONFIG_SPIRAM_MEMTEST= CONFIG_LWIP_LOCAL_HOSTNAME="UMOMGS3" - -CONFIG_TINYUSB_DESC_CUSTOM_VID=0x303A -CONFIG_TINYUSB_DESC_CUSTOM_PID=0x8225 -CONFIG_TINYUSB_DESC_BCD_DEVICE=0x0100 -CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID=n -CONFIG_TINYUSB_DESC_USE_DEFAULT_PID=n -CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Unexpected Maker" -CONFIG_TINYUSB_DESC_PRODUCT_STRING="OMGS3" -CONFIG_TINYUSB_DESC_SERIAL_STRING="_omgs3_" diff --git a/ports/esp32/boards/UM_PROS3/mpconfigboard.cmake b/ports/esp32/boards/UM_PROS3/mpconfigboard.cmake index 41a96f26e..c8267f728 100644 --- a/ports/esp32/boards/UM_PROS3/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_PROS3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz boards/sdkconfig.spiram_sx diff --git a/ports/esp32/boards/UM_PROS3/mpconfigboard.h b/ports/esp32/boards/UM_PROS3/mpconfigboard.h index cc0ebbefa..eea6f6e65 100644 --- a/ports/esp32/boards/UM_PROS3/mpconfigboard.h +++ b/ports/esp32/boards/UM_PROS3/mpconfigboard.h @@ -8,3 +8,8 @@ #define MICROPY_HW_SPI1_MOSI (35) #define MICROPY_HW_SPI1_MISO (37) #define MICROPY_HW_SPI1_SCK (36) + +#define MICROPY_HW_USB_VID 0x303A +#define MICROPY_HW_USB_PID 0x80D4 +#define MICROPY_HW_USB_MANUFACTURER_STRING "Unexpected Maker" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "ProS3" diff --git a/ports/esp32/boards/UM_PROS3/sdkconfig.board b/ports/esp32/boards/UM_PROS3/sdkconfig.board index 75ca58d80..1b10ba11c 100644 --- a/ports/esp32/boards/UM_PROS3/sdkconfig.board +++ b/ports/esp32/boards/UM_PROS3/sdkconfig.board @@ -4,12 +4,3 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_SPIRAM_MEMTEST= CONFIG_LWIP_LOCAL_HOSTNAME="UMProS3" - -CONFIG_TINYUSB_DESC_CUSTOM_VID=0x303A -CONFIG_TINYUSB_DESC_CUSTOM_PID=0x80D4 -CONFIG_TINYUSB_DESC_BCD_DEVICE=0x0100 -CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID=n -CONFIG_TINYUSB_DESC_USE_DEFAULT_PID=n -CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Unexpected Maker" -CONFIG_TINYUSB_DESC_PRODUCT_STRING="ProS3" -CONFIG_TINYUSB_DESC_SERIAL_STRING="_ps3_" diff --git a/ports/esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.cmake b/ports/esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.cmake index 8b29cb344..4686fc588 100644 --- a/ports/esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz boards/sdkconfig.spiram_sx diff --git a/ports/esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.h b/ports/esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.h index 118412479..2d83cba16 100644 --- a/ports/esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.h +++ b/ports/esp32/boards/UM_RGBTOUCH_MINI/mpconfigboard.h @@ -8,3 +8,8 @@ #define MICROPY_HW_SPI1_MOSI (35) #define MICROPY_HW_SPI1_MISO (37) #define MICROPY_HW_SPI1_SCK (36) + +#define MICROPY_HW_USB_VID 0x303A +#define MICROPY_HW_USB_PID 0x81FF +#define MICROPY_HW_USB_MANUFACTURER_STRING "Unexpected Maker" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "RGBTouchMini" diff --git a/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board b/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board index 7d244fdc1..234aed6b2 100644 --- a/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board +++ b/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board @@ -5,12 +5,3 @@ CONFIG_ESPTOOLPY_AFTER_NORESET=y CONFIG_SPIRAM_MEMTEST= CONFIG_LWIP_LOCAL_HOSTNAME="UMRGBTouchMini" - -CONFIG_TINYUSB_DESC_CUSTOM_VID=0x303A -CONFIG_TINYUSB_DESC_CUSTOM_PID=0x81FF -CONFIG_TINYUSB_DESC_BCD_DEVICE=0x0100 -CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID=n -CONFIG_TINYUSB_DESC_USE_DEFAULT_PID=n -CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Unexpected Maker" -CONFIG_TINYUSB_DESC_PRODUCT_STRING="RGBTouchMini" -CONFIG_TINYUSB_DESC_SERIAL_STRING="_rgbtouch_mini_" diff --git a/ports/esp32/boards/UM_TINYS2/mpconfigboard.cmake b/ports/esp32/boards/UM_TINYS2/mpconfigboard.cmake index 70cb4a814..4b0c6bfcc 100644 --- a/ports/esp32/boards/UM_TINYS2/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_TINYS2/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s2) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base boards/sdkconfig.spiram_sx - boards/sdkconfig.usb boards/UM_TINYS2/sdkconfig.board ) diff --git a/ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake b/ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake index 6c7f34009..3935a66f8 100644 --- a/ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_TINYS3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz boards/sdkconfig.spiram_sx diff --git a/ports/esp32/boards/UM_TINYS3/mpconfigboard.h b/ports/esp32/boards/UM_TINYS3/mpconfigboard.h index 74c7622cb..2cba3f1b0 100644 --- a/ports/esp32/boards/UM_TINYS3/mpconfigboard.h +++ b/ports/esp32/boards/UM_TINYS3/mpconfigboard.h @@ -8,3 +8,8 @@ #define MICROPY_HW_SPI1_MOSI (35) #define MICROPY_HW_SPI1_MISO (37) #define MICROPY_HW_SPI1_SCK (36) + +#define MICROPY_HW_USB_VID 0x303A +#define MICROPY_HW_USB_PID 0x80D1 +#define MICROPY_HW_USB_MANUFACTURER_STRING "Unexpected Maker" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "TinyS3" diff --git a/ports/esp32/boards/UM_TINYS3/sdkconfig.board b/ports/esp32/boards/UM_TINYS3/sdkconfig.board index 2474c5fb2..b9cb80695 100644 --- a/ports/esp32/boards/UM_TINYS3/sdkconfig.board +++ b/ports/esp32/boards/UM_TINYS3/sdkconfig.board @@ -4,12 +4,3 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_SPIRAM_MEMTEST= CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyS3" - -CONFIG_TINYUSB_DESC_CUSTOM_VID=0x303A -CONFIG_TINYUSB_DESC_CUSTOM_PID=0x80D1 -CONFIG_TINYUSB_DESC_BCD_DEVICE=0x0100 -CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID=n -CONFIG_TINYUSB_DESC_USE_DEFAULT_PID=n -CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Unexpected Maker" -CONFIG_TINYUSB_DESC_PRODUCT_STRING="TinyS3" -CONFIG_TINYUSB_DESC_SERIAL_STRING="_ts3_" diff --git a/ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.cmake b/ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.cmake index 089149c44..2978dcd6f 100644 --- a/ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.cmake +++ b/ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.cmake @@ -2,7 +2,6 @@ set(IDF_TARGET esp32s3) set(SDKCONFIG_DEFAULTS boards/sdkconfig.base - boards/sdkconfig.usb boards/sdkconfig.ble boards/sdkconfig.240mhz boards/sdkconfig.spiram_sx diff --git a/ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.h b/ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.h index 6cc134541..c8fc554a1 100644 --- a/ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.h +++ b/ports/esp32/boards/UM_TINYWATCHS3/mpconfigboard.h @@ -8,3 +8,8 @@ #define MICROPY_HW_SPI1_MOSI (35) #define MICROPY_HW_SPI1_MISO (37) #define MICROPY_HW_SPI1_SCK (36) + +#define MICROPY_HW_USB_VID 0x303A +#define MICROPY_HW_USB_PID 0x81B1 +#define MICROPY_HW_USB_MANUFACTURER_STRING "Unexpected Maker" +#define MICROPY_HW_USB_PRODUCT_FS_STRING "TinyWATCHS3" diff --git a/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board b/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board index 10121d235..da804fe30 100644 --- a/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board +++ b/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board @@ -4,12 +4,3 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_SPIRAM_MEMTEST= CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyWATCHS3" - -CONFIG_TINYUSB_DESC_CUSTOM_VID=0x303A -CONFIG_TINYUSB_DESC_CUSTOM_PID=0x81B1 -CONFIG_TINYUSB_DESC_BCD_DEVICE=0x0100 -CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID=n -CONFIG_TINYUSB_DESC_USE_DEFAULT_PID=n -CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Unexpected Maker" -CONFIG_TINYUSB_DESC_PRODUCT_STRING="TinyWATCHS3" -CONFIG_TINYUSB_DESC_SERIAL_STRING="_tws3_" diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index e1b30b8e5..6c8368ac4 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -124,7 +124,6 @@ CONFIG_UART_ISR_IN_IRAM=y # IDF 5 deprecated CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN=y -CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN=y CONFIG_ETH_USE_SPI_ETHERNET=y diff --git a/ports/esp32/boards/sdkconfig.usb b/ports/esp32/boards/sdkconfig.usb deleted file mode 100644 index 4090c710e..000000000 --- a/ports/esp32/boards/sdkconfig.usb +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG_USB_OTG_SUPPORTED=y -CONFIG_TINYUSB_CDC_ENABLED=y -CONFIG_TINYUSB_CDC_RX_BUFSIZE=256 -CONFIG_TINYUSB_CDC_TX_BUFSIZE=256 diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 79a60adac..807d71283 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -97,6 +97,14 @@ if(MICROPY_PY_TINYUSB) list(APPEND MICROPY_INC_TINYUSB ${MICROPY_DIR}/shared/tinyusb/ ) + + # Build the Espressif tinyusb component with MicroPython shared/tinyusb/tusb_config.h + idf_component_get_property(tusb_lib espressif__tinyusb COMPONENT_LIB) + target_include_directories(${tusb_lib} PRIVATE + ${MICROPY_DIR}/shared/tinyusb + ${MICROPY_DIR} + ${MICROPY_PORT_DIR} + ${MICROPY_BOARD_DIR}) endif() list(APPEND MICROPY_SOURCE_PORT @@ -262,6 +270,14 @@ target_compile_options(${MICROPY_TARGET} PUBLIC target_include_directories(${MICROPY_TARGET} PUBLIC ${IDF_PATH}/components/bt/host/nimble/nimble ) +if (IDF_VERSION VERSION_LESS "5.3") +# Additional include directories needed for private RMT header. +# IDF 5.x versions before 5.3.1 + message(STATUS "Using private rmt headers for ${IDF_VERSION}") + target_include_directories(${MICROPY_TARGET} PRIVATE + ${IDF_PATH}/components/driver/rmt + ) +endif() # Add additional extmod and usermod components. if (MICROPY_PY_BTREE) diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index f3bfbecdd..85a98c291 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2019 "Matt Trentini" <matt.trentini@gmail.com> + * Copyright (c) 2024 "Elvis Pfützenreuter" <elvis.pfutzenreuter@gmail.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,13 +27,16 @@ #include "py/mphal.h" #include "py/runtime.h" +#include "py/stream.h" #include "modmachine.h" #include "modesp32.h" #include "esp_task.h" #if SOC_RMT_SUPPORTED -#include "driver/rmt.h" +#include "esp_clk_tree.h" +#include "driver/rmt_tx.h" +#include "driver/rmt_encoder.h" // This exposes the ESP32's RMT module to MicroPython. RMT is provided by the Espressif ESP-IDF: // @@ -46,105 +50,101 @@ // Originally designed to generate infrared remote control signals, the module is very // flexible and quite easy-to-use. // -// This current MicroPython implementation lacks some major features, notably receive pulses -// and carrier output. - -// Last available RMT channel that can transmit. -#define RMT_LAST_TX_CHANNEL (SOC_RMT_TX_CANDIDATES_PER_GROUP - 1) +// This code exposes the RMT TX feature. // Forward declaration extern const mp_obj_type_t esp32_rmt_type; typedef struct _esp32_rmt_obj_t { mp_obj_base_t base; - uint8_t channel_id; + rmt_channel_handle_t channel; + bool enabled; gpio_num_t pin; - uint8_t clock_div; - mp_uint_t num_items; - rmt_item32_t *items; - bool loop_en; + uint32_t clock_freq; + int resolution_hz; + mp_uint_t cap_items; + rmt_symbol_word_t *items; + int loop_count; + int tx_ongoing; + + rmt_encoder_handle_t encoder; + mp_uint_t idle_level; } esp32_rmt_obj_t; -// Current channel used for machine.bitstream, in the machine_bitstream_high_low_rmt -// implementation. A value of -1 means do not use RMT. -int8_t esp32_rmt_bitstream_channel_id = RMT_LAST_TX_CHANNEL; - -#if MP_TASK_COREID == 0 - -typedef struct _rmt_install_state_t { - SemaphoreHandle_t handle; - uint8_t channel_id; - esp_err_t ret; -} rmt_install_state_t; - -static void rmt_install_task(void *pvParameter) { - rmt_install_state_t *state = pvParameter; - state->ret = rmt_driver_install(state->channel_id, 0, 0); - xSemaphoreGive(state->handle); - vTaskDelete(NULL); - for (;;) { - } -} - -// Call rmt_driver_install on core 1. This ensures that the RMT interrupt handler is -// serviced on core 1, so that WiFi (if active) does not interrupt it and cause glitches. -esp_err_t rmt_driver_install_core1(uint8_t channel_id) { - TaskHandle_t th; - rmt_install_state_t state; - state.handle = xSemaphoreCreateBinary(); - state.channel_id = channel_id; - xTaskCreatePinnedToCore(rmt_install_task, "rmt_install_task", 2048 / sizeof(StackType_t), &state, ESP_TASK_PRIO_MIN + 1, &th, 1); - xSemaphoreTake(state.handle, portMAX_DELAY); - vSemaphoreDelete(state.handle); - return state.ret; -} - -#else +// Decide RMT usage in the machine_bitstream_high_low_rmt implementation. +bool esp32_rmt_bitstream_enabled = true; -// MicroPython runs on core 1, so we can call the RMT installer directly and its -// interrupt handler will also run on core 1. -esp_err_t rmt_driver_install_core1(uint8_t channel_id) { - return rmt_driver_install(channel_id, 0, 0); +static bool IRAM_ATTR esp32_rmt_tx_trans_done(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *edata, void *user_ctx) { + esp32_rmt_obj_t *self = user_ctx; + self->tx_ongoing -= 1; + return false; } -#endif // MP_TASK_COREID==0 - static mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_id, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_clock_div, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, // 100ns resolution + { MP_QSTR_resolution_hz, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_clock_div, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_idle_level, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, // low voltage { MP_QSTR_tx_carrier, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // no carrier + { MP_QSTR_num_symbols, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = SOC_RMT_MEM_WORDS_PER_CHANNEL} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_uint_t channel_id = args[0].u_int; + // RMT channel is an opaque struct in current RMT API and channel_id is a dummy parameter + // mp_uint_t channel_id = args[0].u_int; gpio_num_t pin_id = machine_pin_get_id(args[1].u_obj); - mp_uint_t clock_div = args[2].u_int; - mp_uint_t idle_level = args[3].u_bool; - mp_obj_t tx_carrier_obj = args[4].u_obj; - if (esp32_rmt_bitstream_channel_id >= 0 && channel_id == esp32_rmt_bitstream_channel_id) { - mp_raise_ValueError(MP_ERROR_TEXT("channel used by bitstream")); + uint32_t clock_freq; + check_esp_err(esp_clk_tree_src_get_freq_hz(RMT_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clock_freq)); + + mp_uint_t resolution_hz; + if (args[2].u_obj != mp_const_none && args[3].u_obj != mp_const_none) { + mp_raise_ValueError(MP_ERROR_TEXT("resolution_hz and clock_div are mutually exclusive")); + } else if (args[2].u_obj == mp_const_none && args[3].u_obj == mp_const_none) { + // default value + resolution_hz = 10000000; + } else if (args[2].u_obj != mp_const_none) { + resolution_hz = mp_obj_get_int(args[2].u_obj); + if (resolution_hz <= 0) { + mp_raise_ValueError(MP_ERROR_TEXT("resolution_hz must be positive")); + } + } else if (args[3].u_obj != mp_const_none) { + mp_uint_t clock_div = mp_obj_get_int(args[3].u_obj); + if (clock_div < 1 || clock_div > 255) { + mp_raise_ValueError(MP_ERROR_TEXT("clock_div must be between 1 and 255")); + } + resolution_hz = clock_freq / clock_div; } - if (clock_div < 1 || clock_div > 255) { - mp_raise_ValueError(MP_ERROR_TEXT("clock_div must be between 1 and 255")); + mp_uint_t idle_level = args[4].u_bool; + mp_obj_t tx_carrier_obj = args[5].u_obj; + mp_uint_t num_symbols = args[6].u_int; + + if (num_symbols < SOC_RMT_MEM_WORDS_PER_CHANNEL || ((num_symbols % 2) == 1)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("num_symbols must be even and at least %d"), SOC_RMT_MEM_WORDS_PER_CHANNEL); } esp32_rmt_obj_t *self = mp_obj_malloc_with_finaliser(esp32_rmt_obj_t, &esp32_rmt_type); - self->channel_id = channel_id; + self->channel = NULL; self->pin = pin_id; - self->clock_div = clock_div; - self->loop_en = false; + self->clock_freq = clock_freq; + self->resolution_hz = resolution_hz; + self->loop_count = 0; + self->tx_ongoing = 0; + self->idle_level = idle_level; + self->enabled = false; + + rmt_tx_channel_config_t tx_chan_config = { + .clk_src = RMT_CLK_SRC_DEFAULT, + .gpio_num = self->pin, + .mem_block_symbols = num_symbols, + .resolution_hz = resolution_hz, + .trans_queue_depth = 4, + }; - rmt_config_t config = {0}; - config.rmt_mode = RMT_MODE_TX; - config.channel = (rmt_channel_t)self->channel_id; - config.gpio_num = self->pin; - config.mem_block_num = 1; - config.tx_config.loop_en = 0; + check_esp_err(rmt_new_tx_channel(&tx_chan_config, &self->channel)); if (tx_carrier_obj != mp_const_none) { mp_obj_t *tx_carrier_details = NULL; @@ -160,21 +160,21 @@ static mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz mp_raise_ValueError(MP_ERROR_TEXT("tx_carrier duty must be 0..100")); } - config.tx_config.carrier_en = 1; - config.tx_config.carrier_freq_hz = frequency; - config.tx_config.carrier_duty_percent = duty; - config.tx_config.carrier_level = level; - } else { - config.tx_config.carrier_en = 0; + rmt_carrier_config_t tx_carrier_cfg = { + .duty_cycle = ((float)duty) / 100.0, + .frequency_hz = frequency, + .flags.polarity_active_low = !level, + }; + check_esp_err(rmt_apply_carrier(self->channel, &tx_carrier_cfg)); } - config.tx_config.idle_output_en = 1; - config.tx_config.idle_level = idle_level; - - config.clk_div = self->clock_div; + rmt_copy_encoder_config_t copy_encoder_config = {}; + check_esp_err(rmt_new_copy_encoder(©_encoder_config, &self->encoder)); - check_esp_err(rmt_config(&config)); - check_esp_err(rmt_driver_install_core1(config.channel)); + rmt_tx_event_callbacks_t callbacks = { + .on_trans_done = esp32_rmt_tx_trans_done, + }; + check_esp_err(rmt_tx_register_event_callbacks(self->channel, &callbacks, self)); return MP_OBJ_FROM_PTR(self); } @@ -182,33 +182,72 @@ static mp_obj_t esp32_rmt_make_new(const mp_obj_type_t *type, size_t n_args, siz static void esp32_rmt_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->pin != -1) { - bool idle_output_en; - rmt_idle_level_t idle_level; - check_esp_err(rmt_get_idle_level(self->channel_id, &idle_output_en, &idle_level)); - mp_printf(print, "RMT(channel=%u, pin=%u, source_freq=%u, clock_div=%u, idle_level=%u)", - self->channel_id, self->pin, APB_CLK_FREQ, self->clock_div, idle_level); + mp_printf(print, "RMT(pin=%u, source_freq=%u, resolution_hz=%u, idle_level=%u)", + self->pin, self->clock_freq, self->resolution_hz, self->idle_level); } else { mp_printf(print, "RMT()"); } } +static void esp32_rmt_deactivate(esp32_rmt_obj_t *self) { + if (self->enabled) { + // FIXME: panics in ESP32 if called while TX is ongoing and TX sequence is long (>300ms) + // Does not panic in ESP32-S3, ESP32-C3 and ESP32-C6. + // Tested with ESP-IDF up to 5.5 + // ESP-IDF issue: https://github.com/espressif/esp-idf/issues/17692 + // + // Cause is Interrupt WDT to trigger because ESP-IDF rmt_disable() disables + // interrupts and spinlocks until the ongoing TX sequence is finished. + // + // Workaround is never try to stop RMT sequences longer than 300ms (which are unusual + // anyway). Or apply the patch mentioned at the GitHub issue to ESP-IDF. + rmt_disable(self->channel); + self->enabled = false; + } +} + +static mp_obj_t esp32_rmt_active(size_t n_args, const mp_obj_t *args) { + esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (n_args == 1) { + return mp_obj_new_bool(self->enabled && self->tx_ongoing > 0); + } else if (mp_obj_is_true(args[1])) { + mp_raise_ValueError(MP_ERROR_TEXT("activate by calling write_pulses()")); + } + + esp32_rmt_deactivate(self); + + return mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_rmt_active_obj, 1, 2, esp32_rmt_active); + static mp_obj_t esp32_rmt_deinit(mp_obj_t self_in) { - // fixme: check for valid channel. Return exception if error occurs. esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->pin != -1) { // Check if channel has already been deinitialised. - rmt_driver_uninstall(self->channel_id); + esp32_rmt_deactivate(self); + rmt_tx_event_callbacks_t callbacks = { + .on_trans_done = NULL, + }; + rmt_tx_register_event_callbacks(self->channel, &callbacks, self); + rmt_del_encoder(self->encoder); + rmt_del_channel(self->channel); self->pin = -1; // -1 to indicate RMT is unused + self->tx_ongoing = 0; m_free(self->items); } + return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(esp32_rmt_deinit_obj, esp32_rmt_deinit); // Return the source frequency. -// Currently only the APB clock (80MHz) can be used but it is possible other +// Currently only the default clock (80MHz) can be used but it is possible other // clock sources will added in the future. static mp_obj_t esp32_rmt_source_freq() { - return mp_obj_new_int(APB_CLK_FREQ); + uint32_t clock_freq; + check_esp_err(esp_clk_tree_src_get_freq_hz(RMT_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clock_freq)); + return mp_obj_new_int(clock_freq); } static MP_DEFINE_CONST_FUN_OBJ_0(esp32_rmt_source_freq_obj, esp32_rmt_source_freq); static MP_DEFINE_CONST_STATICMETHOD_OBJ(esp32_rmt_source_obj, MP_ROM_PTR(&esp32_rmt_source_freq_obj)); @@ -216,7 +255,11 @@ static MP_DEFINE_CONST_STATICMETHOD_OBJ(esp32_rmt_source_obj, MP_ROM_PTR(&esp32_ // Return the clock divider. static mp_obj_t esp32_rmt_clock_div(mp_obj_t self_in) { esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_int(self->clock_div); + if (self->pin == -1) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("already deinitialized")); + } + + return mp_obj_new_int(self->clock_freq / self->resolution_hz); } static MP_DEFINE_CONST_FUN_OBJ_1(esp32_rmt_clock_div_obj, esp32_rmt_clock_div); @@ -233,29 +276,86 @@ static mp_obj_t esp32_rmt_wait_done(size_t n_args, const mp_obj_t *pos_args, mp_ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj); + if (self->pin == -1) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("already deinitialized")); + } else if (!self->enabled) { + return mp_const_true; + } else if (args[1].u_int == 0 && self->tx_ongoing > 0) { + // shortcut to avoid console spamming with timeout msgs by rmt_tx_wait_all_done() + return mp_const_false; + } - esp_err_t err = rmt_wait_tx_done(self->channel_id, args[1].u_int / portTICK_PERIOD_MS); + esp_err_t err = rmt_tx_wait_all_done(self->channel, args[1].u_int); return err == ESP_OK ? mp_const_true : mp_const_false; } static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_rmt_wait_done_obj, 1, esp32_rmt_wait_done); +static mp_uint_t esp32_rmt_stream_ioctl( + mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + if (request != MP_STREAM_POLL) { + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; + } + esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret = 0; + if ((arg & MP_STREAM_POLL_WR) && self->tx_ongoing == 0) { + ret |= MP_STREAM_POLL_WR; + } + return ret; +} + +static const mp_stream_p_t esp32_rmt_stream_p = { + .ioctl = esp32_rmt_stream_ioctl, +}; + +static void esp32_rmt_loop_in(esp32_rmt_obj_t *self, int new_loop_count) { + if (self->enabled && self->tx_ongoing > 0 && self->loop_count != 0 && new_loop_count == 0) { + // Break ongoing loop + esp32_rmt_deactivate(self); + } + self->loop_count = new_loop_count; +} + static mp_obj_t esp32_rmt_loop(mp_obj_t self_in, mp_obj_t loop) { esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(self_in); - self->loop_en = mp_obj_get_int(loop); - if (!self->loop_en) { - bool loop_en; - check_esp_err(rmt_get_tx_loop_mode(self->channel_id, &loop_en)); - if (loop_en) { - check_esp_err(rmt_set_tx_loop_mode(self->channel_id, false)); - check_esp_err(rmt_set_tx_intr_en(self->channel_id, true)); - } + if (self->pin == -1) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("already deinitialized")); } + + bool loop_en = mp_obj_get_int(loop); + esp32_rmt_loop_in(self, loop_en ? -1 : 0); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_2(esp32_rmt_loop_obj, esp32_rmt_loop); +static mp_obj_t esp32_rmt_loop_count(mp_obj_t self_in, mp_obj_t loop) { + esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->pin == -1) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("already deinitialized")); + } + + int loop_count = mp_obj_get_int(loop); + if (loop_count < -1) { + mp_raise_ValueError(MP_ERROR_TEXT("arg must be -1, 0 or positive")); + } + esp32_rmt_loop_in(self, loop_count); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(esp32_rmt_loop_count_obj, esp32_rmt_loop_count); + static mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *args) { esp32_rmt_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (self->pin == -1) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("already deinitialized")); + } + + if (self->enabled) { + rmt_tx_wait_all_done(self->channel, -1); + } else { + check_esp_err(rmt_enable(self->channel)); + self->enabled = true; + } + mp_obj_t duration_obj = args[1]; mp_obj_t data_obj = n_args > 2 ? args[2] : mp_const_true; @@ -290,14 +390,12 @@ static mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *args) { if (num_pulses == 0) { mp_raise_ValueError(MP_ERROR_TEXT("No pulses")); } - if (self->loop_en && num_pulses > 126) { - mp_raise_ValueError(MP_ERROR_TEXT("Too many pulses for loop")); - } mp_uint_t num_items = (num_pulses / 2) + (num_pulses % 2); - if (num_items > self->num_items) { - self->items = (rmt_item32_t *)m_realloc(self->items, num_items * sizeof(rmt_item32_t *)); - self->num_items = num_items; + + if (num_items > self->cap_items) { + self->items = (rmt_symbol_word_t *)m_realloc(self->items, num_items * sizeof(rmt_symbol_word_t *)); + self->cap_items = num_items; } for (mp_uint_t item_index = 0, pulse_index = 0; item_index < num_items; item_index++) { @@ -314,63 +412,62 @@ static mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *args) { } } - if (self->loop_en) { - bool loop_en; - check_esp_err(rmt_get_tx_loop_mode(self->channel_id, &loop_en)); - if (loop_en) { - check_esp_err(rmt_set_tx_intr_en(self->channel_id, true)); - check_esp_err(rmt_set_tx_loop_mode(self->channel_id, false)); - } - check_esp_err(rmt_wait_tx_done(self->channel_id, portMAX_DELAY)); - } - - #if !CONFIG_IDF_TARGET_ESP32S3 - check_esp_err(rmt_write_items(self->channel_id, self->items, num_items, false)); - #endif - - if (self->loop_en) { - check_esp_err(rmt_set_tx_intr_en(self->channel_id, false)); - check_esp_err(rmt_set_tx_loop_mode(self->channel_id, true)); - } + rmt_transmit_config_t tx_config = { + .loop_count = self->loop_count, + .flags.eot_level = self->idle_level ? 1 : 0, + }; - #if CONFIG_IDF_TARGET_ESP32S3 - check_esp_err(rmt_write_items(self->channel_id, self->items, num_items, false)); - #endif + rmt_encoder_reset(self->encoder); + check_esp_err(rmt_transmit(self->channel, self->encoder, self->items, num_items * sizeof(rmt_symbol_word_t), &tx_config)); + self->tx_ongoing += 1; return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_rmt_write_pulses_obj, 2, 3, esp32_rmt_write_pulses); +static mp_obj_t esp32_rmt_bitstream_rmt(size_t n_args, const mp_obj_t *args) { + if (n_args > 0) { + esp32_rmt_bitstream_enabled = mp_obj_is_true(args[0]); + } + return esp32_rmt_bitstream_enabled ? mp_const_true : mp_const_false; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_rmt_bitstream_rmt_fun_obj, 0, 1, esp32_rmt_bitstream_rmt); +static MP_DEFINE_CONST_STATICMETHOD_OBJ(esp32_rmt_bitstream_rmt_obj, MP_ROM_PTR(&esp32_rmt_bitstream_rmt_fun_obj)); + static mp_obj_t esp32_rmt_bitstream_channel(size_t n_args, const mp_obj_t *args) { if (n_args > 0) { if (args[0] == mp_const_none) { - esp32_rmt_bitstream_channel_id = -1; + esp32_rmt_bitstream_enabled = false; } else { mp_int_t channel_id = mp_obj_get_int(args[0]); - if (channel_id < 0 || channel_id > RMT_LAST_TX_CHANNEL) { + if (channel_id < 0) { mp_raise_ValueError(MP_ERROR_TEXT("invalid channel")); } - esp32_rmt_bitstream_channel_id = channel_id; + esp32_rmt_bitstream_enabled = true; } } - if (esp32_rmt_bitstream_channel_id < 0) { + if (!esp32_rmt_bitstream_enabled) { return mp_const_none; } else { - return MP_OBJ_NEW_SMALL_INT(esp32_rmt_bitstream_channel_id); + return MP_OBJ_NEW_SMALL_INT(1); } } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_rmt_bitstream_channel_fun_obj, 0, 1, esp32_rmt_bitstream_channel); static MP_DEFINE_CONST_STATICMETHOD_OBJ(esp32_rmt_bitstream_channel_obj, MP_ROM_PTR(&esp32_rmt_bitstream_channel_fun_obj)); + static const mp_rom_map_elem_t esp32_rmt_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&esp32_rmt_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&esp32_rmt_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&esp32_rmt_active_obj) }, { MP_ROM_QSTR(MP_QSTR_clock_div), MP_ROM_PTR(&esp32_rmt_clock_div_obj) }, { MP_ROM_QSTR(MP_QSTR_wait_done), MP_ROM_PTR(&esp32_rmt_wait_done_obj) }, { MP_ROM_QSTR(MP_QSTR_loop), MP_ROM_PTR(&esp32_rmt_loop_obj) }, + { MP_ROM_QSTR(MP_QSTR_loop_count), MP_ROM_PTR(&esp32_rmt_loop_count_obj) }, { MP_ROM_QSTR(MP_QSTR_write_pulses), MP_ROM_PTR(&esp32_rmt_write_pulses_obj) }, // Static methods + { MP_ROM_QSTR(MP_QSTR_bitstream_rmt), MP_ROM_PTR(&esp32_rmt_bitstream_rmt_obj) }, { MP_ROM_QSTR(MP_QSTR_bitstream_channel), MP_ROM_PTR(&esp32_rmt_bitstream_channel_obj) }, // Class methods @@ -387,7 +484,8 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_TYPE_FLAG_NONE, make_new, esp32_rmt_make_new, print, esp32_rmt_print, - locals_dict, &esp32_rmt_locals_dict + locals_dict, &esp32_rmt_locals_dict, + protocol, &esp32_rmt_stream_p ); #endif // SOC_RMT_SUPPORTED diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32 b/ports/esp32/lockfiles/dependencies.lock.esp32 index 71eccf032..4b0b2d972 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32 @@ -30,6 +30,6 @@ direct_dependencies: - espressif/lan867x - espressif/mdns - idf -manifest_hash: 3b18b5bbac91c9fe5098d3759a37c84ed0828546d8cbc92e26e4c1698e689c8a +manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 target: esp32 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32c2 b/ports/esp32/lockfiles/dependencies.lock.esp32c2 index 5f46e6156..5c5cea0c9 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32c2 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32c2 @@ -16,6 +16,6 @@ dependencies: direct_dependencies: - espressif/mdns - idf -manifest_hash: 3b18b5bbac91c9fe5098d3759a37c84ed0828546d8cbc92e26e4c1698e689c8a +manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 target: esp32c2 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32c3 b/ports/esp32/lockfiles/dependencies.lock.esp32c3 index 5fb11084e..4c4c869c2 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32c3 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32c3 @@ -16,6 +16,6 @@ dependencies: direct_dependencies: - espressif/mdns - idf -manifest_hash: 3b18b5bbac91c9fe5098d3759a37c84ed0828546d8cbc92e26e4c1698e689c8a +manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 target: esp32c3 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32c5 b/ports/esp32/lockfiles/dependencies.lock.esp32c5 new file mode 100644 index 000000000..6f24d0134 --- /dev/null +++ b/ports/esp32/lockfiles/dependencies.lock.esp32c5 @@ -0,0 +1,21 @@ +dependencies: + espressif/mdns: + component_hash: 46ee81d32fbf850462d8af1e83303389602f6a6a9eddd2a55104cb4c063858ed + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.0 + idf: + source: + type: idf + version: 5.5.1 +direct_dependencies: +- espressif/mdns +- idf +manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 +target: esp32c5 +version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32c6 b/ports/esp32/lockfiles/dependencies.lock.esp32c6 index 4debf5548..b7435e107 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32c6 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32c6 @@ -16,6 +16,6 @@ dependencies: direct_dependencies: - espressif/mdns - idf -manifest_hash: 3b18b5bbac91c9fe5098d3759a37c84ed0828546d8cbc92e26e4c1698e689c8a +manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 target: esp32c6 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32s2 b/ports/esp32/lockfiles/dependencies.lock.esp32s2 index bb1443947..a13d9fd40 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32s2 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32s2 @@ -1,18 +1,4 @@ dependencies: - espressif/esp_tinyusb: - component_hash: 96d232ced7afe1976119b62f7fbf1944a2a78b36228ff6f7b9318394ac1153cc - dependencies: - - name: idf - require: private - version: '>=5.0' - - name: espressif/tinyusb - registry_url: https://components.espressif.com - require: public - version: '>=0.14.2' - source: - registry_url: https://components.espressif.com/ - type: service - version: 1.7.6~1 espressif/mdns: component_hash: 46ee81d32fbf850462d8af1e83303389602f6a6a9eddd2a55104cb4c063858ed dependencies: @@ -24,27 +10,28 @@ dependencies: type: service version: 1.1.0 espressif/tinyusb: - component_hash: aa65639878f27a44d349044afd9c3fc134a92bd560874fdac1d836019b5c07ca + component_hash: ee1c962cff61eb975d508258d509974d58031cc27ff0d6c4117a67a613a49594 dependencies: - name: idf - require: private version: '>=5.0' source: - registry_url: https://components.espressif.com - type: service + git: https://github.com/micropython/tinyusb-espressif.git + path: . + type: git targets: - esp32s2 - esp32s3 - esp32p4 - version: 0.18.0~4 + - esp32h4 + version: e4c0ec3caab3d9c25374de7047653b9ced8f14ff idf: source: type: idf version: 5.5.1 direct_dependencies: -- espressif/esp_tinyusb - espressif/mdns +- espressif/tinyusb - idf -manifest_hash: 3b18b5bbac91c9fe5098d3759a37c84ed0828546d8cbc92e26e4c1698e689c8a +manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 target: esp32s2 version: 2.0.0 diff --git a/ports/esp32/lockfiles/dependencies.lock.esp32s3 b/ports/esp32/lockfiles/dependencies.lock.esp32s3 index 38c17f5d6..d5e7045b7 100644 --- a/ports/esp32/lockfiles/dependencies.lock.esp32s3 +++ b/ports/esp32/lockfiles/dependencies.lock.esp32s3 @@ -1,18 +1,4 @@ dependencies: - espressif/esp_tinyusb: - component_hash: 96d232ced7afe1976119b62f7fbf1944a2a78b36228ff6f7b9318394ac1153cc - dependencies: - - name: idf - require: private - version: '>=5.0' - - name: espressif/tinyusb - registry_url: https://components.espressif.com - require: public - version: '>=0.14.2' - source: - registry_url: https://components.espressif.com/ - type: service - version: 1.7.6~1 espressif/mdns: component_hash: 46ee81d32fbf850462d8af1e83303389602f6a6a9eddd2a55104cb4c063858ed dependencies: @@ -24,27 +10,28 @@ dependencies: type: service version: 1.1.0 espressif/tinyusb: - component_hash: aa65639878f27a44d349044afd9c3fc134a92bd560874fdac1d836019b5c07ca + component_hash: ee1c962cff61eb975d508258d509974d58031cc27ff0d6c4117a67a613a49594 dependencies: - name: idf - require: private version: '>=5.0' source: - registry_url: https://components.espressif.com - type: service + git: https://github.com/micropython/tinyusb-espressif.git + path: . + type: git targets: - esp32s2 - esp32s3 - esp32p4 - version: 0.18.0~4 + - esp32h4 + version: e4c0ec3caab3d9c25374de7047653b9ced8f14ff idf: source: type: idf version: 5.5.1 direct_dependencies: -- espressif/esp_tinyusb - espressif/mdns +- espressif/tinyusb - idf -manifest_hash: 3b18b5bbac91c9fe5098d3759a37c84ed0828546d8cbc92e26e4c1698e689c8a +manifest_hash: da32add5eb5e196ac97a99eb579025222ec572f5db4038873fbf9d3b9d6ed5a3 target: esp32s3 version: 2.0.0 diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index c5575d45e..432df3d3a 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -100,6 +100,13 @@ static const machine_adc_obj_t madc_obj[] = { {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_3, GPIO_NUM_3}, {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_4, GPIO_NUM_4}, {{&machine_adc_type}, ADCBLOCK2, ADC_CHANNEL_0, GPIO_NUM_5}, + #elif CONFIG_IDF_TARGET_ESP32C5 + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_0, GPIO_NUM_1}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_1, GPIO_NUM_2}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_2, GPIO_NUM_3}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_3, GPIO_NUM_4}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_4, GPIO_NUM_5}, + {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_5, GPIO_NUM_6}, #elif CONFIG_IDF_TARGET_ESP32C6 {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_0, GPIO_NUM_0}, {{&machine_adc_type}, ADCBLOCK1, ADC_CHANNEL_1, GPIO_NUM_1}, diff --git a/ports/esp32/machine_bitstream.c b/ports/esp32/machine_bitstream.c index ed7fcc407..60addcc15 100644 --- a/ports/esp32/machine_bitstream.c +++ b/ports/esp32/machine_bitstream.c @@ -91,96 +91,95 @@ static void IRAM_ATTR machine_bitstream_high_low_bitbang(mp_hal_pin_obj_t pin, u } #if SOC_RMT_SUPPORTED + /******************************************************************************/ // RMT implementation -#include "driver/rmt.h" - -// Logical 0 and 1 values (encoded as a rmt_item32_t). -// The duration fields will be set later. -static rmt_item32_t bitstream_high_low_0 = {{{ 0, 1, 0, 0 }}}; -static rmt_item32_t bitstream_high_low_1 = {{{ 0, 1, 0, 0 }}}; - -// See https://github.com/espressif/esp-idf/blob/master/examples/common_components/led_strip/led_strip_rmt_ws2812.c -// This is called automatically by the IDF during rmt_write_sample in order to -// convert the byte stream to rmt_item32_t's. -static void IRAM_ATTR bitstream_high_low_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size, size_t wanted_num, size_t *translated_size, size_t *item_num) { - if (src == NULL || dest == NULL) { - *translated_size = 0; - *item_num = 0; - return; - } - - size_t size = 0; - size_t num = 0; - uint8_t *psrc = (uint8_t *)src; - rmt_item32_t *pdest = dest; - while (size < src_size && num < wanted_num) { - for (int i = 0; i < 8; i++) { - // MSB first - if (*psrc & (1 << (7 - i))) { - pdest->val = bitstream_high_low_1.val; - } else { - pdest->val = bitstream_high_low_0.val; - } - num++; - pdest++; - } - size++; - psrc++; - } - - *translated_size = size; - *item_num = num; -} - -// Use the reserved RMT channel to stream high/low data on the specified pin. -static void machine_bitstream_high_low_rmt(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len, uint8_t channel_id) { - rmt_config_t config = RMT_DEFAULT_CONFIG_TX(pin, channel_id); +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) +#include "rmt_private.h" +#endif +#include "driver/rmt_tx.h" +#include "driver/rmt_encoder.h" +static bool machine_bitstream_high_low_rmt(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len) { // Use 40MHz clock (although 2MHz would probably be sufficient). - config.clk_div = 2; - - // Install the driver on this channel & pin. - check_esp_err(rmt_config(&config)); - check_esp_err(rmt_driver_install_core1(config.channel)); + uint32_t clock_div = 2; + rmt_channel_handle_t channel = NULL; + rmt_tx_channel_config_t tx_chan_config = { + .clk_src = RMT_CLK_SRC_DEFAULT, + .gpio_num = pin, + .mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL, + .resolution_hz = APB_CLK_FREQ / clock_div, + .trans_queue_depth = 1, + }; + if (rmt_new_tx_channel(&tx_chan_config, &channel) != ESP_OK) { + return false; + } + check_esp_err(rmt_enable(channel)); // Get the tick rate in kHz (this will likely be 40000). - uint32_t counter_clk_khz = 0; - check_esp_err(rmt_get_counter_clock(config.channel, &counter_clk_khz)); - + uint32_t counter_clk_khz = APB_CLK_FREQ / clock_div; counter_clk_khz /= 1000; // Convert nanoseconds to pulse duration. - bitstream_high_low_0.duration0 = (counter_clk_khz * timing_ns[0]) / 1e6; - bitstream_high_low_0.duration1 = (counter_clk_khz * timing_ns[1]) / 1e6; - bitstream_high_low_1.duration0 = (counter_clk_khz * timing_ns[2]) / 1e6; - bitstream_high_low_1.duration1 = (counter_clk_khz * timing_ns[3]) / 1e6; - - // Install the bits->highlow translator. - rmt_translator_init(config.channel, bitstream_high_low_rmt_adapter); - - // Stream the byte data using the translator. - check_esp_err(rmt_write_sample(config.channel, buf, len, true)); - - // Wait 50% longer than we expect (if every bit takes the maximum time). - uint32_t timeout_ms = (3 * len / 2) * (1 + (8 * MAX(timing_ns[0] + timing_ns[1], timing_ns[2] + timing_ns[3])) / 1000); - check_esp_err(rmt_wait_tx_done(config.channel, pdMS_TO_TICKS(timeout_ms))); - - // Uninstall the driver. - check_esp_err(rmt_driver_uninstall(config.channel)); + // Example: 500ns = 40000 * 500 / 1e6 = 20 ticks + // 20 ticks / 40MHz = 500e-9 + rmt_bytes_encoder_config_t bytes_encoder_config = { + .bit0 = { + .level0 = 1, + .duration0 = (counter_clk_khz * timing_ns[0]) / 1e6, + .level1 = 0, + .duration1 = (counter_clk_khz * timing_ns[1]) / 1e6, + }, + .bit1 = { + .level0 = 1, + .duration0 = (counter_clk_khz * timing_ns[2]) / 1e6, + .level1 = 0, + .duration1 = (counter_clk_khz * timing_ns[3]) / 1e6, + }, + .flags.msb_first = 1 + }; + + // Install the bits->highlow encoder. + rmt_encoder_handle_t encoder; + check_esp_err(rmt_new_bytes_encoder(&bytes_encoder_config, &encoder)); + + rmt_transmit_config_t tx_config = { + .loop_count = 0, + .flags.eot_level = 0, + }; + + // Stream the byte data using the encoder. + rmt_encoder_reset(encoder); + check_esp_err(rmt_transmit(channel, encoder, buf, len, &tx_config)); + + // Wait until completion. + rmt_tx_wait_all_done(channel, -1); + + // Disable and release channel. + check_esp_err(rmt_del_encoder(encoder)); + rmt_disable(channel); + #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) + channel->del(channel); + #else + rmt_del_channel(channel); + #endif // Cancel RMT output to GPIO pin. esp_rom_gpio_connect_out_signal(pin, SIG_GPIO_OUT_IDX, false, false); + + return true; } -#endif + +#endif // SOC_RMT_SUPPORTED + /******************************************************************************/ // Interface to machine.bitstream void machine_bitstream_high_low(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len) { #if SOC_RMT_SUPPORTED - if (esp32_rmt_bitstream_channel_id >= 0) { - machine_bitstream_high_low_rmt(pin, timing_ns, buf, len, esp32_rmt_bitstream_channel_id); + if (esp32_rmt_bitstream_enabled && machine_bitstream_high_low_rmt(pin, timing_ns, buf, len)) { + // Use of RMT was successful. return; } #endif diff --git a/ports/esp32/machine_i2c.c b/ports/esp32/machine_i2c.c index 74679d01d..9fb89660f 100644 --- a/ports/esp32/machine_i2c.c +++ b/ports/esp32/machine_i2c.c @@ -35,6 +35,7 @@ #include "driver/i2c_master.h" #else #include "driver/i2c.h" +#include "esp_clk_tree.h" #include "hal/i2c_ll.h" #endif @@ -224,6 +225,8 @@ int machine_hw_i2c_transfer(mp_obj_base_t *self_in, uint16_t addr, size_t n, mp_ #if SOC_I2C_SUPPORT_XTAL #if CONFIG_XTAL_FREQ > 0 #define I2C_SCLK_FREQ (CONFIG_XTAL_FREQ * 1000000) +#elif CONFIG_XTAL_FREQ == 0 && CONFIG_IDF_TARGET_ESP32C5 +// The crystal is auto-detected, so the I2C sclk frequency will be computed at runtime. #else #error "I2C uses XTAL but no configured freq" #endif // CONFIG_XTAL_FREQ @@ -257,7 +260,13 @@ static void machine_hw_i2c_init(machine_hw_i2c_obj_t *self, bool first_init) { .master.clk_speed = self->freq, }; i2c_param_config(self->port, &conf); - int timeout = i2c_ll_calculate_timeout_us_to_reg_val(I2C_SCLK_FREQ, self->timeout_us); + #if CONFIG_IDF_TARGET_ESP32C5 + uint32_t i2c_sclk_freq; + check_esp_err(esp_clk_tree_src_get_freq_hz(I2C_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &i2c_sclk_freq)); + #else + uint32_t i2c_sclk_freq = I2C_SCLK_FREQ; + #endif + int timeout = i2c_ll_calculate_timeout_us_to_reg_val(i2c_sclk_freq, self->timeout_us); i2c_set_timeout(self->port, (timeout > I2C_LL_MAX_TIMEOUT) ? I2C_LL_MAX_TIMEOUT : timeout); i2c_driver_install(self->port, I2C_MODE_MASTER, 0, 0, 0); } diff --git a/ports/esp32/machine_i2s.c b/ports/esp32/machine_i2s.c index ba6377a76..27b70f1c8 100644 --- a/ports/esp32/machine_i2s.c +++ b/ports/esp32/machine_i2s.c @@ -470,4 +470,4 @@ static void mp_machine_i2s_irq_update(machine_i2s_obj_t *self) { } } -MP_REGISTER_ROOT_POINTER(struct _machine_i2s_obj_t *machine_i2s_obj[I2S_NUM_AUTO]); +MP_REGISTER_ROOT_POINTER(struct _machine_i2s_obj_t *machine_i2s_obj[SOC_I2S_NUM]); diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index 9999223b5..efe673319 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -152,7 +152,7 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ // reset the pin to digital if this is a mode-setting init (grab it back from ADC) if (args[ARG_mode].u_obj != mp_const_none) { if (rtc_gpio_is_valid_gpio(index)) { - #if !(CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6) + #if !(CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6) rtc_gpio_deinit(index); #endif } @@ -163,6 +163,11 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE); } #endif + #if CONFIG_IDF_TARGET_ESP32C5 && !MICROPY_HW_ESP_USB_SERIAL_JTAG + if (index == 13 || index == 14) { + CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE); + } + #endif #if CONFIG_IDF_TARGET_ESP32C6 && !MICROPY_HW_ESP_USB_SERIAL_JTAG if (index == 12 || index == 13) { CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE); diff --git a/ports/esp32/machine_pin.h b/ports/esp32/machine_pin.h index 6fe9ef0e7..9e247a736 100644 --- a/ports/esp32/machine_pin.h +++ b/ports/esp32/machine_pin.h @@ -111,6 +111,32 @@ #define MICROPY_HW_ENABLE_GPIO20 (1) #define MICROPY_HW_ENABLE_GPIO21 (1) +#elif CONFIG_IDF_TARGET_ESP32C5 + +#define MICROPY_HW_ENABLE_GPIO0 (1) +#define MICROPY_HW_ENABLE_GPIO1 (1) +#define MICROPY_HW_ENABLE_GPIO2 (1) +#define MICROPY_HW_ENABLE_GPIO3 (1) +#define MICROPY_HW_ENABLE_GPIO4 (1) +#define MICROPY_HW_ENABLE_GPIO5 (1) +#define MICROPY_HW_ENABLE_GPIO6 (1) +#define MICROPY_HW_ENABLE_GPIO7 (1) +#define MICROPY_HW_ENABLE_GPIO8 (1) +#define MICROPY_HW_ENABLE_GPIO9 (1) +#define MICROPY_HW_ENABLE_GPIO10 (1) +#define MICROPY_HW_ENABLE_GPIO11 (1) +#define MICROPY_HW_ENABLE_GPIO12 (1) +#if !MICROPY_HW_ESP_USB_SERIAL_JTAG +#define MICROPY_HW_ENABLE_GPIO13 (1) +#define MICROPY_HW_ENABLE_GPIO14 (1) +#endif +#define MICROPY_HW_ENABLE_GPIO23 (1) +#define MICROPY_HW_ENABLE_GPIO24 (1) +#define MICROPY_HW_ENABLE_GPIO25 (1) +#define MICROPY_HW_ENABLE_GPIO26 (1) +#define MICROPY_HW_ENABLE_GPIO27 (1) +#define MICROPY_HW_ENABLE_GPIO28 (1) + #elif CONFIG_IDF_TARGET_ESP32C6 #define MICROPY_HW_ENABLE_GPIO0 (1) diff --git a/ports/esp32/main.c b/ports/esp32/main.c index bd5775bc6..41eea29b0 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -107,7 +107,7 @@ void mp_task(void *pvParameter) { #if MICROPY_HW_ESP_USB_SERIAL_JTAG usb_serial_jtag_init(); #elif MICROPY_HW_ENABLE_USBDEV - usb_init(); + usb_phy_init(); #endif #if MICROPY_HW_ENABLE_UART_REPL uart_stdout_init(); @@ -145,6 +145,11 @@ soft_reset: // run boot-up scripts pyexec_frozen_module("_boot.py", false); int ret = pyexec_file_if_exists("boot.py"); + + #if MICROPY_HW_ENABLE_USBDEV + mp_usbd_init(); + #endif + if (ret & PYEXEC_FORCED_EXIT) { goto soft_reset_exit; } @@ -182,7 +187,9 @@ soft_reset_exit: // Deinit uart before timers, as esp32 uart // depends on a timer instance + #if MICROPY_PY_MACHINE_UART machine_uart_deinit_all(); + #endif machine_timer_deinit_all(); #if MICROPY_PY_ESP32_PCNT @@ -193,7 +200,7 @@ soft_reset_exit: mp_thread_deinit(); #endif - #if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE + #if MICROPY_HW_ENABLE_USBDEV mp_usbd_deinit(); #endif @@ -205,7 +212,9 @@ soft_reset_exit: mp_hal_stdout_tx_str("MPY: soft reboot\r\n"); // deinitialise peripherals + #if MICROPY_PY_MACHINE_PWM machine_pwm_deinit_all(); + #endif // TODO: machine_rmt_deinit_all(); machine_pins_deinit(); #if MICROPY_PY_MACHINE_I2C_TARGET @@ -219,6 +228,7 @@ soft_reset_exit: mp_deinit(); fflush(stdout); + goto soft_reset; } diff --git a/ports/esp32/main/idf_component.yml b/ports/esp32/main/idf_component.yml index cb14ffde6..77904865b 100644 --- a/ports/esp32/main/idf_component.yml +++ b/ports/esp32/main/idf_component.yml @@ -1,10 +1,13 @@ ## IDF Component Manager Manifest File dependencies: espressif/mdns: "~1.1.0" - espressif/esp_tinyusb: + espressif/tinyusb: rules: - if: "target in [esp32s2, esp32s3]" - version: "~1.7.6" + # Temporary workaround for https://github.com/hathach/tinyusb/issues/3154 + # Can be removed once fix is released in espressif/tinyusb + git: https://github.com/micropython/tinyusb-espressif.git + version: cherrypick/dwc2_zlp_fix espressif/lan867x: version: "~1.0.0" rules: diff --git a/ports/esp32/modesp32.h b/ports/esp32/modesp32.h index 81ab94dc6..60c386565 100644 --- a/ports/esp32/modesp32.h +++ b/ports/esp32/modesp32.h @@ -1,6 +1,8 @@ #ifndef MICROPY_INCLUDED_ESP32_MODESP32_H #define MICROPY_INCLUDED_ESP32_MODESP32_H +#include "driver/rmt_tx.h" + #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 #define RTC_VALID_EXT_PINS \ @@ -59,7 +61,7 @@ #define RTC_IS_VALID_EXT_PIN(pin_id) ((1ll << (pin_id)) & RTC_VALID_EXT_PINS) -extern int8_t esp32_rmt_bitstream_channel_id; +extern bool esp32_rmt_bitstream_enabled; extern const mp_obj_type_t esp32_nvs_type; extern const mp_obj_type_t esp32_partition_type; @@ -72,6 +74,4 @@ extern const mp_obj_type_t esp32_pcnt_type; void esp32_pcnt_deinit_all(void); #endif -esp_err_t rmt_driver_install_core1(uint8_t channel_id); - #endif // MICROPY_INCLUDED_ESP32_MODESP32_H diff --git a/ports/esp32/modnetwork.h b/ports/esp32/modnetwork.h index ba69d5cc0..a68db41a3 100644 --- a/ports/esp32/modnetwork.h +++ b/ports/esp32/modnetwork.h @@ -26,6 +26,7 @@ #ifndef MICROPY_INCLUDED_ESP32_MODNETWORK_H #define MICROPY_INCLUDED_ESP32_MODNETWORK_H +#include "esp_wifi_types.h" #include "esp_netif.h" // lan867x component requires newer IDF version diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 09d75cd1a..7b973ebb9 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -9,8 +9,6 @@ #include "esp_random.h" #include "esp_system.h" #include "freertos/FreeRTOS.h" -#include "driver/i2s_std.h" -#include "esp_wifi_types.h" #ifndef MICROPY_CONFIG_ROM_LEVEL #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) @@ -172,6 +170,8 @@ #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-esp32c2" #elif CONFIG_IDF_TARGET_ESP32C3 #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-esp32c3" +#elif CONFIG_IDF_TARGET_ESP32C5 +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-esp32c5" #elif CONFIG_IDF_TARGET_ESP32C6 #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-esp32c6" #endif @@ -223,19 +223,14 @@ #ifndef MICROPY_HW_USB_VID #define USB_ESPRESSIF_VID 0x303A -#if CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID #define MICROPY_HW_USB_VID (USB_ESPRESSIF_VID) -#else -#define MICROPY_HW_USB_VID (CONFIG_TINYUSB_DESC_CUSTOM_VID) #endif #ifndef MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE #define MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE (1) // Support machine.USBDevice #endif -#endif #ifndef MICROPY_HW_USB_PID -#if CONFIG_TINYUSB_DESC_USE_DEFAULT_PID #define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) // A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. // Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. @@ -244,25 +239,16 @@ #define USB_TUSB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ _PID_MAP(MIDI, 3)) // | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) ) #define MICROPY_HW_USB_PID (USB_TUSB_PID) -#else -#define MICROPY_HW_USB_PID (CONFIG_TINYUSB_DESC_CUSTOM_PID) -#endif #endif +// These Manufacturer & Product strings are the defaults when using the +// esp_tinyusb component (which MicroPython used in the past). #ifndef MICROPY_HW_USB_MANUFACTURER_STRING -#ifdef CONFIG_TINYUSB_DESC_MANUFACTURER_STRING -#define MICROPY_HW_USB_MANUFACTURER_STRING CONFIG_TINYUSB_DESC_MANUFACTURER_STRING -#else -#define MICROPY_HW_USB_MANUFACTURER_STRING "MicroPython" -#endif +#define MICROPY_HW_USB_MANUFACTURER_STRING "Espressif Systems" #endif #ifndef MICROPY_HW_USB_PRODUCT_FS_STRING -#ifdef CONFIG_TINYUSB_DESC_PRODUCT_STRING -#define MICROPY_HW_USB_PRODUCT_FS_STRING CONFIG_TINYUSB_DESC_PRODUCT_STRING -#else -#define MICROPY_HW_USB_PRODUCT_FS_STRING "Board in FS mode" -#endif +#define MICROPY_HW_USB_PRODUCT_FS_STRING "Espressif Device" #endif #endif // MICROPY_HW_ENABLE_USBDEV diff --git a/ports/esp32/usb.c b/ports/esp32/usb.c index 750dd59ee..b90f53aa4 100644 --- a/ports/esp32/usb.c +++ b/ports/esp32/usb.c @@ -38,7 +38,7 @@ static usb_phy_handle_t phy_hdl; -void usb_init(void) { +void usb_phy_init(void) { // ref: https://github.com/espressif/esp-usb/blob/4b6a798d0bed444fff48147c8dcdbbd038e92892/device/esp_tinyusb/tinyusb.c // Configure USB PHY @@ -51,10 +51,6 @@ void usb_init(void) { // Init ESP USB Phy usb_new_phy(&phy_conf, &phy_hdl); - - // Init MicroPython / TinyUSB - mp_usbd_init(); - } #if CONFIG_IDF_TARGET_ESP32S3 diff --git a/ports/esp32/usb.h b/ports/esp32/usb.h index 2bfa3d31a..99437266d 100644 --- a/ports/esp32/usb.h +++ b/ports/esp32/usb.h @@ -28,7 +28,7 @@ #define MICROPY_HW_USB_CDC_TX_TIMEOUT_MS (500) -void usb_init(void); +void usb_phy_init(void); void usb_usj_mode(void); #endif // MICROPY_INCLUDED_ESP32_USB_H diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index 22809916e..dcb1ede16 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -170,8 +170,12 @@ int main(void) { #if MICROPY_PY_NETWORK mod_network_deinit(); #endif + #if MICROPY_PY_MACHINE_UART machine_uart_deinit_all(); + #endif + #if MICROPY_PY_MACHINE_PWM machine_pwm_deinit_all(); + #endif soft_timer_deinit(); gc_sweep_all(); mp_deinit(); diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 120d07bcc..c4a081e2e 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -644,12 +644,6 @@ separate_arguments(MICROPY_CPP_FLAGS_EXTRA) # Include the main MicroPython cmake rules. include(${MICROPY_DIR}/py/mkrules.cmake) -set(GEN_PINS_AF_CSV "${MICROPY_PORT_DIR}/boards/rp2_af.csv") -set(GEN_PINS_PREFIX "${MICROPY_PORT_DIR}/boards/rp2_prefix.c") -set(GEN_PINS_MKPINS "${MICROPY_PORT_DIR}/boards/make-pins.py") -set(GEN_PINS_SRC "${CMAKE_BINARY_DIR}/pins_${MICROPY_BOARD}.c") -set(GEN_PINS_HDR "${MICROPY_GENHDR_DIR}/pins.h") - if(NOT PICO_NUM_GPIOS) set(PICO_NUM_GPIOS 30) endif() @@ -658,6 +652,21 @@ if(NOT PICO_NUM_EXT_GPIOS) set(PICO_NUM_EXT_GPIOS 10) endif() +if(PICO_RP2040) + set(GEN_PINS_AF_CSV "${MICROPY_PORT_DIR}/boards/rp2040_af.csv") +elseif(PICO_RP2350) + if(PICO_NUM_GPIOS EQUAL 48) + set(GEN_PINS_AF_CSV "${MICROPY_PORT_DIR}/boards/rp2350b_af.csv") + else() + set(GEN_PINS_AF_CSV "${MICROPY_PORT_DIR}/boards/rp2350_af.csv") + endif() +endif() + +set(GEN_PINS_PREFIX "${MICROPY_PORT_DIR}/boards/rp2_prefix.c") +set(GEN_PINS_MKPINS "${MICROPY_PORT_DIR}/boards/make-pins.py") +set(GEN_PINS_SRC "${CMAKE_BINARY_DIR}/pins_${MICROPY_BOARD}.c") +set(GEN_PINS_HDR "${MICROPY_GENHDR_DIR}/pins.h") + if(NOT MICROPY_BOARD_PINS) set(MICROPY_BOARD_PINS "${MICROPY_BOARD_DIR}/pins.csv") endif() diff --git a/ports/rp2/boards/make-pins.py b/ports/rp2/boards/make-pins.py index 1da2edb7c..07ce052bb 100755 --- a/ports/rp2/boards/make-pins.py +++ b/ports/rp2/boards/make-pins.py @@ -60,6 +60,15 @@ class Rp2Pin(boardgen.Pin): m = re.match("([A-Z][A-Z0-9][A-Z]+)(([0-9]+)(_.*)?)?", af) af_fn = m.group(1) af_unit = int(m.group(3)) if m.group(3) is not None else 0 + if af_idx == 10: + # AF11 uses UART_AUX in lieu of UART + af_fn = "UART_AUX" + if af_fn.startswith("QMI"): + # The full func name is GPIO_FUNC_XIP_CS1 + af_fn = "XIP_CS1" + if af_fn.startswith("TRACE"): + # The various TRACE functions all belong under CORESIGHT_TRACE + af_fn = "CORESIGHT_TRACE" if af_fn == "PIO": # Pins can be either PIO unit (unlike, say, I2C where a # pin can only be I2C0 _or_ I2C1, both sharing the same AF diff --git a/ports/rp2/boards/rp2_af.csv b/ports/rp2/boards/rp2040_af.csv index 454f7ed2d..454f7ed2d 100644 --- a/ports/rp2/boards/rp2_af.csv +++ b/ports/rp2/boards/rp2040_af.csv diff --git a/ports/rp2/boards/rp2350_af.csv b/ports/rp2/boards/rp2350_af.csv new file mode 100644 index 000000000..cca30d9e7 --- /dev/null +++ b/ports/rp2/boards/rp2350_af.csv @@ -0,0 +1,31 @@ +Pin,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11 +GPIO0,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_OVCUR_DET, +GPIO1,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,PIO2,TRACECLK,USB_VBUS_DET, +GPIO2,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,PIO2,TRACEDATA0,USB_VBUS_EN,UART0_TX +GPIO3,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,PIO2,TRACEDATA1,USB_OVCUR_DET,UART0_RX +GPIO4,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,PIO2,TRACEDATA2,USB_VBUS_DET, +GPIO5,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,PIO2,TRACEDATA3,USB_VBUS_EN, +GPIO6,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_TX +GPIO7,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_RX +GPIO8,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_EN, +GPIO9,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, +GPIO10,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_TX +GPIO11,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_RX +GPIO12,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN0,USB_OVCUR_DET, +GPIO13,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT0,USB_VBUS_DET, +GPIO14,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM7_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN1,USB_VBUS_EN,UART0_TX +GPIO15,SPI1_TX,UART0_RTS,I2C1_SCL,PWM7_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT1,USB_OVCUR_DET,UART0_RX +GPIO16,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, +GPIO17,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +GPIO18,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART0_TX +GPIO19,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_DET,UART0_RX +GPIO20,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN0,USB_VBUS_EN, +GPIO21,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT0,USB_OVCUR_DET, +GPIO22,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN1,USB_VBUS_DET,UART1_TX +GPIO23,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT1,USB_VBUS_EN,UART1_RX +GPIO24,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT2,USB_OVCUR_DET, +GPIO25,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT3,USB_VBUS_DET, +GPIO26,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_TX +GPIO27,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_RX +GPIO28,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, +GPIO29,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, diff --git a/ports/rp2/boards/rp2350b_af.csv b/ports/rp2/boards/rp2350b_af.csv new file mode 100644 index 000000000..3b9053f58 --- /dev/null +++ b/ports/rp2/boards/rp2350b_af.csv @@ -0,0 +1,49 @@ +Pin,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11 +GPIO0,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_OVCUR_DET, +GPIO1,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,PIO2,TRACECLK,USB_VBUS_DET, +GPIO2,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,PIO2,TRACEDATA0,USB_VBUS_EN,UART0_TX +GPIO3,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,PIO2,TRACEDATA1,USB_OVCUR_DET,UART0_RX +GPIO4,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,PIO2,TRACEDATA2,USB_VBUS_DET, +GPIO5,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,PIO2,TRACEDATA3,USB_VBUS_EN, +GPIO6,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_TX +GPIO7,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_RX +GPIO8,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_EN, +GPIO9,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, +GPIO10,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_TX +GPIO11,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_RX +GPIO12,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN0,USB_OVCUR_DET, +GPIO13,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT0,USB_VBUS_DET, +GPIO14,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM7_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN1,USB_VBUS_EN,UART0_TX +GPIO15,SPI1_TX,UART0_RTS,I2C1_SCL,PWM7_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT1,USB_OVCUR_DET,UART0_RX +GPIO16,SPI0_RX,UART0_TX,I2C0_SDA,PWM0_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, +GPIO17,SPI0_CS,UART0_RX,I2C0_SCL,PWM0_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +GPIO18,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM1_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART0_TX +GPIO19,SPI0_TX,UART0_RTS,I2C1_SCL,PWM1_B,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_DET,UART0_RX +GPIO20,SPI0_RX,UART1_TX,I2C0_SDA,PWM2_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN0,USB_VBUS_EN, +GPIO21,SPI0_CS,UART1_RX,I2C0_SCL,PWM2_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT0,USB_OVCUR_DET, +GPIO22,SPI0_SCK,UART1_CTS,I2C1_SDA,PWM3_A,SIO,PIO0,PIO1,PIO2,GPCK_GPIN1,USB_VBUS_DET,UART1_TX +GPIO23,SPI0_TX,UART1_RTS,I2C1_SCL,PWM3_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT1,USB_VBUS_EN,UART1_RX +GPIO24,SPI1_RX,UART1_TX,I2C0_SDA,PWM4_A,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT2,USB_OVCUR_DET, +GPIO25,SPI1_CS,UART1_RX,I2C0_SCL,PWM4_B,SIO,PIO0,PIO1,PIO2,GPCK_GPOUT3,USB_VBUS_DET, +GPIO26,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM5_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_TX +GPIO27,SPI1_TX,UART1_RTS,I2C1_SCL,PWM5_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_RX +GPIO28,SPI1_RX,UART0_TX,I2C0_SDA,PWM6_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, +GPIO29,SPI1_CS,UART0_RX,I2C0_SCL,PWM6_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +GPIO30,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM7_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART0_TX +GPIO31,SPI1_TX,UART0_RTS,I2C1_SCL,PWM7_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART0_RX +GPIO32,SPI0_RX,UART0_TX,I2C0_SDA,PWM8_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +GPIO33,SPI0_CS,UART0_RX,I2C0_SCL,PWM8_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, +GPIO34,SPI0_SCK,UART0_CTS,I2C1_SDA,PWM9_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART0_TX +GPIO35,SPI0_TX,UART0_RTS,I2C1_SCL,PWM9_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART0_RX +GPIO36,SPI0_RX,UART1_TX,I2C0_SDA,PWM10_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, +GPIO37,SPI0_CS,UART1_RX,I2C0_SCL,PWM10_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, +GPIO38,SPI0_SCK,UART1_CTS,I2C1_SCL,PWM11_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN,UART1_TX +GPIO39,SPI0_TX,UART1_RTS,I2C1_SCL,PWM11_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_RX +GPIO40,SPI1_RX,UART1_TX,I2C0_SDA,PWM8_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET, +GPIO41,SPI1_CS,UART1_RX,I2C0_SCL,PWM8_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +GPIO42,SPI1_SCK,UART1_CTS,I2C1_SDA,PWM9_A,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET,UART1_TX +GPIO43,SPI1_TX,UART1_RTS,I2C1_SCL,PWM9_B,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART1_RX +GPIO44,SPI1_RX,UART0_TX,I2C0_SDA,PWM10_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_EN, +GPIO45,SPI1_CS,UART0_RX,I2C0_SCL,PWM10_B,SIO,PIO0,PIO1,PIO2,,USB_OVCUR_DET, +GPIO46,SPI1_SCK,UART0_CTS,I2C1_SDA,PWM11_A,SIO,PIO0,PIO1,PIO2,,USB_VBUS_DET,UART0_TX +GPIO47,SPI1_TX,UART0_RTS,I2C1_SCL,PWM11_B,SIO,PIO0,PIO1,PIO2,QMI_CS1,USB_VBUS_EN,UART0_RX diff --git a/ports/rp2/machine_pin.c b/ports/rp2/machine_pin.c index 7a2de0c0b..e1a184f32 100644 --- a/ports/rp2/machine_pin.c +++ b/ports/rp2/machine_pin.c @@ -515,8 +515,16 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_ALT_SIO), MP_ROM_INT(GPIO_FUNC_SIO) }, { MP_ROM_QSTR(MP_QSTR_ALT_PIO0), MP_ROM_INT(GPIO_FUNC_PIO0) }, { MP_ROM_QSTR(MP_QSTR_ALT_PIO1), MP_ROM_INT(GPIO_FUNC_PIO1) }, + #if NUM_PIOS >= 3 + { MP_ROM_QSTR(MP_QSTR_ALT_PIO2), MP_ROM_INT(GPIO_FUNC_PIO2) }, + #endif { MP_ROM_QSTR(MP_QSTR_ALT_GPCK), MP_ROM_INT(GPIO_FUNC_GPCK) }, { MP_ROM_QSTR(MP_QSTR_ALT_USB), MP_ROM_INT(GPIO_FUNC_USB) }, + #if PICO_RP2350 + { MP_ROM_QSTR(MP_QSTR_ALT_XIP_CS1), MP_ROM_INT(GPIO_FUNC_XIP_CS1) }, + { MP_ROM_QSTR(MP_QSTR_ALT_CORESIGHT_TRACE), MP_ROM_INT(GPIO_FUNC_CORESIGHT_TRACE) }, + { MP_ROM_QSTR(MP_QSTR_ALT_UART_AUX), MP_ROM_INT(GPIO_FUNC_UART_AUX) }, + #endif }; static MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table); diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 1ffcabdfa..f01522f24 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -249,15 +249,21 @@ int main(int argc, char **argv) { #if MICROPY_PY_NETWORK mod_network_deinit(); #endif + #if MICROPY_PY_MACHINE_I2S machine_i2s_deinit_all(); + #endif rp2_dma_deinit(); rp2_pio_deinit(); #if MICROPY_PY_BLUETOOTH mp_bluetooth_deinit(); #endif + #if MICROPY_PY_MACHINE_PWM machine_pwm_deinit_all(); + #endif machine_pin_deinit(); + #if MICROPY_PY_MACHINE_UART machine_uart_deinit_all(); + #endif #if MICROPY_PY_MACHINE_I2C_TARGET mp_machine_i2c_target_deinit_all(); #endif diff --git a/ports/rp2/pendsv.c b/ports/rp2/pendsv.c index 05b521fde..a3a855b8d 100644 --- a/ports/rp2/pendsv.c +++ b/ports/rp2/pendsv.c @@ -180,5 +180,7 @@ void PendSV_Handler(void) { #if MICROPY_PY_THREAD mp_thread_recursive_mutex_unlock(&pendsv_mutex); + // Check if a dispatch occurred while the interrupt was being serviced + pendsv_resume_run_dispatch(); #endif } diff --git a/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h index ccc3fa051..90ea1eae3 100644 --- a/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h @@ -74,6 +74,17 @@ #define MICROPY_HW_USB_HS_IN_FS (1) #define MICROPY_HW_USB_MAIN_DEV (USB_PHY_HS_ID) +// Ethernet via RMII +#define MICROPY_HW_ETH_MDC (pin_G11) +#define MICROPY_HW_ETH_MDIO (pin_F4) +#define MICROPY_HW_ETH_RMII_REF_CLK (pin_F7) +#define MICROPY_HW_ETH_RMII_CRS_DV (pin_F10) +#define MICROPY_HW_ETH_RMII_RXD0 (pin_F14) +#define MICROPY_HW_ETH_RMII_RXD1 (pin_F15) +#define MICROPY_HW_ETH_RMII_TX_EN (pin_F11) +#define MICROPY_HW_ETH_RMII_TXD0 (pin_F12) +#define MICROPY_HW_ETH_RMII_TXD1 (pin_F13) + /******************************************************************************/ // Bootloader configuration diff --git a/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk index fa64cb170..777f22e61 100644 --- a/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk @@ -24,3 +24,6 @@ endif # MicroPython settings MICROPY_FLOAT_IMPL = double +MICROPY_PY_LWIP = 1 +MICROPY_PY_SSL = 1 +MICROPY_SSL_MBEDTLS = 1 diff --git a/ports/stm32/boards/NUCLEO_N657X0/pins.csv b/ports/stm32/boards/NUCLEO_N657X0/pins.csv index 033f0a552..a8a3f6c5a 100644 --- a/ports/stm32/boards/NUCLEO_N657X0/pins.csv +++ b/ports/stm32/boards/NUCLEO_N657X0/pins.csv @@ -44,6 +44,17 @@ A5,PG15 -SPI5_MISO,PG1 -SPI5_MOSI,PG2 +# ETH1 RMII +,PG11 +,PF4 +,PF7 +,PF10 +,PF11 +,PF12 +,PF13 +,PF14 +,PF15 + -BUTTON,PC13 LED_BLUE,PG8 LED_RED,PG10 diff --git a/ports/stm32/boards/OPENMV_N6/mpconfigboard.h b/ports/stm32/boards/OPENMV_N6/mpconfigboard.h index 6e6bb4357..0d63ff224 100644 --- a/ports/stm32/boards/OPENMV_N6/mpconfigboard.h +++ b/ports/stm32/boards/OPENMV_N6/mpconfigboard.h @@ -126,6 +126,24 @@ #define MICROPY_HW_USB_PID_CDC_HID (MICROPY_HW_USB_PID) #define MICROPY_HW_USB_PID_CDC_MSC_HID (MICROPY_HW_USB_PID) +// Ethernet via RGMII +#define NETWORK_LAN_PHY (ETH_PHY_RTL8211) +#define MICROPY_HW_ETH_MDC (pin_D1) +#define MICROPY_HW_ETH_MDIO (pin_D12) +#define MICROPY_HW_ETH_RGMII_CLK125 (pin_F2) +#define MICROPY_HW_ETH_RGMII_GTX_CLK (pin_F0) +#define MICROPY_HW_ETH_RGMII_TXD0 (pin_F12) +#define MICROPY_HW_ETH_RGMII_TXD1 (pin_F13) +#define MICROPY_HW_ETH_RGMII_TXD2 (pin_G3) +#define MICROPY_HW_ETH_RGMII_TXD3 (pin_G4) +#define MICROPY_HW_ETH_RGMII_TX_CTL (pin_F11) +#define MICROPY_HW_ETH_RGMII_RX_CLK (pin_F7) +#define MICROPY_HW_ETH_RGMII_RXD0 (pin_F14) +#define MICROPY_HW_ETH_RGMII_RXD1 (pin_F15) +#define MICROPY_HW_ETH_RGMII_RXD2 (pin_F8) +#define MICROPY_HW_ETH_RGMII_RXD3 (pin_F9) +#define MICROPY_HW_ETH_RGMII_RX_CTL (pin_F10) + // Murata 1YN configuration #define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/w43439_sdio_1yn_7_95_59_combined.h" #define CYW43_WIFI_NVRAM_INCLUDE_FILE "lib/cyw43-driver/firmware/wifi_nvram_1yn.h" diff --git a/ports/stm32/boards/OPENMV_N6/pins.csv b/ports/stm32/boards/OPENMV_N6/pins.csv index 52f6c9249..2ca075a15 100644 --- a/ports/stm32/boards/OPENMV_N6/pins.csv +++ b/ports/stm32/boards/OPENMV_N6/pins.csv @@ -95,7 +95,20 @@ P16,PE12 I2C4_SCL,PE13 I2C4_SDA,PE14 ,PE15 +,PF0 +,PF2 +,PF7 +,PF8 +,PF9 +,PF10 +,PF11 +,PF12 +,PF13 +,PF14 +,PF15 P6,PG0 +,PG3 +,PG4 P9,PG12 P7,PG13 BAT_ADC,PG15 diff --git a/ports/stm32/boards/stm32n657_af.csv b/ports/stm32/boards/stm32n657_af.csv index 220d27b3f..c02c658bd 100644 --- a/ports/stm32/boards/stm32n657_af.csv +++ b/ports/stm32/boards/stm32n657_af.csv @@ -20,6 +20,7 @@ PortC,PC9 , , , , PortC,PC10, , , , , , , , , , ,SDMMC1_D2 , , , , , , PortC,PC11, , , , , , , , , , ,SDMMC1_D3 , , , , , , PortC,PC12, , , , , , , , , , ,SDMMC1_CK , , , , , , +PortD,PD1 , , , , , , , , , , , ,ETH1_MDC , , , , , PortD,PD2 , , , , , , , , , , , ,SDMMC2_CK , , , , , PortD,PD5 , , , , , , , ,USART2_TX , , , , , , , , , PortD,PD6 , , , , ,TIM15_CH2 , , , , , , , , , , , , @@ -27,6 +28,7 @@ PortD,PD7 , , , , PortD,PD8 , , , , , , , ,USART3_TX , , , , , , , , , PortD,PD9 , , , , , , , ,USART3_RX , , , , , , , , , PortD,PD11, , , , , ,SPI2_MISO , , , , , , , , , , , +PortD,PD12, , , , , , , , , , , ,ETH1_MDIO , , , , , PortD,PD13, , ,TIM4_CH2 , , , , , , , , , , , , , , PortE,PE5 , , , , , , , ,USART1_TX , , , , , , , , , PortE,PE6 , , , , , , , ,USART1_RX , , , , , , , , , @@ -37,13 +39,28 @@ PortE,PE12, , , , PortE,PE13, , , , ,I2C4_SCL , , , , , , , , , , , , PortE,PE14, , , , ,I2C4_SDA , , , , , , , , , , , , PortE,PE15, , , , , ,SPI5_SCK , , , , , , , , , , , +PortF,PF0 , , , , , , , , , , , , ,ETH1_RGMII_GTX_CLK , , , , +PortF,PF2 , , , , , , , , , , , ,ETH1_RGMII_CLK125 , , , , , PortF,PF3 , , , , , , , ,USART2_RTS , , , , , , , , ,ADC1_INP16 +PortF,PF4 , , , , , , , , , , , ,ETH1_MDIO , , , , , PortF,PF6 , , , , , , , ,USART2_RX , , , , , , , , , +PortF,PF7 , , , , , , , , , , , ,ETH1_RMII_REF_CLK/ETH1_RGMII_RX_CLK , , , , , +PortF,PF8 , , , , , , , , , , , ,ETH1_RGMII_RXD2 , , , , , +PortF,PF9 , , , , , , , , , , , ,ETH1_RGMII_RXD3 , , , , , +PortF,PF10, , , , , , , , , , , ,ETH1_RMII_CRS_DV/ETH1_RGMII_RX_CTL , , , , , +PortF,PF11, , , , , , , , , , , ,ETH1_RMII_TX_EN/ETH1_RGMII_TX_CTL , , , , , +PortF,PF12, , , , , , , , , , , ,ETH1_RMII_TXD0/ETH1_RGMII_TXD0 , , , , , +PortF,PF13, , , , , , , , , , , ,ETH1_RMII_TXD1/ETH1_RGMII_TXD1 , , , , , +PortF,PF14, , , , , , , , , , , ,ETH1_RMII_RXD0/ETH1_RGMII_RXD0 , , , , , +PortF,PF15, , , , , , , , , , , ,ETH1_RMII_RXD1/ETH1_RGMII_RXD1 , , , , , PortG,PG0 , , ,TIM12_CH1 , , , , , , , , , , , , , , PortG,PG1 , , , , , ,SPI5_MISO , , , , , , , , , , , PortG,PG2 , , , , , ,SPI5_MOSI , , , , , , , , , , , +PortG,PG3 , , , , , , , , , , , ,ETH1_RGMII_TXD2 , , , , , +PortG,PG4 , , , , , , , , , , , ,ETH1_RGMII_TXD3 , , , , , PortG,PG5 , , , , , , , ,USART2_CTS , , , , , , , , , PortG,PG8 , , , , , , , , , , , ,SDMMC2_D1 , , , , , +PortG,PG11, , , , , , , , , , , ,ETH1_MDC , , , , , PortG,PG12, ,TIM17_CH1 , , , , , , , , , , , , , , , PortG,PG13, , ,TIM4_CH1 , , , , ,USART3_RTS , , , , , , , , , PortG,PG15, , , , , , , , , , , , , , , , ,ADC12_INP7/ADC12_INN3 diff --git a/ports/stm32/boards/stm32n6xx_hal_conf_base.h b/ports/stm32/boards/stm32n6xx_hal_conf_base.h index 641a003d8..87556cf15 100644 --- a/ports/stm32/boards/stm32n6xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32n6xx_hal_conf_base.h @@ -160,7 +160,6 @@ #include "stm32n6xx_hal_dcmipp.h" #include "stm32n6xx_hal_dma2d.h" #include "stm32n6xx_hal_dts.h" -#include "stm32n6xx_hal_eth.h" #include "stm32n6xx_hal_exti.h" #include "stm32n6xx_hal_fdcan.h" #include "stm32n6xx_hal_gfxmmu.h" diff --git a/ports/stm32/eth.c b/ports/stm32/eth.c index 9f6553068..60f2a23de 100644 --- a/ports/stm32/eth.c +++ b/ports/stm32/eth.c @@ -41,8 +41,19 @@ #include "lwip/dhcp.h" #include "netif/ethernet.h" +// Register and IRQ compatibility for STM32N6. +#if defined(STM32N6) +#define ETH ETH1 +#define ETH_MACMDIOAR_MB (ETH_MACMDIOAR_GB) +#define ETH_MACMDIOAR_MOC_Msk (ETH_MACMDIOAR_GOC_Msk) +#define ETH_MACMDIOAR_MOC_WR (ETH_MACMDIOAR_GOC_0) +#define ETH_MACMDIOAR_MOC_RD (ETH_MACMDIOAR_GOC_1 | ETH_MACMDIOAR_GOC_0) +#define ETH_IRQn ETH1_IRQn +#define ETH_IRQHandler ETH1_IRQHandler +#endif + // ETH DMA RX and TX descriptor definitions -#if defined(STM32H5) +#if defined(STM32H5) || defined(STM32N6) #define RX_DESCR_3_OWN_Pos (31) #define RX_DESCR_3_IOC_Pos (30) #define RX_DESCR_3_BUF1V_Pos (24) @@ -52,6 +63,7 @@ #define TX_DESCR_3_FD_Pos (29) #define TX_DESCR_3_LD_Pos (28) #define TX_DESCR_3_CIC_Pos (16) +#define TX_DESCR_2_IOC_Pos (31) #define TX_DESCR_2_B1L_Pos (0) #define TX_DESCR_2_B1L_Msk (0x3fff << TX_DESCR_2_B1L_Pos) #elif defined(STM32H7) @@ -85,15 +97,38 @@ #define TX_DESCR_1_TBS1_Pos (0) #endif +// Static alternate function macro. +#if defined(STM32N6) +#define STATIC_AF_ETH(signal) STATIC_AF_ETH1_##signal +#else +#define STATIC_AF_ETH(signal) STATIC_AF_ETH_##signal +#endif + // Configuration values #define PHY_INIT_TIMEOUT_MS (10000) -#define RX_BUF_SIZE (1524) // includes 4-byte CRC at end -#define TX_BUF_SIZE (1524) +// These buffer sizes need to be a multiple of 8 (for STM32N6 at least). +#define RX_BUF_SIZE (1528) // includes 4-byte CRC at end +#define TX_BUF_SIZE (1528) +#if defined(MICROPY_HW_ETH_RMII_REF_CLK) +// RMII in use. #define RX_BUF_NUM (5) #define TX_BUF_NUM (5) +#define USE_PBUF_REF_FOR_TX (0) +#else +// RGMII in use, so increase number of buffers and use pbuf zero copy if possible. +#define RX_BUF_NUM (16) +#define TX_BUF_NUM (16) +#define USE_PBUF_REF_FOR_TX (1) +#endif + +#if defined(STM32N6) +// The N6 has two DMA channels, so use one for RX and one for TX. +#define RX_DMA_CH (0) +#define TX_DMA_CH (1) +#endif typedef struct _eth_dma_rx_descr_t { volatile uint32_t rdes0, rdes1, rdes2, rdes3; @@ -106,11 +141,18 @@ typedef struct _eth_dma_tx_descr_t { typedef struct _eth_dma_t { eth_dma_rx_descr_t rx_descr[RX_BUF_NUM]; eth_dma_tx_descr_t tx_descr[TX_BUF_NUM]; - uint8_t rx_buf[RX_BUF_NUM * RX_BUF_SIZE] __attribute__((aligned(4))); - uint8_t tx_buf[TX_BUF_NUM * TX_BUF_SIZE] __attribute__((aligned(4))); - size_t rx_descr_idx; - size_t tx_descr_idx; - uint8_t padding[16384 - 15408]; + uint8_t rx_buf[RX_BUF_NUM * RX_BUF_SIZE] __attribute__((aligned(8))); + #if !USE_PBUF_REF_FOR_TX + uint8_t tx_buf[TX_BUF_NUM * TX_BUF_SIZE] __attribute__((aligned(8))); + #endif + #if !defined(STM32H5) && !defined(STM32N6) + // Make sure the size of this struct is 16k, for the MPU. + uint8_t padding[16 * 1024 + - sizeof(eth_dma_rx_descr_t) * RX_BUF_NUM + - sizeof(eth_dma_tx_descr_t) * TX_BUF_NUM + - RX_BUF_NUM * RX_BUF_SIZE + - TX_BUF_NUM * TX_BUF_SIZE]; + #endif } eth_dma_t; typedef struct _eth_t { @@ -118,18 +160,30 @@ typedef struct _eth_t { struct netif netif; struct dhcp dhcp_struct; uint32_t phy_addr; + void (*phy_init)(uint32_t phy_addr); int16_t (*phy_get_link_status)(uint32_t phy_addr); } eth_t; +// This struct contains RX and TX buffers shared with the DMA, and they may need +// to go in a special RAM section, or have MPU settings applied. static eth_dma_t eth_dma MICROPY_HW_ETH_DMA_ATTRIBUTE; +#if USE_PBUF_REF_FOR_TX +// This array holds lwIP pbufs that are currently in use by the DMA. +static struct pbuf *eth_dma_pbuf[TX_BUF_NUM]; +#endif + +// These variables index the buffers in eth_dma and are not shared with DMA. +static size_t eth_dma_rx_descr_idx; +static size_t eth_dma_tx_descr_idx; + eth_t eth_instance; static void eth_mac_deinit(eth_t *self); static void eth_process_frame(eth_t *self, size_t len, const uint8_t *buf); void eth_phy_write(uint32_t phy_addr, uint32_t reg, uint32_t val) { - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) while (ETH->MACMDIOAR & ETH_MACMDIOAR_MB) { } uint32_t ar = ETH->MACMDIOAR; @@ -157,7 +211,7 @@ void eth_phy_write(uint32_t phy_addr, uint32_t reg, uint32_t val) { } uint32_t eth_phy_read(uint32_t phy_addr, uint32_t reg) { - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) while (ETH->MACMDIOAR & ETH_MACMDIOAR_MB) { } uint32_t ar = ETH->MACMDIOAR; @@ -188,24 +242,47 @@ int eth_init(eth_t *self, int mac_idx, uint32_t phy_addr, int phy_type) { mp_hal_get_mac(mac_idx, &self->netif.hwaddr[0]); self->netif.hwaddr_len = 6; self->phy_addr = phy_addr; + self->phy_init = eth_phy_generic_init; if (phy_type == ETH_PHY_DP83825 || phy_type == ETH_PHY_DP83848) { self->phy_get_link_status = eth_phy_dp838xx_get_link_status; } else if (phy_type == ETH_PHY_LAN8720 || phy_type == ETH_PHY_LAN8742) { self->phy_get_link_status = eth_phy_lan87xx_get_link_status; + } else if (phy_type == ETH_PHY_RTL8211) { + self->phy_init = eth_phy_rtl8211_init; + self->phy_get_link_status = eth_phy_rtl8211_get_link_status; } else { return -1; } - // Configure GPIO - mp_hal_pin_config_alt_static(MICROPY_HW_ETH_MDC, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_MDC); - mp_hal_pin_config_alt_static(MICROPY_HW_ETH_MDIO, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_MDIO); - mp_hal_pin_config_alt_static_speed(MICROPY_HW_ETH_RMII_REF_CLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_MEDIUM, STATIC_AF_ETH_RMII_REF_CLK); - mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_CRS_DV, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_CRS_DV); - mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_RXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_RXD0); - mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_RXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_RXD1); - mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TX_EN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TX_EN); - mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TXD0); - mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TXD1); + // Configure GPIO for management data. + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_MDC, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(MDC)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_MDIO, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(MDIO)); + + #if defined(MICROPY_HW_ETH_RMII_REF_CLK) + // Configure GPIO for RMII interface. + mp_hal_pin_config_alt_static_speed(MICROPY_HW_ETH_RMII_REF_CLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, MP_HAL_PIN_SPEED_MEDIUM, STATIC_AF_ETH(RMII_REF_CLK)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_CRS_DV, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RMII_CRS_DV)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_RXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RMII_RXD0)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_RXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RMII_RXD1)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TX_EN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RMII_TX_EN)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RMII_TXD0)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RMII_TXD1)); + #else + // Configure GPIO for RGMII interface. + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_CLK125, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_CLK125)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_GTX_CLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_GTX_CLK)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_TXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_TXD0)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_TXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_TXD1)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_TXD2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_TXD2)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_TXD3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_TXD3)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_TX_CTL, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_TX_CTL)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_RX_CLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_RX_CLK)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_RXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_RXD0)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_RXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_RXD1)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_RXD2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_RXD2)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_RXD3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_RXD3)); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RGMII_RX_CTL, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH(RGMII_RX_CTL)); + #endif // Enable peripheral clock #if defined(STM32H5) @@ -216,6 +293,11 @@ int eth_init(eth_t *self, int mac_idx, uint32_t phy_addr, int phy_type) { __HAL_RCC_ETH1MAC_CLK_ENABLE(); __HAL_RCC_ETH1TX_CLK_ENABLE(); __HAL_RCC_ETH1RX_CLK_ENABLE(); + #elif defined(STM32N6) + __HAL_RCC_ETH1_CLK_ENABLE(); + __HAL_RCC_ETH1MAC_CLK_ENABLE(); + __HAL_RCC_ETH1TX_CLK_ENABLE(); + __HAL_RCC_ETH1RX_CLK_ENABLE(); #else __HAL_RCC_ETH_CLK_ENABLE(); #endif @@ -229,9 +311,10 @@ void eth_set_trace(eth_t *self, uint32_t value) { static int eth_mac_init(eth_t *self) { // Configure MPU uint32_t irq_state = mpu_config_start(); - #if defined(STM32H5) - mpu_config_region(MPU_REGION_ETH, (uint32_t)ð_dma, MPU_CONFIG_ETH(16 * 1024)); + #if defined(STM32H5) || defined(STM32N6) + mpu_config_region(MPU_REGION_ETH, (uint32_t)ð_dma, MPU_CONFIG_ETH(sizeof(eth_dma_t))); #else + MP_STATIC_ASSERT(sizeof(eth_dma_t) == 16 * 1024); mpu_config_region(MPU_REGION_ETH, (uint32_t)ð_dma, MPU_CONFIG_ETH(MPU_REGION_SIZE_16KB)); #endif mpu_config_end(irq_state); @@ -241,16 +324,33 @@ static int eth_mac_init(eth_t *self) { __HAL_RCC_ETH_FORCE_RESET(); #elif defined(STM32H7) __HAL_RCC_ETH1MAC_FORCE_RESET(); + #elif defined(STM32N6) + __HAL_RCC_ETH1_FORCE_RESET(); #else __HAL_RCC_ETHMAC_FORCE_RESET(); #endif - // Select RMII interface + // Select clock sources. + #if defined(STM32N6) + LL_RCC_SetETHREFTXClockSource(LL_RCC_ETH1REFTX_CLKSOURCE_EXT); // max 25MHz + LL_RCC_SetETHREFRXClockSource(LL_RCC_ETH1REFRX_CLKSOURCE_EXT); // max 125MHz + LL_RCC_SetETHClockSource(LL_RCC_ETH1_CLKSOURCE_IC12); // max 125MHz + LL_RCC_SetETH1PTPDivider(LL_RCC_ETH1PTP_DIV_1); + LL_RCC_SetETHPTPClockSource(LL_RCC_ETH1PTP_CLKSOURCE_HCLK); // max 200MHz + #endif + + // Select RMII or RGMII interface #if defined(STM32H5) __HAL_RCC_SBS_CLK_ENABLE(); SBS->PMCR = (SBS->PMCR & ~SBS_PMCR_ETH_SEL_PHY_Msk) | SBS_PMCR_ETH_SEL_PHY_2; #elif defined(STM32H7) SYSCFG->PMCR = (SYSCFG->PMCR & ~SYSCFG_PMCR_EPIS_SEL_Msk) | SYSCFG_PMCR_EPIS_SEL_2; + #elif defined(STM32N6) + #if defined(MICROPY_HW_ETH_RGMII_CLK125) + LL_RCC_SetETHPHYInterface(LL_RCC_ETH1PHY_IF_RGMII); + #else + LL_RCC_SetETHPHYInterface(LL_RCC_ETH1PHY_IF_RMII); + #endif #else __HAL_RCC_SYSCFG_CLK_ENABLE(); SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL; @@ -268,6 +368,13 @@ static int eth_mac_init(eth_t *self) { __HAL_RCC_ETH1MAC_CLK_SLEEP_ENABLE(); __HAL_RCC_ETH1TX_CLK_SLEEP_ENABLE(); __HAL_RCC_ETH1RX_CLK_SLEEP_ENABLE(); + #elif defined(STM32N6) + __HAL_RCC_ETH1_RELEASE_RESET(); + + __HAL_RCC_ETH1_CLK_SLEEP_ENABLE(); + __HAL_RCC_ETH1MAC_CLK_SLEEP_ENABLE(); + __HAL_RCC_ETH1TX_CLK_SLEEP_ENABLE(); + __HAL_RCC_ETH1RX_CLK_SLEEP_ENABLE(); #else __HAL_RCC_ETHMAC_RELEASE_RESET(); @@ -277,7 +384,7 @@ static int eth_mac_init(eth_t *self) { #endif // Do a soft reset of the MAC core - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) #define ETH_SOFT_RESET(eth) do { eth->DMAMR = ETH_DMAMR_SWR; } while (0) #define ETH_IS_RESET(eth) (eth->DMAMR & ETH_DMAMR_SWR) #else @@ -299,7 +406,7 @@ static int eth_mac_init(eth_t *self) { // Set MII clock range uint32_t hclk = HAL_RCC_GetHCLKFreq(); uint32_t cr_div; - #if defined(STM32H5) + #if defined(STM32H5) || defined(STM32N6) cr_div = ETH->MACMDIOAR & ~ETH_MACMDIOAR_CR; if (hclk < 35000000) { cr_div |= ETH_MACMDIOAR_CR_DIV16; @@ -311,8 +418,17 @@ static int eth_mac_init(eth_t *self) { cr_div |= ETH_MACMDIOAR_CR_DIV62; } else if (hclk < 250000000) { cr_div |= ETH_MACMDIOAR_CR_DIV102; + #if defined(STM32H5) } else { cr_div |= ETH_MACMDIOAR_CR_DIV124; + #else + } else if (hclk < 300000000) { + cr_div |= ETH_MACMDIOAR_CR_DIV124; + } else if (hclk < 500000000) { + cr_div |= ETH_MACMDIOAR_CR_DIV204; + } else { + cr_div |= ETH_MACMDIOAR_CR_DIV324; + #endif } ETH->MACMDIOAR = cr_div; #elif defined(STM32H7) @@ -344,14 +460,21 @@ static int eth_mac_init(eth_t *self) { ETH->MACMIIAR = cr_div; #endif + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) + // Configure the MAC 1-us tick counter register. + WRITE_REG(ETH->MAC1USTCR, HAL_RCC_GetHCLKFreq() / 1000000U - 1U); + #endif + #if defined(STM32H5) || defined(STM32H7) // don't skip 32bit words since our descriptors are continuous in memory ETH->DMACCR &= ~(ETH_DMACCR_DSL_Msk); + #elif defined(STM32N6) + ETH->DMA_CH[RX_DMA_CH].DMACCR &= ~(ETH_DMACxCR_DSL_Msk); + ETH->DMA_CH[TX_DMA_CH].DMACCR &= ~(ETH_DMACxCR_DSL_Msk); #endif - // Reset the PHY - eth_phy_write(self->phy_addr, PHY_BCR, PHY_BCR_SOFT_RESET); - mp_hal_delay_ms(50); + // Reset and initialize the PHY. + self->phy_init(self->phy_addr); // Wait for the PHY link to be established int phy_state = 0; @@ -397,7 +520,7 @@ static int eth_mac_init(eth_t *self) { uint16_t phy_scsr = self->phy_get_link_status(self->phy_addr); // Burst mode configuration - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) ETH->DMASBMR = ETH->DMASBMR & ~ETH_DMASBMR_AAL & ~ETH_DMASBMR_FB; #else ETH->DMABMR = 0; @@ -410,6 +533,21 @@ static int eth_mac_init(eth_t *self) { | ETH_DMACIER_NIE // enable normal interrupts | ETH_DMACIER_RIE // enable RX interrupt ; + #elif defined(STM32N6) + ETH->DMA_CH[RX_DMA_CH].DMACIER = + ETH_DMACxIER_NIE // enable normal interrupts + | ETH_DMACxIER_RIE // enable RX interrupt + ; + #if USE_PBUF_REF_FOR_TX + #if RX_DMA_CH == TX_DMA_CH + ETH->DMA_CH[TX_DMA_CH].DMACIER |= ETH_DMACxIER_TIE; // enable TX interrupt + #else + ETH->DMA_CH[TX_DMA_CH].DMACIER = + ETH_DMACxIER_NIE // enable normal interrupts + | ETH_DMACxIER_TIE // enable TX interrupt + ; + #endif + #endif #else ETH->DMAIER = ETH_DMAIER_NISE // enable normal interrupts @@ -419,7 +557,7 @@ static int eth_mac_init(eth_t *self) { // Configure RX descriptor lists for (size_t i = 0; i < RX_BUF_NUM; ++i) { - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) eth_dma.rx_descr[i].rdes3 = 1 << RX_DESCR_3_OWN_Pos | (1 << RX_DESCR_3_BUF1V_Pos) // buf1 address valid @@ -439,17 +577,22 @@ static int eth_mac_init(eth_t *self) { #if defined(STM32H5) || defined(STM32H7) ETH->DMACRDLAR = (uint32_t)ð_dma.rx_descr[0]; + #elif defined(STM32N6) + // Set number of RX descriptors and buffer pointers. + ETH->DMA_CH[RX_DMA_CH].DMACRXRLR = RX_BUF_NUM - 1; + ETH->DMA_CH[RX_DMA_CH].DMACRXDLAR = (uint32_t)ð_dma.rx_descr[0]; + ETH->DMA_CH[RX_DMA_CH].DMACRXDTPR = (uint32_t)ð_dma.rx_descr[RX_BUF_NUM - 1]; #else ETH->DMARDLAR = (uint32_t)ð_dma.rx_descr[0]; #endif - eth_dma.rx_descr_idx = 0; + eth_dma_rx_descr_idx = 0; // Configure TX descriptor lists for (size_t i = 0; i < TX_BUF_NUM; ++i) { - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) eth_dma.tx_descr[i].tdes0 = 0; eth_dma.tx_descr[i].tdes1 = 0; - eth_dma.tx_descr[i].tdes2 = TX_BUF_SIZE & TX_DESCR_2_B1L_Msk; + eth_dma.tx_descr[i].tdes2 = 0; eth_dma.tx_descr[i].tdes3 = 0; #else eth_dma.tx_descr[i].tdes0 = 1 << TX_DESCR_0_TCH_Pos; @@ -465,10 +608,20 @@ static int eth_mac_init(eth_t *self) { ETH->DMACRDRLR = RX_BUF_NUM - 1; ETH->DMACTDLAR = (uint32_t)ð_dma.tx_descr[0]; + #elif defined(STM32N6) + // Set number of TX descriptors and buffer pointers. + ETH->DMA_CH[TX_DMA_CH].DMACTXRLR = TX_BUF_NUM - 1; + ETH->DMA_CH[TX_DMA_CH].DMACTXDLAR = (uint32_t)ð_dma.tx_descr[0]; + ETH->DMA_CH[TX_DMA_CH].DMACTXDTPR = (uint32_t)ð_dma.tx_descr[0]; #else ETH->DMATDLAR = (uint32_t)ð_dma.tx_descr[0]; #endif - eth_dma.tx_descr_idx = 0; + eth_dma_tx_descr_idx = 0; + #if USE_PBUF_REF_FOR_TX + for (int i = 0; i < TX_BUF_NUM; ++i) { + eth_dma_pbuf[i] = NULL; + } + #endif // Configure DMA #if defined(STM32H5) || defined(STM32H7) @@ -476,6 +629,11 @@ static int eth_mac_init(eth_t *self) { ETH->MTLRQOMR = ETH_MTLRQOMR_RSF; // transmission starts when a full packet resides in the Tx queue ETH->MTLTQOMR = ETH_MTLTQOMR_TSF; + #elif defined(STM32N6) + // read from RX FIFO only after a full frame is written + ETH->MTL_QUEUE[0].MTLRXQOMR = ETH_MTLRXQxOMR_RSF; + // transmission starts when a full packet resides in the Tx queue + ETH->MTL_QUEUE[0].MTLTXQOMR = ETH_MTLTXQxOMR_TSF; #else ETH->DMAOMR = ETH_DMAOMR_RSF // read from RX FIFO after a full frame is written @@ -485,7 +643,7 @@ static int eth_mac_init(eth_t *self) { mp_hal_delay_ms(2); // Select MAC filtering options - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) ETH->MACPFR = ETH_MACPFR_RA; // pass all frames up #else ETH->MACFFR = @@ -501,15 +659,50 @@ static int eth_mac_init(eth_t *self) { ETH->MACA0LR = mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0]; mp_hal_delay_ms(2); + // Work out the line speed configuration for MACCR. + uint32_t maccr = 0; + if (phy_scsr & PHY_SPEED_100HALF) { + maccr |= ETH_MACCR_FES; + } + if (phy_scsr & PHY_DUPLEX) { + maccr |= ETH_MACCR_DM; + } + + #if defined(STM32N6) + + if (!(phy_scsr & PHY_SPEED_1000HALF)) { + maccr |= ETH_MACCR_PS; + } + + maccr |= + ETH_MACCR_IPG_96BIT + | ETH_MACCR_SARC_REPADDR0 + | ETH_MACCR_IPC + | ETH_MACCR_BL_10 + | ETH_MACCR_PRELEN_7; + + ETH->MACCR = maccr; + ETH->MACECR = 0x618U; + ETH->MACWTR = ETH_MACWTR_WTO_2KB; + ETH->MACQ0TXFCR = ETH_MACQ0TXFCR_PLT_MINUS4; + ETH->MACRXFCR = 0; + ETH->MACRXQC0R = ETH_MACRXQC0R_RXQ0EN_GT | ETH_MACRXQC0R_RXQ1EN_NOT; + + ETH->MTLOMR = ETH_MTLOMR_SCHALG_SP | ETH_MTLOMR_RAA_SP; + ETH->MTLRXQDMAMR = ETH_MTLRXQDMAMR_Q0MDMACH_DMACH0 | ETH_MTLRXQDMAMR_Q1MDMACH_DMACH1; + ETH->MTL_QUEUE[0].MTLTXQOMR = ETH_MTLTXQxOMR_TXQEN_EN | ETH_MTLTXQxOMR_TSF | 7 << ETH_MTLTXQxOMR_TQS_Pos; + ETH->MTL_QUEUE[1].MTLTXQOMR = ETH_MTLTXQxOMR_TXQEN_EN | ETH_MTLTXQxOMR_TSF | 7 << ETH_MTLTXQxOMR_TQS_Pos; + ETH->MTL_QUEUE[0].MTLRXQOMR = ETH_MTLRXQxOMR_RSF | 15 << ETH_MTLRXQxOMR_RQS_Pos; + ETH->MTL_QUEUE[1].MTLRXQOMR = ETH_MTLRXQxOMR_RSF | 15 << ETH_MTLRXQxOMR_RQS_Pos; + + #else + // Set main MAC control register - ETH->MACCR = - phy_scsr == PHY_SPEED_10FULL ? ETH_MACCR_DM - : phy_scsr == PHY_SPEED_100HALF ? ETH_MACCR_FES - : phy_scsr == PHY_SPEED_100FULL ? (ETH_MACCR_FES | ETH_MACCR_DM) - : 0 - ; + ETH->MACCR = maccr; mp_hal_delay_ms(2); + #endif + // Start MAC layer ETH->MACCR |= ETH_MACCR_TE // enable TX @@ -521,6 +714,15 @@ static int eth_mac_init(eth_t *self) { #if defined(STM32H5) || defined(STM32H7) ETH->DMACRCR |= ETH_DMACRCR_SR; // start RX ETH->DMACTCR |= ETH_DMACTCR_ST; // start TX + #elif defined(STM32N6) + ETH->MTL_QUEUE[0].MTLTXQOMR |= ETH_MTLTXQxOMR_FTQ; // flush TX FIFO + ETH->MTL_QUEUE[1].MTLTXQOMR |= ETH_MTLTXQxOMR_FTQ; // flush TX FIFO + ETH->DMA_CH[RX_DMA_CH].DMACRXCR = RX_BUF_SIZE << ETH_DMACxRXCR_RBSZ_Pos; + ETH->DMA_CH[RX_DMA_CH].DMACRXCR |= ETH_DMACxRXCR_SR; // start RX + ETH->DMA_CH[TX_DMA_CH].DMACTXCR = 4 << ETH_DMACxTXCR_TXPBL_Pos; + ETH->DMA_CH[TX_DMA_CH].DMACTXCR |= ETH_DMACxTXCR_ST; // start TX + ETH->DMA_CH[RX_DMA_CH].DMACSR |= ETH_DMACxSR_TPS | ETH_DMACxSR_RPS; // clear TX/RX process stopped flags + ETH->DMA_CH[TX_DMA_CH].DMACSR |= ETH_DMACxSR_TPS | ETH_DMACxSR_RPS; // clear TX/RX process stopped flags #else ETH->DMAOMR |= ETH_DMAOMR_ST // start TX @@ -547,6 +749,10 @@ static void eth_mac_deinit(eth_t *self) { __HAL_RCC_ETH1MAC_FORCE_RESET(); __HAL_RCC_ETH1MAC_RELEASE_RESET(); __HAL_RCC_ETH1MAC_CLK_DISABLE(); + #elif defined(STM32N6) + __HAL_RCC_ETH1_FORCE_RESET(); + __HAL_RCC_ETH1_RELEASE_RESET(); + __HAL_RCC_ETH1_CLK_DISABLE(); #else __HAL_RCC_ETHMAC_FORCE_RESET(); __HAL_RCC_ETHMAC_RELEASE_RESET(); @@ -554,16 +760,18 @@ static void eth_mac_deinit(eth_t *self) { #endif } -static int eth_tx_buf_get(size_t len, uint8_t **buf) { +#if !USE_PBUF_REF_FOR_TX + +int eth_tx_buf_get(size_t len, uint8_t **buf) { if (len > TX_BUF_SIZE) { return -MP_EINVAL; } // Wait for DMA to release the current TX descriptor (if it has it) - eth_dma_tx_descr_t *tx_descr = ð_dma.tx_descr[eth_dma.tx_descr_idx]; + eth_dma_tx_descr_t *tx_descr = ð_dma.tx_descr[eth_dma_tx_descr_idx]; uint32_t t0 = mp_hal_ticks_ms(); for (;;) { - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) if (!(tx_descr->tdes3 & (1 << TX_DESCR_3_OWN_Pos))) { break; } @@ -577,44 +785,67 @@ static int eth_tx_buf_get(size_t len, uint8_t **buf) { } } - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) // Update TX descriptor with length and buffer pointer - *buf = ð_dma.tx_buf[eth_dma.tx_descr_idx * TX_BUF_SIZE]; + *buf = ð_dma.tx_buf[eth_dma_tx_descr_idx * TX_BUF_SIZE]; tx_descr->tdes2 = len & TX_DESCR_2_B1L_Msk; tx_descr->tdes0 = (uint32_t)*buf; #else // Update TX descriptor with length, buffer pointer and linked list pointer - *buf = ð_dma.tx_buf[eth_dma.tx_descr_idx * TX_BUF_SIZE]; + *buf = ð_dma.tx_buf[eth_dma_tx_descr_idx * TX_BUF_SIZE]; tx_descr->tdes1 = len << TX_DESCR_1_TBS1_Pos; tx_descr->tdes2 = (uint32_t)*buf; - tx_descr->tdes3 = (uint32_t)ð_dma.tx_descr[(eth_dma.tx_descr_idx + 1) % TX_BUF_NUM]; + tx_descr->tdes3 = (uint32_t)ð_dma.tx_descr[(eth_dma_tx_descr_idx + 1) % TX_BUF_NUM]; #endif return 0; } -static int eth_tx_buf_send(void) { - // Get TX descriptor and move to next one - eth_dma_tx_descr_t *tx_descr = ð_dma.tx_descr[eth_dma.tx_descr_idx]; - eth_dma.tx_descr_idx = (eth_dma.tx_descr_idx + 1) % TX_BUF_NUM; +#else - // Schedule to send next outgoing frame - #if defined(STM32H5) || defined(STM32H7) - tx_descr->tdes3 = - 1 << TX_DESCR_3_OWN_Pos // owned by DMA - | 1 << TX_DESCR_3_LD_Pos // last segment - | 1 << TX_DESCR_3_FD_Pos // first segment - | 3 << TX_DESCR_3_CIC_Pos // enable all checksums inserted by hardware - ; - #else - tx_descr->tdes0 = - 1 << TX_DESCR_0_OWN_Pos // owned by DMA - | 1 << TX_DESCR_0_LS_Pos // last segment - | 1 << TX_DESCR_0_FS_Pos // first segment - | 3 << TX_DESCR_0_CIC_Pos // enable all checksums inserted by hardware - | 1 << TX_DESCR_0_TCH_Pos // TX descriptor is chained - ; - #endif +int eth_tx_buf_get_ref(size_t len, uint8_t *buf, unsigned int idx) { + // Wait for DMA to release the current TX descriptor (if it has it). + eth_dma_tx_descr_t *tx_descr = ð_dma.tx_descr[(eth_dma_tx_descr_idx + idx) % TX_BUF_NUM]; + uint32_t t0 = mp_hal_ticks_ms(); + while (tx_descr->tdes3 & (1 << TX_DESCR_3_OWN_Pos)) { + if (mp_hal_ticks_ms() - t0 > 1000) { + return -MP_ETIMEDOUT; + } + } + + MP_HAL_CLEAN_DCACHE(buf, len); + tx_descr->tdes2 = (len & TX_DESCR_2_B1L_Msk) | (1 << TX_DESCR_2_IOC_Pos); + tx_descr->tdes0 = (uint32_t)buf; + + return 0; +} + +#endif + +static int eth_tx_buf_send(unsigned int num_segments) { + for (unsigned int segment = 0; segment < num_segments; ++segment) { + // Get TX descriptor and move to next one + eth_dma_tx_descr_t *tx_descr = ð_dma.tx_descr[eth_dma_tx_descr_idx]; + eth_dma_tx_descr_idx = (eth_dma_tx_descr_idx + 1) % TX_BUF_NUM; + + // Schedule to send next outgoing frame + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) + tx_descr->tdes3 = + 1 << TX_DESCR_3_OWN_Pos // owned by DMA + | (segment == num_segments - 1) << TX_DESCR_3_LD_Pos // last segment + | (segment == 0) << TX_DESCR_3_FD_Pos // first segment + | 3 << TX_DESCR_3_CIC_Pos // enable all checksums inserted by hardware + ; + #else + tx_descr->tdes0 = + 1 << TX_DESCR_0_OWN_Pos // owned by DMA + | (segment == num_segments - 1) << TX_DESCR_0_LS_Pos // last segment + | (segment == 0) << TX_DESCR_0_FS_Pos // first segment + | 3 << TX_DESCR_0_CIC_Pos // enable all checksums inserted by hardware + | 1 << TX_DESCR_0_TCH_Pos // TX descriptor is chained + ; + #endif + } // Notify ETH DMA that there is a new TX descriptor for sending __DMB(); @@ -622,7 +853,12 @@ static int eth_tx_buf_send(void) { if (ETH->DMACSR & ETH_DMACSR_TBU) { ETH->DMACSR = ETH_DMACSR_TBU; } - ETH->DMACTDTPR = (uint32_t)ð_dma.tx_descr[eth_dma.tx_descr_idx]; + ETH->DMACTDTPR = (uint32_t)ð_dma.tx_descr[eth_dma_tx_descr_idx]; + #elif defined(STM32N6) + if (ETH->DMA_CH[TX_DMA_CH].DMACSR & ETH_DMACxSR_TBU) { + ETH->DMA_CH[TX_DMA_CH].DMACSR = ETH_DMACxSR_TBU; + } + ETH->DMA_CH[TX_DMA_CH].DMACTXDTPR = (uint32_t)ð_dma.tx_descr[eth_dma_tx_descr_idx]; #else if (ETH->DMASR & ETH_DMASR_TBUS) { ETH->DMASR = ETH_DMASR_TBUS; @@ -635,12 +871,12 @@ static int eth_tx_buf_send(void) { static void eth_dma_rx_free(void) { // Get RX descriptor, RX buffer and move to next one - eth_dma_rx_descr_t *rx_descr = ð_dma.rx_descr[eth_dma.rx_descr_idx]; - uint8_t *buf = ð_dma.rx_buf[eth_dma.rx_descr_idx * RX_BUF_SIZE]; - eth_dma.rx_descr_idx = (eth_dma.rx_descr_idx + 1) % RX_BUF_NUM; + eth_dma_rx_descr_t *rx_descr = ð_dma.rx_descr[eth_dma_rx_descr_idx]; + uint8_t *buf = ð_dma.rx_buf[eth_dma_rx_descr_idx * RX_BUF_SIZE]; + eth_dma_rx_descr_idx = (eth_dma_rx_descr_idx + 1) % RX_BUF_NUM; // Schedule to get next incoming frame - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) rx_descr->rdes0 = (uint32_t)buf; rx_descr->rdes3 = 1 << RX_DESCR_3_OWN_Pos; // owned by DMA rx_descr->rdes3 |= 1 << RX_DESCR_3_BUF1V_Pos; // buf 1 address valid @@ -651,24 +887,32 @@ static void eth_dma_rx_free(void) { | RX_BUF_SIZE << RX_DESCR_1_RBS1_Pos // maximum buffer length ; rx_descr->rdes2 = (uint32_t)buf; - rx_descr->rdes3 = (uint32_t)ð_dma.rx_descr[eth_dma.rx_descr_idx]; + rx_descr->rdes3 = (uint32_t)ð_dma.rx_descr[eth_dma_rx_descr_idx]; rx_descr->rdes0 = 1 << RX_DESCR_0_OWN_Pos; // owned by DMA #endif // Notify ETH DMA that there is a new RX descriptor available __DMB(); #if defined(STM32H5) || defined(STM32H7) - ETH->DMACRDTPR = (uint32_t)&rx_descr[eth_dma.rx_descr_idx]; + ETH->DMACRDTPR = (uint32_t)&rx_descr[eth_dma_rx_descr_idx]; + #elif defined(STM32N6) + ETH->DMA_CH[RX_DMA_CH].DMACRXDTPR = (uint32_t)&rx_descr[eth_dma_rx_descr_idx]; #else ETH->DMARPDR = 0; #endif } void ETH_IRQHandler(void) { + MP_STATIC_ASSERT(ETH_IRQn > 0); + #if defined(STM32H5) || defined(STM32H7) uint32_t sr = ETH->DMACSR; ETH->DMACSR = ETH_DMACSR_NIS; uint32_t rx_interrupt = sr & ETH_DMACSR_RI; + #elif defined(STM32N6) + uint32_t sr = ETH->DMA_CH[RX_DMA_CH].DMACSR; + ETH->DMA_CH[RX_DMA_CH].DMACSR = ETH_DMACxSR_NIS; + uint32_t rx_interrupt = sr & ETH_DMACxSR_RI; #else uint32_t sr = ETH->DMASR; ETH->DMASR = ETH_DMASR_NIS; @@ -677,18 +921,20 @@ void ETH_IRQHandler(void) { if (rx_interrupt) { #if defined(STM32H5) || defined(STM32H7) ETH->DMACSR = ETH_DMACSR_RI; + #elif defined(STM32N6) + ETH->DMA_CH[RX_DMA_CH].DMACSR = ETH_DMACxSR_RI; #else ETH->DMASR = ETH_DMASR_RS; #endif for (;;) { - #if defined(STM32H5) || defined(STM32H7) - eth_dma_rx_descr_t *rx_descr_l = ð_dma.rx_descr[eth_dma.rx_descr_idx]; + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) + eth_dma_rx_descr_t *rx_descr_l = ð_dma.rx_descr[eth_dma_rx_descr_idx]; if (rx_descr_l->rdes3 & (1 << RX_DESCR_3_OWN_Pos)) { // No more RX descriptors ready to read break; } #else - eth_dma_rx_descr_t *rx_descr = ð_dma.rx_descr[eth_dma.rx_descr_idx]; + eth_dma_rx_descr_t *rx_descr = ð_dma.rx_descr[eth_dma_rx_descr_idx]; if (rx_descr->rdes0 & (1 << RX_DESCR_0_OWN_Pos)) { // No more RX descriptors ready to read break; @@ -696,14 +942,14 @@ void ETH_IRQHandler(void) { #endif // Get RX buffer containing new frame - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) size_t len = (rx_descr_l->rdes3 & RX_DESCR_3_PL_Msk); #else size_t len = (rx_descr->rdes0 & RX_DESCR_0_FL_Msk) >> RX_DESCR_0_FL_Pos; #endif len -= 4; // discard CRC at end - #if defined(STM32H5) || defined(STM32H7) - uint8_t *buf = ð_dma.rx_buf[eth_dma.rx_descr_idx * RX_BUF_SIZE]; + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) + uint8_t *buf = ð_dma.rx_buf[eth_dma_rx_descr_idx * RX_BUF_SIZE]; #else uint8_t *buf = (uint8_t *)rx_descr->rdes2; #endif @@ -713,6 +959,28 @@ void ETH_IRQHandler(void) { eth_dma_rx_free(); } } + + #if USE_PBUF_REF_FOR_TX + #if RX_DMA_CH != TX_DMA_CH + sr = ETH->DMA_CH[TX_DMA_CH].DMACSR; + ETH->DMA_CH[TX_DMA_CH].DMACSR = ETH_DMACxSR_NIS; + #endif + uint32_t tx_interrupt = sr & ETH_DMACxSR_TI; + if (tx_interrupt) { + ETH->DMA_CH[TX_DMA_CH].DMACSR = ETH_DMACxSR_TI; + for (int i = 0; i < TX_BUF_NUM; ++i) { + eth_dma_tx_descr_t *tx_descr = ð_dma.tx_descr[i]; + if (!(tx_descr->tdes3 & (1 << TX_DESCR_3_OWN_Pos))) { + // DMA does not own it + if (eth_dma_pbuf[i] != NULL) { + // release pbuf + pbuf_free(eth_dma_pbuf[i]); + eth_dma_pbuf[i] = NULL; + } + } + } + } + #endif } /*******************************************************************************/ @@ -749,13 +1017,64 @@ static err_t eth_netif_output(struct netif *netif, struct pbuf *p) { LINK_STATS_INC(link.xmit); eth_trace(netif->state, (size_t)-1, p, NETUTILS_TRACE_IS_TX | NETUTILS_TRACE_NEWLINE); + #if USE_PBUF_REF_FOR_TX + + // Work out how many segments the pbuf has, and if it needs a copy made. + bool made_pbuf_copy = false; + unsigned int num_segments = 0; + for (struct pbuf *pb = p; pb != NULL; pb = pb->next) { + if (PBUF_NEEDS_COPY(pb)) { + // Note: this path is called for large UDP packets that are fragmented, + // because the fragments use PBUF_REF to divide up the original data. + p = pbuf_clone(PBUF_RAW, PBUF_RAM, p); + made_pbuf_copy = true; + num_segments = 1; + break; + } + ++num_segments; + } + + // Allocate TX buffer slots. + unsigned int idx = 0; + for (struct pbuf *pb = p; pb != NULL; pb = pb->next) { + int ret = eth_tx_buf_get_ref(pb->len, pb->payload, idx++); + if (ret != 0) { + if (made_pbuf_copy) { + pbuf_free(p); + } + return ERR_BUF; + } + } + + // Take references to pbufs + idx = 0; + for (struct pbuf *pb = p; pb != NULL; pb = pb->next) { + unsigned int tx_idx = (eth_dma_tx_descr_idx + idx) % TX_BUF_NUM; + if (eth_dma_pbuf[tx_idx] != NULL) { + pbuf_free(eth_dma_pbuf[tx_idx]); + } + if (!made_pbuf_copy) { + pbuf_ref(pb); + } + eth_dma_pbuf[tx_idx] = pb; + ++idx; + } + + // Start the transmission. + int ret = eth_tx_buf_send(num_segments); + + #else + + // Allocate TX slot, copy the pbuf, and start the transmission. uint8_t *buf; int ret = eth_tx_buf_get(p->tot_len, &buf); if (ret == 0) { pbuf_copy_partial(p, buf, p->tot_len, 0); - ret = eth_tx_buf_send(); + ret = eth_tx_buf_send(1); } + #endif + return ret ? ERR_BUF : ERR_OK; } @@ -875,6 +1194,8 @@ void eth_low_power_mode(eth_t *self, bool enable) { // Enable eth clock #if defined(STM32H7) __HAL_RCC_ETH1MAC_CLK_ENABLE(); + #elif defined(STM32N6) + __HAL_RCC_ETH1_CLK_ENABLE(); #else __HAL_RCC_ETH_CLK_ENABLE(); #endif @@ -886,6 +1207,8 @@ void eth_low_power_mode(eth_t *self, bool enable) { // Disable eth clock. #if defined(STM32H7) __HAL_RCC_ETH1MAC_CLK_DISABLE(); + #elif defined(STM32N6) + __HAL_RCC_ETH1_CLK_DISABLE(); #else __HAL_RCC_ETH_CLK_DISABLE(); #endif diff --git a/ports/stm32/eth.h b/ports/stm32/eth.h index 564744969..6556f4a7c 100644 --- a/ports/stm32/eth.h +++ b/ports/stm32/eth.h @@ -30,7 +30,8 @@ enum { ETH_PHY_LAN8742 = 0, ETH_PHY_LAN8720, ETH_PHY_DP83848, - ETH_PHY_DP83825 + ETH_PHY_DP83825, + ETH_PHY_RTL8211 }; typedef struct _eth_t eth_t; diff --git a/ports/stm32/eth_phy.c b/ports/stm32/eth_phy.c index 56cddba9c..cdc632f26 100644 --- a/ports/stm32/eth_phy.c +++ b/ports/stm32/eth_phy.c @@ -31,18 +31,45 @@ #if defined(MICROPY_HW_ETH_MDC) #define PHY_SCSR_LAN87XX (0x001f) -#define PHY_SCSR_LAN87XX_SPEED_Pos (2) -#define PHY_SCSR_LAN87XX_SPEED_Msk (7) +#define PHY_SCSR_LAN87XX_10M_Msk (0x0004) +#define PHY_SCSR_LAN87XX_100M_Msk (0x0008) +#define PHY_SCSR_LAN87XX_DUPLEX_Msk (0x0010) #define PHY_SCSR_DP838XX (0x0010) #define PHY_RECR_DP838XX (0x0015) #define PHY_SCSR_DP838XX_DUPLEX_Msk (4) #define PHY_SCSR_DP838XX_10M_Msk (2) +#define PHY_RTL8211_DEFAULT_PAGE (0xa42) +#define PHY_RTL8211_PAGSR_ADDR (0x1f) +#define PHY_RTL8211_PHYSR_PAGE (0xa43) +#define PHY_RTL8211_PHYSR_ADDR (0x1a) +#define PHY_RTL8211_PHYSR_SPEED_Pos (4) +#define PHY_RTL8211_PHYSR_SPEED_Msk (3 << PHY_RTL8211_PHYSR_SPEED_Pos) +#define PHY_RTL8211_PHYSR_DUPLEX_Msk (0x0008) +#define PHY_RTL8211_LCR_PAGE (0xd04) +#define PHY_RTL8211_LCR_ADDR (0x10) + +void eth_phy_generic_init(uint32_t phy_addr) { + // Reset the PHY. + eth_phy_write(phy_addr, PHY_BCR, PHY_BCR_SOFT_RESET); + mp_hal_delay_ms(50); +} + int16_t eth_phy_lan87xx_get_link_status(uint32_t phy_addr) { // Get the link mode & speed - int16_t scsr = eth_phy_read(phy_addr, PHY_SCSR_LAN87XX); - return (scsr >> PHY_SCSR_LAN87XX_SPEED_Pos) & PHY_SCSR_LAN87XX_SPEED_Msk; + uint16_t scsr = eth_phy_read(phy_addr, PHY_SCSR_LAN87XX); + int16_t status = 0; + if (scsr & PHY_SCSR_LAN87XX_10M_Msk) { + status |= PHY_SPEED_10HALF; + } + if (scsr & PHY_SCSR_LAN87XX_100M_Msk) { + status |= PHY_SPEED_100HALF; + } + if (scsr & PHY_SCSR_LAN87XX_DUPLEX_Msk) { + status |= PHY_DUPLEX; + } + return status; } int16_t eth_phy_dp838xx_get_link_status(uint32_t phy_addr) { @@ -56,4 +83,37 @@ int16_t eth_phy_dp838xx_get_link_status(uint32_t phy_addr) { return scsr; } +void eth_phy_rtl8211_init(uint32_t phy_addr) { + // Perform generic PHY initialization. + eth_phy_generic_init(phy_addr); + + // Configure LED0 output to show 10/100/1000 link speed, and activity. + eth_phy_write(phy_addr, PHY_RTL8211_PAGSR_ADDR, PHY_RTL8211_LCR_PAGE); + eth_phy_write(phy_addr, PHY_RTL8211_LCR_ADDR, 0x001b); + eth_phy_write(phy_addr, PHY_RTL8211_PAGSR_ADDR, PHY_RTL8211_DEFAULT_PAGE); +} + +int16_t eth_phy_rtl8211_get_link_status(uint32_t phy_addr) { + // Get the link mode & speed + eth_phy_write(phy_addr, PHY_RTL8211_PAGSR_ADDR, PHY_RTL8211_PHYSR_PAGE); + int16_t physr = eth_phy_read(phy_addr, PHY_RTL8211_PHYSR_ADDR); + eth_phy_write(phy_addr, PHY_RTL8211_PAGSR_ADDR, PHY_RTL8211_DEFAULT_PAGE); + int16_t status = 0; + switch ((physr & PHY_RTL8211_PHYSR_SPEED_Msk) >> PHY_RTL8211_PHYSR_SPEED_Pos) { + case 0: + status |= PHY_SPEED_10HALF; + break; + case 1: + status |= PHY_SPEED_100HALF; + break; + case 2: + status |= PHY_SPEED_1000HALF; + break; + } + if (physr & PHY_RTL8211_PHYSR_DUPLEX_Msk) { + status |= PHY_DUPLEX; + } + return status; +} + #endif diff --git a/ports/stm32/eth_phy.h b/ports/stm32/eth_phy.h index dccfb7951..7d4bf4c46 100644 --- a/ports/stm32/eth_phy.h +++ b/ports/stm32/eth_phy.h @@ -50,17 +50,22 @@ #define PHY_ANAR_SPEED_100FULL (0x0100) #define PHY_ANAR_IEEE802_3 (0x0001) -#define PHY_SPEED_10HALF (1) -#define PHY_SPEED_10FULL (5) -#define PHY_SPEED_100HALF (2) -#define PHY_SPEED_100FULL (6) -#define PHY_DUPLEX (4) +#define PHY_SPEED_10HALF (0x01) +#define PHY_SPEED_100HALF (0x02) +#define PHY_SPEED_1000HALF (0x04) +#define PHY_DUPLEX (0x08) +#define PHY_SPEED_10FULL (PHY_DUPLEX | PHY_SPEED_10HALF) +#define PHY_SPEED_100FULL (PHY_DUPLEX | PHY_SPEED_100HALF) +#define PHY_SPEED_1000FULL (PHY_DUPLEX | PHY_SPEED_1000HALF) uint32_t eth_phy_read(uint32_t phy_addr, uint32_t reg); void eth_phy_write(uint32_t phy_addr, uint32_t reg, uint32_t val); +void eth_phy_generic_init(uint32_t phy_addr); int16_t eth_phy_lan87xx_get_link_status(uint32_t phy_addr); int16_t eth_phy_dp838xx_get_link_status(uint32_t phy_addr); +void eth_phy_rtl8211_init(uint32_t phy_addr); +int16_t eth_phy_rtl8211_get_link_status(uint32_t phy_addr); #endif diff --git a/ports/stm32/lwip_inc/lwipopts.h b/ports/stm32/lwip_inc/lwipopts.h index ad1143845..5711ba894 100644 --- a/ports/stm32/lwip_inc/lwipopts.h +++ b/ports/stm32/lwip_inc/lwipopts.h @@ -1,6 +1,8 @@ #ifndef MICROPY_INCLUDED_STM32_LWIP_LWIPOPTS_H #define MICROPY_INCLUDED_STM32_LWIP_LWIPOPTS_H +#include STM32_HAL_H + #define LWIP_NETIF_EXT_STATUS_CALLBACK 1 #define LWIP_LOOPIF_MULTICAST 1 @@ -12,11 +14,12 @@ // Increase memory for lwIP to get better performance. #if defined(STM32N6) -#define MEM_SIZE (16 * 1024) +#define MEM_SIZE (64 * 1024) +#define PBUF_POOL_SIZE (32) #define TCP_MSS (1460) -#define TCP_WND (8 * TCP_MSS) -#define TCP_SND_BUF (8 * TCP_MSS) -#define MEMP_NUM_TCP_SEG (32) +#define TCP_WND (16 * TCP_MSS) +#define TCP_SND_BUF (16 * TCP_MSS) +#define MEMP_NUM_TCP_SEG (64) #endif // Include common lwIP configuration. diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 2d97adb1d..6f7413694 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -318,11 +318,14 @@ static void risaf_init(void) { rimc_master.MasterCID = RIF_CID_1; rimc_master.SecPriv = RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV; - HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_ADC12, RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV); HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_SDMMC1, &rimc_master); - HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_SDMMC1, RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV); HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_SDMMC2, &rimc_master); + HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_ETH1, &rimc_master); + + HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_ADC12, RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV); + HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_SDMMC1, RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV); HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_SDMMC2, RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV); + HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_ETH1, RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV); } #endif diff --git a/ports/stm32/network_lan.c b/ports/stm32/network_lan.c index 0ef33e297..ea03329ad 100644 --- a/ports/stm32/network_lan.c +++ b/ports/stm32/network_lan.c @@ -33,6 +33,11 @@ #include "lwip/netif.h" +// A board can customize the default PHY by defining this setting. +#ifndef NETWORK_LAN_PHY +#define NETWORK_LAN_PHY ETH_PHY_LAN8742 +#endif + typedef struct _network_lan_obj_t { mp_obj_base_t base; eth_t *eth; @@ -57,7 +62,7 @@ static mp_obj_t network_lan_make_new(const mp_obj_type_t *type, size_t n_args, s enum { ARG_phy_addr, ARG_phy_type}; static const mp_arg_t allowed_args[] = { { MP_QSTR_phy_addr, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_phy_type, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = ETH_PHY_LAN8742} }, + { MP_QSTR_phy_type, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NETWORK_LAN_PHY} }, }; // Parse args. mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; diff --git a/ports/stm32/powerctrlboot.c b/ports/stm32/powerctrlboot.c index a8ef8c34a..7baaa6773 100644 --- a/ports/stm32/powerctrlboot.c +++ b/ports/stm32/powerctrlboot.c @@ -482,6 +482,11 @@ void SystemClock_Config(void) { LL_RCC_IC11_SetDivider(1); LL_RCC_IC11_Enable(); + // Configure IC12 at 100MHz for ETH1CLKSEL. + LL_RCC_IC12_SetSource(LL_RCC_ICCLKSOURCE_PLL1); + LL_RCC_IC12_SetDivider(8); + LL_RCC_IC12_Enable(); + // Configure IC14 at 100MHz for slower peripherals. LL_RCC_IC14_SetSource(LL_RCC_ICCLKSOURCE_PLL1); LL_RCC_IC14_SetDivider(8); diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 81751bf0f..70e4f3391 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -220,6 +220,7 @@ SRC_C += \ SHARED_SRC_C += $(addprefix shared/,\ runtime/gchelper_generic.c \ + runtime/pyexec.c \ timeutils/timeutils.c \ $(SHARED_SRC_C_EXTRA) \ ) diff --git a/ports/unix/main.c b/ports/unix/main.c index db50e12f5..ecc692449 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -55,9 +55,10 @@ #include "genhdr/mpversion.h" #include "input.h" #include "stack_size.h" +#include "shared/runtime/pyexec.h" // Command line options, with their defaults -static bool compile_only = false; +bool mp_compile_only = false; static uint emit_opt = MP_EMIT_OPT_NONE; #if MICROPY_ENABLE_GC @@ -110,8 +111,6 @@ static int handle_uncaught_exception(mp_obj_base_t *exc) { } #define LEX_SRC_STR (1) -#define LEX_SRC_VSTR (2) -#define LEX_SRC_FILENAME (3) #define LEX_SRC_STDIN (4) // Returns standard error codes: 0 for success, 1 for all other errors, @@ -127,12 +126,6 @@ static int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu if (source_kind == LEX_SRC_STR) { const char *line = source; lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line, strlen(line), false); - } else if (source_kind == LEX_SRC_VSTR) { - const vstr_t *vstr = source; - lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, false); - } else if (source_kind == LEX_SRC_FILENAME) { - const char *filename = (const char *)source; - lex = mp_lexer_new_from_file(qstr_from_str(filename)); } else { // LEX_SRC_STDIN lex = mp_lexer_new_from_fd(MP_QSTR__lt_stdin_gt_, 0, false); } @@ -158,7 +151,7 @@ static int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu mp_obj_t module_fun = mp_compile(&parse_tree, source_name, is_repl); - if (!compile_only) { + if (!mp_compile_only) { // execute it mp_call_function_0(module_fun); } @@ -195,95 +188,32 @@ static char *strjoin(const char *s1, int sep_char, const char *s2) { #endif static int do_repl(void) { - mp_hal_stdout_tx_str(MICROPY_BANNER_NAME_AND_VERSION); - mp_hal_stdout_tx_str("; " MICROPY_BANNER_MACHINE); - mp_hal_stdout_tx_str("\nUse Ctrl-D to exit, Ctrl-E for paste mode\n"); - #if MICROPY_USE_READLINE == 1 - // use MicroPython supplied readline + // use MicroPython supplied readline-based REPL - vstr_t line; - vstr_init(&line, 16); + int ret = 0; for (;;) { - mp_hal_stdio_mode_raw(); - - input_restart: - // If the GC is locked at this point there is no way out except a reset, - // so force the GC to be unlocked to help the user debug what went wrong. - MP_STATE_THREAD(gc_lock_depth) = 0; - vstr_reset(&line); - int ret = readline(&line, mp_repl_get_ps1()); - mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT; - - if (ret == CHAR_CTRL_C) { - // cancel input - mp_hal_stdout_tx_str("\r\n"); - goto input_restart; - } else if (ret == CHAR_CTRL_D) { - // EOF - printf("\n"); - mp_hal_stdio_mode_orig(); - vstr_clear(&line); - return 0; - } else if (ret == CHAR_CTRL_E) { - // paste mode - mp_hal_stdout_tx_str("\npaste mode; Ctrl-C to cancel, Ctrl-D to finish\n=== "); - vstr_reset(&line); - for (;;) { - char c = mp_hal_stdin_rx_chr(); - if (c == CHAR_CTRL_C) { - // cancel everything - mp_hal_stdout_tx_str("\n"); - goto input_restart; - } else if (c == CHAR_CTRL_D) { - // end of input - mp_hal_stdout_tx_str("\n"); - break; - } else { - // add char to buffer and echo - vstr_add_byte(&line, c); - if (c == '\r') { - mp_hal_stdout_tx_str("\n=== "); - } else { - mp_hal_stdout_tx_strn(&c, 1); - } - } - } - parse_input_kind = MP_PARSE_FILE_INPUT; - } else if (line.len == 0) { - if (ret != 0) { - printf("\n"); + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if ((ret = pyexec_raw_repl()) != 0) { + break; } - goto input_restart; } else { - // got a line with non-zero length, see if it needs continuing - while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) { - vstr_add_byte(&line, '\n'); - ret = readline(&line, mp_repl_get_ps2()); - if (ret == CHAR_CTRL_C) { - // cancel everything - printf("\n"); - goto input_restart; - } else if (ret == CHAR_CTRL_D) { - // stop entering compound statement - break; - } + if ((ret = pyexec_friendly_repl()) != 0) { + break; } } - - mp_hal_stdio_mode_orig(); - - ret = execute_from_lexer(LEX_SRC_VSTR, &line, parse_input_kind, true); - if (ret & FORCED_EXIT) { - return ret; - } } + return ret; #else // use simple readline + mp_hal_stdout_tx_str(MICROPY_BANNER_NAME_AND_VERSION); + mp_hal_stdout_tx_str("; " MICROPY_BANNER_MACHINE); + mp_hal_stdout_tx_str("\nUse Ctrl-D to exit, Ctrl-E for paste mode\n"); + for (;;) { char *line = prompt((char *)mp_repl_get_ps1()); if (line == NULL) { @@ -311,12 +241,40 @@ static int do_repl(void) { #endif } +static inline int convert_pyexec_result(int ret) { + #if MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING + // With exit code handling enabled: + // pyexec returns exit code with PYEXEC_FORCED_EXIT flag set for SystemExit + // Unix port expects: 0 for success, non-zero for error/exit + if (ret & PYEXEC_FORCED_EXIT) { + // SystemExit: extract exit code from lower bits + return ret & 0xFF; + } + // Normal execution or exception: return as-is (0 for success, 1 for exception) + return ret; + #else + // pyexec returns 1 for success, 0 for exception, PYEXEC_FORCED_EXIT for SystemExit + // Convert to unix port's expected codes: 0 for success, 1 for exception, FORCED_EXIT|val for SystemExit + if (ret == 1) { + return 0; // success + } else if (ret & PYEXEC_FORCED_EXIT) { + return ret; // SystemExit with exit value in lower 8 bits + } else { + return 1; // exception + } + #endif +} + static int do_file(const char *file) { - return execute_from_lexer(LEX_SRC_FILENAME, file, MP_PARSE_FILE_INPUT, false); + return convert_pyexec_result(pyexec_file(file)); } static int do_str(const char *str) { - return execute_from_lexer(LEX_SRC_STR, str, MP_PARSE_FILE_INPUT, false); + vstr_t vstr; + vstr.buf = (char *)str; + vstr.len = strlen(str); + int ret = pyexec_vstr(&vstr, true); + return convert_pyexec_result(ret); } static void print_help(char **argv) { @@ -385,7 +343,7 @@ static void pre_process_options(int argc, char **argv) { } if (0) { } else if (strcmp(argv[a + 1], "compile-only") == 0) { - compile_only = true; + mp_compile_only = true; } else if (strcmp(argv[a + 1], "emit=bytecode") == 0) { emit_opt = MP_EMIT_OPT_BYTECODE; #if MICROPY_EMIT_NATIVE diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index f07439863..854da1dbd 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -151,6 +151,12 @@ typedef long mp_off_t; // Enable sys.executable. #define MICROPY_PY_SYS_EXECUTABLE (1) +// Enable support for compile-only mode. +#define MICROPY_PYEXEC_COMPILE_ONLY (1) + +// Enable handling of sys.exit() exit codes. +#define MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING (1) + #define MICROPY_PY_SOCKET_LISTEN_BACKLOG_DEFAULT (SOMAXCONN < 128 ? SOMAXCONN : 128) // Bare-metal ports don't have stderr. Printing debug to stderr may give tests diff --git a/ports/unix/mphalport.h b/ports/unix/mphalport.h index 0efd6940b..97c24978a 100644 --- a/ports/unix/mphalport.h +++ b/ports/unix/mphalport.h @@ -25,6 +25,7 @@ */ #include <errno.h> #include <unistd.h> +#include <stdbool.h> #ifndef CHAR_CTRL_C #define CHAR_CTRL_C (3) @@ -47,6 +48,9 @@ MP_THREAD_GIL_ENTER(); \ } while (0) +// The port provides `mp_hal_stdio_mode_raw()` and `mp_hal_stdio_mode_orig()`. +#define MICROPY_HAL_HAS_STDIO_MODE_SWITCH (1) + void mp_hal_set_interrupt_char(char c); #define mp_hal_stdio_poll unused // this is not implemented, nor needed @@ -117,3 +121,6 @@ enum { void mp_hal_get_mac(int idx, uint8_t buf[6]); #endif + +// Global variable to control compile-only mode. +extern bool mp_compile_only; diff --git a/ports/windows/Makefile b/ports/windows/Makefile index 9eee98cdd..4129b7fe2 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -83,6 +83,7 @@ OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) ifeq ($(MICROPY_USE_READLINE),1) CFLAGS += -DMICROPY_USE_READLINE=1 SRC_C += shared/readline/readline.c +SRC_C += shared/runtime/pyexec.c endif LIB += -lws2_32 diff --git a/ports/windows/micropython.vcxproj b/ports/windows/micropython.vcxproj index 9326f3f4c..f8bbec82c 100644 --- a/ports/windows/micropython.vcxproj +++ b/ports/windows/micropython.vcxproj @@ -89,6 +89,7 @@ <ClCompile Include="@(PyExtModSource)" /> <ClCompile Include="$(PyBaseDir)shared\readline\*.c" /> <ClCompile Include="$(PyBaseDir)shared\runtime\gchelper_generic.c" /> + <ClCompile Include="$(PyBaseDir)shared\runtime\pyexec.c" /> <ClCompile Include="$(PyBaseDir)ports\windows\*.c" /> <ClCompile Include="$(PyBaseDir)ports\windows\msvc\*.c" /> <ClCompile Include="$(PyBaseDir)ports\unix\gccollect.c"/> diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index 68744ba93..1edcd0ede 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -161,6 +161,12 @@ #define MICROPY_MACHINE_MEM_GET_READ_ADDR mod_machine_mem_get_addr #define MICROPY_MACHINE_MEM_GET_WRITE_ADDR mod_machine_mem_get_addr +// Enable support for compile-only mode. +#define MICROPY_PYEXEC_COMPILE_ONLY (1) + +// Enable handling of sys.exit() exit codes. +#define MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING (1) + #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) #define MICROPY_ERROR_PRINTER (&mp_stderr_print) #define MICROPY_WARNINGS (1) diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index e7a55d804..9db0a95c6 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -55,6 +55,7 @@ set(MICROPY_SOURCE_PORT uart_core.c zephyr_device.c zephyr_storage.c + zephyr_filesystem.c mpthreadport.c ) list(TRANSFORM MICROPY_SOURCE_PORT PREPEND ${MICROPY_PORT_DIR}/) @@ -81,14 +82,33 @@ set(MICROPY_QSTRDEFS_PORT ${MICROPY_PORT_DIR}/qstrdefsport.h ) -set(MICROPY_SOURCE_LIB +if (CONFIG_MICROPY_VFS_FAT) + +list(APPEND MICROPY_SOURCE_LIB oofatfs/ff.c oofatfs/ffunicode.c +) + +endif() + +if (CONFIG_MICROPY_VFS_LFS1) + +list(APPEND MICROPY_SOURCE_LIB littlefs/lfs1.c littlefs/lfs1_util.c +) + +endif() + +if (CONFIG_MICROPY_VFS_LFS2) + +list(APPEND MICROPY_SOURCE_LIB littlefs/lfs2.c littlefs/lfs2_util.c ) + +endif() + list(TRANSFORM MICROPY_SOURCE_LIB PREPEND ${MICROPY_DIR}/lib/) set(MICROPY_SOURCE_QSTR diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md index eedcdcb58..f3fcd8f70 100644 --- a/ports/zephyr/README.md +++ b/ports/zephyr/README.md @@ -208,3 +208,47 @@ run the following after you built an image with the previous command: $ west build -t run +File Systems +------------ + +The Zephyr Micropython port provides 2 options for handling filesystems on the device: +The first is the Micropython filesystem management, which uses Micropython's filesystem code and +relies on zephyr's FlashArea API, this is enabled by default when +`CONFIG_FLASH` and `CONFIG_FLASH_MAP` are turned on. +The second option is using Zephyr's Filesystem management: +This relies on Zephyr's File System features and enables sharing access between zephyr and +micropython code. Several configuration options must be enabled: + + CONFIG_FLASH_MAP=y # Requirement for the file system subsystem + CONFIG_FILE_SYSTEM=y # Enables the file system subsystem + CONFIG_FILE_SYSTEM_LITTLEFS=y # Enables the littlefs support in zephyr + CONFIG_FILE_SYSTEM_MKFS=y # Enables the ability to create new file systems + +Then, a fstab must be added to the dts overlay, for example: + + + fstab { + compatible = "zephyr,fstab"; + lfs2: lfs2 { + compatible = "zephyr,fstab,littlefs"; + mount-point = "/flash"; + partition = <&storage_partition>; + read-size=<16>; + prog-size=<4096>; + cache-size=<4096>; + lookahead-size=<32>; + block-cycles=<4>; + }; + }; + +It is then possible to use the FS like a normal Micropython filesystem: + + import vfs, zephyr + zfs = zephyr.FileSystem(zephyr.FileSystem.fstab()[0]) + vfs.mount(zfs, "/zephyr") + +You may disable Micropython's File system code to save space: + + CONFIG_MICROPY_VFS_FAT=n + CONFIG_MICROPY_VFS_LFS1=n + CONFIG_MICROPY_VFS_LFS2=n diff --git a/ports/zephyr/boards/rpi_pico.conf b/ports/zephyr/boards/rpi_pico.conf index 683279ddc..6a0be1c37 100644 --- a/ports/zephyr/boards/rpi_pico.conf +++ b/ports/zephyr/boards/rpi_pico.conf @@ -18,3 +18,13 @@ CONFIG_SPI=y # MicroPython config. CONFIG_MICROPY_HEAP_SIZE=196608 + +# File System Configuration +CONFIG_FILE_SYSTEM=y +CONFIG_FILE_SYSTEM_LITTLEFS=y +CONFIG_FILE_SYSTEM_MKFS=y +CONFIG_MICROPY_VFS_FAT=n +CONFIG_MICROPY_VFS_LFS1=n +CONFIG_MICROPY_VFS_LFS2=n +# Default heap for littlefs is too small +CONFIG_FS_LITTLEFS_FC_HEAP_SIZE=8192 diff --git a/ports/zephyr/boards/rpi_pico.overlay b/ports/zephyr/boards/rpi_pico.overlay index d63ed73bd..436e4210a 100644 --- a/ports/zephyr/boards/rpi_pico.overlay +++ b/ports/zephyr/boards/rpi_pico.overlay @@ -9,6 +9,20 @@ /* Use USB CDC ACM as the console. */ zephyr,console = &cdc_acm_uart0; }; + + fstab { + compatible = "zephyr,fstab"; + lfs: lfs { + compatible = "zephyr,fstab,littlefs"; + mount-point = "/flash"; + partition = <&storage_partition>; + read-size=<16>; + prog-size=<256>; + cache-size=<1024>; + lookahead-size=<32>; + block-cycles=<4>; + }; + }; }; /* Delete defined partitions and make a layout matching the rp2 port RPI_PICO configuration. */ diff --git a/ports/zephyr/modules/_boot.py b/ports/zephyr/modules/_boot.py index d0ba21d3f..ab3ed9c63 100644 --- a/ports/zephyr/modules/_boot.py +++ b/ports/zephyr/modules/_boot.py @@ -11,12 +11,39 @@ FlashArea = getattr(zephyr, "FlashArea", None) # DiskAccess depends on CONFIG_DISK_ACCESS DiskAccess = getattr(zephyr, "DiskAccess", None) +# zephyr.FileSystem depends on CONFIG_FILE_SYSTEM and CONFIG_FLASH_MAP +FileSystem = getattr(zephyr, "FileSystem", None) + _FLASH = const("/flash") _FLASH_LIB = const("/flash/lib") _STORAGE_KEY = const("storage") _FLASH_EXISTS = False +def mount_filesystem_flash(): + """Create a filesystem if needed on the FS partition "/flash" + and mount it on /flash. + Return True if successful, False otherwise. + """ + if _FLASH in FileSystem.fstab(): + fs = FileSystem(_FLASH) + retval = True + try: + vfs.mount(fs, _FLASH) + except OSError: + if getattr(fs, "mkfs", None): + try: + fs.mkfs() + vfs.mount(fs, _FLASH) + except OSError: + print("Error formatting flash partition") + retval = False + else: + retval = False + return retval + return False + + def create_flash_partition(): """Create an LFS2 filesystem on the partition labeled storage and mount it on /flash. @@ -54,7 +81,10 @@ def mount_all_disks(): return retval -if FlashArea and create_flash_partition(): +# Prefer FileSystem over FlashArea Access +if FileSystem and mount_filesystem_flash(): + _FLASH_EXISTS = True +elif FlashArea and create_flash_partition(): _FLASH_EXISTS = True # Prefer disks to /flash @@ -67,6 +97,6 @@ if _FLASH_EXISTS: sys.path.append(_FLASH_LIB) # Cleanup globals for boot.py/main.py -del FlashArea, DiskAccess, zephyr +del FlashArea, DiskAccess, FileSystem, zephyr del sys, vfs, os, const -del create_flash_partition, mount_all_disks, _FLASH_EXISTS +del mount_filesystem_flash, create_flash_partition, mount_all_disks, _FLASH_EXISTS diff --git a/ports/zephyr/modzephyr.c b/ports/zephyr/modzephyr.c index 08fdf5c5a..7726737e1 100644 --- a/ports/zephyr/modzephyr.c +++ b/ports/zephyr/modzephyr.c @@ -86,6 +86,9 @@ static const mp_rom_map_elem_t mp_module_time_globals_table[] = { #ifdef CONFIG_FLASH_MAP { MP_ROM_QSTR(MP_QSTR_FlashArea), MP_ROM_PTR(&zephyr_flash_area_type) }, #endif + #ifdef CONFIG_FILE_SYSTEM + { MP_ROM_QSTR(MP_QSTR_FileSystem), MP_ROM_PTR(&zephyr_filesystem_type) }, + #endif }; static MP_DEFINE_CONST_DICT(mp_module_time_globals, mp_module_time_globals_table); diff --git a/ports/zephyr/modzephyr.h b/ports/zephyr/modzephyr.h index f9b7e8eea..834ec4a31 100644 --- a/ports/zephyr/modzephyr.h +++ b/ports/zephyr/modzephyr.h @@ -37,4 +37,8 @@ extern const mp_obj_type_t zephyr_disk_access_type; extern const mp_obj_type_t zephyr_flash_area_type; #endif +#ifdef CONFIG_FILE_SYSTEM +extern const mp_obj_type_t zephyr_filesystem_type; +#endif + #endif // MICROPY_INCLUDED_ZEPHYR_MODZEPHYR_H diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index 8d5d60ed2..7f8497348 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -84,9 +84,10 @@ #endif #define MICROPY_PY_MACHINE_PWM (1) #define MICROPY_PY_MACHINE_PWM_INCLUDEFILE "ports/zephyr/machine_pwm.c" -#ifdef CONFIG_NETWORKING -// If we have networking, we likely want errno comfort +#if defined(CONFIG_NETWORKING) || defined(CONFIG_FILE_SYSTEM) #define MICROPY_PY_ERRNO (1) +#endif +#ifdef CONFIG_NETWORKING #define MICROPY_PY_SOCKET (1) #endif #ifdef CONFIG_BT diff --git a/ports/zephyr/zephyr_filesystem.c b/ports/zephyr/zephyr_filesystem.c new file mode 100644 index 000000000..2ca726253 --- /dev/null +++ b/ports/zephyr/zephyr_filesystem.c @@ -0,0 +1,782 @@ +/* +* This file is part of the MicroPython project, http://micropython.org/ +* +* The MIT License (MIT) +* +* Copyright (c) 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 "py/mpconfig.h" + +#if CONFIG_FILE_SYSTEM + +#if !MICROPY_VFS +#error "with CONFIG_FILE_SYSTEM enabled, must also enable MICROPY_VFS" +#endif + +#include <zephyr/fs/fs.h> +#include <zephyr/fs/fs_sys.h> + +#include <string.h> +#include "py/obj.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "extmod/vfs.h" +#include "shared/timeutils/timeutils.h" + +// Declare each FSTAB entry +#define FOREACH_FS_DEFINE(fs) FS_FSTAB_DECLARE_ENTRY(fs); + +#define FOREACH_FSTAB_DEFINE(n) DT_FOREACH_CHILD(n, FOREACH_FS_DEFINE) + +DT_FOREACH_STATUS_OKAY(zephyr_fstab, FOREACH_FSTAB_DEFINE) + +// Add all FSTAB entries to a table for us to use dynamically +#define FOREACH_FS(fs) FS_FSTAB_ENTRY(fs) + +#define FOREACH_FSTAB(n) DT_FOREACH_CHILD(n, FOREACH_FS) + +static struct fs_mount_t *const zephyr_fs_mounts[] = { + &DT_FOREACH_STATUS_OKAY(zephyr_fstab, FOREACH_FSTAB), +}; + +#define zephyr_fs_mounts_size sizeof(zephyr_fs_mounts) / sizeof(struct fs_mount_t *) + +typedef struct _zephyr_fs_obj_t { + mp_obj_base_t base; + struct fs_mount_t *mount; + vstr_t cur_dir; + vstr_t root_dir; +} zephyr_fs_obj_t; + +const char *zephyr_fs_make_path(zephyr_fs_obj_t *self, mp_obj_t path_in) { + const char *path = mp_obj_str_get_str(path_in); + + if (path[0] != '/') { + size_t l = vstr_len(&self->root_dir); + size_t lc = vstr_len(&self->cur_dir); + vstr_add_str(&self->root_dir, "/"); + vstr_add_strn(&self->root_dir, self->cur_dir.buf, lc); + vstr_add_str(&self->root_dir, path); + path = vstr_null_terminated_str(&self->root_dir); + self->root_dir.len = l; + } else { + size_t l = vstr_len(&self->root_dir); + vstr_add_str(&self->root_dir, path); + path = vstr_null_terminated_str(&self->root_dir); + self->root_dir.len = l; + } + return path; +} + +typedef struct _zephyr_file_obj_t { + mp_obj_base_t base; + struct fs_file_t fp; +} zephyr_file_obj_t; + +static void file_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + mp_printf(print, "<io.%s %p>", mp_obj_get_type_str(self_in), MP_OBJ_TO_PTR(self_in)); +} + +static mp_uint_t file_obj_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { + zephyr_file_obj_t *self = MP_OBJ_TO_PTR(self_in); + ssize_t err; + + err = fs_read(&self->fp, buf, size); + if (err < 0) { + *errcode = -err; + return MP_STREAM_ERROR; + } + return err; +} + +static mp_uint_t file_obj_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { + zephyr_file_obj_t *self = MP_OBJ_TO_PTR(self_in); + ssize_t err; + + err = fs_write(&self->fp, buf, size); + if (err < 0) { + *errcode = -err; + return MP_STREAM_ERROR; + } + return err; +} + +static mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { + zephyr_file_obj_t *self = MP_OBJ_TO_PTR(o_in); + int err = -EINVAL; + + if (request == MP_STREAM_SEEK) { + struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)(uintptr_t)arg; + + MP_STATIC_ASSERT(FS_SEEK_SET == MP_SEEK_SET); + MP_STATIC_ASSERT(FS_SEEK_CUR == MP_SEEK_CUR); + MP_STATIC_ASSERT(FS_SEEK_END == MP_SEEK_END); + + err = fs_seek(&self->fp, s->offset, s->whence); + + if (err < 0) { + *errcode = -err; + return MP_STREAM_ERROR; + } + + off_t tell = fs_tell(&self->fp); + if (tell < 0) { + *errcode = -err; + return MP_STREAM_ERROR; + } + s->offset = tell; + return 0; + + } else if (request == MP_STREAM_FLUSH) { + err = fs_sync(&self->fp); + if (err < 0) { + *errcode = -err; + return MP_STREAM_ERROR; + } + return 0; + + } else if (request == MP_STREAM_CLOSE) { + err = fs_close(&self->fp); + if (err < 0) { + *errcode = -err; + return MP_STREAM_ERROR; + } + return 0; + + } else { + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; + } +} + +static const mp_rom_map_elem_t zephyr_fs_rawfile_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) }, + { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&mp_stream___exit___obj) }, +}; + +static MP_DEFINE_CONST_DICT(zephyr_fs_rawfile_locals_dict, zephyr_fs_rawfile_locals_dict_table); + +static const mp_stream_p_t zephyr_fs_fileio_stream_p = { + .read = file_obj_read, + .write = file_obj_write, + .ioctl = file_obj_ioctl, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_zephyr_fs_fileio, + MP_QSTR_FileIO, + MP_TYPE_FLAG_ITER_IS_STREAM, + print, file_obj_print, + protocol, &zephyr_fs_fileio_stream_p, + locals_dict, &zephyr_fs_rawfile_locals_dict + ); + +static const mp_stream_p_t zephyr_fs_textio_stream_p = { + .read = file_obj_read, + .write = file_obj_write, + .ioctl = file_obj_ioctl, + .is_text = true, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_zephyr_fs_textio, + MP_QSTR_TextIOWrapper, + MP_TYPE_FLAG_ITER_IS_STREAM, + print, file_obj_print, + protocol, &zephyr_fs_textio_stream_p, + locals_dict, &zephyr_fs_rawfile_locals_dict + ); + +// Factory function for I/O stream classes +static mp_obj_t zephyr_fs_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(self_in); + const mp_obj_type_t *type = &mp_type_zephyr_fs_textio; + int flags = 0; + int err; + bool x_mode = false; + const char *mode_str = mp_obj_str_get_str(mode_in); + const char *path = zephyr_fs_make_path(self, path_in); + + for (; *mode_str; ++mode_str) { + int new_flags = 0; + switch (*mode_str) { + case 'r': + new_flags = FS_O_READ; + break; + case 'w': + new_flags = FS_O_WRITE | FS_O_CREATE | FS_O_TRUNC; + break; + case 'x': + new_flags = FS_O_WRITE; + x_mode = true; + break; + case 'a': + new_flags = FS_O_WRITE | FS_O_CREATE | FS_O_APPEND; + break; + case '+': + flags |= FS_O_RDWR; + break; + case 'b': + type = &mp_type_zephyr_fs_fileio; + break; + case 't': + type = &mp_type_zephyr_fs_textio; + break; + } + if (new_flags) { + if (flags) { + mp_raise_ValueError(NULL); + } + flags = new_flags; + } + } + + zephyr_file_obj_t *o = mp_obj_malloc_with_finaliser(zephyr_file_obj_t, type); + + err = fs_open(&o->fp, path, flags); + if (x_mode && err < 0) { + err = fs_open(&o->fp, path, flags | FS_O_CREATE); + } else if (x_mode) { + fs_close(&o->fp); + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("file %s Already exists: %q"), path, mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + return mp_const_none; + } + if (err < 0) { + m_del_obj(zephyr_file_obj_t, o); + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("error opening file %s: %q"), path, mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + return mp_const_none; + } + + return MP_OBJ_FROM_PTR(o); +} +MP_DEFINE_CONST_FUN_OBJ_3(zephyr_fs_open_obj, zephyr_fs_open); + +typedef struct _mp_zephyr_fs_ilistdir_it_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_fun_1_t finaliser; + bool is_str; + struct fs_dir_t dir; +} mp_zephyr_fs_ilistdir_it_t; + +static mp_obj_t mp_zephyr_fs_ilistdir_it_iternext(mp_obj_t self_in) { + mp_zephyr_fs_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in); + int err; + + for (;;) { + struct fs_dirent stats; + err = fs_readdir(&self->dir, &stats); + char *fn = stats.name; + if (err < 0 || stats.name[0] == 0) { + // stop on error or end of dir + break; + } + + // Note that Zephyr FS also already filters . and .., so we don't need to + + // make 4-tuple with info about this entry + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(4, NULL)); + if (self->is_str) { + t->items[0] = mp_obj_new_str_from_cstr(fn); + } else { + t->items[0] = mp_obj_new_bytes((const byte *)fn, strlen(fn)); + } + if (stats.type == FS_DIR_ENTRY_DIR) { + // dir + t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); + } else { + // file + t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG); + } + t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number + t->items[3] = mp_obj_new_int_from_uint(stats.size); + + return MP_OBJ_FROM_PTR(t); + } + + // ignore error because we may be closing a second time + fs_closedir(&self->dir); + + return MP_OBJ_STOP_ITERATION; +} + +static mp_obj_t mp_zephyr_fs_ilistdir_it_del(mp_obj_t self_in) { + mp_zephyr_fs_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in); + // ignore result / error because we may be closing a second time. + fs_closedir(&self->dir); + return mp_const_none; +} + +static mp_obj_t zephyr_fs_ilistdir_func(size_t n_args, const mp_obj_t *args) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(args[0]); + bool is_str_type = true; + const char *path; + int err; + + if (n_args == 2) { + if (mp_obj_get_type(args[1]) == &mp_type_bytes) { + is_str_type = false; + } + path = zephyr_fs_make_path(self, args[1]); + } else { + path = ""; + } + + // Create a new iterator object to list the dir + mp_zephyr_fs_ilistdir_it_t *iter = mp_obj_malloc_with_finaliser(mp_zephyr_fs_ilistdir_it_t, &mp_type_polymorph_iter_with_finaliser); + iter->iternext = mp_zephyr_fs_ilistdir_it_iternext; + iter->finaliser = mp_zephyr_fs_ilistdir_it_del; + iter->is_str = is_str_type; + + err = fs_opendir(&iter->dir, path); + if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("could not open directory %s: %q"), path, mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + return mp_const_none; + } + + return MP_OBJ_FROM_PTR(iter); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(zephyr_fs_ilistdir_obj, 1, 2, zephyr_fs_ilistdir_func); + +static mp_obj_t zephyr_fs_mkdir(mp_obj_t vfs_in, mp_obj_t path_o) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(vfs_in); + const char *path = zephyr_fs_make_path(self, path_o); + int err; + + err = fs_mkdir(path); + if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("could not mkdir %s: %q"), path, mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + return mp_const_none; + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_fs_mkdir_obj, zephyr_fs_mkdir); + + +static mp_obj_t zephyr_fs_chdir(mp_obj_t vfs_in, mp_obj_t path_in) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(vfs_in); + const char *path = mp_obj_str_get_str(path_in); + size_t lc = vstr_len(&self->cur_dir); + size_t l = vstr_len(&self->root_dir); + vstr_t chdir; + struct fs_dirent stats; + int err; + + + vstr_init(&chdir, 32); + vstr_add_strn(&chdir, self->root_dir.buf, l); + if (path[0] != '/') { + vstr_add_byte(&chdir, '/'); + vstr_add_strn(&chdir, self->cur_dir.buf, lc); + } + vstr_add_str(&chdir, path); + vstr_add_byte(&chdir, '/'); + + #define CWD_LEN (vstr_len(&chdir)) + size_t to = 1; + size_t from = 1; + char *cwd = vstr_str(&chdir); + while (from < CWD_LEN) { + for (; from < CWD_LEN && cwd[from] == '/'; ++from) { + // Scan for the start + } + if (from > to) { + // Found excessive slash chars, squeeze them out + vstr_cut_out_bytes(&chdir, to, from - to); + from = to; + } + for (; from < CWD_LEN && cwd[from] != '/'; ++from) { + // Scan for the next / + } + if ((from - to) == 1 && cwd[to] == '.') { + // './', ignore + vstr_cut_out_bytes(&chdir, to, ++from - to); + from = to; + } else if ((from - to) == 2 && cwd[to] == '.' && cwd[to + 1] == '.') { + // '../', skip back + if (to > 1) { + // Only skip back if not at the tip + for (--to; to > 1 && cwd[to - 1] != '/'; --to) { + // Skip back + } + } + vstr_cut_out_bytes(&chdir, to, ++from - to); + from = to; + } else { + // Normal element, keep it and just move the offset + to = ++from; + } + } + + err = fs_stat(vstr_null_terminated_str(&chdir), &stats); + vstr_cut_out_bytes(&chdir, 0, l + 1); + lc = vstr_len(&chdir); + if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("could not chdir to %s: %q"), path, mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + return mp_const_none; + } + + vstr_reset(&self->cur_dir); + vstr_add_strn(&self->cur_dir, chdir.buf, lc); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_fs_chdir_obj, zephyr_fs_chdir); + +static mp_obj_t zephyr_fs_getcwd(mp_obj_t vfs_in) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(vfs_in); + char *path; + + if (vstr_len(&self->cur_dir) == 0) { + return MP_OBJ_NEW_QSTR(MP_QSTR__slash_); + } else { + size_t l = vstr_len(&self->root_dir); + size_t lc = vstr_len(&self->cur_dir); + vstr_add_str(&self->root_dir, "/"); + vstr_add_strn(&self->root_dir, self->cur_dir.buf, lc); + path = vstr_null_terminated_str(&self->root_dir); + self->root_dir.len = l; + return mp_obj_new_str_from_cstr(path + l); + } +} +static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_fs_getcwd_obj, zephyr_fs_getcwd); + +static mp_obj_t zephyr_fs_rmdir(mp_obj_t vfs_in, mp_obj_t path_in) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(vfs_in); + const char *path = zephyr_fs_make_path(self, path_in); + struct fs_dirent stats; + int err; + + err = fs_stat(path, &stats); + if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("error getting stats of Zephyr File System at %s: %q"), path, mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + return mp_const_none; + } + if (stats.type == FS_DIR_ENTRY_FILE) { + mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("not for removing files")); + return mp_const_none; + } + err = fs_unlink(path); + if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("error removing directory at %s: %q"), path, mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_fs_rmdir_obj, zephyr_fs_rmdir); + +static mp_obj_t zephyr_fs_remove(mp_obj_t vfs_in, mp_obj_t path_in) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(vfs_in); + const char *path = zephyr_fs_make_path(self, path_in); + struct fs_dirent stats; + int err; + + err = fs_stat(path, &stats); + if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("error getting stats of Zephyr File System at %s: %q"), path, mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + return mp_const_none; + } + if (stats.type == FS_DIR_ENTRY_DIR) { + mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("not for removing directories")); + return mp_const_none; + } + err = fs_unlink(path); + if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("error removing file at %s: %q"), path, mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_fs_remove_obj, zephyr_fs_remove); + +static mp_obj_t zephyr_fs_rename(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t path_out) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(vfs_in); + const char *old_path = zephyr_fs_make_path(self, path_in); + const char *new_path = zephyr_fs_make_path(self, path_out); + int err; + + err = fs_rename(old_path, new_path); + if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("error renaming file from %s to %s: %q"), old_path, new_path, mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(zephyr_fs_rename_obj, zephyr_fs_rename); + +static mp_import_stat_t zephyr_fs_import_stat(void *vfs_in, const char *path) { + zephyr_fs_obj_t *self = vfs_in; + struct fs_dirent stats; + assert(self != NULL); + const char *ppath = zephyr_fs_make_path(self, mp_obj_new_str_from_cstr(path)); + int err; + + err = fs_stat(ppath, &stats); + if (err == 0) { + if (stats.type == FS_DIR_ENTRY_DIR) { + return MP_IMPORT_STAT_DIR; + } else { + return MP_IMPORT_STAT_FILE; + } + } + return MP_IMPORT_STAT_NO_EXIST; +} + +static mp_obj_t zephyr_fs_stat(mp_obj_t vfs_in, mp_obj_t path_in) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(vfs_in); + int err; + struct fs_dirent stats; + const char *path = zephyr_fs_make_path(self, path_in); + + err = fs_stat(path, &stats); + if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("error getting stats of Zephyr File System at %s: %q"), path, mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + return mp_const_none; + } + + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); + mp_int_t mode = 0; + if (stats.type == FS_DIR_ENTRY_DIR) { + mode |= MP_S_IFDIR; + } else { + mode |= MP_S_IFREG; + } + t->items[0] = MP_OBJ_NEW_SMALL_INT(mode); // st_mode + t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino + t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev + t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // st_nlink + t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid + t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid + t->items[6] = mp_obj_new_int_from_uint(stats.size); // st_size + t->items[7] = mp_obj_new_int_from_uint(0); // st_atime + t->items[8] = mp_obj_new_int_from_uint(0); // st_mtime + t->items[9] = mp_obj_new_int_from_uint(0); // st_ctime + + return MP_OBJ_FROM_PTR(t); +} +static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_fs_stat_obj, zephyr_fs_stat); + +static mp_obj_t zephyr_fs_statvfs(mp_obj_t vfs_in, mp_obj_t path_in) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(vfs_in); + (void)path_in; + int err; + struct fs_statvfs stats; + unsigned long bsize; + + err = fs_statvfs(self->mount->mnt_point, &stats); + if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("error getting stats of Zephyr File System: %q"), mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + return mp_const_none; + } + + if (stats.f_frsize > stats.f_bsize) { + bsize = stats.f_frsize; + } else { + bsize = stats.f_bsize; + } + + mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); + + t->items[0] = MP_OBJ_NEW_SMALL_INT(bsize); // f_bsize + t->items[1] = MP_OBJ_NEW_SMALL_INT(stats.f_frsize); // f_frsize + t->items[2] = MP_OBJ_NEW_SMALL_INT(stats.f_blocks); // f_blocks + t->items[3] = MP_OBJ_NEW_SMALL_INT(stats.f_bfree); // f_bfree + t->items[4] = t->items[3]; // f_bavail + t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // f_files + t->items[6] = MP_OBJ_NEW_SMALL_INT(0); // f_ffree + t->items[7] = MP_OBJ_NEW_SMALL_INT(0); // f_favail + t->items[8] = MP_OBJ_NEW_SMALL_INT(0); // f_flags + t->items[9] = MP_OBJ_NEW_SMALL_INT(MAX_FILE_NAME); // f_namemax. + + return MP_OBJ_FROM_PTR(t); +} +static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_fs_statvfs_obj, zephyr_fs_statvfs); + +static mp_obj_t zephyr_fs_umount(mp_obj_t self_in) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(self_in); + int err; + + err = fs_unmount(self->mount); + if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("error un-mounting Zephyr File System: %q"), mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + return mp_const_none; + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_fs_umount_obj, zephyr_fs_umount); + +static mp_obj_t zephyr_fs_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(self_in); + int err; + (void)readonly; + (void)mkfs; + + err = fs_mount(self->mount); + if (err == -EBUSY) { + err = fs_unmount(self->mount); + if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("error un-mounting Zephyr File System: %q"), mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + return mp_const_none; + } + err = fs_mount(self->mount); + } + + if (err == -EROFS) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("read only filesystem that requires formatting")); + return mp_const_none; + } else if (err < 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("error mounting Zephyr File System: %q"), mp_errno_to_str(MP_OBJ_NEW_SMALL_INT(-err))); + return mp_const_none; + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(zephyr_fs_mount_obj, zephyr_fs_mount); + +#if CONFIG_FILE_SYSTEM_MKFS +static mp_obj_t zephyr_fs_mkfs(mp_obj_t self_in) { + zephyr_fs_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // Yes, they correspond indirectly + if (self->mount->fs->mkfs((int)self->mount->storage_dev, self->mount->fs_data, self->mount->flags) < 0) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("error formatting Zephyr File System")); + } + + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_fs_mkfs_obj, zephyr_fs_mkfs); +#endif + +static mp_obj_t zephyr_fs_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, 1, false); + + if (!mp_obj_is_str(args[0])) { + mp_raise_ValueError(MP_ERROR_TEXT("argument must be a zephyr FSTAB mountpoint string")); + return mp_const_none; + } + + const char *_mountpoint = mp_obj_str_get_str(args[0]); + int i = 0; + + for (; i < zephyr_fs_mounts_size; i++) { + if (strcmp(_mountpoint, zephyr_fs_mounts[i]->mnt_point) == 0) { + break; + } + } + + if (i == zephyr_fs_mounts_size) { + mp_raise_ValueError(MP_ERROR_TEXT("couldn't find zephyr mountpoint")); + return mp_const_none; + } + + if (zephyr_fs_mounts[i]->fs == 0) { + if (fs_mount(zephyr_fs_mounts[i]) != 0) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("FS is invalid, try adding automount to fstab")); + return mp_const_none; + } + } + + // create new object + zephyr_fs_obj_t *fs = mp_obj_malloc(zephyr_fs_obj_t, type); + fs->mount = zephyr_fs_mounts[i]; + vstr_init(&fs->cur_dir, 32); + vstr_init(&fs->root_dir, 128); + vstr_add_str(&fs->root_dir, zephyr_fs_mounts[i]->mnt_point); + + return MP_OBJ_FROM_PTR(fs); +} + +static mp_obj_t zephyr_fs_fstab(void) { + mp_obj_t list = mp_obj_new_list(0, NULL); + + for (int i = 0; i < zephyr_fs_mounts_size; i++) { + mp_obj_list_append(list, mp_obj_new_str_from_cstr(zephyr_fs_mounts[i]->mnt_point)); + } + + return list; +} +static MP_DEFINE_CONST_FUN_OBJ_0(zephyr_fs_fstab_fun_obj, zephyr_fs_fstab); +static MP_DEFINE_CONST_STATICMETHOD_OBJ(zephyr_fs_fstab_obj, MP_ROM_PTR(&zephyr_fs_fstab_fun_obj)); + +static const mp_rom_map_elem_t zephyr_fs_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&zephyr_fs_mount_obj) }, + { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&zephyr_fs_umount_obj) }, + { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&zephyr_fs_statvfs_obj) }, + { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&zephyr_fs_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&zephyr_fs_rename_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&zephyr_fs_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&zephyr_fs_rmdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&zephyr_fs_getcwd_obj) }, + { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&zephyr_fs_chdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&zephyr_fs_mkdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&zephyr_fs_open_obj) }, + { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&zephyr_fs_ilistdir_obj) }, + #if CONFIG_FILE_SYSTEM_MKFS + { MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&zephyr_fs_mkfs_obj) }, + #endif + // Not part of the VFS API, used to list available File Systems + { MP_ROM_QSTR(MP_QSTR_fstab), MP_ROM_PTR(&zephyr_fs_fstab_obj) }, +}; +static MP_DEFINE_CONST_DICT(zephyr_fs_locals_dict, zephyr_fs_locals_dict_table); + +static const mp_vfs_proto_t zephyr_fs_proto = { + .import_stat = zephyr_fs_import_stat, +}; + +const mp_obj_type_t zephyr_filesystem_type; + +MP_DEFINE_CONST_OBJ_TYPE( + zephyr_filesystem_type, + MP_QSTR_FileSystem, + MP_TYPE_FLAG_NONE, + make_new, zephyr_fs_make_new, + protocol, &zephyr_fs_proto, + locals_dict, &zephyr_fs_locals_dict + ); + +#endif // CONFIG_FILE_SYSTEM |
