00001 /* 00002 * Filename: lcd.c 00003 * Version: 0.2.2 - last change: two-controller cursor-switching 00004 * Description: HD44780 Display Library for ATMEL AVR 00005 * License: Public Domain 00006 * 00007 * Author: Copyright (C) Max Gaukler <development@maxgaukler.de> 00008 * Date: 2010 00009 * 00010 * I, the copyright holder of this work, hereby release it into the public domain. This applies worldwide. 00011 * In case this is not legally possible: 00012 * I grant anyone the right to use this work for any purpose, without any conditions, unless such conditions are required by law. 00013 * 00014 * This program is provided "as is" with absolutely no warranty, neither explicit nor implicit, as far as this is possible by law. 00015 * 00016 */ 00017 00018 #include "lcd.h" 00019 /// global variable that stores the current position. \see lcd_set_position() \ingroup lcd_functions 00020 uint8_t lcd_position=0; 00021 /// global variable that stores the active controller (or 255 for all controllers). \see lcd_set_controller() \ingroup lcd_functions 00022 uint8_t lcd_controller=0; 00023 /// global variable that stores the last cursor mode \see lcd_set_cursor() \ingroup lcd_functions 00024 uint8_t lcd_cursor_mode=0; 00025 00026 /** \defgroup lowlevelio Low-level IO functions 00027 * Edit these functions (and possibly lcd_write()) to implement your own way of interfacing the LCD. 00028 * @{ 00029 */ 00030 /** Set data outputs 00031 * 00032 * In 4bit mode, this sets D4-D7 to bits 0-3 of the input value. In 8bit mode (not yet implemented!), this sets D0-D7 to the input value. 00033 */ 00034 inline void LCD_DATA_OUT(uint8_t data) { 00035 LCD_OUT=(LCD_OUT&(~(0x0F<<LCD_D4_PIN))) | (((data)&0x0F)<<LCD_D4_PIN); 00036 LCD_OUT_UPDATE(); 00037 } 00038 00039 /** Set the RS output pin 00040 00041 RS 00042 */ 00043 inline void LCD_RS(uint8_t rs) { 00044 if (rs) { 00045 LCD_OUT|=(1<<LCD_RS_PIN); 00046 } else { 00047 LCD_OUT&=~(1<<LCD_RS_PIN); 00048 } 00049 LCD_OUT_UPDATE(); 00050 } 00051 00052 /** Set the EN output pin 00053 00054 "EN" (Enable) is the clock pin of the HD44780 00055 */ 00056 inline void LCD_EN(uint8_t en) { 00057 // lcd_controller=1; 00058 #ifdef LCD_TWO_CONTROLLERS 00059 if (((lcd_controller==0) || (lcd_controller==255)) && en) { 00060 LCD_OUT|=(1<<LCD_EN_PIN); 00061 } else { 00062 LCD_OUT&=~(1<<LCD_EN_PIN); 00063 } 00064 #else 00065 if (en) { 00066 LCD_OUT|=(1<<LCD_EN_PIN); 00067 } else { 00068 LCD_OUT&=~(1<<LCD_EN_PIN); 00069 } 00070 #endif 00071 00072 #ifdef LCD_TWO_CONTROLLERS 00073 if (((lcd_controller==1) || (lcd_controller==255)) && en) { 00074 LCD_OUT|=(1<<LCD_EN2_PIN); 00075 } else { 00076 LCD_OUT&=~(1<<LCD_EN2_PIN); 00077 } 00078 #endif 00079 LCD_OUT_UPDATE(); 00080 } 00081 00082 /** Update output port from buffer 00083 00084 This does nothing for direct output. For shift registers it sends the (modified) output buffer to the shift register. 00085 You can control inlining of this function by defining LCD_OUT_UPDATE_NEVER_INLINE (less size but less performance) or LCD_OUT_UPDATE_ALWAYS_INLINE (more size and more performance). By default it is inlined for all cases except LCD_MODE_SHIFT_4BIT and LCD_MODE_SHIFT_8BIT. 00086 */ 00087 00088 // inline LCD_OUT_UPDATE for all trivial cases (direct output) 00089 #if !defined(DOXYGEN) && ( (!defined LCD_MODE_SHIFT_4BIT && !defined LCD_MODE_SHIFT_8BIT && !defined LCD_OUT_UPDATE_NEVER_INLINE) || defined LCD_OUT_UPDATE_ALWAYS_INLINE ) 00090 inline /* inline the following function: */ 00091 #endif 00092 void LCD_OUT_UPDATE(void) { 00093 #if defined(LCD_MODE_SHIFT_4BIT) 00094 // 4bit output with shift register 00095 uint8_t i; 00096 for (i=0;i<8;i++) { 00097 if (LCD_OUT&(1<<(7-i))) { 00098 LCD_SHIFT_PORT|=(1<<LCD_SHIFT_DATA_PIN); 00099 } else { 00100 LCD_SHIFT_PORT&=~(1<<LCD_SHIFT_DATA_PIN); 00101 } 00102 _delay_us(1); 00103 LCD_SHIFT_PORT&=~(1<<LCD_SHIFT_CLOCK_PIN); 00104 _delay_us(1); 00105 LCD_SHIFT_PORT|=1<<LCD_SHIFT_CLOCK_PIN; 00106 _delay_us(1); 00107 } 00108 LCD_SHIFT_PORT&=~(1<<LCD_SHIFT_LATCH_PIN); 00109 _delay_us(1); 00110 LCD_SHIFT_PORT|=1<<LCD_SHIFT_LATCH_PIN; 00111 _delay_us(1); 00112 #elif defined(LCD_MODE_SHIFT_8BIT) 00113 // 8bit output with shift registers 00114 #error SHIFT_8BIT NOT YET IMPLEMENTED 00115 #else 00116 // do nothing for diect output 00117 #endif 00118 } 00119 00120 /*@}*/ 00121 00122 00123 /** \defgroup lcd_functions Library Functions 00124 00125 These functions are used for accessing the display. 00126 @{ 00127 */ 00128 00129 00130 00131 /// Send a raw command and wait for the execution time (3ms for home and clear, 100µs for all other commands) 00132 void lcd_command(uint8_t data) { 00133 lcd_write(data,0); 00134 // execution time 37us 00135 _delay_us(100); 00136 if ((data==LCD_HOME) || (data==LCD_CLEAR)) { 00137 // execution time 1.52ms 00138 _delay_ms(3); 00139 } 00140 } 00141 00142 /// Send data and wait 50µs 00143 void lcd_data(uint8_t data) { 00144 lcd_write(data,1); 00145 // execution time 37+4 us 00146 _delay_us(100); 00147 } 00148 00149 /** Write data or commands to the LCD 00150 00151 Edit this function to add your own way of 8 interfacing the LCD in 8-bit-mode (e.g. by shift register). 00152 In 4-bit-mode this calls lcd_nibble() to send the half-bytes. */ 00153 void lcd_write(uint8_t data, uint8_t rs) { 00154 // RS 00155 LCD_RS(rs); 00156 00157 // address setup time 60ns 00158 _delay_us(0.1); 00159 lcd_nibble(data>>4); 00160 00161 lcd_nibble(data&0x0F); 00162 } 00163 00164 /** Write a half-byte in 4-bit-mode to the LCD */ 00165 void lcd_nibble(uint8_t data) { 00166 // Send a nibble in 4bit mode 00167 LCD_DATA_OUT(data); 00168 _delay_us(10); // data setup time 80ns, enable pulse width 230ns 00169 LCD_EN(1); 00170 _delay_us(10); 00171 LCD_EN(0); 00172 _delay_us(10); 00173 // data hold time 10ns 00174 } 00175 00176 00177 00178 /** Initialise the LCD 00179 00180 Call this function before using the LCD and 100ms after the supply voltage has become stable. */ 00181 void lcd_init(void) { 00182 lcd_set_controller(LCD_ALL_CONTROLLERS); 00183 LCD_EN(0); 00184 lcd_position=0; 00185 LCD_RS(0); 00186 #if defined(LCD_MODE_SHIFT_4BIT) || defined(LCD_MODE_SHIFT_8BIT) 00187 LCD_SHIFT_DDR|=(1<<LCD_SHIFT_DATA_PIN)|(1<<LCD_SHIFT_LATCH_PIN)|(1<<LCD_SHIFT_CLOCK_PIN); 00188 LCD_SHIFT_PORT|=(1<<LCD_SHIFT_LATCH_PIN) | (1<<LCD_SHIFT_CLOCK_PIN); 00189 // all unused shift pins are automatically set to 0 because this is the buffer's start value 00190 #else 00191 LCD_DDR|=(1<<LCD_D4_PIN)|(1<<(LCD_D4_PIN+1))|(1<<(LCD_D4_PIN+2))|(1<<(LCD_D4_PIN+3))|(1<<LCD_RS_PIN)|(1<<LCD_EN_PIN); 00192 00193 #ifdef LCD_RW_PIN 00194 LCD_DDR|=(1<<LCD_RW_PIN); 00195 LCD_PORT&=~(1<<LCD_RW_PIN); 00196 #endif /* LCD_RW_PIN */ 00197 00198 #ifdef LCD_TWO_CONTROLLERS 00199 LCD_DDR|=(1<<LCD_EN2_PIN); 00200 #endif /* LCD_TWO_CONTROLLERS */ 00201 00202 #endif 00203 // See HD44780U datasheet: "Initializing by instruction, 4bit mode" 00204 // Set interface to 8bit three times 00205 uint8_t i; 00206 for (i=0;i<3;i++) { 00207 lcd_nibble((LCD_FUNCTION|LCD_FUNCTION_8BIT)>>4); 00208 _delay_ms(10); 00209 } 00210 _delay_us(10); 00211 00212 // Switch to 4bit mode (display is still in 8bit mode during this command) 00213 lcd_nibble((LCD_FUNCTION|LCD_FUNCTION_4BIT)>>4); 00214 00215 // // First command 00216 // lcd_nibble((LCD_FUNCTION|LCD_FUNCTION_4BIT)>>4); 00217 // lcd_nibble((LCD_FUNCTION|LCD_FUNCTION_4BIT|LCD_FUNCTION_2LINES|LCD_FUNCTION_5x10)&0x0f); 00218 00219 // Now the display is mostly initialised and the real configuration can be set 00220 uint8_t init_command=LCD_FUNCTION|LCD_FUNCTION_4BIT; 00221 #if defined(LCD_5x10_PIXEL_CHARS) 00222 init_command |= LCD_FUNCTION_5x10; 00223 #else 00224 init_command |= LCD_FUNCTION_5x8; 00225 #endif 00226 uint8_t lines_per_controller=LCD_LINES; 00227 #if defined(LCD_HALF_LINES) 00228 // two "half" lines are one real line 00229 lines_per_controller/=2; 00230 #endif 00231 #if defined LCD_TWO_CONTROLLERS 00232 // each controller has half of all lines 00233 lines_per_controller/=2; 00234 #endif 00235 /// \todo automatisches Setzen von 2LINES austesten 00236 if (lines_per_controller==2) { 00237 init_command|=LCD_FUNCTION_2LINES; 00238 } 00239 lcd_command(LCD_ENTRYMODE|LCD_ENTRYMODE_INCREMENT); 00240 lcd_clear(); // clear and go to home position 00241 lcd_home(); // reset any shift-options 00242 lcd_set_cursor(0); // LCD_ON_CURSOR_OFF 00243 lcd_set_controller(0); 00244 } 00245 00246 /** Go to the start position (takes 3ms to execute) 00247 * This also resets some shift-options that can be set by special LCD commands. 00248 */ 00249 00250 void lcd_home(void) { 00251 #ifdef LCD_TWO_CONTROLLERS 00252 lcd_set_controller(1); 00253 lcd_command(LCD_HOME); // home also resets some shift options 00254 lcd_set_controller(0); 00255 #endif 00256 lcd_position=0; 00257 lcd_command(LCD_HOME); 00258 #ifdef LCD_TWO_CONTROLLERS 00259 // call lcd_set_cursor() to switch off cursor on inactive controller for two-controller displays 00260 lcd_set_cursor(lcd_cursor_mode); 00261 #endif 00262 } 00263 00264 /** (internal function) Select the controller 00265 00266 @param num The controller number (counted from zero) or LCD_ALL_CONTROLLERS. */ 00267 inline void lcd_set_controller(uint8_t num) { 00268 #ifdef LCD_TWO_CONTROLLERS 00269 lcd_controller=num; 00270 #endif 00271 // lcd_controller=LCD_ALL_CONTROLLERS; 00272 } 00273 00274 /// Clear the LCD (takes 3ms to execute) and go back to the start position 00275 /// For two-controller displays: switch back to the first controller after this 00276 void lcd_clear(void) { 00277 lcd_set_controller(LCD_ALL_CONTROLLERS); 00278 lcd_command(LCD_CLEAR); 00279 lcd_set_position(0); 00280 } 00281 00282 /// Go to the start of the specified line 00283 void lcd_set_line(uint8_t x) { 00284 lcd_set_position(x*LCD_COLS); 00285 } 00286 00287 /** Go to the specified position 00288 00289 The position is stored in the global variable lcd_position. The command LCD_SET_DDRAM_ADDR then sets the position in the LCD controller. This also affects the cursor. 00290 @param position The character number (Example: for a 4x20 character display the third line starts at 40) 00291 */ 00292 void lcd_set_position(uint8_t position) { 00293 if (position>LCD_LINES*LCD_COLS) { 00294 position=position%(LCD_LINES*LCD_COLS); 00295 } 00296 lcd_position=position; 00297 uint8_t line=lcd_position/LCD_COLS; 00298 #ifdef LCD_HALF_LINES 00299 line=line/2; 00300 #endif 00301 #ifdef LCD_TWO_CONTROLLERS 00302 uint8_t lcd_old_controller=lcd_controller; 00303 // two-controller displays must have at least two lines! 00304 if (line<LCD_LINES/2) { 00305 lcd_set_controller(0); 00306 } else { 00307 lcd_set_controller(1); 00308 } 00309 if (lcd_controller != lcd_old_controller) { 00310 // call lcd_set_cursor() to switch off cursor on inactive controller for two-controller displays 00311 lcd_set_cursor(lcd_cursor_mode); 00312 } 00313 #endif 00314 #ifdef LCD_SPECIAL_LINE_LAYOUT 00315 uint8_t line_addr; 00316 if (line==0) { 00317 line_addr=LCD_LINE1_ADDR; 00318 } else if (line==1) { 00319 line_addr=LCD_LINE2_ADDR; 00320 } else if (line==2) { 00321 line_addr=LCD_LINE3_ADDR; 00322 } else if (line==3) { 00323 line_addr=LCD_LINE4_ADDR; 00324 } 00325 #else 00326 uint8_t line_addr=(line%2)*0x40; 00327 #endif 00328 lcd_command(LCD_SET_DDRAM_ADDR|(line_addr+lcd_position%LCD_COLS)); 00329 } 00330 00331 /// Cursor Mode: No cursor 00332 #define LCD_ON_CURSOR_OFF 0 00333 /// Cursor Mode: Cursor on (underline the current character) 00334 #define LCD_ON_CURSOR_ON 1 00335 /// Cursor Mode: Blink the current character 00336 #define LCD_ON_CURSOR_BLINK 2 00337 /// Cursor Mode: Cursor and Blink (underline and blink the current character) 00338 #define LCD_ON_CURSOR_AND_BLINK 3 00339 /// Cursor Mode: Display completely off 00340 #define LCD_OFF 4 00341 00342 00343 00344 00345 /** Set the cursor mode, switches the LCD on or off 00346 00347 @param mode LCD_CURSOR_OFF, LCD_CURSOR_ON, LCD_BLINK or LCD_CURSOR_AND_BLINK 00348 For two-controller LCDs: The cursor is only enabled for the current controller and disabled for the other controller. As soon as the cursor reaches the second display, the cursor is disabled on the first display and enabled on the second: When a controller change is done by lcd_set_position(), it calls lcd_set_cursor() again with the current mode, which is stored in the global variable lcd_cursor_mode. lcd_set_position is called automatically through lcd_linewrap() by lcd_putchar(), lcd_print() and lcd_putstr(). 00349 \todo test the new version of this function, especially two-controller cursor handling 00350 */ 00351 void lcd_set_cursor(uint8_t mode) { 00352 lcd_cursor_mode=mode; 00353 #ifdef LCD_TWO_CONTROLLERS 00354 uint8_t lcd_old_controller=lcd_controller; 00355 #endif 00356 // Modes "on, no cursor" and "off" are for both controllers 00357 if (mode==LCD_ON_CURSOR_OFF) { 00358 lcd_set_controller(LCD_ALL_CONTROLLERS); 00359 lcd_command(LCD_CONTROL|LCD_CONTROL_DISPLAY_ON|LCD_CONTROL_CURSOR_OFF); 00360 } else if (mode==LCD_OFF) { 00361 lcd_set_controller(LCD_ALL_CONTROLLERS); 00362 lcd_command(LCD_CONTROL|LCD_CONTROL_DISPLAY_OFF); 00363 } else { 00364 // Mode "cursor enabled" is only for the active controller 00365 if (mode==LCD_ON_CURSOR_ON) { 00366 lcd_command(LCD_CONTROL|LCD_CONTROL_DISPLAY_ON|LCD_CONTROL_CURSOR_ON); 00367 } else if (mode==LCD_ON_CURSOR_BLINK) { 00368 lcd_command(LCD_CONTROL|LCD_CONTROL_DISPLAY_ON|LCD_CONTROL_CURSOR_OFF|LCD_CONTROL_BLINK); 00369 } else if (mode==LCD_ON_CURSOR_AND_BLINK) { 00370 lcd_command(LCD_CONTROL|LCD_CONTROL_DISPLAY_ON|LCD_CONTROL_CURSOR_ON|LCD_CONTROL_BLINK); 00371 } 00372 #ifdef LCD_TWO_CONTROLLERS 00373 lcd_set_controller((lcd_controller+1)%2); // switch to inactive controller 00374 // disable the cursor for the inactive controller 00375 lcd_command(LCD_CONTROL|LCD_CONTROL_DISPLAY_ON|LCD_CONTROL_CURSOR_OFF); 00376 #endif 00377 } 00378 #ifdef LCD_TWO_CONTROLLERS 00379 lcd_set_controller(lcd_old_controller); 00380 #endif 00381 } 00382 00383 /** (internal function) If the cursor has passed the end of a line, set the position to the beginning of the next line 00384 00385 This internal function is used for automatic line wrapping. It is necessary because the memory addresses of a line are not always consecutive: A 2x8 character display has line 1 at address 0-7 and line 2 at address 40-47. */ 00386 void lcd_linewrap(void) { 00387 // Overflow: go back to the start 00388 if (lcd_position>=LCD_LINES*LCD_COLS) { 00389 lcd_position=0; 00390 } 00391 // start of new line reached, set position 00392 if (lcd_position%LCD_COLS==0) { 00393 lcd_set_position(lcd_position); 00394 } 00395 } 00396 00397 /** Write a single char to the LCD at the current position 00398 * 00399 * This will print a single char and move to the next position (with automatic linewrapping). If the current character was the last character on the display, the cursor will remain at the end of the display. 00400 * 00401 * @param chr The character (even 0x00 is allowed and will be printed). 00402 */ 00403 void lcd_putchar(char chr) { 00404 00405 // Test case 1: When writing the last character of a LCD, the cursor should stay there and blink 00406 // Test case 2: After the full first line of a display the cursor should be at the beginning of the second line of the display 00407 lcd_linewrap(); 00408 lcd_data(chr); 00409 lcd_position++; 00410 if (lcd_position==LCD_LINES*LCD_COLS) { 00411 // Last char has been reached: Move cursor to last char and not to the beginning of the screen 00412 lcd_set_position(LCD_LINES*LCD_COLS-1); 00413 lcd_position=LCD_LINES*LCD_COLS; 00414 // lcd_linewrap() will be called before writing the next char 00415 } 00416 } 00417 00418 /** Write a string starting at the current position 00419 * 00420 * This will print a string with automatic linewrapping. If the string is longer than the display, it will be displayed in multiple parts without a delay inbetween. 00421 * 00422 * @param str The string to be displayed (zero-terminated array of char) 00423 */ 00424 void lcd_putstr(char *str) { 00425 while (*str!=0) { 00426 lcd_putchar(*str); 00427 str++; 00428 } 00429 } 00430 00431 /** Write a string from ROM starting at the current position 00432 * 00433 * This will print a string with automatic linewrapping. If the string is longer than the display, it will be displayed in multiple parts without a delay inbetween. 00434 * 00435 * @param str The string to be displayed (zero-terminated array of char in ROM) 00436 */ 00437 void lcd_putstr_P(PGM_P str) { 00438 while (pgm_read_byte(str)!=0) { 00439 lcd_putchar(pgm_read_byte(str)); 00440 str++; 00441 } 00442 } 00443 00444 /** Write a string after clearing the display 00445 * 00446 * This clears the display, and then writes a string starting at the home position. 00447 * 00448 * @param str The string to be displayed (zero-terminated array of char) 00449 */ 00450 void lcd_print(char *str) { 00451 lcd_clear(); 00452 lcd_putstr(str); 00453 } 00454 00455 /** Add a custom character 00456 00457 This writes a custom character into character generator RAM. For displays with 5x8 pixel characters (the default), eight characters consisting of eight lines each can be stored. The eigth line is the cursor position which should be kept empty. Displays with 5x10 pixel characters can only store four characters consisting of eleven lines where the eleventh line is the cursor position. 00458 00459 @param number The number can be between 0 and 7 (or 0-3 for 5x10 char displays). The saved character can be accessed by this number: You can include your characters in a string like "hello\001". 00460 Due to technical limitations in C the character number zero can currently not really be used - 0x00 is the string terminating character so it cannot be included into strings. It can only be printed using lcd_putchar(0). 00461 00462 @param data The character is supplied as array of uint8_t. Each line is one byte, bit 4 is the leftmost pixel and bit 0 is the rightmost pixel. The top line is the first byte. 00463 00464 Example for 5x8 pixel character: This displays a black outlined box (the eight line is the cursor position). 00465 @code 00466 uint8_t character_box[]= { 00467 0b00011111, 00468 0b00010001, 00469 0b00010001, 00470 0b00010001, 00471 0b00010001, 00472 0b00010001, 00473 0b00011111, 00474 0b00000000 } 00475 lcd_customchar(1,character_box); 00476 lcd_print("This is a box: \001"); 00477 @endcode 00478 */ 00479 void lcd_customchar(uint8_t number, uint8_t *data) { 00480 #if defined LCD_5x10_PIXEL_CHARS 00481 lcd_command(LCD_SET_CGRAM_ADDR|number<<4); 00482 uint8_t bytes=11; 00483 #else 00484 lcd_command(LCD_SET_CGRAM_ADDR|number<<3); 00485 uint8_t bytes=8; 00486 #endif 00487 uint8_t i; 00488 for (i=0;i<bytes;i++) { 00489 lcd_data(*data); 00490 data++; 00491 } 00492 #if defined LCD_5x10_PIXEL_CHARS 00493 for (i=0;i<5;i++) { 00494 lcd_data(0); 00495 } 00496 uint8_t bytes=11; 00497 #endif 00498 lcd_command(LCD_SET_DDRAM_ADDR|0); 00499 lcd_home(); 00500 } 00501 /** Add a custom character from ROM 00502 This is like lcd_customchar(), but the character is stored in flash ROM and does not consume RAM space. 00503 \todo test this 00504 00505 Example: 00506 @code 00507 // this needs to be global (out of any function, out of main): 00508 const PROGMEM uint8_t character_box[]= { 00509 0b00011111, 00510 0b00010001, 00511 0b00010001, 00512 0b00010001, 00513 0b00010001, 00514 0b00010001, 00515 0b00011111, 00516 0b00000000 } 00517 00518 // in the function: 00519 lcd_customchar_P(1,character_box); 00520 lcd_print("This is a box: \001"); 00521 @endcode 00522 */ 00523 00524 void lcd_customchar_P(uint8_t number, PGM_P data) { 00525 #warning lcd_customchar_P untested 00526 #if defined LCD_5x10_PIXEL_CHARS 00527 lcd_command(LCD_SET_CGRAM_ADDR|number<<4); 00528 uint8_t bytes=11; 00529 #else 00530 lcd_command(LCD_SET_CGRAM_ADDR|number<<3); 00531 uint8_t bytes=8; 00532 #endif 00533 uint8_t i; 00534 for (i=0;i<bytes;i++) { 00535 lcd_data(pgm_read_byte(data)); 00536 data++; 00537 } 00538 #if defined LCD_5x10_PIXEL_CHARS 00539 for (i=0;i<5;i++) { 00540 lcd_data(0); 00541 } 00542 uint8_t bytes=11; 00543 #endif 00544 lcd_command(LCD_SET_DDRAM_ADDR|0); 00545 lcd_home(); 00546 } 00547 00548 /*@}*/ 00549 00550