From 63c49d432dd050fda73c852d9930830b70bd13dc Mon Sep 17 00:00:00 2001 From: "peter.marcisovsky" Date: Thu, 2 Jul 2026 14:59:17 +0200 Subject: [PATCH] feat(usb_device): Add SDMMC support for MSC USB Device example for esp32s31 target - Added CI build of the MSC device example with SDMMC config --- examples/peripherals/.build-test-rules.yml | 15 +++++ .../peripherals/usb/device/tusb_msc/README.md | 61 +++++++++++++++---- .../device/tusb_msc/main/Kconfig.projbuild | 12 ++-- .../usb/device/tusb_msc/main/tusb_msc_main.c | 7 ++- ...ig.ci.esp32s3_sdmmc => sdkconfig.ci.sdmmc} | 1 - 5 files changed, 76 insertions(+), 20 deletions(-) rename examples/peripherals/usb/device/tusb_msc/{sdkconfig.ci.esp32s3_sdmmc => sdkconfig.ci.sdmmc} (56%) diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index d96380deb48..14c607dde44 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -827,6 +827,21 @@ examples/peripherals/usb/device/tusb_cdc_acm_wakeup: disable: - if: SOC_USB_OTG_SUPPORTED != 1 or SOC_PM_SUPPORT_USB_WAKEUP != 1 +examples/peripherals/usb/device/tusb_msc: + disable: + - if: SOC_USB_OTG_SUPPORTED != 1 + - if: CONFIG_NAME == "sdmmc" and SOC_SDMMC_HOST_SUPPORTED != 1 + reason: Only USB-OTG supported targets with SDMMC support + disable_test: + - if: IDF_TARGET not in ["esp32s2", "esp32p4"] + temporary: true + reason: lack of runners with usb_device tag + depends_components: + - fatfs + - soc + depends_filepatterns: + - examples/peripherals/usb/device/tusb_msc/**/* + examples/peripherals/usb/device/tusb_ncm: disable: - if: SOC_USB_OTG_SUPPORTED != 1 or SOC_WIFI_SUPPORTED != 1 diff --git a/examples/peripherals/usb/device/tusb_msc/README.md b/examples/peripherals/usb/device/tusb_msc/README.md index 7454130be00..653f53b1526 100644 --- a/examples/peripherals/usb/device/tusb_msc/README.md +++ b/examples/peripherals/usb/device/tusb_msc/README.md @@ -28,9 +28,9 @@ As a USB stack, a TinyUSB component is used. 3. USB which accesses the ESP MSC Partition is plugged-in at boot-up. After boot-up, it is ejected on Host PC manually by user. - Result: Host PC can't access the partition over USB MSC. Application example can perform operations (read, write) on partition. 4. USB which accesses the ESP MSC Partition is plugged-in at boot-up. It is then unplugged(removed) from Host PC manually by user. - - Result: The behaviour is different for bus-powered devices and self-powered devices + - Result: The behavior is different for bus-powered devices and self-powered devices - (a) Bus-Powered devices - Both Host PC as well as application example can't access the partition over USB MSC. Here, the device will be Powered-off. - - (b) Self-Powered devices - Here, the device can be powered-on even after unplugging the device from Host PC. These behaviour can be further categorize in two ways: + - (b) Self-Powered devices - Here, the device can be powered-on even after unplugging the device from Host PC. These behavior can be further categorize in two ways: - (i) Self-Powered Devices without VBUS monitoring - Both Host PC as well as application example can't access the partition over USB MSC. - (ii) Self-Powered Devices with VBUS monitoring - Host PC can't access the partition over USB MSC. Application example can perform operations (read, write) on partition. Here, in ``tinyusb_config_t`` user must set ``self_powered`` to ``true`` and ``vbus_monitor_io`` to GPIO number (``VBUS_MONITORING_GPIO_NUM``) that will be used for VBUS monitoring. @@ -47,14 +47,27 @@ See common pin assignments for USB Device examples from [upper level](../../READ Next, for Self-Powered Devices with VBUS monitoring, user must set ``self_powered`` to ``true`` and ``vbus_monitor_io`` to GPIO number (``VBUS_MONITORING_GPIO_NUM``) that will be used for VBUS monitoring. -### Additional Pin assignments for ESP32-S3 for accessing SD MMC Card +### SD MMC Card Pin Assignments -On ESP32-S3, SDMMC peripheral is connected to GPIO pins using GPIO matrix. This allows arbitrary GPIOs to be used to connect an SD card. In this example, GPIOs can be configured in two ways: +When using SD MMC Card as storage media, additional pins are required beyond the USB pins. SD MMC card support is available on ESP32-S3, ESP32-P4, and ESP32-S31. -1. Using menuconfig: Run `idf.py menuconfig` in the project directory, open "USB DEV MSC Example Configuration" and select "SDMMC CARD" for "Storage Media Used". +#### SD MMC Configuration + +GPIOs and SD bus settings can be configured in two ways: + +1. Using menuconfig: Run `idf.py menuconfig` in the project directory, open "TinyUSB MSC Example Configuration", and: + - Select "SDMMC CARD" for "Storage Media Used" + - Select "SD/MMC bus width" — "4 lines (D0 - D3)" or "1 line (D0)" (see `CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_1`) + - On ESP32-S3 and ESP32-P4: set the GPIO numbers for the SD card interface 2. In the source code: See the initialization of ``sdmmc_slot_config_t slot_config`` structure in the example code. -The table below lists the default pin assignments. +By default, this example uses 4-line SD mode, utilizing 6 pins: CLK, CMD, D0 - D3. + +Note that even if card's D3 line is not connected to the ESP chip, it still has to be pulled up, otherwise the card will go into SPI protocol mode. + +#### ESP32-S3 + +On ESP32-S3, the SDMMC peripheral is connected to GPIO pins using the GPIO matrix. This allows arbitrary GPIOs to be used to connect an SD card. The table below lists the default pin assignments. When using an ESP32-S3-USB-OTG board, this example runs without any extra modifications required. Only an SD card needs to be inserted into the slot. @@ -67,18 +80,40 @@ GPIO38 | D1 | not used in 1-line SD mode; 10k pullup in 4-line m GPIO33 | D2 | not used in 1-line SD mode; 10k pullup in 4-line mode GPIO34 | D3 | not used in 1-line SD mode, but card's D3 pin must have a 10k pullup -By default, this example uses 4 line SD mode, utilizing 6 pins: CLK, CMD, D0 - D3. It is possible to use 1-line mode (CLK, CMD, D0) by changing "SD/MMC bus width" in the example configuration menu (see `CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_1`). +#### ESP32-P4 -Note that even if card's D3 line is not connected to the ESP chip, it still has to be pulled up, otherwise the card will go into SPI protocol mode. +On ESP32-P4, Slot 1 of the SDMMC peripheral is connected to GPIO pins using the GPIO matrix. This allows arbitrary GPIOs to be used to connect an SD card. The table below lists the default pin assignments. + +ESP32-P4 pin | SD card pin | Notes +--------------|-------------|------------ +GPIO43 | CLK | 10k pullup +GPIO44 | CMD | 10k pullup +GPIO39 | D0 | 10k pullup +GPIO40 | D1 | not used in 1-line SD mode; 10k pullup in 4-line mode +GPIO41 | D2 | not used in 1-line SD mode; 10k pullup in 4-line mode +GPIO42 | D3 | not used in 1-line SD mode, but card's D3 pin must have a 10k pullup + +Default dedicated pins on ESP32-P4 are able to connect to an ultra high-speed SD card (UHS-I) which requires 1.8V switching (instead of the regular 3.3V). This means the user has to provide an external LDO power supply to use them, or enable and configure the internal LDO via `idf.py menuconfig` -> `TinyUSB MSC Example Configuration` -> `SDMMC powered by internal LDO` and `LDO IO`. + +When using different GPIO pins this is not required and the `SDMMC powered by internal LDO` setting can be disabled. + +#### ESP32-S31 + +On ESP32-S31, the SDMMC peripheral is connected to specific GPIO pins using the IO MUX. GPIO pins cannot be customized. The table below lists the pin connections. + +ESP32-S31 pin | SD card pin | Notes +--------------|-------------|------------ +GPIO24 | CLK | 10k pullup +GPIO25 | CMD | 10k pullup +GPIO20 | D0 | 10k pullup +GPIO21 | D1 | not used in 1-line SD mode; 10k pullup in 4-line mode +GPIO22 | D2 | not used in 1-line SD mode; 10k pullup in 4-line mode +GPIO23 | D3 | not used in 1-line SD mode, but card's D3 pin must have a 10k pullup ### Build and Flash 1. By default, the example will compile to access SPI Flash as storage media. Here, SPI Flash Wear Levelling WL_SECTOR_SIZE is set to 512 and WL_SECTOR_MODE is set to PERF in Menuconfig. -2. In order to access SD MMC card as storage media, configuration has to be changed using `idf.py menuconfig`: - - i. Open "USB Dev MSC Example Configuration" and select "SDMMC CARD" for "Storage Media Used" - - ii. Open "SD/MMC bus width" and select between "4 lines (D0 - D3)" or "1 line (D0)" - - iii. Select the GPIO Pin numbers for SD Card Pin. - - iv. Save the configuration. +2. In order to access SD MMC card as storage media, change the configuration as described in [SD MMC Configuration](#sd-mmc-configuration) above. Build the project and flash it to the board, then run monitor tool to view serial output: diff --git a/examples/peripherals/usb/device/tusb_msc/main/Kconfig.projbuild b/examples/peripherals/usb/device/tusb_msc/main/Kconfig.projbuild index a43bb71aebc..cf96882701c 100644 --- a/examples/peripherals/usb/device/tusb_msc/main/Kconfig.projbuild +++ b/examples/peripherals/usb/device/tusb_msc/main/Kconfig.projbuild @@ -13,7 +13,7 @@ menu "TinyUSB MSC Example Configuration" config EXAMPLE_STORAGE_MEDIA_SDMMC bool "SDMMC CARD" - depends on IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4 + depends on IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4 || IDF_TARGET_ESP32S31 endchoice if EXAMPLE_STORAGE_MEDIA_SDMMC @@ -79,19 +79,21 @@ menu "TinyUSB MSC Example Configuration" endif # SOC_SDMMC_USE_GPIO_MATRIX config EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO - depends on SOC_SDMMC_IO_POWER_EXTERNAL + depends on SOC_SDMMC_IO_POWER_EXTERNAL || SOC_SDMMC_IO_UHS_POWER_EXTERNAL bool "SDMMC powered by internal LDO" default y help - Enable when internal LDO of ESP32-P4 is used as a power supply for SD card. Specify the LDO IO. + Enable when internal LDO of an ESP32 is used as a power supply for SD card. Specify the LDO IO. Disable when external power supply is used as a power supply for SD card. config EXAMPLE_SD_PWR_CTRL_LDO_IO_ID - depends on SOC_SDMMC_IO_POWER_EXTERNAL && EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO + depends on SOC_SDMMC_IO_POWER_EXTERNAL || SOC_SDMMC_IO_UHS_POWER_EXTERNAL + depends on EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO int "LDO IO" default 4 if IDF_TARGET_ESP32P4 + default 1 if IDF_TARGET_ESP32S31 help - There are several IOs in ESP32-P4 which are in "SD card IO" power domain. + There are several IOs in ESP32 SD card capable targets which are in "SD card IO" power domain. Power has to be supplied to this power domain externally (from outside the chip) via one of the pins. Based on the schematic, specify the LDO IO pin. diff --git a/examples/peripherals/usb/device/tusb_msc/main/tusb_msc_main.c b/examples/peripherals/usb/device/tusb_msc/main/tusb_msc_main.c index 44d64df5535..969bf52277b 100644 --- a/examples/peripherals/usb/device/tusb_msc/main/tusb_msc_main.c +++ b/examples/peripherals/usb/device/tusb_msc/main/tusb_msc_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -375,6 +375,11 @@ static esp_err_t storage_init_sdmmc(sdmmc_card_t **card) host.pwr_ctrl_handle = pwr_ctrl_handle; #endif +#if SOC_SDMMC_IO_UHS_POWER_EXTERNAL + //for uhs-i power + host.io_voltage = 1.8f; +#endif + // This initializes the slot without card detect (CD) and write protect (WP) signals. // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); diff --git a/examples/peripherals/usb/device/tusb_msc/sdkconfig.ci.esp32s3_sdmmc b/examples/peripherals/usb/device/tusb_msc/sdkconfig.ci.sdmmc similarity index 56% rename from examples/peripherals/usb/device/tusb_msc/sdkconfig.ci.esp32s3_sdmmc rename to examples/peripherals/usb/device/tusb_msc/sdkconfig.ci.sdmmc index 47f8df9f61c..5ac32b17dcf 100644 --- a/examples/peripherals/usb/device/tusb_msc/sdkconfig.ci.esp32s3_sdmmc +++ b/examples/peripherals/usb/device/tusb_msc/sdkconfig.ci.sdmmc @@ -1,2 +1 @@ -CONFIG_IDF_TARGET="esp32s3" CONFIG_EXAMPLE_STORAGE_MEDIA_SDMMC=y