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