Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SD card implementation does not set SPI mode before activating card #9218

Closed
1 task done
and3rson opened this issue Feb 6, 2024 · 5 comments
Closed
1 task done
Assignees
Labels
Status: Awaiting triage Issue is waiting for triage

Comments

@and3rson
Copy link
Contributor

and3rson commented Feb 6, 2024

Board

ESP32-C3

Device Description

ESP32-C3 SuperMini with ST7789 display and micro SD Card on the same SPI bus.

Hardware Configuration

Several pushbuttons on different pins (unrelated to the problem)

Version

v2.0.14 v2.0.6

IDE Name

nvim

Operating System

ArchLinux

Flash frequency

40MHz

PSRAM enabled

no

Upload speed

N/A

Description

SD library sets SPI mode to 0 as expected.

However, if SPI bus config changes between SD calls (e. g. a TFT display sets mode to 3), the SD library will attempt to use the incorrect mode, fail, and re-init the card.

Possible solution is to ensure that SPI mode is valid when enabling the card, and set it to 0 otherwise.

Signal diagram:
зображення

As you can see, SCK is initially 1 (CPOL=1, mode 3, set by TFT earlier). After 3 attempts, card is re-initialized (notice the burst on the right), and afterwards the mode is set to 0.

Sketch

Arduino_ST7789 gfx(...); // https://github.com/moononournation/Arduino_GFX

// ...

SD.open("/").close();
gfx.fillScreen(display.color565(0, 0, 0));
SD.open("/").close(); // Triggers the error message

Debug Message

[  6140][E][sd_diskio.cpp:200] sdCommand(): Card Failed! cmd: 0x0d
[  6141][E][sd_diskio.cpp:622] ff_sd_status(): Check status failed

Other Steps to Reproduce

No response

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

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.
@and3rson and3rson added the Status: Awaiting triage Issue is waiting for triage label Feb 6, 2024
@me-no-dev
Copy link
Member

This is a bit hard to believe, because SD access is guarded by SPI transactions that do set the mode to 0: https://github.com/espressif/arduino-esp32/blob/master/libraries/SD/src/sd_diskio.cpp#L468

If what you say indeed happens, then you must have hit some edge case/bug that makes transactions not working.
@P-R-O-C-H-Y PTAL

@and3rson
Copy link
Contributor Author

and3rson commented Feb 7, 2024

Thanks for a heads up. I will try to make a minimum reproduction project where it's reproduced 100% of the time and post it shortly.

@and3rson
Copy link
Contributor Author

and3rson commented Feb 7, 2024

I think I found the problem: it's due to speed. If SPI speed is changed to a high value between SD calls, subsequent SD calls will fail.

Example code:

#include <SD.h>

#define SD_SCK 0
#define SD_MISO 8
#define SD_MOSI 1
#define SD_SS 20

#define DBG_PIN 5

#define INTERNAL_TRANSACTION_SPEED 40000000 // 40 MHz

void debugMark() {
    // Helps to visually identify stuff in logic analyzer
    delay(50);
    digitalWrite(DBG_PIN, HIGH);
    delay(100);
    digitalWrite(DBG_PIN, LOW);
    delay(50);
}

void setup() {
    Serial.begin(115200);
    SPI.begin(SD_SCK, SD_MISO, SD_MOSI);
    pinMode(DBG_PIN, OUTPUT);
    digitalWrite(5, LOW);

    delay(2000);

    // Init SD card
    Serial.print("Initializing SD card... ");
    if (!SD.begin(SD_SS)) {
        Serial.println("FAILED!");
        return;
    }
    Serial.println("OK.");

    // Read file
    File f = SD.open("/foo.txt");
    if (f) {
        Serial.println("foo.txt: " + f.readString());
        f.close();
    }

    debugMark();

    // Start internal transaction
    for (int i = 0; i < 256; i++) {
        SPI.beginTransaction(SPISettings(INTERNAL_TRANSACTION_SPEED, MSBFIRST, SPI_MODE3));
        SPI.write(i);
        SPI.endTransaction();
    }

    debugMark();

    // Read file again
    f = SD.open("/foo.txt");
    if (f) {
        Serial.println("foo.txt: " + f.readString());
        f.close();
    }
}

void loop() {}

If INTERNAL_TRANSACTION_SPEED is set to 40 MHz, the following output is observed:

Initializing SD card... OK.
foo.txt: This is file foo.
[ 2801][E][sd_diskio.cpp:200] sdCommand(): Card Failed! cmd: 0x0d
[ 2801][E][sd_diskio.cpp:622] ff_sd_status(): Check status failed
foo.txt: This is file foo.

Timing diagram looks as follows:

зображення

Details:

  • 1 - SPI freq is 400 KHz, then 4 MHz. Card is being properly initialized.
  • 2 - the "internal transaction", i. e. SPI transaction with speed beefed up to 40 MHz.
  • 3..5 - SD library attempts to talk to card, speed is still 40 MHz. This fails several times. This takes ~300 ms.
  • 6 - SD library throws an error and re-initializes the card. This brings speed down to 400 KHz, then 4 MHz. Finally, things are back to normal.

All frequencies listed above are taken directly from the logic analyzer. I specified the exact values since the image is too small for them to be visible. :)

Fixes

If I set INTERNAL_TRANSACTION_SPEED to 4 MHz, this issue is not reproduced.

Alternatively, if I add this...

    SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE3)); // Reset speed back to 4 MHz
    SPI.write(0);
    SPI.endTransaction();

...just before "// Read file again" comment, the issue is not being reproduced as well.

@and3rson
Copy link
Contributor Author

and3rson commented Feb 7, 2024

I also noticed another curious (unrelated) thing: SPI clock polarity is not affected by beginTransaction until the first write. Is this the expected behavior?
e. g.

    SPI.beginTransaction(SPISettings(..., ..., SPI_MODE0));
    SPI.write(0xFF);
    SPI.endTransaction();
    // SCK is now LOW (mode 0)
    SPI.beginTransaction(SPISettings(..., ..., SPI_MODE3));
    SPI.endTransaction();
    // SCK is STILL LOW (despite being in mode 3)

This has affected my TFT display, since clock polarity was not being changed before activating the display.
The only solution was to write a dummy byte directly after beginTransaction.
Related issue: moononournation/Arduino_GFX#433

EDIT: On a second thought, this comment might be off-copic, so I created another issue for this problem: #9221

@and3rson
Copy link
Contributor Author

and3rson commented Feb 7, 2024

Update: my bad, this was only reproducible with v2.0.6. Turns out my project was being built with v2.0.6 for some reason. Sorry for wasting your time.

I cannot reproduce this anymore with v2.0.14. #9221 still persists though.

@and3rson and3rson closed this as completed Feb 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Awaiting triage Issue is waiting for triage
Projects
None yet
Development

No branches or pull requests

3 participants