This library provides an interface between the microcontroller and LCD module. Note that this code is not for I2C modules.
Main features:
- Supports 16x1, 16x2, 16x4, 20x4, 20x2, 32x2, 40x2 LCD display modules
- Option for automatically wrapping the text to a new line
- Numbers can be padded with zeros to maintain user interface layout
- Scrolling a string of characters
- Includes two types of big digits numerical fonts for making a clock
- Has support for user defined fonts and other special fonts included by default in the LCD memory
- Support for 8 and 4 bit mode interface
- LCD backlight dimming or on/off control using PWM
Contents:
- Hardware interfacing
- Software interfacing
- Printing strings on the LCD display
- Printing numbers
- Displaying float numbers
- Clearing the LCD display
- Moving the LCD cursor
- Scrolling a string of characters from right to left
- Controlling the LCD backlight brightness using PWM
- Custom LCD digits - 3 characters wide digits
- Custom LCD digits - sharp digits
- Print special characters located inside the HD44780 LCD memory
- Using custom LCD symbols
- Download LCD Library
Hardware interfacing ATmega328 AVR microcontroller with a 16x2 LCD module with PWM brightness control
This LCD modules can be connected in 4 bit mode or 8 bit mode. Using 4 bit mode is recommended because it uses less pins but the code is a bit more complex. In the following example I use 4 bit mode.
LCD pins:
1 – VSS: power supply (GND)
2 – VDD: power supply (+5v)
3 – Vo or VEE on other schematics: contrast adjust. I have used a 5k
variable resistor for adjusting the contrast. If you power up the LCD and
don’t see anything, first check the contrast. The characters could be
displayed but you can’t see them.
4 – RS (Register Select):
set this LOW for sending instructions to LCD or HIGH for sending data
(characters).
5 – R/W (Read/Write):
set this to LOW to write to LCD module or HIGH to read from LCD.
6 – E (Enable Signal):
signals to LCD that it can process the commands.
7 – 14 – DB0…7 (Data Bus Lines):
used for sending binary data.
15 – A (Anode): power supply (+5v) for LCD backlight.
16 – K (Cathode): power supply (GND) for LCD backlight. If you don’t
want to control the LCD backlight brightness using PWM, then connect this
pin to ground through a 100 ohm resistor. Some LCD modules have a current
limiting resistors some don’t. It doesn’t hurt if you put one.
Software interfacing AVR ATmega328 microcontroller with an LCD module
STEP 1 - I/O and LCD Type Configuration:
First you need to tell the library what pins did you chose to connect the LCD and what type of LCD module do you use. This is easily done by opening the library file and in the setup section do the necessary modifications.
Many features can be disabled if not needed by setting the defines to FALSE thus reducing the code size. No point in including support for floating numbers or text scrolling if they will not be used anyway.
The DDR's, PORT's and pins are just as an example. Replace them according to your circuit setup.
/************************************************************* USER SETUP SECTION **************************************************************/ #define LCD_DATA_DDR DDRD // Data bus (DB0 to DB7 on LCD pins) #define LCD_DATA_PORT PORTD #define LCD_DATA_PIN PIND // Used to check busy flag // In 8-bit mode pins 0-7 will be used so set this to 0 // In 4-bit mode put here the lowest pin number of the LCD_DATA_PORT // For example if the 4 data pins are on pins 0 to 3 LCD_DATA_START_PIN is 0 // Or if the 4 data pins are on pins 4, 5, 6, 7 LCD_DATA_START_PIN is 4 #define LCD_DATA_START_PIN 0 // Register selection signal - RS #define LCD_RS_CONTROL_DDR DDRD #define LCD_RS_CONTROL_PORT PORTD #define LCD_RS_PIN PD6 // Read/write signal - RW #define LCD_RW_CONTROL_DDR DDRD #define LCD_RW_CONTROL_PORT PORTD #define LCD_RW_PIN PD5 // Enable signal - E #define LCD_E_CONTROL_DDR DDRD #define LCD_E_CONTROL_PORT PORTD #define LCD_E_PIN PD4 // LCD type #define LCD_NR_OF_CHARACTERS 16 // e.g 16 if LCD is 16x2 type #define LCD_NR_OF_ROWS 2 // e.g 2 if LCD is 16x2 type // MCU bits #define PORT_SIZE 8 // 8 bit microcontroller // Backlight brightness control using PWM // TRUE or FALSE - TRUE only if you want to dim the backlight using
// PWM or turn it on/off and the OC0B pin in available for this use #define LCD_BACKLIGHT_PWM FALSE #define LCD_PWM_DDR DDRD // OC0B DDR for backlight brightness control #define LCD_PWM_PORT PORTD #define LCD_PWM_PIN PD5 // OC0B pin for backlight brightness control // Select 4 or 8 bit mode - 4 bit mode is preffered since only
// 4 pins are needed for the data port instead of 8 #define LCD_DATA_4_BITS 4 #define LCD_DATA_8_BITS 8 #define LCD_DATA_BUS_SIZE LCD_DATA_4_BITS // LCD_DATA_4_BITS or LCD_DATA_4_BITS // Text wrap - If the text length is greater than the numbers of LCD characters // the cursor will be set on the beginning of the next line #define LCD_WRAP_TEXT TRUE // TRUE or FALSE // To be able to use LCDWriteFloat(), make this equal to 1. Enabling this, will load // the math library and will add over a KB to the program size. If you are not planning to // display floats, then make this equal to 0. If you are using it, then uncomment MATH_LIB = -lm // in your Make file to reduce the loaded math library to half. #define LCD_DISPLAY_FLOATS FALSE // TRUE or FALSE // Use of custom characters. BIG_DIGITS_1_CHARACTERS and BIG_DIGITS_3_CHARACTERS must be FALSE #define LCD_CUSTOM_CHARS FALSE // TRUE or FALSE // Use of big double height digits. CUSTOM_CHARS must be FALSE // Only one type of big digits can be TRUE #define BIG_DIGITS_1_CHARACTERS FALSE // TRUE or FALSE - double height 1 character wide font #define BIG_DIGITS_3_CHARACTERS FALSE // TRUE or FALSE - double height 3 character wide font
SETP 2 - Initialization:
Run the setup function inside the main() function
LCDSetup(cursorStyle);
cursorStyle can be: LCD_CURSOR_BLINK, LCD_CURSOR_ULINE,
LCD_CURSOR_NONE
Printing strings on the LCD display
LCDWriteString(“Text on LCD”);
After writing something on the display, the cursor will be automatically incremented. At first run the cursor is at the first character and after every character displayed (space included) is incremented. If there is no more space on the LCD and #define LCD_WRAP_TEXT is set to TRUE then the characters will be printed on the next line provided that the LCD display has more than 1 line.
To print a string starting at a certain location use
LCDWriteStringXY(3, 2, “Text on LCD”);
The text will be printed starting from character 3 on line 2 hence the X Y naming.
Printing numbers on the display
LCDWriteInt(number, number_of_digits);First parameter is the number (int32_t) and if negative the minus sign will be displayed in front of it.
The second parameter number_of_digits is how many digits are desired. If the number has less digits than this second parameter then it will be padded with zeros. This is useful when having a user interface and you want the layout to be consistent regardless of how many digits the number has. Example:
LCDWriteInt(120, 3) will print 120 because 120 has 3 digits so no padding
LCDWriteInt(12, 3) will print 012 as to maintain 3 digits
LCDWriteInt(12, 5) will print 00012
LCDWriteInt(120, 2) will have no effect because 2 is less than 3 digits that 120 is made of
If number_of_digits is 0 then the padding is ignored.
To print a number at a certain position use
LCDWriteIntXY(5, 1, 120, 3);The number 120 will be printed starting from character 5 on line 1 hence the X Y naming.
Displaying float numbers on the LCD
LCDWriteFloat(float_number, number_of_digits, number_of_decimals)
For numbers with decimals another function is needed. In order to include this function set LCD_DISPLAY_FLOATS to TRUE.
The parameter number_of_digits has the same function like in the LCDWriteInt() function.
number_of_decimals is used to trim the decimals (how many digits
after the dot). For example instead of printing pi 3.14159265359 setting
number_of_decimals to 4 will print 3.1415
Here are a few examples with different float numbers and their output on the display:
LCDWriteFloat(0.00271234, 0, 5) 0.00271 notice that the number was trimmed to 5 decimals
LCDWriteFloat(-22.00271234, 3, 5) -022.00271 again the number has 5 decimals and 22 is padded with 1 zero to have 3 digits given by the second parameter.
Clearing the LCD display
LCDClear();
This function will clear the display. However if the characters are not many it is much faster and efficient to replace them with spaces or overwrite them by moving the cursor to that position and sending new data. For example to display a counter in a loop without using the clear function one could do:
int main(void){ uint8_t i = 0; while(1){ for(i = 0; i < 255; i++){ LCDWriteIntXY(1, 1, i, 3); _delay_ms(100); } } }
This will display 001, 002, 003... at the same location.
Moving the LCD cursor
Go to character 1, line 1
LCDHome();Move the cursor to a specific location
LCDGotoXY(character_position, row_number)
Scrolling a string of characters from right to left
LCDScrollText("A long text that doesn't fit on the LCD display and must be scrolled");
To include this function, LCD_ANIMATIONS must be set to TRUE. The
scroll speed can be changed using LCD_SCROLL_SPEED define. Check the
included video to see this function in action. Looks better in person
than on camera.
Controlling the LCD backlight brightness using PWM
LCDBacklightPWM(brightness)
LCD_BACKLIGHT_PWM must be set TRUE for this function to be included
This function dims the LCD backlight using Timer0 in fast PWM mode using
OC0B pin and OCR0A as TOP. Frequency is set to 400 to prevent flickering.
Circuit: a small signal transistor can be used with emitter connected to
ground. Connect OC0B pin to the base of transistor through a resistor.
Connect LCD backlight anode to Vcc and cathode to collector. See the
schematic at the top of this page. If the LCD backlight LED takes 20mA or
less then the transistor can be omitted since an AVR microcontroller can
sink 20mA provided that all the pins of the microcontroller are not sourcing
more than 200mA.
Custom LCD digits - 3 characters wide digits:
LCDWriteIntBig3Chars(number, nrOfDigits);
BIG_DIGITS_3_CHARACTERS must be set to TRUE to use this function.
BIG_DIGITS_1_CHARACTERS and LCD_CUSTOM_CHARS must be both FALSE. The file "double_height_3_characters_round_digits.h" will be included automatically so be sure to download and put it in the same folder.
The parameter number_of_digits has the same function like in the
LCDWriteInt() function.
#include <avr/io.h>; #include <util/delay.h>; #include "OnLCDLib.h" int main(void){ uint8_t seconds = 0; uint8_t minutes = 0; LCDSetup(LCD_CURSOR_NONE); while(1){ if(seconds > 59){ minutes += 1; seconds = 0; if(minutes > 59) minutes = 0; } LCDHome(); LCDWriteIntBig3Chars(minutes, 2); LCDWriteBigSeparator(); LCDWriteIntBig3Chars(seconds, 2); seconds += 1; _delay_ms(1000); } }
Custom LCD digits - sharp digits:
LCDWriteIntBig(number, nrOfDigits);
BIG_DIGITS_1_CHARACTERS must be set to TRUE to use this function.
BIG_DIGITS_3_CHARACTERS and LCD_CUSTOM_CHARS must be both FALSE. The file "double_height_sharp_digits.h" will be included automatically so be sure to download and put it in the same folder.
The parameter number_of_digits has the same function like in the LCDWriteInt() function.
Print special characters located inside the HD44780 LCD memory
Apart from the regular alphanumerical characters and punctuation signs, the HD44780 controller has few other special characters inside it's memory.
Suppose you want to display the omega (ohm) symbol. The address can be found by combining the upper 4 bits (column) with the lower 4 bits (row). In this example the memory address for the omega symbol is: 0b11110100. The memory address for the pi symbol would be 0b11110111and so on. The following function can be used for displaying the symbols:
LCDPrintExtraChar(char_address);
char_address can also be one of the following defines:
#define LCD_SPECIAL_SYMBOL_DEGREE 0b11011111 #define LCD_SPECIAL_SYMBOL_ARROW_RIGHT 0b01111110 #define LCD_SPECIAL_SYMBOL_ARROW_LEFT 0b01111111 #define LCD_SPECIAL_SYMBOL_DIVIDE 0b11111101 #define LCD_SPECIAL_SYMBOL_OHM 0b11110100 #define LCD_SPECIAL_SYMBOL_EPSILON 0b11110110 #define LCD_SPECIAL_SYMBOL_PI 0b11110111 #define LCD_SPECIAL_SYMBOL_MICRO 0b11100100 #define LCD_SPECIAL_SYMBOL_ALPHA 0b11100000 #define LCD_SPECIAL_SYMBOL_BETA 0b11100010
Using custom LCD symbols
HD44780 LCD controller has room for 8 user defined characters. There are many LCD custom character generators online that can be used to build your own symbols for the LCD. For example you could try https://maxpromer.github.io/LCD-Character-Creator then copy the generated code inside the array to the following array in the library
static const uint8_t LCD_custom_chars[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, //Char0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x1F, //Char1 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x1F, //Char2 0x00, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x1F, 0x1F, //Char3 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, //Char4 0x00, 0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, //Char5 0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, //Char6 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, //Char7 };
Each row inside the array represents 1 character so there are 8 characters from 0 to 7. Notice that this array has by default 0x1F values that can print on the LCD something like battery level or volume.
The default 8 symbols included in LCD_custom_chars array. They can be used for volume or battery level |
Of course not all symbols must be displayed at once but this is just a demonstration. They could also be animated inside a loop to indicate that a battery is charging.
LCD_CUSTOM_CHARS must be TRUE and both BIG_DIGITS_1_CHARACTERS and BIG_DIGITS_3_CHARACTERS must be FALSE for this to work.
The function for printing user defined symbols is
LCDPrintCustomChar(char_index);char_index - is the index of the character inside the LCD_custom_chars[] array from 0 to 7.
Under the LCD_custom_chars[] array there are also some defines that the user can rename depending on the custom symbols and can be passed to the above function. By default the defines are named as follows
// This defines can be renamed and used as a parameter for LCDPrintCustomChar function #define BATTERY_LEVEL_1 0 // these indicates an index for LCD_custom_chars array #define BATTERY_LEVEL_2 1 #define BATTERY_LEVEL_3 2 #define BATTERY_LEVEL_4 3 #define BATTERY_LEVEL_5 4 #define BATTERY_LEVEL_6 5 #define BATTERY_LEVEL_7 6 #define BATTERY_LEVEL_8 7
and they can be used like so: LCDPrintCustomChar(BATTERY_LEVEL_8) will display the full bar symbol.
Download LCD Library:
Version 2.2Download big digits LCD characters:
Other resources:
If you want to learn more in depth about LCDs and their protocols used to communicate with a microcontroller I've provided some links bellow.
https://en.wikipedia.org/wiki/Hitachi_HD44780_LCD_controller
https://www.8051projects.net/lcd-interfacing/introduction.php
https://www.engineersgarage.com/knowledge_share/making-custom-characters-on-16x2-lcd