Demo of controlling a bare LCD with an Arduino (no driver, no library)

I wanted to learn about how 7 segment LCDs work, without having also to learn how to interface to and send correct data to an LCD driver. I'm very proud of how tight I've managed to make this code.

You can see it working here https://www.instagram.com/p/DKJxbdnow4E/ with an earlier version of the code in the background.

It only drives one digit (I strapped 3 digits together on the little panel I salvaged from a dead carbon monoxide monitor) and making it drive all three independently would use another 4 output pins taking the total pin count to 10, unless some external logic circuits were added - but then, might as well add a display driver?

Good fun though.


void setup() {
}

void setPin(int pin, int polarity, bool on){
  if(on){                         // pin is needed to be activated HIGH or LOW
    pinMode(pin,OUTPUT);          // so set it to OUTPUT
    digitalWrite(pin,polarity);   // and assign the specified polarity (HIGH or LOW)
  } else {                        // else
    pinMode(pin,INPUT);           // set the pin to high impedance
  }
}

void setPins(int digit){
  // pins 2,3,4,5 are connected to each of the 4 "backplanes" ("common") and pins 6 and 7 are connected to the two segment subsets (in my test LCD: DP,C,B,A and D,E,G,F) in the digit
  const char segs[10]={235,96,199,229,108,173,175,224,239,237}; // bit pattern stores segments to "light" for each digit 0 .. 9
  unsigned int bitpattern = segs[digit];                        // bit pattern for the digit we want to display
  unsigned int timeslot = (millis()/2) % 8;                     // create a cycling timeframe with 8 slots
  unsigned int polarity = timeslot % 2;                         // polarity alternates 01010101 across the 8 time slots
  unsigned int segNumber = timeslot / 2;                        // segment number goes 01234567 across the 8 time slots
  unsigned int comNumber = segNumber % 4;                       // common  number goes 01230123 across the 8 time slots
  setPin(2, polarity, comNumber==0);                            // if comNumber = 0, activate common pin 0 with polarity 0 or 1, else set to high impedance  
  setPin(3, polarity, comNumber==1);                            // if comNumber = 1, activate common pin 1 with polarity 0 or 1, else set to high impedance  
  setPin(4, polarity, comNumber==2);                            // if comNumber = 2, activate common pin 2 with polarity 0 or 1, else set to high impedance  
  setPin(5, polarity, comNumber==3);                            // if comNumber = 3, activate common pin 3 with polarity 0 or 1, else set to high impedance  
  setPin(6, 1-polarity, bitpattern>>segNumber & 1);             // if bit "segNumber" (0 to 3) in "bitpattern" is set, activate first subset of segments with opposite polarity, else set to high impedance
  setPin(7, 1-polarity, bitpattern>>(segNumber+4) & 1);         // if bit "segNumber+4" (4 to 7) in "bitpattern" is set, activate second subset of segments with opposite polarity, else set to high impedance
}

void loop() {
  unsigned int digit = millis()/500 % 10;   // set digit = 0,1,2,3,4,5,6,7,8,9 changing twice a second
  setPins(digit);                           // call setPins to display the digit on the LCD
}

4 Likes

Hi, I also have had a lot of fun making a 4-digit 7-segment LCD work. In a previous post on LCDs, there is a link to a Youtube video, where this Australian guy explains the basics. Next stop was Rachel de Barras' piece on interrupts, which included a neat trick for toggling an output. I used a Mega to get enough I/O for the display, so no need of any other components (some of the 31 wires were less than 1cm, with 12 of them 1-to-1with the Mega).
Will post the rest after lunch!



Using a variable for the I/O saves lines of code!
e.g.

setup()```
  for (c = 14; c < 21; c++) pinMode(c, OUTPUT);
  for (c = 22; c < 54; c+=2) pinMode(c, OUTPUT);
  for (c = 62; c < 70; c++) pinMode(c, OUTPUT);

void clear_lcd(void) {
  for (d = 14; d < 70; d++) digitalWrite(d, LOW);
}

void LCD_refresh(void) {  //toggle all LCD pins
  for (r = 14; r < 21; r++) digitalWrite(r, !digitalRead(r));
  for (r = 22; r < 54; r += 2) digitalWrite(r, !digitalRead(r));
  for (r = 62; r < 70; r++) digitalWrite(r, !digitalRead(r));
}

Each digit has its own array of outputs for each character to display:

byte lcd1[13][7] = {
  //array of outputs for segments of LCD1 for values 0-9, u & d
  //outputs for segments A-G, skip if 0
  { 26, 28, 17, 16, 15, 24, 0 },   //0, ABCDEF
  { 0, 28, 17, 0, 0, 0, 0 },       //1, BC
  { 26, 28, 0, 16, 15, 0, 22 },    //2, ABDEG
  { 26, 28, 17, 16, 0, 0, 22 },    //3, ABCDG
  { 0, 28, 17, 0, 0, 24, 22 },     //4, BCFG
  { 26, 0, 17, 16, 0, 24, 22 },    //5, ACDFG
  { 26, 0, 17, 16, 15, 24, 22 },   //6, CDEFG
  { 26, 28, 17, 0, 0, 0, 0 },      //7, ABC
  { 26, 28, 17, 16, 15, 24, 22 },  //8, ABCDEFG
  { 26, 28, 17, 0, 0, 24, 22 },    //9, ABCFG
  { 0, 0, 0, 0, 0, 0, 0 },         //10, blank
  { 0, 0, 17, 16, 15, 0, 0 },      //u, CDE
  { 0, 28, 17, 16, 15, 0, 22 },    //d, BCDEG
};

When writing to the LCD, I first clear it, send the data to display, then set the BP pin LOW.

void lcd_1(void) {  //0-9, 10 = u, 11 = d
  for (c = 0; c < 7; c++)
    if (lcd1[lcd_digit][c] > 0) digitalWrite(lcd1[lcd_digit][c], HIGH);
  digitalWrite(14, LOW);  //BP
}

Easy when you know how!

`

2 Likes

That’s nice and compact! Nice looking LCD too!

I have a neighbour who's 92 & has vision problems. He spends 30 minutes a day on his bike on rollers in his garage. He had trouble reading the bike's display, then got a Garmin GPS with bigger characters. Still nogo.
My idea was to show him what was possible with characters at least twice as big (also to have fun learning something new). Still nogo! Black on light gray was difficult.
He said red is better, so I have just ordered some 4" 7-segment red led modules. If that doesn't work, I'll give up!
What to do with the LCD? I already have a 12-digit 7-segment DCF77 clock above my desk, so not another clock.