[SOLVED]: Nokia 3110/5110 lcd not working with Hardware SPI

I have a Nokia 5110 LCD Module (https://www.sparkfun.com/products/10168) connected to my Arduino. I followed the sparkfun tutorial https://www.sparkfun.com/tutorials/300 and used the setup with resistors. The sparkfun graphics library worked. Then I proceeded to look for some smaller code and found a PCD8544 tutorial on the Arduino Playground (http://playground.arduino.cc/Code/PCD8544). I checked my wiring and used this code:

#define PIN_SCE   4
#define PIN_RESET 3
#define PIN_DC    5
#define PIN_SDIN  6
#define PIN_SCLK  7

#define LCD_C     LOW
#define LCD_D     HIGH

#define LCD_X     84
#define LCD_Y     48

static const byte ASCII[][5] =
{
 {0x00, 0x00, 0x00, 0x00, 0x00} // 20  
,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 !
,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 "
,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 #
,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $
,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 %
,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 &
,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 '
,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 (
,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 )
,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a *
,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b +
,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c ,
,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d -
,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e .
,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f /
,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0
,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1
,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2
,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3
,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4
,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5
,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6
,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7
,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8
,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9
,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a :
,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ;
,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c <
,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d =
,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e >
,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ?
,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @
,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A
,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B
,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C
,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D
,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E
,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F
,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G
,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H
,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I
,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J
,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K
,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L
,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M
,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N
,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O
,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P
,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q
,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R
,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S
,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T
,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U
,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V
,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W
,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X
,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y
,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z
,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [
,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c ¥
,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ]
,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^
,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _
,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 `
,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a
,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b
,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c
,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d
,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e
,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f
,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g
,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h
,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i
,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j 
,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k
,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l
,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m
,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n
,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o
,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p
,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q
,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r
,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s
,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t
,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u
,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v
,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w
,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x
,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y
,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z
,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b {
,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c |
,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d }
,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ?
,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f ?
};

void LcdCharacter(char character)
{
  LcdWrite(LCD_D, 0x00);
  for (int index = 0; index < 5; index++)
  {
    LcdWrite(LCD_D, ASCII[character - 0x20][index]);
  }
  LcdWrite(LCD_D, 0x00);
}

void LcdClear(void)
{
  for (int index = 0; index < LCD_X * LCD_Y / 8; index++)
  {
    LcdWrite(LCD_D, 0x00);
  }
}

void LcdInitialise(void)
{
  pinMode(PIN_SCE, OUTPUT);
  pinMode(PIN_RESET, OUTPUT);
  pinMode(PIN_DC, OUTPUT);
  pinMode(PIN_SDIN, OUTPUT);
  pinMode(PIN_SCLK, OUTPUT);
  digitalWrite(PIN_RESET, LOW);
  digitalWrite(PIN_RESET, HIGH);
  LcdWrite(LCD_C, 0x21 );  // LCD Extended Commands.
  LcdWrite(LCD_C, 0xB1 );  // Set LCD Vop (Contrast). 
  LcdWrite(LCD_C, 0x04 );  // Set Temp coefficent. //0x04
  LcdWrite(LCD_C, 0x14 );  // LCD bias mode 1:48. //0x13
  LcdWrite(LCD_C, 0x0C );  // LCD in normal mode.
  LcdWrite(LCD_C, 0x20 );
  LcdWrite(LCD_C, 0x0C );
}

void LcdString(char *characters)
{
  while (*characters)
  {
    LcdCharacter(*characters++);
  }
}

void LcdWrite(byte dc, byte data)
{
  digitalWrite(PIN_DC, dc);
  digitalWrite(PIN_SCE, LOW);
  shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data);
  digitalWrite(PIN_SCE, HIGH);
}

/* 
   gotoXY routine to position cursor 
   x - range: 0 to 84
   y - range: 0 to 5
*/
void gotoXY(int x, int y)
{
  LcdWrite( 0, 0x80 | x);  // Column.
  LcdWrite( 0, 0x40 | y);  // Row.  
}

void setup(void)
{
  LcdInitialise();
  LcdClear();
  
  
  gotoXY(0,0);
  LcdString("Hello World!");
  gotoXY(0,0);
  LcdString("0123456789012");
  LcdString("0123456789012");
}

void loop(void)
{
}

This worked just nicely. However, I want to implement the LCD in a project using Hardware SPI. I have been fidgeting for some hours and can not get it to work. I had to remap some pins and converted the above program to what I deemed the minimal form:

#include <SPI.h>

#define PIN_SCE   10  // 4
#define PIN_RESET 3
#define PIN_DC    7  // 5
#define PIN_SDIN  11 // 6
#define PIN_SCLK  13 // 7

#define LCD_C     LOW
#define LCD_D     HIGH

void setup()
{
  pinMode(PIN_RESET, OUTPUT);
  pinMode(PIN_DC, OUTPUT);
  pinMode(PIN_SDIN, OUTPUT);
  pinMode(PIN_SCLK, OUTPUT);
  pinMode(PIN_SCE, OUTPUT);
  digitalWrite (PIN_SCE, HIGH);

  SPI.begin();  
  SPI.setDataMode(0);
  SPI.setBitOrder(MSBFIRST);

  digitalWrite(PIN_RESET, HIGH);
  digitalWrite(PIN_RESET, LOW);
  digitalWrite(PIN_RESET, HIGH);

  digitalWrite(PIN_DC, LCD_C);
  sendData( 0x21 );  // LCD Extended Commands.
  sendData( 0xB1 );  // Set LCD Vop (Contrast). 
  sendData( 0x04 );  // Set Temp coefficent. //0x04
  sendData( 0x14 );  // LCD bias mode 1:48. //0x13
  sendData( 0x0C );  // LCD in normal mode.
  sendData( 0x20 );
  sendData( 0x0C );

  digitalWrite(PIN_DC, LCD_D);
  sendData( 0xFF );
}

void sendData(byte data)
{
  digitalWrite(PIN_SCE, LOW);
  SPI.transfer(data);
  digitalWrite(PIN_SCE, HIGH);  
}

void loop()
{
}

I can’t get the display to respond whatsoever. I must be overlooking something. I checked the wiring several times (it’s 5 cables). I have no idea where the problem is, but I suspect in my code. I hope someone with a fresh view can point to a solution.

Jack

Are these two programs supposed to be functionally equivalent or is the second one just a 'proof of concept'?

If they are supposed to be functionally equivalent then I think you have gotten a bit more minimal than you should have in your second version.

If the second program is just 'proof of concept' then you might start out by minimizing the original program first (to do only what you expect the second program to do) and then converting the new version of the original program to use hardware SPI.

I haven't used SPI recently and when I did it was on a different processor, but with a casual look at your code it seems that you will basically be substituting sendData() for LcdWrite(). This won't save much code although it will offload some work from the processor to the Serial subsystem.

Don

I actually started out with substituting LcdWrite() with sendData(), but to no avail. Then I started stripping the program until I was left with just the proof of concept, which doesn't work.

As far as I can tell hardware SPI and software SPI are not compatible on the same set of pins (11,12,13), so if I include SPI.h into my original sketch it stops working.

Right. The display can be controlled up to 2 MHz, my hardware SPI was running too fast, I had to add

  SPI.setClockDivider(SPI_CLOCK_DIV16);

to the setup(); Now it works, so I will mark this as SOLVED. Sending a single byte with sendData() will take 20 us.

It however poses a new challenge, because my SPI calls are called from an interrupt. The interrupt is attached to a timer and is called upon with intervals of 50 us. Within the interrupt at least two SPI calls are made. I'll have to think about how to manage this. Questions I will pose under Programming.

EDIT: the 20 us are true when using digitalWrite, if I exchange that for direct port manipulation the sendData function only takes 9 us. A lot better, and workable. I guess I will change the SPI speed in code, depending on which peripheral I'm controlling.

Thanks for your comment.

I still do not understand the reason for the divide by 16. This would make SPI run at clock speed divided by multiplier, in this case 16 MHz / 16 = 1 MHz, so sending a byte would take 8 us, corresponding to my own measurements. The PCD8544 can accept data @ 4Mbits/s, that makes 0.5 Mbyte/s, so 0.5 byte per us, so a byte takes up 2 us. I do not understand why the PCD9544 wouldn't work with say - a divide by 8 or 4...

CaptainJack:
I want to implement the LCD in a project using Hardware SPI. I have been fidgeting for some hours and can not get it to work. I had to remap some pins and converted the above program to what I deemed the minimal form:

#include <SPI.h>

#define PIN_SCE   10  // 4
#define PIN_RESET 3
#define PIN_DC    7  // 5
#define PIN_SDIN  11 // 6
#define PIN_SCLK  13 // 7

#define LCD_C     LOW
#define LCD_D     HIGH

void setup()
{
 pinMode(PIN_RESET




I can't get the display to respond whatsoever. I must be overlooking something. I checked the wiring several times (it's 5 cables). I have no idea where the problem is, but I suspect in my code. I hope someone with a fresh view can point to a solution.

This gets confusing and it might be time to suspect what the 5110 is really about.

I understood from Lang that the 5110 operates on the SPI bus.

http://ianlangelectronic.webeden.co.uk/#/lcd-module-0/4569058582

And I made up my 5110 boards with that in mind

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

At the same time, I was obliged to move from Uno to Mega. The LCDs worked fine, but they aren’t actually on the SPI bus, just plain old pins 11,13. I now believe I have never actually run the 5110 on SPI except once, recently, when I brought the Uno back out of retirement, I was just testing a clock, and the display didn’t work. I never worked out what happened but the LCD is now fine - back on a Mega.

The following thread, in which I distinguish myself by giving the OP a bit of a bum steer, may be of use

http://arduino.cc/forum/index.php/topic,162167.0.html

Thank you for your reply, I had already found and studied the post you refer to. I think I might have to make a new post with my new question, because the originally stated problem has been solved - my bad.

The 3110 / 5110 series use the PCD8544, which is controlled by SPI. Most sketches I found use software SPI (shiftout) on any pins you would like to select yourself. The software SPI is relatively slow (compared to hardware SPI) so no problems arise.

Switching to hardware SPI (11 for MoSi, 13 for CLK) gave some problems, mostly because it was too fast for the dsheisplay. I notched down the speed and everything works just fine, albeit a bit slower.

I will study the speeds I can accomplish with the PCD8544. The datasheet says it can be updated with 4Mbit/s (so 4 MHz), I have it working at 1 MHz, and sparkfun forums claim it can be controlled at 2 MHz. I can not match these numbers.