view history edit print login register

Epson S1D15G10 Nokia LCD (Work In Progress)

Interfacing the Arduino to the 128x128 12bit color 3V Epson Nokia Knock-Off LCD.

Hardware Introduction

This is the first color, graphic LCD display I've worked with. I made some assumptions when I first begain working with it. From previously working with counter ICs, I assumed CS and RESET could just be tied high. It wont work if you do that. They must be wired to the microcontroller to get reliable operation from the LCD.

After wiring everything up, I desperately wanted some sign of life from the LCD, to know if it was wired correctly. Figuring out how to use the LCD without reading the datasheet is impossible. You could send random data to the display for a thousand years and never see any indication that it's working. Towards the end of the data sheet are application notes and a nice walkthrough for configuring the display. Usually, after setting the contrast for it, and writing some data to the display's ram, you'll see it do something. Otherwise, simply powering the LCD and backlight (necessary) will give you a blue screen, and maybe just some sporadic lines from noise.

Speaking of noise, this project has had the worst problems of any I've ever done. I tried several capacitors between VCC and GND, more or less randomly, in an attempt to reduce the problem. If I had an oscilloscope on hand, I could've used that to find better capacitors. If you're worried about your power supplies being noisey, as a rule of thumb I'd recommend at least two capacitors, one electrolytic around 500uF (or bigger if you've got one) and a small ceramic 5pF~200pF for eliminating HF noise. If you can see horizontal lines on your screen, or your program doesn't work 100% of the time, it's safe to assume you've got noise problems.

In addition, the booster circuit built-in to the breakout board is a great concept, but in practice was disappointing. You can disable it by following SparkFun's directions, desoldering the two solder-jumpers, and connecting your circuit to the side of the breakout board that has more connections. If you want to try to improve your booster, check out the manufacturer's datasheet for the tiny 5pin booster SMD IC. There's an example circuit, and information about noise reduction using different resistor values. SparkFun also has a schematic for the breakout board, showing what is what... However, if I remember correctly, the side of the booster IC with three wires was mislabelled. Tread carefully if you try modifying it.

Software Introduction

Circuit Notes

Requires 5V to 3V converters; with a 7407 IC, transistors or resistor voltage dividers The easiest way would be resistor voltage dividers. I used a 7407, however of the three I had on hand, only one worked acceptably. Test the converter you're going to use, before connecting it to the 3.3V display.

Schematic

Sparkfun's Nokia 6100 Knockoff

Sparkfun's part# LCD-08600 does not work properly with the code below. You can read the post in the forums here

Using Thomas J's library with Phokur's edits, the resulting library can be downloaded

Source Code

/* S1D15G10 Nokia LCD, SPI Test Program
 * ---------------
 *
 * Draws a test pattern on the Nokia LCD.
 * This program is optimized for speed,
 * especially the RGB12 and FILL functions.
 *
 * A faster version of this program, could
 * use SRAM, a counter and a 2~8MHz clock to transmit
 * faster than 1Mbps.
 */

// NOTES
// 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
//  1,   2,   4,   8,   16,   32,   64,   128
//  1,   2,   3,   4,   5,    6,    7,    8
// PORTB bit vs. pin: 0x1=8, 0x2=9, 0x4=10, 0x8=11, 0x10=12, 0x20=13, 0x40=n/a, 0x80=n/a

/********************
/* BINARY CONSTANTS *
*********************/

// the enumerated constants bXXXX can be used like any other constant (e.g. #define, 0xFF, 1234, 'a' or "ABCD")
// they behave like other constants; they use no sram, and only 1 or 2 bytes of flash memory (only when the constants are used somewhere)
// just having these constants in your program will not increase your hex file's size
enum binary_nibble {
  b0000=0, b0001, b0010, b0011, b0100, b0101, b0110, b0111,
  b1000, b1001, b1010, b1011, b1100, b1101, b1110, b1111
};

// Any and every item in an enumerated list may be assigned a constant value.
// ...following items are one greater than the previous; so b00000001 is 1, b00000010 is 2, etc.
// I assigned zero to the first item, each item in the list is one greater than the previous.
// b11111111 is literally the same as the integer constant 255 and 0xFF
enum binary_byte {
  b00000000=0, b00000001, b00000010, b00000011, b00000100, b00000101, b00000110, b00000111,
  b00001000, b00001001, b00001010, b00001011, b00001100, b00001101, b00001110, b00001111,
  b00010000, b00010001, b00010010, b00010011, b00010100, b00010101, b00010110, b00010111,
  b00011000, b00011001, b00011010, b00011011, b00011100, b00011101, b00011110, b00011111,
  b00100000, b00100001, b00100010, b00100011, b00100100, b00100101, b00100110, b00100111,
  b00101000, b00101001, b00101010, b00101011, b00101100, b00101101, b00101110, b00101111,
  b00110000, b00110001, b00110010, b00110011, b00110100, b00110101, b00110110, b00110111,
  b00111000, b00111001, b00111010, b00111011, b00111100, b00111101, b00111110, b00111111,
  b01000000, b01000001, b01000010, b01000011, b01000100, b01000101, b01000110, b01000111,
  b01001000, b01001001, b01001010, b01001011, b01001100, b01001101, b01001110, b01001111,
  b01010000, b01010001, b01010010, b01010011, b01010100, b01010101, b01010110, b01010111,
  b01011000, b01011001, b01011010, b01011011, b01011100, b01011101, b01011110, b01011111,
  b01100000, b01100001, b01100010, b01100011, b01100100, b01100101, b01100110, b01100111,
  b01101000, b01101001, b01101010, b01101011, b01101100, b01101101, b01101110, b01101111,
  b01110000, b01110001, b01110010, b01110011, b01110100, b01110101, b01110110, b01110111,
  b01111000, b01111001, b01111010, b01111011, b01111100, b01111101, b01111110, b01111111,
  b10000000, b10000001, b10000010, b10000011, b10000100, b10000101, b10000110, b10000111,
  b10001000, b10001001, b10001010, b10001011, b10001100, b10001101, b10001110, b10001111,
  b10010000, b10010001, b10010010, b10010011, b10010100, b10010101, b10010110, b10010111,
  b10011000, b10011001, b10011010, b10011011, b10011100, b10011101, b10011110, b10011111,
  b10100000, b10100001, b10100010, b10100011, b10100100, b10100101, b10100110, b10100111,
  b10101000, b10101001, b10101010, b10101011, b10101100, b10101101, b10101110, b10101111,
  b10110000, b10110001, b10110010, b10110011, b10110100, b10110101, b10110110, b10110111,
  b10111000, b10111001, b10111010, b10111011, b10111100, b10111101, b10111110, b10111111,
  b11000000, b11000001, b11000010, b11000011, b11000100, b11000101, b11000110, b11000111,
  b11001000, b11001001, b11001010, b11001011, b11001100, b11001101, b11001110, b11001111,
  b11010000, b11010001, b11010010, b11010011, b11010100, b11010101, b11010110, b11010111,
  b11011000, b11011001, b11011010, b11011011, b11011100, b11011101, b11011110, b11011111,
  b11100000, b11100001, b11100010, b11100011, b11100100, b11100101, b11100110, b11100111,
  b11101000, b11101001, b11101010, b11101011, b11101100, b11101101, b11101110, b11101111,
  b11110000, b11110001, b11110010, b11110011, b11110100, b11110101, b11110110, b11110111,
  b11111000, b11111001, b11111010, b11111011, b11111100, b11111101, b11111110, b11111111
};

/*************************************
/* DEFINES - LCD CONTROLLER COMMANDS *
**************************************/

#define DISON       0xaf   
#define DISOFF      0xae   
#define DISNOR      0xa6  
#define DISINV      0xa7  
#define COMSCN      0xbb   
#define DISCTL      0xca   
#define SLPIN       0x95   
#define SLPOUT      0x94   
#define PASET       0x75   
#define CASET       0x15   
#define DATCTL      0xbc   
#define RGBSET8     0xce   
#define RAMWR       0x5c   
#define RAMRD       0x5d   
#define PTLIN       0xa8   
#define PTLOUT      0xa9   
#define RMWIN       0xe0   
#define RMWOUT      0xee   
#define ASCSET      0xaa   
#define SCSTART     0xab   
#define OSCON       0xd1   
#define OSCOFF      0xd2   
#define PWRCTR      0x20   
#define VOLCTR      0x81   
#define VOLUP       0xd6   
#define VOLDOWN     0xd7   
#define TMPGRD      0x82   
#define EPCTIN      0xcd   
#define EPCOUT      0xcc   
#define EPMWR       0xfc   
#define EPMRD       0xfd   
#define EPSRRD1     0x7c   
#define EPSRRD2     0x7d   
#define NOP         0x25

// RAMWR b01011100 0x5c
#define spi_RAMWR() { PORTB=b00100010; PORTB=b00101010;  \
  PORTB=b00100010; PORTB=b00101010; \
  PORTB=b00110010; PORTB=b00111010; \
  PORTB=b00100010; PORTB=b00101010; \
  PORTB=b00110010; PORTB=b00111010; \
  PORTB=b00110010; PORTB=b00111010; \
  PORTB=b00110010; PORTB=b00111010; \
  PORTB=b00100010; PORTB=b00101010; \
  PORTB=b00100010; PORTB=b00101010; \
  PORTB=b00101110; }

// RGBSET8 b11001110 0xce
#define spi_RGBSET8() { PORTB=b00100010; PORTB=b00101010;  \
  PORTB=b00110010; PORTB=b00111010; \
  PORTB=b00110010; PORTB=b00111010; \
  PORTB=b00100010; PORTB=b00101010; \
  PORTB=b00100010; PORTB=b00101010; \
  PORTB=b00110010; PORTB=b00111010; \
  PORTB=b00110010; PORTB=b00111010; \
  PORTB=b00110010; PORTB=b00111010; \
  PORTB=b00100010; PORTB=b00101010; \
  PORTB=b00101110; }

// b00000000 send null data (fast)
#define spi_NULLDATA() { PORTB=b00110010; PORTB=b00111010; \
  for ( unsigned char _nd = 8 ; _nd ; _nd-- ) { PORTB=b00100010; PORTB=b00101010; } \
  PORTB=b00101110; }

/*************************
/* DEFINES - PINS & BITS *
**************************/

// the pins (unused)
#define pinPOWER  13
#define pinSDATA  12
#define pinSCLK   11
#define pinCS     10
#define pinRST    9

// how far to shift left for each pin
#define shiftPOWER  5
// if b00000001 is shifted left five times, it becomes b00100000 (1<<5 is 0x20)
#define shiftSDATA  4
#define shiftSCLK   3
#define shiftCS     2
#define shiftRST    1

// the bcd bit for the corresponding pin
#define bitPOWER  0x20
#define bitSDATA  0x10
#define bitSCLK   0x8
#define bitCS     0x4
#define bitRST    0x2

#define bitMASK   b11100111
// this bit mask is used by the SPI_OUT() macro
// a bitwise and of PORTB and bitMASK discards the previous SDATA and SCLK value
// then a bitwise or of PORTB and the new SDATA and SCLK values can be performed

/**********
/* MACROS *
***********/

// Upper Byte, Lower Byte
// combines two bytes into a word
//#define UBLB(a,b)  ( ( (a) << 8) | (b) )
// Upper Nibble, Lower Nibble
// combines two nibbles into a byte
//#define UNLN(a,b)  ( ( (a) << 4) | (b) )
//#define PULSE(pin,level)  { digitalWrite((pin),(level)); digitalWrite((pin),!(level)); }
//#define GETBIT(src,bit)  ( ( ( (src) & (bit) )>0 ) )
//#define BMASK(p,m,b)  { (p)=(((p) & (m)) | (b)); }

// set CS LOW, set CLK HIGH
#define SPI_ENABLE()  { (PORTB)=(((PORTB) & b11110011) | (b00001000)); }

// set CS HIGH, set CLK HIGH
#define SPI_DISABLE() { (PORTB)=(((PORTB) & b11110011) | (b00001100)); }

// friendlier, slow version of SPI_OUT
//#define SPI_OUT(bit)  { digitalWrite(pinSDATA,bit); digitalWrite(pinSCLK,LOW); digitalWrite(pinSCLK,HIGH); }

// faster "write only" optimized version of SPI_OUT
#define SPI_OUT(bit)  { (PORTB)=((b11100011) | ( ((bit)>0) << shiftSDATA)); (PORTB)=((b11100011) | (( ((bit)>0) << shiftSDATA) | bitSCLK)); }

// even faster "write only" further optimized to calculate output bits less often
#define SPI_OUT_LOW(data)  { (PORTB)=((b11100011) | (data)); (PORTB)=((b11100011) | ( (data) | bitSCLK)); }


/*************
/* FUNCTIONS *
**************/

void lcd_init();

void spi_command(unsigned char cmd);
void spi_data(unsigned char v);
void delay_n(unsigned int ms);

void fill(unsigned int b);

void RGBSET8_default();
void RGB8(unsigned char rgb);
void RGB12(unsigned int rgb);

void SET_XYWH(unsigned char x, unsigned char y, unsigned char w, unsigned char h);

void setup(void) {
  DDRB=0xFF; // set all pins 13 through 9 as outputs

  lcd_init();
  cli(); // disables interrupts (not required)
}

void loop(void) {

  unsigned char offset = 0, shift=1;

  do {

    SET_XYWH(0,0,43,131);

    RGB12(0xF000); // red=15, green=0, blue=0
    fill(132*44);
    //delay_n(1000);

    SET_XYWH(44,0,43,131);

    RGB12(0x0F00); // red=0, green=15, blue=0
    fill(132*44);
    //delay_n(1000);

    SET_XYWH(88,0,43,131);

    RGB12(0x00F0); // red=0, green=0, blue=15
    fill(132*44);
    //delay_n(1000);


    SET_XYWH(offset-2,offset+2,66,66);

    RGB12(0xFF00);
    fill(66*66);
    delay_n(10);
    RGB12(0x0FF0);
    fill(66*66);
    delay_n(10);

    offset+=shift;
    if (offset>64)
      shift=-1;
    else if (offset<2)
      shift=1;

  } while (true);

  // fill the screen with black
  RGB12(0);
  fill(132*132);

  spi_command(DATCTL);  // data control
  spi_data(b0000);
  spi_data(b0000);
  //spi_data(b0001); // 8 bit color (needs RGBSET8)
  spi_data(b0110); // 12 bit color (mode B)

  SET_XYWH(0,30,131,101);
  spi_RAMWR();
  for ( unsigned char r = 0 ; r<16 ; r++ ) {
    for ( unsigned char g = 0 ; g<16 ; g++ ) {
      for ( unsigned char b = 0 ; b<16 ; b++ ) {
    PORTB=b00110010; PORTB=b00111010; // this is data

    // RED
    SPI_OUT( (r & 0x8) );
    SPI_OUT( (r & 0x4) );
    SPI_OUT( (r & 0x2) );
    SPI_OUT( (r & 0x1) );

    SPI_OUT( (g & 0x8) );
    SPI_OUT( (g & 0x4) );
    SPI_OUT( (g & 0x2) );
    SPI_OUT( (g & 0x1) );

    SPI_OUT( (b & 0x8) );
    SPI_OUT( (b & 0x4) );
    SPI_OUT( (b & 0x2) );
    SPI_OUT( (b & 0x1) );

    PORTB=b00101110; // end data, cs=low
      }
    }
  }

  do {} while (true);

  return;
}

void lcd_init() {
  digitalWrite(pinCS,HIGH);
  digitalWrite(pinRST,LOW);
  delay(500);
  digitalWrite(pinRST,HIGH);
  delay(100);

  spi_command(DISCTL);  // display control
  spi_data(b0011);
  spi_data(b00100000);
  spi_data(b00001100);
  //spi_data(b00000000);

  spi_command(COMSCN);  // common scan direction
    spi_data(b0001);

  spi_command(OSCON);  // oscillator on

  spi_command(SLPOUT);  // sleep out

  spi_command(VOLCTR);
  spi_data(15); // 63 is max power (higher=brighter)
  spi_data(6); // 7 is max value for internal resistor (higher=brighter)

  spi_command(PWRCTR);  // power ctrl
  spi_data(b1101);      //everything on, except secondary booster

  //spi_command(TMPGRD); // temperature gradient
  //spi_data(b0000); // max 3

  //spi_command(PTLIN);
  //spi_data(b00000011); // 63 max - top margin begin
  //spi_data(b00011110); // 63 max - bottom margin end

  spi_command(DISINV);  // display mode

  spi_command(DATCTL);  // data control
  spi_data(b0000);
  spi_data(b0000);
  spi_data(b0001); // 8 bit color (needs RGBSET8)
  //spi_data(b0110); // 12 bit color (mode B)

  RGBSET8_default();

  spi_command(NOP);  // nop

  spi_command(PASET);   // page start/end ram
  spi_data(0);            // for some reason starts at 2
  spi_data(131);          

  spi_command(CASET);   // column start/end ram
  spi_data(0);          
  spi_data(131);

  spi_command(DISON);   // display on
}

// send 8 bits as a command
void spi_command(unsigned char cmd) {
  // b00NDPCR0
  SPI_ENABLE(); // set CS low, set SCLK high

  SPI_OUT(LOW);
  // send mode (high=data, low=command)

  // send data from MSB to LSB
  SPI_OUT(cmd & 0x80);
  SPI_OUT(cmd & 0x40);
  SPI_OUT(cmd & 0x20);
  SPI_OUT(cmd & 0x10);
  SPI_OUT(cmd & 0x8);
  SPI_OUT(cmd & 0x4);
  SPI_OUT(cmd & 0x2);
  SPI_OUT(cmd & 0x1);

  SPI_DISABLE(); // set CS high, set SCLK high
  // SPI must be enabled and disabled every time, for both commands and data
}

// send 8 bits as a parameter/data
void spi_data(unsigned char v) {
  // b00NDPCR0
  SPI_ENABLE();

  SPI_OUT(HIGH);
  // send mode (high=data, low=command)

  // send data from MSB to LSB
  SPI_OUT(v & 0x80);
  SPI_OUT(v & 0x40);
  SPI_OUT(v & 0x20);
  SPI_OUT(v & 0x10);
  SPI_OUT(v & 0x8);
  SPI_OUT(v & 0x4);
  SPI_OUT(v & 0x2);
  SPI_OUT(v & 0x1);

  SPI_DISABLE();
  // SPI must be enabled and disabled every time, for commands and data
}

// wait X*4000 cycles (X is milliseconds, approx.)
void delay_n(unsigned int ms) {
  unsigned int p10;
  for ( ; ms>0 ; ms--) {
    for ( p10 = 4000 ; p10>0 ; p10-- ) {
      asm volatile ("NOP");
    }
  }
}

// fills b pixels
// use RGB8 or RGB12 to choose the color
void fill(unsigned int b) {
  spi_RAMWR();
  for ( b-- ; b ; b--){
    PORTB=b00110010; PORTB=b00111010; // this is data

    // RED
    PORTB=b00100010; PORTB=b00101010; // 8 bit color (see DATCTL)
    PORTB=b00100010; PORTB=b00101010;
    PORTB=b00100010; PORTB=b00101010;
    // GREEN
    PORTB=b00100010; PORTB=b00101010;
    PORTB=b00100010; PORTB=b00101010;
    PORTB=b00100010; PORTB=b00101010;
    // BLUE
    PORTB=b00100010; PORTB=b00101010;
    PORTB=b00100010; PORTB=b00101010;

    PORTB=b00101110; // end data, cs=low
  }
}

void RGBSET8_default() {
  spi_RGBSET8();   // 8 to 12 bit color lookup table
  //RED
  spi_data(0); // lowest red intensity
  spi_data(2);
  spi_data(4);
  spi_data(6);
  spi_data(8);
  spi_data(10);
  spi_data(12);
  spi_data(15); // greatest red intensity
  // GREEN
  spi_data(0); // lowest green intensity
  spi_data(2);
  spi_data(4);
  spi_data(6);
  spi_data(8);
  spi_data(10);
  spi_data(12);
  spi_data(15); // greatest green intensity
  //BLUE
  spi_data(0); // lowest blue intensity
  spi_data(4);
  spi_data(9);
  spi_data(15); // greatest blue intensity
}

// set an 8 bit fill color
void RGB8(unsigned char rgb) {
  spi_RGBSET8();
  //RED
  spi_data( (rgb & b11100000) >> 5 );
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  // GREEN
  spi_data( (rgb & b00011100) >> 2);
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  //BLUE
  spi_data(rgb & b00000011);
}

// set a 12 bit fill color
// the fill functions can do any 12 bit color - as fast as if drawing an 8 bit color
void RGB12(unsigned int rgb) {
  spi_RGBSET8();
  //RED
  spi_data( (rgb & 0xF000) >> 11 );
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  // GREEN
  spi_data( (rgb & 0x0F00) >> 7 );
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  spi_NULLDATA();
  //BLUE
  spi_data( (rgb & 0x00F0) >> 3 );
}

void SET_XYWH(unsigned char x, unsigned char y, unsigned char w, unsigned char h) {
  spi_command(PASET);   // page start/end ram
  spi_data(y);
  spi_data(y+h);          

  spi_command(CASET);   // column start/end ram
  spi_data(x);          
  spi_data(x+w);
}