SD card interferes with TFT display

I'm making a bike speedometer using a ESP32 DEV board, a Adafruit 3.5" TFT display (using SPI mode) and a NEO-6M GPS module. I have it working and displaying, a valid connection, latitude, longitude, speed, and distance traveled.

I now want to track the ride by storing my positions onto the SD card that is on the back of the TFT display. Right now all I have in my sketch is trying to initialize the SD card. On the serial monitor I get the message "Card failed, or not present" and the display only shows the first line of information. I'm assuming some interaction between the card and the display but I don't know if it's a connection or programming problem.

The SD card is formatted as FAT32 and I have tested it using a separate SD module and SD test sketch.

Here are the connections to the ESP32 and the TFT display

TFT ESP32
CLK 18
MISO 19
MOSI 23
CS 15
D/C 27
RST 4
SD-CS 5

GPS module is using pins 16 and 17.

Thanks for all help and comments
John

Sorry I forgot to include the sketch, it's early here.

// Bike Speedometer using GPS NEO-6M-0-001
// with ESP32 DEV bd
// with Adafruit display

#include <TinyGPS++.h>
#include <SPI.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include "Adafruit_HX8357.h"

//#include <TFT_eSPI.h>      // Hardware-specific library
#include <SD.h>

// These are 'flexible' lines that can be changed
#define TFT_CS 15
#define TFT_DC 27
#define TFT_RST 4 // RST can be set to -1 if you tie it to Arduino's reset

//TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
// SoftSPI - note that on some processors this might be *faster* than hardware SPI!
Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, MOSI, SCK, TFT_RST, MISO);


// Connect the GPS Power pin to 5V
// Connect the GPS Ground pin to ground
// Connect the GPS TX (transmit) pin to Digital 16
// Connect the GPS RX (receive) pin to Digital 17

#define RXD2 16
#define TXD2 17
HardwareSerial neogps(1);
TinyGPSPlus GPS;

// GPS Variables

String NMEA1; //Variable for first NMEA sentence
String NMEA2; //Variable for second NMEA sentence
char c; //to read characters coming from the GPS
float deg; //Will hold positin data in simple degree format
float degWhole; //Variable for the whole part of position
float degDec;  //Variable for the decimal part of degree
float GPS_Lon = 00;
float GPS_Lat = 00;
float Latitude = 0.0;
float Longitude = 0.0;
float distance = 0.0;
bool newData = false;
int cntr = 0;

unsigned long int scan_time = millis();
int GPS_Scanned = 0;

// Color definitions
#define BLACK    0x0000
#define BLUE     0x001F
#define RED      0xF800
#define GREEN    0x07E0
#define CYAN     0x07FF
#define MAGENTA  0xF81F
#define YELLOW   0xFFE0 
#define WHITE    0xFFFF

// SD Card Select pin
const int chipSelect = 5;

void setup() {
  // connect at 115200 so we can read the GPS fast enough and echo without dropping chars
  // also spit it out
  Serial.begin(115200);
  delay(5000);

  
   // Set all chip selects high to avoid bus contention during initialisation of each peripheral
//  digitalWrite(21, HIGH); // Touch controller chip select (if used)
//  digitalWrite(15, HIGH); // TFT screen chip select
//  digitalWrite(5, HIGH); // SD card chips select, must use GPIO 5 (ESP32 SS)
  
// GPS
  // Begin serail communications with Neo6mGPS
  Serial.println("Start GPS");
  neogps.begin(9600, SERIAL_8N1, RXD2, TXD2);

  tft.begin();
  tft.fillScreen(BLACK); //clears screen, sets to Black
  tft.setRotation(0);  // rotates screen 90' for landscape mode

    // Sets the background color of the screen to black
  tft.fillScreen(BLACK); 

 // Back to Home button
  /*
  tft.fillRoundRect(30, 20, 50, 30, 10, BLUE);
  tft.drawRoundRect(30, 20, 50, 30, 10, WHITE);
  tft.setCursor(40, 27);
  tft.setTextColor(WHITE);
  tft.setTextSize(2);
  tft.print("<-"); 

  tft.setCursor(100, 30);
  tft.setTextColor(WHITE);
  tft.setTextSize(1);
  tft.print("Back to Main Menu"); 
*/
  // Prints the title on the screen
  tft.setCursor(65, 20);
  tft.setTextColor(WHITE);
  tft.setTextSize(3);
  tft.print("GPS Status"); 

  // Draws the red line under the title
  tft.drawFastHLine(10, 60, 320, RED);

  // draw start/stop logging button
  // (X, Y, W, H, R)
//  tft.fillRoundRect(30, 420, 50, 30, 10, WHITE);
  
 scan_time = millis(); // reset scan time
/*
// Initialize SD card
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    //don't do anything more:
    //while (1);
  }
  else {
    Serial.println("card initialized.");
  }
*/


} // end setup()

void loop() {

  
   // read GPS
  
    while (neogps.available())
    {
      if (GPS.encode(neogps.read()))
      {
        newData = true;
      } // end if (gps.encode(neogps.read
       
    } // end while
    
  if (((millis() - scan_time) > 1000)) {

  
   //if (GPS.location.isValid())
    if(newData == true)
  {
      newData = false;
      Serial.println(F("VALID"));
      Serial.print("Lat = ");
      Serial.print(GPS.location.lat(), 4);
      Serial.print(F("\t"));
      Serial.print("Lon = ");
      Serial.print(GPS.location.lng(), 4);
      Serial.print(F("\t"));
      Serial.print("Speed = ");
      Serial.println(GPS.speed.mph()); // Speed in miles per hour (double)

      // Show green circle when GPS connected
      tft.fillCircle(290, 30, 10, GREEN);

    // test if this part of sketch is working
      cntr = cntr + 1;
      Serial.println(cntr);

   // calculate distance
   if(GPS.speed.mph() > 0.5){
    distance = distance+(GPS.speed.mph()/3600); 
   } // end if(GPS.speed.mph() > 0.5)
   
  }
  else
  {
      Serial.println(F("INVALID"));
      tft.fillCircle(290, 30, 10, RED);
  }

  // Update screen variables
// Display Longitude
    tft.setCursor(50, 70); 
    tft.setTextColor(WHITE);
    tft.setTextSize(2);
    tft.print("Longitude"); 
    tft.setCursor(50, 100);
    tft.setTextColor(WHITE, BLACK);
    tft.setTextSize(4);
    tft.print(GPS.location.lng(), 4);

// Display Latitude
    tft.setCursor(50, 150); 
    tft.setTextColor(WHITE);
    tft.setTextSize(2);
    tft.print("Latitude"); 
    tft.setCursor(50, 180);
    tft.setTextColor(WHITE, BLACK);
    tft.setTextSize(4);
    tft.print(GPS.location.lat(), 4);  

// Display Speed
    tft.setCursor(50, 230); 
    tft.setTextColor(WHITE);
    tft.setTextSize(2);
    tft.print("Speed"); 
    tft.setCursor(50, 260);
    tft.setTextColor(WHITE, BLACK);
    tft.setTextSize(4);
    tft.print(GPS.speed.mph(), 1);

// Display Distance
    tft.setCursor(50, 310); 
    tft.setTextColor(WHITE);
    tft.setTextSize(2);
    tft.print("Distance"); 
    tft.setCursor(50, 340);
    tft.setTextColor(WHITE, BLACK);
    tft.setTextSize(4);
    tft.print(distance);

  
  if (millis() > 5000 && GPS.charsProcessed() < 10)
  {
      Serial.println(F("No GPS detected: check wiring."));
    while(true);
  }

  scan_time = millis(); // reset scan time
  
  } // end (((millis() - scan_time) > 1000)


  if ((millis() - scan_time ) > 3000) {
    scan_time = millis();
    Serial.println("reset");
    GPS_Scanned = 0;



  } // end (millis() - scan_time > 3000)


} // end loop

You can't mix Software SPI with HW SPI on the same pins.
Your sketch does not use the SD at all.

If you want to use SD card. Put it on the same HW bus as the TFT. Make sure that unused CS signals are HIGH. Only one CS signal is active at any one time.

I guess that the HW constructor will be

Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_RST);

You will need to check whether CLK GPIO18, MISO GPIO19, MOSI GPIO23 is correct for HW SPI.

Simple rules for SPI bus:

  1. only one CS active at any one time.
  2. all are genuine SPI Slaves
  3. never mix SW and HW on the same pins.

David.

@david_prentice, good point about HW SPI and Software SPI. My first attempt at this was with another display that used the ILI9488 using the TFT_eSPI library. But that driver can not have any other SPI devices on the same bus. I tried setting up the HSPI bus but couldn't get that to work either so I switched the display. Some of my code still has that configuration, I'll clean that up.

As far as I've found out the pins I have for MOSI, MISO, CLK and CS are the default pins on the ESP32. They do work when I just have the display.

The SD card holder is part of the display so they are on the same bus, no separate connections. The only pin I had to add was the CS for the SD card.

In the sketch I included is the SD card initialization code which currently is commented out.

// Initialize SD card
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    //don't do anything more:
    //while (1);
  }
  else {
    Serial.println("card initialized.");
  }

When I load the sketch in with this section commented out my display shows all the info. When I uncomment that section, I get the "Card Fail" message and the display only shows the first line, although I have tested my code and can see all the info in serial monitor.

Thanks for your help

ILI9488 does not play cricket.
In 4-Wire SPI mode it does not 3-state MISO properly when CS is inactive.

However you can disable the separate SDO pin on ILI9488. Only enable SDO pin when you actually want to read from the ILI9488 e.g. screen memory, ID, ...

As mentioned in a current thread. SPI works fine but you only have one /CS pin active at any one time.

David.

I did have a couple of digitalWrite to set both the TFT-CS and the SD-CS high in setup to disable both CS signals at start up. I assumed, maybe my problem, that after that as I updated the display or did something with the SD card there was internal logic that would clear those automatically after the function was complete. Do I have to put logic in there to set and clear each CS as I need?

Thanks

Most sensibly designed boards will have external pullup(s) on the /CS pin(s)
Which means that you don't need to worry.

Adafruit design things pretty well. And provide schematics so that you can see for yourself.

Chinese Red SPI boards don't have pullups. You have to invest in your own 10k resistor(s).

Adafruit rely on this. They know that their boards will work e.g. have external pullup on TFT_RST. So they omit the RST argument in their example constructors.

If you can't find a schematic you can use your DMM to measure whether there is a pullup resistor between /CS and 3.3V (the output from the AMS1117-3.3)

A general rule of AVR software is to make /SS pin OUTPUT HIGH whether you use SS or not. (AVR haywire-ability feature)

Oh, some hardware chips have weak internal pullups. So they are better than no pullup at all.

Oh, and some level-shifter designs destroy the innate slave pullup behaviour because they do not 3-state. e.g. HC4050
The world will be a better place when every 5V Uno and 5V Mega2560 has been buried in a deep hole.

LOL, don't hold back David tell me what you really think.

Thanks for all the info, very helpful, I'll look into it later when I get home