esp32/machine_sdcard: Add SDCard pin assignments for ESP32-S3 support.

Previously ESP32-S3 SDMMC could only use fixed pin assignments, however the
ESP-IDF defaults don't match common boards. The chip also supports using
GPIO Matrix to assign any pin.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
This commit is contained in:
Angus Gratton
2025-02-05 15:16:47 +11:00
committed by Damien George
parent 4d65b4e261
commit 79fb5aa878
3 changed files with 154 additions and 44 deletions

View File

@@ -747,7 +747,7 @@ See :ref:`machine.SDCard <machine.SDCard>`. ::
import machine, os, vfs import machine, os, vfs
# Slot 2 uses pins sck=18, cs=5, miso=19, mosi=23 # On original ESP32, slot 2 uses pins sck=18, cs=5, miso=19, mosi=23
sd = machine.SDCard(slot=2) sd = machine.SDCard(slot=2)
vfs.mount(sd, '/sd') # mount vfs.mount(sd, '/sd') # mount

View File

@@ -23,7 +23,8 @@ arguments that might need to be set in order to use either a non-standard slot
or a non-standard pin assignment. The exact subset of arguments supported will or a non-standard pin assignment. The exact subset of arguments supported will
vary from platform to platform. vary from platform to platform.
.. class:: SDCard(slot=1, width=1, cd=None, wp=None, sck=None, miso=None, mosi=None, cs=None, freq=20000000) .. class:: SDCard(slot=1, width=1, cd=None, wp=None, sck=None, miso=None, mosi=None,
cs=None, cmd=None, data=None, freq=20000000)
This class provides access to SD or MMC storage cards using either This class provides access to SD or MMC storage cards using either
a dedicated SD/MMC interface hardware or through an SPI channel. a dedicated SD/MMC interface hardware or through an SPI channel.
@@ -37,7 +38,8 @@ vary from platform to platform.
- *slot* selects which of the available interfaces to use. Leaving this - *slot* selects which of the available interfaces to use. Leaving this
unset will select the default interface. unset will select the default interface.
- *width* selects the bus width for the SD/MMC interface. - *width* selects the bus width for the SD/MMC interface. This many data
pins must be connected to the SD card.
- *cd* can be used to specify a card-detect pin. - *cd* can be used to specify a card-detect pin.
@@ -51,7 +53,14 @@ vary from platform to platform.
- *cs* can be used to specify an SPI chip select pin. - *cs* can be used to specify an SPI chip select pin.
- *freq* selects the SD/MMC interface frequency in Hz (only supported on the ESP32). The following additional parameters are only present on ESP32 port:
- *cmd* can be used to specify the SD CMD pin (ESP32-S3 only).
- *data* can be used to specify a list or tuple of SD data bus pins
(ESP32-S3 only).
- *freq* selects the SD/MMC interface frequency in Hz.
Implementation-specific details Implementation-specific details
------------------------------- -------------------------------
@@ -67,52 +76,106 @@ The standard PyBoard has just one slot. No arguments are necessary or supported.
ESP32 ESP32
````` `````
The ESP32 provides two channels of SD/MMC hardware and also supports SD cards support access in both SD/MMC mode and the simpler (but slower) SPI
access to SD Cards through either of the two SPI ports that are mode. ESP32 and ESP32-S3 chips can access SD cards using either mode. SPI mode
generally available to the user. As a result the *slot* argument can makes use of a `SPI` host peripheral, which cannot concurrently be used for
take a value between 0 and 3, inclusive. Slots 0 and 1 use the something else.
built-in SD/MMC hardware while slots 2 and 3 use the SPI ports. Slot 0
supports 1, 4 or 8-bit wide access while slot 1 supports 1 or 4-bit
access; the SPI slots only support 1-bit access.
.. note:: Slot 0 is used to communicate with on-board flash memory The ``slot`` argument determines which mode is used. Different values are
on most ESP32 modules and so will be unavailable to the available on different chips:
user.
.. note:: Most ESP32 modules that provide an SD card slot using the ====== ================= ============ ========================
dedicated hardware only wire up 1 data pin, so the default Slot Supported chips Mode Supported data width
value for *width* is 1. ====== ================= ============ ========================
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.
====== ================= ============ ========================
The pins used by the dedicated SD/MMC hardware are fixed. The pins .. note:: On the original ESP32, SDMMC slot 0 is unavailable as its pins are
used by the SPI hardware can be reassigned. used to communicate with on-board flash memory.
.. note:: If any of the SPI signals are remapped then all of the SPI .. note:: Most ESP32 modules that provide an SD card slot using the
signals will pass through a GPIO multiplexer unit which dedicated hardware only wire up 1 data pin, so the default
can limit the performance of high frequency signals. Since value for ``width`` is 1.
the normal operating speed for SD cards is 40MHz this can
cause problems on some cards.
The default (and preferred) pin assignment are as follows: Additional details depend on which ESP32 family chip is in use:
====== ====== ====== ====== ====== Original ESP32
Slot 0 1 2 3 ~~~~~~~~~~~~~~
------ ------ ------ ------ ------
Signal Pin Pin Pin Pin 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.
sck 6 14 18 14
cmd 11 15 The default pin assignments are as follows:
cs 5 15
miso 19 12 ====== ====== ====== ====== ============
mosi 23 13 Slot 1 2 3 Can be set
D0 7 2 ------ ------ ------ ------ ------------
D1 8 4 Signal Pin Pin Pin
D2 9 12 ====== ====== ====== ====== ============
D3 10 13 CLK 14 No
D4 16 CMD 15 No
D5 17 D0 2 No
D6 5 D1 4 No
D7 18 D2 12 No
====== ====== ====== ====== ====== D3 13 No
sck 18 14 Yes
cs 5 15 Yes
miso 19 12 Yes
mosi 23 13 Yes
====== ====== ====== ====== ============
The ``cd`` and ``wp`` pins are not fixed in either mode and default to disabled, unless set.
ESP32-S3
~~~~~~~~
The ESP32-S3 chip allows pins to be set to different values for both SD/MMC and
SPI mode access.
If not set, default pin assignments are as follows:
======== ====== ====== ====== ======
Slot 0 1 2 3
-------- ------ ------ ------ ------
Signal Pin Pin Pin Pin
======== ====== ====== ====== ======
CLK 14 14
CMD 15 15
D0 2 2
D1 4 4
D2 12 12
D3 13 13
D4 33*
D5 34*
D6 35*
D7 36*
sck 37* 14
cs 34* 13
miso 37* 2
mosi 35* 15
======== ====== ====== ====== ======
.. note:: Slots 0 and 1 cannot both be in use at the same time.
.. note:: Pins marked with an asterisk * in the table must be changed from the
default if the ESP32-S3 board is configured for Octal SPI Flash or
PSRAM.
To access a card in SD/MMC mode, set ``slot`` parameter value 0 or 1 and
parameters ``sck`` (for CLK), ``cmd`` and ``data`` as needed to assign pins. If
the ``data`` argument is passed then it should be a list or tuple of data pins
or pin numbers with length equal to the ``width`` argument. For example::
sd = SDCard(slot=0, width=4, sck=8, cmd=9, data=(10, 11, 12, 13))
To access a card in SPI mode, set ``slot`` parameter value 2 or 3 and pass
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.
cc3200 cc3200
`````` ``````

View File

@@ -177,6 +177,10 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
ARG_mosi, ARG_mosi,
ARG_sck, ARG_sck,
ARG_cs, ARG_cs,
#if SOC_SDMMC_USE_GPIO_MATRIX
ARG_cmd,
ARG_data,
#endif
ARG_freq, ARG_freq,
}; };
static const mp_arg_t allowed_args[] = { static const mp_arg_t allowed_args[] = {
@@ -189,6 +193,11 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
{ MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_cs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_cs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
// Optional assignment of SDMMC interface pins, if host supports this
#if SOC_SDMMC_USE_GPIO_MATRIX
{ MP_QSTR_cmd, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_data, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
#endif
// freq is valid for both SPI and SDMMC interfaces // freq is valid for both SPI and SDMMC interfaces
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 20000000} }, { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 20000000} },
}; };
@@ -211,6 +220,11 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
arg_vals[ARG_miso].u_obj, arg_vals[ARG_mosi].u_obj, arg_vals[ARG_miso].u_obj, arg_vals[ARG_mosi].u_obj,
arg_vals[ARG_sck].u_obj, arg_vals[ARG_cs].u_obj); arg_vals[ARG_sck].u_obj, arg_vals[ARG_cs].u_obj);
#if SOC_SDMMC_USE_GPIO_MATRIX
DEBUG_printf(" cmd=%p, data=%p",
arg_vals[ARG_cmd].u_obj, arg_vals[ARG_data].u_obj);
#endif
int slot_num = arg_vals[ARG_slot].u_int; int slot_num = arg_vals[ARG_slot].u_int;
if (slot_num < 0 || slot_num > 3) { if (slot_num < 0 || slot_num > 3) {
mp_raise_ValueError(MP_ERROR_TEXT("slot number must be between 0 and 3 inclusive")); mp_raise_ValueError(MP_ERROR_TEXT("slot number must be between 0 and 3 inclusive"));
@@ -221,6 +235,13 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
if (is_spi) { if (is_spi) {
slot_num -= 2; slot_num -= 2;
} }
// Verify valid argument combinations
#if SOC_SDMMC_USE_GPIO_MATRIX
if (is_spi && (arg_vals[ARG_cmd].u_obj != mp_const_none
|| arg_vals[ARG_data].u_obj != mp_const_none)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid config: SPI slot with SDMMC pin arguments"));
}
#endif
DEBUG_printf(" Setting up host configuration"); DEBUG_printf(" Setting up host configuration");
@@ -307,6 +328,32 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
mp_raise_ValueError(MP_ERROR_TEXT("width must be 1 or 4 (or 8 on slot 0)")); mp_raise_ValueError(MP_ERROR_TEXT("width must be 1 or 4 (or 8 on slot 0)"));
} }
#if SOC_SDMMC_USE_GPIO_MATRIX
// Optionally configure all the SDMMC pins, if chip supports this
SET_CONFIG_PIN(slot_config, clk, ARG_sck); // reuse SPI SCK for CLK
SET_CONFIG_PIN(slot_config, cmd, ARG_cmd);
if (arg_vals[ARG_data].u_obj != mp_const_none) {
mp_obj_t *data_vals;
size_t data_len;
mp_obj_get_array(arg_vals[ARG_data].u_obj, &data_len, &data_vals);
if (data_len != width) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("data argument length must match width %d"), width);
}
slot_config.d0 = machine_pin_get_id(data_vals[0]);
if (width > 1) {
slot_config.d1 = machine_pin_get_id(data_vals[1]);
slot_config.d2 = machine_pin_get_id(data_vals[2]);
slot_config.d3 = machine_pin_get_id(data_vals[3]);
}
if (width == 8) {
slot_config.d4 = machine_pin_get_id(data_vals[4]);
slot_config.d5 = machine_pin_get_id(data_vals[5]);
slot_config.d6 = machine_pin_get_id(data_vals[6]);
slot_config.d7 = machine_pin_get_id(data_vals[7]);
}
}
#endif
DEBUG_printf(" Calling init_slot()"); DEBUG_printf(" Calling init_slot()");
check_esp_err(sdmmc_host_init_slot(self->host.slot, &slot_config)); check_esp_err(sdmmc_host_init_slot(self->host.slot, &slot_config));
} }