/* ---------------------------------------------------------------------------* 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 > 1static int8_t lcd_posy; // Mirror Register with Position Y (1..LCD_LINES)#endif// Send a Nibble or Byte to the LCD COntrollerstatic voidlcd_send_nibble(uint8_t rs, uint8_t data){// Select Register or Dataif (rs)LCD_RS_PORT |= (1<<LCD_RS_BIT);elseLCD_RS_PORT &= ~(1<<LCD_RS_BIT);// Put 4bit/8bit dataLCD_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 offLCD_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 == 4static voidlcd_send_byte(uint8_t rs, uint8_t data){lcd_send_nibble(rs, data >> 4); // High Order Datalcd_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 Homevoidlcd_home(){lcd_send_cmd(LCD_HD44780_HOME); // Zero Cursor Position and Offset#if LCD_LINES > 1lcd_posx=lcd_posy=1;#elselcd_posx=1;#endif_delay_ms(2);}// Clear Displayvoidlcd_clear(){lcd_send_cmd(LCD_HD44780_CLR); // Clear Memory_delay_ms(2);}// Switch Cursor Onvoidlcd_cursor_on(){lcd_send_cmd(LCD_HD44780_CURSORON);}// Switch Cursor Offvoidlcd_cursor_off(){lcd_send_cmd(LCD_HD44780_CURSOROFF);}// Clear Display and Goto Home with no Cursorvoidlcd_clear_home(){lcd_clear(); // Clear Memorylcd_home(); // Zero Cursor Position and Offsetlcd_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 > 1switch (y){case 2:Adr+=LCD_LINE_2;break;#if LCD_LINES > 2case 3:Adr+=LCD_LINE_3;break;case 4:Adr+=LCD_LINE_4;break;#endif}#endiflcd_send_cmd(0x80 | (Adr & 0x7F) );lcd_posx=x;#if LCD_LINES > 1lcd_posy=y;#endif}// Increment Positionvoidlcd_inc_pos(){// Next Positionlcd_posx++;// Correct End of Line#if LCD_LINES == 1if (lcd_posx > 40)lcd_posx = 1;#elif LCD_LINES == 2if (lcd_posx > 40){lcd_posx = 1;lcd_posy++; // on the Next Line}#elif LCD_LINES > 2if ( ((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 1lcd_posy++; // on the Next Line}#endif// Correct End of Last Line#if LCD_LINES > 1if (lcd_posy > LCD_LINES){lcd_posy = 1;}#endif}// Decrement Positionvoidlcd_dec_pos(){// Correct Beginning of Lineif (--lcd_posx==0) // Step Left{ // If Beginning of the Line#if LCD_LINES > 1if(--lcd_posy==0); // Step Uplcd_posy = LCD_LINES; // If we are on Top Go to the Bottom#endif#if LCD_LINES <= 2lcd_posx = 40;#elseif(lcd_posy & 1) // If Odd Line (the Short One)lcd_posx = LCD_CHARS; // Set End of the Short Lineelse // Elselcd_posx = 40-LCD_CHARS; // Set End of Long Line#endif}}// Move Cursor Leftvoidlcd_cursor_left(){lcd_send_cmd(LCD_HD44780_CURSORLEFT);lcd_dec_pos();}// Move Cursor Rightvoidlcd_cursor_right(){lcd_send_cmd(LCD_HD44780_CURSORRIGHT);lcd_inc_pos();}// Init LCD Displayvoidlcd_init(void){// Port Init DirectionLCD_E_PORT &= ~_BV(LCD_E_BIT); // Enable offLCD_E_DDR |= _BV(LCD_E_BIT); // Enable as OutputLCD_RS_DDR |= _BV(LCD_RS_BIT); // Register Select as Output#ifdef LCD_RWLCD_RW_DDR |= _BV(LCD_RW_BIT); // Read Write as Output#endifLCD_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 == 4lcd_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 modelcd_send_nibble(0, LCD_HD44780_FNSET >> 4);#elif LCD_INTERFACE_BITS == 8lcd_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 Initlcd_send_cmd(LCD_HD44780_FNSET); // 4/8 bits 1/2 lineslcd_send_cmd(LCD_HD44780_ENTMODE_DEF); // increment/decrement, shift/no shiftlcd_clear_home(); // display on, no cursor, clear and home}// LCD Char Outputintlcd_putc(char c){static uint8_t mode=0;switch (c){case '\f':lcd_clear_home(); // Clear Displaybreak;case '\n':#if LCD_LINES > 1if (lcd_posy <= LCD_LINES) // Go to the Next Linelcd_posy++;#endifcase '\r':#if LCD_LINES > 1lcd_gotoxy(1,lcd_posy); // Go to the Beginning of the Line#elselcd_home();#endifbreak;case '\b':lcd_cursor_left(); // Cursor (Position) Move Backbreak;default:if (mode==0 && c=='\v') // Startr of Definition String{mode=1; // Mode Next Char will be Defined Charbreak;}if (mode==1) // First Char is Position Number{lcd_send_cmd(0x40 | ((c & 0x07)<<3) ); // Set CGRAM Addressmode++; // Mode Define Char Paternbreak;}if (mode==2 && c=='\v') // End of Definition String{mode=0;#if LCD_LINES > 1lcd_gotoxy(lcd_posx,lcd_posy);#elselcd_gotoxy(lcd_posx,1);#endifbreak;}if (mode != 2) // Ordinary Chars{if (c<0x20) // Remap User Defind Charc &= 0x07; // from rage 0x10-0x1F to 0x00-0x0flcd_inc_pos(); // Next Position}lcd_send_data(c); // Send Byte to LCDbreak;}return 0; // Success}// LCD Char Output (for Stream Library)#ifdef _STDIO_H_static intlcd_putc_stream(char c, FILE *unused){return lcd_putc(c);}#endif