| 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 |