summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAngus Gratton <angus@redyak.com.au>2025-02-05 15:17:27 +1100
committerDamien George <damien@micropython.org>2025-03-13 12:27:23 +1100
commitc85eefc55b3cc8a460230129e11fb490a0a15f29 (patch)
treebe9f6dd8ec55b42899d4605d49362d3669599a03
parent79fb5aa8789e71c9bcd8430b58267d96552be94f (diff)
esp32/machine_sdcard: Add SDCard SPI mode support for ESP32-S2,C3,C6.
These micros don't have full SDMMC host support, but they can initialise the SDCard in SPI mode. A bit limited on C3 and C6 as they only have one host SPI peripheral. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
-rw-r--r--docs/library/machine.SDCard.rst60
-rw-r--r--ports/esp32/machine_sdcard.c104
2 files changed, 121 insertions, 43 deletions
diff --git a/docs/library/machine.SDCard.rst b/docs/library/machine.SDCard.rst
index 62da8c3c2..c4a0d5d17 100644
--- a/docs/library/machine.SDCard.rst
+++ b/docs/library/machine.SDCard.rst
@@ -77,24 +77,34 @@ ESP32
`````
SD cards support access in both SD/MMC mode and the simpler (but slower) SPI
-mode. ESP32 and ESP32-S3 chips can access SD cards using either mode. SPI mode
-makes use of a `SPI` host peripheral, which cannot concurrently be used for
-something else.
+mode.
-The ``slot`` argument determines which mode is used. Different values are
-available on different chips:
-
-====== ================= ============ ========================
-Slot Supported chips Mode Supported data width
-====== ================= ============ ========================
-0 ESP32-S3 SD/MMC 1, 4, or 8 bits.
-1 ESP32, ESP32-S3 SD/MMC 1 or 4 bits.
-2 ESP32, ESP32-S3 `SPI` (id=1) 1 bit.
-3 ESP32, ESP32-S3 `SPI` (id=0) 1 bit.
-====== ================= ============ ========================
+SPI mode makes use of a `SPI` host peripheral, which cannot concurrently be used
+for other SPI interactions.
-.. note:: On the original ESP32, SDMMC slot 0 is unavailable as its pins are
- used to communicate with on-board flash memory.
+The ``slot`` argument determines which mode is used. Different values are
+supported on different chips:
+
+========== ======== ======== ============ ============
+Chip Slot 0 Slot 1 Slot 2 Slot 3
+========== ======== ======== ============ ============
+ESP32 SD/MMC SPI (id=1) SPI (id=0)
+ESP32-C3 SPI (id=0)
+ESP32-C6 SPI (id=0)
+ESP32-S2 SPI (id=1) SPI (id=0)
+ESP32-S3 SD/MMC SD/MMC SPI (id=1) SPI (id=0)
+========== ======== ======== ============ ============
+
+Different slots support different data bus widths (number of data pins):
+
+========== ========== =====================
+Slot Type Supported data widths
+========== ========== =====================
+0 SD/MMC 1, 4, 8
+1 SD/MMC 1, 4
+2 SPI 1
+3 SPI 1
+========== ========== =====================
.. note:: Most ESP32 modules that provide an SD card slot using the
dedicated hardware only wire up 1 data pin, so the default
@@ -105,8 +115,9 @@ Additional details depend on which ESP32 family chip is in use:
Original ESP32
~~~~~~~~~~~~~~
-Pin assignments in SD/MMC mode are fixed on the original ESP32. When accessing a
-card in SPI mode, pins can be set to different values in the constructor.
+In SD/MMC mode (slot 1), pin assignments in SD/MMC mode are fixed on the
+original ESP32. The SPI mode slots (2 & 3) allow pins to be set to different
+values in the constructor.
The default pin assignments are as follows:
@@ -177,6 +188,19 @@ parameters ``sck``, ``cs``, ``miso``, ``mosi`` as needed to assign pins.
In either mode the ``cd`` and ``wp`` pins default to disabled, unless set in the
constructor.
+Other ESP32 chips
+~~~~~~~~~~~~~~~~~
+
+Other ESP32 family chips do not have hardware SD/MMC host controllers and can
+only access SD cards in SPI mode.
+
+To access a card in SPI mode, set ``slot`` parameter value 2 or 3 and pass
+parameters ``sck``, ``cs``, ``miso``, ``mosi`` to assign pins.
+
+.. note:: ESP32-C3 and ESP32-C6 only have one available `SPI` bus, so the only
+ valid ``slot`` parameter value is 2. Using this bus for the SD card
+ will prevent also using it for :class:`machine.SPI`.
+
cc3200
``````
diff --git a/ports/esp32/machine_sdcard.c b/ports/esp32/machine_sdcard.c
index 48fd96c6d..0f8bd8446 100644
--- a/ports/esp32/machine_sdcard.c
+++ b/ports/esp32/machine_sdcard.c
@@ -33,7 +33,9 @@
#if MICROPY_HW_ENABLE_SDCARD
+#if SOC_SDMMC_HOST_SUPPORTED
#include "driver/sdmmc_host.h"
+#endif
#include "driver/sdspi_host.h"
#include "sdmmc_cmd.h"
#include "esp_log.h"
@@ -69,18 +71,34 @@ typedef struct _sdcard_obj_t {
#define _SECTOR_SIZE(self) (self->card.csd.sector_size)
+// Number SPI buses available for firmware app (including for SD)
+#define NUM_SD_SPI_BUS (SOC_SPI_PERIPH_NUM - 1)
+
+#if CONFIG_IDF_TARGET_ESP32
+#define SD_SLOT_MIN 1
+#elif SOC_SDMMC_HOST_SUPPORTED
+#define SD_SLOT_MIN 0
+#else
+#define SD_SLOT_MIN 2
+#endif
+#define SD_SLOT_MAX (NUM_SD_SPI_BUS + 1) // Inclusive
+
// SPI bus default bus and device configuration.
-static const spi_bus_config_t spi_bus_defaults[2] = {
+static const spi_bus_config_t spi_bus_defaults[NUM_SD_SPI_BUS] = {
{
#if CONFIG_IDF_TARGET_ESP32
.miso_io_num = GPIO_NUM_19,
.mosi_io_num = GPIO_NUM_23,
.sclk_io_num = GPIO_NUM_18,
- #else
+ #elif CONFIG_IDF_TARGET_ESP32S3
.miso_io_num = GPIO_NUM_36,
.mosi_io_num = GPIO_NUM_35,
.sclk_io_num = GPIO_NUM_37,
+ #else
+ .miso_io_num = GPIO_NUM_NC,
+ .mosi_io_num = GPIO_NUM_NC,
+ .sclk_io_num = GPIO_NUM_NC,
#endif
.data2_io_num = GPIO_NUM_NC,
.data3_io_num = GPIO_NUM_NC,
@@ -92,6 +110,7 @@ static const spi_bus_config_t spi_bus_defaults[2] = {
.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_MOSI,
.intr_flags = 0,
},
+ #if NUM_SD_SPI_BUS > 1
{
.miso_io_num = GPIO_NUM_2,
.mosi_io_num = GPIO_NUM_15,
@@ -106,28 +125,34 @@ static const spi_bus_config_t spi_bus_defaults[2] = {
.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_MOSI,
.intr_flags = 0,
},
+ #endif
};
#if CONFIG_IDF_TARGET_ESP32
-static const uint8_t spi_dma_channel_defaults[2] = {
+static const uint8_t spi_dma_channel_defaults[NUM_SD_SPI_BUS] = {
2,
1,
};
#endif
-static const sdspi_device_config_t spi_dev_defaults[2] = {
+static const sdspi_device_config_t spi_dev_defaults[NUM_SD_SPI_BUS] = {
+ #if NUM_SD_SPI_BUS > 1
{
#if CONFIG_IDF_TARGET_ESP32
.host_id = VSPI_HOST,
.gpio_cs = GPIO_NUM_5,
- #else
+ #elif CONFIG_IDF_TARGET_ESP32S3
.host_id = SPI3_HOST,
.gpio_cs = GPIO_NUM_34,
+ #else
+ .host_id = SPI3_HOST,
+ .gpio_cs = GPIO_NUM_NC,
#endif
.gpio_cd = SDSPI_SLOT_NO_CD,
.gpio_wp = SDSPI_SLOT_NO_WP,
.gpio_int = SDSPI_SLOT_NO_INT,
},
+ #endif
SDSPI_DEVICE_CONFIG_DEFAULT(), // HSPI (ESP32) / SPI2 (ESP32S3)
};
@@ -159,12 +184,15 @@ static esp_err_t sdcard_ensure_card_init(sdcard_card_obj_t *self, bool force) {
// Expose the SD card or MMC as an object with the block protocol.
// Create a new SDCard object
-// The driver supports either the host SD/MMC controller (default) or SPI mode
-// In both cases there are two "slots". Slot 0 on the SD/MMC controller is
-// typically tied up with the flash interface in most ESP32 modules but in
-// theory supports 1, 4 or 8-bit transfers. Slot 1 supports only 1 and 4-bit
-// transfers. Only 1-bit is supported on the SPI interfaces.
-// card = SDCard(slot=1, width=None, present_pin=None, wp_pin=None)
+//
+// SD/MMC or SPI mode is determined by the slot argument
+// 0,1 is SD/MMC mode where supported.
+// 2,3 is SPI mode where supported (1-bit only)
+//
+// Original ESP32 can't use 0
+// ESP32-C3/C6/etc can only use 2 (only one SPI bus, no SD/MMC controller)
+//
+// Consult machine.SDCard docs for more details.
static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
// check arguments
@@ -183,8 +211,13 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
#endif
ARG_freq,
};
+ #if SOC_SDMMC_HOST_SUPPORTED
+ static const int DEFAULT_SLOT = 1;
+ #else
+ static const int DEFAULT_SLOT = SD_SLOT_MAX;
+ #endif
static const mp_arg_t allowed_args[] = {
- { MP_QSTR_slot, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
+ { MP_QSTR_slot, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SLOT} },
{ MP_QSTR_width, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
{ MP_QSTR_cd, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_wp, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
@@ -226,15 +259,21 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
#endif
int slot_num = arg_vals[ARG_slot].u_int;
- if (slot_num < 0 || slot_num > 3) {
- mp_raise_ValueError(MP_ERROR_TEXT("slot number must be between 0 and 3 inclusive"));
+ if (slot_num < SD_SLOT_MIN || slot_num > SD_SLOT_MAX) {
+ mp_raise_ValueError(MP_ERROR_TEXT("invalid slot number"));
}
+ #if SOC_SDMMC_HOST_SUPPORTED
// Slots 0 and 1 are native SD/MMC, slots 2 and 3 are SPI
bool is_spi = (slot_num >= 2);
+ #else
+ bool is_spi = true;
+ #endif
if (is_spi) {
slot_num -= 2;
+ assert(slot_num < NUM_SD_SPI_BUS);
}
+
// Verify valid argument combinations
#if SOC_SDMMC_USE_GPIO_MATRIX
if (is_spi && (arg_vals[ARG_cmd].u_obj != mp_const_none
@@ -242,6 +281,13 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
mp_raise_ValueError(MP_ERROR_TEXT("invalid config: SPI slot with SDMMC pin arguments"));
}
#endif
+ #if SOC_SDMMC_HOST_SUPPORTED
+ if (!is_spi && (arg_vals[ARG_miso].u_obj != mp_const_none
+ || arg_vals[ARG_mosi].u_obj != mp_const_none
+ || arg_vals[ARG_cs].u_obj != mp_const_none)) {
+ mp_raise_ValueError(MP_ERROR_TEXT("invalid config: SDMMC slot with SPI pin arguments"));
+ }
+ #endif
DEBUG_printf(" Setting up host configuration");
@@ -253,21 +299,17 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
if (is_spi) {
sdmmc_host_t _temp_host = SDSPI_HOST_DEFAULT();
_temp_host.max_freq_khz = freq / 1000;
+ // SPI SDMMC sets the slot to the SPI host ID
+ _temp_host.slot = spi_dev_defaults[slot_num].host_id;
self->host = _temp_host;
- } else {
+ }
+ #if SOC_SDMMC_HOST_SUPPORTED
+ else {
sdmmc_host_t _temp_host = SDMMC_HOST_DEFAULT();
_temp_host.max_freq_khz = freq / 1000;
self->host = _temp_host;
}
-
- if (is_spi) {
- // Needs to match spi_dev_defaults above.
- #if CONFIG_IDF_TARGET_ESP32
- self->host.slot = slot_num ? HSPI_HOST : VSPI_HOST;
- #else
- self->host.slot = slot_num ? SPI2_HOST : SPI3_HOST;
- #endif
- }
+ #endif
DEBUG_printf(" Calling host.init()");
@@ -294,6 +336,15 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
SET_CONFIG_PIN(dev_config, gpio_cd, ARG_cd);
SET_CONFIG_PIN(dev_config, gpio_wp, ARG_wp);
+ // On chips other than original ESP32 and S3, there are not
+ // always default SPI pins assigned
+ if (dev_config.gpio_cs == GPIO_NUM_NC
+ || bus_config.miso_io_num == GPIO_NUM_NC
+ || bus_config.mosi_io_num == GPIO_NUM_NC
+ || bus_config.sclk_io_num == GPIO_NUM_NC) {
+ mp_raise_ValueError(MP_ERROR_TEXT("SPI pin values required"));
+ }
+
DEBUG_printf(" Calling spi_bus_initialize()");
check_esp_err(spi_bus_initialize(spi_host_id, &bus_config, dma_channel));
@@ -309,7 +360,9 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
spi_bus_free(spi_host_id);
mp_raise_ValueError(MP_ERROR_TEXT("SPI bus already in use"));
}
- } else {
+ }
+ #if SOC_SDMMC_HOST_SUPPORTED
+ else {
// SD/MMC interface
DEBUG_printf(" Setting up SDMMC slot configuration");
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
@@ -357,6 +410,7 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
DEBUG_printf(" Calling init_slot()");
check_esp_err(sdmmc_host_init_slot(self->host.slot, &slot_config));
}
+ #endif // SOC_SDMMC_HOST_SUPPORTED
DEBUG_printf(" Returning new card object: %p", self);
return MP_OBJ_FROM_PTR(self);