Nokia 5110 works until I add SD library (also SPI)

Sorry for cross posting in multiple forums, but I wasn't getting any replies in the networking forum.

So I have my Nokia 5110 LCD working and the SD card shield working; I just can't seem to get the LCD working when I initialize the SD library. I've already hacked the PCD8544 to allow me to define all the SPI pins to use to be the same SPI pins as the SD library (11-13 on my Uno), but something about the SD library is breaking the SPI bus for the 5110. Note: It doesn't matter if the SD shield is connected or not. The SD card seems to work just fine (I can write files).

If you're curious, my hacked version of the PCD8544 library is here: Index of /arduino/PCD8544/

Ideas?

#include <PCD8544.h>
#include <SD.h>

#define SPI_CLK         13 /* SPI CLK */
#define SPI_MISO        12 /* SPI MISO */
#define SPI_MOSI        11 /* SPI MOSI */
#define SD_SELECT       10 /* SD Card SPI Select */
#define LCD_SELECT      9  /* LCD SPI Select */
#define LCD_DC          8  /* LCD data/command */
#define LCD_RESET       7  /* LCD Reset */


PCD8544 LCD;

void setup() {
    pinMode(LCD_SELECT, OUTPUT);
    pinMode(SD_SELECT, OUTPUT);

    Serial.begin(9600);

    Serial.println("Starting LCD");

    /* initialize LCD */
    LCD.begin_custpins(84, 48, SPI_CLK, SPI_MOSI, LCD_DC,
            LCD_RESET, LCD_SELECT);

    // don't talk to LCD while we init the SD
    digitalWrite(LCD_SELECT, HIGH);

    /* initialize SD Card on SPI Bus */
    if (!SD.begin(SD_SELECT)) {
        Serial.println("SD card initialize failed, or not present");
    } else {
        Serial.println("SD card initialize success!");
    }

    LCD.setCursor(0, 0);
    LCD.print("Started LCD!");
}

void loop() {
    delay(1000);
    LCD.println("LCD is running!");  // this does nothing when the SD library is initalized
}

Do you ever the the LCD_SELECT pin back to LOW before writing to the LCD? Try this:

#include <PCD8544.h>
#include <SD.h>

#define SPI_CLK         13 /* SPI CLK */
#define SPI_MISO        12 /* SPI MISO */
#define SPI_MOSI        11 /* SPI MOSI */
#define SD_SELECT       10 /* SD Card SPI Select */
#define LCD_SELECT      9  /* LCD SPI Select */
#define LCD_DC          8  /* LCD data/command */
#define LCD_RESET       7  /* LCD Reset */


PCD8544 LCD;

void setup() {
    pinMode(LCD_SELECT, OUTPUT);
    pinMode(SD_SELECT, OUTPUT);

    Serial.begin(9600);

    Serial.println("Starting LCD");

    /* initialize LCD */
    LCD.begin_custpins(84, 48, SPI_CLK, SPI_MOSI, LCD_DC,
            LCD_RESET, LCD_SELECT);

    // don't talk to LCD while we init the SD
    digitalWrite(LCD_SELECT, HIGH);
    digitalWrite(SD_SELECT, LOW);

    /* initialize SD Card on SPI Bus */
    if (!SD.begin(SD_SELECT)) {
        Serial.println("SD card initialize failed, or not present");
    } else {
        Serial.println("SD card initialize success!");
    }
    
    digitalWrite(LCD_SELECT, LOW);
    digitalWrite(SD_SELECT, HIGH);

    LCD.setCursor(0, 0);
    LCD.print("Started LCD!");
    digitalWrite(LCD_SELECT, HIGH);
    digitalWrite(SD_SELECT, LOW);
}

void loop() {
    digitalWrite(LCD_SELECT, LOW);
    digitalWrite(SD_SELECT, HIGH);
    delay(1000);
    LCD.println("LCD is running!");  // this does nothing when the SD library is initalized
    digitalWrite(LCD_SELECT, HIGH);
    digitalWrite(SD_SELECT, LOW);
}

The only thing I've done is what I've added digitalWrites at some places to be sure what the SD_SELECT and the LCD_SELECT always are talked to if they should, but not if the other one is talked to.

JanD

what type of atmega are you using? if at8 or 168 Your problem is lack of RAM (sd card uses 512b and lcd also uses 512b)

JanD: that was good advice, but doesn't seem to help any. :frowning:

zachwiej: It's an Arduino Uno board. I believe they have 2K of RAM?

At least I think the problem is in that.

JanD

yes. Do You use the same SS pin (device select) ?

For SS, I use pin 10 for the SD and pin 9 for the LCD.

Well I tracked this problem down to the SD library. Specifically Sd2Card::init() in Sd2Card.cpp. I tried finding a specific line or lines, but the answer seems to be "most of them"- even those which appear at first glance to be talking to the SD card (the SD select pin is set low).

Anyone have any experience getting the SD library working with any other SPI device on the bus? Trying to figure out if this issue is specific to this LCD or a common problem with the SD library.

Ok, i'm pretty sure I understand what is going on now...

The PCD8544 library uses software SPI via shiftOut() while the SD library uses hardware SPI via spiSend() and spiRec(). I'm still digging through the Atmega spec sheet, but I'd have a hard time believing you can run both hardware and software SPI over the same pins at the same time.

Anyone know why all the PCD8544 libraries use software SPI? Is there a problem with hardware SPI and this device? I figure I could just port the code to use the SPI library... shouldn't be that hard. As it is, I'm pretty low on pins for my project- I may have to rethink it if I can't share the SPI clock/miso/mosi pins.

Happy to say that was it. Fixing the PCD8544 library (the one from google code) was pretty easy.

  1. First change the #define's for the SPI pins in PCD8544.h to match the hardware SPI pins. Make sure your SPI select pin is different from the SD card library (default 10).

  2. Rewrite the PCD8544::send() method to use the hardware SPI method of sending data:

void PCD8544::send(unsigned char type, unsigned char data)
{
    digitalWrite(this->dc, type);
  
    digitalWrite(this->sce, LOW);
    SPDR = data;
    while (!(SPSR & (1 << SPIF)));
    digitalWrite(this->sce, HIGH);
}
  1. Initialize the SD library before PCD8544 so it can configure the HW SPI bus. It doesn't matter if the SD library initializes properly (ie: you don't need a SD card inserted).

Oops, shoulda done a full test I guesss.

Turns out there's a 4th step:

  1. You may also need to edit SD/SD.cpp to lower the SPI speed since the LCD max speed is 4.0Mhz and the SD default speed is 1/2 clock rate, so for the Arduino Uno which has a default clock rate of 16Mhz you need to drop the SPI speed to 1/4 clock:
boolean SDClass::begin(uint8_t csPin) {
  /*

    Performs the initialisation required by the sdfatlib library.

    Return true if initialization succeeds, false otherwise.

   */
  return card.init(SPI_QUARTER_SPEED, csPin) && 
      volume.init(card) && 
      root.openRoot(volume);
}

I will say I'm kinda disappointed in many of the Arduino libraries... I know they're designed to be easy to use, but there are so many hard coded values (which pins to use, speeds, etc) it really makes it hard to consider them as "reusable" across multiple projects. Not to mention for SPI related libraries, it would be nice to see a consistent use of hw vs. sw SPI to reduce the number of pins required without significant debugging & hacking. Not to say I don't appreciate the effort the library authors have put in (after reading the data sheet for the LCD I'm amazed anyone can figure out how to program it), but I guess it would be nice if the Arduino community came up with a set of standards for how libraries should be configured that authors could follow.

Agree on the part of "Arduino Community Standard",
this was the same issue I was going through with my LCD (5110) & SD card module, until I came across this post. Thank for all your contributions... synfinatic,JanD,zachwiej.

I submit an "Arduino Community Standard" is just an invitation for those who don't know what they are doing to stir the pot and confuse everybody else. A 5110 with the standard PCD8544 library and an SD with its standard library coexist just fine, and I think the original problem is most likely clear enough - the PCD8544 library was hacked. It so happens I hacked my library too, and indeed to put the display on Uno SPI pins. It works OK, but I didn't know what I was doing either, and it is totally unwarranted.

Below is my code and am again facing the issue of blank LCD 5110 when SPI with SD card.
Any suggestions and help is appreciated....

#include "DHT.h"
#include "RTClib.h"
#include <SPI.h>
#include <SD.h>
#include <PCD8544.h>


#define SPI_CLK         13 /* SPI CLK */
#define SPI_MISO        12 /* SPI MISO */
#define SPI_MOSI        11 /* SPI MOSI */
#define SD_SELECT       4 /* SD Card SPI Select */
#define LCD_SELECT      10  /* LCD SPI Select */
#define LCD_DC          9  /* LCD data/command */
#define LCD_RESET       8  /* LCD Reset */

PCD8544 LCD;


DHT dht1(2, DHT22);
DHT dht2(3, DHT22);
RTC_DS3231 rtc;
File myFile;

void setup() 
{
    pinMode(LCD_SELECT, OUTPUT);
    pinMode(SD_SELECT, OUTPUT);
    Serial.begin(9600); 

    //LCD.begin_custpins(84, 48 SPI_CLK, SPI_MOSI, LCD_DC,LCD_RESET, LCD_SELECT);
    
    digitalWrite(LCD_SELECT, HIGH);
    digitalWrite(SD_SELECT, LOW);
    
    SPI.begin();
    LCD.setContrast(57);
    LCD.clear();
    LCD.setCursor(0, 5);
   
    dht1.begin();
    dht2.begin();
    if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
    }
  if (rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
  }  
    Serial.print("Initializing SD card...");
    LCD.setCursor(0, 5);
    LCD.print("SD card....");
    LCD.clear();
    delay(1000);
      
    if (!SD.begin(SD_SELECT)) {
    Serial.println("Card failed, or not present");
    } else {
    
    Serial.println("SD card initialized Success");
    
      }

      digitalWrite(LCD_SELECT, LOW);
      digitalWrite(SD_SELECT, HIGH);
      Serial.println("card initialized.");
      
      LCD.setCursor(0, 0);
      LCD.print("LCD Started");
      digitalWrite(LCD_SELECT, HIGH);
      digitalWrite(SD_SELECT, LOW);

}

void loop() 
{
    // Reading temperature or humidity takes about 250 milliseconds!
    //Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
    digitalWrite(LCD_SELECT, LOW);
    digitalWrite(SD_SELECT, HIGH);
    delay(1000);
    LCD.println("LCD is running!");
    digitalWrite(LCD_SELECT, HIGH);
    digitalWrite(SD_SELECT, LOW);
    
    float DHT22_1_h = dht1.readHumidity();
    float DHT22_1_t = dht1.readTemperature();
    float DHT22_2_h = dht2.readHumidity();
    float DHT22_2_t = dht2.readTemperature();
    DateTime now = rtc.now();

    File myFile = SD.open("datalog.txt", FILE_WRITE);

      if (myFile) {
        myFile.print(now.year(), DEC);
        myFile.print('/');
        myFile.print(now.month(), DEC);
        myFile.print('/');
        myFile.print(now.day(), DEC);
        myFile.print(" ");
        myFile.print(now.hour(), DEC);
        myFile.print(':');
        myFile.print(now.minute(), DEC);
        myFile.print(':');
        myFile.print(now.second(), DEC);
        myFile.print(" ");
        myFile.print(DHT22_1_h);
        myFile.print(" "); 
        myFile.print(DHT22_1_t);
        myFile.print(" "); 
        myFile.print(DHT22_2_h);
        myFile.print(" "); 
        myFile.print(DHT22_2_t);
        myFile.println(" ");        
        myFile.close();
  }
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  }
    
    // check if returns are valid, if they are NaN (not a number) then something went wrong!
    if (isnan(DHT22_1_t) || isnan(DHT22_1_h)) 
    {
        Serial.println("Failed to read from DHT1");
    } 
    if (isnan(DHT22_2_t) || isnan(DHT22_2_h)) 
    {
        Serial.println("Failed to read from DHT2");
    } 
    else 
    {
        Serial.print(now.year(), DEC);
        Serial.print('/');
        Serial.print(now.month(), DEC);
        Serial.print('/');
        Serial.print(now.day(), DEC);
        Serial.print(" ");
        Serial.print(now.hour(), DEC);
        Serial.print(':');
        Serial.print(now.minute(), DEC);
        Serial.print(':');
        Serial.print(now.second(), DEC);
        Serial.print(" ");
        Serial.print("H1: "); 
        Serial.print(DHT22_1_h);
        Serial.print(" %\t");
        Serial.print("T1: "); 
        Serial.print(DHT22_1_t);
        Serial.print(" *C\t");
        Serial.print("H2: "); 
        Serial.print(DHT22_2_h);
        Serial.print(" %\t");
        Serial.print("T2: "); 
        Serial.print(DHT22_2_t);
        Serial.println(" *C");        
              
      LCD.setCursor(0, 0);
      LCD.print("T1-");
      LCD.println(DHT22_1_t);
      LCD.print(" H1-");
      LCD.println(DHT22_1_h);
      LCD.print("T2-");
      LCD.println(DHT22_2_t);
      LCD.print(" H2-");
      LCD.println(DHT22_2_h);
      //LCD.clear();
      delay(2000); 
    }
    
}

Probably self-inflicted pin clash or pin call, or maybe just slack wiring.
God only knows where the code you are using came from, but you can be sure it is junk, maybe just ancient junk, but junk all the same.

Check the SD examples in an IDE newer than 2012.

There is no need to specify the pins on the SPI bus, you don't have any choice as to what they are anyway, and probably never did.

Similarly the pins for 5110 are specified in the PCD8544 library. Even if you have edited the library to change the pin call, there is still no need to call them in your programme. If your pin call is different from what is in the library, hacked or otherwise, you deserve all the grief you will probably get.

Your problem is probably different from OPs, but I hope they are really the same. I can't understand why we suddenly have problems with SD<>5110, people have been using them together for years without so much as a wimper of protest.

The "Hello World" example in the standard Philips PCD8544 library has all you need for the 5110, and anything by Mellis and Igoe has all you need for SD. The last thing you need is Synfanatic's hacked library.

If you really need to change pins for the 5110, and I bet you don't, you can do this with the Henning Karlsen library from RinkyDink, which allows you to nominate them in your programme.

Nick,
Thank for the reply and clarification, please do help me by sharing the link for these standard Libraries....

I'm sure you can find these with some diligent Googling. Henning Karlsen's library has graphic capability which may be of interest. I have never actually used it.