3.3.7
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
# SD_MMC library
|
||||
|
||||
This library provides the integration of ESP32 and ESP32-S3 with SD (Secure Digital) and MMC (Multi Media Card) cards using a built-in SDMMC module.
|
||||
|
||||
Please note that SD_MMC is only available for ESP32 and ESP32-S3. For other SoCs please use the SD library based on SPI.
|
||||
|
||||
## Wiring:
|
||||
|
||||

|
||||
|
||||
Image source: [Wikipedia](https://upload.wikimedia.org/wikipedia/commons/thumb/a/ab/MMC-SD-miniSD-microSD-Color-Numbers-Names.gif/330px-MMC-SD-miniSD-microSD-Color-Numbers-Names.gif)
|
||||
|
||||

|
||||
|
||||
Image source: [Wikipedia](https://commons.wikimedia.org/wiki/File:15-04-29-MMC-Karte-dscf4734-e.jpg)
|
||||
|
||||
pin number (refer to the picture) | micro SD - SD mode | micro SD - SPI mode | mini SD - SD mode | mini SD - SPI mode | SD - SD mode | SD - SPI mode | MMC (MMC3) - MMC mode | MMC (MMC3) - SPI mode | MMCplus / MMCmobile (MMC4) - MMC mode | MMCplus / MMCmobile (MMC4) - SPI mode
|
||||
----------------------------------|--------------------|---------------------|-------------------|--------------------|--------------|---------------|-----------------------|-----------------------|-----------------------------------------|--------------------------------------
|
||||
1 | D2 | not used | D3 | CS | D3 | CS | RES | CS | D3 | CS
|
||||
2 | D3 | CS | CMD | DI | CMD | DI | CMD | DI | CMD | DI
|
||||
3 | CMD | DI | VSS1 (GND) | VSS1 (GND) | VSS1 (GND) | VSS1 (GND) | VSS1 (GND) | VSS1 (GND) | VSS1 (GND) | VSS1 (GND)
|
||||
4 | VDD (3.3V) | VDD (3.3V) | VDD (3.3V) | VDD (3.3V) | VDD (3.3V) | VDD (3.3V) | VDD (3.3V) | VDD (3.3V) | VDD (3.3V) | VDD (3.3V)
|
||||
5 | CLK | SCLK | CLK | SCLK | CLK | SCLK | CLK | SCLK | CLK | SCLK
|
||||
6 | VSS (GND) | VSS (GND) | VSS2 (GND) | VSS2 (GND) | VSS2 (GND) | VSS2 (GND) | VSS2 (GND) | VSS2 (GND) | VSS2 (GND) | VSS2 (GND)
|
||||
7 | D0 | DO | D0 | DO | D0 | DO | DAT | DO | D0 | DO
|
||||
8 | D1 | not used | D1 | not used | D1 | not used | - | - | D1 | not used
|
||||
9 | - | - | D2 | not used | D2 | not used | - | - | D2 | not used
|
||||
10 | - | - | For future use | For future use | - | - | - | - | D3 | not used
|
||||
11 | - | - | For future use | For future use | - | - | - | - | D4 | not used
|
||||
12 | - | - | - | - | - | - | - | - | D5 | not used
|
||||
13 | - | - | - | - | - | - | - | - | D6 | not used
|
||||
|
||||
### Pin assignments for ESP32
|
||||
|
||||
On ESP32, SD_MMC peripheral is connected to specific GPIO pins and cannot be changed (rerouted). Please see the table below for the pin connections.
|
||||
|
||||
When using an ESP-WROVER-KIT board, this example runs without any extra modifications required. Only an SD card needs to be inserted into the slot.
|
||||
|
||||
ESP32 pin | SD card pin | Notes
|
||||
--------------|-------------|------------
|
||||
GPIO14 (MTMS) | CLK | 10k pullup in SD mode
|
||||
GPIO15 (MTDO) | CMD | 10k pullup in SD mode
|
||||
GPIO2 | D0 | 10k pullup in SD mode, pull low to go into download mode (see Note about GPIO2 below!)
|
||||
GPIO4 | D1 | not used in 1-line SD mode; 10k pullup in 4-line SD mode
|
||||
GPIO12 (MTDI) | D2 | not used in 1-line SD mode; 10k pullup in 4-line SD mode (see Note about GPIO12 below!)
|
||||
GPIO13 (MTCK) | D3 | not used in 1-line SD mode, but card's D3 pin must have a 10k pullup
|
||||
|
||||
|
||||
### Pin assignments for ESP32-S3
|
||||
|
||||
On ESP32-S3, SDMMC peripheral is connected to GPIO pins using GPIO matrix. This allows arbitrary GPIOs to be used to connect an SD card or MMC. The GPIOs can be configured with the following commands:
|
||||
```
|
||||
setPins(int clk, int cmd, int d0))
|
||||
setPins(int clk, int cmd, int d0, int d1, int d2, int d3))
|
||||
```
|
||||
|
||||
The table below lists the default pin assignments.
|
||||
|
||||
When using an ESP32-S3-USB-OTG board, this example runs without any extra modifications required. Only an SD card needs to be inserted into the slot.
|
||||
|
||||
ESP32-S3 pin | SD card pin | Notes
|
||||
--------------|-------------|------------
|
||||
GPIO36 | CLK | 10k pullup
|
||||
GPIO35 | CMD | 10k pullup
|
||||
GPIO37 | D0 | 10k pullup
|
||||
GPIO38 | D1 | not used in 1-line SD mode; 10k pullup in 4-line mode
|
||||
GPIO33 | D2 | not used in 1-line SD mode; 10k pullup in 4-line mode
|
||||
GPIO34 | D3 | not used in 1-line SD mode, but card's D3 pin must have a 10k pullup
|
||||
|
||||
Warning: ESP32-S3-WROOM-2 is using most of the default GPIOs (33-37) to interface with on-board OPI flash. If the SD_MMC is initialized with default pins it will result in rebooting loop - please reassign the pins elsewhere using the mentioned command `setPins`.
|
||||
|
||||
> **Note:** ESP32-S3-DevKitC-1 v1.1 does NOT have GPIOs 33 and 34 broken out, so it will be necessary to change at least the pin for D2 and D3.
|
||||
|
||||
### 4-line and 1-line SD modes
|
||||
|
||||
By default, this library uses 4-bit line mode, utilizing 6 pins: CLK, CMD, D0 - D3 and 2 power lines (3.3V and GND). It is possible to use 1-bit line mode (CLK, CMD, D0, 3.3V, GND) by passing the second argument `mode1bit==true`:
|
||||
```
|
||||
SD_MMC.begin("/sdcard", true);
|
||||
```
|
||||
|
||||
> **Note:** Even if card's D3 line is not connected to the ESP chip, it still has to be pulled up, otherwise the card will go into SPI protocol mode.
|
||||
|
||||
### Note about GPIO2 (ESP32 only)
|
||||
|
||||
GPIO2 pin is used as a bootstrapping pin, and should be low to enter UART download mode. One way to do this is to connect GPIO0 and GPIO2 using a jumper, and then the auto-reset circuit on most development boards will pull GPIO2 low along with GPIO0, when entering download mode.
|
||||
|
||||
- Some boards have pulldown and/or LED on GPIO2. LED is usually ok, but pulldown will interfere with D0 signals and must be removed. Check the schematic of your development board for anything connected to GPIO2.
|
||||
|
||||
### Note about GPIO12 (ESP32 only)
|
||||
|
||||
GPIO12 is used as a bootstrapping pin to select output voltage of an internal regulator which powers the flash chip (VDD_SDIO). This pin has an internal pulldown so if left unconnected it will read low at reset (selecting default 3.3V operation). When adding a pullup to this pin for SD card operation, consider the following:
|
||||
|
||||
## FAQ:
|
||||
|
||||
#### Do I need any additional modules, like the Arduino SD module?
|
||||
|
||||
No, just wire your SD card directly to ESP32.
|
||||
|
||||
Tip: If you are using a microSD card and have a spare adapter to full-sized SD, you can solder Dupont pins on the adapter.
|
||||
|
||||
|
||||
#### What is the difference between SD and SD_MMC libraries?
|
||||
|
||||
SD runs on SPI, and SD_MMC uses the SDMMC hardware bus on the ESP32.
|
||||
The SPI uses 4 communication pins + 2 power connections and operates on up to 80 MHz. The SPI option offers flexibility on pin connection because the data connections can be routed through GPIO matrix to any data pin.
|
||||
SD-SPI speed is approximately half of the SD-MMC even when used on 1-bit line.
|
||||
You can read more about SD SPI in the [documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/sdspi_host.html)
|
||||
|
||||
SD_MMC is supported only by ESP32 and ESP32-S3 and can be connected only to dedicated pins. SD_MMC allows to use of 1, 4 or 8 data pins + 2 additional communication pins and 2 power pins. The data pins need to be pulled up externally.
|
||||
You can read more about SD_MMC in the [documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/sdmmc_host.html)
|
||||
1-bit: SD_MMC_ speed is approximately two-times faster than SPI mode
|
||||
4-bit: SD_MMC speed is approximately three-times faster than SPI mode.
|
||||
@@ -0,0 +1,102 @@
|
||||
#if !SOC_USB_OTG_SUPPORTED || ARDUINO_USB_MODE
|
||||
#error Device does not support USB_OTG or native USB CDC/JTAG is selected
|
||||
#endif
|
||||
|
||||
#include <USB.h>
|
||||
#include <USBMSC.h>
|
||||
#include <SD_MMC.h>
|
||||
|
||||
// USB Mass Storage Class (MSC) object
|
||||
USBMSC msc;
|
||||
|
||||
int clk = 36;
|
||||
int cmd = 35;
|
||||
int d0 = 37;
|
||||
int d1 = 38;
|
||||
int d2 = 33;
|
||||
int d3 = 34;
|
||||
bool onebit = true; // set to false for 4-bit. 1-bit will ignore the d1-d3 pins (but d3 must be pulled high)
|
||||
|
||||
static int32_t onWrite(uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
|
||||
uint32_t secSize = SD_MMC.sectorSize();
|
||||
if (!secSize) {
|
||||
return false; // disk error
|
||||
}
|
||||
log_v("Write lba: %ld\toffset: %ld\tbufsize: %ld", lba, offset, bufsize);
|
||||
for (int x = 0; x < bufsize / secSize; x++) {
|
||||
uint8_t blkbuffer[secSize];
|
||||
memcpy(blkbuffer, (uint8_t *)buffer + secSize * x, secSize);
|
||||
if (!SD_MMC.writeRAW(blkbuffer, lba + x)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return bufsize;
|
||||
}
|
||||
|
||||
static int32_t onRead(uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
|
||||
uint32_t secSize = SD_MMC.sectorSize();
|
||||
if (!secSize) {
|
||||
return false; // disk error
|
||||
}
|
||||
log_v("Read lba: %ld\toffset: %ld\tbufsize: %ld\tsector: %lu", lba, offset, bufsize, secSize);
|
||||
for (int x = 0; x < bufsize / secSize; x++) {
|
||||
if (!SD_MMC.readRAW((uint8_t *)buffer + (x * secSize), lba + x)) {
|
||||
return false; // outside of volume boundary
|
||||
}
|
||||
}
|
||||
return bufsize;
|
||||
}
|
||||
|
||||
static bool onStartStop(uint8_t power_condition, bool start, bool load_eject) {
|
||||
log_i("Start/Stop power: %u\tstart: %d\teject: %d", power_condition, start, load_eject);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void usbEventCallback(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
|
||||
if (event_base == ARDUINO_USB_EVENTS) {
|
||||
arduino_usb_event_data_t *data = (arduino_usb_event_data_t *)event_data;
|
||||
switch (event_id) {
|
||||
case ARDUINO_USB_STARTED_EVENT: Serial.println("USB PLUGGED"); break;
|
||||
case ARDUINO_USB_STOPPED_EVENT: Serial.println("USB UNPLUGGED"); break;
|
||||
case ARDUINO_USB_SUSPEND_EVENT: Serial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en); break;
|
||||
case ARDUINO_USB_RESUME_EVENT: Serial.println("USB RESUMED"); break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting Serial");
|
||||
|
||||
Serial.println("Mounting SDcard");
|
||||
SD_MMC.setPins(clk, cmd, d0, d1, d2, d3);
|
||||
if (!SD_MMC.begin("/sdcard", onebit)) {
|
||||
Serial.println("Mount Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("Initializing MSC");
|
||||
// Initialize USB metadata and callbacks for MSC (Mass Storage Class)
|
||||
msc.vendorID("ESP32");
|
||||
msc.productID("USB_MSC");
|
||||
msc.productRevision("1.0");
|
||||
msc.onRead(onRead);
|
||||
msc.onWrite(onWrite);
|
||||
msc.onStartStop(onStartStop);
|
||||
msc.mediaPresent(true);
|
||||
msc.begin(SD_MMC.numSectors(), SD_MMC.sectorSize());
|
||||
|
||||
Serial.println("Initializing USB");
|
||||
|
||||
USB.begin();
|
||||
USB.onEvent(usbEventCallback);
|
||||
|
||||
Serial.printf("Card Size: %lluMB\n", SD_MMC.totalBytes() / 1024 / 1024);
|
||||
Serial.printf("Sector: %d\tCount: %d\n", SD_MMC.sectorSize(), SD_MMC.numSectors());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(-1);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
requires:
|
||||
- CONFIG_SOC_SDMMC_HOST_SUPPORTED=y
|
||||
- CONFIG_TINYUSB_MSC_ENABLED=y
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* pin 1 - D2 | Micro SD card |
|
||||
* pin 2 - D3 | /
|
||||
* pin 3 - CMD | |__
|
||||
* pin 4 - VDD (3.3V) | |
|
||||
* pin 5 - CLK | 8 7 6 5 4 3 2 1 /
|
||||
* pin 6 - VSS (GND) | ▄ ▄ ▄ ▄ ▄ ▄ ▄ ▄ /
|
||||
* pin 7 - D0 | ▀ ▀ █ ▀ █ ▀ ▀ ▀ |
|
||||
* pin 8 - D1 |_________________|
|
||||
* ║ ║ ║ ║ ║ ║ ║ ║
|
||||
* ╔═══════╝ ║ ║ ║ ║ ║ ║ ╚═════════╗
|
||||
* ║ ║ ║ ║ ║ ║ ╚══════╗ ║
|
||||
* ║ ╔═════╝ ║ ║ ║ ╚═════╗ ║ ║
|
||||
* Connections for ║ ║ ╔═══╩═║═║═══╗ ║ ║ ║
|
||||
* full-sized ║ ║ ║ ╔═╝ ║ ║ ║ ║ ║
|
||||
* SD card ║ ║ ║ ║ ║ ║ ║ ║ ║
|
||||
* ESP32-P4 Func EV | 40 39 GND 43 3V3 GND 44 43 42 | SLOT 0 (IO_MUX)
|
||||
* ESP32-S3 DevKit | 21 47 GND 39 3V3 GND 40 41 42 |
|
||||
* ESP32-S3-USB-OTG | 38 37 GND 36 3V3 GND 35 34 33 |
|
||||
* ESP32 | 4 2 GND 14 3V3 GND 15 13 12 |
|
||||
* Pin name | D1 D0 VSS CLK VDD VSS CMD D3 D2 |
|
||||
* SD pin number | 8 7 6 5 4 3 2 1 9 /
|
||||
* | █/
|
||||
* |__▍___▊___█___█___█___█___█___█___/
|
||||
* WARNING: ALL data pins must be pulled up to 3.3V with an external 10k Ohm resistor!
|
||||
* Note to ESP32 pin 2 (D0): Add a 1K Ohm pull-up resistor to 3.3V after flashing
|
||||
*
|
||||
* SD Card | ESP32
|
||||
* D2 12
|
||||
* D3 13
|
||||
* CMD 15
|
||||
* VSS GND
|
||||
* VDD 3.3V
|
||||
* CLK 14
|
||||
* VSS GND
|
||||
* D0 2 (add 1K pull up after flashing)
|
||||
* D1 4
|
||||
*
|
||||
* For more info see file README.md in this library or on URL:
|
||||
* https://github.com/espressif/arduino-esp32/tree/master/libraries/SD_MMC
|
||||
*/
|
||||
|
||||
#include "FS.h"
|
||||
#include "SD_MMC.h"
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S3
|
||||
// Default pins for ESP-S3
|
||||
// Warning: ESP32-S3-WROOM-2 is using most of the default GPIOs (33-37) to interface with on-board OPI flash.
|
||||
// If the SD_MMC is initialized with default pins it will result in rebooting loop - please
|
||||
// reassign the pins elsewhere using the mentioned command `setPins`.
|
||||
// Note: ESP32-S3-WROOM-1 does not have GPIO 33 and 34 broken out.
|
||||
// Note: if it's ok to use default pins, you do not need to call the setPins
|
||||
int clk = 36;
|
||||
int cmd = 35;
|
||||
int d0 = 37;
|
||||
int d1 = 38;
|
||||
int d2 = 33;
|
||||
int d3 = 39; // GPIO 34 is not broken-out on ESP32-S3-DevKitC-1 v1.1
|
||||
#endif
|
||||
|
||||
void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
|
||||
Serial.printf("Listing directory: %s\n", dirname);
|
||||
|
||||
File root = fs.open(dirname);
|
||||
if (!root) {
|
||||
Serial.println("Failed to open directory");
|
||||
return;
|
||||
}
|
||||
if (!root.isDirectory()) {
|
||||
Serial.println("Not a directory");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (file.isDirectory()) {
|
||||
Serial.print(" DIR : ");
|
||||
Serial.println(file.name());
|
||||
if (levels) {
|
||||
listDir(fs, file.path(), levels - 1);
|
||||
}
|
||||
} else {
|
||||
Serial.print(" FILE: ");
|
||||
Serial.print(file.name());
|
||||
Serial.print(" SIZE: ");
|
||||
Serial.println(file.size());
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
void createDir(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Creating Dir: %s\n", path);
|
||||
if (fs.mkdir(path)) {
|
||||
Serial.println("Dir created");
|
||||
} else {
|
||||
Serial.println("mkdir failed");
|
||||
}
|
||||
}
|
||||
|
||||
void removeDir(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Removing Dir: %s\n", path);
|
||||
if (fs.rmdir(path)) {
|
||||
Serial.println("Dir removed");
|
||||
} else {
|
||||
Serial.println("rmdir failed");
|
||||
}
|
||||
}
|
||||
|
||||
void readFile(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Reading file: %s\n", path);
|
||||
|
||||
File file = fs.open(path);
|
||||
if (!file) {
|
||||
Serial.println("Failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.print("Read from file: ");
|
||||
while (file.available()) {
|
||||
Serial.write(file.read());
|
||||
}
|
||||
}
|
||||
|
||||
void writeFile(fs::FS &fs, const char *path, const char *message) {
|
||||
Serial.printf("Writing file: %s\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_WRITE);
|
||||
if (!file) {
|
||||
Serial.println("Failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
if (file.print(message)) {
|
||||
Serial.println("File written");
|
||||
} else {
|
||||
Serial.println("Write failed");
|
||||
}
|
||||
}
|
||||
|
||||
void appendFile(fs::FS &fs, const char *path, const char *message) {
|
||||
Serial.printf("Appending to file: %s\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_APPEND);
|
||||
if (!file) {
|
||||
Serial.println("Failed to open file for appending");
|
||||
return;
|
||||
}
|
||||
if (file.print(message)) {
|
||||
Serial.println("Message appended");
|
||||
} else {
|
||||
Serial.println("Append failed");
|
||||
}
|
||||
}
|
||||
|
||||
void renameFile(fs::FS &fs, const char *path1, const char *path2) {
|
||||
Serial.printf("Renaming file %s to %s\n", path1, path2);
|
||||
if (fs.rename(path1, path2)) {
|
||||
Serial.println("File renamed");
|
||||
} else {
|
||||
Serial.println("Rename failed");
|
||||
}
|
||||
}
|
||||
|
||||
void deleteFile(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Deleting file: %s\n", path);
|
||||
if (fs.remove(path)) {
|
||||
Serial.println("File deleted");
|
||||
} else {
|
||||
Serial.println("Delete failed");
|
||||
}
|
||||
}
|
||||
|
||||
void testFileIO(fs::FS &fs, const char *path) {
|
||||
File file = fs.open(path);
|
||||
static uint8_t buf[512];
|
||||
size_t len = 0;
|
||||
uint32_t start = millis();
|
||||
uint32_t end = start;
|
||||
if (file) {
|
||||
len = file.size();
|
||||
size_t flen = len;
|
||||
start = millis();
|
||||
while (len) {
|
||||
size_t toRead = len;
|
||||
if (toRead > 512) {
|
||||
toRead = 512;
|
||||
}
|
||||
file.read(buf, toRead);
|
||||
len -= toRead;
|
||||
}
|
||||
end = millis() - start;
|
||||
Serial.printf("%zu bytes read for %lu ms\n", flen, end);
|
||||
file.close();
|
||||
} else {
|
||||
Serial.println("Failed to open file for reading");
|
||||
}
|
||||
|
||||
file = fs.open(path, FILE_WRITE);
|
||||
if (!file) {
|
||||
Serial.println("Failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
start = millis();
|
||||
for (i = 0; i < 2048; i++) {
|
||||
file.write(buf, 512);
|
||||
}
|
||||
end = millis() - start;
|
||||
Serial.printf("%u bytes written for %lu ms\n", 2048 * 512, end);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
/*
|
||||
// If you want to change the pin assignment or you get an error that some pins
|
||||
// are not assigned on ESP32-S3/ESP32-P4 uncomment this block and the appropriate
|
||||
// line depending if you want to use 1-bit or 4-bit line.
|
||||
// Please note that ESP32 does not allow pin change and setPins() will always fail.
|
||||
//if(! SD_MMC.setPins(clk, cmd, d0)){
|
||||
//if(! SD_MMC.setPins(clk, cmd, d0, d1, d2, d3)){
|
||||
// Serial.println("Pin change failed!");
|
||||
// return;
|
||||
//}
|
||||
*/
|
||||
|
||||
if (!SD_MMC.begin()) {
|
||||
Serial.println("Card Mount Failed");
|
||||
return;
|
||||
}
|
||||
uint8_t cardType = SD_MMC.cardType();
|
||||
|
||||
if (cardType == CARD_NONE) {
|
||||
Serial.println("No SD_MMC card attached");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.print("SD_MMC Card Type: ");
|
||||
if (cardType == CARD_MMC) {
|
||||
Serial.println("MMC");
|
||||
} else if (cardType == CARD_SD) {
|
||||
Serial.println("SDSC");
|
||||
} else if (cardType == CARD_SDHC) {
|
||||
Serial.println("SDHC");
|
||||
} else {
|
||||
Serial.println("UNKNOWN");
|
||||
}
|
||||
|
||||
uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
|
||||
Serial.printf("SD_MMC Card Size: %lluMB\n", cardSize);
|
||||
|
||||
listDir(SD_MMC, "/", 0);
|
||||
createDir(SD_MMC, "/mydir");
|
||||
listDir(SD_MMC, "/", 0);
|
||||
removeDir(SD_MMC, "/mydir");
|
||||
listDir(SD_MMC, "/", 2);
|
||||
writeFile(SD_MMC, "/hello.txt", "Hello ");
|
||||
appendFile(SD_MMC, "/hello.txt", "World!\n");
|
||||
readFile(SD_MMC, "/hello.txt");
|
||||
deleteFile(SD_MMC, "/foo.txt");
|
||||
renameFile(SD_MMC, "/hello.txt", "/foo.txt");
|
||||
readFile(SD_MMC, "/foo.txt");
|
||||
testFileIO(SD_MMC, "/test.txt");
|
||||
Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024));
|
||||
Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(10);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
requires:
|
||||
- CONFIG_SOC_SDMMC_HOST_SUPPORTED=y
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* pin 1 - D2 | Micro SD card |
|
||||
* pin 2 - D3 | /
|
||||
* pin 3 - CMD | |__
|
||||
* pin 4 - VDD (3.3V) | |
|
||||
* pin 5 - CLK | 8 7 6 5 4 3 2 1 /
|
||||
* pin 6 - VSS (GND) | ▄ ▄ ▄ ▄ ▄ ▄ ▄ ▄ /
|
||||
* pin 7 - D0 | ▀ ▀ █ ▀ █ ▀ ▀ ▀ |
|
||||
* pin 8 - D1 |_________________|
|
||||
* ║ ║ ║ ║ ║ ║ ║ ║
|
||||
* ╔═══════╝ ║ ║ ║ ║ ║ ║ ╚═════════╗
|
||||
* ║ ║ ║ ║ ║ ║ ╚══════╗ ║
|
||||
* ║ ╔═════╝ ║ ║ ║ ╚═════╗ ║ ║
|
||||
* Connections for ║ ║ ╔═══╩═║═║═══╗ ║ ║ ║
|
||||
* full-sized ║ ║ ║ ╔═╝ ║ ║ ║ ║ ║
|
||||
* SD card ║ ║ ║ ║ ║ ║ ║ ║ ║
|
||||
* ESP32-P4 Func EV | 40 39 GND 43 3V3 GND 44 43 42 | SLOT 0 (IO_MUX)
|
||||
* ESP32-S3 DevKit | 21 47 GND 39 3V3 GND 40 41 42 |
|
||||
* ESP32-S3-USB-OTG | 38 37 GND 36 3V3 GND 35 34 33 |
|
||||
* ESP32 | 4 2 GND 14 3V3 GND 15 13 12 |
|
||||
* Pin name | D1 D0 VSS CLK VDD VSS CMD D3 D2 |
|
||||
* SD pin number | 8 7 6 5 4 3 2 1 9 /
|
||||
* | █/
|
||||
* |__▍___▊___█___█___█___█___█___█___/
|
||||
* WARNING: ALL data pins must be pulled up to 3.3V with external 10k Ohm resistor!
|
||||
* Note to ESP32 pin 2 (D0): Add 1K Ohm pull-up resistor to 3.3V after flashing
|
||||
*
|
||||
* For more info see file README.md in this library or on URL:
|
||||
* https://github.com/espressif/arduino-esp32/tree/master/libraries/SD_MMC
|
||||
*/
|
||||
|
||||
#include "FS.h"
|
||||
#include "SD_MMC.h"
|
||||
#include "SPI.h"
|
||||
#include <time.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
const char *ssid = "your-ssid";
|
||||
const char *password = "your-password";
|
||||
|
||||
long timezone = 1;
|
||||
byte daysavetime = 1;
|
||||
|
||||
// Default pins for ESP-S3
|
||||
// Warning: ESP32-S3-WROOM-2 is using most of the default SD_MMC GPIOs (33-37) to interface with on-board OPI flash.
|
||||
// If the SD_MMC is initialized with default pins it will result in rebooting loop - please
|
||||
// reassign the pins elsewhere using the mentioned command `setPins`.
|
||||
// Note: ESP32-S3-WROOM-1 does not have GPIO 33 and 34 broken out.
|
||||
// Note: The board ESP32-S3-USB-OTG has predefined default pins and the following definitions with the setPins() call will not be compiled.
|
||||
// Note: Pins in this definition block are ordered from top down in order in which they are on the full-sized SD card
|
||||
// from left to right when facing the pins down (when connected to a breadboard)
|
||||
|
||||
#if defined(SOC_SDMMC_USE_GPIO_MATRIX) && not defined(BOARD_HAS_SDMMC)
|
||||
int d1 = 21; // SD pin 8 - Add a 10k Ohm pull-up resistor to 3.3V if using 4-bit mode (use_1_bit_mode = false)
|
||||
int d0 = 47; // SD pin 7 - Add a 10k Ohm pull-up resistor to 3.3V
|
||||
// GND pin - SD pin 6
|
||||
int clk = 39; // SD pin 5 - Add a 10k Ohm pull-up resistor to 3.3V
|
||||
// 3.3V pin - SD pin 4
|
||||
// GND pin - SD pin 3
|
||||
int cmd = 40; // SD pin 2 - Add a 10k Ohm pull-up resistor to 3.3V
|
||||
int d3 = 41; // SD pin 1 - Add a 10k Ohm pull-up resistor to 3.3V to card's pin even when using 1-bit mode
|
||||
int d2 = 42; // SD pin 9 - Add a 10k Ohm pull-up resistor to 3.3V if using 4-bit mode (use_1_bit_mode = false)
|
||||
#endif
|
||||
|
||||
bool use_1_bit_mode = false; // Change the value to `true` to use 1-bit mode instead of the 4-bit mode
|
||||
|
||||
void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
|
||||
Serial.printf("Listing directory: %s\n", dirname);
|
||||
|
||||
File root = fs.open(dirname);
|
||||
if (!root) {
|
||||
Serial.println("Failed to open directory");
|
||||
return;
|
||||
}
|
||||
if (!root.isDirectory()) {
|
||||
Serial.println("Not a directory");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (file.isDirectory()) {
|
||||
Serial.print(" DIR : ");
|
||||
Serial.print(file.name());
|
||||
time_t t = file.getLastWrite();
|
||||
struct tm tmstruct;
|
||||
localtime_r(&t, &tmstruct);
|
||||
Serial.printf(
|
||||
" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min,
|
||||
tmstruct.tm_sec
|
||||
);
|
||||
if (levels) {
|
||||
listDir(fs, file.path(), levels - 1);
|
||||
}
|
||||
} else {
|
||||
Serial.print(" FILE: ");
|
||||
Serial.print(file.name());
|
||||
Serial.print(" SIZE: ");
|
||||
Serial.print(file.size());
|
||||
time_t t = file.getLastWrite();
|
||||
struct tm tmstruct;
|
||||
localtime_r(&t, &tmstruct);
|
||||
Serial.printf(
|
||||
" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min,
|
||||
tmstruct.tm_sec
|
||||
);
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
void createDir(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Creating Dir: %s\n", path);
|
||||
if (fs.mkdir(path)) {
|
||||
Serial.println("Dir created");
|
||||
} else {
|
||||
Serial.println("mkdir failed");
|
||||
}
|
||||
}
|
||||
|
||||
void removeDir(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Removing Dir: %s\n", path);
|
||||
if (fs.rmdir(path)) {
|
||||
Serial.println("Dir removed");
|
||||
} else {
|
||||
Serial.println("rmdir failed");
|
||||
}
|
||||
}
|
||||
|
||||
void readFile(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Reading file: %s\n", path);
|
||||
|
||||
File file = fs.open(path);
|
||||
if (!file) {
|
||||
Serial.println("Failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.print("Read from file: ");
|
||||
while (file.available()) {
|
||||
Serial.write(file.read());
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void writeFile(fs::FS &fs, const char *path, const char *message) {
|
||||
Serial.printf("Writing file: %s\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_WRITE);
|
||||
if (!file) {
|
||||
Serial.println("Failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
if (file.print(message)) {
|
||||
Serial.println("File written");
|
||||
} else {
|
||||
Serial.println("Write failed");
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void appendFile(fs::FS &fs, const char *path, const char *message) {
|
||||
Serial.printf("Appending to file: %s\n", path);
|
||||
|
||||
File file = fs.open(path, FILE_APPEND);
|
||||
if (!file) {
|
||||
Serial.println("Failed to open file for appending");
|
||||
return;
|
||||
}
|
||||
if (file.print(message)) {
|
||||
Serial.println("Message appended");
|
||||
} else {
|
||||
Serial.println("Append failed");
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void renameFile(fs::FS &fs, const char *path1, const char *path2) {
|
||||
Serial.printf("Renaming file %s to %s\n", path1, path2);
|
||||
if (fs.rename(path1, path2)) {
|
||||
Serial.println("File renamed");
|
||||
} else {
|
||||
Serial.println("Rename failed");
|
||||
}
|
||||
}
|
||||
|
||||
void deleteFile(fs::FS &fs, const char *path) {
|
||||
Serial.printf("Deleting file: %s\n", path);
|
||||
if (fs.remove(path)) {
|
||||
Serial.println("File deleted");
|
||||
} else {
|
||||
Serial.println("Delete failed");
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
// We start by connecting to a WiFi network
|
||||
Serial.println();
|
||||
Serial.println();
|
||||
Serial.print("Connecting to ");
|
||||
Serial.println(ssid);
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("WiFi connected");
|
||||
Serial.println("IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
Serial.println("Contacting Time Server");
|
||||
configTime(3600 * timezone, daysavetime * 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
|
||||
struct tm tmstruct;
|
||||
delay(2000);
|
||||
tmstruct.tm_year = 0;
|
||||
getLocalTime(&tmstruct, 5000);
|
||||
Serial.printf(
|
||||
"\nNow is : %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min,
|
||||
tmstruct.tm_sec
|
||||
);
|
||||
Serial.println("");
|
||||
|
||||
// If you are using any other ESP32-S3 board than ESP32-S3-USB-OTG which has preset default pins, you will
|
||||
// need to specify the pins with the following example of SD_MMC.setPins()
|
||||
// If you want to use only 1-bit mode, you can use the line with only one data pin (d0) begin changed.
|
||||
// Please note that ESP32 does not allow pin changes and will fail unless you enter the same pin config as is the hardwired.
|
||||
#if defined(SOC_SDMMC_USE_GPIO_MATRIX) && not defined(BOARD_HAS_SDMMC)
|
||||
if (use_1_bit_mode) {
|
||||
if (!SD_MMC.setPins(clk, cmd, d0)) { // 1-bit line version
|
||||
Serial.println("Pin change failed!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!SD_MMC.setPins(clk, cmd, d0, d1, d2, d3)) { // 4-bit line version
|
||||
Serial.println("Pin change failed!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!SD_MMC.begin("/sdcard", use_1_bit_mode)) {
|
||||
Serial.println("Card Mount Failed.");
|
||||
Serial.println("Increase log level to see more info: Tools > Core Debug Level > Verbose");
|
||||
Serial.println("Make sure that all data pins have a 10k Ohm pull-up resistor to 3.3V");
|
||||
#ifdef SOC_SDMMC_USE_GPIO_MATRIX
|
||||
Serial.println("Make sure that when using generic ESP32-S3 board the pins are setup using SD_MMC.setPins()");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
uint8_t cardType = SD_MMC.cardType();
|
||||
|
||||
if (cardType == CARD_NONE) {
|
||||
Serial.println("No SD card attached");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.print("SD Card Type: ");
|
||||
if (cardType == CARD_MMC) {
|
||||
Serial.println("MMC");
|
||||
} else if (cardType == CARD_SD) {
|
||||
Serial.println("SDSC");
|
||||
} else if (cardType == CARD_SDHC) {
|
||||
Serial.println("SDHC");
|
||||
} else {
|
||||
Serial.println("UNKNOWN");
|
||||
}
|
||||
|
||||
uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
|
||||
Serial.printf("SD Card Size: %lluMB\n", cardSize);
|
||||
|
||||
listDir(SD_MMC, "/", 0);
|
||||
removeDir(SD_MMC, "/mydir");
|
||||
createDir(SD_MMC, "/mydir");
|
||||
deleteFile(SD_MMC, "/hello.txt");
|
||||
writeFile(SD_MMC, "/hello.txt", "Hello ");
|
||||
appendFile(SD_MMC, "/hello.txt", "World!\n");
|
||||
listDir(SD_MMC, "/", 0);
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
@@ -0,0 +1,6 @@
|
||||
requires:
|
||||
- CONFIG_SOC_SDMMC_HOST_SUPPORTED=y
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_WIFI_SUPPORTED=y
|
||||
- CONFIG_ESP_WIFI_REMOTE_ENABLED=y
|
||||
@@ -0,0 +1,9 @@
|
||||
name=SD_MMC
|
||||
version=3.3.7
|
||||
author=Hristo Gochkov, Ivan Grokhtkov
|
||||
maintainer=Hristo Gochkov <hristo@espressif.com>
|
||||
sentence=ESP32 SDMMC File System
|
||||
paragraph=
|
||||
category=Data Storage
|
||||
url=
|
||||
architectures=esp32
|
||||
@@ -0,0 +1,429 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Disable the automatic pin remapping of the API calls in this file
|
||||
#define ARDUINO_CORE_BUILD
|
||||
|
||||
#include "pins_arduino.h"
|
||||
#include "io_pin_remap.h"
|
||||
#include "SD_MMC.h"
|
||||
#ifdef SOC_SDMMC_HOST_SUPPORTED
|
||||
#include "vfs_api.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "driver/sdmmc_defs.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "diskio_sdmmc.h"
|
||||
#include "diskio.h"
|
||||
#include "soc/sdmmc_pins.h"
|
||||
#include "ff.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
|
||||
#if SOC_SDMMC_IO_POWER_EXTERNAL
|
||||
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
|
||||
#endif
|
||||
|
||||
using namespace fs;
|
||||
|
||||
SDMMCFS::SDMMCFS(FSImplPtr impl) : FS(impl), _card(nullptr) {
|
||||
#if defined(SOC_SDMMC_USE_GPIO_MATRIX) && defined(BOARD_HAS_SDMMC) && !defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
_pin_clk = SDMMC_CLK;
|
||||
_pin_cmd = SDMMC_CMD;
|
||||
_pin_d0 = SDMMC_D0;
|
||||
#ifndef BOARD_HAS_1BIT_SDMMC
|
||||
_pin_d1 = SDMMC_D1;
|
||||
_pin_d2 = SDMMC_D2;
|
||||
_pin_d3 = SDMMC_D3;
|
||||
#endif // BOARD_HAS_1BIT_SDMMC
|
||||
|
||||
#elif defined(SOC_SDMMC_USE_IOMUX) && defined(CONFIG_IDF_TARGET_ESP32)
|
||||
_pin_clk = SDMMC_SLOT1_IOMUX_PIN_NUM_CLK;
|
||||
_pin_cmd = SDMMC_SLOT1_IOMUX_PIN_NUM_CMD;
|
||||
_pin_d0 = SDMMC_SLOT1_IOMUX_PIN_NUM_D0;
|
||||
#ifndef BOARD_HAS_1BIT_SDMMC
|
||||
_pin_d1 = SDMMC_SLOT1_IOMUX_PIN_NUM_D1;
|
||||
_pin_d2 = SDMMC_SLOT1_IOMUX_PIN_NUM_D2;
|
||||
_pin_d3 = SDMMC_SLOT1_IOMUX_PIN_NUM_D3;
|
||||
#endif // BOARD_HAS_1BIT_SDMMC
|
||||
|
||||
// ESP32-P4 can use either IOMUX or GPIO matrix
|
||||
#elif defined(BOARD_HAS_SDMMC) && defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
#if defined(BOARD_SDMMC_SLOT) && (BOARD_SDMMC_SLOT == 0)
|
||||
_pin_clk = SDMMC_SLOT0_IOMUX_PIN_NUM_CLK;
|
||||
_pin_cmd = SDMMC_SLOT0_IOMUX_PIN_NUM_CMD;
|
||||
_pin_d0 = SDMMC_SLOT0_IOMUX_PIN_NUM_D0;
|
||||
#ifndef BOARD_HAS_1BIT_SDMMC
|
||||
_pin_d1 = SDMMC_SLOT0_IOMUX_PIN_NUM_D1;
|
||||
_pin_d2 = SDMMC_SLOT0_IOMUX_PIN_NUM_D2;
|
||||
_pin_d3 = SDMMC_SLOT0_IOMUX_PIN_NUM_D3;
|
||||
#endif // BOARD_HAS_1BIT_SDMMC
|
||||
#else
|
||||
_pin_clk = SDMMC_CLK;
|
||||
_pin_cmd = SDMMC_CMD;
|
||||
_pin_d0 = SDMMC_D0;
|
||||
#ifndef BOARD_HAS_1BIT_SDMMC
|
||||
_pin_d1 = SDMMC_D1;
|
||||
_pin_d2 = SDMMC_D2;
|
||||
_pin_d3 = SDMMC_D3;
|
||||
#endif // BOARD_HAS_1BIT_SDMMC
|
||||
#endif // BOARD_SDMMC_SLOT_NO
|
||||
#endif
|
||||
#if defined(SOC_SDMMC_IO_POWER_EXTERNAL) && defined(BOARD_SDMMC_POWER_CHANNEL)
|
||||
_power_channel = BOARD_SDMMC_POWER_CHANNEL;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SDMMCFS::sdmmcDetachBus(void *bus_pointer) {
|
||||
SDMMCFS *bus = (SDMMCFS *)bus_pointer;
|
||||
if (bus->_card) {
|
||||
bus->end();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDMMCFS::setPins(int clk, int cmd, int d0) {
|
||||
return setPins(clk, cmd, d0, GPIO_NUM_NC, GPIO_NUM_NC, GPIO_NUM_NC);
|
||||
}
|
||||
|
||||
bool SDMMCFS::setPins(int clk, int cmd, int d0, int d1, int d2, int d3) {
|
||||
if (_card != nullptr) {
|
||||
log_e("SD_MMC.setPins must be called before SD_MMC.begin");
|
||||
return false;
|
||||
}
|
||||
|
||||
// map logical pins to GPIO numbers
|
||||
clk = digitalPinToGPIONumber(clk);
|
||||
cmd = digitalPinToGPIONumber(cmd);
|
||||
d0 = digitalPinToGPIONumber(d0);
|
||||
d1 = digitalPinToGPIONumber(d1);
|
||||
d2 = digitalPinToGPIONumber(d2);
|
||||
d3 = digitalPinToGPIONumber(d3);
|
||||
|
||||
#if defined(SOC_SDMMC_USE_GPIO_MATRIX) && !defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
// SoC supports SDMMC pin configuration via GPIO matrix. Save the pins for later use in SDMMCFS::begin.
|
||||
_pin_clk = (int8_t)clk;
|
||||
_pin_cmd = (int8_t)cmd;
|
||||
_pin_d0 = (int8_t)d0;
|
||||
_pin_d1 = (int8_t)d1;
|
||||
_pin_d2 = (int8_t)d2;
|
||||
_pin_d3 = (int8_t)d3;
|
||||
return true;
|
||||
#elif CONFIG_IDF_TARGET_ESP32
|
||||
// ESP32 doesn't support SDMMC pin configuration via GPIO matrix.
|
||||
// Since SDMMCFS::begin hardcodes the usage of slot 1, only check if
|
||||
// the pins match slot 1 pins.
|
||||
bool pins_ok =
|
||||
(clk == (int)SDMMC_SLOT1_IOMUX_PIN_NUM_CLK) && (cmd == (int)SDMMC_SLOT1_IOMUX_PIN_NUM_CMD) && (d0 == (int)SDMMC_SLOT1_IOMUX_PIN_NUM_D0)
|
||||
&& (((d1 == -1) && (d2 == -1) && (d3 == -1)) || ((d1 == (int)SDMMC_SLOT1_IOMUX_PIN_NUM_D1) && (d2 == (int)SDMMC_SLOT1_IOMUX_PIN_NUM_D2) && (d3 == (int)SDMMC_SLOT1_IOMUX_PIN_NUM_D3)));
|
||||
if (!pins_ok) {
|
||||
log_e("SDMMCFS: specified pins are not supported by this chip.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
#if defined(BOARD_SDMMC_SLOT) && (BOARD_SDMMC_SLOT == 0)
|
||||
// ESP32-P4 can use either IOMUX or GPIO matrix
|
||||
bool pins_ok =
|
||||
(clk == (int)SDMMC_SLOT0_IOMUX_PIN_NUM_CLK) && (cmd == (int)SDMMC_SLOT0_IOMUX_PIN_NUM_CMD) && (d0 == (int)SDMMC_SLOT0_IOMUX_PIN_NUM_D0)
|
||||
&& (((d1 == -1) && (d2 == -1) && (d3 == -1)) || ((d1 == (int)SDMMC_SLOT0_IOMUX_PIN_NUM_D1) && (d2 == (int)SDMMC_SLOT0_IOMUX_PIN_NUM_D2) && (d3 == (int)SDMMC_SLOT0_IOMUX_PIN_NUM_D3)));
|
||||
if (!pins_ok) {
|
||||
log_e("SDMMCFS: specified pins are not supported when using IOMUX (SDMMC SLOT 0).");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
_pin_clk = (int8_t)clk;
|
||||
_pin_cmd = (int8_t)cmd;
|
||||
_pin_d0 = (int8_t)d0;
|
||||
_pin_d1 = (int8_t)d1;
|
||||
_pin_d2 = (int8_t)d2;
|
||||
_pin_d3 = (int8_t)d3;
|
||||
return true;
|
||||
#endif
|
||||
#else
|
||||
#error SoC not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SOC_SDMMC_IO_POWER_EXTERNAL
|
||||
bool SDMMCFS::setPowerChannel(int power_channel) {
|
||||
if (_card != nullptr) {
|
||||
log_e("SD_MMC.setPowerChannel must be called before SD_MMC.begin");
|
||||
return false;
|
||||
}
|
||||
_power_channel = power_channel;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool SDMMCFS::begin(const char *mountpoint, bool mode1bit, bool format_if_mount_failed, int sdmmc_frequency, uint8_t maxOpenFiles) {
|
||||
if (_card) {
|
||||
return true;
|
||||
}
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_SDMMC_CLK, SDMMCFS::sdmmcDetachBus);
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_SDMMC_CMD, SDMMCFS::sdmmcDetachBus);
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_SDMMC_D0, SDMMCFS::sdmmcDetachBus);
|
||||
if (!mode1bit) {
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_SDMMC_D1, SDMMCFS::sdmmcDetachBus);
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_SDMMC_D2, SDMMCFS::sdmmcDetachBus);
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_SDMMC_D3, SDMMCFS::sdmmcDetachBus);
|
||||
}
|
||||
//mount
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
#if (defined(SOC_SDMMC_USE_GPIO_MATRIX) && !defined(CONFIG_IDF_TARGET_ESP32P4)) \
|
||||
|| (defined(CONFIG_IDF_TARGET_ESP32P4) && ((defined(BOARD_SDMMC_SLOT) && (BOARD_SDMMC_SLOT == 1)) || !defined(BOARD_HAS_SDMMC)))
|
||||
log_d("pin_cmd: %d, pin_clk: %d, pin_d0: %d, pin_d1: %d, pin_d2: %d, pin_d3: %d", _pin_cmd, _pin_clk, _pin_d0, _pin_d1, _pin_d2, _pin_d3);
|
||||
// SoC supports SDMMC pin configuration via GPIO matrix.
|
||||
// Check that the pins have been set either in the constructor or setPins function.
|
||||
if (_pin_cmd == -1 || _pin_clk == -1 || _pin_d0 == -1 || (!mode1bit && (_pin_d1 == -1 || _pin_d2 == -1 || _pin_d3 == -1))) {
|
||||
log_e("SDMMCFS: some SD pins are not set");
|
||||
return false;
|
||||
}
|
||||
|
||||
slot_config.clk = (gpio_num_t)_pin_clk;
|
||||
slot_config.cmd = (gpio_num_t)_pin_cmd;
|
||||
slot_config.d0 = (gpio_num_t)_pin_d0;
|
||||
slot_config.d1 = (gpio_num_t)_pin_d1;
|
||||
slot_config.d2 = (gpio_num_t)_pin_d2;
|
||||
slot_config.d3 = (gpio_num_t)_pin_d3;
|
||||
slot_config.width = 4;
|
||||
#endif // SOC_SDMMC_USE_GPIO_MATRIX
|
||||
|
||||
if (!perimanClearPinBus(_pin_cmd)) {
|
||||
return false;
|
||||
}
|
||||
if (!perimanClearPinBus(_pin_clk)) {
|
||||
return false;
|
||||
}
|
||||
if (!perimanClearPinBus(_pin_d0)) {
|
||||
return false;
|
||||
}
|
||||
if (!mode1bit) {
|
||||
if (!perimanClearPinBus(_pin_d1)) {
|
||||
return false;
|
||||
}
|
||||
if (!perimanClearPinBus(_pin_d2)) {
|
||||
return false;
|
||||
}
|
||||
if (!perimanClearPinBus(_pin_d3)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
host.flags = SDMMC_HOST_FLAG_4BIT;
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32P4) && defined(BOARD_SDMMC_SLOT) && (BOARD_SDMMC_SLOT == 0)
|
||||
host.slot = SDMMC_HOST_SLOT_0;
|
||||
// reconfigure slot_config to remove all pins in order to use IO_MUX
|
||||
// Use 0 instead of GPIO_NUM_NC (-1) because ESP-IDF's s_check_pin_not_set()
|
||||
// function uses !pin which doesn't work correctly with -1 (GPIO_NUM_NC)
|
||||
slot_config = sdmmc_slot_config_t{
|
||||
.clk = GPIO_NUM_0,
|
||||
.cmd = GPIO_NUM_0,
|
||||
.d0 = GPIO_NUM_0,
|
||||
.d1 = GPIO_NUM_0,
|
||||
.d2 = GPIO_NUM_0,
|
||||
.d3 = GPIO_NUM_0,
|
||||
.d4 = GPIO_NUM_0,
|
||||
.d5 = GPIO_NUM_0,
|
||||
.d6 = GPIO_NUM_0,
|
||||
.d7 = GPIO_NUM_0,
|
||||
.cd = SDMMC_SLOT_NO_CD,
|
||||
.wp = SDMMC_SLOT_NO_WP,
|
||||
.width = 4,
|
||||
.flags = 0,
|
||||
};
|
||||
#else
|
||||
host.slot = SDMMC_HOST_SLOT_1;
|
||||
#endif
|
||||
host.max_freq_khz = sdmmc_frequency;
|
||||
#ifdef BOARD_HAS_1BIT_SDMMC
|
||||
mode1bit = true;
|
||||
#endif
|
||||
if (mode1bit) {
|
||||
host.flags = SDMMC_HOST_FLAG_1BIT; //use 1-line SD mode
|
||||
slot_config.width = 1;
|
||||
}
|
||||
_mode1bit = mode1bit;
|
||||
|
||||
#ifdef SOC_SDMMC_IO_POWER_EXTERNAL
|
||||
if (_power_channel == -1) {
|
||||
log_i("On-chip power channel specified, use external power for SDMMC");
|
||||
} else {
|
||||
sd_pwr_ctrl_ldo_config_t ldo_config = {
|
||||
.ldo_chan_id = _power_channel,
|
||||
};
|
||||
sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL;
|
||||
|
||||
if (sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle) != ESP_OK) {
|
||||
log_e("Failed to create a new on-chip LDO power control driver");
|
||||
return false;
|
||||
}
|
||||
host.pwr_ctrl_handle = pwr_ctrl_handle;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BOARD_SDMMC_POWER_PIN)
|
||||
#ifndef BOARD_SDMMC_POWER_ON_LEVEL
|
||||
#error "BOARD_SDMMC_POWER_ON_LEVEL not defined, please define it in pins_arduino.h"
|
||||
#endif
|
||||
pinMode(BOARD_SDMMC_POWER_PIN, OUTPUT);
|
||||
digitalWrite(BOARD_SDMMC_POWER_PIN, !BOARD_SDMMC_POWER_ON_LEVEL);
|
||||
delay(200);
|
||||
digitalWrite(BOARD_SDMMC_POWER_PIN, BOARD_SDMMC_POWER_ON_LEVEL);
|
||||
perimanSetPinBusExtraType(BOARD_SDMMC_POWER_PIN, "SDMMC_POWER");
|
||||
#endif
|
||||
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = format_if_mount_failed,
|
||||
.max_files = maxOpenFiles,
|
||||
.allocation_unit_size = 0,
|
||||
.disk_status_check_enable = false,
|
||||
.use_one_fat = false
|
||||
};
|
||||
|
||||
esp_err_t ret = esp_vfs_fat_sdmmc_mount(mountpoint, &host, &slot_config, &mount_config, &_card);
|
||||
if (ret != ESP_OK) {
|
||||
if (ret == ESP_FAIL) {
|
||||
log_e("Failed to mount filesystem. If you want the card to be formatted, set format_if_mount_failed = true.");
|
||||
} else if (ret == ESP_ERR_INVALID_STATE) {
|
||||
_impl->mountpoint(mountpoint);
|
||||
log_w("SD Already mounted");
|
||||
return true;
|
||||
} else {
|
||||
log_e("Failed to initialize the card (0x%x). Make sure SD card lines have pull-up resistors in place.", ret);
|
||||
}
|
||||
_card = NULL;
|
||||
return false;
|
||||
}
|
||||
_impl->mountpoint(mountpoint);
|
||||
_pdrv = ff_diskio_get_pdrv_card(_card);
|
||||
|
||||
if (!perimanSetPinBus(_pin_cmd, ESP32_BUS_TYPE_SDMMC_CMD, (void *)(this), -1, -1)) {
|
||||
goto err;
|
||||
}
|
||||
if (!perimanSetPinBus(_pin_clk, ESP32_BUS_TYPE_SDMMC_CLK, (void *)(this), -1, -1)) {
|
||||
goto err;
|
||||
}
|
||||
if (!perimanSetPinBus(_pin_d0, ESP32_BUS_TYPE_SDMMC_D0, (void *)(this), -1, -1)) {
|
||||
goto err;
|
||||
}
|
||||
if (!mode1bit) {
|
||||
if (!perimanSetPinBus(_pin_d1, ESP32_BUS_TYPE_SDMMC_D1, (void *)(this), -1, -1)) {
|
||||
goto err;
|
||||
}
|
||||
if (!perimanSetPinBus(_pin_d2, ESP32_BUS_TYPE_SDMMC_D2, (void *)(this), -1, -1)) {
|
||||
goto err;
|
||||
}
|
||||
if (!perimanSetPinBus(_pin_d3, ESP32_BUS_TYPE_SDMMC_D3, (void *)(this), -1, -1)) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
err:
|
||||
log_e("Failed to set all pins bus to SDMMC");
|
||||
SDMMCFS::sdmmcDetachBus((void *)(this));
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDMMCFS::end() {
|
||||
if (_card) {
|
||||
esp_vfs_fat_sdcard_unmount(_impl->mountpoint(), _card);
|
||||
_impl->mountpoint(NULL);
|
||||
_card = NULL;
|
||||
perimanClearPinBus(_pin_cmd);
|
||||
perimanClearPinBus(_pin_clk);
|
||||
perimanClearPinBus(_pin_d0);
|
||||
if (!_mode1bit) {
|
||||
perimanClearPinBus(_pin_d1);
|
||||
perimanClearPinBus(_pin_d2);
|
||||
perimanClearPinBus(_pin_d3);
|
||||
}
|
||||
#if defined(BOARD_SDMMC_POWER_PIN)
|
||||
perimanClearPinBus(BOARD_SDMMC_POWER_PIN);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
sdcard_type_t SDMMCFS::cardType() {
|
||||
if (!_card) {
|
||||
return CARD_NONE;
|
||||
}
|
||||
return (_card->ocr & SD_OCR_SDHC_CAP) ? CARD_SDHC : CARD_SD;
|
||||
}
|
||||
|
||||
uint64_t SDMMCFS::cardSize() {
|
||||
if (!_card) {
|
||||
return 0;
|
||||
}
|
||||
return (uint64_t)_card->csd.capacity * _card->csd.sector_size;
|
||||
}
|
||||
|
||||
uint64_t SDMMCFS::totalBytes() {
|
||||
FATFS *fsinfo;
|
||||
DWORD fre_clust;
|
||||
if (f_getfree("0:", &fre_clust, &fsinfo) != 0) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t size = ((uint64_t)(fsinfo->csize)) * (fsinfo->n_fatent - 2)
|
||||
#if _MAX_SS != 512
|
||||
* (fsinfo->ssize);
|
||||
#else
|
||||
* 512;
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
|
||||
uint64_t SDMMCFS::usedBytes() {
|
||||
FATFS *fsinfo;
|
||||
DWORD fre_clust;
|
||||
if (f_getfree("0:", &fre_clust, &fsinfo) != 0) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t size = ((uint64_t)(fsinfo->csize)) * ((fsinfo->n_fatent - 2) - (fsinfo->free_clst))
|
||||
#if _MAX_SS != 512
|
||||
* (fsinfo->ssize);
|
||||
#else
|
||||
* 512;
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
|
||||
int SDMMCFS::sectorSize() {
|
||||
if (!_card) {
|
||||
return 0;
|
||||
}
|
||||
return _card->csd.sector_size;
|
||||
}
|
||||
|
||||
int SDMMCFS::numSectors() {
|
||||
if (!_card) {
|
||||
return 0;
|
||||
}
|
||||
return (totalBytes() / _card->csd.sector_size);
|
||||
}
|
||||
|
||||
bool SDMMCFS::readRAW(uint8_t *buffer, uint32_t sector) {
|
||||
return (disk_read(_pdrv, buffer, sector, 1) == 0);
|
||||
}
|
||||
|
||||
bool SDMMCFS::writeRAW(uint8_t *buffer, uint32_t sector) {
|
||||
return (disk_write(_pdrv, buffer, sector, 1) == 0);
|
||||
}
|
||||
|
||||
SDMMCFS SD_MMC = SDMMCFS(FSImplPtr(new VFSImpl()));
|
||||
#endif /* SOC_SDMMC_HOST_SUPPORTED */
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef _SDMMC_H_
|
||||
#define _SDMMC_H_
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#ifndef SOC_SDMMC_HOST_SUPPORTED
|
||||
#ifdef ARDUINO
|
||||
#warning The SDMMC library requires a device with an SDIO Host
|
||||
#endif
|
||||
#else
|
||||
|
||||
#include "FS.h"
|
||||
#include "driver/sdmmc_types.h"
|
||||
#include "sd_defines.h"
|
||||
|
||||
// If reading/writing to the SD card is unstable,
|
||||
// you can define BOARD_MAX_SDMMC_FREQ with lower value (Ex. SDMMC_FREQ_DEFAULT)
|
||||
// in pins_arduino.h for your board variant.
|
||||
#ifndef BOARD_MAX_SDMMC_FREQ
|
||||
#define BOARD_MAX_SDMMC_FREQ SDMMC_FREQ_HIGHSPEED
|
||||
#endif
|
||||
|
||||
namespace fs {
|
||||
|
||||
class SDMMCFS : public FS {
|
||||
protected:
|
||||
sdmmc_card_t *_card;
|
||||
int8_t _pin_clk = -1;
|
||||
int8_t _pin_cmd = -1;
|
||||
int8_t _pin_d0 = -1;
|
||||
int8_t _pin_d1 = -1;
|
||||
int8_t _pin_d2 = -1;
|
||||
int8_t _pin_d3 = -1;
|
||||
#ifdef SOC_SDMMC_IO_POWER_EXTERNAL
|
||||
int8_t _power_channel = -1;
|
||||
#endif
|
||||
uint8_t _pdrv = 0xFF;
|
||||
bool _mode1bit = false;
|
||||
|
||||
public:
|
||||
SDMMCFS(FSImplPtr impl);
|
||||
bool setPins(int clk, int cmd, int d0);
|
||||
bool setPins(int clk, int cmd, int d0, int d1, int d2, int d3);
|
||||
#ifdef SOC_SDMMC_IO_POWER_EXTERNAL
|
||||
bool setPowerChannel(int power_channel);
|
||||
#endif
|
||||
bool begin(
|
||||
const char *mountpoint = "/sdcard", bool mode1bit = false, bool format_if_mount_failed = false, int sdmmc_frequency = BOARD_MAX_SDMMC_FREQ,
|
||||
uint8_t maxOpenFiles = 5
|
||||
);
|
||||
void end();
|
||||
sdcard_type_t cardType();
|
||||
uint64_t cardSize();
|
||||
uint64_t totalBytes();
|
||||
uint64_t usedBytes();
|
||||
int sectorSize();
|
||||
int numSectors();
|
||||
bool readRAW(uint8_t *buffer, uint32_t sector);
|
||||
bool writeRAW(uint8_t *buffer, uint32_t sector);
|
||||
|
||||
private:
|
||||
static bool sdmmcDetachBus(void *bus_pointer);
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD_MMC)
|
||||
extern fs::SDMMCFS SD_MMC;
|
||||
#endif
|
||||
|
||||
#endif /* SOC_SDMMC_HOST_SUPPORTED */
|
||||
#endif /* _SDMMC_H_ */
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef _SD_DEFINES_H_
|
||||
#define _SD_DEFINES_H_
|
||||
|
||||
typedef enum {
|
||||
CARD_NONE,
|
||||
CARD_MMC,
|
||||
CARD_SD,
|
||||
CARD_SDHC,
|
||||
CARD_UNKNOWN
|
||||
} sdcard_type_t;
|
||||
|
||||
#endif /* _SD_DISKIO_H_ */
|
||||
Reference in New Issue
Block a user