Skip to content

Commit c85eefc

Browse files
projectgusdpgeorge
authored andcommitted
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 <[email protected]>
1 parent 79fb5aa commit c85eefc

File tree

2 files changed

+121
-43
lines changed

2 files changed

+121
-43
lines changed

docs/library/machine.SDCard.rst

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,24 +77,34 @@ ESP32
7777
`````
7878

7979
SD cards support access in both SD/MMC mode and the simpler (but slower) SPI
80-
mode. ESP32 and ESP32-S3 chips can access SD cards using either mode. SPI mode
81-
makes use of a `SPI` host peripheral, which cannot concurrently be used for
82-
something else.
80+
mode.
8381

84-
The ``slot`` argument determines which mode is used. Different values are
85-
available on different chips:
86-
87-
====== ================= ============ ========================
88-
Slot Supported chips Mode Supported data width
89-
====== ================= ============ ========================
90-
0 ESP32-S3 SD/MMC 1, 4, or 8 bits.
91-
1 ESP32, ESP32-S3 SD/MMC 1 or 4 bits.
92-
2 ESP32, ESP32-S3 `SPI` (id=1) 1 bit.
93-
3 ESP32, ESP32-S3 `SPI` (id=0) 1 bit.
94-
====== ================= ============ ========================
82+
SPI mode makes use of a `SPI` host peripheral, which cannot concurrently be used
83+
for other SPI interactions.
9584

96-
.. note:: On the original ESP32, SDMMC slot 0 is unavailable as its pins are
97-
used to communicate with on-board flash memory.
85+
The ``slot`` argument determines which mode is used. Different values are
86+
supported on different chips:
87+
88+
========== ======== ======== ============ ============
89+
Chip Slot 0 Slot 1 Slot 2 Slot 3
90+
========== ======== ======== ============ ============
91+
ESP32 SD/MMC SPI (id=1) SPI (id=0)
92+
ESP32-C3 SPI (id=0)
93+
ESP32-C6 SPI (id=0)
94+
ESP32-S2 SPI (id=1) SPI (id=0)
95+
ESP32-S3 SD/MMC SD/MMC SPI (id=1) SPI (id=0)
96+
========== ======== ======== ============ ============
97+
98+
Different slots support different data bus widths (number of data pins):
99+
100+
========== ========== =====================
101+
Slot Type Supported data widths
102+
========== ========== =====================
103+
0 SD/MMC 1, 4, 8
104+
1 SD/MMC 1, 4
105+
2 SPI 1
106+
3 SPI 1
107+
========== ========== =====================
98108

99109
.. note:: Most ESP32 modules that provide an SD card slot using the
100110
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:
105115
Original ESP32
106116
~~~~~~~~~~~~~~
107117

108-
Pin assignments in SD/MMC mode are fixed on the original ESP32. When accessing a
109-
card in SPI mode, pins can be set to different values in the constructor.
118+
In SD/MMC mode (slot 1), pin assignments in SD/MMC mode are fixed on the
119+
original ESP32. The SPI mode slots (2 & 3) allow pins to be set to different
120+
values in the constructor.
110121

111122
The default pin assignments are as follows:
112123

@@ -177,6 +188,19 @@ parameters ``sck``, ``cs``, ``miso``, ``mosi`` as needed to assign pins.
177188
In either mode the ``cd`` and ``wp`` pins default to disabled, unless set in the
178189
constructor.
179190

191+
Other ESP32 chips
192+
~~~~~~~~~~~~~~~~~
193+
194+
Other ESP32 family chips do not have hardware SD/MMC host controllers and can
195+
only access SD cards in SPI mode.
196+
197+
To access a card in SPI mode, set ``slot`` parameter value 2 or 3 and pass
198+
parameters ``sck``, ``cs``, ``miso``, ``mosi`` to assign pins.
199+
200+
.. note:: ESP32-C3 and ESP32-C6 only have one available `SPI` bus, so the only
201+
valid ``slot`` parameter value is 2. Using this bus for the SD card
202+
will prevent also using it for :class:`machine.SPI`.
203+
180204
cc3200
181205
``````
182206

ports/esp32/machine_sdcard.c

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333

3434
#if MICROPY_HW_ENABLE_SDCARD
3535

36+
#if SOC_SDMMC_HOST_SUPPORTED
3637
#include "driver/sdmmc_host.h"
38+
#endif
3739
#include "driver/sdspi_host.h"
3840
#include "sdmmc_cmd.h"
3941
#include "esp_log.h"
@@ -69,18 +71,34 @@ typedef struct _sdcard_obj_t {
6971

7072
#define _SECTOR_SIZE(self) (self->card.csd.sector_size)
7173

74+
// Number SPI buses available for firmware app (including for SD)
75+
#define NUM_SD_SPI_BUS (SOC_SPI_PERIPH_NUM - 1)
76+
77+
#if CONFIG_IDF_TARGET_ESP32
78+
#define SD_SLOT_MIN 1
79+
#elif SOC_SDMMC_HOST_SUPPORTED
80+
#define SD_SLOT_MIN 0
81+
#else
82+
#define SD_SLOT_MIN 2
83+
#endif
84+
#define SD_SLOT_MAX (NUM_SD_SPI_BUS + 1) // Inclusive
85+
7286
// SPI bus default bus and device configuration.
7387

74-
static const spi_bus_config_t spi_bus_defaults[2] = {
88+
static const spi_bus_config_t spi_bus_defaults[NUM_SD_SPI_BUS] = {
7589
{
7690
#if CONFIG_IDF_TARGET_ESP32
7791
.miso_io_num = GPIO_NUM_19,
7892
.mosi_io_num = GPIO_NUM_23,
7993
.sclk_io_num = GPIO_NUM_18,
80-
#else
94+
#elif CONFIG_IDF_TARGET_ESP32S3
8195
.miso_io_num = GPIO_NUM_36,
8296
.mosi_io_num = GPIO_NUM_35,
8397
.sclk_io_num = GPIO_NUM_37,
98+
#else
99+
.miso_io_num = GPIO_NUM_NC,
100+
.mosi_io_num = GPIO_NUM_NC,
101+
.sclk_io_num = GPIO_NUM_NC,
84102
#endif
85103
.data2_io_num = GPIO_NUM_NC,
86104
.data3_io_num = GPIO_NUM_NC,
@@ -92,6 +110,7 @@ static const spi_bus_config_t spi_bus_defaults[2] = {
92110
.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_MOSI,
93111
.intr_flags = 0,
94112
},
113+
#if NUM_SD_SPI_BUS > 1
95114
{
96115
.miso_io_num = GPIO_NUM_2,
97116
.mosi_io_num = GPIO_NUM_15,
@@ -106,28 +125,34 @@ static const spi_bus_config_t spi_bus_defaults[2] = {
106125
.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MISO | SPICOMMON_BUSFLAG_MOSI,
107126
.intr_flags = 0,
108127
},
128+
#endif
109129
};
110130

111131
#if CONFIG_IDF_TARGET_ESP32
112-
static const uint8_t spi_dma_channel_defaults[2] = {
132+
static const uint8_t spi_dma_channel_defaults[NUM_SD_SPI_BUS] = {
113133
2,
114134
1,
115135
};
116136
#endif
117137

118-
static const sdspi_device_config_t spi_dev_defaults[2] = {
138+
static const sdspi_device_config_t spi_dev_defaults[NUM_SD_SPI_BUS] = {
139+
#if NUM_SD_SPI_BUS > 1
119140
{
120141
#if CONFIG_IDF_TARGET_ESP32
121142
.host_id = VSPI_HOST,
122143
.gpio_cs = GPIO_NUM_5,
123-
#else
144+
#elif CONFIG_IDF_TARGET_ESP32S3
124145
.host_id = SPI3_HOST,
125146
.gpio_cs = GPIO_NUM_34,
147+
#else
148+
.host_id = SPI3_HOST,
149+
.gpio_cs = GPIO_NUM_NC,
126150
#endif
127151
.gpio_cd = SDSPI_SLOT_NO_CD,
128152
.gpio_wp = SDSPI_SLOT_NO_WP,
129153
.gpio_int = SDSPI_SLOT_NO_INT,
130154
},
155+
#endif
131156
SDSPI_DEVICE_CONFIG_DEFAULT(), // HSPI (ESP32) / SPI2 (ESP32S3)
132157
};
133158

@@ -159,12 +184,15 @@ static esp_err_t sdcard_ensure_card_init(sdcard_card_obj_t *self, bool force) {
159184
// Expose the SD card or MMC as an object with the block protocol.
160185

161186
// Create a new SDCard object
162-
// The driver supports either the host SD/MMC controller (default) or SPI mode
163-
// In both cases there are two "slots". Slot 0 on the SD/MMC controller is
164-
// typically tied up with the flash interface in most ESP32 modules but in
165-
// theory supports 1, 4 or 8-bit transfers. Slot 1 supports only 1 and 4-bit
166-
// transfers. Only 1-bit is supported on the SPI interfaces.
167-
// card = SDCard(slot=1, width=None, present_pin=None, wp_pin=None)
187+
//
188+
// SD/MMC or SPI mode is determined by the slot argument
189+
// 0,1 is SD/MMC mode where supported.
190+
// 2,3 is SPI mode where supported (1-bit only)
191+
//
192+
// Original ESP32 can't use 0
193+
// ESP32-C3/C6/etc can only use 2 (only one SPI bus, no SD/MMC controller)
194+
//
195+
// Consult machine.SDCard docs for more details.
168196

169197
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) {
170198
// check arguments
@@ -183,8 +211,13 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
183211
#endif
184212
ARG_freq,
185213
};
214+
#if SOC_SDMMC_HOST_SUPPORTED
215+
static const int DEFAULT_SLOT = 1;
216+
#else
217+
static const int DEFAULT_SLOT = SD_SLOT_MAX;
218+
#endif
186219
static const mp_arg_t allowed_args[] = {
187-
{ MP_QSTR_slot, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
220+
{ MP_QSTR_slot, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SLOT} },
188221
{ MP_QSTR_width, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
189222
{ MP_QSTR_cd, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
190223
{ MP_QSTR_wp, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
@@ -226,22 +259,35 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
226259
#endif
227260

228261
int slot_num = arg_vals[ARG_slot].u_int;
229-
if (slot_num < 0 || slot_num > 3) {
230-
mp_raise_ValueError(MP_ERROR_TEXT("slot number must be between 0 and 3 inclusive"));
262+
if (slot_num < SD_SLOT_MIN || slot_num > SD_SLOT_MAX) {
263+
mp_raise_ValueError(MP_ERROR_TEXT("invalid slot number"));
231264
}
232265

266+
#if SOC_SDMMC_HOST_SUPPORTED
233267
// Slots 0 and 1 are native SD/MMC, slots 2 and 3 are SPI
234268
bool is_spi = (slot_num >= 2);
269+
#else
270+
bool is_spi = true;
271+
#endif
235272
if (is_spi) {
236273
slot_num -= 2;
274+
assert(slot_num < NUM_SD_SPI_BUS);
237275
}
276+
238277
// Verify valid argument combinations
239278
#if SOC_SDMMC_USE_GPIO_MATRIX
240279
if (is_spi && (arg_vals[ARG_cmd].u_obj != mp_const_none
241280
|| arg_vals[ARG_data].u_obj != mp_const_none)) {
242281
mp_raise_ValueError(MP_ERROR_TEXT("invalid config: SPI slot with SDMMC pin arguments"));
243282
}
244283
#endif
284+
#if SOC_SDMMC_HOST_SUPPORTED
285+
if (!is_spi && (arg_vals[ARG_miso].u_obj != mp_const_none
286+
|| arg_vals[ARG_mosi].u_obj != mp_const_none
287+
|| arg_vals[ARG_cs].u_obj != mp_const_none)) {
288+
mp_raise_ValueError(MP_ERROR_TEXT("invalid config: SDMMC slot with SPI pin arguments"));
289+
}
290+
#endif
245291

246292
DEBUG_printf(" Setting up host configuration");
247293

@@ -253,21 +299,17 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
253299
if (is_spi) {
254300
sdmmc_host_t _temp_host = SDSPI_HOST_DEFAULT();
255301
_temp_host.max_freq_khz = freq / 1000;
302+
// SPI SDMMC sets the slot to the SPI host ID
303+
_temp_host.slot = spi_dev_defaults[slot_num].host_id;
256304
self->host = _temp_host;
257-
} else {
305+
}
306+
#if SOC_SDMMC_HOST_SUPPORTED
307+
else {
258308
sdmmc_host_t _temp_host = SDMMC_HOST_DEFAULT();
259309
_temp_host.max_freq_khz = freq / 1000;
260310
self->host = _temp_host;
261311
}
262-
263-
if (is_spi) {
264-
// Needs to match spi_dev_defaults above.
265-
#if CONFIG_IDF_TARGET_ESP32
266-
self->host.slot = slot_num ? HSPI_HOST : VSPI_HOST;
267-
#else
268-
self->host.slot = slot_num ? SPI2_HOST : SPI3_HOST;
269-
#endif
270-
}
312+
#endif
271313

272314
DEBUG_printf(" Calling host.init()");
273315

@@ -294,6 +336,15 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
294336
SET_CONFIG_PIN(dev_config, gpio_cd, ARG_cd);
295337
SET_CONFIG_PIN(dev_config, gpio_wp, ARG_wp);
296338

339+
// On chips other than original ESP32 and S3, there are not
340+
// always default SPI pins assigned
341+
if (dev_config.gpio_cs == GPIO_NUM_NC
342+
|| bus_config.miso_io_num == GPIO_NUM_NC
343+
|| bus_config.mosi_io_num == GPIO_NUM_NC
344+
|| bus_config.sclk_io_num == GPIO_NUM_NC) {
345+
mp_raise_ValueError(MP_ERROR_TEXT("SPI pin values required"));
346+
}
347+
297348
DEBUG_printf(" Calling spi_bus_initialize()");
298349
check_esp_err(spi_bus_initialize(spi_host_id, &bus_config, dma_channel));
299350

@@ -309,7 +360,9 @@ static mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args
309360
spi_bus_free(spi_host_id);
310361
mp_raise_ValueError(MP_ERROR_TEXT("SPI bus already in use"));
311362
}
312-
} else {
363+
}
364+
#if SOC_SDMMC_HOST_SUPPORTED
365+
else {
313366
// SD/MMC interface
314367
DEBUG_printf(" Setting up SDMMC slot configuration");
315368
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
357410
DEBUG_printf(" Calling init_slot()");
358411
check_esp_err(sdmmc_host_init_slot(self->host.slot, &slot_config));
359412
}
413+
#endif // SOC_SDMMC_HOST_SUPPORTED
360414

361415
DEBUG_printf(" Returning new card object: %p", self);
362416
return MP_OBJ_FROM_PTR(self);

0 commit comments

Comments
 (0)