1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #19786 from gschorcht/cpu/esp32/periph/sdmmc

cpu/esp32/periph: add low-level SDMMC peripheral driver for ESP32 and ESP32-S3
This commit is contained in:
benpicco 2024-01-07 16:51:16 +00:00 committed by GitHub
commit bbf5e23cf9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1014 additions and 102 deletions

View File

@ -7,7 +7,7 @@
*/
/**
* @ingroup boards_common
* @ingroup boards_common_esp32x
* @brief Board definitions that are common for all ESP32x boards.
*
* This file contains board configurations that are valid for all ESP32.
@ -103,17 +103,30 @@ extern "C" {
#if MODULE_MTD_SDCARD_DEFAULT || DOXYGEN
#define MTD_1 mtd_dev_get(1) /**< MTD device for the SD Card */
#elif MODULE_MTD_SDMMC_DEFAULT
#define MTD_1 mtd_dev_get(1) /**< MTD device for the SD/MMC Card */
#endif /* MODULE_MTD_SDCARD_DEFAULT || DOXYGEN */
/**
* @brief MTD offset for SD Card interfaces
* @brief Default MTD offset for SPI SD Card interfaces
*
* MTD_1 is used for SD Card.
* mtd1 is used for SPI SD Cards by default if module `mtd_sdcard_default`
* is used.
*/
#ifndef CONFIG_SDCARD_GENERIC_MTD_OFFSET
#define CONFIG_SDCARD_GENERIC_MTD_OFFSET 1
#endif
/**
* @brief Default MTD offset for SD/MMC interfaces
*
* mtd1 is used for SD/MMCs by default if module `mtd_sdmmc_default`
* is used.
*/
#ifndef CONFIG_SDMMC_GENERIC_MTD_OFFSET
#define CONFIG_SDMMC_GENERIC_MTD_OFFSET 1
#endif
/** @} */
#endif /* MODULE_MTD || DOXYGEN */

View File

@ -338,6 +338,10 @@ static const spi_conf_t spi_config[] = {
* @note SPI_NUMOF definition must not be changed.
*/
#define SPI_NUMOF ARRAY_SIZE(spi_config)
#if IS_USED(MODULE_PERIPH_SPI)
static_assert(SPI_NUMOF != 0, "No SPI devices defined");
#endif
/** @} */
/**

View File

@ -1 +1,13 @@
# if the sdcard_spi module is enabled, the SD Card Shield is used
ifneq (,$(filter sdcard_spi,$(USEMODULE)))
# default to using fatfs on SD card
ifneq (,$(filter vfs_default,$(USEMODULE)))
USEMODULE += fatfs_vfs
USEMODULE += mtd
endif
ifneq (,$(filter mtd,$(USEMODULE)))
USEMODULE += mtd_sdcard_default
endif
endif
include $(RIOTBOARD)/common/esp32/Makefile.dep

View File

@ -31,7 +31,7 @@ interesting development kit as it uses in the stackable Wemos D1 Mini format.
Thus, all [shields for Wemos D1 mini](https://docs.wemos.cc/en/latest/d1_mini_shield/index.html)
for ESP8266 can also be used with ESP32. Examples for such shields are:
- Micro SD-Card Shield
- Micro SD-Card Shield (enable module `sdcard_spi` to use it)
- MRF24J40 IEEE 802.15.4 radio Shield
- Button Shield
- RGB LED Shield

View File

@ -17,8 +17,11 @@ config BOARD_ESP32_OLIMEX_EVB
select HAS_PERIPH_ADC if USEMODULE_OLIMEX_ESP32_GATEWAY
select HAS_PERIPH_I2C
select HAS_PERIPH_PWM
select HAS_PERIPH_SDMMC
select HAS_PERIPH_SPI
select HAS_PERIPH_CAN
select HAS_PERIPH_IR
select HAVE_MTD_SDMMC_DEFAULT
source "$(RIOTBOARD)/common/esp32/Kconfig"

View File

@ -4,3 +4,13 @@ include $(RIOTBOARD)/common/esp32/Makefile.dep
ifneq (,$(filter netdev_default,$(USEMODULE)))
USEMODULE += esp_eth
endif
# default to using fatfs on SD card
ifneq (,$(filter vfs_default,$(USEMODULE)))
USEMODULE += fatfs_vfs
USEMODULE += mtd
endif
ifneq (,$(filter mtd,$(USEMODULE)))
USEMODULE += mtd_sdmmc_default
endif

View File

@ -3,13 +3,20 @@ CPU_MODEL = esp32-wroom_32
# common board and CPU features
include $(RIOTBOARD)/common/esp32/Makefile.features
# additional features provided by the board (no ADC and no DAC)
ifneq (,$(filter olimex_esp32_gateway,$(USEMODULE)))
# additional features provided by Olimex ESP32 Gateway
FEATURES_PROVIDED += periph_adc
else
# SPI interface is not available on Olimex ESP32 Gateway
FEATURES_PROVIDED += periph_spi
endif
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_sdmmc
FEATURES_CONFLICT += periph_sdmmc:periph_spi
FEATURES_CONFLICT_MSG += "SD/MMC and SPI cannot be used at the same time on this board."
# unique features of the board
FEATURES_PROVIDED += esp_eth # Ethernet MAC (EMAC)

View File

@ -92,15 +92,15 @@ overridden by \ref esp32_application_specific_configurations
<center>
Pin | Configuration\n ESP32-EVB | Configuration\n ESP32-GATEWAY | Remarks / Prerequisites | Configuration
:------|:------------------|:-----------------|-|-|
GPIO13 | I2C_DEV(0):SDA | SDCARD_CS | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_i2c_interfaces "I2C Interfaces"
GPIO13 | I2C_DEV(0):SDA | SDMMC_DEV(0):DAT3 | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_i2c_interfaces "I2C Interfaces", \ref esp32_sdmmc_interfaces "SDMMC Interfaces"
GPIO16 | I2C_DEV(0):SCL | I2C_DEV(0):SCL | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_i2c_interfaces "I2C Interfaces"
GPIO14 | SPI_DEV(0):CLK | SDCARD_CLK | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces"
GPIO2 | SPI_DEV(0):MISO | SDCARD_MISO | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces"
GPIO15 | SPI_DEV(0):MOSI | SDCARD_MOSI | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces"
GPIO14 | SPI_DEV(0):CLK, SDMMC_DEV(0):CLK | SDMMC_DEV(0):CLK | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces", \ref esp32_sdmmc_interfaces "SDMMC Interfaces"
GPIO2 | SPI_DEV(0):MISO, SDMMC_DEV(0):DAT0 | SDMMC_DEV(0):DAT0 | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces", \ref esp32_sdmmc_interfaces "SDMMC Interfaces"
GPIO15 | SPI_DEV(0):MOSI, SDMMC_DEV(0):CMD | SDMMC_DEV(0):CMD | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces", \ref esp32_sdmmc_interfaces "SDMMC Interfaces"
GPIO17 | SPI_DEV(0):CS0 | I2C_DEV(0):SDA | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_spi_interfaces "SPI Interfaces"
GPIO1 | UART_DEV(0):TxD | UART_DEV(0):TxD | Console (cannot be changed) | \ref esp32_uart_interfaces "UART interfaces"
GPIO3 | UART_DEV(0):RxD | UART_DEV(0):RxD | Console (cannot be changed) | \ref esp32_uart_interfaces "UART interfaces"
GPIO4 | UART_DEV(1):TxD | N/A | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_uart_interfaces "UART interfaces"
GPIO4 | UART_DEV(1):TxD | SDMMC_DEV(0):DAT1 | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_uart_interfaces "UART interfaces", \ref esp32_sdmmc_interfaces "SDMMC Interfaces"
GPIO36 | UART_DEV(1):RxD | ADC_LINE(2) | on ESP32-EVB available at [UEXT1](https://www.olimex.com/Products/Modules/UEXT) | \ref esp32_uart_interfaces "UART interfaces"
GPIO32 | Relais 1 | ADC_LINE(0) | | \ref esp32_adc_channels "ADC Channels"
GPIO33 | Relais 2 | LED0 | | |
@ -109,7 +109,7 @@ GPIO9 | PWM_DEV(0):0 | PWM_DEV(0):0 | | \ref esp32_pwm_channels "PWM C
GPIO10 | PWM_DEV(0):1 | PWM_DEV(0):1 | | \ref esp32_pwm_channels "PWM Channels"
GPIO5 | CAN_DEV(0):TX | | | \ref esp32_can_interfaces "CAN Interfaces"
GPIO35 | CAN_DEV(0):RX | ADC_LINE(1) | | \ref esp32_adc_channels "ADC Channels"
GPIO12 | IR_DEV(0):TX | N/A | IR is not yet supported | |
GPIO12 | IR_DEV(0):TX | SDMMC_DEV(0):DAT2 | IR is not yet supported | \ref esp32_sdmmc_interfaces "SDMMC Interfaces" |
GPIO39 | IR_DEV(0):RX | ADC_LINE(3) | IR is not yet supported | \ref esp32_adc_channels "ADC Channels"
GPIO18 | EMAC_SMI:MDIO | EMAC_SMI:MDIO | LAN interface | \ref esp32_ethernet_network_interface "Ethernet MAC"
GPIO23 | EMAC_SMI:MDC | EMAC_SMI:MDC | LAN interface | \ref esp32_ethernet_network_interface "Ethernet MAC"

View File

@ -36,6 +36,8 @@
#include <stdint.h>
#include "periph_cpu.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -139,6 +141,29 @@ extern "C" {
/** @} */
/**
* @name SD/MMC host controller configuration
* @{
*/
/** SDMMC devices */
static const sdmmc_conf_t sdmmc_config[] = {
{
.slot = SDMMC_SLOT_1,
.cd = GPIO_UNDEF,
.wp = GPIO_UNDEF,
#if MODULE_OLIMEX_ESP32_GATEWAY
.bus_width = 4,
#else
.bus_width = 1,
#endif
},
};
/** Number of configured SDMMC devices */
#define SDMMC_CONFIG_NUMOF 1
/** @} */
/**
* @name SPI configuration
* @{
@ -147,12 +172,7 @@ extern "C" {
* @brief HSPI is used as SPI_DEV(0)
*
* It is available at the [UEXT] connector on Olimex ESP32-EVB.
*
* Although the SD card interface of the Olimex ESP32-EVB is also available at
* the `SPI_DEV(0)` interface, it does not have a CS signal. Therefore,
* it cannot be used in SPI mode with the `sdcard_spi` module. Olimex
* ESP32-GATEWAY uses the integrated SD card interface with another GPIO for
* the CS signal.
* If the SD Card/MMC interface is used, the SPI interface is not available.
*
* @note The GPIOs listed in the configuration are first initialized as SPI
* signals when the corresponding SPI interface is used for the first time
@ -160,6 +180,8 @@ extern "C" {
* function. That is, they are not allocated as SPI signals before and can
* be used for other purposes as long as the SPI interface is not used.
*/
#if !MODULE_PERIPH_SDMMC && !MODULE_OLIMEX_ESP32_GATEWAY
#ifndef SPI0_CTRL
#define SPI0_CTRL HSPI
#endif
@ -173,14 +195,11 @@ extern "C" {
#ifndef SPI0_MOSI
#define SPI0_MOSI GPIO15 /**< MOSI [UEXT] / SD Card interface] */
#endif
#ifndef SPI0_CS0
#ifndef MODULE_OLIMEX_ESP32_GATEWAY
#define SPI0_CS0 GPIO17 /**< CS0 [UEXT] */
#else /* MODULE_OLIMEX_ESP32_GATEWAY */
#define SPI0_CS0 GPIO13 /**< CS0 SD Card interface */
#endif /* MODULE_OLIMEX_ESP32_GATEWAY */
#endif /* SPI0_CS0 */
#endif
#endif /* !MODULE_PERIPH_SDMMC && !MODULE_OLIMEX_ESP32_GATEWAY */
/** @} */
@ -201,12 +220,16 @@ extern "C" {
#define UART0_TXD GPIO1 /**< direct I/O pin for UART_DEV(0), can't be changed */
#define UART0_RXD GPIO3 /**< direct I/O pin for UART_DEV(0), can't be changed */
#if !MODULE_OLIMEX_ESP32_GATEWAY
#ifndef UART1_TXD
#define UART1_TXD GPIO4 /**< UART_DEV(1) TxD */
#endif
#ifndef UART1_RXD
#define UART1_RXD GPIO36 /**< UART_DEV(1) RxD */
#endif
#endif /* !MODULE_OLIMEX_ESP32_GATEWAY */
/** @} */
#ifdef __cplusplus

View File

@ -18,11 +18,13 @@ config BOARD_ESP32_WROVER_KIT
select HAS_PERIPH_ADC
select HAS_PERIPH_I2C
select HAS_PERIPH_PWM
select HAS_PERIPH_SDMMC
select HAS_PERIPH_SPI
select HAS_SDCARD_SPI
select HAVE_ILI9341
select HAVE_MTD_SDCARD_DEFAULT
select HAVE_MTD_SDMMC_DEFAULT if !MODULE_SDCARD_SPI
select MODULE_FATFS_VFS if MODULE_VFS_DEFAULT
source "$(RIOTBOARD)/common/esp32/Kconfig"

View File

@ -5,14 +5,17 @@ endif
# Sets up configuration for openocd
USEMODULE += esp_jtag
ifneq (,$(filter mtd,$(USEMODULE)))
USEMODULE += mtd_sdcard_default
endif
# default to using fatfs on SD card
ifneq (,$(filter vfs_default,$(USEMODULE)))
USEMODULE += fatfs_vfs
USEMODULE += mtd
endif
ifneq (,$(filter mtd,$(USEMODULE)))
ifeq (,$(filter sdcard_spi,$(USEMODULE)))
# use mtd_sdmmc_default if sdcard_spi isn't explicitly enabled
USEMODULE += mtd_sdmmc_default
endif
endif
include $(RIOTBOARD)/common/esp32/Makefile.dep

View File

@ -7,6 +7,7 @@ include $(RIOTBOARD)/common/esp32/Makefile.features
FEATURES_PROVIDED += periph_adc
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_sdmmc
FEATURES_PROVIDED += periph_spi
# unique features provided by the board

View File

@ -83,7 +83,7 @@ configuration can be overridden by
These abbreviations are used in subsequent tables:
*SDC* = SD-Card interface is used (module **sdcard_spi** is enabled)\n
*SDC* = SD-Card interface is used (module **periph_sdmmc** is enabled)\n
*CAM* = Camera is plugged in/used
<center>
@ -95,12 +95,18 @@ These abbreviations are used in subsequent tables:
| `ADC_LINE(2)` | `GPIO36` | `GPIO36` | - | - | `CAMERA_D4` | \ref esp32_adc_channels |
| `ADC_LINE(3)` | `GPIO39` | `GPIO39` | - | - | `CAMERA_D5` | \ref esp32_adc_channels |
| `PWM_DEV(0):0 / LED0` | `GPIO0` | `GPIO0` | - | - | `LED_RED` / `CAMERA_RESET` | \ref esp32_pwm_channels |
| `PWM_DEV(0):2 / LED2` | `GPIO4` | `GPIO4` | - | - | `LED_BLUE` / `CAMERA_D0` | \ref esp32_pwm_channels |
| `PWM_DEV(0):1 / LED2` | `GPIO4` | `GPIO4` | - | - | `LED_BLUE` / `CAMERA_D0` | \ref esp32_pwm_channels |
| `LED1` | `GPIO2` | `GPIO2` | `GPIO2` | `GPIO2` | `LED_GREEN` | |
| `I2C_DEV(0):SCL` | `GPIO27` | `GPIO27` | `GPIO27` | `GPIO27` | `CAMERA_SIO_C` | \ref esp32_i2c_interfaces |
| `I2C_DEV(0):SDA` | `GPIO26` | `GPIO26` | `GPIO26` | `GPIO27` | `CAMERA_SIO_D` | \ref esp32_i2c_interfaces |
| `I2C_DEV(0):SDA` | `GPIO26` | `GPIO26` | `GPIO26` | `GPIO26` | `CAMERA_SIO_D` | \ref esp32_i2c_interfaces |
| `UART_DEV(0):TX` | `GPIO1` | `GPIO1` | `GPIO1` | `GPIO1` | | \ref esp32_uart_interfaces |
| `UART_DEV(0):RX` | `GPIO3` | `GPIO3` | `GPIO3` | `GPIO3` | | \ref esp32_uart_interfaces |
| `SDMMC_DEV(0):CLK` | `GPIO14` | `GPIO14` | - | - | SD-Card | \ref esp32_sdmmc_interfaces |
| `SDMMC_DEV(0):CMD` | `GPIO15` | `GPIO` | - | - | SD-Card | \ref esp32_sdmmc_interfaces |
| `SDMMC_DEV(0):DAT0` | `GPIO2` | `GPIO2` | - | - | SD-Card | \ref esp32_sdmmc_interfaces |
| `SDMMC_DEV(0):DAT1` | `GPIO4` | `GPIO4` | - | - | SD-Card | \ref esp32_sdmmc_interfaces |
| `SDMMC_DEV(0):DAT2` | `GPIO12` | `GPIO` | - | - | SD-Card | \ref esp32_sdmmc_interfaces |
| `SDMMC_DEV(0):DAT3` | `GPIO13` | `GPIO` | - | - | SD-Card | \ref esp32_sdmmc_interfaces |
| `SPI_DEV(0):SCK` | `GPIO14` | `GPIO14` | `GPIO14` | `GPIO14` | HSPI: SD-Card / Peripherals | \ref esp32_spi_interfaces |
| `SPI_DEV(0):MOSI` | `GPIO15` | `GPIO15` | `GPIO15` | `GPIO15` | HSPI: SD-Card / Peripherals | \ref esp32_spi_interfaces |
| `SPI_DEV(0):CS0` | `GPIO13` | `GPIO13` | `GPIO13` | `GPIO13` | HSPI: SD-Card CS | \ref esp32_spi_interfaces |
@ -133,17 +139,18 @@ These abbreviations are used in subsequent tables:
</center>
Following table shows the default board configuration sorted by GPIOs.
Following table shows the default board configuration sorted by GPIOs depending
on used hardware.
<center>
| Pin | None | SDC | CAM | SDC+CAM | Remarks |
| Pin | None | SDC 4-bit | CAM | SDC 1-bit + CAM | Remarks |
|:-------|:-----------------------|:--------------------|:--------------------|:---------------------------|:-----|
| GPIO0 | PWM_DEV(0):0 / LED0 | PWM_DEV(0):0 / LED0 | CAMERA_RESET | CAMERA_RESET | |
| GPIO1 | UART_DEV(0):TX | UART_DEV(0):TX | UART_DEV(0):TX | UART_DEV(0):TX | |
| GPIO2 | SPI_DEV(0):MISO / LED1 | SPI_DEV(0):MISO | SPI_DEV(0):MISO | SPI_DEV(0):MISO | HSPI |
| GPIO2 | SPI_DEV(0):MISO / LED1 | SDMMC_DEV(0):DAT0 | SPI_DEV(0):MISO | SDMMC_DEV(0):DAT0 | HSPI |
| GPIO3 | UART_DEV(0):RX | UART_DEV(0):RX | UART_DEV(0):RX | UART_DEV(0):RX | |
| GPIO4 | PWM_DEV(0):1 / LED2 | PWM_DEV(0):1 / LED2 | CAMERA_D0 | CAMERA_D0 | |
| GPIO4 | PWM_DEV(0):1 / LED2 | SDMMC_DEV(0):DAT1 | CAMERA_D0 | CAMERA_D0 | |
| GPIO5 | LCD LED | LCD_LED | CAMERA_D1 | CAMERA_D1 | |
| GPIO6 | Flash CLK | Flash CLK | Flash CLK | Flash CLK | |
| GPIO7 | Flash SD0 | Flash SD0 | Flash SD0 | Flash SD0 | |
@ -151,10 +158,10 @@ Following table shows the default board configuration sorted by GPIOs.
| GPIO9 | | | | | |
| GPIO10 | | | | | |
| GPIO11 | Flash CMD | Flash CMD | Flash CMD | Flash CMD | |
| GPIO12 | | | | | |
| GPIO13 | SPI_DEV(0):CS0 | SPI_DEV(0):CS0 | SPI_DEV(0):CS0 | SPI_DEV(0):CS0 | HSPI / SD-Card CS |
| GPIO14 | SPI_DEV(0):SCK | SPI_DEV(0):SCK | SPI_DEV(0):SCK | SPI_DEV(0):SCK | HSPI |
| GPIO15 | SPI_DEV(0):MOSI | SPI_DEV(0):MOSI | SPI_DEV(0):MOSI | SPI_DEV(0):MOSI | HSPI |
| GPIO12 | | SDMMC_DEV(0):DAT2 | | | |
| GPIO13 | SPI_DEV(0):CS0 | SDMMC_DEV(0):DAT3 | SPI_DEV(0):CS0 | | HSPI / SPI SD-Card CS |
| GPIO14 | SPI_DEV(0):SCK | SDMMC_DEV(0):CLK | SPI_DEV(0):SCK | | HSPI |
| GPIO15 | SPI_DEV(0):MOSI | SDMMC_DEV(0):CMD | SPI_DEV(0):MOSI | | HSPI |
| GPIO16 | N/A | N/A | N/A | N/A | see below |
| GPIO17 | N/A | N/A | N/A | N/A | see below |
| GPIO18 | LCD_RESET | LCD_RESET | LCD_RESET | CAMERA_D2 | |

View File

@ -46,6 +46,7 @@
#define PERIPH_CONF_H
#include <stdint.h>
#include "periph_cpu.h"
#ifdef __cplusplus
extern "C" {
@ -109,10 +110,6 @@
*
* LEDs are used as PWM channels for device PWM_DEV(0).
*
* @note As long as the according PWM device is not initialized with function
* pwm_init, the GPIOs declared for this device can be used for other
* purposes.
*
* @note As long as the according PWM device is not initialized with
* the `pwm_init`, the GPIOs declared for this device can be used
* for other purposes.
@ -120,8 +117,10 @@
* @{
*/
#ifndef PWM0_GPIOS
#if !MODULE_ESP32_WROVER_KIT_CAMERA || DOXYGEN
#if (!MODULE_ESP32_WROVER_KIT_CAMERA && !MODULE_PERIPH_SDMMC) || DOXYGEN
#define PWM0_GPIOS { GPIO0, GPIO4 } /**< only available when camera is not connected */
#elif !MODULE_ESP32_WROVER_KIT_CAMERA
#define PWM0_GPIOS { GPIO0 }
#else
#define PWM0_GPIOS { }
#endif
@ -129,6 +128,33 @@
/** @} */
/**
* @name SD/MMC host controller configuration
*
* @warning If the camera is plugged in, the SD Card has to be used in
* 1-bit mode.
* @{
*/
/** SDMMC devices */
static const sdmmc_conf_t sdmmc_config[] = {
{
.slot = SDMMC_SLOT_1,
.cd = GPIO21,
.wp = GPIO_UNDEF,
#if MODULE_ESP32_WROVER_KIT_CAMERA
/* if camera used, only DAT0 is available */
.bus_width = 1,
#else
.bus_width = 4,
#endif
},
};
/** Number of configured SDMMC devices */
#define SDMMC_CONFIG_NUMOF 1
/** @} */
/**
* @name SPI configuration
*
@ -137,6 +163,12 @@
* HSPI is always available and therefore used as SPI_DEV(0)
* VSPI is only available when the camera is not plugged.
*
* @warning In order not to change the index of the SPI devices depending on
* the different hardware configuration options including the camera,
* SPI_DEV(0) is also defined in case of using the SD/MMC host
* controller, by default but cannot be used once an SD card is
* inserted. Use SPI_DEV(1) instead in this case.
*
* @{
*/

View File

@ -14,6 +14,9 @@ config CPU_FAM_ESP32
select HAS_ESP_BLE
select HAS_ESP_BLE_ESP32
select HAS_PUF_SRAM
select HAS_PERIPH_SDMMC_AUTO_CLK
select HAS_PERIPH_SDMMC_AUTO_CMD12
select HAS_PERIPH_SDMMC_MMC
config CPU_FAM
default "esp32" if CPU_FAM_ESP32

View File

@ -15,6 +15,10 @@ config CPU_FAM_ESP32S3
select HAS_BLE_PHY_2MBIT
select HAS_ESP_BLE
select HAS_ESP_BLE_ESP32C3
select HAS_PERIPH_SDMMC_AUTO_CLK
select HAS_PERIPH_SDMMC_AUTO_CMD12
select HAS_PERIPH_SDMMC_HS
select HAS_PERIPH_SDMMC_MMC
select MODULE_USBDEV_SYNOPSYS_DWC2 if MODULE_PERIPH_USBDEV
select MODULE_USB_BOARD_RESET if MODULE_USBUS_CDC_ACM || MODULE_TINYUSB_CLASS_CDC

View File

@ -26,6 +26,7 @@ config CPU_COMMON_ESP32X
select PACKAGE_ESP32_SDK if TEST_KCONFIG
select MODULE_PERIPH_GPIO_IRQ if MODULE_PERIPH_SDMMC
select MODULE_PERIPH_RTT if HAS_PERIPH_RTT && MODULE_PM_LAYERED
select MODULE_PS if MODULE_SHELL
select MODULE_PTHREAD if MODULE_CPP

View File

@ -105,6 +105,11 @@ ifneq (,$(filter periph_i2c,$(USEMODULE)))
endif
endif
ifneq (,$(filter periph_sdmmc,$(USEMODULE)))
USEMODULE += esp_idf_sdmmc
USEMODULE += periph_gpio_irq
endif
ifneq (,$(filter esp_spi_ram,$(USEMODULE)))
FEATURES_REQUIRED += esp_spi_ram
FEATURES_OPTIONAL += esp_spi_oct

View File

@ -46,6 +46,15 @@ else ifneq (,$(filter esp32c3 esp32s3,$(CPU_FAM)))
FEATURES_PROVIDED += esp_ble_esp32c3
endif
ifneq (,$(filter esp32 esp32s3,$(CPU_FAM)))
FEATURES_PROVIDED += periph_sdmmc_auto_clk
FEATURES_PROVIDED += periph_sdmmc_auto_cmd12
FEATURES_PROVIDED += periph_sdmmc_mmc
ifeq (esp32s3,$(CPU_FAM))
FEATURES_PROVIDED += periph_sdmmc_hs
endif
endif
ifneq (,$(filter esp32-wrover% esp32s2%r2 esp32s3%r2 esp32s3%r8 esp32s3%r8v,$(CPU_MODEL)))
FEATURES_PROVIDED += esp_spi_ram
ifneq (,$(filter esp32s3%r8 esp32s3%r8v,$(CPU_MODEL)))

View File

@ -55,13 +55,14 @@ This document describes the RIOT implementation for supported variants
3. [DAC Channels](#esp32_dac_channels)
4. [I2C Interfaces](#esp32_i2c_interfaces)
5. [PWM Channels](#esp32_pwm_channels)
6. [SPI Interfaces](#esp32_spi_interfaces)
7. [Timers](#esp32_timers)
8. [RTT Implementation](#esp32_rtt_counter)
9. [UART Interfaces](#esp32_uart_interfaces)
10. [CAN Interfaces](#esp32_can_interfaces)
11. [Power Management](#esp32_power_management)
12. [Other Peripherals](#esp32_other_peripherals)
6. [SDMMC Interfaces](#esp32_sdmmc_interfaces)
8. [SPI Interfaces](#esp32_spi_interfaces)
9. [Timers](#esp32_timers)
10. [RTT Implementation](#esp32_rtt_counter)
12. [UART Interfaces](#esp32_uart_interfaces)
13. [CAN Interfaces](#esp32_can_interfaces)
14. [Power Management](#esp32_power_management)
15. [Other Peripherals](#esp32_other_peripherals)
7. [Special On-board Peripherals](#esp32_special_on_board_peripherals)
1. [SPI RAM Modules](#esp32_spi_ram)
2. [SPIFFS Device](#esp32_spiffs_device)
@ -212,13 +213,14 @@ The key features of ESP32 are:
| Flash | 512 KiB ... 16 MiB | yes |
| Frequency | 240 MHz, 160 MHz, 80 MHz | yes |
| Power Consumption | 68 mA @ 240 MHz <br> 44 mA @ 160 MHz (34 mA @ 160 MHz single core) <br> 31 mA @ 80 MHz (25 mA @ 80 MHz single core) <br> 800 uA in light sleep mode <br> 10 uA in deep sleep mode | yes <br> yes <br> yes <br> yes <br> yes |
| Timers | 4 x 64 bit | yes |
| ADCs | 2 x SAR-ADC with up to 18 x 12 bit channels total | yes |
| DACs | 2 x DAC with 8 bit | yes |
| GPIOs | 34 (6 are only inputs, 18 are RTC GPIOs) | yes |
| I2Cs | 2 | yes |
| SPIs | 4 | yes (2) |
| UARTs | 3 | yes |
| Timer | 4 x 64 bit | yes |
| ADC | 2 x SAR-ADC with up to 18 x 12 bit channels total | yes |
| DAC | 2 x DAC with 8 bit | yes |
| GPIO | 34 (6 are only inputs, 18 are RTC GPIOs) | yes |
| I2C | 2 | yes |
| SDMMC | 2 | yes |
| SPI | 4 | yes (2) |
| UART | 3 | yes |
| WiFi | IEEE 802.11 b/g/n built in | yes |
| Bluetooth | v4.2 BR/EDR and BLE | yes |
| Ethernet | MAC interface with dedicated DMA and IEEE 1588 support | yes |
@ -248,13 +250,13 @@ The key features of ESP32-C3 are:
| Flash | 8 MiB | yes |
| Frequency | 160 MHz, 80 MHz | yes |
| Power Consumption | 20 mA @ 240 MHz <br> 15 mA @ 80 MHz <br> 130 uA in light sleep mode <br> 5 uA in deep sleep mode | yes <br> yes <br> yes <br> yes |
| Timers | 2 x 54 bit | yes |
| ADCs | 2 x SAR-ADC with up to 6 x 12 bit channels total | yes |
| DACs | - | - |
| GPIOs | 22 | yes |
| I2Cs | 1 | yes |
| SPIs | 3 | yes (1) |
| UARTs | 2 | yes |
| Timer | 2 x 54 bit | yes |
| ADC | 2 x SAR-ADC with up to 6 x 12 bit channels total | yes |
| DAC | - | - |
| GPIO | 22 | yes |
| I2C | 1 | yes |
| SPI | 3 | yes (1) |
| UART | 2 | yes |
| WiFi | IEEE 802.11 b/g/n built in | yes |
| Bluetooth | Bluetooth 5 (LE) | yes |
| Ethernet | - | - |
@ -284,13 +286,13 @@ The key features of ESP32-S2 are:
| Flash | 512 KiB ... 32 MiB Dual/Quad/Octal SPI (external or internal) | yes |
| Frequency | 240 MHz, 160 MHz, 80 MHz | yes |
| Power Consumption | 66 mA @ 240 MHz <br> 50 mA @ 160 MHz (40 mA @ 160 MHz single core) <br> 33 mA @ 80 MHz (28 mA @ 80 MHz single core) <br> 19 mA @ 40 MHz (16 mA @ 40 MHz single core) <br>240 uA in light sleep mode <br> 8 uA in deep sleep mode | yes <br> yes <br> yes <br> yes <br> yes <br> yes |
| Timers | 4 x 54 bit | yes |
| ADCs | 2 x SAR-ADC with up to 20 x 13 bit channels total | yes |
| DACs | 2 x DAC with 8 bit | - |
| GPIOs | 43 (22 are RTC GPIOs) | yes |
| I2Cs | 2 | yes |
| SPIs | 4 | yes (2) |
| UARTs | 2 | yes |
| Timer | 4 x 54 bit | yes |
| ADC | 2 x SAR-ADC with up to 20 x 13 bit channels total | yes |
| DAC | 2 x DAC with 8 bit | - |
| GPIO | 43 (22 are RTC GPIOs) | yes |
| I2C | 2 | yes |
| SPI | 4 | yes (2) |
| UART | 2 | yes |
| WiFi | IEEE 802.11 b/g/n built in | yes |
| Bluetooth | - | - |
| Ethernet | - | - |
@ -320,13 +322,14 @@ The key features of ESP32-S3 are:
| Flash | 512 KiB ... 32 MiB Dual/Quad/Octal SPI (external or internal) | yes |
| Frequency | 240 MHz, 160 MHz, 80 MHz | yes |
| Power Consumption | 66 mA @ 240 MHz <br> 50 mA @ 160 MHz (40 mA @ 160 MHz single core) <br> 33 mA @ 80 MHz (28 mA @ 80 MHz single core) <br> 19 mA @ 40 MHz (16 mA @ 40 MHz single core) <br>240 uA in light sleep mode <br> 8 uA in deep sleep mode | yes <br> yes <br> yes <br> yes <br> yes <br> yes |
| Timers | 4 x 54 bit | yes |
| ADCs | 2 x SAR-ADC with up to 20 x 12 bit channels total | yes |
| DACs | - | - |
| GPIOs | 45 (22 are RTC GPIOs) | yes |
| I2Cs | 2 | yes |
| SPIs | 4 | yes (2) |
| UARTs | 3 | yes |
| Timer | 4 x 54 bit | yes |
| ADC | 2 x SAR-ADC with up to 20 x 12 bit channels total | yes |
| DAC | - | - |
| GPIO | 45 (22 are RTC GPIOs) | yes |
| I2C | 2 | yes |
| SDMMC | 2 | yes |
| SPI | 4 | yes (2) |
| UART | 3 | yes |
| WiFi | IEEE 802.11 b/g/n built in | yes |
| Bluetooth | Bluetooth 5 (LE) | yes |
| Ethernet | - | - |
@ -1012,6 +1015,62 @@ definition of `PWM0_GPIOS`, `PWM1_GPIOS`, `PWM2_GPIOS` and `PWM3_GPIOS`.
[Back to table of contents](#esp32_toc)
## SDMMC Interfaces {#esp32_sdmmc_interfaces}
ESP32 and ESP32-S3 variants integrate a SD/MMC host controller which supports
two slots for
- SD Memory Cards,
- SDIO Cards and
- MMC/eMMCs.
The SD/MMC host controller on the ESP32 variant
- supports 1-bit, 4-bit and 8-bit data bus width for slot 0 (@ref SDMMC_SLOT_0),
- supports 1-bit and 4-bit data bus width for slot 1 (@ref SDMMC_SLOT_1),
- uses direct I/O for SD/MMC signals so that the GPIOs used for the slots are
therefore fixed.
The SD/MMC host controller on the ESP32-S3 variant
- supports 1-, 4- and 8-bit data bus width for both slots
- uses the GPIO matrix to connect the SD/MMC signals to the GPIOs so that
all pins are configurable.
@note Since the GPIOs are fixed on the ESP32 variant and the same GPIOs are
used for slot 0 and the flash, slot 0 cannot be used on ESP32 variant.
The board-specific configuration is realized by defining the array
@ref sdmmc_config
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
static const sdmmc_conf_t sdmmc_config[] = {
{
.slot = SDMMC_SLOT_1,
.cd = GPIO_UNDEF,
.wp = GPIO_UNDEF,
...
},
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
and the macro `SDMMC_NUMOF`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
#define SDMMC_NUMOF 1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
where the value of @ref SDMMC_NUMOF must correspond to the number of elements
in @ref sdmmc_config.
While for the ESP32 variant it is sufficient to define the data bus width, used
GPIOs have to be defined in the configuration for the ESP32-S3 variant instead.
For details of ESP32x variant specific configuration, see:
- \ref esp32_sdmmc_interfaces_esp32 "ESP32"
- \ref esp32_sdmmc_interfaces_esp32s3 "ESP32-S3"
If the board supports a Card Detect pin or a Write Protect pin, the
corresponding GPIOs have to be defined in @ref sdmmc_conf_t::cd and
@ref sdmmc_conf_t::wp. Otherwise they have to be set to undefined
(@ref GPIO_UNDEF).
## SPI Interfaces {#esp32_spi_interfaces}
ESP32x SoCs have up to four SPI controllers dependent on the specific ESP32x

View File

@ -139,6 +139,52 @@ I2C_DEV(0) | SDA | GPIO21 | `#I2C0_SDA` | -
The ESP32 LEDC module has 2 channel groups with 8 channels each. Each of
these channels can be clocked by one of the 4 timers.
## SDMMC Interfaces {#esp32_sdmmc_interfaces_esp32}
The ESP32 variant uses the direct I/O (i.e. `SOC_SDMMC_USE_IOMUX` is defined in
the SoC capabilities file). The GPIOs used for SDMMC signals are therefore fixed
for each slot. Since the GPIOs used for slot 0 are the same as those used
for the Flash, slot 0 cannot be used. Therefore, only slot 1 can be used.
The GPIOs used by ESP32 for slot 1 are:
<center>
| Signal | GPIO Slot 1 |
|:------ |:------------|
| CLK | GPIO14 |
| CMD | GPIO15 |
| DAT0 | GPIO2 |
| DAT1 | GPIO4 |
| DAT2 | GPIO12 |
| DAT3 | GPIO13 |
</center>
The board-specific configuration is realized by defining the @ref sdmmc_config
array, for example:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
static const sdmmc_conf_t sdmmc_config[] = {
{
.slot = SDMMC_SLOT_1,
.cd = GPIO21,
.wp = GPIO_UNDEF,
.bus_width = 1,
},
};
#define SDMMC_NUMOF 1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Used data bus width has to be defined in sdmmc_conf_t::bus_width in addition
to the slot, where 1 and 4 are valid values.
@note The slot must be @ref SDMMC_SLOT_1 for ESP32.
If the board supports a Card Detect pin or a Write Protect
pin, the corresponding GPIOs have to be defined in @ref sdmmc_conf_t::cd and
@ref sdmmc_conf_t::wp. Otherwise they have to be set to undefined
(@ref GPIO_UNDEF).
## SPI Interfaces {#esp32_spi_interfaces_esp32}
ESP32 has four SPI controllers where SPI0 and SPI1 share the same bus and

View File

@ -209,6 +209,54 @@ I2C_DEV(0) | SDA | GPIO8 | `#I2C0_SDA` | -
The ESP32-S3 LEDC module has 1 channel group with 8 channels. Each of
these channels can be clocked by one of the 4 timers.
## SDMMC Interfaces {#esp32_sdmmc_interfaces_esp32s3}
The ESP32-S3 variant uses the GPIO matrix (i.e. `SOC_SDMMC_USE_GPIO_MATRIX`
is defined in the SoC Capabilities file) to route the SDMMC signals to
arbitrary pins. The GPIOs used for the SDMMC signals are therefore
configurable and have to be defined in the board-specific configuration in
array @ref sdmmc_config in addition to the used slot.
The width of the data bus used is determined by the GPIOs defined for the
DAT lines. To use a 1-bit data bus, only DAT0 (@ref sdmmc_conf_t::dat0)
must be defined. All other GPIOs for the DAT lines must be set undefined
(@ref GPIO_UNDEF). For a 4-bit data bus, the GPIOs for pins DAT1 to DAT3
(@ref sdmmc_conf_t::dat1 ... @ref sdmmc_conf_t::dat3) must also be defined.
An 8-bit data bus width requires the definition of DAT4 to DAT7
(@ref sdmmc_conf_t::dat4 ... @ref sdmmc_conf_t::dat7) and the enabling
of the `periph_sdmmc_8bit` module.
The following example shows a configuration with 4-bit or 8-bit data
bus width dependent on whether the `periph_sdmmc_8bit` module is enabled.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
static const sdmmc_conf_t sdmmc_config[] = {
{
.slot = SDMMC_SLOT_0,
.cd = GPIO16,
.wp = GPIO_UNDEF,
.clk = GPIO14,
.cmd = GPIO15,
.dat0 = GPIO2,
.dat1 = GPIO4,
.dat2 = GPIO12,
.dat3 = GPIO13,
#if IS_USED(MODULE_PERIPH_SMMC_8BIT)
.dat4 = GPIO33,
.dat5 = GPIO33,
.dat6 = GPIO33,
.dat7 = GPIO33,
#endif
},
};
#define SDMMC_NUMOF 1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the board supports a Card Detect pin or a Write Protect pin, the
corresponding GPIOs have to be defined in @ref sdmmc_conf_t::cd and
@ref sdmmc_conf_t::wp. Otherwise they have to be set to undefined
(@ref GPIO_UNDEF).
## SPI Interfaces {#esp32_spi_interfaces_esp32s3}
ESP32-S3 has four SPI controllers where SPI0 and SPI1 share the same bus

View File

@ -29,6 +29,7 @@ rsource "heap/Kconfig"
rsource "lcd/Kconfig"
rsource "nvs_flash/Kconfig"
rsource "rmt/Kconfig"
rsource "sdmmc/Kconfig"
rsource "spi_flash/Kconfig"
rsource "spi_ram/Kconfig"
rsource "usb/Kconfig"

View File

@ -48,6 +48,10 @@ ifneq (,$(filter esp_idf_rmt,$(USEMODULE)))
DIRS += rmt
endif
ifneq (,$(filter esp_idf_sdmmc,$(USEMODULE)))
DIRS += sdmmc
endif
ifneq (,$(filter esp_idf_spi_flash,$(USEMODULE)))
DIRS += spi_flash
endif

View File

@ -0,0 +1,17 @@
# Copyright (c) 2021 HAW Hamburg
# 2022 Gunar Schorcht
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
config MODULE_ESP_IDF_SDMMC
bool
depends on TEST_KCONFIG
depends on MODULE_ESP_IDF
default y if MODULE_PERIPH_SDMMC
select PACKAGE_TLSF
help
ESP-IDF heap library. This library is required if external SPI RAM
or the WiFi interface is used.

View File

@ -0,0 +1,18 @@
MODULE = esp_idf_sdmmc
# source files to be compiled for this module
ESP32_SDK_SRC = \
components/driver/sdmmc_host.c \
components/driver/sdmmc_transaction.c \
components/soc/$(CPU_FAM)/sdmmc_periph.c \
#
# additional include pathes required by this module
# INCLUDES += -I$(ESP32_SDK_DIR)/components/driver/include
include $(RIOTBASE)/Makefile.base
ESP32_SDK_BIN = $(BINDIR)/$(MODULE)
include ../esp_idf.mk
include ../esp_idf_cflags.mk

View File

@ -52,6 +52,7 @@ extern "C" {
#define CPU_INUM_FRC2 20 /**< Level interrupt with medium priority 2 */
#define CPU_INUM_SYSTIMER 20 /**< Level interrupt with medium priority 2 */
#define CPU_INUM_BLE 21 /**< Level interrupt with medium priority 2 */
#define CPU_INUM_SDMMC 23 /**< Level interrupt with medium priority 2 */
#define CPU_INUM_CACHEERR 25 /**< Level interrupt with high priority 4 */
/** @} */

View File

@ -604,6 +604,82 @@ typedef struct {
/** @} */
/**
* @name SDMMC configuration
*
* ESP32x SoC with SDMMC peripheral provide two SDMMC interfaces called slots.
* How many slots can be used depends on the ESP32x SoC, see @ref sdmmc_slot_t.
*
* @{
*/
/**
* @brief SDIO/SDMMC slots
*
* ESP32x SoCs that have a SDMMC peripheral provide two SDMMC interfaces called
* slots.
*
* @note If the ESP32x variant uses direct I/O functions for the SDMMC signals
* (i.e. `SOC_SDMMC_USE_IOMUX` is defined in SoC capabilities), the
* GPIOs used for the SDMMC slots are fixed. In this case, slot 0
* can't be used because the GPIOs are defined for Slot 0 are the
* same as those used for the Flash. If the ESP32x variant uses
* the GPIO matrix to route the SDMMC signals to arbitrary pins
* (i.e. `SOC_SDMMC_USE_GPIO_MATRIX` is defined in SoC capabilities),
* slot 0 can be used but the GPIOs used for the slot have to be
* different from those used for the Flash.
*/
typedef enum {
#if IS_USED(SOC_SDMMC_USE_GPIO_MATRIX) || DOXYGEN
SDMMC_SLOT_0 = 0, /**< SD/MMC host controller slot 0 (not usable on ESP32 variant) */
#endif
SDMMC_SLOT_1 = 1, /**< SD/MMC host controller slot 1 */
} sdmmc_slot_t;
/**
* @brief SDMMC slot configuration
*
* If the ESP32x variant uses the GPIO matrix to route the SDMMC signals
* to arbitrary pins (i.e. `SOC_SDMMC_USE_GPIO_MATRIX` is defined in SoC
* capabilities file), the pins must be configured. The bus width is then
* determined from the defined pins. Define the pins for the DAT lines
* as `GPIO_UNDEF` to use a smaller data bus width.
* If the ESP32x variant uses direct I/O (i.e. `SOC_SDMMC_USE_IOMUX` is
* defined in SoC capabilities file), the bus width has to be specified instead.
*/
typedef struct {
sdmmc_slot_t slot; /**< SDMMC slot used [ SDMMC_SLOT_0 | SDMMC_SLOT_1] */
gpio_t cd; /**< Card Detect pin (must be GPIO_UNDEF if not connected) */
gpio_t wp; /**< Write Protect pin (must be GPIO_UNDEF if not connected) */
#if IS_USED(SOC_SDMMC_USE_GPIO_MATRIX) || DOXYGEN
gpio_t clk; /**< CLK pin (must be defined) */
gpio_t cmd; /**< CMD pin (must be defined) */
gpio_t dat0; /**< DAT[0] pin (must be defined) */
gpio_t dat1; /**< DAT[1] pin (GPIO_UNDEF if not connected) */
gpio_t dat2; /**< DAT[2] pin (GPIO_UNDEF if not connected) */
gpio_t dat3; /**< DAT[3] pin (GPIO_UNDEF if not connected) */
#if IS_USED(MODULE_PERIPH_SMMC_8BIT) || DOXYGEN
gpio_t dat4; /**< DAT[4] pin (GPIO_UNDEF if not connected) */
gpio_t dat5; /**< DAT[5] pin (GPIO_UNDEF if not connected) */
gpio_t dat6; /**< DAT[6] pin (GPIO_UNDEF if not connected) */
gpio_t dat7; /**< DAT[7] pin (GPIO_UNDEF if not connected) */
#endif /* IS_USED(MODULE_PERIPH_SMMC_8BIT) */
#else /* IS_USED(SOC_SDMMC_USE_IOMUX) */
uint8_t bus_width; /**< Bus width */
#endif
} sdmmc_conf_t;
/**
* @brief SDIO/SDMMC buffer instantiation requirement for SDHC
*/
#define SDMMC_CPU_DMA_REQUIREMENTS __attribute__((aligned(SDMMC_CPU_DMA_ALIGNMENT)))
/**
* @brief SDIO/SDMMC buffer alignment for SDHC because of DMA/FIFO buffer restrictions
*/
#define SDMMC_CPU_DMA_ALIGNMENT 4
/** @} */
/**
* @name SPI configuration
*
@ -857,7 +933,8 @@ typedef struct {
* @name USB device configuration
* @{
*
* ESP32x SoCs integrate depending on the specific ESP32x SoC variant (family) an USB OTG FS controller based on the Synopsys DWC2 IP core.
* ESP32x SoCs integrate depending on the specific ESP32x SoC variant (family)
* an USB OTG FS controller based on the Synopsys DWC2 IP core.
*/
#include "usbdev_synopsys_dwc2.h"

View File

@ -95,8 +95,6 @@ extern "C" {
* - Vref can be read with function #adc_line_vref_to_gpio at GPIO25.
*/
/** @} */
/**
* @name DAC configuration
*
@ -130,6 +128,10 @@ extern "C" {
* these channels can be clocked by one of the 4 timers.
*/
/**
* @name SDMMC configuration
*/
/**
* @name SPI configuration
*
@ -213,6 +215,7 @@ extern "C" {
#define TIMER_NUMOF (2)
#define TIMER_CHANNEL_NUMOF (1)
#endif
/** @} */
/**
* @name UART configuration

View File

@ -92,6 +92,9 @@ static const struct intr_handle_data_t _irq_data_table[] = {
#elif defined(CPU_FAM_ESP32S3)
{ ETS_LCD_CAM_INTR_SOURCE, CPU_INUM_LCD, 1 },
#endif
#if defined(CPU_FAM_ESP32) || defined(CPU_FAM_ESP32S2) || defined(CPU_FAM_ESP32S3)
{ ETS_SDIO_HOST_INTR_SOURCE, CPU_INUM_SDMMC, 2 },
#endif
};
#define IRQ_DATA_TABLE_SIZE ARRAY_SIZE(_irq_data_table)

481
cpu/esp32/periph/sdmmc.c Normal file
View File

@ -0,0 +1,481 @@
/*
* Copyright (C) 2023 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp32
* @{
*
* @file
* @brief Low-level SDIO/SD/MMC peripheral driver interface for ESP32
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include "assert.h"
#include "bitarithm.h"
#include "container.h"
#include "log.h"
#include "periph/gpio.h"
#include "syscalls.h"
#include "ztimer.h"
#include "sdmmc/sdmmc.h"
#include "driver/sdmmc_types.h"
#include "driver/sdmmc_host.h"
#include "soc/sdmmc_reg.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/* CLK_EDGE_SEL - clock phase selection register */
#define SDMMC_CLOCK_REG_CCLKIN_EDGE_SAM_SEL_S (3)
#define SDMMC_CLOCK_REG_CCLKIN_EDGE_SAM_SEL_M (0x7 << SDMMC_CLOCK_REG_CCLKIN_EDGE_SAM_SEL_S)
/* we have to redefine it here since we can't include "gpio_types.h" due to
* naming conflicts */
#define GPIO_NUM_NC (GPIO_UNDEF)
/* debounce time for CD pin */
#define CONFIG_CD_PIN_DEBOUNCE_US 25000
/* limit the Default and High Speed clock rates for debugging */
#if CONFIG_SDMMC_CLK_MAX_400KHZ
#define CONFIG_SDMMC_CLK_MAX KHZ(400)
#elif CONFIG_SDMMC_CLK_MAX_1MHZ
#define CONFIG_SDMMC_CLK_MAX MHZ(1)
#elif CONFIG_SDMMC_CLK_MAX_4MHZ
#define CONFIG_SDMMC_CLK_MAX MHZ(4)
#elif CONFIG_SDMMC_CLK_MAX_10MHZ
#define CONFIG_SDMMC_CLK_MAX MHZ(10)
#elif CONFIG_SDMMC_CLK_MAX_20MHZ || !IS_USED(MODULE_PERIPH_SDMMC_HS)
#define CONFIG_SDMMC_CLK_MAX MHZ(20)
#else
#define CONFIG_SDMMC_CLK_MAX MHZ(40)
#endif
/* millisecond timer definitions dependent on active ztimer backend */
#if IS_USED(MODULE_ZTIMER_MSEC)
#define _ZTIMER_SLEEP_MS(n) ztimer_sleep(ZTIMER_MSEC, n)
#elif IS_USED(MODULE_ZTIMER_USEC)
#define _ZTIMER_SLEEP_MS(n) ztimer_sleep(ZTIMER_USEC, n * US_PER_MS)
#else
#error "Either ztimer_msec or ztimer_usec is needed"
#endif
/* forward declaration of _driver */
static const sdmmc_driver_t _driver;
/* driver related */
typedef struct {
sdmmc_dev_t sdmmc_dev; /**< Inherited sdmmc_dev_t struct */
const sdmmc_conf_t *config; /**< SDIO/SD/MMC peripheral config */
uint32_t last_cd_pin_irq; /**< Last CD Pin IRQ time for debouncing */
bool data_transfer; /**< Transfer active */
} esp32_sdmmc_dev_t;
static esp32_sdmmc_dev_t _sdmmc_devs[] = {
{
.sdmmc_dev = {
.driver = &_driver,
},
.config = &sdmmc_config[0],
},
#if SDMMC_CONFIG_NUMOF == 2
{
.sdmmc_dev = {
.driver = &_driver,
},
.config = &sdmmc_config[1],
}
#endif
};
/* sanity check of configuration */
static_assert(SDMMC_CONFIG_NUMOF == ARRAY_SIZE(sdmmc_config),
"SDMMC_CONFIG_NUMOF and the number of elements in sdmmc_config differ");
static_assert(SDMMC_CONFIG_NUMOF == ARRAY_SIZE(_sdmmc_devs),
"SDMMC_CONFIG_NUMOF and the number of elements in sdmmc_devs differ");
static_assert(SDMMC_CONFIG_NUMOF <= SOC_SDMMC_NUM_SLOTS,
"SDMMC_CONFIG_NUMOF is greater than the supported number of slots");
XFA_CONST(sdmmc_devs, 0) sdmmc_dev_t * const _sdmmc_0 = (sdmmc_dev_t * const)&_sdmmc_devs[0];
#if SDMMC_CONFIG_NUMOF > 1
XFA_CONST(sdmmc_devs, 0) sdmmc_dev_t * const _sdmmc_1 = (sdmmc_dev_t * const)&_sdmmc_devs[1];
#endif
/* forward declaration of internal functions */
static int _esp_err_to_sdmmc_err_code(esp_err_t code);
static void _isr_cd_pin(void *arg);
static void _init(sdmmc_dev_t *sdmmc_dev)
{
esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
assert(dev);
const sdmmc_conf_t *conf = dev->config;
assert(conf);
/* additional sanity checks */
assert(conf->slot < SOC_SDMMC_NUM_SLOTS);
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
#if IS_USED(CPU_FAM_ESP32)
/* On ESP32 only Slot 1 can be used */
assert(conf->slot == SDMMC_SLOT_1);
/* Slot 1 has only 4 data lines */
assert((conf->bus_width == 1) || (conf->bus_width == 4));
sdmmc_dev->bus_width = conf->bus_width;
#elif IS_USED(CPU_FAM_ESP32S3)
assert(gpio_is_valid(conf->clk) && !gpio_is_equal(conf->clk, GPIO0));
assert(gpio_is_valid(conf->cmd) && !gpio_is_equal(conf->cmd, GPIO0));
assert(gpio_is_valid(conf->dat0) && !gpio_is_equal(conf->dat0, GPIO0));
/* TODO Check for collision with Flash GPIOs */
slot_config.clk = conf->clk;
slot_config.cmd = conf->cmd;
slot_config.d0 = conf->dat0;
sdmmc_dev->bus_width = SDMMC_BUS_WIDTH_1BIT;
slot_config.d1 = GPIO_UNDEF;
slot_config.d2 = GPIO_UNDEF;
slot_config.d3 = GPIO_UNDEF;
slot_config.d4 = GPIO_UNDEF;
slot_config.d5 = GPIO_UNDEF;
slot_config.d6 = GPIO_UNDEF;
slot_config.d7 = GPIO_UNDEF;
if (gpio_is_valid(conf->dat1) && !gpio_is_equal(conf->dat1, GPIO0) &&
gpio_is_valid(conf->dat2) && !gpio_is_equal(conf->dat2, GPIO0) &&
gpio_is_valid(conf->dat3) && !gpio_is_equal(conf->dat3, GPIO0)) {
slot_config.d1 = conf->dat1;
slot_config.d2 = conf->dat2;
slot_config.d3 = conf->dat3;
sdmmc_dev->bus_width = SDMMC_BUS_WIDTH_4BIT;
#if IS_USED(MODULE_PERIPH_SDMMC_8BIT)
if (gpio_is_valid(conf->dat4) && !gpio_is_equal(conf->dat4, GPIO0) &&
gpio_is_valid(conf->dat5) && !gpio_is_equal(conf->dat5, GPIO0) &&
gpio_is_valid(conf->dat6) && !gpio_is_equal(conf->dat6, GPIO0) &&
gpio_is_valid(conf->dat7) && !gpio_is_equal(conf->dat7, GPIO0)) {
slot_config.d4 = conf->dat4;
slot_config.d5 = conf->dat5;
slot_config.d6 = conf->dat6;
slot_config.d7 = conf->dat7;
sdmmc_dev->bus_width = SDMMC_BUS_WIDTH_8BIT;
}
#endif
}
#else
#error "ESP32x variant not supported"
#endif
#if IS_USED(CONFIG_SDMMC_INTERNAL_PULLUP)
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
#endif
slot_config.width = sdmmc_dev->bus_width;
dev->data_transfer = false;
esp_err_t res;
if ((res = sdmmc_host_init())) {
LOG_ERROR("[sdmmc] Could not initialize SDMMC host controller\n");
assert(false);
}
if ((res = sdmmc_host_init_slot(dev->config->slot, &slot_config))) {
LOG_ERROR("[sdmmc] Could not initialize SDMMC slot\n");
assert(false);
}
if (gpio_is_valid(conf->cd)) {
dev->last_cd_pin_irq = system_get_time();
gpio_init_int(conf->cd, GPIO_IN, GPIO_BOTH, _isr_cd_pin, sdmmc_dev);
sdmmc_dev->present = gpio_read(conf->cd) == 0;
}
else {
sdmmc_dev->present = true;
}
sdmmc_dev->bus_width = SDMMC_BUS_WIDTH_1BIT; // SDMMC_BUS_WIDTH_4BIT;
}
static int _send_cmd(sdmmc_dev_t *sdmmc_dev, sdmmc_cmd_t cmd_idx, uint32_t arg,
sdmmc_resp_t resp_type, uint32_t *resp)
{
/* to ensure that `sdmmc_send_acmd` is used for application specific commands */
assert((cmd_idx & SDMMC_ACMD_PREFIX) == 0);
esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
assert(dev);
assert(dev->config);
if (dev->data_transfer) {
/* data transfer command is issued in _xfer_execute as one transaction
* together with data phase */
return 0;
}
sdmmc_command_t cmd = {
.opcode =cmd_idx,
.flags = 0,
.arg = arg,
.data = 0,
.datalen = 0,
.blklen = 0,
.timeout_ms = 100,
};
switch (resp_type) {
case SDMMC_R1:
cmd.flags |= SCF_RSP_R1;
break;
case SDMMC_R1B:
cmd.flags |= SCF_RSP_R1B;
break;
case SDMMC_R2:
cmd.flags |= SCF_RSP_R2;
break;
case SDMMC_R3:
cmd.flags |= SCF_RSP_R3;
break;
case SDMMC_R4:
cmd.flags |= SCF_RSP_R4;
break;
case SDMMC_R5:
cmd.flags |= SCF_RSP_R5;
break;
case SDMMC_R6:
cmd.flags |= SCF_RSP_R7;
break;
case SDMMC_R7:
cmd.flags |= SCF_RSP_R7;
break;
default:
break;
}
esp_err_t res = sdmmc_host_do_transaction(dev->config->slot, &cmd);
if (res) {
return _esp_err_to_sdmmc_err_code(res);
}
else if (cmd.error) {
return _esp_err_to_sdmmc_err_code(cmd.error);
}
if ((resp_type == SDMMC_R1) || (resp_type == SDMMC_R1B)) {
sdmmc_dev->status = cmd.response[0];
}
if (resp) {
if (resp_type == SDMMC_R2) {
resp[0] = cmd.response[3];
resp[1] = cmd.response[2];
resp[2] = cmd.response[1];
resp[3] = cmd.response[0];
}
else if (resp_type != SDMMC_NO_R) {
resp[0] = cmd.response[0];
}
}
return 0;
}
static int _set_bus_width(sdmmc_dev_t *sdmmc_dev, sdmmc_bus_width_t width)
{
DEBUG("[sdmmc] %s width=%d\n", __func__, width);
esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
assert(dev);
esp_err_t res = sdmmc_host_set_bus_width(dev->config->slot, width);
if (res) {
return _esp_err_to_sdmmc_err_code(res);
}
return 0;
}
static int _set_clock_rate(sdmmc_dev_t *sdmmc_dev, sdmmc_clock_rate_t rate)
{
DEBUG("[sdmmc] %s rate=%"PRIu32" ", __func__, (uint32_t)rate);
esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
assert(dev);
if (rate > CONFIG_SDMMC_CLK_MAX) {
rate = CONFIG_SDMMC_CLK_MAX;
}
DEBUG("actual_rate=%"PRIu32"\n", (uint32_t)rate);
esp_err_t res = sdmmc_host_set_card_clk(dev->config->slot, rate / KHZ(1));
if (res) {
return _esp_err_to_sdmmc_err_code(res);
}
#if SOC_SDMMC_USE_GPIO_MATRIX
/* phase has to be modified to get it working for MMCs if
* SOC_SDMMC_USE_GPIO_MATRIX is used */
uint32_t reg = *((uint32_t *)SDMMC_CLOCK_REG);
reg &= ~SDMMC_CLOCK_REG_CCLKIN_EDGE_SAM_SEL_M;
reg |= (6 << SDMMC_CLOCK_REG_CCLKIN_EDGE_SAM_SEL_S);
*((uint32_t *)SDMMC_CLOCK_REG) = reg;
#endif
return 0;
}
static int _xfer_prepare(sdmmc_dev_t *sdmmc_dev, sdmmc_xfer_desc_t *xfer)
{
esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
assert(dev);
assert(dev->config);
/* SDIO/SD/MMC uses 32-bit words */
/* TODO: at the moment only 32-bit words supported */
assert((xfer->block_size % sizeof(uint32_t)) == 0);
dev->data_transfer = true;
return 0;
}
static int _xfer_execute(sdmmc_dev_t *sdmmc_dev, sdmmc_xfer_desc_t *xfer,
const void *data_wr, void *data_rd,
uint16_t *done)
{
assert(xfer);
assert((xfer->write && data_wr) || (!xfer->write && data_rd));
/* check the alignment required for the buffers */
assert(HAS_ALIGNMENT_OF(data_wr, SDMMC_CPU_DMA_ALIGNMENT));
assert(HAS_ALIGNMENT_OF(data_rd, SDMMC_CPU_DMA_ALIGNMENT));
esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
assert(dev);
assert(dev->config);
sdmmc_command_t cmd = {
.opcode = xfer->cmd_idx & ~SDMMC_ACMD_PREFIX,
.flags = SCF_RSP_R1 | (xfer->write ? 0 : SCF_CMD_READ),
.arg = xfer->arg,
.data = xfer->write ? (void *)data_wr : data_rd,
.datalen = xfer->block_num * xfer->block_size,
.blklen = xfer->block_size,
.timeout_ms = xfer->write ? 2500 : 1000, // TODO
};
if (done) {
*done = 0;
}
esp_err_t res = sdmmc_host_do_transaction(dev->config->slot, &cmd);
if (res) {
return _esp_err_to_sdmmc_err_code(res);
}
else if (cmd.error) {
return _esp_err_to_sdmmc_err_code(cmd.error);
}
if (done) {
*done = xfer->block_num;
}
return 0;
}
static int _xfer_finish(sdmmc_dev_t *sdmmc_dev, sdmmc_xfer_desc_t *xfer)
{
(void)xfer;
esp32_sdmmc_dev_t *dev = container_of(sdmmc_dev, esp32_sdmmc_dev_t, sdmmc_dev);
dev->data_transfer = false;
return 0;
}
static int _esp_err_to_sdmmc_err_code(esp_err_t error)
{
switch (error) {
case ESP_ERR_TIMEOUT:
DEBUG("[sdmmc] Timeout error\n");
return -ETIMEDOUT;
case ESP_ERR_INVALID_CRC:
DEBUG("[sdmmc] CRC error\n");
return -EBADMSG;
case ESP_ERR_INVALID_RESPONSE:
DEBUG("[sdmmc] Invalid response\n");
return -EIO;
case ESP_ERR_INVALID_SIZE:
DEBUG("[sdmmc] Invalid size\n");
return -EIO;
case ESP_ERR_INVALID_ARG:
DEBUG("[sdmmc] Invalid argument\n");
return -EIO;
default:
DEBUG("[sdmmc] Other error\n");
return -EIO;
}
}
static void _isr_cd_pin(void *arg)
{
uint32_t state = irq_disable();
esp32_sdmmc_dev_t *dev = arg;
assert(dev);
/* for debouncing handle only the first CD Pin interrupts and ignore further
* interrupts that happen within the debouncing time interval */
if ((system_get_time() - dev->last_cd_pin_irq) > CONFIG_CD_PIN_DEBOUNCE_US) {
dev->last_cd_pin_irq = system_get_time();
sdmmc_dev_t *sdmmc_dev = &dev->sdmmc_dev;
sdmmc_dev->present = !sdmmc_dev->present;
sdmmc_dev->init_done = false;
if (sdmmc_dev->event_cb) {
sdmmc_dev->event_cb(sdmmc_dev,
sdmmc_dev->present ? SDMMC_EVENT_CARD_INSERTED
: SDMMC_EVENT_CARD_REMOVED);
}
}
irq_restore(state);
}
static const sdmmc_driver_t _driver = {
.init = _init,
.card_init = NULL, /* no own card init function */
.send_cmd = _send_cmd,
.set_bus_width = _set_bus_width,
.set_clock_rate = _set_clock_rate,
.xfer_prepare = _xfer_prepare,
.xfer_execute = _xfer_execute,
.xfer_finish = _xfer_finish,
};

View File

@ -51,16 +51,28 @@
FATFS fat_fs; /* FatFs work area needed for each volume */
#ifdef MODULE_MTD_NATIVE
/* mtd device for native is provided in boards/native/board_init.c */
mtd_dev_t *fatfs_mtd_devs[1];
#elif MODULE_MTD_SDMMC
#include "mtd_sdmmc.h"
mtd_dev_t *fatfs_mtd_devs[1];
#elif MODULE_MTD_SDCARD
#include "mtd_sdcard.h"
#include "sdcard_spi_params.h"
#define SDCARD_SPI_NUM ARRAY_SIZE(sdcard_spi_params)
/* sdcard devs are provided by drivers/sdcard_spi/sdcard_spi.c */
extern sdcard_spi_t sdcard_spi_devs[SDCARD_SPI_NUM];
mtd_sdcard_t mtd_sdcard_devs[SDCARD_SPI_NUM];
mtd_dev_t *fatfs_mtd_devs[SDCARD_SPI_NUM];
#endif
#define MTD_NUM ARRAY_SIZE(fatfs_mtd_devs)
@ -105,11 +117,11 @@ static int _mount(int argc, char **argv)
}
else {
#if FF_MAX_SS == FF_MIN_SS
#if FF_MAX_SS == FF_MIN_SS
uint16_t sector_size = TEST_FATFS_FIXED_SECTOR_SIZE;
#else
#else
uint16_t sector_size = fs->ssize;
#endif
#endif
uint64_t total_bytes = (fs->n_fatent - TEST_FATFS_FATENT_OFFSET) * fs->csize;
total_bytes *= sector_size;
@ -375,7 +387,7 @@ static const shell_command_t shell_commands[] = {
int main(void)
{
#if FATFS_FFCONF_OPT_FS_NORTC == 0
#if FATFS_FFCONF_OPT_FS_NORTC == 0
/* the rtc is used in diskio.c for timestamps of files */
puts("Initializing the RTC driver");
rtc_poweron();
@ -396,11 +408,14 @@ int main(void)
time.tm_min,
time.tm_sec);
rtc_set_time(&time);
#endif
#endif
#if MODULE_MTD_NATIVE
#if MODULE_MTD_NATIVE
fatfs_mtd_devs[0] = mtd_dev_get(0);
#elif MODULE_MTD_SDCARD
#elif MODULE_MTD_SDMMC
extern mtd_sdmmc_t mtd_sdmmc_dev0;
fatfs_mtd_devs[0] = &mtd_sdmmc_dev0.base;
#elif MODULE_MTD_SDCARD
for (unsigned int i = 0; i < SDCARD_SPI_NUM; i++){
mtd_sdcard_devs[i].base.driver = &mtd_sdcard_driver;
mtd_sdcard_devs[i].sd_card = &sdcard_spi_devs[i];
@ -413,7 +428,7 @@ int main(void)
printf("init sdcard_mtd %u [FAILED]\n", i);
}
}
#endif
#endif
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);

View File

@ -29,7 +29,9 @@
#include "kernel_defines.h"
#ifdef MODULE_MTD_SDCARD
#if MODULE_MTD_SDMMC
#include "mtd_sdmmc.h"
#elif MODULE_MTD_SDCARD
#include "mtd_sdcard.h"
#include "sdcard_spi.h"
#include "sdcard_spi_params.h"
@ -64,9 +66,9 @@ static vfs_mount_t _test_vfs_mount = {
#if defined(MODULE_MTD_NATIVE) || defined(MODULE_MTD_MCI)
/* mtd devices are provided in the board's board_init.c*/
#endif
#if defined(MODULE_MTD_SDCARD)
#elif defined(MODULE_MTD_SDMMC)
extern mtd_sdmmc_t mtd_sdmmc_dev0;
#elif defined(MODULE_MTD_SDCARD)
#define SDCARD_SPI_NUM ARRAY_SIZE(sdcard_spi_params)
extern sdcard_spi_t sdcard_spi_devs[SDCARD_SPI_NUM];
mtd_sdcard_t mtd_sdcard_devs[SDCARD_SPI_NUM];
@ -398,20 +400,18 @@ static void test_libc(void)
int main(void)
{
#if MODULE_MTD_SDCARD
#if defined(MODULE_MTD_NATIVE) || defined(MODULE_MTD_MCI)
fatfs.dev = mtd_dev_get(0);
#elif defined(MODULE_MTD_SDMMC)
fatfs.dev = &mtd_sdmmc_dev0.base;
#elif defined(MODULE_MTD_SDCARD)
for(unsigned int i = 0; i < SDCARD_SPI_NUM; i++){
mtd_sdcard_devs[i].base.driver = &mtd_sdcard_driver;
mtd_sdcard_devs[i].sd_card = &sdcard_spi_devs[i];
mtd_sdcard_devs[i].params = &sdcard_spi_params[i];
mtd_init(&mtd_sdcard_devs[i].base);
}
#endif
#if defined(MODULE_MTD_NATIVE) || defined(MODULE_MTD_MCI)
fatfs.dev = mtd_dev_get(0);
#endif
#if defined(MODULE_MTD_SDCARD)
fatfs.dev = mtd_sdcard;
#endif