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