Blame | Last modification | View Log | Download
/* ---------------------------------------------------------------------------
* AVR_MLIB - HD 44780 LCD Display Driver
* www.mlab.cz miho 2008
* ---------------------------------------------------------------------------
* LCD display driver for standard Hitachi 1/2/4 line character LCD modules
* for AVR processors. It uses 4 or 8 bit interface without readback.
* In the Examples section there is a demo application for this library.
* ---------------------------------------------------------------------------
* 00.00 2008/03/28 First Version
* ---------------------------------------------------------------------------
*/
// What should be set and done before here
// ---------------------------------------
//
// #include <stdio.h> // If you want to use printf, ...
//
// #define LCD_DATA B // 4 or 8 bits field (lsb bit of the port)
// #define LCD_DATA_BIT 4
//
// #define LCD_RS D // Register Select (port and bit)
// #define LCD_RS_BIT 4
//
// #define LCD_E D // Enable (port and bit)
// #define LCD_E_BIT 3
//
//
// // LCD Display Parameters
// #define LCD_INTERFACE_BITS 4 // 4 or 8 bit interface
// #define LCD_LINES 1 // 1 or 2 or 4 lines
// #define LCD_CHARS 20 // usualy 16 or 20, important for 4 line display only
//
// #include "lcd_hd44780.h" // Use LCD Library
//
//
// How to use the library
// ----------------------
//
// void lcd_init(void) // Init LCD Display
//
// void lcd_home() // Goto Home
//
// void lcd_clear() // Clear Display
//
// void lcd_clear_home() // Clear Display and Goto Home with no Cursor
//
// void lcd_cursor_on() // Switch Cursor On
//
// void lcd_cursor_off() // Switch Cursor Off
//
// void lcd_cursor_left() // Move Cursor Left
//
// void lcd_cursor_right() // Move Cursor Right
//
// void lcd_gotoxy(uint8_t x, uint8_t y) // Move to Position (1,1 is the first position)
//
// int lcd_putc(char c) // LCD Char Output
//
// int lcd_putc_stream(char c, FILE *unused) // LCD Char Output (for Stream Library)
//
//
// How to use printf
// -----------------
//
// 1) Define FILE structure
//
// static FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putc_stream, NULL, _FDEV_SETUP_WRITE);
//
// 2) Connect it with standard output
//
// stdout = &lcd_stream; // Connect stdout to LCD Stream
//
// 3) Use printf
//
// printf("\fHello World!\n------------");
//
// 4) Use special chars
//
// \f - clear display and goto home
// \n - goto the beginning of the next line
// \r - goto to the beginning of curent line
// \b - backspace
// \v - start and end definition of user defined char
//
//
// How to use User Defined symbols
// -------------------------------
//
// That is easy. Just print the definition to lcd. Look at the example
//
// printf("\v" "\x10" LCD_CHAR_BAT50 "\v"); // definition (redefines CGRAM content of the LCD)
// printf("Battery Status \x10"); // usage
//
// \v starts the definition
// \x10 first (of eight) user defined char
// LCD_CHAR_BAT50 half battery symbol, you can define more symbols here (up to 8)
// \v end of definition
//
// Check Defined Values and use Default Values if possible
// -------------------------------------------------------
// 1 / 2 / 4 Line
#ifndef LCD_CHARS
#if LCD_LINES > 2
#error "LCD: Undefined LCD_CHARS"
#else
// Dafault Value
#define LCD_CHARS 20
#endif
#endif
#ifndef LCD_LINE_1
// Address of the 1st char on the 1st line
#define LCD_LINE_1 0
#endif
#ifndef LCD_LINE_2
// Address of the 1st char on the 2nd line
#define LCD_LINE_2 64
#endif
#ifndef LCD_LINE_3
// Address of the 1st char on the 3rd line
#define LCD_LINE_3 LCD_CHARS
#endif
#ifndef LCD_LINE_4
// Address of the 1st char on the 4th line
#define LCD_LINE_4 (LCD_LINE_2 + LCD_CHARS)
#endif
// Data Interface
#if LCD_INTERFACE_BITS == 4
#define LCD_DATA_MASK (0x0F << LCD_DATA_BIT)
#elif LCD_INTERFACE_BITS==8
#define LCD_DATA_MASK (0xFF << LCD_DATA_BIT)
#else
#error "LCD: Wrong Value: LCD_INTERFACE_BITS"
#endif
#if LCD_DATA_MASK > 0xFF
#error "LCD: Value too Big: LCD_DATA_BIT"
#endif
// Need Delay Library
// ------------------
#ifndef F_CPU
#error "LCD: Undefined F_CPU"
#endif
#include <util/delay.h> // Delay Routines
// Need IO Pins
// ------------
#include <avr/io.h> // Device Specific Defines
#define GLUE(a,b) a##b
#define PORT(a) GLUE(PORT,a)
#define PIN(a) GLUE(PIN,a)
#define DDR(a) GLUE(DDR,a)
#define LCD_E_PORT PORT(LCD_E)
#define LCD_E_DDR DDR(LCD_E)
#define LCD_RS_PORT PORT(LCD_RS)
#define LCD_RS_DDR DDR(LCD_RS)
#define LCD_DATA_PORT PORT(LCD_DATA)
#define LCD_DATA_DDR DDR(LCD_DATA)
#ifdef LCD_RW
#define LCD_RW_PORT PORT(LCD_RW)
#define LCD_RW_DDR DDR(LCD_RW)
#endif
// LCD Chip Commands
// -----------------
// Comand Clear LCD Display
#define LCD_HD44780_CLR 0x01
// Command Home Cursor
#define LCD_HD44780_HOME 0x02
// Command Entry Mode (increment/decrement, shift/no shift)
#define LCD_HD44780_ENTMODE(inc, shift) \
(0x04 | ((inc)? 0x02: 0) | ((shift)? 1: 0))
#define LCD_HD44780_ENTMODE_DEF LCD_HD44780_ENTMODE(1,0) // Increment Position, No Shift
// Command Display Controll (display on/off, cursor on/off, cursor blinking on/off)
#define LCD_HD44780_DISPCTL(disp, cursor, blink) \
(0x08 | ((disp)? 0x04: 0) | ((cursor)? 0x02: 0) | ((blink)? 1: 0))
#define LCD_HD44780_CURSORON LCD_HD44780_DISPCTL(1,1,0) // on, cursor on,
#define LCD_HD44780_CURSOROFF LCD_HD44780_DISPCTL(1,0,0) // on, cursor off
// Command Cursor or Display Shift (shift display/cursor, left/right)
#define LCD_HD44780_SHIFT(shift, right) \
(0x10 | ((shift)? 0x08: 0) | ((right)? 0x04: 0))
#define LCD_HD44780_CURSORLEFT LCD_HD44780_SHIFT(0,0)
#define LCD_HD44780_CURSORRIGHT LCD_HD44780_SHIFT(0,1)
// Command Function Set ( 4/8-bit interface / 1 or 2 lines )
#define LCD_HD44780_4BIT1LINE 0x20 // 4-bit 1-line font 5x7
#define LCD_HD44780_4BIT2LINES 0x28 // 4-bit 2-lines font 5x7
#define LCD_HD44780_8BIT1LINE 0x30 // 8-bit 1-line font 5x7
#define LCD_HD44780_8BIT2LINES 0x38 // 8-bit 2-lines font 5x7
// Select Apropriate Mode
#if LCD_INTERFACE_BITS==4
#if LCD_LINES == 1
#define LCD_HD44780_FNSET LCD_HD44780_4BIT1LINE // 4-bit 1-line
#else
#define LCD_HD44780_FNSET LCD_HD44780_4BIT2LINES // 4-bit 2-lines
#endif
#elif LCD_INTERFACE_BITS==8
#if LCD_LINES == 1
#define LCD_HD44780_FNSET LCD_HD44780_8BIT1LINE // 8-bit 1-line
#else
#define LCD_HD44780_FNSET LCD_HD44780_8BIT2LINES // 8-bit 2-lines
#endif
#endif
// User Defined Chars
// ------------------
// Definitions only.
// Because these definitions may be sent to lcd via printf,
// it is impossible to contain 0 bytes (end of string in C)
// so we ored 0x80 to each byte
#define LCD_CHAR_SPACE "\x80\x80\x80\x80\x80\x80\x80\x80" /* space (blank char) */
#define LCD_CHAR_BAT100 "\x8E\x9F\x9F\x9F\x9F\x9F\x9F\x1F" /* symbol battery full */
#define LCD_CHAR_BAT50 "\x8E\x9F\x91\x91\x93\x97\x9F\x1F" /* symbol baterry half */
#define LCD_CHAR_BAT0 "\x8E\x9F\x91\x91\x91\x91\x91\x1F" /* symbol baterry empty */
#define LCD_CHAR_UP "\x80\x84\x8E\x95\x84\x84\x84\x80" /* symbol arrow up */
#define LCD_CHAR_DOWN "\x80\x84\x84\x84\x95\x8E\x84\x80" /* symbol arrow down */
#define LCD_CHAR_LUA "\x84\x8E\x91\x91\x9F\x91\x91\x80" /* A s carkou */
#define LCD_CHAR_LLA "\x81\x82\x8E\x81\x9F\x91\x8F\x80" /* a s carkou */
#define LCD_CHAR_HUC "\x8A\x8E\x91\x90\x90\x91\x8E\x80" /* C s hackem */
#define LCD_CHAR_HLC "\x8A\x84\x8E\x90\x90\x91\x8E\x80" /* c s hackem */
#define LCD_CHAR_HUD "\x8A\x9C\x92\x91\x91\x92\x9C\x80" /* D s hackem */
#define LCD_CHAR_HLD "\x85\x83\x8D\x93\x91\x91\x8F\x80" /* d s hackem */
#define LCD_CHAR_LUE "\x84\x9F\x90\x90\x9E\x90\x9F\x80" /* E s carkou */
#define LCD_CHAR_LLE "\x81\x82\x8E\x91\x9F\x90\x8E\x80" /* e s carkou */
#define LCD_CHAR_HUE "\x8A\x9F\x90\x9E\x90\x90\x9F\x80" /* E s hackem */
#define LCD_CHAR_HLE "\x8A\x84\x8E\x91\x9F\x90\x8E\x80" /* e s hackem */
#define LCD_CHAR_LUI "\x84\x8E\x84\x84\x84\x84\x8E\x80" /* I s carkou */
#define LCD_CHAR_LLI "\x82\x84\x80\x8C\x84\x84\x8E\x80" /* i s carkou */
#define LCD_CHAR_HUN "\x8A\x95\x91\x99\x95\x93\x91\x80" /* N s hackem */
#define LCD_CHAR_HLN "\x8A\x84\x96\x99\x91\x91\x91\x80" /* n s hackem */
#define LCD_CHAR_LUO "\x84\x8E\x91\x91\x91\x91\x8E\x80" /* O s carkou */
#define LCD_CHAR_LLO "\x82\x84\x8E\x91\x91\x91\x8E\x80" /* o s carkou */
#define LCD_CHAR_HUR "\x8A\x9E\x91\x9E\x94\x92\x91\x80" /* R s hackem */
#define LCD_CHAR_HLR "\x8A\x84\x96\x99\x90\x90\x90\x80" /* r s hackem */
#define LCD_CHAR_HUS "\x8A\x8F\x90\x8E\x81\x81\x9E\x80" /* S s hackem */
#define LCD_CHAR_HLS "\x8A\x84\x8E\x90\x8E\x81\x9E\x80" /* s s hackem */
#define LCD_CHAR_HUT "\x8A\x9F\x84\x84\x84\x84\x84\x80" /* T s hackem */
#define LCD_CHAR_HLT "\x8A\x8C\x9C\x88\x88\x89\x86\x80" /* t s hackem */
#define LCD_CHAR_LUU "\x82\x95\x91\x91\x91\x91\x8E\x80" /* U s carkou */
#define LCD_CHAR_LLU "\x82\x84\x91\x91\x91\x93\x8D\x80" /* u s carkou */
#define LCD_CHAR_CUU "\x86\x97\x91\x91\x91\x91\x8E\x80" /* U s krouzkem */
#define LCD_CHAR_CLU "\x86\x86\x91\x91\x91\x91\x8E\x80" /* u s krouzkem */
#define LCD_CHAR_LUY "\x82\x95\x91\x8A\x84\x84\x84\x80" /* Y s carkou */
#define LCD_CHAR_LLY "\x82\x84\x91\x91\x8F\x81\x8E\x80" /* y s carkou */
#define LCD_CHAR_HUZ "\x8A\x9F\x81\x82\x84\x88\x9F\x80" /* Z s hackem */
#define LCD_CHAR_HLZ "\x8A\x84\x9F\x82\x84\x88\x9F\x80" /* z s hackem */
// Program
// -------
static int8_t lcd_posx; // Mirror Register with Position X (1..LCD_CHARS)
#if LCD_LINES > 1
static int8_t lcd_posy; // Mirror Register with Position Y (1..LCD_LINES)
#endif
// Send a Nibble or Byte to the LCD COntroller
static void
lcd_send_nibble(uint8_t rs, uint8_t data)
{
// Select Register or Data
if (rs)
LCD_RS_PORT |= (1<<LCD_RS_BIT);
else
LCD_RS_PORT &= ~(1<<LCD_RS_BIT);
// Put 4bit/8bit data
LCD_DATA_PORT = (LCD_DATA_PORT & ~LCD_DATA_MASK) | ((data<<LCD_DATA_BIT)&LCD_DATA_MASK);
_delay_us(1); // Data Setup Time
// Click Enable on and off
LCD_E_PORT |= 1<<LCD_E_BIT;
_delay_us(1);
LCD_E_PORT &= ~(1<<LCD_E_BIT);
_delay_us(40);
}
// Send a Byte to the LCD Controller
#if LCD_INTERFACE_BITS == 4
static void
lcd_send_byte(uint8_t rs, uint8_t data)
{
lcd_send_nibble(rs, data >> 4); // High Order Data
lcd_send_nibble(rs, data); // Low Order Data
}
#else
#define lcd_send_byte lcd_send_nibble
#endif
// Send a Command to the LCD Controller (RS=0)
#define lcd_send_cmd(n) lcd_send_byte(0, (n))
// Send a Data Byte to the LCD Controller (RS=1)
#define lcd_send_data(n) lcd_send_byte(1, (n))
// Goto Home
void
lcd_home()
{
lcd_send_cmd(LCD_HD44780_HOME); // Zero Cursor Position and Offset
#if LCD_LINES > 1
lcd_posx=lcd_posy=1;
#else
lcd_posx=1;
#endif
_delay_ms(2);
}
// Clear Display
void
lcd_clear()
{
lcd_send_cmd(LCD_HD44780_CLR); // Clear Memory
_delay_ms(2);
}
// Switch Cursor On
void
lcd_cursor_on()
{
lcd_send_cmd(LCD_HD44780_CURSORON);
}
// Switch Cursor Off
void
lcd_cursor_off()
{
lcd_send_cmd(LCD_HD44780_CURSOROFF);
}
// Clear Display and Goto Home with no Cursor
void
lcd_clear_home()
{
lcd_clear(); // Clear Memory
lcd_home(); // Zero Cursor Position and Offset
lcd_cursor_off(); // No Cursor
}
// Move to Position (1,1 is the first position)
void lcd_gotoxy(uint8_t x, uint8_t y)
{
uint8_t Adr;
Adr=x-1;
#if LCD_LINES > 1
switch (y)
{
case 2:
Adr+=LCD_LINE_2;
break;
#if LCD_LINES > 2
case 3:
Adr+=LCD_LINE_3;
break;
case 4:
Adr+=LCD_LINE_4;
break;
#endif
}
#endif
lcd_send_cmd(0x80 | (Adr & 0x7F) );
lcd_posx=x;
#if LCD_LINES > 1
lcd_posy=y;
#endif
}
// Increment Position
void
lcd_inc_pos()
{
// Next Position
lcd_posx++;
// Correct End of Line
#if LCD_LINES == 1
if (lcd_posx > 40)
lcd_posx = 1;
#elif LCD_LINES == 2
if (lcd_posx > 40)
{
lcd_posx = 1;
lcd_posy++; // on the Next Line
}
#elif LCD_LINES > 2
if ( ((lcd_posy & 1) && (lcd_posx > LCD_CHARS)) // Odd Lines are Short
|| (lcd_posx > 40-LCD_CHARS) ) // Memory is up to 40 Bytes
{
lcd_posx = 1; // Position 1
lcd_posy++; // on the Next Line
}
#endif
// Correct End of Last Line
#if LCD_LINES > 1
if (lcd_posy > LCD_LINES)
{
lcd_posy = 1;
}
#endif
}
// Decrement Position
void
lcd_dec_pos()
{
// Correct Beginning of Line
if (--lcd_posx==0) // Step Left
{ // If Beginning of the Line
#if LCD_LINES > 1
if(--lcd_posy==0); // Step Up
lcd_posy = LCD_LINES; // If we are on Top Go to the Bottom
#endif
#if LCD_LINES <= 2
lcd_posx = 40;
#else
if(lcd_posy & 1) // If Odd Line (the Short One)
lcd_posx = LCD_CHARS; // Set End of the Short Line
else // Else
lcd_posx = 40-LCD_CHARS; // Set End of Long Line
#endif
}
}
// Move Cursor Left
void
lcd_cursor_left()
{
lcd_send_cmd(LCD_HD44780_CURSORLEFT);
lcd_dec_pos();
}
// Move Cursor Right
void
lcd_cursor_right()
{
lcd_send_cmd(LCD_HD44780_CURSORRIGHT);
lcd_inc_pos();
}
// Init LCD Display
void
lcd_init(void)
{
// Port Init Direction
LCD_E_PORT &= ~_BV(LCD_E_BIT); // Enable off
LCD_E_DDR |= _BV(LCD_E_BIT); // Enable as Output
LCD_RS_DDR |= _BV(LCD_RS_BIT); // Register Select as Output
#ifdef LCD_RW
LCD_RW_DDR |= _BV(LCD_RW_BIT); // Read Write as Output
#endif
LCD_DATA_DDR |= LCD_DATA_MASK; // Data as Output
// Initial Delay
_delay_ms(40); // Delay for Vcc
// Sync 8/4 bit Interface
#if LCD_INTERFACE_BITS == 4
lcd_send_nibble(0, LCD_HD44780_8BIT1LINE >> 4); // 8 bit mode - sync nibble/byte
_delay_ms(4.1);
lcd_send_nibble(0, LCD_HD44780_8BIT1LINE >> 4);
_delay_us(100);
lcd_send_nibble(0, LCD_HD44780_8BIT1LINE >> 4);
// Set 4 bit mode
lcd_send_nibble(0, LCD_HD44780_FNSET >> 4);
#elif LCD_INTERFACE_BITS == 8
lcd_send_nibble(0, LCD_HD44780_8BIT1LINE); // 8 bit mode - sync nibble/byte
_delay_ms(4.1);
lcd_send_nibble(0, LCD_HD44780_8BIT1LINE);
_delay_us(100);
lcd_send_nibble(0, LCD_HD44780_8BIT1LINE);
#endif
// Set and Init
lcd_send_cmd(LCD_HD44780_FNSET); // 4/8 bits 1/2 lines
lcd_send_cmd(LCD_HD44780_ENTMODE_DEF); // increment/decrement, shift/no shift
lcd_clear_home(); // display on, no cursor, clear and home
}
// LCD Char Output
int
lcd_putc(char c)
{
static uint8_t mode=0;
switch (c)
{
case '\f':
lcd_clear_home(); // Clear Display
break;
case '\n':
#if LCD_LINES > 1
if (lcd_posy <= LCD_LINES) // Go to the Next Line
lcd_posy++;
#endif
case '\r':
#if LCD_LINES > 1
lcd_gotoxy(1,lcd_posy); // Go to the Beginning of the Line
#else
lcd_home();
#endif
break;
case '\b':
lcd_cursor_left(); // Cursor (Position) Move Back
break;
default:
if (mode==0 && c=='\v') // Startr of Definition String
{
mode=1; // Mode Next Char will be Defined Char
break;
}
if (mode==1) // First Char is Position Number
{
lcd_send_cmd(0x40 | ((c & 0x07)<<3) ); // Set CGRAM Address
mode++; // Mode Define Char Patern
break;
}
if (mode==2 && c=='\v') // End of Definition String
{
mode=0;
#if LCD_LINES > 1
lcd_gotoxy(lcd_posx,lcd_posy);
#else
lcd_gotoxy(lcd_posx,1);
#endif
break;
}
if (mode != 2) // Ordinary Chars
{
if (c<0x20) // Remap User Defind Char
c &= 0x07; // from rage 0x10-0x1F to 0x00-0x0f
lcd_inc_pos(); // Next Position
}
lcd_send_data(c); // Send Byte to LCD
break;
}
return 0; // Success
}
// LCD Char Output (for Stream Library)
#ifdef _STDIO_H_
static int
lcd_putc_stream(char c, FILE *unused)
{
return lcd_putc(c);
}
#endif