0,0 → 1,602 |
/* --------------------------------------------------------------------------- |
* 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 |