ESP32 Wroom, LCD and SD sharing VSPI not working

Hello,

I'm currently working on a project and have several components connected to an ESP32 (Wemos D1 Mini ESP32). Most parts are working fine, only I can't get my MicroSD card and the LCD/TFT working using the same SPI pins, sharing VSPI (using different select pins).
The LCD is working properly, but the SD card can not be used. Using this setup without changing the hardware connections, I can activate the SD card. Running the sketch the verbose output shows this:

21:38:44.104 -> Trying to connect to SD card with 20 MHz.
21:38:44.104 -> [  1036][W][sd_diskio.cpp:174] sdCommand(): no token received
21:38:44.231 -> [  1137][W][sd_diskio.cpp:174] sdCommand(): no token received
21:38:44.313 -> [  1237][W][sd_diskio.cpp:174] sdCommand(): no token received
21:38:44.428 -> [  1337][E][sd_diskio.cpp:199] sdCommand(): Card Failed! cmd: 0x00
21:38:44.428 -> [  1337][W][sd_diskio.cpp:516] ff_sd_initialize(): GO_IDLE_STATE failed
21:38:44.428 -> [  1338][E][sd_diskio.cpp:805] sdcard_mount(): f_mount failed: (3) The physical drive cannot work
21:38:44.428 -> [  1347][W][sd_diskio.cpp:174] sdCommand(): no token received
21:38:44.529 -> [  1452][W][sd_diskio.cpp:174] sdCommand(): no token received
21:38:44.598 -> [  1552][W][sd_diskio.cpp:174] sdCommand(): no token received
21:38:44.712 -> [  1652][E][sd_diskio.cpp:199] sdCommand(): Card Failed! cmd: 0x00
21:38:44.712 -> Error talking to SD card!

I reduced the sketch:

/*
 * Using Arduino IDE v2.2.1
 * Settings:
 * Board: ESP32 / Wemos D1 Mini ESP32
 * CPU frequency: 240 MHz (WiFi/BT)
 * Core Debung Level: "Verbose"
 * Erase all Flash before sketch upload: disabled
 * Flash Frequency: 80MHz
 * Partition Scheme: No OTA (Large APP)
 * Upload Speed: 921600
 *
 * Hardware:
 * - Wemos D1 Mini ESP32
 * - 1.54 Inch Full Color IPS LCD Display Module 240x240 SPI Interface ST7789 8Pin
 * - Micro SD card module
 *
 * Connections:
 * ESP32   ST7789 (Display, also used by TTGO T-Display)
 *  IO4     BLK
 *  IO5     CS VSPI:CS
 * IO16     DC
 * IO23     RES (Reset) VSPI:MOSI
 * IO19     SDA VSPI:MISO
 * IO18     SCL VSPI:SCLK
 *
 * SD Card:
 * IO19     SDA VSPI:MISO
 * IO18     SCL VSPI:SCLK
 * IO23     RES VSPI:MOSI
 * IO17     CS
 */

#define USESDCARD
// SD Card chip select
#define SD_CS  17
#define LOGFILENAME "/test.log"

// WiFi
#include "WiFi.h"
#define WIFI_SCAN_PERIOD 5000
long lastWiFiScanMillis;
bool bWifiEnabled;
bool bWifiScanComplete = true;

// LCD/TFT display
// TFT_eSPI version 2.3.70
// use "#include <User_Setups/Setup25_TTGO_T_Display.h>"
#include <TFT_eSPI.h>
#include <SPI.h>
#include <Wire.h>
int tft_width = 240;
int tft_height = 240;
uint8_t tft_rotation = 3;
static const uint16_t tft_sizeScaler = 2;
TFT_eSPI tft = TFT_eSPI( tft_width, tft_height );

// If SD card should be supported
#ifdef USESDCARD
  #include "SD.h"
  uint32_t sdCardSize = 0l; // Card size in byte
  bool sdAvailable = false;
  File logfile;
#endif

// Brownout detector
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"

void setup()
{
  // Disable brownout detector
  WRITE_PERI_REG( RTC_CNTL_BROWN_OUT_REG, 0 );

  Serial.begin( 115200 );
  vTaskDelay( 300 );

  pinMode( TFT_CS, OUTPUT );
  digitalWrite( TFT_CS, HIGH ); // TFT Chip select high (inactive)
  pinMode( SD_CS, OUTPUT );
  digitalWrite( SD_CS, HIGH ); // SDCard Chip select high (inactive)

  enableWiFi();

  // Initialize the screen
  tft.init();
  tft.setTextSize( tft_sizeScaler );
  tft.setRotation( tft_rotation );
  tft.fillRect( 0, 0, tft_width - 1, tft_height - 1, TFT_WHITE );

#ifdef USESDCARD
  digitalWrite( TFT_CS, HIGH ); // TFT Chip select high (inactive)
  sdAvailable = SDCardInit();
#endif

  Serial.println( F( "Initialized" ) );
  tft.drawString( "Hello", 10, 10, 1 );
}

void loop()
{
  unsigned long currentMillis = millis();
  if( bWifiEnabled )
  {
    if( ( currentMillis - lastWiFiScanMillis > WIFI_SCAN_PERIOD ) && ( bWifiScanComplete == true ) )
    {
      bWifiScanComplete = false;
      WiFi.scanNetworks( true );
      Serial.print( "\nScan start ... " );
      lastWiFiScanMillis = currentMillis;
    }
    checkWiFis();
  }
  vTaskDelay( 100 );
}


#ifdef USESDCARD
bool SDCardInit()
{
  bool init = false;
  Serial.println( "Trying to connect to SD card with " + String( SPI_FREQUENCY / 1000000 ) + " MHz." );
  if( SD.begin( SD_CS, tft.getSPIinstance() ) )
  {
    Serial.println( "SD card initialized successfull" );
    Serial.print( "SD card type=" );
    switch( SD.cardType() )
    {
      case CARD_NONE:
        Serial.println( "None" );
        break;
      case CARD_MMC:
        Serial.println( "MMC" );
        break;
      case CARD_SD:
        Serial.println( "SD" );
        break;
      case CARD_SDHC:
        Serial.println( "SDHC" );
        break;
      case CARD_UNKNOWN:
        Serial.println( "Unknown" );
        break;
      default:
        Serial.println( "Unknown" );
        break;
    }

    // Get card size in MB
    sdCardSize = SD.cardSize() / 1000000;
    Serial.print( "Card size:   " ); Serial.print( SD.cardSize() / 1000000 ); Serial.println( "MB" );
    Serial.print( "Total bytes: " ); Serial.print( SD.totalBytes() / 1000000 ); Serial.println( "MB" );
    Serial.print( "Used bytes:  " ); Serial.print( SD.usedBytes() / 1000000 ); Serial.println( "MB" );
  }
  else
  {
    Serial.println( "Error talking to SD card!" );
  }

  return init;
}

// Unmount SD card
void SDCardUnmount()
{
  if( sdAvailable )
  {
    Serial.print( "Unmounting SD card..." );
    SD.end();
    Serial.println( "done." );
    sdAvailable = false;
  }
}

void appendFile( const char *path, const char *message )
{
  if( sdAvailable )
  {
    Serial.printf( "Appending to file: %s\n", path );
    File file = SD.open( path, FILE_APPEND );
    if( !file )
    {
      Serial.println( F( "Failed to open file for appending" ) );
      return;
    }
  
    if( file.print( message ) )
    {
      Serial.println( F( "Message appended" ) );
    }
    else
    {
      Serial.println( F( "Append failed" ) );
    }
    file.close();

  }
}
#endif

void enableWiFi()
{
  bWifiScanComplete = true;
  esp_err_t result;
  bool bresult;

  if( WiFi.isConnected() )
  {
    result = WiFi.disconnect() ;
    Serial.print( F( "WiFi.disconnect()=" ) );
    Serial.println( result );
  }

  bresult = WiFi.mode( WIFI_STA );
  Serial.print( F( "WiFi.mode( WIFI_STA )=" ) );
  if( bresult )
    Serial.println( "OK" );
  else
    Serial.println( "not OK" );

  bWifiEnabled = true;
  lastWiFiScanMillis = millis();
  if( lastWiFiScanMillis > WIFI_SCAN_PERIOD )
    lastWiFiScanMillis -= WIFI_SCAN_PERIOD;
}

void disableWiFi()
{
  esp_err_t result;
  bool bresult;

  if( WiFi.isConnected() )
  {
    result = WiFi.disconnect() ;
    Serial.print( F( "WiFi.disconnect()=" ) );
    Serial.println( result );
  }

  bresult = WiFi.mode( WIFI_OFF );
  Serial.print( F( "WiFi.mode( WIFI_OFF )=" ) );
  if( bresult )
    Serial.println( "OK" );
  else
    Serial.println( "not OK" );

  bWifiEnabled = false;
}

void checkWiFis()
{
  int n = WiFi.scanComplete();
  String tmp;
  if( ( n != WIFI_SCAN_RUNNING ) && ( n != WIFI_SCAN_FAILED ) )
  {
    bWifiScanComplete = true;
    Serial.println( String( n ) + " network(s) found:" );
    for( int i = 0; i < n; i++ )
    {
      tmp = WiFi.SSID( i ) + ", Channel " + String ( WiFi.channel( i ) );
      Serial.println( tmp );
#ifdef USESDCARD
      appendFile( LOGFILENAME, tmp.c_str() );
#endif
    }
    WiFi.scanDelete();
  }
}

My question is: is it possible to share the SPI (using VSPI as per default) between SD and TFT?
The HSPI is used by an other component so I can't / won't share this.

For the complete project the speed of the TFT is not really important, I reduced the SPI frequency down to 20MHz by modifying "Setup25_TTGO_T_Display.h":
#define SPI_FREQUENCY 20000000 // Maximum for ILI9341

Or did I miss something?

Best regards
Nils

Try slowing it down a lot more and see what happens, once it works you can then speed it up.

I have read that some TFT displays do not free up the SPI bus (do not set certain pins to tri-state) when deselected, and therefore won't share the bus. Some research on line might find the culprit.

Also some SD modules do not free up the SPI bus MISO pin.

on an arduino the SD card module will initialize without an SD card inserted. on an ESP you must have a card in the module to get it to initialize.

have you considered adding an HSPI interface?

Hi,
I have to do more research inside the eTFT library. It might be possible that the MISO pin isn't used by the chosen TFT so it is not defined in the setup. This might be a reason, mabe I have to change this and move the therefore used reset pin to an other one.

SD: the module itself does not contain a chip, AFAIK SPI is a function which is directly implemented in the SD card itself. So some may work, some not with the four wire setup (power, ground, chip select, miso, mosi, clock).

I did some tests some time before with different SD cards, but this one I'm trying to use should work. Without the TFT initialization (and of course set the used VSPI pins inside the sketch) the type, size and free space will be reported correctly.

HSPI is currently not an option, this is used by an other component.

Regards
Nils

If you have both the screen and card connected, but use code for only SD, does it run?

Did you try Gilschultz' suggestion of lowering the speed even more? Maybe 2MHz or less, as a test?

I'm sorry, I've found the issue...
The TFT eSPI library contains several pre defined setups which can be used. I took the inital part of the project from an TTGO-ESP32 which included
Setup25_TTGO_T_Display.h
In this file some configurations have been set:

//#define TFT_MISO -1
#define TFT_MOSI            19
#define TFT_SCLK            18
#define TFT_CS              5
#define TFT_DC              16
#define TFT_RST             23

So the MOSI wasn't in use. Since pin 23 is regulary VSPI:MISO (on ESP32 Wroom) I did not really noticed that the macro "TFT_RST" has an other meaning internally. :frowning: (it is the TFT reset).

So I had to change the wiring and also define the MISO pin to an unused pin:

#define TFT_MISO 15

So I ended with this sketch:

/*
 * Using Arduino IDE v2.2.1
 * Settings:
 * Board: ESP32 / Wemos D1 Mini ESP32
 * CPU frequency: 240 MHz (WiFi/BT)
 * Core Debung Level: "Verbose"
 * Erase all Flash before sketch upload: disabled
 * Flash Frequency: 80MHz
 * Partition Scheme: No OTA (Large APP)
 * Upload Speed: 921600
 *
 * Hardware:
 * - Wemos D1 Mini ESP32
 * - 1.54 Inch Full Color IPS LCD Display Module 240x240 SPI Interface ST7789 8Pin
 * - Micro SD card module
 *
 * Connections:
 * ESP32   ST7789 (Display, also used by TTGO T-Display)
 *  IO4     BLK              (backlight control)
 *  IO5     CS    VSPI:CS    (cable select)
 * IO16     DC               (data/command)
 * IO23     RES              (reset)         
 * IO19     SDA   VSPI:MOSI
 * IO18     SCL   VSPI:SCLK  (clock)
 * IO15 not connected to TFT  VSPI:MISO
 * TFT display pins (left to right):
 * GND VCC SCL SDA RES DC CS BLK
 *
 * SD Card:
 * IO15     VSPI:MISO 
 * IO18     VSPI:SCLK
 * IO19     VSPI:MOSI
 * IO17     CS (cable select)
 */

#define USESDCARD
// SD Card chip select
#define SD_CS  17
#define LOGFILENAME "/test.log"

// WiFi
#include "WiFi.h"
#define WIFI_SCAN_PERIOD 5000
long lastWiFiScanMillis;
bool bWifiEnabled;
bool bWifiScanComplete = true;

// LCD/TFT display
// TFT_eSPI version 2.5.0
// use "#include <User_Setups/Setup25_TTGO_T_Display.h>"
//#include <TFT_eSPI_Setups/Setup25_TTGO_T_Display.h>    // Setup file for ESP32 and TTGO T-Display ST7789V SPI bus TFT
#include <TFT_eSPI.h>
#include <SPI.h>
#include <Wire.h>
int tft_width = 240;
int tft_height = 240;
uint8_t tft_rotation = 3;
static const uint16_t tft_sizeScaler = 2;
TFT_eSPI tft = TFT_eSPI( tft_width, tft_height );

// If SD card should be supported
#ifdef USESDCARD
  #include "SD.h"
  uint32_t sdCardSize = 0l; // Card size in byte
  bool sdAvailable = false;
  File logfile;
#endif

// Brownout detector
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"

void setup()
{
  // Disable brownout detector
  WRITE_PERI_REG( RTC_CNTL_BROWN_OUT_REG, 0 );

  Serial.begin( 115200 );
  vTaskDelay( 300 );

  pinMode( TFT_CS, OUTPUT );
  digitalWrite( TFT_CS, HIGH ); // TFT Chip select high (inactive)
  pinMode( SD_CS, OUTPUT );
  digitalWrite( SD_CS, HIGH ); // SDCard Chip select high (inactive)

//  disableWiFi();
  enableWiFi();

  // Initialize the screen
  tft.init();
  tft.setTextSize( tft_sizeScaler );
  tft.setRotation( tft_rotation );
  tft.fillRect( 0, 0, tft_width - 1, tft_height - 1, TFT_WHITE );

#ifdef USESDCARD
//  digitalWrite( TFT_CS, HIGH ); // TFT Chip select high (inactive)
  sdAvailable = SDCardInit();
#endif

  Serial.println( F( "Initialized" ) );
  tft.drawString( "Hello", 10, 10, 1 );
}

void loop()
{
  unsigned long currentMillis = millis();
  if( bWifiEnabled )
  {
    if( ( currentMillis - lastWiFiScanMillis > WIFI_SCAN_PERIOD ) && ( bWifiScanComplete == true ) )
    {
      bWifiScanComplete = false;
      WiFi.scanNetworks( true );
      Serial.print( "\nScan start ... " );
      lastWiFiScanMillis = currentMillis;
    }
    checkWiFis();
  }
  vTaskDelay( 100 );
}


#ifdef USESDCARD
bool SDCardInit()
{
  bool init = false;
  Serial.println( "Trying to connect to SD card with " + String( SPI_FREQUENCY / 1000000 ) + " MHz." );
  if( SD.begin( SD_CS, tft.getSPIinstance() ) )
  {
    Serial.println( "SD card initialized successfull" );
    Serial.print( "SD card type=" );
    switch( SD.cardType() )
    {
      case CARD_NONE:
        Serial.println( "None" );
        break;
      case CARD_MMC:
        Serial.println( "MMC" );
        break;
      case CARD_SD:
        Serial.println( "SD" );
        break;
      case CARD_SDHC:
        Serial.println( "SDHC" );
        break;
      case CARD_UNKNOWN:
        Serial.println( "Unknown" );
        break;
      default:
        Serial.println( "Unknown" );
        break;
    }

    // Get card size in MB
    sdCardSize = SD.cardSize() / 1000000;
    Serial.print( "Card size:   " ); Serial.print( SD.cardSize() / 1000000 ); Serial.println( "MB" );
    Serial.print( "Total bytes: " ); Serial.print( SD.totalBytes() / 1000000 ); Serial.println( "MB" );
    Serial.print( "Used bytes:  " ); Serial.print( SD.usedBytes() / 1000000 ); Serial.println( "MB" );
    init = true;
  }
  else
  {
    Serial.println( "Error talking to SD card!" );
  }

  return init;
}

// Unmount SD card
void SDCardUnmount()
{
  if( sdAvailable )
  {
    Serial.print( "Unmounting SD card..." );
    SD.end();
    Serial.println( "done." );
    sdAvailable = false;
  }
}

void appendFile( const char *path, const char *message )
{
  if( sdAvailable )
  {
    Serial.printf( "Appending to file: %s\n", path );
    File file = SD.open( path, FILE_APPEND );
    if( !file )
    {
      Serial.println( F( "Failed to open file for appending" ) );
      return;
    }
  
    if( file.print( message ) )
    {
      Serial.println( F( "Message appended" ) );
    }
    else
    {
      Serial.println( F( "Append failed" ) );
    }
    file.close();
  }
  else
  {
    Serial.println( F( "SD not available" ) );
  }
}
#endif

void enableWiFi()
{
  bWifiScanComplete = true;
  esp_err_t result;
  bool bresult;

  if( WiFi.isConnected() )
  {
    result = WiFi.disconnect() ;
    Serial.print( F( "WiFi.disconnect()=" ) );
    Serial.println( result );
  }

  bresult = WiFi.mode( WIFI_STA );
  Serial.print( F( "WiFi.mode( WIFI_STA )=" ) );
  if( bresult )
    Serial.println( "OK" );
  else
    Serial.println( "not OK" );

  bWifiEnabled = true;
  lastWiFiScanMillis = millis();
  if( lastWiFiScanMillis > WIFI_SCAN_PERIOD )
    lastWiFiScanMillis -= WIFI_SCAN_PERIOD;
}

void disableWiFi()
{
  esp_err_t result;
  bool bresult;

  if( WiFi.isConnected() )
  {
    result = WiFi.disconnect() ;
    Serial.print( F( "WiFi.disconnect()=" ) );
    Serial.println( result );
  }

  bresult = WiFi.mode( WIFI_OFF );
  Serial.print( F( "WiFi.mode( WIFI_OFF )=" ) );
  if( bresult )
    Serial.println( "OK" );
  else
    Serial.println( "not OK" );

  bWifiEnabled = false;
}

void checkWiFis()
{
  int n = WiFi.scanComplete();
  String tmp;
  if( ( n != WIFI_SCAN_RUNNING ) && ( n != WIFI_SCAN_FAILED ) )
  {
    bWifiScanComplete = true;
    Serial.println( String( n ) + " network(s) found:" );
    for( int i = 0; i < n; i++ )
    {
      tmp = WiFi.SSID( i ) + ", Channel " + String ( WiFi.channel( i ) );
      Serial.println( tmp );
#ifdef USESDCARD
      appendFile( LOGFILENAME, tmp.c_str() );
#endif
    }
    WiFi.scanDelete();
  }
}

also updated the library to 2.5.0 and I can access the SD card in parallel with the TFT running on 40MHz. :slight_smile:

Thank you all
Nils

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.