Nokia 5110/PCD8544 Hardware SPI with SD help

Hello All,

I'm currently trying to develop a program that combines the PCD8544, an SD module and RTC (DS1307). As for my current progress, I have been successful at running the RTC and PCD8544 together to display the current date and time. I also have had the SD read/write with the SD.h ReadWrite program.

Now that I have begun to combine all three, I started to run into a problem. I have read on a few forum posts not to run the LCD through software SPI along with the SD using hardware SPI. I am currently using the Adafruit_GFX.h along with Adafruit_PCD8544.h for the LCD (which seems to have support to at least select the hardware pins) and SD.h for the SD card. I tried to use the older PCD8544.h library, but didn't have much success (i'm willing to try again if someone can help me implement the correct code).

The pins I have been using for the LCD are listed in the code. SD pins are 10-13. 10 being CS and the hardware SPI pins for 11-13.

Basically, what is the easiest way to get this LCD working on hardware SPI along with the SD? Here is the code I have been trying to use. I don't mind to alter the library as long as someone is willing to help teach me how.

/* Logging Test 
Test DS1307 RTC, PCD8544 LCD, and SD card (w/75LVC245 level shifter)
by Jeremy Phillips
vitruvianaquatics.webs.com
*/
#include <SD.h>                //SD Card Library 
const int SDSelect = 10;     //Set SD Chip Select Pin


#include <RTClib.h>            //RTC Library
#include <Wire.h>              //SPI Interface Library
RTC_DS1307 RTC;

#include <Adafruit_GFX.h>      //PCD8544 Library (call first)
#include <Adafruit_PCD8544.h>  //PCD8544 Library
// pin(13) - Serial clock out (SCLK)
// pin(11) - Serial data out (DIN)
// pin 5 - Data/Command select (D/C)
// pin 4 - LCD chip select (CS)
// pin 3 - LCD reset (RST)
const int LCDSelect = 4;
Adafruit_PCD8544 display = Adafruit_PCD8544(13, 11, 5, LCDSelect, 3);

//Define Variables
long id = 1; 
int lastTime = -1;

void setup()   {
  Serial.begin(9600);
  pinMode(SDSelect, OUTPUT);
  // don't talk to LCD while we init the SD
  digitalWrite(LCDSelect, HIGH);
  digitalWrite(SDSelect, LOW);  
  // Initialize SdFat or print a detailed error message and halt
  // Use half speed like the native library.
  // change to SPI_FULL_SPEED for more performance.
  if (!SD.begin(SDSelect)) 
  {
    Serial.println("Card Failure");
    // don't talk to SD while we init the LCD
    digitalWrite(LCDSelect, LOW);
    digitalWrite(SDSelect, HIGH);
    display.setTextSize(1);
    display.setTextColor(BLACK);
    display.setCursor(0,0);
    display.println("Card Failure!");
  } else {
    Serial.println("Card Sucessfully Initialized!");
  }
  
  // don't talk to SD while we init the LCD
  digitalWrite(SDSelect, HIGH);  
  display.begin();
  // init done
  display.setContrast(60); //set LCD contrast
  display.display(); // show splashscreen
  delay(1000);
  display.clearDisplay();   // clears the screen and buffer
  digitalWrite(LCDSelect, HIGH);
  
  RTC.begin();
  //initialize RTC
  if (! RTC.isrunning()) {
    Serial.println("RTC is NOT running"); 
    display.setTextSize(1);
    display.setTextColor(BLACK);
    display.setCursor(0,0);
    display.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    RTC.adjust(DateTime(__DATE__, __TIME__));
  } else {
    Serial.println("RTC initialized");
  }  
  // don't talk to LCD while we init the SD
  digitalWrite(LCDSelect, HIGH);
  digitalWrite(SDSelect, LOW);
  //Begin Log File Header
  File logFile = SD.open("DATALOG.csv", FILE_WRITE);
  if (logFile)
  {
    logFile.println(", , , , , ,"); //Just a leading blank line, incase there was previous data
    String header = "ID, Year, Month, Day, Hour, Minute, Second";
    logFile.println(header);
    logFile.close();
    Serial.println(header);
  }
  else
  {
    Serial.println("Couldn't open log file");
    // don't talk to SD while we init the LCD
    digitalWrite(LCDSelect, LOW);
    digitalWrite(SDSelect, HIGH);
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(BLACK);
    display.setCursor(0,0);
    display.println("Couldn't open log file");
  }
}
void loop () 
{
  DateTime now = RTC.now();  //Get Date/Time
  
  int time = now.second();
  if (abs(time - lastTime) > 1)
  {
    // don't talk to SD while we init the LCD
    digitalWrite(LCDSelect, LOW);
    digitalWrite(SDSelect, HIGH);
    display.clearDisplay(); //clear Display
    display.setTextSize(1);
    display.setTextColor(BLACK);
    display.setCursor(0,0); //set where to write on LCD
    int Year = now.year();
    int Mon = now.year();
    int Day = now.day();
    int Hour = now.day();
    int Min = now.minute();
    int Sec = now.second();
    display.print(Year); 
    display.print("/");
    display.print(Mon);
    display.print("/");
    display.println(Day);
    display.print(Hour);
    display.print(":");
    display.print(Min);
    display.print(":");
    display.print(Sec);
    display.display();  //display 
    // don't talk to LCD while we init the SD
    digitalWrite(LCDSelect, HIGH);
    digitalWrite(SDSelect, LOW);
    //Create Data string for storing to SD card
    //We will use CSV Format  
    String dataString = String(id) + ", " + String(Year) + ", " + String(Mon) + ", " + String(Day) + ", " + String(Hour) + ", " + String(Min) + ", " + String(Sec); 
    
    //Open a file to write to
    //Only one file can be open at a time
    File logFile = SD.open("DATALOG.csv", FILE_WRITE);
    if (logFile)
    {
      logFile.println(dataString);
      logFile.close();
      Serial.println(dataString);
    }
    else
    {
      Serial.println("Couldn't open log file");
      // don't talk to SD while we init the LCD
      digitalWrite(LCDSelect, LOW);
      digitalWrite(SDSelect, HIGH);
      display.clearDisplay();
      display.setTextSize(1);
      display.setTextColor(BLACK);
      display.setCursor(0,0);
      display.println("Couldn't open log file");
    }
    
   //Increment ID number
   id++;
   lastTime = time;
   }
}

I am doing much the same thing as you. I can't see what your problem is but here is some code. It uses the Philips library. My LCD pins are much the same.

c5110 pin assignments

1 RST D6
2 CE D7
3 DC D5
4 DIN D11 MOSI 11
5 CLK D13 CLK 13
6 VCC 3v3
7 LED to GND GND
8 GND GND

Notable differences are

const int SDSelect = 10;     //Set SD Chip Select Pin
const int LCDSelect = 4;
.................
    // don't talk to SD while we init the LCD
    digitalWrite(LCDSelect, LOW);
    digitalWrite(SDSelect, HIGH);
..................
  // don't talk to LCD while we init the SD
  digitalWrite(LCDSelect, HIGH);
  digitalWrite(SDSelect, LOW);

None of which I do. Those digital writes may be the problem - just in the wrong order or something.

/*  NO COSM HERE
EtherTen COM 4, Uno R3 COM 10 ON DESKTOP

//  This Arduino sketch reads DS18B20 "1-Wire" digital
//  temperature sensors.
//  http://www.hacktronics.com/Tutorials/arduino-1-wire-tutorial.html
//  Serial print commands are for PLX-DAQ
 size 22 894
 */
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>                  

#include <PCD8544.h>             // Nokia 5110
#include <SD.h>                  // SD card
#include <string.h>              // from Date As Filename 
#include "RTClib.h"              // from Date As Filename
#include "Wire.h"                // MUST HAVE SPI lib for LCD disp, SD card

#define DS1307_ADDRESS 0x68

RTC_DS1307 RTC;
static PCD8544 lcd;

File myFile;
char filename[] = "00000000.CSV";

// Custom symbols
static const byte DEGREES_CHAR = 1;
static const byte degrees_glyph[] = { 0x00, 0x07, 0x05, 0x07, 0x00 };
static const byte SLASH_CHAR = 2;
static const byte slash_glyph[] = {0x00,0x20,0x10,0x08};

byte InThermo[8] =  {
  0x28, 0x69, 0xC2, 0xB0, 0x03, 0x00, 0x00, 0X9F};
byte OutThermo[8] = {
  0x28, 0x7A, 0x8B, 0xC0, 0x03, 0x00, 0x00, 0x2F};
byte DrainThermo[8] = {
  0x28, 0x09, 0xA9, 0xC0, 0x03, 0x00, 0x00, 0x95}; 

#define ONE_WIRE_BUS 3
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

int  second, minute, hour, weekDay, monthDay, month, year;

int k=0;
float InTemp, OutTemp, DrainTemp;    

// Define the strings for our datastream IDs
char sensorId0[] = "InThermo";
char sensorId1[] = "OutThermo";
char sensorId2[] = "DrainThermo";
char calcId1[] = "diff";

void setup() {
   lcd.begin(84, 48);
     // Register the custom symbols...
  lcd.createChar(DEGREES_CHAR, degrees_glyph);
  lcd.createChar(SLASH_CHAR, slash_glyph);
  Wire.begin();
  Serial.begin(9600);
  Serial.print("    filename   ");
  delay(300);//Wait for newly restarted system to stabilize
  lcd.setCursor (0,0);
  lcd.print("Initializing");
  delay(2000);
  lcd.setCursor (0,1);

  pinMode(10, OUTPUT);

  if (!SD.begin(4)) 
  {
    lcd.print("failed!");
    delay (2000);
    return;
  }
  lcd.print("init. OK!");
  delay(2000);
      getFileName();
      Serial.println(filename);
  lcd.clear();

  Serial.println("LABEL,Time,InTemp,OutTemp,diff,DrainTemp");

  sensors.setResolution(InThermo, 12);
  sensors.setResolution(OutThermo, 12);
  sensors.setResolution(DrainThermo, 12);
}

void loop() {
  running();
  GetClock();
  if (hour == 0 && minute == 0 && second <2)
  {
    getFileName();
  }
 Serial.print("DATA,TIME,       "); 
  int ret=0;
  //get the values from the DS8B20's 
  sensors.requestTemperatures();

  float InTemp = (sensorValue(InThermo));
  float OutTemp = (sensorValue(OutThermo));  
  float DrainTemp = (sensorValue(DrainThermo)); 

  float diff = OutTemp - InTemp;

  Serial.print(InTemp);
  Serial.print(" ,  ");
  Serial.print(OutTemp);
  Serial.print(" ,  ");
  Serial.print(DrainTemp);
  Serial.println(" ,  ");

  lcd.setCursor(49,0);
  lcd.print(InTemp);
  lcd.setCursor(49,1);
  lcd.print (OutTemp);
  lcd.setCursor(49,2);
  lcd.print(DrainTemp);

  k=k+1;  

  if (k>9 )
  {  
  myFile = SD.open(filename, FILE_WRITE);//<<<<<<<<<<<<< OPEN
  myFile.print(hour);
  myFile.print(":");
  myFile.print(minute);
  myFile.print(":");
  myFile.print(second);
  myFile.print(",");

  myFile.print(InTemp);
  myFile.print(",");
  myFile.print(OutTemp);
  myFile.print(",");
  myFile.print(DrainTemp);
  myFile.print(",");
  myFile.println();
       myFile.close();//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>CLOSE
       
      k=0;
  }
  delay(850);
}  // loop ends here

//sensorValue function
float sensorValue (byte deviceAddress[])
{
  float tempC = sensors.getTempC (deviceAddress);
  return tempC;
}

byte bcdToDec(byte val)  {
  // Convert binary coded decimal to normal decimal numbers
  return ( (val/16*10) + (val%16) );
}

void GetClock(){
  // Reset the register pointer
  Wire.beginTransmission(DS1307_ADDRESS);
  byte zero = 0x00;
  Wire.write(zero);
  Wire.endTransmission();
  Wire.requestFrom(DS1307_ADDRESS, 7);

  second = bcdToDec(Wire.read());
  minute = bcdToDec(Wire.read());
  hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
  weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
  monthDay = bcdToDec(Wire.read());
  month = bcdToDec(Wire.read());
  year = bcdToDec(Wire.read());
}

void getFileName(){

  DateTime now = RTC.now();

  filename[0] = (now.year()/1000)%10 + '0'; //To get 1st digit from year()
  filename[1] = (now.year()/100)%10 + '0'; //To get 2nd digit from year()
  filename[2] = (now.year()/10)%10 + '0'; //To get 3rd digit from year()
  filename[3] = now.year()%10 + '0'; //To get 4th digit from year()
  filename[4] = now.month()/10 + '0'; //To get 1st digit from month()
  filename[5] = now.month()%10 + '0'; //To get 2nd digit from month()
  filename[6] = now.day()/10 + '0'; //To get 1st digit from day()
  filename[7] = now.day()%10 + '0'; //To get 2nd digit from day()
}

void running(){
  lcd.setCursor(0,0);
  lcd.print("In");
  lcd.setCursor(31,0);
  lcd.print("\001C ");
  lcd.setCursor(0,1);
  lcd.print("Out");
  lcd.setCursor(31,1);
  lcd.print("\001C ");
  lcd.setCursor(0,2);
  lcd.print("Drain");
  lcd.setCursor(31,2);
  lcd.print("\001C ");
  lcd.setCursor(0,3);
  lcd.print("F");
  lcd.setCursor(5,3);
  lcd.print("l");
  lcd.setCursor(10,3);
  lcd.print("ow");
  lcd.setCursor(24,3);
  lcd.print("l");
  lcd.setCursor(28,3);
  lcd.print("\002");
  lcd.print("m");
  lcd.setCursor(39,3);
  lcd.print("i");  
  lcd.setCursor(44,3);
  lcd.print("n");
  lcd.setCursor(14,4);
  lcd.print("Conv %");
  lcd.setCursor(21,5);
  lcd.print("kW");
}

I'll be sure to give it a shot with some of your example code tomorrow morning. Is your Philips PCD8544.h customized in any way?

You must disable all the CS pins at startup - even better use pull-up resistors on them so they are
inactive as the Arduino resets or powers up.

What flavour SPI (CPOL, CPHA, clock rate) are each of the devices? SD is 3.3V, is everything 3.3V then?

They are both 3.3v. Actually not sure on clock polarity. I'll have to check the data sheets.

jephil08:
I'll be sure to give it a shot with some of your example code tomorrow morning. Is your Philips PCD8544.h customized in any way?

Yes. The pin definitions are in the library and may have to be edited, hence the absence of pin defs in the programme! I don't think libraries should do that but, once you realise what the problem is, it isn't hard to do.

The only other customising relevant to the 5110 are the glyph calls in the programme. These are derived from an on-line glyph editor. I imagine this works with any version of the PCD8544.

Note that I have been getting the impression of late that I have been a bit lucky with pin conflict. The tight open-write-close sequence for the filewrites may have more relevance than merely looking good on paper.......

SO I changed my library to the PCD8544.h and changed the pins to match in the file. It works with the helloWorld example and begins with the new code I've tried to implement. Here is the new code I've switched to. Basically it's a combination of the ReadWrite from SD and your code to use the LCD display. This code displays the 'Init...' on the LCD, but does not continue after the SD is initialized. The SD card reads and writes fine.

Here is the code:

#include <PCD8544.h>           //LCD Library
static PCD8544 lcd;

#include <SD.h>                //SD Card Library 
File myFile;

//Set Variables
const int LCDSelect =  4;
const int SDSelect = 10;

void setup()   {
  lcd.begin(84, 48);
  Serial.begin(9600);
  delay(1000);  //allow system to stabilize
  lcd.setCursor(0,0);
  lcd.print("Init..."); //Initialize screen
  delay(2000);
  lcd.setCursor(0,1);
    
  pinMode(SDSelect, OUTPUT);
    
    if (!SD.begin(SDSelect)) 
  {
    lcd.setCursor(0,1);
    lcd.print("Card Failure");
    delay(2000);
    Serial.println("Card Failure");
    return;
  } 
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.println("Init. OK!");
  Serial.println("Card Initialized!");
  delay(2000);
  lcd.clear();
   
  // 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");
  }
  
  // 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()) {
    	Serial.write(myFile.read());
    }
    // close the file:
    myFile.close();
  } else {
  	// if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }  
}
void loop () 
{
}

This shakes the confidence a bit.
Do you see the "init OK" on the screen?

If not, then clearly the card initialisation is interfering with the LCD operation. This is a problem I don't have and I can only assume it is to do with the pin assignments for the SD. I forgot to point out that my SD is built-in on the EtherTen and included in the Ethernet shield for all my other stuff , i.e. no configuration on my part.

The only other place where I can point the bone is your

const int LCDSelect = 4;
const int SDSelect = 10;

and this is only because I don't use it. They my be required with the Adafruit library you were using but I'm only guessing.

You clearly have the library sorted so, do me a favour and try my code verbatim. If it doesn't work, that may indicate hardware differences, differences that I don't really understand.

Looks like it's the SD card. I'm still only getting the initializing screen on the LCD using your code. Do you happen to have a way to find out what pins are used for your SD card? I'm using pin 10 for CS, 11 MOSI, 12MISO, 13 CLK. I'm using the Sparkfun SD breakout.

Also just tried switching to the newest version of SdFat.h to control the SD card. Same problem persists. If anyone has any suggestions on how to modify the libraries so that I can toggle between the two it may fix the problem.

I had tried to toggle CS pins to select which one to use by setting the digitalWrite for that pin, but I didn't have any luck.

Ok. I Finally got the SD card and the PCD8544 to play nice. I had to enable SOFTWARE SPI (change value to 1) in the SdFatConfig.h using SdFat instead of the typical Arduino SD.h. Now to translate my original code to SdFat language instead of SD....

Looks like I spoke too soon. With software SPI enabled, i'm only able to use the LCD during setup, not loop. any suggestions from anyone? I really appreciate the help I've gotten so far. :slight_smile:

I don't think changing the SD library will fix anything. If I can get a result with the plain vanilla SD library, you should too. Did you try the code I sent verbatim? I think pin 10 might be the villain.

Pin 4 is vital too. I had to clip the pin and divert in order to get my 16x2 to work with the SD.

I don't see

pinMode(10, OUTPUT);

in your code, which is more or less de rigeur, and I think that other stuff you sprinkled in is suss.

so would you recommend not using pins 4 and 10 at all (at least not for SD/LCD)? when you clipped it, you just routed it to another pin?

I also just ran your code again (without DS18B20 stuffs) and am only getting the initializing portions again. Serial is outputting numbers though (even if they are all zeros).

Let me give it a shot with the exact same pins you are using. maybe that will fix my problem.

Pins have been swapped to the same pins you are currently using. Still only getting to the "initializing" section for the LCD. Tried changing the SD CS to pin 3 also to see if that was the problem. Same results. It should be at least spitting out zeros... it is saving them to the SD.

(Oooh christ....) I didn't realise you are using pin 10 for the 5110. I assume that is CE. I use pin 7 for that, something I have come to regret. Practically anything will do except 0,1,4,10,11,12,13!

I clipped pin 4 on a proto shield that is interposed between the 16 x2 LCD shield and the main board, and ran a jumper thereon to Pin 16. This wasn't very smart, I should have run the jumper on the LCD shield.

I'm not an expert on pin 4 and 10 for the SD, but I understand 4 is the basic select hence my 16x2 LCD problems. As I said, I have no choice on the SD pins, but everything works OK, and with common programming.

re your latest, note again that I had to edit the library. I assume you have done that OK. If your 5110 pin order is the same as mine, I recommend you (later) try using pin 6 for CE and 7 for RST. This is just for more elegant wiring.

Don't give up!

I have already edited the library to the correct pins. I am now using 5,6,7, 11, 13 (in same setup as you listed earlier) for the 5110. The SD is what was on pin 10. I moved it to pin 4 and then now to pin 3. Pin 10 is still set up as an output.

Trust me. I'm not giving up yet. I've put WAY too many hours into this project. Now if I can just get it running by my deadline...

I really appreciate the help. I've only made a few arduino projects before and most of those were copy/edit slightly other people's code. This is my first big plunge into really programming one and i've thrown myself into the middle of the ocean (way past jumping into the deep end).

My eventual goal is to have a slave interpreting an analog input into a few variables as well and sending most of the parameters to the screen as well as saving to SD. Hopefully I'll be able to get everything done.

OK. There is something ironic here in that most people have SD problems rather than the display.

The wiring and library are the biggest problems and you clearly have that sorted. Can you comment out the SD stuff, just to ensure the feed to LCD is kosher?

SD pins are as follows:

CS: 3
MOSI: 11
MISO: 12
CLK: 13

LCD and SD share MOSI and CLK pins. Pins 11 and 13 run through a level shifter (SN74LVC245AN) to translate 5V from arduino to 3.3v.

Reading directly from the PCD8544.h file,

CLK: 13
DIN: 11 (MOSI)
DC: 5 (data select)
RST: 6
SCE: 7 (chip enable)

all run through level shifter (HEF4050BP) from 5V arduino to 3.3v

5v powered by UA7805, 3.3v powered by LM317 (output actually about 3.45v)