The circuits presented today allow the control of a LCD display that has a very common chip to be controlled by a micro-controller HD44780 or one that is compatible with this one.
There are several formats for this LCD, however the most used is the 16x2 (16 columns with 2 lines). There are also several colors for the LCDs.
Being an LCD display that receives the information that will present in serial format, this uses a very significant number of pins of a micro-controller.
The HD44780 is a module that exposes the following pins:
Pin | Name | Description |
---|---|---|
1 | VSS | Power (GND) |
2 | VCC | Power (+5V) |
3 | VEE | Contrast Adjustment |
4 | RS | 0 = instruction input, 1 = data input |
5 | R/W | 0 = write to the module, 1 - read from the module |
6 | EN | Enable Signal |
7 | D0 | Data bus line 0 (LSB) |
8 | D1 | Data bus line 1 |
9 | D2 | Data bus line 2 |
10 | D3 | Data bus line 3 |
11 | D4 | Data bus line 4 |
12 | D5 | Data bus line 5 |
13 | D6 | Data bus line 6 |
14 | D7 | Data bus line 7 (MSB) |
15 | A | LED Backlight + |
16 | K | LED Backlight - |
To send data or commands to the module, follow these steps:
- Set the Enable to HIGH
- Set RS and D0-D7 to the desired values.
- Set the Enable to LOW
It is necessary to ensure that there is a timing of about 37 usec between the described operations.
The module has two modes of operation:
- 8-bits mode
- 4-bits mode
The first uses the 8 data pins and the second only 4 of them (D4 to D7).
In the circuits presented we will use the 4-bit mode using 6 pins (4 pins for the data, one for the Enable and another for the Instruction type). This saves 4 pins (D0 to D3 are not used) and the data is sent in two steps (two nibbles).
The HD44780 supports a set of commands that are documented in its datasheet, however as we will use a library it is not necessary to know in detail these Commands.
To decrease the number of pins used we will implement the communication via I2C or TWI. The second circuit presented has an Integrated - PCF8574 - which implements the I2C interface and allows the sending of commands to the HD44780 via this protocol. This uses only 2 pins of the I2C protocol (SDA, SCL) plus the 5V and GND.
In this circuit we will use 6 of the 8 pins that the PCF8574 makes available to us. The same 6 that were needed in the first circuit.
When we are using an Integrated I2C it allows you to configure the address by pins. The PCF8574 has 3 pins for this purpose A0 to A2.
These must be connected to GND or 5V (with a pull-up) to set the desired address. In our case they were all connected to GND. It gets integrated with the address 0x20.
Pin combination:
A2 | A1 | A0 | I2C Address |
---|---|---|---|
L | L | L | 0x20 |
L | L | H | 0x21 |
L | H | L | 0x22 |
L | H | H | 0x23 |
H | L | L | 0x24 |
H | L | H | 0x25 |
H | H | L | 0x26 |
H | H | H | 0x27 |
As shown on Malpartida’s site, the performance of the I2C interface is much lower than having the HD44780 directly connected to the AVR, but when it is intended porpuse is just showing information the pin savings may be required.
Schematic
Bill of material (BOM)
Circuit 1:
- 1x LCD (DS1)
- 1x 330 Ohms Resistor (R1)
- 1x 10K Variable Resistor (RV1)
Circuit 2:
- 1x LCD (DS2)
- 1x 330 Ohms Resistor (R2)
- 1x 10K Variable Resistor (RV2)
- 1x PCF8574 (U1)
IC Pin-out
Sketch to run in CSEduino / Arduino
The code shown uses an external library that can be downloaded from this link, or locally on this link and it has to be installed in the Arduino IDE.
This library replaces the Arduino library including support for I2C communication among other things.
Code for the 4-bit serial connection:
#include <Wire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// Creat a set of new characters
byte smiley[8] = {
0b00000,
0b00000,
0b01010,
0b00000,
0b00000,
0b10001,
0b01110,
0b00000
};
byte armsUp[8] = {
0b00100,
0b01010,
0b00100,
0b10101,
0b01110,
0b00100,
0b00100,
0b01010
};
byte frownie[8] = {
0b00000,
0b00000,
0b01010,
0b00000,
0b00000,
0b00000,
0b01110,
0b10001
};
void setup()
{
lcd.begin(16,2); // initialize the lcd
lcd.createChar (0, smiley); // load character to the LCD
lcd.createChar (1, armsUp); // load character to the LCD
lcd.createChar (2, frownie); // load character to the LCD
lcd.home (); // go home
lcd.print("Hello, CSEduino ");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print (" FORUM - fm ");
}
void loop()
{
// Do a little animation by writing to the same location
lcd.setCursor ( 14, 1 );
lcd.print (char(2));
delay (200);
lcd.setCursor ( 14, 1 );
lcd.print ( char(0));
delay (200);
}
//Sketch uses 4,048 bytes (12%) of program storage space. Maximum is 32,256 bytes.
// Global variables use 327 bytes (15%) of dynamic memory, leaving 1,721 bytes for local variables. Maximum is 2,048 bytes.
Code for 4-bit connection over I2C:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define BACKLIGHT_PIN 13
LiquidCrystal_I2C lcd(0x20, BACKLIGHT_PIN, POSITIVE); // Set the LCD I2C address
// Creat a set of new characters
const uint8_t charBitmap[][8] = {
{ 0xc, 0x12, 0x12, 0xc, 0, 0, 0, 0 },
{ 0x6, 0x9, 0x9, 0x6, 0, 0, 0, 0 },
{ 0x0, 0x6, 0x9, 0x9, 0x6, 0, 0, 0x0 },
{ 0x0, 0xc, 0x12, 0x12, 0xc, 0, 0, 0x0 },
{ 0x0, 0x0, 0xc, 0x12, 0x12, 0xc, 0, 0x0 },
{ 0x0, 0x0, 0x6, 0x9, 0x9, 0x6, 0, 0x0 },
{ 0x0, 0x0, 0x0, 0x6, 0x9, 0x9, 0x6, 0x0 },
{ 0x0, 0x0, 0x0, 0xc, 0x12, 0x12, 0xc, 0x0 }
};
void setup()
{
int charBitmapSize = (sizeof(charBitmap ) / sizeof (charBitmap[0]));
// Switch on the backlight
pinMode ( BACKLIGHT_PIN, OUTPUT );
digitalWrite ( BACKLIGHT_PIN, HIGH );
lcd.begin(16,2); // initialize the lcd
for ( int i = 0; i < charBitmapSize; i++ )
{
lcd.createChar ( i, (uint8_t *)charBitmap[i] );
}
lcd.home (); // go home
lcd.print("Hello, Altlab");
lcd.setCursor ( 0, 1 ); // go to the next line
lcd.print("CSEduino LCD i2C");
delay ( 10000 );
}
void loop()
{
lcd.home ();
// Do a little animation by writing to the same location
for ( int i = 0; i < 2; i++ )
{
for ( int j = 0; j < 16; j++ )
{
lcd.print (char(random(7)));
}
lcd.setCursor ( 0, 1 );
}
delay (200);
}
// Sketch uses 4,784 bytes (14%) of program storage space. Maximum is 32,256 bytes.
// Global variables use 374 bytes (18%) of dynamic memory, leaving 1,674 bytes for local variables. Maximum is 2,048 bytes.