Exploiting LCD refresh with Arduino

2023-03-08

I had an interesting idea to get around the 8 custom character limit on these
cheap LCDs based on the HD44780 by rapidly redefining the character set
while refreshing the display (every 27ms) in between each set.
I wanted to use millis() and play "At Doom's Gate", but I could never get it
to work correctly.
This was done with PlatformIO in VSCode.

Defining a character set

I created a separate 'DOOM.h' to define my custom characters for the DOOM logo.
It's also available on Pastebin.


/* LOGO */
byte top[8][8] = {
  // UL
  {
    B11111,
    B01111,
    B01111,
    B01110,
    B01110,
    B01110,
    B01110,
    B01110
  },
  // UR
  {
    B11100,
    B11110,
    B11111,
    B00111,
    B00111,
    B00111,
    B00111,
    B00111
  },
  // UL
  {
    B00001,
    B00011,
    B00111,
    B01110,
    B01110,
    B01110,
    B01110,
    B01111
  },
  // UR
  {
    B11000,
    B11100,
    B11110,
    B00111,
    B00111,
    B00111,
    B00111,
    B00111
  },
  // UL
  {
    B00011,
    B00111,
    B01111,
    B11100,
    B11100,
    B11100,
    B11100,
    B11100
  },
  // UR
  {
    B10000,
    B11000,
    B11100,
    B01110,
    B01110,
    B01110,
    B01110,
    B11110
  },
  // UL
  {
    B11000,
    B11000,
    B11100,
    B11100,
    B11110,
    B11111,
    B11111,
    B11111
  },
  // UR
  {
    B00000,
    B00111,
    B00111,
    B00111,
    B01111,
    B11111,
    B11111,
    B11111
  }

};

byte bottom[8][8] = {
    // LL
  {
    B01110,
    B01110,
    B01110,
    B01111,
    B01111,
    B01111,
    B01100,
    B01000
  },
  // LR
  {
    B00111,
    B01111,
    B11110,
    B11100,
    B11000,
    B10000,
    B00000,
    B00000
  },
  // LL
  {
    B01111,
    B01111,
    B00111,
    B00011,
    B00001,
    B00000,
    B00000,
    B00000
  },
  // LR
  {
    B00111,
    B10111,
    B11110,
    B11100,
    B11000,
    B10000,
    B00000,
    B00000
  },
  // LL
  {
    B11100,
    B11101,
    B01111,
    B00111,
    B00011,
    B00001,
    B00000,
    B00000
  },
  // LR
  {
    B11110,
    B11110,
    B11100,
    B11000,
    B10000,
    B00000,
    B00000,
    B00000
  },
   // LL
  {
    B11111,
    B11111,
    B11101,
    B11101,
    B01100,
    B00100,
    B00000,
    B00000
  },
  // LR
  {
    B11111,
    B11111,
    B10111,
    B10111,
    B00111,
    B00111,
    B00011,
    B00001
  }
};

Now the actual sketch

And the main Arduino sketch: also on Pastebin
This can be done with 4 lines instead of 8, but it may be better with 8.
It will still work nonetheless: tweaking the delays may help.


#include <Arduino.h>
#include <LiquidCrystal.h>
#include "DOOM.h"

#define LCD_RS    9
#define LCD_EN    8
#define LCD_D0   14 // A0
#define LCD_D1   15 // A1
#define LCD_D2    2
#define LCD_D3    3
#define LCD_D4    4
#define LCD_D5    5
#define LCD_D6    6
#define LCD_D7    7

#define LCD_W    16
#define LCD_H     2

#define EMPTY    "                "
#define STARTMSG "Hello, world!"

LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D0, LCD_D1, LCD_D2, LCD_D3, LCD_D4,
                  LCD_D5, LCD_D6, LCD_D7);

void drawDOOM_1();
void drawDOOM_2();

void setup() {
  lcd.begin(LCD_W, LCD_H);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(STARTMSG);
  delay(1500);
  lcd.clear();
}

void loop() {
  drawDOOM_1();
  delay(27);
  drawDOOM_2();
  delay(27);
}

void drawDOOM_1() {
  lcd.setCursor(4, 1);
  lcd.print("        ");
  lcd.createChar(0, bottom[0]);
  lcd.createChar(1, bottom[1]);
  lcd.createChar(2, bottom[2]);
  lcd.createChar(3, bottom[3]);
  lcd.createChar(4, bottom[4]);
  lcd.createChar(5, bottom[5]);
  lcd.createChar(6, bottom[6]);
  lcd.createChar(7, bottom[7]);

  lcd.setCursor(4, 1);
  lcd.write(byte(0));
  lcd.setCursor(5, 1);
  lcd.write(byte(1));
  lcd.setCursor(6, 1);
  lcd.write(byte(2));
  lcd.setCursor(7, 1);
  lcd.write(byte(3));
  lcd.setCursor(8, 1);
  lcd.write(byte(4));
  lcd.setCursor(9, 1);
  lcd.write(byte(5));
  lcd.setCursor(10, 1);
  lcd.write(byte(6));
  lcd.setCursor(11, 1);
  lcd.write(byte(7));
}

void drawDOOM_2() {
  lcd.setCursor(4, 0);
  lcd.print("        ");
  lcd.createChar(0, top[0]);
  lcd.createChar(1, top[1]);
  lcd.createChar(2, top[2]);
  lcd.createChar(3, top[3]);
  lcd.createChar(4, top[4]);
  lcd.createChar(5, top[5]);
  lcd.createChar(6, top[6]);
  lcd.createChar(7, top[7]);

  lcd.setCursor(4, 0);
  lcd.write(byte(0));
  lcd.setCursor(5, 0);
  lcd.write(byte(1));
  lcd.setCursor(6, 0);
  lcd.write(byte(2));
  lcd.setCursor(7, 0);
  lcd.write(byte(3));
  lcd.setCursor(8, 0);
  lcd.write(byte(4));
  lcd.setCursor(9, 0);
  lcd.write(byte(5));
  lcd.setCursor(10, 0);
  lcd.write(byte(6));
  lcd.setCursor(11, 0);
  lcd.write(byte(7));
}