Why is SD.begin required after tri-stating the SPI pins?

Using a Nano and Adafruit 3.5" TFT with microSD socket, the following test code opens, writes to, and closes a file.

Then the MOSI and CLK are tri-stated (set to inputs) while the slave selects are held high. After a brief delay, MOSI and CLK are re-set to outputs.

Then the file is opened again, written to, and read.

If the second SD.begin is not used (the one between the *******), the attempt to read the file goes into an infinite loop at the “while(myFile.available()) {read the file}” (and it reads garbage). If MOSI is tri-stated, but the CLK is not tri-stated (left as an output), the same thing happens (infinite read loop).

With the second SD.begin, the read works just fine.

The serial monitor and a TFT (also an SPI device) are used for the debugging statements (the TFT appears to have no problems after the SPI pins are tri-stated and then “un-tri-stated”).

Why is a second SD.begin needed? How does the SD card, code, or break-out “know” that it has been interrupted (especially if CLK is not messed with)? Is something related to the SD happening “in the background”?

Other details: the 5 volt Nano is powered via USB, and the Nano powers the TFT/SD. IDE v1.65-r5 on Windows 7 x64. The slave select pins have external pull-up resistors, so that they are definitely “off” while the Nano pins are tri-stated.

/* test effect of changing SPI pins to inputs
   (to "tri-state" them) and then switch back to outputs
*/

#include <SPI.h>
#include "Adafruit_GFX.h"
#include "Adafruit_HX8357.h"

#include <SdFat.h>

const byte SPI_CLK = 13;
const byte SPI_MISO = 12; // stays input
const byte SPI_MOSI = 11;
const byte TFT_CS = 10; // pulled up by 10k resistor, for unambiguous "off" state while tri-stated
const byte TFT_DC = 9;
const byte SD_CS = 4;   // pulled up by 10k resistor...

byte i = 0;
byte j = 0;

Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC);

SdFat SD;
File myFile;

void setup() {
  Serial.begin(115200);
  Serial.println("oneMasterTFTnSD");

  // start TFT
  tft.begin(HX8357D);
  tft.setRotation(3);
  tft.fillScreen(HX8357_BLACK);
  tft.setTextColor(HX8357_YELLOW);
  tft.setTextSize(2);

  // start SD
  if (!SD.begin(SD_CS)) {

    tft.setCursor(150, j += 20);
    tft.print("SD init fail");

    return;
  }

  // open file and write something
  myFile = SD.open("test.txt", FILE_WRITE);
  if (myFile) {

    tft.setCursor(10, i += 20);
    tft.print(F("Writing test.txt B4 tristate"));
    Serial.println(F("Writing test.txt B4 tristate"));

    myFile.println(F("testing B4 tristate"));
    myFile.close();

    tft.setCursor(10, i += 20);
    tft.println("done.");

  } else {

    tft.setCursor(150, j += 20);
    tft.print(F("err opng test.txt B4 tristate"));
    Serial.println(F("err opng test.txt B4 tristate"));

  }

  // tri-state the pins
  tft.setCursor(10, i += 20);
  tft.print("swch 2 inputs");
  Serial.println("swch 2 inputs");

  pinMode(SPI_CLK, INPUT);
  pinMode(SPI_MOSI, INPUT);
  pinMode(TFT_CS, INPUT);
  pinMode(TFT_DC, INPUT);
  pinMode(SD_CS, INPUT);

  delay(1000);
  Serial.println(F("tristate done, switching back to outputs"));

  // set pins back to outputs
  pinMode(SPI_CLK, OUTPUT);
  pinMode(SPI_MOSI, OUTPUT);
  pinMode(TFT_CS, OUTPUT);
  pinMode(TFT_DC, OUTPUT);
  pinMode(SD_CS, OUTPUT);

  tft.setCursor(10, i += 20);
  tft.print("back 2 outputs");
  Serial.println("back 2 outputs");

  //******************************************
  // if SD.begin code below is not included
  // the "read" while (myfile.available() goes
  // on forever.

  if (!SD.begin(SD_CS)) {

    tft.setCursor(150, j += 20);
    tft.print("SD init fail 2");

    return;
  }
  //*****************************************

  // write something to file
  myFile = SD.open("test.txt", FILE_WRITE);
  if (myFile) {

    tft.setCursor(10, i += 20);
    tft.print("Writing test.txt");
    Serial.println("Writing test.txt");
    
    myFile.println(F("Test after tristate"));
    myFile.close();

    tft.setCursor(10, i += 20);
    tft.println("done writing.");
    Serial.println("done writing test.txt after tristate");

  } else {

    tft.setCursor(150, j += 20);
    tft.print(F("err opng test.txt for write after tristate"));
    Serial.println(F("err opng test.txt for write after tristate"));
    
  }

  // read file
  myFile = SD.open("test.txt");
  if (myFile) {

    tft.setCursor(10, i += 20);
    tft.print("test.txt:");
    Serial.println("opening test.txt for read");

    while (myFile.available()) {  // <-- infinite loop here if 2nd SD.begin is not used
      Serial.write(myFile.read());
    }
    myFile.close();

  } else {

    tft.setCursor(150, j += 20);
    tft.print(F("err opng test.txt for read after tristate"));
    Serial.println(F("err opng test.txt for read after tristate"));
  }

  tft.setCursor(10, i += 20);
  tft.print("done entire tst");
  Serial.println("done with entire test");
}


void loop() {
}

Oh, and the “why do this”? I’d like the Nano to share the TFT and SD with another “master” (first attempt will be to share with another Nano, but eventually, the goal is to share with an ESP8266). This is a baby step toward that goal.

DaveEvans:
Using a Nano and Adafruit 3.5" TFT with microSD socket, the following test code opens, writes to, and closes a file.

Then the MOSI, CLK, and slave select pins are tri-stated (set to inputs). After a brief delay, they are re-set to outputs.

Then the file is opened again, written to, and read.

If the second SD.begin is not used (the one between the *******), the attempt to read the file goes into an infinite loop at the “while(myFile.available()) {read the file}” (and it reads garbage). If MOSI and slave select are tri-stated, but the CLK is not tri-stated (left as an output), the same thing happens (infinite read loop).

With the second SD.begin, the read works just fine.

The serial monitor and a TFT (also an SPI device) are used for the debugging statements (the TFT appears to have no problems after the SPI pins are tri-stated and then “un-tri-stated”).

Why is a second SD.begin needed? How does the SD card, code, or break-out “know” that it has been interrupted (especially if CLK is not messed with)? Is something related to the SD happening “in the background”?

Other details: the 5 volt Nano is powered via USB, and the Nano powers the TFT/SD. IDE v1.65-r5 on Windows 7 x64. The slave select pins have external pull-up resistors, so that they are definitely “off” while the Nano pins are tri-stated.

/* test effect of changing SPI pins to inputs

(to “tri-state” them) and then switch back to outputs
*/

#include <SPI.h>
#include “Adafruit_GFX.h”
#include “Adafruit_HX8357.h”

#include <SdFat.h>

const byte SPI_CLK = 13;
const byte SPI_MISO = 12; // stays input
const byte SPI_MOSI = 11;
const byte TFT_CS = 10; // pulled up by 10k resistor, for unambiguous “off” state while tri-stated
const byte TFT_DC = 9;
const byte SD_CS = 4;   // pulled up by 10k resistor…

byte i = 0;
byte j = 0;

Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC);

SdFat SD;
File myFile;

void setup() {
 Serial.begin(115200);
 Serial.println(“oneMasterTFTnSD”);

// start TFT
 tft.begin(HX8357D);
 tft.setRotation(3);
 tft.fillScreen(HX8357_BLACK);
 tft.setTextColor(HX8357_YELLOW);
 tft.setTextSize(2);

// start SD
 if (!SD.begin(SD_CS)) {

tft.setCursor(150, j += 20);
   tft.print(“SD init fail”);

return;
 }

// open file and write something
 myFile = SD.open(“test.txt”, FILE_WRITE);
 if (myFile) {

tft.setCursor(10, i += 20);
   tft.print(F(“Writing test.txt B4 tristate”));
   Serial.println(F(“Writing test.txt B4 tristate”));

myFile.println(F(“testing B4 tristate”));
   myFile.close();

tft.setCursor(10, i += 20);
   tft.println(“done.”);

} else {

tft.setCursor(150, j += 20);
   tft.print(F(“err opng test.txt B4 tristate”));
   Serial.println(F(“err opng test.txt B4 tristate”));

}

// tri-state the pins
 tft.setCursor(10, i += 20);
 tft.print(“swch 2 inputs”);
 Serial.println(“swch 2 inputs”);

pinMode(SPI_CLK, INPUT);
 pinMode(SPI_MOSI, INPUT);
 pinMode(TFT_CS, INPUT);
 pinMode(TFT_DC, INPUT);
 pinMode(SD_CS, INPUT);

delay(1000);
 Serial.println(F(“tristate done, switching back to outputs”));

// set pins back to outputs
 pinMode(SPI_CLK, OUTPUT);
 pinMode(SPI_MOSI, OUTPUT);
 pinMode(TFT_CS, OUTPUT);
 pinMode(TFT_DC, OUTPUT);
 pinMode(SD_CS, OUTPUT);

tft.setCursor(10, i += 20);
 tft.print(“back 2 outputs”);
 Serial.println(“back 2 outputs”);

//******************************************
 // if SD.begin code below is not included
 // the “read” while (myfile.available() goes
 // on forever.

if (!SD.begin(SD_CS)) {

tft.setCursor(150, j += 20);
   tft.print(“SD init fail 2”);

return;
 }
 //*****************************************

// write something to file
 myFile = SD.open(“test.txt”, FILE_WRITE);
 if (myFile) {

tft.setCursor(10, i += 20);
   tft.print(“Writing test.txt”);
   Serial.println(“Writing test.txt”);
   
   myFile.println(F(“Test after tristate”));
   myFile.close();

tft.setCursor(10, i += 20);
   tft.println(“done writing.”);
   Serial.println(“done writing test.txt after tristate”);

} else {

tft.setCursor(150, j += 20);
   tft.print(F(“err opng test.txt for write after tristate”));
   Serial.println(F(“err opng test.txt for write after tristate”));
   
 }

// read file
 myFile = SD.open(“test.txt”);
 if (myFile) {

tft.setCursor(10, i += 20);
   tft.print(“test.txt:”);
   Serial.println(“opening test.txt for read”);

while (myFile.available()) {  // ← infinite loop here if 2nd SD.begin is not used
     Serial.write(myFile.read());
   }
   myFile.close();

} else {

tft.setCursor(150, j += 20);
   tft.print(F(“err opng test.txt for read after tristate”));
   Serial.println(F(“err opng test.txt for read after tristate”));
 }

tft.setCursor(10, i += 20);
 tft.print(“done entire tst”);
 Serial.println(“done with entire test”);
}

void loop() {
}




Oh, and the "why do this"? I'd like the Nano to share the TFT and SD with another "master" (first attempt will be to share with another Nano, but eventually, the goal is to share with an ESP8266). This is a baby step toward that goal.

The simplest explanation is that the SD card is getting confused. Without driving the MOSI, SCK pins they can float up and down depending on the RFI environment. The SD.begin() goes through a reset sequence to init the SDcard.

I built a custom MEGA board with an embedded SD card slot. If I leave a SD card inserted, sometimes, uploading a new program fails (using a AVRISPmkII). I have verified that the SD !CS pin is always high, but sometimes Depending on the Exact flash data sequence of the new program, the SD card responds during the ISP attempt (While !CS for the SDCard is HIGH!). This causes the upload sequence to fail. You might be able to install weak pullups or pulldowns to inhibit this behavior.

Chuck.

Thanks for the reply.

but sometimes...the SD card responds during the ISP attempt (While !CS for the SDCard is HIGH!)

It seems very strange that the slave would respond even though it is not selected, but I guess that is happening in my situation, too.

I thought that slaves should completely ignore MOSI and CLK until they are selected. Just in case interrupting the CLK was the issue, I tried tri-stating only MOSI, but got the same problem.

I'll try pullups or downs on MOSI and CLK and see what happens.

I tried a modified version of your code on a Mega 2560 and had no problem. I don't have a TFT screen though, but that shouldn't make any difference. Your problem may be limited to the Nano.

The SD card does have one small problem that could affect a sketch upload. It will occasionally leave the MISO line driven, even with the SS HIGH. This is corrected with the SdFat library, but not the SD library. The solution to freeing it is to do a SPI.transfer(0); call after the SD access.

I have a fix for the SD library on the forum somewhere. If you want it, I'll look for it.

Thanks for testing (+1)! Interesting that you got it to work on the Mega. I'll try it with just an SD breakout board (no TFT). Shouldn't be related to the TFT, but ya never know.

Thx for the offer re SD lib and MISO left high, but I'm using SdFat, so shouldn't need it (ChuckTodd might, tho').

DaveEvans:
Thanks for the reply.

It seems very strange that the slave would respond even though it is not selected, but I guess that is happening in my situation, too.

I thought that slaves should completely ignore MOSI and CLK until they are selected. Just in case interrupting the CLK was the issue, I tried tri-stating only MOSI, but got the same problem.

I'll try pullups or downs on MOSI and CLK and see what happens.

The SPI interface of SDCards is a secondary interface. The primary interface, has the !CS pin as Data3. depending on the the exact sequence of bits on the MOSI (CMD) it can switch to its primary interface. and the Primary interface can be 1 bit, 4 bit or 8 bits wide. So, sometimes The SPI bus is miss interpreted by the SDCard. Here is a good explaination of the Primary SDCard interface from Seabright Technology

Chuck.

chucktodd - thanks for the link. I scrolled through it just now; looks like good stuff. I will have to take a closer look.

SurferTim - turns out the TFT has something to do with the problem. I tried the test with an Adafruit microSD breakout board only (no TFT) and it worked without the second SD.begin()!

Next, I will switch back to the TFT/SD and test chucktodd's idea of using pullups or downs on the MOSI and CLK lines.

The SD card will not listen to the SPI bus, except for a single clock pulse to release the MISO line. If you are using SdFat, the library does that for you every time it sets the SS HIGH.

I would check the TFT library to insure it is not hanging up the SPI bus.

Every TFT operation uses this...

    SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
    SPI.transfer(c);
    SPI.endTransaction();

...which should not hang up the bus, right?

There are no calls to SPI.end() in the TFT library.

I hope you are just abbreviating that operation. It should set the TFT slave select LOW, do the transfer, then set the TFT slave select HIGH.

    SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
    digitalWrite(TFT_SS, LOW);
    SPI.transfer(c);
    digitalWrite(TFT_SS, HIGH);
    SPI.endTransaction();

It sets the TFT slave select LOW, does those three lines, and then sets the TFT slave select HIGH. Full example:

void Adafruit_HX8357::drawPixel(int16_t x, int16_t y, uint16_t color) {

  if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return;

  setAddrWindow(x,y,x,y);

  *dcport |=  dcpinmask; // set dc high
  *csport &= ~cspinmask; // set chip select low

  spiwrite(color >> 8);
  spiwrite(color);

  *csport |= cspinmask; // set chip select high

}

and

void Adafruit_HX8357::spiwrite(uint8_t c) {

    SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
    SPI.transfer(c);
    SPI.endTransaction();
}

I see that the Arduino reference section says "With most SPI devices, after SPI.beginTransaction(), you will write the slave select pin LOW, call SPI.transfer() any number of times to transfer data, then write the SS pin HIGH, and finally call SPI.endTransaction(). "

But does it really matter that the SPI.beginTransaction and SPI.endTransaction are called while the TFT slave select is LOW?

PS: I’m not sure the above example from the TFT library is pertinent, since I’m not printing any graphics to the TFT in the test code. I’m only printing text to the TFT, and I have been unable to find any code in the TFT library for TFT.print() I looked in print.h and print.cpp but don’t see any SPI-like commands there.

The beginTransaction call has two functions. It sets the SPI settings (speed, mode, bit order), and disables interrupts for other SPI devices.

The SPI setting changes should be complete before pulling the SS LOW. It can cause problems with the device if the mode is changed. It may cause a clock level shift which the device may interpret as data arriving (SPI.transfer).

Also, the interrupts should be disabled before the SS is pulled LOW for obvious reasons. If one SPI device enters an interrupt and uses the SPI bus with the other SPI device SS LOW, well...

I'm not saying that is the problem, but it is a glitch in the TFT library code.

Verrry interesting. I'll post a note on Adafruit's forum.

I did another test - stripped out all the TFT code and used the SD socket on the TFT board. It worked without the second SD.begin()...just as it did using a stand-alone SD breakout board.

And in another test, I included just the TFT.begin, setRotation, etc. and left out all TFT.print()s. It failed without the second begin.

So it's not necessarily the TFT.print statements, but it is definitely related to use of the TFT, somehow.

There is a way to determine if there is a "transaction collision" between devices/libraries. This is from SPI.h, line 33. Maybe this "collision" is what you are experiencing? Just a thought.

// Uncomment this line to add detection of mismatched begin/end transactions.
// A mismatch occurs if other libraries fail to use SPI.endTransaction() for
// each SPI.beginTransaction().  Connect an LED to this pin.  The LED will turn
// on if any mismatch is ever detected.
//#define SPI_TRANSACTION_MISMATCH_LED 5

note: Use a current limiting resistor with the LED.

Sorry - was out of town for a couple of days.

Thanks for the idea, but nope - no transaction collisions indicated. Sigh.

I still haven't tried chucktodd's idea re pullups/downs on MOSI and CLK; will do that next.

While testing for collisions, I noticed that the second write (the one after tri-stating) takes much longer than the first write. The first one takes 7 ms; the 2nd takes 12 s.

I tried a test on an Uno with an Adafruit Data Logging shield and didn’t see your problem.

I used a slightly modified version of the SD.h ReadWrite example.

I added the following between the write and read sections of the example.

  pinMode(SCK, INPUT);
  pinMode(MOSI, INPUT);
  Serial.println("SCK, MOSI to INPUT mode"); 
  delay(2000);
  pinMode(SCK, OUTPUT);
  pinMode(MOSI, OUTPUT);
  Serial.println("SCK, MOSI to OUTPUT mode");

I change the read section to test for read errors:

      int c = myFile.read();
      if (c < 0) {
        Serial.println("myFile.read() failed");
        return;
      }
      Serial.write(c);

Here is the output:

Initializing SD card…initialization done.
Writing to test.txt…done.
SCK, MOSI to INPUT mode
SCK, MOSI to OUTPUT mode
test.txt:
testing 1, 2, 3.

If I leave SCK and MOSI in INPUT mode, the open for read is successful since the directory entry is cached from the call to close but the read fails.

Writing to test.txt…done.
SCK, MOSI to INPUT mode
test.txt:
myFile.read() failed

Here is the entire example for you to try.

/*
  SD card read/write

 This example shows how to read and write data to and from an SD card file
 The circuit:
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4

 created   Nov 2010
 by David A. Mellis
 modified 9 Apr 2012
 by Tom Igoe

 This example code is in the public domain.

 */

#include <SPI.h>
#include <SD.h>

#define CS_PIN 10

File myFile;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.print("Initializing SD card...");

  if (!SD.begin(CS_PIN)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  myFile = SD.open("test.txt", FILE_WRITE);

  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test.txt...");
    myFile.println("testing 1, 2, 3.");
    // close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt for write");
    return;
  }

 
  pinMode(SCK, INPUT);
  pinMode(MOSI, INPUT);
  Serial.println("SCK, MOSI to INPUT mode"); 
  delay(2000);
  pinMode(SCK, OUTPUT);
  pinMode(MOSI, OUTPUT);
  Serial.println("SCK, MOSI to OUTPUT mode");
     
  // re-open the file for reading:
  myFile = SD.open("test.txt");
  if (myFile) {
    Serial.println("test.txt:");
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      int c = myFile.read();
      if (c < 0) {
        Serial.println("myFile.read() failed");
        return;
      }
      Serial.write(c);
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt for read");
  }
}

void loop() {
  // nothing happens after setup
}

fat16lib-thank you for the testing and the modified code. I was able to run your code (it was similar to my test with SD only–no TFT code – post #6).

However, when I add in the TFT (btwn the **** in following code), the read fails, unless the TFT’s D/C (data/command) pin is not tri-stated.

But if I test it with the TFT only (no SD code), tri-stating the D/C pin (along with CLK, MOSI, and SS) gives no problems.

So there is some interaction between the two devices, for as-yet unknown reasons.

#include <SPI.h>

//********************************************************
//#include <SD.h>
#include <SdFat.h>
#include "Adafruit_GFX.h"
#include "Adafruit_HX8357.h"
const byte TFT_DC = 9;
const byte TFT_CS = 10; // pulled up by 10k resistor, for unambiguous state while tri-stated
Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC);
SdFat SD;
//********************************************************

#define CS_PIN 4  // pulled up by 10k resistor, for unambiguous state while tri-stated

File myFile;

void setup() {

  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  Serial.print("Initializing SD card...");

  if (!SD.begin(CS_PIN)) {
    Serial.println("failed!");
    return;
  }
  Serial.println("done.");

  //********************************************************
  Serial.print("init tft...");
  tft.begin(HX8357D);
  tft.setRotation(3);
  tft.fillScreen(HX8357_BLACK);
  tft.setTextColor(HX8357_YELLOW);
  tft.setTextSize(2);
  tft.setCursor(20, 20);
  tft.print("Hello World....");
  Serial.println("done");
  //********************************************************

  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  myFile = SD.open("test22.txt", FILE_WRITE);

  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test22.txt...");
    myFile.println("testing 1, 2, 3.");
    // close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test22.txt for write");
    return;
  }

  //********************************************************
  //pinMode(TFT_DC, INPUT);
  pinMode(TFT_CS, INPUT);
  pinMode(CS_PIN, INPUT);
  //********************************************************

  pinMode(SCK, INPUT);
  pinMode(MOSI, INPUT);
  Serial.println("pins to INPUT mode");
  delay(2000);

  //********************************************************
  //pinMode(TFT_DC, OUTPUT);
  pinMode(TFT_CS, OUTPUT);
  pinMode(CS_PIN, OUTPUT);
  //********************************************************

  pinMode(SCK, OUTPUT);
  pinMode(MOSI, OUTPUT);
  Serial.println("pins  to OUTPUT mode");

  //********************************************************
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  myFile = SD.open("test22.txt", FILE_WRITE);

  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test22.txt...");
    myFile.println("testing 4, 5, 6.");
    // close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test22.txt for write");
    return;
  }
  //********************************************************

  // re-open the file for reading:
  myFile = SD.open("test22.txt");
  if (myFile) {
    Serial.println("test22.txt:");
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      int c = myFile.read();
      if (c < 0) {
        Serial.println("myFile.read() failed");
        return;
      }
      Serial.write(c);
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test22.txt for read");
  }
  Serial.println("done");
  tft.setCursor(20, 40);
  tft.print("done");

}

void loop() {
  // nothing happens after setup
}

Adafruit forum post re. the sequence of chip select and begin/end SPI transactions in the TFT library is here.