Due to high demand expect some shipping delays at this time, orders may not ship for 1-2 business days.
0

tft.println not working with SD card
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

tft.println not working with SD card

by dougl on Thu Mar 07, 2013 9:24 am

I have the following all purchased from adafruit all plugged in together as designed.

Arduino Uno R3
2.8" TFT Touch Shield for Arduino
Proto-Screwshield

tftbmp_shield is woring with reading SD card and putting bmp on screen.
tftpaint_shield is working

Problem is that tft.println is not woring once the SD is opened, see code below:
it prints on the TFT screen the first tft.println("screen works 1") but once the SD card is initialized the second tft.println("screen works 2"); does not show up on the TFT screen. The bmp draw of the files does display.

So it seems that once the SD card is opened tft.print no longer works.

Why is this so? How do I get around this?

Code: Select all | TOGGLE FULL SIZE
  tft.begin(identifier);
  tft.println("screen works 1");

  progmemPrint(PSTR("Initializing SD card..."));
  if (!SD.begin(SD_CS)) {
    progmemPrintln(PSTR("failed!"));
    return;
  }
   tft.println("screen works 2");
   delay(4000);

  progmemPrintln(PSTR("OK!"));
  spi_save = SPCR;

  bmpDraw("tiger.bmp", 0, 0);
  delay(1000);
DougL
Teralign

dougl
 
Posts: 8
Joined: Fri Feb 22, 2013 1:57 pm

Re: tft.println not working with SD card

by adafruit_support_rick on Thu Mar 07, 2013 11:23 am

Hmmm… not sure why that wouldn't work. Where does "screen works 1" appear on the screen? At the bottom? You haven't called tft.setAddrWindow to locate the cursor, so the second println may just be falling off the screen.

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: tft.println not working with SD card

by dougl on Thu Mar 07, 2013 11:30 am

driverblock wrote:Hmmm… not sure why that wouldn't work. Where does "screen works 1" appear on the screen? At the bottom? You haven't called tft.setAddrWindow to locate the cursor, so the second println may just be falling off the screen.


Thanks for input.

When I comment out the lines that open the SD card the "screen works 2" appears on the screen below the "screen works 1" , it is only when opening the SD card does the the tft.print stop appearing on the screen.
DougL
Teralign

dougl
 
Posts: 8
Joined: Fri Feb 22, 2013 1:57 pm

Re: tft.println not working with SD card

by adafruit_support_rick on Thu Mar 07, 2013 1:28 pm

Verrrry Eeenteresting.
I've replicated the problem here. What's happening is that there is a little dance going on behind the scenes between the SD card and the TFT. Both are using SPI to communicate with the arduino - that is, they are sharing the same data and control pins. What differentiates them is the SPI chip-select line. the SD card chip select is digital pin 5, and the TFT chip select is pin 4.

You put your tft.println("works screen 2") in the one place where the dancers manage to get out of step - in between SD.begin(SD_CS) and the first bmpDraw("woof.bmp",0,0).

If you look at the original tftbmp_shield example, you'll see that the line "spi_save = SPCR" occurs right after SD.begin successfully completes. What that does is to save the current contents of the hardware SPI control register. What's happening is that SD.begin leaves the hardware SPI controller configured for communicating with the SD card, and that's why the tft.println("works screen 2") doesn't work.

If you look at bmpDraw, you'll see that before it does any SD card reads, it loads the SPCR control register with the contents of spi_save, so that SPI is correctly configured for talking to the SD card. Before it calls any of the tft functions, however, it resets SPCR to 0, which is the correct configuration for the tft.

To make your code work, you must put the tft.println after the spi_save = SPCR; and you must set SPCR to 0 before the call to tft.println:
Code: Select all | TOGGLE FULL SIZE
  tft.begin(identifier);
  tft.println("screen works 1");

  progmemPrint(PSTR("Initializing SD card..."));
  if (!SD.begin(SD_CS)) {
    progmemPrintln(PSTR("failed!"));
    return;
  }
  progmemPrintln(PSTR("OK!"));
  spi_save = SPCR;

  SPCR = 0;
  tft.println("screen works 2");
  delay(4000);

  bmpDraw("tiger.bmp", 0, 0);
  delay(1000);

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: tft.println not working with SD card

by dougl on Thu Mar 07, 2013 2:36 pm

Great that did it.

So if I use SD and tft printing do I have to use

spi_save = SPCR;
SPCR = 0;

between each of the operations?

I am playing with it now so later I am sure I will have more questions.

This has been a lot of help.
DougL
Teralign

dougl
 
Posts: 8
Joined: Fri Feb 22, 2013 1:57 pm

Re: tft.println not working with SD card

by adafruit_support_rick on Thu Mar 07, 2013 3:05 pm

You don't need to do spi_save = SPCR; anymore. Once in setup() is enough.

bmpDraw always sets up SPCR, so you never have to do anything special before calling it.

If I were doing this, I think I would modify bmpDraw to preserve SPCR on entry, and restore it on exit.

Try replacing bmpDraw with this version I modified. It should allow you to call tft functions wherever, without worrying about SPCR.
Code: Select all | TOGGLE FULL SIZE
void bmpDraw(char *filename, int x, int y) {
  File     bmpFile;
  int      bmpWidth, bmpHeight;   // W+H in pixels
  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
  uint32_t bmpImageoffset;        // Start of image data in file
  uint32_t rowSize;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer[3*BUFFPIXEL]; // pixel in buffer (R+G+B per pixel)
  uint16_t lcdbuffer[BUFFPIXEL];  // pixel out buffer (16-bit per pixel)
  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
  boolean  goodBmp = false;       // Set to true on valid header parse
  boolean  flip    = true;        // BMP is stored bottom-to-top
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();
  uint8_t  lcdidx = 0;
  boolean  first = true;
 
  uint8_t save_spcr = SPCR;    //preserve a copy of SPCR on entry

  if((x >= tft.width()) || (y >= tft.height()))
  {
    SPCR = save_spcr;  //restore SPCR
    return;
  }

  Serial.println();
  Serial.print("Loading image '");
  Serial.print(filename);
  Serial.println('\'');
  // Open requested file on SD card
  SPCR = spi_save;
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.print("File not found");
    SPCR = save_spcr;  //restore SPCR
    return;
  }

  // Parse BMP header
  if(read16(bmpFile) == 0x4D42) { // BMP signature
    progmemPrint(PSTR("File size: ")); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    progmemPrint(PSTR("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
    // Read DIB header
    progmemPrint(PSTR("Header size: ")); Serial.println(read32(bmpFile));
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) { // # planes -- must be '1'
      bmpDepth = read16(bmpFile); // bits per pixel
      progmemPrint(PSTR("Bit Depth: ")); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

        goodBmp = true; // Supported BMP format -- proceed!
        progmemPrint(PSTR("Image size: "));
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);

        // BMP rows are padded (if needed) to 4-byte boundary
        rowSize = (bmpWidth * 3 + 3) & ~3;

        // If bmpHeight is negative, image is in top-down order.
        // This is not canon but has been observed in the wild.
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        // Crop area to be loaded
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;

        // Set TFT address window to clipped image bounds
        SPCR = 0;
        tft.setAddrWindow(x, y, x+w-1, y+h-1);

        for (row=0; row<h; row++) { // For each scanline...
          // Seek to start of scan line.  It might seem labor-
          // intensive to be doing this on every line, but this
          // method covers a lot of gritty details like cropping
          // and scanline padding.  Also, the seek only takes
          // place if the file position actually needs to change
          // (avoids a lot of cluster math in SD library).
          if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     // Bitmap is stored top-to-bottom
            pos = bmpImageoffset + row * rowSize;
          SPCR = spi_save;
          if(bmpFile.position() != pos) { // Need seek?
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
          }

          for (col=0; col<w; col++) { // For each column...
            // Time to read more pixel data?
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              // Push LCD buffer to the display first
              if(lcdidx > 0) {
                SPCR   = 0;
                tft.pushColors(lcdbuffer, lcdidx, first);
                lcdidx = 0;
                first  = false;
              }
              SPCR = spi_save;
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
            }

            // Convert pixel from BMP to TFT format
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            lcdbuffer[lcdidx++] = tft.color565(r,g,b);
          } // end pixel
        } // end scanline
        // Write any remaining data to LCD
        if(lcdidx > 0) {
          SPCR = 0;
          tft.pushColors(lcdbuffer, lcdidx, first);
        }
        progmemPrint(PSTR("Loaded in "));
        Serial.print(millis() - startTime);
        Serial.println(" ms");
      } // end goodBmp
    }
  }

  bmpFile.close();
  if(!goodBmp) Serial.println("BMP format not recognized.");

    SPCR = save_spcr;  //restore SPCR
}

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: tft.println not working with SD card

by dougl on Thu Mar 07, 2013 7:02 pm

All this has been very helpful and is now working.

I will not be using the code the reads the bmp files, just had it there to prove the SD was working.

I am using the SD to log data and the TFT to show data on the screen. I found to use the SD card I had to use the following code:

  SPCR =   spi_save;  //return SPI to same conditions for SD
  dataFile.print("test ");
  dataFile.println(x);
  dataFile.flush(); //makes sure data saved, slows it up but safer
  spi_save = SPCR;  //save contents of the hardware SPI control register
  SPCR = 0;



I had to return the SPI where it was before then save it again after use. Now it is successfully writing the data and writing to the screen.

Thanks for pointing me in the right direction.
DougL
Teralign

dougl
 
Posts: 8
Joined: Fri Feb 22, 2013 1:57 pm

Re: tft.println not working with SD card

by technerdchris on Sun Aug 11, 2013 2:56 am

adafruit_support_rick » 07 Mar 2013 19:05
You don't need to do spi_save = SPCR; anymore. Once in setup() is enough. bmpDraw always sets up SPCR, so you never have to do anything special before calling it. If I were doing this, I think I would modify bmpDraw to preserve SPCR on entry, and restore it on exit. Try replacing bmpDraw with this version I modified. It should allow you to call tft functions wherever, without worrying about SPCR.


Oh wow, this needs to be more clear in the examples. :? I spent hours trying to get my 1.8" TFT / Joystick / SD Card shield to see files on the SD Card. The "examples" start with the "card info" PDE sketch:
Code: Select all | TOGGLE FULL SIZE
// we'll use the initialization code from the utility libraries
  // since we're just testing if the card is working!
  while (!card.init(SPI_HALF_SPEED, chipSelect)) {



But I didn't notice that everywhere else, this is how to initialize the SD card:
Code: Select all | TOGGLE FULL SIZE
  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");


Maybe please update that card info one? :mrgreen:

technerdchris
 
Posts: 46
Joined: Sun Nov 25, 2012 4:08 am

Re: tft.println not working with SD card

by adafruit_support_rick on Sun Aug 11, 2013 11:53 am

unexpectedly wrote:Maybe please update that card info one?

Yeah - not sure why that one is different. I'll look into it...

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: tft.println not working with SD card

by technerdchris on Mon Aug 12, 2013 2:40 am

Thanks. :) Probably, most people catch that faster than I did. :P Sometimes, I'm among the sharpest tools in the shed, but when I'm not, whoooooooo it's pretty bad! :D

technerdchris
 
Posts: 46
Joined: Sun Nov 25, 2012 4:08 am

Re: tft.println not working with SD card

by adafruit_support_rick on Mon Aug 12, 2013 5:59 pm

cardinfo is different because it need to use the lower-level classes directly in order to get detailed information about the card. When you use SD, all those same classes are there, doing all the same things, but they are hidden underneath SD.

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: tft.println not working with SD card

by technerdchris on Wed Aug 21, 2013 2:22 am

adafruit_support_rick wrote:You don't need to do spi_save = SPCR; anymore. Once in setup() is enough.

bmpDraw always sets up SPCR, so you never have to do anything special before calling it.

If I were doing this, I think I would modify bmpDraw to preserve SPCR on entry, and restore it on exit.


I'm still getting hung up on this SPCR thing. Do we intentionally ever set it to 0? This is my setup and it is probably among the most complicated Arduino systems I have ever seen actually built on the internet. But it's sooooo close!

I can:
- Print ANY image that's in PROGMEM
- Print small-ish images that are stored on the SD Card in the format that your bitmapImageConvert.pde processing program creates. (I wrote a php script to take the bytes of the .h file and write them to a file)
- What I'm finding is that at some point in file size, everything loses its mind and the Arduino stops responding (but at least resets)

Because this is on a Mega:
Code: Select all | TOGGLE FULL SIZE
#define ADA_SCLK   52
#define ADA_MOSI   51
#define ADA_CS      53
#define ADA_DC       8
#define ADA_RST      -1
//   Initialise using Hardware SPI
Adafruit_ST7735 tft = Adafruit_ST7735(ADA_CS, ADA_DC, ADA_RST);
// analog pin 3 for button input
#define ADA_JOYSTICK 3
// SD card chip select digital pin 4
#define SD_CS 4 


Here's my setup:
Code: Select all | TOGGLE FULL SIZE
void setup() {
   //  TFT setup
   tft.initR(INITR_BLACKTAB);  // magic here; each TFT came with color tab on screen protector
   // The rotation parameter can be
   // 1, landscape (wide) mode, with the USB jack at the bottom right
   tft.setRotation(1);
   tft.setTextColor(ST7735_RED);
   tft.setTextSize(2); //  1 = 5x8, 2 = 10x16; chars leave blank pixel on bottom
   tft.fillScreen(BLACK);

   // Thermal Printer setup
   // from adafruit
   printer.begin();

   //  Real Time Clock
   Wire.begin();

   //  SD Card
   if (!SD.begin(SD_CS)) {
      printer.println("SD Card initialization failed!");
   } else {
      sdcard_spcr=SPCR;
        File myFile;
        myFile = SD.open("cw100x68.bin");
        if( myFile ){  // this totally works!
         printer.printBitmap(100, 68, dynamic_cast<Stream*>(&myFile) );
        } else {
         printer.println("Opening file cw100x68.bin failed!");
      }
      myFile.close();
      //  done with the SD Card, put SPCR back
//      SPCR = 0;
   }


If I un-comment the SPCR=0 after the SD Card prints out my company's logo from the bin file, the Arduino locks up...

And in my graph plotting function, I'm trying to read an "empty" 384x384 bitmap with a border and then write it byte by byte to a temp file that I would then print. (I was able to manipulate a byte array bitmap declared in SRAM but that only works up to 192x192 pixels).

Since reading and writing the file didn't work, I tried to just simply print the 384 sq box bitmap:
Code: Select all | TOGGLE FULL SIZE
   File printFile = SD.open("box384.bin");
   if( printFile )
      printer.printBitmap(384, 384, dynamic_cast<Stream*>(&printFile) );
   else
      printer.println("Error opening report.bin");
   printer.feed(6);
   printFile.close();
   SPCR = 0;


It never gets past the first couple lines of the box. If I pull on the paper as it prints, it shows that the printer is trying...

I feel like there's a simple "A HA!" I am missing. I know I could just settle and do it for a 192x192 bitmap and if I can't get it in a few days, I probably will. :P

Also, I've got a 12V -> 9V DC converter coming. Someone said on the Net that these printers work really well on 9V and abysmally on 5V. So I'll be trying that, too.

Thanks,
Chris

technerdchris
 
Posts: 46
Joined: Sun Nov 25, 2012 4:08 am

Re: tft.println not working with SD card

by adafruit_support_rick on Wed Aug 21, 2013 11:24 am

Wait- you're running on a Mega? The mega uses different pins for SPI. Have you jumpered the SPI lines over to digital 11, 12, 13, or are you using software SPI?

unexpectedly wrote:If I un-comment the SPCR=0 after the SD Card prints out my company's logo from the bin file, the Arduino locks up...

Yup, that will happen, if you are using hardware SPI and you have jumpered the Mega's SPI pins over to the shield. I'll assume that's what you have done...

You need to understand what SPCR does. It's the actual SPI Control Register in the hardware. SD.begin configures SPCR according to its own purposes, and expects that it will stay configured that way.

The 2.8" TFT Shield doesn't use SPI, but it uses some of the same pins. So, when you want to control the TFT, you have to turn off SPI. That's when you write 0 to the SPI Control Register.

Now, if you just leave SPCR that way, the SD will no longer work. So you have to restore SPCR to what it was; i.e., turn SPI back on.

So, the logic is this:
- after SD.begin, you save the current value of SPCR. Only need to do this the one time.
- before doing any TFT operations, you write 0 to SPCR to turn off SPI
- after doing TFT operations, you must restore SPCR to the saved value.

You can make this easy on yourself by wrapping all of your SD and TFT code into functions which only access one device or the other.

For example, you have a function that does some TFT operations. You simply set SPCR to 0 on entry, and restore SPCR to saved_SPCR on exit.

Instead of directly doing a read from SD, you have the SD read wrapped in another function which does the same kind of thing - it sets SPCR to saved_SPCR on entry, and restores it to 0 on exit.

As long as all of your TFT and SD operations are wrapped in functions like this, you can call them from anywhere in any sort of order, and you'll never have to worry about SPCR.

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: tft.println not working with SD card

by adafruit_support_rick on Wed Aug 21, 2013 11:26 am

Apart from the SPCR issue, it sounds like you may be running into SRAM limitations, even on a Mega. Have a look at our Memories tutorial:
http://learn.adafruit.com/memories-of-an-arduino

adafruit_support_rick
 
Posts: 35095
Joined: Tue Mar 15, 2011 11:42 am
Location: Buffalo, NY

Re: tft.println not working with SD card

by technerdchris on Thu Aug 22, 2013 9:06 pm

Update: made two changes and I can at least print the 384box.bin file from the SD Card successfully.

Mod 1: I bought this $7.50 12V -> 9V DC converter from Amazon and am using its 9V to feed the printer. I can report it does print faster. I can probably tweak other delays out of the system??

Mod 2: I removed the SPCR=0; from the printGraph function where my setup previously lost its mind.

Observation: My actual problem was my misuse of SPCR. Coincidentally, 5V really isn't enough to feed that printer. What sucks is I built my design around that nifty 5V/12V power supply. :P Ah well.

Current status: I can print the sample empty box in its entirety from my function. The print stalls in the middle and pauses for quite some time. The thermal.h code has a lot of delay added in which probably can be removed now that I'm using proper 9V power. So now I can go back to trying to read in the sample empty box, altering the few bits I need, then writing it to the SD Card to then be read and fed to the printer.

I just discovered ChipKit and its uC32. With 32K of flash, I can handily process a 19K array all within SRAM and be rid of micky-mousing around with the SD Card. It shows up tomorrow... we'll see if all this stuff is really "easy" when I transfer this reality over to there!

technerdchris
 
Posts: 46
Joined: Sun Nov 25, 2012 4:08 am

Please be positive and constructive with your questions and comments.