Interfacing the Arduino to the 128x128 12bit color 3V Epson Nokia Knock-Off LCD.
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.
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.

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
/* 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);
}