Introduction
LCD displays are mostly used in Arduino-based systems to show output of your application. They are cheap, widely-available, easy-to-use. Therefore, you can find many kinds of LCD display on the market. One drawback is that it occupies to many pins on your Arduino board. As a solution, serial LCD displays has emerged, thanks to kindhearted manufacturers :)
Background
Arduino IDE comes with a LiquidCrystal
library ([3]) for Hitachi-HD44780-based LCDs ([4]). You command the controller by selecting & writing registers according to your needs. In 4-bit usage, you need 6 digital pin of your Arduino out of 14. What a waste.
Figure 1: A 2x16 LCD
Some functionality provided by Hitachi-based controller:
- Positioning cursor
- Setting cursor type (hide, underline, blink)
- Scrolling display (to-left, to-right)
- Display on/off
- Clear screen
LiquidCrystal
library exposes those functions by commanding Hitachi controller. Dedicated pins are specified in the constructor.
Let's see an example:
#include <Arduino.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12,11,2,3,4,5);
void setup() {
lcd.begin(16,2);
lcd.print("that is a test");
delay(5000);
lcd.clear();
lcd.setCursor(1,1);
}
void loop() {
while(1);
}
A LiquidCrystal
instance (lcd
) is created with a given pin configuration at the beginning of the code. The library uses this pin configuration to send commands and data to LCD.
LiquidCrystal lcd(12,11,2,3,4,5);
In setup
function, LCD is initialized with its columns & rows count. The library uses this information when it sets cursor position.
lcd.begin(16,2);
For example, if you want to move cursor to second column of second row, target cursor position is decided by using values provided in begin
function. The next print
will start from that position.
lcd.setCursor(1,1);
I hope this sample is enough to see how to use an LCD with LiquidCrystal
library.
Serial LCD
Now, it is time to talk about serial LCD. When it comes to serial LCD, those low-level driving issues are diverted to another microcontroller. You command this microcontroller, and it commands LCD (Hitachi controller). To do this, it provides you a serial interface with an only one line. Information flow is one-way, from Arduino to serial LCD, therefore it needs only one digital pin of your Arduino. You use only the transmit line of HardwareSerial
([7]) or SoftwareSerial
([8]) to communicate with your serial LCD.
Figure 2: A serial LCD (connected with SoftwareSerial
)
What I have done in this library is to implement communication protocol ([2]) with my specific serial LCD ([1]). My implementation has similar functionality with LiquidCrystal
as you may expect since both are based on Hitachi controller.
Commands to serial LCD are composed of 1-byte directives with parameters. To drive serial LCD, you send those commands over serial transmit.
Command: Set backlight
Command byte: 0x80
Parameter: 0x00-0xFF (0-255). corresponds to brightness between 0%-100%
Command: Set baudrate (after reset), default 9600
Command byte: 0x81
Parameters: 0x00-0x0A (0-10), corresponding values 300, 1200, 2400, 2400, 9600, 14400, 19200, 28800, 38400, 57600, 115200
Following commands control LCD's behavior and all are preceded by 0xFE command byte.
Command: Clear screen, set cursor position to 0
Command byte: 0x01 (no parameter)
Command: Move cursor right
Command byte: 0x14 (no parameter)
Command: Move cursor left
Command byte: 0x10 (no parameter)
Command: Scroll display right
Command byte: 0x1C (no parameter)
Command: Display off (backlight doesn't change)
Command byte: 0x08 (no parameter)
Command: Display on
Command byte: 0x0C (no parameter)
Command: Underline cursor
Command byte: 0x0E (no parameter)
Command: Blink cursor
Command byte: 0x0D (no parameter)
Command: Cursor off (if display is ON)
Command byte: 0x0C (no parameter)
Command: Set cursor position
Command byte: 0x80
Parameter: 1-byte position index, starting from 0 (top-left)
Command: Toggle splash screen
Command byte: 0x1E (no parameter)
(I had developed another library ([9]) for serial LCD, but it is not good enough in terms of design & functionality. I prefer to count it as an experimental study.)
Using the code
Here is the class declaration of SerialLcd2
:
class SerialLcd2: public Print {
protected:
Stream* _Stream;
public:
SerialLcd2(Stream* stream): _Stream(stream) { };
size_t write(uint8_t b) { return this->_Stream->write(b); };
void printLine() { this->write(SL_CHR_LF); };
void del() { this->write(SL_CHR_BCK); };
void moveCursor(uint8_t cmd);
void showCursor(uint8_t cmd);
void setCursorPos(uint8_t pos);
void clr();
void setBacklight(uint8_t b);
void scrollScreen(uint8_t cmd);
void displayOn(bool);
void toggleSplash();
void setBaudRate(uint8_t);
};
What I did here is to implement serial communication protocol and some other handy functions.
Let's see an example.
SerialLcd2* lcd;
Stream* s;
To connect via hardware serial (Serial), we first need to initialize serial port of Arduino. I haven't changed my LCD's baud rate, therefore baud rate of the connection will be 9600 bps.
Serial.begin(9600);
s = &Serial;
If we want to connect with SoftwareSerial
, similar initialization is needed except we have to reserve pins for SoftwareSerial
.
SoftwareSerial ss(2,3);
ss.begin(9600);
s = &ss;
Then create a SerialLcd2
instance with its serial Stream
([6]).
lcd = new SerialLcd2(s);
Caution! We should connect Stream
's transmit line to the LCD's receive line, which is its only communication line.
Now, we are ready to use it.
First of all, we can print anything as Print
class ([5]) does without any further processing. This is accomplished by overriding virtual write
function of Print
class since Print
class uses it in all print versions.
lcd->print(1);
lcd->print('A');
lcd->print(0.1);
lcd->print("string");
lcd->print(F("flash string"));
Don't use println
, because it doesn't move the cursor to the start of the next line as you may expect. Instead send '\n' directly or use printLine()
function. Then, the cursor goes to the next line, or to the first if it is on the last line of LCD.
lcd->print('\n');
lcd->printLine();
One good thing, you can delete last printed character as if you press backspace.
lcd->del();
The rest is about controlling LCD as in LiquidCrystal
library of Arduino.
lcd->clr();
lcd->setBacklight(0);
lcd->setBacklight(0xFF);
lcd->scrollScreen(SL_CMD_SCROLL_R);
lcd->scrollScreen(SL_CMD_SCROLL_L);
lcd->displayOn(false);
lcd->displayOn(true);
lcd->showCursor(SL_CMD_CURSOR_OFF);
lcd->showCursor(SL_CMD_CURSOR_UNDERLINE);
lcd->showCursor(SL_CMD_CURSOR_BLINK);
lcd->setCursorPos(0);
lcd->setCursorPos(16);
lcd->moveCursor(SL_CMD_CURSOR_MV_R);
lcd->moveCursor(SL_CMD_CURSOR_MV_L);
lcd->setBaudRate(4);
lcd->toggleSplash();
Some points to mention:
- In the serial command of LCD, setting display on/off doesn't have any effect on backlight. I have changed this behavior in my implementation. If you call
displayOn(false)
, backlight is also turned off, and vice versa. - Setting cursor position is different from the
LiquidCrystal
library. You pass [column,row] values to LiquidCrystal::setCursor
function, and pass [offset-from-topleft] to SerialLcd2::setCursorPos
. - Baud rate and splash screen toggling don't exist in
LiquidCrystal
You can find an example application for the usage of the library in the download section.
Points of Interest
The good thing with this library is that it derives from Print
class. Therefore it has the power of Print
class. You can print anything as you do with Print, such as formatted numbers or strings from flash memory of Arduino.
Another thing is that it uses HardwareSerial
or SoftwareSerial
without any difference since it sees both as Stream
. Therefore, you can connect receive line of serial LCD to any pin which is configured as serial transmit.
Behind the scenes, serial LCD kit makes use of LiquidCrystal library in its firmware ([10]). This is achieved by the same techniques used in an Arduino board. Serial kit has a ATmega328 MCU and Arduino bootloader is uploaded on it. Therefore, it is possible to upload any code with its serial programming interface. The firmare is just another Arduino code which can be thought as proxy between your Arduino and LCD. You can even change the communication protocol of serial LCD and create your very own. Enjoy!
If you have any idea/suggestion regarding to this implementation, I will happy to hear them.
Reference
[1] Serial LCD kit from Cooking-Hacks.com
[2] Serial communication with serial LCD
[3] Arduino LiquidCrystal library
[4] Hitachi LCD controller
[5] Arduino Print class
[6] Arduino Stream class
[7] Arduino Serial
[8] Arduino SoftwareSerial
[9] Another implementation of mine for SerialLcd
[10] Firmware of serial LCD kit