add support for sdmmc cards in sd-spi mode (#2644)

This commit is contained in:
Arnim Läuger 2019-02-10 10:11:13 +01:00 committed by GitHub
parent e756dbce9c
commit e9bf51eb8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 169 additions and 48 deletions

View File

@ -12,7 +12,7 @@
#include "sdmmc_cmd.h"
// defined in components/modules/sdmmc.c
extern sdmmc_card_t lsdmmc_card[2];
extern sdmmc_card_t *lsdmmc_card[];
static DSTATUS m_status = STA_NOINIT;
@ -60,7 +60,7 @@ DRESULT disk_read (
UINT count /* Number of sectors to read */
)
{
if (sdmmc_read_sectors( &(lsdmmc_card[pdrv]), buff, sector, count ) == ESP_OK)
if (sdmmc_read_sectors( lsdmmc_card[pdrv], buff, sector, count ) == ESP_OK)
return RES_OK;
return RES_ERROR;
@ -80,7 +80,7 @@ DRESULT disk_write (
UINT count /* Number of sectors to write */
)
{
if (sdmmc_write_sectors( &(lsdmmc_card[pdrv]), buff, sector, count ) == ESP_OK)
if (sdmmc_write_sectors( lsdmmc_card[pdrv], buff, sector, count ) == ESP_OK)
return RES_OK;
return RES_ERROR;

View File

@ -13,12 +13,11 @@ typedef struct {
// Table to map physical drive & partition to a logical volume.
// The first value is the physical drive which relates to the SDMMC slot number
// The second value is the partition number.
#define NUM_LOGICAL_DRIVES 4
PARTITION VolToPart[NUM_LOGICAL_DRIVES] = {
{1, 1}, /* Logical drive "0:" ==> slot 1, 1st partition */
{1, 2}, /* Logical drive "1:" ==> slot 1, 2st partition */
{1, 3}, /* Logical drive "2:" ==> slot 1, 3st partition */
{1, 4} /* Logical drive "3:" ==> slot 1, 4st partition */
PARTITION VolToPart[FF_VOLUMES] = {
{0, 1}, /* Logical drive "0:" ==> slot 0, 1st partition */
{0, 2}, /* Logical drive "1:" ==> slot 0, 2st partition */
{0, 3}, /* Logical drive "2:" ==> slot 0, 3st partition */
{0, 4} /* Logical drive "3:" ==> slot 0, 4st partition */
};
#endif /* __FATFS_CONFIG_H__ */

View File

@ -348,7 +348,7 @@ static vfs_vol *myfatfs_mount( const char *name, int num )
// num argument specifies the physical driver
if (num >= 0) {
for (int i = 0; i < NUM_LOGICAL_DRIVES; i++) {
for (int i = 0; i < FF_VOLUMES; i++) {
if (0 == strncmp( name, volstr[i], strlen( volstr[i] ) )) {
VolToPart[i].pd = num;
}

View File

@ -8,9 +8,19 @@
#include "driver/sdmmc_host.h"
#include "sdmmc_cmd.h"
#include "driver/sdspi_host.h"
#include "common.h"
sdmmc_card_t lsdmmc_card[2];
// the index of cards here must be aligned with the pdrv for fatfs
// see FF_VOLUMES in ffconf.h
#define NUM_CARDS 4
sdmmc_card_t *lsdmmc_card[NUM_CARDS];
// local definition for SDSPI host
#define LSDMMC_HOST_SDSPI 100
#define LSDMMC_HOST_HSPI (LSDMMC_HOST_SDSPI + HSPI_HOST)
#define LSDMMC_HOST_VSPI (LSDMMC_HOST_SDSPI + VSPI_HOST)
typedef struct {
sdmmc_card_t *card;
@ -22,58 +32,109 @@ static int lsdmmc_init( lua_State *L )
{
const char *err_msg = "";
int stack = 0;
int top = lua_gettop( L );
int slot = luaL_checkint( L, ++stack );
luaL_argcheck( L, slot == SDMMC_HOST_SLOT_0 || slot == SDMMC_HOST_SLOT_1,
luaL_argcheck( L, slot == SDMMC_HOST_SLOT_0 || slot == SDMMC_HOST_SLOT_1 ||
slot == LSDMMC_HOST_HSPI || slot == LSDMMC_HOST_VSPI,
stack, "invalid slot" );
bool is_sdspi = false;
if (slot >= LSDMMC_HOST_SDSPI) {
is_sdspi = true;
slot -= LSDMMC_HOST_SDSPI;
}
// set optional defaults
int cd_pin = SDMMC_SLOT_NO_CD;
int wp_pin = SDMMC_SLOT_NO_WP;
int freq_khz = SDMMC_FREQ_DEFAULT;
int width = SDMMC_HOST_FLAG_1BIT;
// additional entries for SDSPI configuration
int sck_pin = -1;
int mosi_pin = -1;
int miso_pin = -1;
int cs_pin = -1;
if (lua_type( L, ++stack ) == LUA_TTABLE) {
lua_pushvalue( L, stack );
// retrieve slot configuration from table
cd_pin = opt_checkint( L, "cd_pin", cd_pin );
wp_pin = opt_checkint( L, "wp_pin", wp_pin );
freq_khz = opt_checkint( L, "fmax", freq_khz * 1000 ) / 1000;
width = opt_checkint( L, "width", width );
lua_getfield( L, stack, "cd_pin" );
cd_pin = luaL_optint( L, -1, cd_pin );
// mandatory entries for SDSPI configuration
if (is_sdspi) {
sck_pin = opt_checkint_range( L, "sck_pin", -1, 0, GPIO_NUM_MAX );
mosi_pin = opt_checkint_range( L, "mosi_pin", -1, 0, GPIO_NUM_MAX );
miso_pin = opt_checkint_range( L, "miso_pin", -1, 0, GPIO_NUM_MAX );
cs_pin = opt_checkint_range( L, "cs_pin", -1, 0, GPIO_NUM_MAX );
}
lua_getfield( L, stack, "wp_pin" );
wp_pin = luaL_optint( L, -1, wp_pin );
lua_getfield( L, stack, "fmax" );
freq_khz = luaL_optint( L, -1, freq_khz * 1000 ) / 1000;
lua_getfield( L, stack, "width" );
width = luaL_optint( L, -1, width );
lua_pop( L, 4 );
lua_settop( L, top );
} else {
// table is optional for SDMMC drivers, but mandatory for SD-SPI
if (is_sdspi)
return luaL_error( L, "missing slot configuration table" );
}
// initialize SDMMC host
// find first free card
int card_idx;
for (card_idx = 0; card_idx < NUM_CARDS; card_idx++) {
if (lsdmmc_card[card_idx] == NULL) {
lsdmmc_card[card_idx] = (sdmmc_card_t *)luaM_malloc( L, sizeof( sdmmc_card_t ) );
break;
}
}
if (card_idx == NUM_CARDS)
return luaL_error( L, "too many cards" );
// initialize host
// tolerate error due to re-initialization
esp_err_t res = sdmmc_host_init();
esp_err_t res;
if (is_sdspi) {
res = sdspi_host_init();
} else {
res = sdmmc_host_init();
}
if (res == ESP_OK || res == ESP_ERR_INVALID_STATE) {
// configure SDMMC slot
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
slot_config.gpio_cd = cd_pin;
slot_config.gpio_wp = wp_pin;
if (sdmmc_host_init_slot( slot, &slot_config ) == ESP_OK) {
if (is_sdspi) {
// configure SDSPI slot
sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
slot_config.gpio_miso = miso_pin;
slot_config.gpio_mosi = mosi_pin;
slot_config.gpio_sck = sck_pin;
slot_config.gpio_cs = cs_pin;
slot_config.gpio_cd = cd_pin;
slot_config.gpio_wp = wp_pin;
res = sdspi_host_init_slot( slot, &slot_config );
} else {
// configure SDMMC slot
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
slot_config.gpio_cd = cd_pin;
slot_config.gpio_wp = wp_pin;
res = sdmmc_host_init_slot( slot, &slot_config );
}
if (res == ESP_OK) {
// initialize card
sdmmc_host_t host_config = SDMMC_HOST_DEFAULT();
host_config.slot = slot;
host_config.flags = width;
host_config.max_freq_khz = freq_khz;
if (sdmmc_card_init( &host_config, &(lsdmmc_card[slot]) ) == ESP_OK) {
sdmmc_host_t sdspi_host_config = SDSPI_HOST_DEFAULT();
sdmmc_host_t sdmmc_host_config = SDMMC_HOST_DEFAULT();
sdmmc_host_t *host_config = is_sdspi ? &sdspi_host_config : &sdmmc_host_config;
host_config->slot = slot;
host_config->flags &= ~SDMMC_HOST_FLAG_8BIT;
host_config->flags |= width;
host_config->max_freq_khz = freq_khz;
if (sdmmc_card_init( host_config, lsdmmc_card[card_idx] ) == ESP_OK) {
lsdmmc_ud_t *ud = (lsdmmc_ud_t *)lua_newuserdata( L, sizeof( lsdmmc_ud_t ) );
if (ud) {
luaL_getmetatable(L, "sdmmc.card");
lua_setmetatable(L, -2);
ud->card = &(lsdmmc_card[slot]);
ud->card = lsdmmc_card[card_idx];
ud->vol = NULL;
// all done
@ -86,7 +147,11 @@ static int lsdmmc_init( lua_State *L )
} else
err_msg = "failed to init slot";
sdmmc_host_deinit();
if (is_sdspi ) {
sdspi_host_deinit();
} else {
sdmmc_host_deinit();
}
} else
err_msg = "failed to init sdmmc host";
@ -113,7 +178,8 @@ static int lsdmmc_read( lua_State * L)
luaL_argcheck( L, num_sec >= 0, stack, "out of range" );
// get read buffer
char *rbuf = luaM_malloc( L, num_sec * 512 );
const size_t rbuf_size = num_sec * 512;
char *rbuf = luaM_malloc( L, rbuf_size );
if (sdmmc_read_sectors( card, rbuf, start_sec, num_sec ) == ESP_OK) {
luaL_Buffer b;
@ -121,7 +187,7 @@ static int lsdmmc_read( lua_State * L)
luaL_addlstring( &b, rbuf, num_sec * 512 );
luaL_pushresult( &b );
luaM_free( L, rbuf );
luaM_freearray( L, rbuf, rbuf_size, uint8_t );
// all ok
return 1;
@ -129,6 +195,7 @@ static int lsdmmc_read( lua_State * L)
} else
err_msg = "card access failed";
luaM_freearray( L, rbuf, rbuf_size, uint8_t );
return luaL_error( L, err_msg );
}
@ -274,6 +341,8 @@ static const LUA_REG_TYPE sdmmc_map[] = {
{ LSTRKEY( "init" ), LFUNCVAL( lsdmmc_init ) },
{ LSTRKEY( "HS1" ), LNUMVAL( SDMMC_HOST_SLOT_0 ) },
{ LSTRKEY( "HS2" ), LNUMVAL( SDMMC_HOST_SLOT_1 ) },
{ LSTRKEY( "HSPI" ), LNUMVAL( LSDMMC_HOST_HSPI ) },
{ LSTRKEY( "VSPI" ), LNUMVAL( LSDMMC_HOST_VSPI ) },
{ LSTRKEY( "W1BIT" ), LNUMVAL( SDMMC_HOST_FLAG_1BIT ) },
{ LSTRKEY( "W4BIT" ), LNUMVAL( SDMMC_HOST_FLAG_1BIT |
SDMMC_HOST_FLAG_4BIT ) },
@ -287,6 +356,11 @@ static int luaopen_sdmmc( lua_State *L )
{
luaL_rometatable(L, "sdmmc.card", (void *)sdmmc_card_map);
// initialize cards
for (int i = 0; i < NUM_CARDS; i++ ) {
lsdmmc_card[i] = NULL;
}
return 0;
}

View File

@ -224,7 +224,15 @@ int lspi_master( lua_State *L )
stack,
"invalid host" );
luaL_checktype( L, ++stack, LUA_TTABLE );
if (lua_type( L, ++stack ) != LUA_TTABLE) {
// no configuration table provided
// assume that host is already initialized and just provide the object
lspi_host_t *ud = (lspi_host_t *)lua_newuserdata( L, sizeof( lspi_host_t ) );
luaL_getmetatable( L, UD_HOST_STR );
lua_setmetatable( L, -2 );
ud->host = host;
return 1;
}
spi_bus_config_t config;
memset( &config, 0, sizeof( config ) );

View File

@ -5,11 +5,19 @@
!!! note
MMC cards are not yet supported due to missing functionality in the IDF driver.
MMC cards are not yet supported on HS1/HS2 interfaces due to missing functionality in the IDF driver. Use the SD SPI mode on HSPI/VSPI instead.
## SD Card connection
The SD card is operated in SDMMC mode, thus the card has to be wired to the ESP pins of the HS1_* or HS2_* interfaces. There are several naming schemes used on different adapters - the following list shows alternative terms:
!!! caution
The adapter does not require level shifters since SD and ESP are supposed to be powered with the same voltage. If your specific model contains level shifters then make sure that both sides can be operated at 3V3.
![1:1 micro-sd adapter](../img/micro_sd-small.jpg "1:1 micro-sd adapter")
### SDMMC Mode
If the SD card is operated in SDMMC mode, then the card has to be wired to the ESP pins of the HS1_* or HS2_* interfaces. There are several naming schemes used on different adapters - the following list shows alternative terms:
| SD mode name | SPI mode name | ESP32 HS1 I/F | ESP32 HS2 I/F |
| :--- | :--- | :--- | :--- |
@ -32,11 +40,24 @@ Connections to `CLK`, `CMD`, and `DAT0` are mandatory and enable basic operation
Connecting DAT0 to GPIO2 can block firmware flashing depending on the electrical configuration at this pin. Disconnect GPIO2 from the card adapter during flashing if unsure.
!!! caution
### SD SPI Mode
The adapter does not require level shifters since SD and ESP are supposed to be powered with the same voltage. If your specific model contains level shifters then make sure that both sides can be operated at 3V3.
In SD SPI mode the SD or MMC card is operated by the SPI host interfaces (HSPI or VSPI). Both hosts can be assigned to any GPIO pins.
![1:1 micro-sd adapter](../img/micro_sd-small.jpg "1:1 micro-sd adapter")
| SPI mode name | ESP32 HSPI, VSPI I/F |
| :--- | :--- |
| `CK, SCLK` | Any GPIO |
| `DI, MOSI` | Any GPIO |
| `DO, MISO` | Any GPIO |
| n/a | n/a |
| n/a | n/a |
| `CS, SS` | Any GPIO |
| n/a | n/a |
| n/a | n/a |
| n/a | n/a |
| n/a | n/a |
| `VCC, VDD` | 3V3 supply |
| `VSS, GND` | common ground |
## sdmmc.init()
Initialize the SDMMC and probe the attached SD card.
@ -44,7 +65,7 @@ Initialize the SDMMC and probe the attached SD card.
#### Syntax
`sdmmc.init(slot[, cfg])`
#### Parameters
#### Parameters SDMMC Mode
- `slot` SDMMC slot, one of `sdmmc.HS1` or `sdmmc.HS2`
- `cfg` optional table containing slot configuration:
- `cd_pin` card detect pin, none if omitted
@ -55,6 +76,17 @@ Initialize the SDMMC and probe the attached SD card.
- `sdmmc.W4BIT`
- `sdmmc.W8BIT`, not supported yet
#### Parameters SD SPI Mode
- `slot` SD SPI slot, one of `sdmmc.HSPI` or `sdmmc.VSPI`
- `cfg` mandatory table containing slot configuration:
- `sck_pin` SPI SCK pin, mandatory
- `mosi_pin`, SPI MOSI pin, mandatory
- `miso_pin`, SPI MISO pin, mandatory
- `cs_pin`, SPI CS pin, mandatory
- `cd_pin` card detect pin, none if omitted
- `wp_pin` write-protcet pin, none if omitted
- `fmax` maximum communication frequency, defaults to 20&nbsp; if omitted
#### Returns
Card object.

View File

@ -17,7 +17,7 @@ The host signals can be mapped to any suitable GPIO pins.
Initializes a bus in master mode and returns a bus master object.
#### Syntax
`spi.master(host, config[, dma])`
`spi.master(host[, config[, dma]])`
#### Parameters
- `host` id, one of
@ -34,6 +34,10 @@ Initializes a bus in master mode and returns a bus master object.
Enabling DMA allows sending and receiving an unlimited amount of bytes but has restrictions in halfduplex mode (see [`spi.master:device()`](#spimasterdevice)).
Disabling DMA limits a transaction to 32&nbsp;bytes max.
!!! not
Omitting the `config` table returns an SPI bus master object without further initialization. This enables sharing the same SPI host with `sdmmc` in SD-SPI mode. First call `sdmmc.init(sdmmc.VSPI, ...)`, then `spi.master(spi.VSPI)`.
#### Returns
SPI bus master object

View File

@ -23,8 +23,12 @@ Refer to the [`sdmmc` module documentation](modules/sdmmc.md).
Before mounting the volume(s) on the SD card, you need to initialize the SDMMC interface from Lua.
```lua
-- for SDMMC mode:
card = sdmmc.init(sdmmc.HS2, {width = sdmmc.W1BIT})
-- for SD SPI mode:
card = sdmmc.init(sdmmc.VSPI, {sck_pin = 18, mosi_pin = 23, miso_pin = 19, cs_pin = 22})
-- then mount the sd
-- note: the card initialization process during `card:mount()` will set spi divider temporarily to 200 (400 kHz)
-- it's reverted back to the current user setting before `card:mount()` finishes