Graphic display 100 x  48 pixels + 5 symbols

based on an Epson SED1531 LCD-controller

!!!! Page updated with Arduino code: scroll down  !!!!



I used the good work of sayang , alias Roland van Leusden.

The BascomAVR program has changed on a few aspects:

1. got rid of the long waits: 200ms at the start is sufficient

2. proper handling of E and shortened to 200ns pulsewidth   < corrected typo >

3. reading busy-flag read   |||| cancelled that in Test8: not necessary

4. added scrolling

5. testing ram readback


Test7 uses the Busy-check and is available for reference purposes

Test8 is what I use now, no more Busy-check but short delays where necessary.

Suitable for Tiny and Mega AVR's upto 20MHz clockspeed, not tested yet with Xmega @ 32 MHz


Thanks to Marco/Patrick/Peter van Merkerk for his excellent library in C

and especially what I learned from it ....

His library can be found here: https://sourceforge.net/projects/glcdsed1531lib/


Short demo video of the Bascom program




Arduino sketch Glcd1531_Plons_revA:

Based on the library as published on TkkrLab

Click here to download the zipped sketch; the _ino at the end of the filename is to distinguish it from this plain zipped txt version

And just to be sure: here the HTML export of the sketch

//  Glcd1531_Plons_revA


//  Based on the library published by TkkrLab on http://tkkrlab.nl/wiki/Glcd_48x100

//  Adaptations by Plons (Nard Awater); Feb 23, 2013
//  Published on http://www.aplomb.nl/TechStuff/graph_lcd_MvN/sed1531_graph_lcd/sed1531_graph_lcd.html
//  Feel free to copy the contents of the webpage above, to publish it on a wiki where it can be found easier
//  Leave this header in as reference to where it came from.


//  There were two problems in the original code
//  1. Incorrect handling of the Enable line of the display
//  2. By powering the display with Digital13, this pin has to supply too much current when the Backlight is turned on
//  Then there were a few other things that have been modified
//  3. Got rid of the unnecessary long delays and shortened other
//  4. Added a blank column between adjacent characters

//  Then there was a problem that was harder to track down: latch-up of the display controller SED1531 by an overshoot of the E-signal.
//  The reason I came to suspect this latch-up phenomenae, is that my own AVR board, with a Bascom program, doesn't suffer from this.
//  When I adapted the TkkrLab library to work simular as my Bascom program, I needed to power the display with Digital13, in order to make it work.
//  And that was odd.
//  The length of the connections between AVR and display is just a few cm's on my own board. On the Arduino I used 12cm jumper wires.
//  AVR's do not just have powerfull output drivers, but these buggers are fast too.
//  That, combined with the few cm's extra wire, was enough to make the E-signal overshoot: and this overshoot is the cause of the latch-up

//  Latch-up is nasty: it can destroy a chip. And to get a latched-up circuit working again, you need to power down, wait a while and then power up again.
//  More info here: http://en.wikipedia.org/wiki/Latchup

//  Bascically, all signals can cause this latch-up, but I found that E is the most susceptable one.
//
//  Now the fix:
//                           ------
//         Digital10  ------|      |----------------------------------------------------------  pin 6 == Enable
//                           ------           |                                   |
//                            470R            |                                   |
//                                           ---                                  |
//                                          |   |  3k3                          -----
//  Arduino                                 |   |           or use a capacitor  -----                   Display
//                                           ---                of 330 pF         |
//                                            |                                   |
//                                            |                                   |
//          Gnd    ----------------------------------------------------------------------------  pin 1 == Gnd      
//
//  Both solutions work. Personally I prefer the resistive divider, but technically the slewrate-limiter 
//  with 470 Ohm and 330 pF to ground is an acceptable cure as well.

//  Note to Duality (who published additional code on http://tkkrlab.nl/wiki/Glcd_48x100
//  Your problem with the display going blank is most likely due to this latch-up phenomenon

//  Here we go:

int lcdA0 = 12;
int lcdRW = 11;
int lcdEnable = 10;
 
// int displayPower = 13;
// INSTEAD: Connect Vcc-pin of display to +5V on the Arduino
 
int lcdDataPins[] = {9,8,7,6,5,4,3,2};
 
char str[] = " "; // was 20 char's long, now 17
 
// 5x7 Font
byte lcdFonts[][5] = {
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }, 
{ 0x00 , 0x00 , 0x5F , 0x00 , 0x00 }, 
{ 0x00 , 0x07 , 0x00 , 0x07 , 0x00 }, 
{ 0x14 , 0x7F , 0x14 , 0x7F , 0x14 }, 
{ 0x24 , 0x2A , 0x7F , 0x2A , 0x12 }, 
{ 0x23 , 0x13 , 0x08 , 0x64 , 0x62 }, 
{ 0x36 , 0x49 , 0x55 , 0x22 , 0x50 }, 
{ 0x00 , 0x05 , 0x03 , 0x00 , 0x00 }, 
{ 0x00 , 0x1C , 0x22 , 0x41 , 0x00 }, 
{ 0x00 , 0x41 , 0x22 , 0x1C , 0x00 }, 
{ 0x08 , 0x2A , 0x1C , 0x2A , 0x08 }, 
{ 0x08 , 0x08 , 0x3E , 0x08 , 0x08 }, 
{ 0x00 , 0x50 , 0x30 , 0x00 , 0x00 }, 
{ 0x08 , 0x08 , 0x08 , 0x08 , 0x08 }, 
{ 0x00 , 0x60 , 0x60 , 0x00 , 0x00 }, 
{ 0x20 , 0x10 , 0x08 , 0x04 , 0x02 }, 
{ 0x3E , 0x51 , 0x49 , 0x45 , 0x3E }, 
{ 0x00 , 0x42 , 0x7F , 0x40 , 0x00 }, 
{ 0x42 , 0x61 , 0x51 , 0x49 , 0x46 }, 
{ 0x21 , 0x41 , 0x45 , 0x4B , 0x31 }, 
{ 0x18 , 0x14 , 0x12 , 0x7F , 0x10 }, 
{ 0x27 , 0x45 , 0x45 , 0x45 , 0x39 }, 
{ 0x3C , 0x4A , 0x49 , 0x49 , 0x30 }, 
{ 0x01 , 0x71 , 0x09 , 0x05 , 0x03 }, 
{ 0x36 , 0x49 , 0x49 , 0x49 , 0x36 }, 
{ 0x06 , 0x49 , 0x49 , 0x29 , 0x1E }, 
{ 0x00 , 0x36 , 0x36 , 0x00 , 0x00 }, 
{ 0x00 , 0x56 , 0x36 , 0x00 , 0x00 }, 
{ 0x00 , 0x08 , 0x14 , 0x22 , 0x41 }, 
{ 0x14 , 0x14 , 0x14 , 0x14 , 0x14 }, 
{ 0x41 , 0x22 , 0x14 , 0x08 , 0x00 }, 
{ 0x02 , 0x01 , 0x51 , 0x09 , 0x06 }, 
{ 0x32 , 0x49 , 0x79 , 0x41 , 0x3E }, 
{ 0x7E , 0x11 , 0x11 , 0x11 , 0x7E }, 
{ 0x7F , 0x49 , 0x49 , 0x49 , 0x36 }, 
{ 0x3E , 0x41 , 0x41 , 0x41 , 0x22 }, 
{ 0x7F , 0x41 , 0x41 , 0x22 , 0x1C }, 
{ 0x7F , 0x49 , 0x49 , 0x49 , 0x41 }, 
{ 0x7F , 0x09 , 0x09 , 0x01 , 0x01 }, 
{ 0x3E , 0x41 , 0x41 , 0x51 , 0x32 }, 
{ 0x7F , 0x08 , 0x08 , 0x08 , 0x7F }, 
{ 0x00 , 0x41 , 0x7F , 0x41 , 0x00 }, 
{ 0x20 , 0x40 , 0x41 , 0x3F , 0x01 }, 
{ 0x7F , 0x08 , 0x14 , 0x22 , 0x41 }, 
{ 0x7F , 0x40 , 0x40 , 0x40 , 0x40 }, 
{ 0x7F , 0x02 , 0x04 , 0x02 , 0x7F }, 
{ 0x7F , 0x04 , 0x08 , 0x10 , 0x7F }, 
{ 0x3E , 0x41 , 0x41 , 0x41 , 0x3E }, 
{ 0x7F , 0x09 , 0x09 , 0x09 , 0x06 }, 
{ 0x3E , 0x41 , 0x51 , 0x21 , 0x5E }, 
{ 0x7F , 0x09 , 0x19 , 0x29 , 0x46 }, 
{ 0x46 , 0x49 , 0x49 , 0x49 , 0x31 }, 
{ 0x01 , 0x01 , 0x7F , 0x01 , 0x01 }, 
{ 0x3F , 0x40 , 0x40 , 0x40 , 0x3F }, 
{ 0x1F , 0x20 , 0x40 , 0x20 , 0x1F }, 
{ 0x7F , 0x20 , 0x18 , 0x20 , 0x7F }, 
{ 0x63 , 0x14 , 0x08 , 0x14 , 0x63 }, 
{ 0x03 , 0x04 , 0x78 , 0x04 , 0x03 }, 
{ 0x61 , 0x51 , 0x49 , 0x45 , 0x43 }, 
{ 0x00 , 0x00 , 0x7F , 0x41 , 0x41 }, 
{ 0x02 , 0x04 , 0x08 , 0x10 , 0x20 }, 
{ 0x41 , 0x41 , 0x7F , 0x00 , 0x00 }, 
{ 0x04 , 0x02 , 0x01 , 0x02 , 0x04 }, 
{ 0x40 , 0x40 , 0x40 , 0x40 , 0x40 }, 
{ 0x00 , 0x01 , 0x02 , 0x04 , 0x00 }, 
{ 0x20 , 0x54 , 0x54 , 0x54 , 0x78 }, 
{ 0x7F , 0x48 , 0x44 , 0x44 , 0x38 }, 
{ 0x38 , 0x44 , 0x44 , 0x44 , 0x20 }, 
{ 0x38 , 0x44 , 0x44 , 0x48 , 0x7F }, 
{ 0x38 , 0x54 , 0x54 , 0x54 , 0x18 }, 
{ 0x08 , 0x7E , 0x09 , 0x01 , 0x02 }, 
{ 0x08 , 0x14 , 0x54 , 0x54 , 0x3C }, 
{ 0x7F , 0x08 , 0x04 , 0x04 , 0x78 }, 
{ 0x00 , 0x44 , 0x7D , 0x40 , 0x00 }, 
{ 0x20 , 0x40 , 0x44 , 0x3D , 0x00 }, 
{ 0x00 , 0x7F , 0x10 , 0x28 , 0x44 }, 
{ 0x00 , 0x41 , 0x7F , 0x40 , 0x00 }, 
{ 0x7C , 0x04 , 0x18 , 0x04 , 0x78 }, 
{ 0x7C , 0x08 , 0x04 , 0x04 , 0x78 }, 
{ 0x38 , 0x44 , 0x44 , 0x44 , 0x38 }, 
{ 0x7C , 0x14 , 0x14 , 0x14 , 0x08 }, 
{ 0x08 , 0x14 , 0x14 , 0x18 , 0x7C }, 
{ 0x7C , 0x08 , 0x04 , 0x04 , 0x08 }, 
{ 0x48 , 0x54 , 0x54 , 0x54 , 0x20 }, 
{ 0x04 , 0x3F , 0x44 , 0x40 , 0x20 }, 
{ 0x3C , 0x40 , 0x40 , 0x20 , 0x7C }, 
{ 0x1C , 0x20 , 0x40 , 0x20 , 0x1C }, 
{ 0x3C , 0x40 , 0x30 , 0x40 , 0x3C }, 
{ 0x44 , 0x28 , 0x10 , 0x28 , 0x44 }, 
{ 0x0C , 0x50 , 0x50 , 0x50 , 0x3C }, 
{ 0x44 , 0x64 , 0x54 , 0x4C , 0x44 }, 
{ 0x00 , 0x08 , 0x36 , 0x41 , 0x00 }, 
{ 0x00 , 0x00 , 0x7F , 0x00 , 0x00 }, 
{ 0x00 , 0x41 , 0x36 , 0x08 , 0x00 }, 
{ 0x08 , 0x08 , 0x2A , 0x1C , 0x08 }, 
{ 0x08 , 0x1C , 0x2A , 0x08 , 0x08 }
};
 
void setup() {
  pinMode(lcdA0, OUTPUT);
  pinMode(lcdRW, OUTPUT);
  pinMode(lcdEnable, OUTPUT);
 
//   pinMode(displayPower, OUTPUT);   // no longer used
 
  for (int i = 0; i <= 7; i++) {
    pinMode(lcdDataPins[i], OUTPUT);
  }
// Define a safe setting of control lines at the very beginning
  digitalWrite(lcdEnable, LOW);
  digitalWrite(lcdRW, HIGH);
  digitalWrite(lcdA0, LOW);

  //digitalWrite(displayPower, LOW);
  //delay(2000);
  //digitalWrite(displayPower, HIGH);
  delay(200); //yes, this is sufficient
  lcdInit();
  //delay(1000);
 
  writeClearLines();                           // agreed, not really a clear but a kind of clear ;-)
                                               // just to get visual evidence that this part is executed
  delay(3000);
  writeSomeText();                            // now do the SomeText - field
}
 
void loop() {
  for (int l = 0; l <= 3; l++) {
    for (byte i = 1; i <= 6; i++) {
      setMarker(i, true);
      delay(400);
      setMarker(i, false);
      delay(100);
    }
  }
 
  setRow(6);
  for( byte j = 1; j <= 4; j++) {
    invertDisplay(true);
    delay(400);
    lcdChar("*-*-LCD REVERSED-*-*");
    invertDisplay(false);
    delay(800);
    resetColumnAdress();
    lcdChar("#-#-#-LCD NORMAL-#-#");
    invertDisplay(true);
  }
 
 
 for (int k = 0; k < 32; k++) {
   writeLCDcontrast(k);
   setLCDcontrast(k);
   delay(250);
 }
 for (int k = 31; k >= 0; k --) {
   writeLCDcontrast(k);
   setLCDcontrast(k);
   delay(250);
 }
 writeLCDcontrast(15);
 setLCDcontrast(15);
}
 
/*
Constrast is a value between 0 and 31.
*/
void setLCDcontrast(byte constrast) {
  writeCMDdisplay(0x80 + constrast);
}
void writeLCDcontrast(int contrast) {
   sprintf(str,"-> Contrast: %d ",contrast);
   lcdChar(str);
}
 
/*
  LCD init commands
*/
void lcdInit() {
 
  //the following actions are performed to init the lcd
  writeCMDdisplay(0xe2);                                //reset display by software command; the SED1531 is very fast in performing this
  delayMicroseconds(10); // so the 10 us is enough
  writeCMDdisplay(0xa1);                                //ADC reverse -- to prevent a mirrorred display
  writeCMDdisplay(0xa2);                                //lcd bias 1/8
//  writeCMDdisplay(0x2c);                                //power
//  writeCMDdisplay(0x2e);                                //power
  writeCMDdisplay(0x2f);                                //power
  writeCMDdisplay(0xa6);                                //normal / reverse
  writeCMDdisplay(0x8f);                                //set electronic control
  writeCMDdisplay(0xa4);                                //display off
  writeCMDdisplay(0xaf);                                //display on
  writeCMDdisplay(0x40);                                //sel dram line 1 for com1
 
}
 
void writeSomeText() { // because of the extra column between adjacent characters, there is less room ....
   setRow(1);                                         
   lcdChar( "0123456789012345 ");
   setRow(2);
   lcdChar( "ABCDEFGHIJKLMNPQ ");
   setRow(3);
   lcdChar( "wxyzabcdefghijkl ");
   setRow(4);
   lcdChar( " !#$%&'( )@^- ");
   setRow(5);
   lcdChar( " *+-/<>?;:[] ");
   setRow(6);
   lcdChar( " Plons version ");
}
 
void writeClearLines() {
   setRow(1);                                         
   lcdChar( " clear ");
   setRow(2);
   lcdChar( " clear ");
   setRow(3);
   lcdChar( " clear ");
   setRow(4);
   lcdChar( " clear ");
   setRow(5);
   lcdChar( " clear ");
   setRow(6);
   lcdChar( "* Plons version *");

 
/*  Control de markers boven in het display
 MARKER 1  20  -- pijltjes boven elkaar
 MARKER 2  31  -- 2 horiz. streepjes van barcode
 MARKER 3  32  -- barcode
 MARKER 4  57  -- batterij
 MARKER 5  69  -- sterretje
 MARKER 6  78  -- pijltje UP
*/
void setMarker(byte marker, boolean on) {
 
 
  byte highNibble, lowNibble;
  byte markerLCD;
 
   switch (marker) {
     case 1 : markerLCD = 20;
             break;
     case 2 : markerLCD = 31;
            break;
     case 3: markerLCD = 32;
            break;
     case 4: markerLCD = 57;
            break;
     case 5: markerLCD = 69;
           break;
     case 6: markerLCD = 78;
           break;
   }
 
   lowNibble = markerLCD & 0xF;    // Mask out upper nibble
   highNibble = markerLCD;        
   highNibble = highNibble >> 4;         // Shift upper 4 bits to lower
   bitSet(highNibble, 4); // Set 5th bit high
 
   writeCMDdisplay(0xb6);          //Set page Address
   writeCMDdisplay(highNibble);     //Set column Address high nibble
   writeCMDdisplay(lowNibble);      //Set column Address low nibble
 
   writeDATAdisplay(on);
}
 
/*
  Put COMMAND to display (for configuration purpose)
*/
void writeCMDdisplay(byte cmd) {
  digitalWrite(lcdA0, LOW);
  digitalWrite(lcdRW, LOW);
 
  byte data = cmd;
 
  for (int i = 7; i >= 0; i--) {
    int value = data & 0x1;
    digitalWrite(lcdDataPins[i], value);
    data = data >> 1;
  }
 
// This is the proper way to handle Enable  
  digitalWrite(lcdEnable, HIGH);
  delayMicroseconds(1);
  digitalWrite(lcdEnable, LOW);

}
 
/*
  Put DATA to display (pixel data)
*/
void writeDATAdisplay(byte lcdData) {
  digitalWrite(lcdA0, HIGH);
  digitalWrite(lcdRW, LOW);
 
  byte data = lcdData;
 
  for (int i = 7; i >= 0 ; i--) {
    int value = data & 0x1;
    digitalWrite(lcdDataPins[i], value);
    data = data >> 1;
  }
  digitalWrite(lcdEnable, HIGH);
  delayMicroseconds(1);
  digitalWrite(lcdEnable, LOW);

}
 
 
void invertDisplay(boolean reverse) {
  writeCMDdisplay(0xA6 + (reverse ? 0:1));
}
 
 
 
/*
 Select the row where to write the text
 
 row value between 1 and 6
*/
void setRow(byte row) {
  byte page = 0xB0 + (row - 1);
 
  writeCMDdisplay(page);
  writeCMDdisplay(0x10);  // This was 0x08 in the original SW, but my interpretation of the datasheet is 0x10
  writeCMDdisplay(0x00);
 
  lcdChar(" "); //Make page empty
 
  writeCMDdisplay(page);
  writeCMDdisplay(0x10);  // This was 0x08 in the original SW, but my interpretation of the datasheet is 0x10
  writeCMDdisplay(0x00);
}
 
 
/*
  Send text string to display
*/
void lcdChar(String text) {
   int charCount = text.length();
 
   //There is only room for 16+a_few_pixels characters
   if (charCount > 17) {
     charCount = 17;      
   }
 
   //Start Read-Modify-Write
   writeCMDdisplay(0xE0);
 
   //Loop trough text
   for (int i = 0; i < charCount; i++) {
     char currentChar = text.charAt(i);
 
     // Character table starts at Ascii-32, so deduct 32 to get it even with the ascii table
     int lcdCharPosition = currentChar - 32;
 
     for (int j = 0; j < 5; j++) {
       writeDATAdisplay(lcdFonts[lcdCharPosition][j]);
       
     }
     writeDATAdisplay(0); //empty column to separate characters
   }
 
   //End Read-Modify-Write
  writeCMDdisplay(0xEE);
 
  resetColumnAdress();
 
}
 
/*
 Reset column address to the first position in the page
*/
void resetColumnAdress() {
  writeCMDdisplay(0x10);
  writeCMDdisplay(0x00);
}

// The End

Two pictures: 

My first attempt: works with TkkrLab lib, but MUST use Digital13 to power the display. 

Wires are appr. 20 cm.




The working version: powered with 5V from the Arduino and the resistive divider on E

Glcd1531_Plons_revA.ino is the sketch running here.


Two notes at the end of this late Saterdaynight:

 1. It's a good engineering practice to add a decoupling capacitor over Vcc and Gnd of the display. 

 2. If your display has latched up and you power it from a lab-supply, have a look at the current the Arduino consumes: it went up with 100mA in my experiments. 

Remove all power, including the Arduino's USB-plug to get rid of the latch-up. 

Resets do not help to get out of the latch-up situation !!

If I feel up to it tomorrow, I'll try the 20cm wiring once more, of course with latch-up-fix and Glcd1531_Plons_revA.ino sketch as SW

Good luck and have fun !

< EOP >