Liquid Crystal Displays (LCDs) are great for creating impressive (and sometimes, useful!) microcontroller projects. The 16 characters / 2 lines display is very cheap and directly supported by the arduino LiquidCrystal library (also works in MSP430 with Energia).
In this post we implement similar functionality for the Tiva C series and Stellaris launchpads. This version of the library works in CCS and can be used with Stellaris LM4F120, TivaC TM4C123G and Tiva C Connected TM4C1294 launchpads with minimal changes.
You may download the complete project from my github page.
The DATA_PORT_BASE points to Port D which is configured for the data bus. CMD_PORT_BASE points to Port E, configured for the RS and EN signals.
D7 D6 D5 D4
Here is the relevant code in initLCD:
void initLCD(void)
{
SysCtlPeripheralEnable(DATA_PERIPH);
SysCtlPeripheralEnable(CMD_PERIPH);
GPIOPinTypeGPIOOutput(DATA_PORT_BASE, ALLDATAPINS);
GPIOPinTypeGPIOOutput(CMD_PORT_BASE, ALLCONTROLPINS);
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS ,0);
GPIOPinWrite(CMD_PORT_BASE, ALLCONTROLPINS ,0);
SysCtlDelay(10000);
setCmd();
SysCtlDelay(15000);
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS, 0b0010);
pulseLCD();
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS, 0b0010);
pulseLCD();
After setting insert mode, the LCD is ready to accept characters for printing. The sendByte function sends a byte as a pair of nibbles:
void sendByte(char byteToSend, int isData)
{
if (isData)
setData();
else
setCmd();
SysCtlDelay(400);
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS, byteToSend >>4);
pulseLCD();
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS, byteToSend);
pulseLCD();
}
The high nibble is set first followed by the low nibble. The display is strobed to accept each nibble. RS is set accordingly for data (high) or command (low).
Timing is important: with the delays currently in the program, the LCD works fine when running at 25MHz frequency. If you need to run at the full 80MHz of the TivaC (or more for the TivaC Connected) you will need to introduce more SysCtlDelay statements or the display will fail to catch and garbled text will be shown!
Printing to the LCD is very simple: Just send the characters of your string one by one to the sendByte function:
void printLCD(char *text)
{
char *c;
c = text;
while ((c != 0) && (*c != 0))
{
sendByte(*c, TRUE);
c++;
}
}
The library implements a few more command of the HD44780 protocol. Here are a few interesting hex codes to try:
Function Definition Hex Code
Set 4 bit mode 0x0
Scroll one char right 0x1E
Scroll one char left 0x18
Goto Home position 0x02
Go one char left 0x10
Go one char right 0x14
Underline cursor on 0x0E
Blinking cursor 0x0F
Invisible cursor 0x0C
Blank display 0x08
Clear display 0x01
Set next position 0x80+Address
More details are available in the HD44780 datasheet.
Happy coding!
In this post we implement similar functionality for the Tiva C series and Stellaris launchpads. This version of the library works in CCS and can be used with Stellaris LM4F120, TivaC TM4C123G and Tiva C Connected TM4C1294 launchpads with minimal changes.
LCD library in action. Also current state of Greek economy :) |
You may download the complete project from my github page.
The HD44780 specification
LCDs that use the HD44780 drivers are commonly called COGs (Chip on Glass) as they contain all the necessary circuits to drive the LCD segments and communication with the host processor is generally limited to the following pins:- DB0 to DB7 Data bus. The bus is used to communicate commands or characters to be written to the screen, one byte at a time. To save MCU pins, the interface can be configured for 4 bit operation, where only the DB4 to DB7 lines are used.
- An RS (Register Select) pin. Specifies whether the data bus is carrying a command to be executed or a character to be displayed. When RS is low, data on the bus represents a command, otherwise it represents a character.
- An EN pin. This is a strobe pin, It has to be pulsed Low-High-Low for the LCD to accept the data in the bus.
When using 4 bit mode, the above connections require 6 pins of your MCU.
The LCD display will also require additional pins to be connected to Vcc or GND, including:
- Power and ground for the LCD and HD44780 ICs.
- Power and ground for the LCD backlight (if applicable)
- Ground connection of the R/W pin (to enable writing to the LCD).
- A potentiometer between Vcc and GND to pin 5 for adjusting display contrast.
Here is the pinout used in my version of the LCD library for the TivaC launchpad. Pin assignment can be easily reconfigured using the appropriate #define statements in display.h file:
LCD pin TivaC
-------------------
DB4 PD0
DB5 PD1
DB6 PD2
DB7 PD3
RS PE4
EN PE5
The Code
Let's have a look at how the library works. It is based on the HD44780 spec and heavily influenced by a similar work for the MSP430 published in the Co-Random Thoughts blog. The display.h file defines some useful macros to represent the pins and ports we use:
#define RS GPIO_PIN_4 // Energia Pin 5
#define EN GPIO_PIN_5 // Energia Pin 6
#define D4 GPIO_PIN_0 // Energia Pin 23
#define D5 GPIO_PIN_1 // Energia Pin 24
#define D6 GPIO_PIN_2 // Energia Pin 25
#define D7 GPIO_PIN_3 // Energia Pin 26
#define ALLDATAPINS D7 | D6 | D5 | D4
#define ALLCONTROLPINS RS | EN
#define DATA_PORT_BASE GPIO_PORTD_BASE
#define CMD_PORT_BASE GPIO_PORTE_BASE
#define DATA_PERIPH SYSCTL_PERIPH_GPIOD
#define CMD_PERIPH SYSCTL_PERIPH_GPIOE
The DATA_PORT_BASE points to Port D which is configured for the data bus. CMD_PORT_BASE points to Port E, configured for the RS and EN signals.
To start up the LCD in 4 bit data mode, we have to send the following command to the D4-D7 pins twice:
D7 D6 D5 D4
0 0 1 0
For this, we need to pull RS low, send the command, strobe the LCD via the EN pin, wait a bit and repeat. After this step the LCD is switched to 4 bit mode. All subsequent 8bit commands / characters are sent as a pair of nibbles.
Here is the relevant code in initLCD:
void initLCD(void)
{
SysCtlPeripheralEnable(DATA_PERIPH);
SysCtlPeripheralEnable(CMD_PERIPH);
GPIOPinTypeGPIOOutput(DATA_PORT_BASE, ALLDATAPINS);
GPIOPinTypeGPIOOutput(CMD_PORT_BASE, ALLCONTROLPINS);
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS ,0);
GPIOPinWrite(CMD_PORT_BASE, ALLCONTROLPINS ,0);
SysCtlDelay(10000);
setCmd();
SysCtlDelay(15000);
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS, 0b0010);
pulseLCD();
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS, 0b0010);
pulseLCD();
PulseLCD is a simple function that pulses EN to Low-High-Low states:
void pulseLCD()
{
// Go Low -> High -> Low
GPIOPinWrite(CMD_PORT_BASE, EN, 0);
GPIOPinWrite(CMD_PORT_BASE, EN, EN);
GPIOPinWrite(CMD_PORT_BASE, EN, 0);
}
Similarly the setCmd() and setData() functions switch the RS pin to low / high respectively.
After successfully initializing the LCD to 4bit mode, we sent a few more commands:
sendByte(0x28,FALSE); // Set two lines
cursorOffLCD(); // Cursor invisible
sendByte(0x06, FALSE); // Set insert mode
void pulseLCD()
{
// Go Low -> High -> Low
GPIOPinWrite(CMD_PORT_BASE, EN, 0);
GPIOPinWrite(CMD_PORT_BASE, EN, EN);
GPIOPinWrite(CMD_PORT_BASE, EN, 0);
}
Similarly the setCmd() and setData() functions switch the RS pin to low / high respectively.
After successfully initializing the LCD to 4bit mode, we sent a few more commands:
sendByte(0x28,FALSE); // Set two lines
cursorOffLCD(); // Cursor invisible
sendByte(0x06, FALSE); // Set insert mode
After setting insert mode, the LCD is ready to accept characters for printing. The sendByte function sends a byte as a pair of nibbles:
void sendByte(char byteToSend, int isData)
{
if (isData)
setData();
else
setCmd();
SysCtlDelay(400);
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS, byteToSend >>4);
pulseLCD();
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS, byteToSend);
pulseLCD();
}
The high nibble is set first followed by the low nibble. The display is strobed to accept each nibble. RS is set accordingly for data (high) or command (low).
Timing is important: with the delays currently in the program, the LCD works fine when running at 25MHz frequency. If you need to run at the full 80MHz of the TivaC (or more for the TivaC Connected) you will need to introduce more SysCtlDelay statements or the display will fail to catch and garbled text will be shown!
Printing to the LCD is very simple: Just send the characters of your string one by one to the sendByte function:
void printLCD(char *text)
{
char *c;
c = text;
while ((c != 0) && (*c != 0))
{
sendByte(*c, TRUE);
c++;
}
}
The library implements a few more command of the HD44780 protocol. Here are a few interesting hex codes to try:
Function Definition Hex Code
Set 4 bit mode 0x0
Scroll one char right 0x1E
Scroll one char left 0x18
Goto Home position 0x02
Go one char left 0x10
Go one char right 0x14
Underline cursor on 0x0E
Blinking cursor 0x0F
Invisible cursor 0x0C
Blank display 0x08
Clear display 0x01
Set next position 0x80+Address
More details are available in the HD44780 datasheet.
Happy coding!