// ************************************************************************************************ /// /// \file lcd_4bit.c /// \brief Interfacing HD44780 compatible LCD /// /// \date $LastChangedDate: 2007-01-31 17:23:42 +0100 (Mi, 31 Jan 2007) $ /// \version $Rev: 533 $ /// \todo Implement timeout, when checking if display is ready /// \todo rename module to lcd.c /// \author Udo Jakobza, Carsten Kögler
Copyright (c) FTZ Leipzig
/// D-04107 Leipzig
Wächterstr. 13 info@easytoweb.de /// \par License /// This library is free software; you can redistribute it and/or modify it /// under the terms of the GNU Lesser General Public License as published by /// the Free Software Foundation; either version 2.1 of the License, or /// (at your option) any later version. This library is distributed in the hope /// that it will be useful, but WITHOUT ANY WARRANTY; without even the implied /// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. /// See the GNU Lesser General Public License for more details. /// see: http://www.gnu.org/copyleft/lesser.html /// You should have received a copy of the GNU Lesser General Public License /// along with this library; if not, write to the Free Software Foundation, /// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

/// Die Bibliothek ist freie Software; Sie dürfen sie unter den Bedingungen der /// GNU Lesser General Public License, wie von der Free Software Foundation /// veröffentlicht, weiterverteilen und/oder modifizieren; entweder gemäß /// Version 2.1 der Lizenz oder (nach Ihrer Option) jeder späteren Version. /// Diese Bibliothek wird in der Hoffnung weiterverbreitet, daß sie nützlich /// sein wird, jedoch OHNE IRGENDEINE GARANTIE, auch ohne die implizierte /// Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK. /// Mehr Details finden Sie in der GNU Lesser General Public License. /// see: http://www.gnu.org/copyleft/lesser.de.html /// Sie sollten eine Kopie der GNU Lesser General Public License zusammen mit /// dieser Bibliothek erhalten haben; falls nicht, schreiben Sie an die FSF, /// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. /// // ************************************************************************************************ #include "project.h" #ifdef _DOXYGEN_PARSER_ #define LCD_4BIT #define LCD_4BIT_DEBUG #endif #ifdef LCD_4BIT #include #include #include "lcd_4bit.h" #include "delay.h" #include // Constants for special characters #define PC_SIGN_LC_AE 228 // PC-Code for "ä" #define PC_SIGN_LC_OE 246 // PC-Code for "ö" #define PC_SIGN_LC_UE 252 // PC-Code for "ü" #define PC_SIGN_UC_AE 196 // PC-Code for "Ä" #define PC_SIGN_UC_OE 214 // PC-Code for "Ö" #define PC_SIGN_UC_UE 220 // PC-Code for "Ü" #define PC_SIGN_SZ 223 // PC-Code for "ß" #define PC_SIGN_AT 64 // PC-Code for "@" #define PC_SIGN_DEGREE 176 // PC-Code for "°" #define PC_SIGN_MU 'µ' // PC-Code for "µ" #ifdef LCD_CONTROLLER_KS0073 // Zeichensatz für AE-DIP-Display mit KS0073-Controller #define LCD_SIGN_LC_AE 123 // LCD-character-code for "ä" #define LCD_SIGN_LC_OE 124 // LCD-character-code for "ö" #define LCD_SIGN_LC_UE 126 // LCD-character-code for "ü" #define LCD_SIGN_MU 228 // LCD-character-Code for "µ" #define LCD_SIGN_SZ 190 // LCD-character-Code for "ß" #define LCD_SIGN_UC_UE 94 // LCD-character-code for "Ü" #define LCD_SIGN_UC_AE 91 // LCD-character-code for "Ä" #define LCD_SIGN_UC_OE 92 // LCD-character-code for "Ö" #define LCD_SIGN_AT 160 // LCD-character-Code for "@" #define LCD_SIGN_DEGREE 128 // LCD-character-Code for "°" #endif #ifdef LCD_CONTROLLER_HD44780 // Zeichensatz laut HD4478-datenblatt: ROM Code A00 #define LCD_SIGN_LC_AE 225 // LCD-character-code for "ä" #define LCD_SIGN_UC_AE 225 // #define LCD_SIGN_LC_OE 239 // LCD-character-code for "ö" #define LCD_SIGN_UC_OE 239 // #define LCD_SIGN_LC_UE 245 // LCD-character-code for "ü" #define LCD_SIGN_UC_UE 245 // #define LCD_SIGN_MU 228 // LCD-character-Code for "µ" #define LCD_SIGN_SZ 226 // LCD-character-Code for "ß" #define LCD_SIGN_AT 64 // LCD-character-Code for "@" #define LCD_SIGN_DEGREE 223 // LCD-character-Code for "°" #endif #define LCD_CURSOR_ON 0x02 #define LCD_CURSOR_OFF 0x00 #define LCD_DISPLAY_ON 0x04 #define LCD_DISPLAY_OFF 0x00 #ifdef LCD_CONTROLLER_HD44780 #define LCD_LINE1_ADDR_OFFSET 0x80 // 1. line 0x00..0x0f + Bit7==0x80 #define LCD_LINE2_ADDR_OFFSET 0xc0 // 2. line 0x40..0x4f + Bit7==0xc0 #define LCD_LINE3_ADDR_OFFSET LCD_X_SIZE+0x80 // 3. line 0x10..0x1f + Bit7==0x90 #define LCD_LINE4_ADDR_OFFSET LCD_X_SIZE+0xc0 // 4. line 0x50..0x5f + Bit7==0xd0 // BIT7 nessesary for command "Set DD RAM address" #endif #ifdef LCD_CONTROLLER_KS0073 #define LCD_LINE1_ADDR_OFFSET 0x80 #define LCD_LINE2_ADDR_OFFSET 0xA0 #define LCD_LINE3_ADDR_OFFSET 0xC0 #define LCD_LINE4_ADDR_OFFSET 0xE0 #endif const unsigned char _base_y[4]={LCD_LINE1_ADDR_OFFSET, LCD_LINE2_ADDR_OFFSET, LCD_LINE3_ADDR_OFFSET, LCD_LINE4_ADDR_OFFSET}; unsigned char _lcd_x,_lcd_y; unsigned char _lcd_data_mirror[LCD_Y_SIZE][LCD_X_SIZE]; #ifdef LCD_BUFFER_ENABLE unsigned char _lcd_data_buffer[LCD_Y_SIZE][LCD_X_SIZE]; unsigned char _lcd_buffer_x,_lcd_buffer_y; #endif // local prototypes void _lcd_ready(void); void _lcd_write_command(unsigned char data); void _lcd_delay(void); void _lcd_write_nibble(unsigned char data); #ifdef LCD_4BIT_DEBUG void lcd_debug_init(void); #endif // ********************************************************************************* /// \brief Initializes the LCD controller /// // ********************************************************************************* void lcd_init(void) { #ifdef LCD_4BIT_DEBUG lcd_debug_init(); #endif lcd_controller_init(); } // ********************************************************************************* /// \brief Initializes the LCD controller module /// // ********************************************************************************* void lcd_controller_init(void) { LCD_EN_LOW; LCD_RS_LOW; LCD_RD_WRITE; LCD_RS_ACTIV; LCD_RD_ACTIV; LCD_EN_ACTIV; LCD_DATA_ACTIV; #ifdef LCD_CONTROLLER_HD44780 delay_ms(100); // wait until the display ready _lcd_write_nibble(0x30); delay_ms(5); _lcd_write_nibble(0x30); delay_ms(1); _lcd_write_nibble(0x30); delay_ms(1); _lcd_write_nibble(0x20); // init 4-bit mode delay_ms(1); _lcd_write_command(0x28); // 4-bit + 2lines (1/16 duty) _lcd_ready(); _lcd_write_command(0x06); // Entry-mode - DD RAM Address increment lcd_clear(); // Display ON; Cursor Off and Clear #endif #ifdef LCD_CONTROLLER_KS0073 delay_ms(20); // wait until the display ready _lcd_write_command(0x20); // function set: 4 bit, RE=0 _lcd_ready(); _lcd_write_command(0x20); // function set: 4 bit, RE=0 _lcd_ready(); _lcd_write_command(0x20); // function set: 4 bit, RE=0 _lcd_ready(); _lcd_write_command(0x24); // function set: 4 bit, RE=1 _lcd_ready(); _lcd_write_command(0x09); // ext. function set: 5 dot, 4 lines _lcd_ready(); _lcd_write_command(0x20); // function set: 4 bit, RE=0 _lcd_ready(); _lcd_write_command(0x06); // cursor increasing _lcd_ready(); lcd_clear(); #endif } // ********************************************************************************* /// \brief Checks, if the LCD is ok. /// /// @retval 0 - ok /// @retval 1 - error // ********************************************************************************* unsigned char lcd_check(void) { unsigned char temp_u8; lcd_write_dd_cg_ram(0x40 | 0x00, 0x0A); temp_u8 = lcd_read_dd_cg_ram(0x40 | 0x00); if (temp_u8 == 0x0A) return 0; else return 1; } // ********************************************************************************* /// \brief Checks the busy flag, see lcd_read_busy_ac() /// // ********************************************************************************* void _lcd_ready(void) { #ifdef LCD_FIX_DELAY delay_ms(1); #else while (lcd_read_busy_ac() & 0x80); #endif // #ifdef LCD_FIX_DELAY } // ********************************************************************************* /// \brief Writes a nibble to LCD dataport /// // ********************************************************************************* void _lcd_write_nibble(unsigned char data) { LCD_DATA_WRITE(data); // write high nibble of data to port high nibble LCD_EN_HIGH; _lcd_delay(); LCD_EN_LOW; _lcd_delay(); } // ********************************************************************************* /// \brief Delay /// /// \note 1ms // ********************************************************************************* void _lcd_delay() { delay_ms(1); } // ********************************************************************************* /// \brief Writes a command to LCD /// /// @param[in] data - data to LCD // ********************************************************************************* void _lcd_write_byte(unsigned char data) { LCD_RD_WRITE; LCD_DATA_ACTIV; _lcd_write_nibble(data&0xF0); // write MSN _lcd_write_nibble(data<<4); // write LSN } // ********************************************************************************* /// \brief writes a command to LCD /// /// @param[in] data - data to LCD // ********************************************************************************* void _lcd_write_command(unsigned char data) { LCD_RS_LOW; _lcd_write_byte(data); } // ********************************************************************************* /// \brief writes data to LCD /// /// @param[in] data - data to LCD // ********************************************************************************* void _lcd_write_data(unsigned char data) { LCD_RS_HIGH; _lcd_write_byte(data); } // ********************************************************************************* /// \brief Reads a nibble from LCD-Dataport /// /// @param[in] data - data to LCD // ********************************************************************************* unsigned char _lcd_read_nibble(void) { unsigned char data; LCD_EN_HIGH; _lcd_delay(); data = LCD_DATA_READ; //read nibble of data (Bit4..Bit7 of Port) LCD_EN_LOW; _lcd_delay(); return data; } // ********************************************************************************* /// \brief Read the Busy-Flag + actual DD- or CG-Address /// /// @return data of busy-bit register // ********************************************************************************* unsigned char _lcd_read_busy_data(void) { unsigned char data; // D7 BusyFlag; // D6-D0 CG- or DD-Address // LCD_RD_READ; LCD_DATA_PASSIV; _lcd_delay(); data = _lcd_read_nibble(); // read MSN data |= _lcd_read_nibble()>>4; // read LSN return data; } // ********************************************************************************* /// \brief Read the Busy-Flag + actual DD- or CG-Address /// /// @return data of busy-bit register // ********************************************************************************* unsigned char lcd_read_busy_ac(void) { // D7 - BusyFlag // D6-D0 CG- or DD-Address LCD_RS_LOW; return _lcd_read_busy_data(); } // ********************************************************************************* /// \brief read the Busy-Flag + actual DD- or CG-Address /// /// @return data of busy-bit register // ********************************************************************************* unsigned char _lcd_read_data(void) { // D7 - BusyFlag // D6-D0 CG- or DD-Address LCD_RS_HIGH; return _lcd_read_busy_data(); } // ********************************************************************************* /// \brief Write a byte to the LCD character generator or display RAM /// /// @param[in] addr - position in CG or DD-RAM /// @param[in] data - CG or DD-RAM data // ********************************************************************************* void lcd_write_dd_cg_ram(unsigned char addr, unsigned char data) { // CG-RAM: BIT7=0 BIT6=1 BIT5..0=address // DD-RAM: BIT7=1 BIT6..0=address _lcd_ready(); _lcd_write_command(addr); // RS=0 _lcd_ready(); _lcd_write_data(data); // RS=1 } // ********************************************************************************* /// \brief Writes a pattern to the character generator RAM (CGRAM) /// /// @param[in] c - number of user-defined character to write /// @param[in] row - row of the character /// @param[in] data - pattern // ********************************************************************************* void lcd_write_cgram(unsigned char c, unsigned char row, unsigned char data) { lcd_write_dd_cg_ram(0x40|c<<3|row, data); } // ********************************************************************************* /// \brief Read a byte from the LCD character generator or display RAM /// /// @param[in] addr - position in CG or DD-RAM /// @return data // ********************************************************************************* unsigned char lcd_read_dd_cg_ram(unsigned char addr) { _lcd_ready(); _lcd_write_command(addr); // RS=0 _lcd_ready(); return _lcd_read_data(); // RS=1 } // ********************************************************************************* /// \brief Set the LCD display position /// /// @param[in] x - position on line of LCD; x=0..LCD_X_SIZE /// @param[in] y - LCD-line; y=0..LCD_Y_SIZE // ********************************************************************************* void lcd_gotoxy(unsigned char x, unsigned char y) { if (x>=LCD_X_SIZE) x-=LCD_X_SIZE; // starts with first valid sign if (y>=LCD_Y_SIZE) y-=LCD_Y_SIZE; // starts with first valid line _lcd_ready(); _lcd_write_command(_base_y[y]+x); _lcd_x=x; _lcd_y=y; } // ********************************************************************************* /// \brief Clear the LCD /// // ********************************************************************************* void lcd_clear(void) { memset(_lcd_data_mirror, ' ', sizeof(_lcd_data_mirror)); _lcd_ready(); _lcd_write_command(0x0c); // Display On and Cursor off _lcd_ready(); _lcd_write_command(0x01); // clear delay_ms(2); _lcd_x=_lcd_y=0; } // ********************************************************************************* /// \brief Clear the line "y" behind position "x" /// /// \note Sets cursor on position x/y afterwards /// @param[in] x - x-position /// @param[in] y - y-position // ********************************************************************************* void lcd_clear_line(unsigned char x, unsigned char y) { unsigned char Lva; lcd_gotoxy(x, y); for (Lva=x; Lva=LCD_X_SIZE) { _lcd_y++; lcd_gotoxy(0,_lcd_y); } } // ********************************************************************************* /// \brief Write a zero-terminated string located in SRAM to the LCD. /// /// Uses the actual position of cursor /// @param[in] str - SRAM-pointer to data // ********************************************************************************* void lcd_puts(char *str) { while (*str) { lcd_putchar(*str); str++; } } // ********************************************************************************* /// \brief Write a zero-terminated string located in FLASH to the LCD. /// /// Uses the actual position of cursor /// @param[in] str - FLASH-pointer to data // ********************************************************************************* void lcd_putsf(unsigned char const* str) { while (*str) { lcd_putchar(*str); str++; } } // ********************************************************************************* /// \brief Set to position 0 of "line"; write "*str" and delete the rest of "line" /// uses the actual position of cursor /// @param[in] str - SRAM-pointer to data /// @param[in] line - LCD-line position // ********************************************************************************* void lcd_puts_line(char *str, unsigned char line) { lcd_gotoxy(0, line); lcd_puts(str); if(_lcd_y==line) // line is not full lcd_clear_line(_lcd_x, line); } /* // ********************************************************************************* /// \brief Set to position 0 of "line"; write "*str" and delete the rest of "line" /// uses the actual position of cursor /// @param[in] str - FLASH-pointer to data /// @param[in] line - LCD-line position // ********************************************************************************* void lcd_putsf_line(char flash *str, unsigned char line) { lcd_gotoxy(0, line); lcd_putsf(str); if(_lcd_y==line) // line is not full lcd_clear_line(_lcd_x, line); } */ #ifdef LCD_BUFFER_ENABLE // ********************************************************************************* /// \brief Initializes the local LCD buffer with actual display content /// // ********************************************************************************* void lcd_buffer_copy(void) { memcpy(_lcd_data_buffer, _lcd_data_mirror, sizeof(_lcd_data_buffer)); _lcd_buffer_x = 0; _lcd_buffer_y = 0; } // ********************************************************************************* /// \brief Clears LCD local buffer /// // ********************************************************************************* void lcd_buffer_clear(void) { memset(_lcd_data_buffer, ' ', sizeof(_lcd_data_buffer)); _lcd_buffer_x = 0; _lcd_buffer_y = 0; } // ********************************************************************************* /// \brief Set cursor in local buffer /// /// @param[in] x - position on line of LCD. x = 0..(LCD_X_SIZE-1) /// @param[in] y - LCD-line. y = 0..(LCD_Y_SIZE-1) // ********************************************************************************* void lcd_buffer_gotoxy(unsigned char x, unsigned char y) { _lcd_buffer_x = x; _lcd_buffer_y = y; } // ********************************************************************************* /// \brief Write a char "c" to the local LCD buffer. /// Uses the actual position of cursor, but checks the end of line and /// switchs over to next line. /// @param[in] c - character // ********************************************************************************* void lcd_buffer_putchar(char c) { switch (c) { case PC_SIGN_LC_AE: c=LCD_SIGN_LC_AE; break; // PC-Code for "ä" case PC_SIGN_LC_OE: c=LCD_SIGN_LC_OE; break; // PC-Code for "ö" case PC_SIGN_LC_UE: c=LCD_SIGN_LC_UE; break; // PC-Code for "ü" case PC_SIGN_UC_AE: c=LCD_SIGN_UC_AE; break; // PC-Code for "Ä" case PC_SIGN_UC_OE: c=LCD_SIGN_UC_OE; break; // PC-Code for "Ö" case PC_SIGN_UC_UE: c=LCD_SIGN_UC_UE; break; // PC-Code for "Ü" case PC_SIGN_SZ: c=LCD_SIGN_SZ; break; // PC-Code for "ß" case PC_SIGN_AT: c=LCD_SIGN_AT; break; // PC-Code for "@" case PC_SIGN_DEGREE: c=LCD_SIGN_DEGREE; break; // PC-Code for "°" case PC_SIGN_MU: c=LCD_SIGN_MU; break; // PC-Code for "µ" } _lcd_data_buffer[_lcd_buffer_y][_lcd_buffer_x] = c; _lcd_buffer_x++; if (_lcd_buffer_x >= LCD_X_SIZE) _lcd_buffer_y++; } // ********************************************************************************* /// \brief Write a 0-terminated string located in SRAM to the local LCD buffer /// Uses the actual position of cursor /// @param[in] str - SRAM-pointer to data // ********************************************************************************* void lcd_buffer_puts(char *str) { while (*str) { lcd_buffer_putchar(*str); str++; } } /* // ********************************************************************************* /// \brief Write a 0-terminated string located in FLASH to the local LCD buffer /// Uses the actual position of cursor /// @param[in] str - FLASH-pointer to data // ********************************************************************************* void lcd_buffer_putsf(char flash *str) { while (*str) { lcd_buffer_putchar(*str); str++; } }*/ // ********************************************************************************* /// \brief Writes the local LCD buffer to the display /// // ********************************************************************************* void lcd_buffer_write(void) { unsigned char x,y; for (y=0; y(LCD_X_SIZE * LCD_Y_SIZE-1)) Lva=(LCD_X_SIZE * LCD_Y_SIZE-1); TCNT1=0; lcd_gotoxy(Lva % LCD_X_SIZE, Lva / LCD_X_SIZE); lcd_print_time(); } else if (strncmpf(command, "-clear", 3)==0) { TCNT1=0; lcd_clear(); lcd_print_time(); } else if (strncmpf(command, "-blink", 6)==0) { TCNT1=0; _lcd_write_command(0x0f); // Display On, Cursor on and Blink lcd_print_time(); } else if (strncmpf(command, "-address", 8)==0) { Lva = debug_get_int_parameter(command); if (Lva==-1) Lva = (LCD_X_SIZE-1); TCNT1=0; _lcd_write_command(0x80 | Lva); // Set new DD RAM address lcd_print_time(); } else if (strncmpf(command, "-puts", 5)==0) { p_param = debug_search_parameter(command); TCNT1=0; if (p_param) lcd_puts(p_param); else lcd_putsf("0123456789"); lcd_print_time(); } else if (strncmpf(command, "-putchar-hex", 12)==0) { Lva = debug_get_int_parameter(command); if (Lva==-1) Lva = 'a'; TCNT1 = 0; lcd_putchar(Lva); lcd_print_time(); return; } else if (strncmpf(command, "-check", 6)==0) { TCNT1 = 0; result = lcd_check(); printf("Result: %1u\r\n", result); lcd_print_time(); } } } #endif // #ifdef LCD_4BIT_DEBUG #endif // #ifdef LCD_4BIT