Skip to content

ESP32-C3 SD SPI mode, arduino example fails, IDF example works (compiled in an arduino project) #6237

@0x0fe

Description

@0x0fe

Board

ESP32-C3 dev module

Device Description

ESP32-C3 dev module

Hardware Configuration

Micro SDHC card 4GB, wired to SCK:3, MOSI: 4, MISO:5, SD_CS:20

Version

v2.0.2

IDE Name

arduino IDE

Operating System

windows 10

Flash frequency

80M

PSRAM enabled

no

Upload speed

CDC

Description

The Micro SD car socket is wired to GPIOs : SCK:3, MOSI: 4, MISO:5, SD_CS:20,

I copied the IDF SPI SD card example into an arduino project, adjusted the GPIOs and it works perfectly, right away (after setting DMA to auto) at 20MHz and below.

But when i open the SD card SPI example provided in the Arduino IDE for ESP32-C3, set to the exact same pinout, on the exact same board / SD it fails miserably, with CRC errors and timeouts all over the place, regardless of the SPI bus speed (tested from 400K to 20M).

When i compare the traces with logic analyzer i cannot even compare, they are too dissimilar and the traces of the Arduino example are filled with 100ms timeouts. Basically all it can do is to read the card information, sector number, size, without error, but any write access leads to immediate failure.

Sketch

//**********IDE example********************
#include "FS.h"
#include "SD.h"
#include "SPI.h"

#define SCK         3
#define MOSI        4
#define MISO        5
#define SD_CS       20

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());
    }
    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 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("%u bytes read for %u 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 %u ms\n", 2048 * 512, end);
    file.close();
}
void sd_test(){

    SPI.begin(SCK, MISO, MOSI, -1);

    Serial.println("SD test");
    if(!SD.begin(SD_CS,SPI,2000000)){
        Serial.println("Card Mount Failed");
        return;
    }
    
    Serial.println("Card Mount OK");
    uint8_t cardType = SD.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.cardSize() / (1024 * 1024);
    Serial.printf("SD Card Size: %lluMB\n", cardSize);

//    listDir(SD, "/", 0);
//    createDir(SD, "/mydir");
//    listDir(SD, "/", 0);
//    removeDir(SD, "/mydir");
//    listDir(SD, "/", 2);
    writeFile(SD, "/hello.txt", "Hello ");
//    appendFile(SD, "/hello.txt", "World!\n");
//    readFile(SD, "/hello.txt");
    deleteFile(SD, "/foo.txt");
    renameFile(SD, "/hello.txt", "/foo.txt");
    readFile(SD, "/foo.txt");
//    testFileIO(SD, "/test.txt");

    Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
    Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));  
}
void setup(){
  
    Serial.begin(115200);
    while(!Serial); 
    delay(1800);
    Serial.println("System init");

    sd_test();
}
void loop(){
}



//***************IDF Example*******************
#ifdef CORE_DEBUG_LEVEL
#undef CORE_DEBUG_LEVEL
#endif

#define CORE_DEBUG_LEVEL 3
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#define SDMMC_FREQ_DEFAULT      2000

#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_vfs_fat.h"
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
#include "sdmmc_cmd.h"
#include "sdkconfig.h"

static const char *TAG = "FGW";

#define MOUNT_POINT "/sdcard"
#define USE_SPI_MODE

#define SPI_DMA_CHAN SPI_DMA_CH_AUTO
#define PIN_NUM_MISO (gpio_num_t)5
#define PIN_NUM_MOSI (gpio_num_t)4
#define PIN_NUM_CLK  (gpio_num_t)3
#define PIN_NUM_CS   (gpio_num_t)20
#define  ESP_LOGI ESP_LOGE

void app_main(void){
  
    esp_err_t ret;
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = true,
        //.format_if_mount_failed = false,
        .max_files = 5,
        .allocation_unit_size = 16 * 1024
    };
    sdmmc_card_t* card;
    const char mount_point[] = MOUNT_POINT;
    ESP_LOGI(TAG, "Initializing SD card");

    ESP_LOGI(TAG, "Using SPI peripheral");
    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
    spi_bus_config_t bus_cfg = {
        .mosi_io_num = PIN_NUM_MOSI,
        .miso_io_num = PIN_NUM_MISO,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 4000,
    };
    ret = spi_bus_initialize((spi_host_device_t)host.slot, &bus_cfg, SPI_DMA_CHAN);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to initialize bus.");
        return;
    }

    // This initializes the slot without card detect (CD) and write protect (WP) signals.
    // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = PIN_NUM_CS;
    slot_config.host_id = (spi_host_device_t)host.slot;

    ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount filesystem. "
                "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
        } else {
            ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        return;
    }

    sdmmc_card_print_info(stdout, card);

    ESP_LOGI(TAG, "Opening file");
    FILE* f = fopen(MOUNT_POINT"/hello.txt", "w");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        return;
    }
    fprintf(f, "Hello %s!\n", card->cid.name);
    fclose(f);
    ESP_LOGI(TAG, "File written");

    // Check if destination file exists before renaming
    struct stat st;
    if (stat(MOUNT_POINT"/foo.txt", &st) == 0) {
        // Delete it if it exists
        unlink(MOUNT_POINT"/foo.txt");
    }

    // Rename original file
    ESP_LOGI(TAG, "Renaming file");
    if (rename(MOUNT_POINT"/hello.txt", MOUNT_POINT"/foo.txt") != 0) {
        ESP_LOGE(TAG, "Rename failed");
        return;
    }

    // Open renamed file for reading
    ESP_LOGI(TAG, "Reading file");
    f = fopen(MOUNT_POINT"/foo.txt", "r");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for reading");
        return;
    }
    char line[64];
    fgets(line, sizeof(line), f);
    fclose(f);
    // strip newline
    char* pos = strchr(line, '\n');
    if (pos) {
        *pos = '\0';
    }
    ESP_LOGI(TAG, "Read from file: '%s'", line);

    esp_vfs_fat_sdcard_unmount(mount_point, card);
    ESP_LOGI(TAG, "Card unmounted");
    spi_bus_free((spi_host_device_t)host.slot);
}
void setup() {
  
  Serial.begin(115200);
  while(!Serial); 
  delay(1800);
  Serial.println("System init ");
  app_main();
}
void loop() {
  
}

Debug Message

//*****************IDE EXAMPLE DEBUG****************
ESP-ROM:esp32c3-api1-20210207
System init
SD test
Card Mount OK
SD Card Type: SDHC
SD Card Size: 3748MB
Writing file: /hello.txt
[  4587][W][sd_diskio.cpp:177] sdCommand(): no token received
[  4691][W][sd_diskio.cpp:177] sdCommand(): no token received
[  4791][W][sd_diskio.cpp:183] sdCommand(): crc error
[  4891][W][sd_diskio.cpp:183] sdCommand(): crc error
File written
[  4991][W][sd_diskio.cpp:183] sdCommand(): crc error
[  5102][W][sd_diskio.cpp:177] sdCommand(): no token received
[  5263][W][sd_diskio.cpp:177] sdCommand(): no token received
Deleting file: /foo.txt
[  5364][W][sd_diskio.cpp:183] sdCommand(): crc error
[  5468][W][sd_diskio.cpp:177] sdCommand(): no token received
[  5568][W][sd_diskio.cpp:183] sdCommand(): crc error
[  5667][W][sd_diskio.cpp:183] sdCommand(): crc error
[  5768][W][sd_diskio.cpp:183] sdCommand(): crc error
File deleted
Renaming file /hello.txt to /foo.txt
[  5901][W][sd_diskio.cpp:177] sdCommand(): no token received
[  6005][W][sd_diskio.cpp:177] sdCommand(): no token received
[  6105][W][sd_diskio.cpp:183] sdCommand(): crc error
[  6204][W][sd_diskio.cpp:183] sdCommand(): crc error
[  6305][W][sd_diskio.cpp:183] sdCommand(): crc error
File renamed
Reading file: /foo.txt
[  6416][W][sd_diskio.cpp:177] sdCommand(): no token received
[  6517][W][sd_diskio.cpp:183] sdCommand(): crc error
[  6617][W][sd_diskio.cpp:183] sdCommand(): crc error
Read from file: [  6717][W][sd_diskio.cpp:183] sdCommand(): crc error
[  6817][W][sd_diskio.cpp:183] sdCommand(): crc error
Hello [  6921][W][sd_diskio.cpp:177] sdCommand(): no token received
[  7020][W][sd_diskio.cpp:183] sdCommand(): crc error
[  7121][W][sd_diskio.cpp:183] sdCommand(): crc error
Total space: 3747MB
[  7221][W][sd_diskio.cpp:183] sdCommand(): crc error
Used space: 1MB



//*****************IDF EXAMPLE DEBUG****************
E (10090) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (10090) task_wdt:  - IDLE (CPU 0)
E (10090) task_wdt: Tasks currently running:
E (10090) task_wdt: CPU 0: loopTask
System init 
E (11898) FGW: Initializing SD card
E (11898) FGW: Using SPI peripheral
Name: 00000
Type: SDHC/SDXC
Speed: 20 MHz
Size: 3748MB
E (11971) FGW: Opening file
E (12020) FGW: File written
E (12049) FGW: Renaming file
E (12061) FGW: Reading file
E (12064) FGW: Read from file: 'Hello 00000!'
E (12064) FGW: Card unmounted

Other Steps to Reproduce

Well, to reproduce just wire a standard Micro SD to SCK:3, MOSI: 4, MISO:5, SD_CS:20 on a ESP32-C3 dev module or board and run each code, the IDE example fails, the IDF example works, which shows something is wrong in the implementation of the SD SPI on the C3.
It seems unlikely to be the SD driver itself since it has been proven. it works with the IDF example, which uses the same SD card driver, so maybe it has to do with the Arduino SPI driver / DMA? I compared quickly the SD.cpp from arduino with the SD driver of IDF and it seems identical for the SDCommand fonction.

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions