Rev Author Line No. Line
1109 miho 1 /* ---------------------------------------------------------------------------
2 * AVR_MLIB - HD 44780 LCD Display Driver
3 * www.mlab.cz miho 2008
4 * ---------------------------------------------------------------------------
5 * LCD display driver for standard Hitachi 1/2/4 line character LCD modules
6 * for AVR processors. It uses 4 or 8 bit interface without readback.
7 * In the Examples section there is a demo application for this library.
8 * ---------------------------------------------------------------------------
9 * 00.00 2008/03/28 First Version
10 * ---------------------------------------------------------------------------
11 */
12  
13  
14 // What should be set and done before here
15 // ---------------------------------------
16 //
17 // #include <stdio.h> // If you want to use printf, ...
18 //
19 // #define LCD_DATA B // 4 or 8 bits field (lsb bit of the port)
20 // #define LCD_DATA_BIT 4
21 //
22 // #define LCD_RS D // Register Select (port and bit)
23 // #define LCD_RS_BIT 4
24 //
25 // #define LCD_E D // Enable (port and bit)
26 // #define LCD_E_BIT 3
27 //
28 //
29 // // LCD Display Parameters
30 // #define LCD_INTERFACE_BITS 4 // 4 or 8 bit interface
31 // #define LCD_LINES 1 // 1 or 2 or 4 lines
32 // #define LCD_CHARS 20 // usualy 16 or 20, important for 4 line display only
33 //
34 // #include "lcd_hd44780.h" // Use LCD Library
35 //
36 //
37 // How to use the library
38 // ----------------------
39 //
40 // void lcd_init(void) // Init LCD Display
41 //
42 // void lcd_home() // Goto Home
43 //
44 // void lcd_clear() // Clear Display
45 //
46 // void lcd_clear_home() // Clear Display and Goto Home with no Cursor
47 //
48 // void lcd_cursor_on() // Switch Cursor On
49 //
50 // void lcd_cursor_off() // Switch Cursor Off
51 //
52 // void lcd_cursor_left() // Move Cursor Left
53 //
54 // void lcd_cursor_right() // Move Cursor Right
55 //
56 // void lcd_gotoxy(uint8_t x, uint8_t y) // Move to Position (1,1 is the first position)
57 //
58 // int lcd_putc(char c) // LCD Char Output
59 //
60 // int lcd_putc_stream(char c, FILE *unused) // LCD Char Output (for Stream Library)
61 //
62 //
63 // How to use printf
64 // -----------------
65 //
66 // 1) Define FILE structure
67 //
68 // static FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putc_stream, NULL, _FDEV_SETUP_WRITE);
69 //
70 // 2) Connect it with standard output
71 //
72 // stdout = &lcd_stream; // Connect stdout to LCD Stream
73 //
74 // 3) Use printf
75 //
76 // printf("\fHello World!\n------------");
77 //
78 // 4) Use special chars
79 //
80 // \f - clear display and goto home
81 // \n - goto the beginning of the next line
82 // \r - goto to the beginning of curent line
83 // \b - backspace
84 // \v - start and end definition of user defined char
85 //
86 //
87 // How to use User Defined symbols
88 // -------------------------------
89 //
90 // That is easy. Just print the definition to lcd. Look at the example
91 //
92 // printf("\v" "\x10" LCD_CHAR_BAT50 "\v"); // definition (redefines CGRAM content of the LCD)
93 // printf("Battery Status \x10"); // usage
94 //
95 // \v starts the definition
96 // \x10 first (of eight) user defined char
97 // LCD_CHAR_BAT50 half battery symbol, you can define more symbols here (up to 8)
98 // \v end of definition
99 //
100  
101  
102 // Check Defined Values and use Default Values if possible
103 // -------------------------------------------------------
104  
105 // 1 / 2 / 4 Line
106 #ifndef LCD_CHARS
107 #if LCD_LINES > 2
108 #error "LCD: Undefined LCD_CHARS"
109 #else
110 // Dafault Value
111 #define LCD_CHARS 20
112 #endif
113 #endif
114  
115 #ifndef LCD_LINE_1
116 // Address of the 1st char on the 1st line
117 #define LCD_LINE_1 0
118 #endif
119  
120 #ifndef LCD_LINE_2
121 // Address of the 1st char on the 2nd line
122 #define LCD_LINE_2 64
123 #endif
124  
125 #ifndef LCD_LINE_3
126 // Address of the 1st char on the 3rd line
127 #define LCD_LINE_3 LCD_CHARS
128 #endif
129  
130 #ifndef LCD_LINE_4
131 // Address of the 1st char on the 4th line
132 #define LCD_LINE_4 (LCD_LINE_2 + LCD_CHARS)
133 #endif
134  
135 // Data Interface
136 #if LCD_INTERFACE_BITS == 4
137 #define LCD_DATA_MASK (0x0F << LCD_DATA_BIT)
138 #elif LCD_INTERFACE_BITS==8
139 #define LCD_DATA_MASK (0xFF << LCD_DATA_BIT)
140 #else
141 #error "LCD: Wrong Value: LCD_INTERFACE_BITS"
142 #endif
143  
144 #if LCD_DATA_MASK > 0xFF
145 #error "LCD: Value too Big: LCD_DATA_BIT"
146 #endif
147  
148  
149 // Need Delay Library
150 // ------------------
151  
152 #ifndef F_CPU
153 #error "LCD: Undefined F_CPU"
154 #endif
155 #include <util/delay.h> // Delay Routines
156  
157  
158 // Need IO Pins
159 // ------------
160  
161 #include <avr/io.h> // Device Specific Defines
162  
163 #define GLUE(a,b) a##b
164 #define PORT(a) GLUE(PORT,a)
165 #define PIN(a) GLUE(PIN,a)
166 #define DDR(a) GLUE(DDR,a)
167  
168  
169 #define LCD_E_PORT PORT(LCD_E)
170 #define LCD_E_DDR DDR(LCD_E)
171  
172 #define LCD_RS_PORT PORT(LCD_RS)
173 #define LCD_RS_DDR DDR(LCD_RS)
174  
175 #define LCD_DATA_PORT PORT(LCD_DATA)
176 #define LCD_DATA_DDR DDR(LCD_DATA)
177  
178 #ifdef LCD_RW
179 #define LCD_RW_PORT PORT(LCD_RW)
180 #define LCD_RW_DDR DDR(LCD_RW)
181 #endif
182  
183  
184 // LCD Chip Commands
185 // -----------------
186  
187 // Comand Clear LCD Display
188 #define LCD_HD44780_CLR 0x01
189  
190 // Command Home Cursor
191 #define LCD_HD44780_HOME 0x02
192  
193 // Command Entry Mode (increment/decrement, shift/no shift)
194 #define LCD_HD44780_ENTMODE(inc, shift) \
195 (0x04 | ((inc)? 0x02: 0) | ((shift)? 1: 0))
196  
197 #define LCD_HD44780_ENTMODE_DEF LCD_HD44780_ENTMODE(1,0) // Increment Position, No Shift
198  
199 // Command Display Controll (display on/off, cursor on/off, cursor blinking on/off)
200 #define LCD_HD44780_DISPCTL(disp, cursor, blink) \
201 (0x08 | ((disp)? 0x04: 0) | ((cursor)? 0x02: 0) | ((blink)? 1: 0))
202  
203 #define LCD_HD44780_CURSORON LCD_HD44780_DISPCTL(1,1,0) // on, cursor on,
204 #define LCD_HD44780_CURSOROFF LCD_HD44780_DISPCTL(1,0,0) // on, cursor off
205  
206 // Command Cursor or Display Shift (shift display/cursor, left/right)
207 #define LCD_HD44780_SHIFT(shift, right) \
208 (0x10 | ((shift)? 0x08: 0) | ((right)? 0x04: 0))
209  
210 #define LCD_HD44780_CURSORLEFT LCD_HD44780_SHIFT(0,0)
211 #define LCD_HD44780_CURSORRIGHT LCD_HD44780_SHIFT(0,1)
212  
213 // Command Function Set ( 4/8-bit interface / 1 or 2 lines )
214 #define LCD_HD44780_4BIT1LINE 0x20 // 4-bit 1-line font 5x7
215 #define LCD_HD44780_4BIT2LINES 0x28 // 4-bit 2-lines font 5x7
216 #define LCD_HD44780_8BIT1LINE 0x30 // 8-bit 1-line font 5x7
217 #define LCD_HD44780_8BIT2LINES 0x38 // 8-bit 2-lines font 5x7
218  
219 // Select Apropriate Mode
220 #if LCD_INTERFACE_BITS==4
221 #if LCD_LINES == 1
222 #define LCD_HD44780_FNSET LCD_HD44780_4BIT1LINE // 4-bit 1-line
223 #else
224 #define LCD_HD44780_FNSET LCD_HD44780_4BIT2LINES // 4-bit 2-lines
225 #endif
226 #elif LCD_INTERFACE_BITS==8
227 #if LCD_LINES == 1
228 #define LCD_HD44780_FNSET LCD_HD44780_8BIT1LINE // 8-bit 1-line
229 #else
230 #define LCD_HD44780_FNSET LCD_HD44780_8BIT2LINES // 8-bit 2-lines
231 #endif
232 #endif
233  
234  
235 // User Defined Chars
236 // ------------------
237  
238 // Definitions only.
239 // Because these definitions may be sent to lcd via printf,
240 // it is impossible to contain 0 bytes (end of string in C)
241 // so we ored 0x80 to each byte
242  
243 #define LCD_CHAR_SPACE "\x80\x80\x80\x80\x80\x80\x80\x80" /* space (blank char) */
244 #define LCD_CHAR_BAT100 "\x8E\x9F\x9F\x9F\x9F\x9F\x9F\x1F" /* symbol battery full */
245 #define LCD_CHAR_BAT50 "\x8E\x9F\x91\x91\x93\x97\x9F\x1F" /* symbol baterry half */
246 #define LCD_CHAR_BAT0 "\x8E\x9F\x91\x91\x91\x91\x91\x1F" /* symbol baterry empty */
247 #define LCD_CHAR_UP "\x80\x84\x8E\x95\x84\x84\x84\x80" /* symbol arrow up */
248 #define LCD_CHAR_DOWN "\x80\x84\x84\x84\x95\x8E\x84\x80" /* symbol arrow down */
249 #define LCD_CHAR_LUA "\x84\x8E\x91\x91\x9F\x91\x91\x80" /* A s carkou */
250 #define LCD_CHAR_LLA "\x81\x82\x8E\x81\x9F\x91\x8F\x80" /* a s carkou */
251 #define LCD_CHAR_HUC "\x8A\x8E\x91\x90\x90\x91\x8E\x80" /* C s hackem */
252 #define LCD_CHAR_HLC "\x8A\x84\x8E\x90\x90\x91\x8E\x80" /* c s hackem */
253 #define LCD_CHAR_HUD "\x8A\x9C\x92\x91\x91\x92\x9C\x80" /* D s hackem */
254 #define LCD_CHAR_HLD "\x85\x83\x8D\x93\x91\x91\x8F\x80" /* d s hackem */
255 #define LCD_CHAR_LUE "\x84\x9F\x90\x90\x9E\x90\x9F\x80" /* E s carkou */
256 #define LCD_CHAR_LLE "\x81\x82\x8E\x91\x9F\x90\x8E\x80" /* e s carkou */
257 #define LCD_CHAR_HUE "\x8A\x9F\x90\x9E\x90\x90\x9F\x80" /* E s hackem */
258 #define LCD_CHAR_HLE "\x8A\x84\x8E\x91\x9F\x90\x8E\x80" /* e s hackem */
259 #define LCD_CHAR_LUI "\x84\x8E\x84\x84\x84\x84\x8E\x80" /* I s carkou */
260 #define LCD_CHAR_LLI "\x82\x84\x80\x8C\x84\x84\x8E\x80" /* i s carkou */
261 #define LCD_CHAR_HUN "\x8A\x95\x91\x99\x95\x93\x91\x80" /* N s hackem */
262 #define LCD_CHAR_HLN "\x8A\x84\x96\x99\x91\x91\x91\x80" /* n s hackem */
263 #define LCD_CHAR_LUO "\x84\x8E\x91\x91\x91\x91\x8E\x80" /* O s carkou */
264 #define LCD_CHAR_LLO "\x82\x84\x8E\x91\x91\x91\x8E\x80" /* o s carkou */
265 #define LCD_CHAR_HUR "\x8A\x9E\x91\x9E\x94\x92\x91\x80" /* R s hackem */
266 #define LCD_CHAR_HLR "\x8A\x84\x96\x99\x90\x90\x90\x80" /* r s hackem */
267 #define LCD_CHAR_HUS "\x8A\x8F\x90\x8E\x81\x81\x9E\x80" /* S s hackem */
268 #define LCD_CHAR_HLS "\x8A\x84\x8E\x90\x8E\x81\x9E\x80" /* s s hackem */
269 #define LCD_CHAR_HUT "\x8A\x9F\x84\x84\x84\x84\x84\x80" /* T s hackem */
270 #define LCD_CHAR_HLT "\x8A\x8C\x9C\x88\x88\x89\x86\x80" /* t s hackem */
271 #define LCD_CHAR_LUU "\x82\x95\x91\x91\x91\x91\x8E\x80" /* U s carkou */
272 #define LCD_CHAR_LLU "\x82\x84\x91\x91\x91\x93\x8D\x80" /* u s carkou */
273 #define LCD_CHAR_CUU "\x86\x97\x91\x91\x91\x91\x8E\x80" /* U s krouzkem */
274 #define LCD_CHAR_CLU "\x86\x86\x91\x91\x91\x91\x8E\x80" /* u s krouzkem */
275 #define LCD_CHAR_LUY "\x82\x95\x91\x8A\x84\x84\x84\x80" /* Y s carkou */
276 #define LCD_CHAR_LLY "\x82\x84\x91\x91\x8F\x81\x8E\x80" /* y s carkou */
277 #define LCD_CHAR_HUZ "\x8A\x9F\x81\x82\x84\x88\x9F\x80" /* Z s hackem */
278 #define LCD_CHAR_HLZ "\x8A\x84\x9F\x82\x84\x88\x9F\x80" /* z s hackem */
279  
280  
281 // Program
282 // -------
283  
284  
285 static int8_t lcd_posx; // Mirror Register with Position X (1..LCD_CHARS)
286 #if LCD_LINES > 1
287 static int8_t lcd_posy; // Mirror Register with Position Y (1..LCD_LINES)
288 #endif
289  
290  
291 // Send a Nibble or Byte to the LCD COntroller
292 static void
293 lcd_send_nibble(uint8_t rs, uint8_t data)
294 {
295 // Select Register or Data
296 if (rs)
297 LCD_RS_PORT |= (1<<LCD_RS_BIT);
298 else
299 LCD_RS_PORT &= ~(1<<LCD_RS_BIT);
300  
301 // Put 4bit/8bit data
302 LCD_DATA_PORT = (LCD_DATA_PORT & ~LCD_DATA_MASK) | ((data<<LCD_DATA_BIT)&LCD_DATA_MASK);
303 _delay_us(1); // Data Setup Time
304  
305 // Click Enable on and off
306 LCD_E_PORT |= 1<<LCD_E_BIT;
307 _delay_us(1);
308 LCD_E_PORT &= ~(1<<LCD_E_BIT);
309 _delay_us(40);
310 }
311  
312  
313 // Send a Byte to the LCD Controller
314 #if LCD_INTERFACE_BITS == 4
315 static void
316 lcd_send_byte(uint8_t rs, uint8_t data)
317 {
318 lcd_send_nibble(rs, data >> 4); // High Order Data
319 lcd_send_nibble(rs, data); // Low Order Data
320 }
321 #else
322 #define lcd_send_byte lcd_send_nibble
323 #endif
324  
325  
326 // Send a Command to the LCD Controller (RS=0)
327 #define lcd_send_cmd(n) lcd_send_byte(0, (n))
328  
329  
330 // Send a Data Byte to the LCD Controller (RS=1)
331 #define lcd_send_data(n) lcd_send_byte(1, (n))
332  
333  
334 // Goto Home
335 void
336 lcd_home()
337 {
338 lcd_send_cmd(LCD_HD44780_HOME); // Zero Cursor Position and Offset
339 #if LCD_LINES > 1
340 lcd_posx=lcd_posy=1;
341 #else
342 lcd_posx=1;
343 #endif
344 _delay_ms(2);
345 }
346  
347  
348 // Clear Display
349 void
350 lcd_clear()
351 {
352 lcd_send_cmd(LCD_HD44780_CLR); // Clear Memory
353 _delay_ms(2);
354 }
355  
356  
357 // Switch Cursor On
358 void
359 lcd_cursor_on()
360 {
361 lcd_send_cmd(LCD_HD44780_CURSORON);
362 }
363  
364  
365 // Switch Cursor Off
366 void
367 lcd_cursor_off()
368 {
369 lcd_send_cmd(LCD_HD44780_CURSOROFF);
370 }
371  
372  
373 // Clear Display and Goto Home with no Cursor
374 void
375 lcd_clear_home()
376 {
377 lcd_clear(); // Clear Memory
378 lcd_home(); // Zero Cursor Position and Offset
379 lcd_cursor_off(); // No Cursor
380 }
381  
382  
383 // Move to Position (1,1 is the first position)
384 void lcd_gotoxy(uint8_t x, uint8_t y)
385 {
386 uint8_t Adr;
387  
388 Adr=x-1;
389 #if LCD_LINES > 1
390 switch (y)
391 {
392 case 2:
393 Adr+=LCD_LINE_2;
394 break;
395 #if LCD_LINES > 2
396 case 3:
397 Adr+=LCD_LINE_3;
398 break;
399 case 4:
400 Adr+=LCD_LINE_4;
401 break;
402 #endif
403 }
404 #endif
405  
406 lcd_send_cmd(0x80 | (Adr & 0x7F) );
407 lcd_posx=x;
408 #if LCD_LINES > 1
409 lcd_posy=y;
410 #endif
411 }
412  
413  
414 // Increment Position
415 void
416 lcd_inc_pos()
417 {
418 // Next Position
419 lcd_posx++;
420  
421 // Correct End of Line
422 #if LCD_LINES == 1
423 if (lcd_posx > 40)
424 lcd_posx = 1;
425 #elif LCD_LINES == 2
426 if (lcd_posx > 40)
427 {
428 lcd_posx = 1;
429 lcd_posy++; // on the Next Line
430 }
431 #elif LCD_LINES > 2
432 if ( ((lcd_posy & 1) && (lcd_posx > LCD_CHARS)) // Odd Lines are Short
433 || (lcd_posx > 40-LCD_CHARS) ) // Memory is up to 40 Bytes
434 {
435 lcd_posx = 1; // Position 1
436 lcd_posy++; // on the Next Line
437 }
438 #endif
439  
440 // Correct End of Last Line
441 #if LCD_LINES > 1
442 if (lcd_posy > LCD_LINES)
443 {
444 lcd_posy = 1;
445 }
446 #endif
447 }
448  
449 // Decrement Position
450 void
451 lcd_dec_pos()
452 {
453 // Correct Beginning of Line
454 if (--lcd_posx==0) // Step Left
455 { // If Beginning of the Line
456 #if LCD_LINES > 1
457 if(--lcd_posy==0); // Step Up
458 lcd_posy = LCD_LINES; // If we are on Top Go to the Bottom
459 #endif
460 #if LCD_LINES <= 2
461 lcd_posx = 40;
462 #else
463 if(lcd_posy & 1) // If Odd Line (the Short One)
464 lcd_posx = LCD_CHARS; // Set End of the Short Line
465 else // Else
466 lcd_posx = 40-LCD_CHARS; // Set End of Long Line
467 #endif
468 }
469 }
470  
471 // Move Cursor Left
472 void
473 lcd_cursor_left()
474 {
475 lcd_send_cmd(LCD_HD44780_CURSORLEFT);
476 lcd_dec_pos();
477 }
478  
479  
480 // Move Cursor Right
481 void
482 lcd_cursor_right()
483 {
484 lcd_send_cmd(LCD_HD44780_CURSORRIGHT);
485 lcd_inc_pos();
486 }
487  
488  
489 // Init LCD Display
490 void
491 lcd_init(void)
492 {
493 // Port Init Direction
494 LCD_E_PORT &= ~_BV(LCD_E_BIT); // Enable off
495 LCD_E_DDR |= _BV(LCD_E_BIT); // Enable as Output
496 LCD_RS_DDR |= _BV(LCD_RS_BIT); // Register Select as Output
497 #ifdef LCD_RW
498 LCD_RW_DDR |= _BV(LCD_RW_BIT); // Read Write as Output
499 #endif
500 LCD_DATA_DDR |= LCD_DATA_MASK; // Data as Output
501  
502 // Initial Delay
503 _delay_ms(40); // Delay for Vcc
504  
505 // Sync 8/4 bit Interface
506 #if LCD_INTERFACE_BITS == 4
507 lcd_send_nibble(0, LCD_HD44780_8BIT1LINE >> 4); // 8 bit mode - sync nibble/byte
508 _delay_ms(4.1);
509 lcd_send_nibble(0, LCD_HD44780_8BIT1LINE >> 4);
510 _delay_us(100);
511 lcd_send_nibble(0, LCD_HD44780_8BIT1LINE >> 4);
512 // Set 4 bit mode
513 lcd_send_nibble(0, LCD_HD44780_FNSET >> 4);
514 #elif LCD_INTERFACE_BITS == 8
515 lcd_send_nibble(0, LCD_HD44780_8BIT1LINE); // 8 bit mode - sync nibble/byte
516 _delay_ms(4.1);
517 lcd_send_nibble(0, LCD_HD44780_8BIT1LINE);
518 _delay_us(100);
519 lcd_send_nibble(0, LCD_HD44780_8BIT1LINE);
520 #endif
521  
522 // Set and Init
523 lcd_send_cmd(LCD_HD44780_FNSET); // 4/8 bits 1/2 lines
524 lcd_send_cmd(LCD_HD44780_ENTMODE_DEF); // increment/decrement, shift/no shift
525 lcd_clear_home(); // display on, no cursor, clear and home
526 }
527  
528  
529 // LCD Char Output
530 int
531 lcd_putc(char c)
532 {
533 static uint8_t mode=0;
534  
535 switch (c)
536 {
537 case '\f':
538 lcd_clear_home(); // Clear Display
539 break;
540  
541 case '\n':
542 #if LCD_LINES > 1
543 if (lcd_posy <= LCD_LINES) // Go to the Next Line
544 lcd_posy++;
545 #endif
546  
547 case '\r':
548 #if LCD_LINES > 1
549 lcd_gotoxy(1,lcd_posy); // Go to the Beginning of the Line
550 #else
551 lcd_home();
552 #endif
553 break;
554  
555 case '\b':
556 lcd_cursor_left(); // Cursor (Position) Move Back
557 break;
558  
559 default:
560 if (mode==0 && c=='\v') // Startr of Definition String
561 {
562 mode=1; // Mode Next Char will be Defined Char
563 break;
564 }
565 if (mode==1) // First Char is Position Number
566 {
567 lcd_send_cmd(0x40 | ((c & 0x07)<<3) ); // Set CGRAM Address
568 mode++; // Mode Define Char Patern
569 break;
570 }
571 if (mode==2 && c=='\v') // End of Definition String
572 {
573 mode=0;
574 #if LCD_LINES > 1
575 lcd_gotoxy(lcd_posx,lcd_posy);
576 #else
577 lcd_gotoxy(lcd_posx,1);
578 #endif
579 break;
580 }
581 if (mode != 2) // Ordinary Chars
582 {
583 if (c<0x20) // Remap User Defind Char
584 c &= 0x07; // from rage 0x10-0x1F to 0x00-0x0f
585 lcd_inc_pos(); // Next Position
586 }
587 lcd_send_data(c); // Send Byte to LCD
588 break;
589 }
590  
591 return 0; // Success
592 }
593  
594  
595 // LCD Char Output (for Stream Library)
596 #ifdef _STDIO_H_
597 static int
598 lcd_putc_stream(char c, FILE *unused)
599 {
600 return lcd_putc(c);
601 }
602 #endif