Removed 'This file is part of DevLib ...'
[bertos.git] / drv / lcd_hd44.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2005 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief LM044L type LCD hardware module (impl.)
34  *
35  * \version $Id$
36  * \author Bernardo Innocenti <bernie@develer.com>
37  * \author Stefano Fedrigo <aleph@develer.com>
38  */
39
40 /*#*
41  *#* $Log$
42  *#* Revision 1.5  2007/10/01 18:59:27  batt
43  *#* Set to const col_address; add static assert check on array dimension.
44  *#*
45  *#* Revision 1.4  2007/10/01 10:46:09  batt
46  *#* Add light LCD position computation.
47  *#*
48  *#* Revision 1.3  2006/09/20 19:55:01  marco
49  *#* Added CONFIG_LCD_4BIT.
50  *#*
51  *#* Revision 1.2  2006/07/19 12:56:25  bernie
52  *#* Convert to new Doxygen style.
53  *#*
54  *#* Revision 1.1  2005/11/04 18:00:42  bernie
55  *#* Import into DevLib.
56  *#*
57  *#* Revision 1.2  2005/06/14 14:43:43  bernie
58  *#* Add DevLib headers.
59  *#*
60  *#* Revision 1.1  2005/05/24 09:17:58  batt
61  *#* Move drivers to top-level.
62  *#*
63  *#* Revision 1.9  2005/05/09 21:58:53  batt
64  *#* Fix doxygen tags.
65  *#*
66  *#* Revision 1.8  2005/05/09 12:52:46  batt
67  *#* lcd_dataRead(): Avoid bus collision; Add back *UNTESTED* 8bit bus support.
68  *#*
69  *#* Revision 1.7  2005/05/09 12:24:13  batt
70  *#* lcd_putc(): Fix latent bug; lcd_hw_init(): Extend timings.
71  *#*/
72
73 #include "lcd_hd44.h"
74 #include "hw_lcd.h"
75 #include <cfg/arch_config.h>
76 #include <drv/timer.h>
77
78 #if defined(LCD_READ_H) && defined(LCD_READ_L) && defined(LCD_WRITE_H) && defined(LCD_WRITE_L)
79         #define CONFIG_LCD_4BIT 1
80 #elif defined(LCD_READ) && defined(LCD_WRITE)
81         #define CONFIG_LCD_4BIT 0
82 #else
83         #error Incomplete or missing LCD_READ/LCD_WRITE macros
84 #endif
85
86 /** Flag di stato del display */
87 #define LCDF_BUSY  BV(7)
88
89 #if CONFIG_LCD_ADDRESS_FAST == 1
90 #define lcd_address(x) lcd_address[x]
91 /**
92  * Addresses of LCD display character positions, expanded
93  * for faster access (DB7 = 1).
94  */
95 static const uint8_t lcd_address[] =
96 {
97         /* row 0 */
98         0x80, 0x81, 0x82, 0x83,
99         0x84, 0x85, 0x86, 0x87,
100         0x88, 0x89, 0x8A, 0x8B,
101         0x8C, 0x8D, 0x8E, 0x8F,
102 #if LCD_COLS > 16
103         0x90, 0x91, 0x92, 0x93,
104 #endif
105
106         /* row 1 */
107         0xC0, 0xC1, 0xC2, 0xC3,
108         0xC4, 0xC5, 0xC6, 0xC7,
109         0xC8, 0xC9, 0xCA, 0xCB,
110         0xCC, 0xCD, 0xCE, 0xCF,
111 #if LCD_COLS > 16
112         0xD0, 0xD1, 0xD2, 0xD3,
113 #endif
114
115 #if LCD_ROWS > 2
116         /* row 2 */
117         0x94, 0x95, 0x96, 0x97,
118         0x98, 0x99, 0x9A, 0x9B,
119         0x9C, 0x9D, 0x9E, 0x9F,
120         0xA0, 0xA1, 0xA2, 0xA3,
121 #if LCD_COLS > 16
122         0xA4, 0xA5, 0xA6, 0xA7,
123 #endif
124
125         /* row 3 */
126         0xD4, 0xD5, 0xD6, 0xD7,
127         0xD8, 0xD9, 0xDA, 0xDB,
128         0xDC, 0xDD, 0xDE, 0xDF,
129         0xE0, 0xE1, 0xE2, 0xE3,
130 #if LCD_COLS > 16
131         0xE4, 0xE5, 0xE6, 0xE7,
132 #endif
133
134 #endif /* LCD_ROWS > 2 */
135 };
136
137 STATIC_ASSERT(countof(lcd_address) == LCD_ROWS * LCD_COLS);
138 #else  /* CONFIG_LCD_ADDRESS_FAST == 0 */
139
140 static const uint8_t col_address[] =
141 {
142         0x80,
143         0xC0,
144 #if LCD_ROWS > 2
145         0x94,
146         0xD4
147 #endif
148 };
149 STATIC_ASSERT(countof(col_address) == LCD_ROWS);
150 /**
151  * Addresses of LCD display character positions, calculated runtime to save RAM
152  */
153 static uint8_t lcd_address(uint8_t addr)
154 {
155         return col_address[addr / LCD_COLS] + addr % LCD_COLS;
156 }
157 #endif /* CONFIG_LCD_ADDRESS_FAST */
158
159 /**
160  * Current display position. We remember this to optimize
161  * LCD output by avoiding to set the address every time.
162  */
163 static lcdpos_t lcd_current_addr;
164
165
166 #if !defined(ARCH_EMUL) || !(ARCH & ARCH_EMUL)
167 /*      __________________
168  * RS
169  *
170  * R/W  __________________
171  *            _______
172  * ENA  _____/       \____
173  *
174  * DATA -<================
175  */
176 INLINE void lcd_dataWrite(uint8_t data)
177 {
178 #if CONFIG_LCD_4BIT
179         /* Write high nibble */
180         LCD_WRITE_H(data);
181         LCD_SET_E;
182         LCD_DELAY_WRITE;
183         LCD_CLR_E;
184         LCD_DELAY_WRITE;
185
186         /* Write low nibble */
187         LCD_WRITE_L(data);
188         LCD_SET_E;
189         LCD_DELAY_WRITE;
190         LCD_CLR_E;
191         LCD_DELAY_WRITE;
192
193 #else /* !CONFIG_LCD_4BIT */
194
195         /* Write data */
196         LCD_WRITE(data);
197         LCD_SET_E;
198         LCD_DELAY_WRITE;
199         LCD_CLR_E;
200         LCD_DELAY_WRITE;
201
202 #endif /* !CONFIG_LCD_4BIT */
203 }
204
205 /*      __________________
206  * RS
207  *         ____________
208  * R/W  __/            \__
209  *            _______
210  * ENA  _____/       \____
211  *        ______      ____
212  * DATA X/      \====/
213  */
214 INLINE uint8_t lcd_dataRead(void)
215 {
216         uint8_t data;
217
218         LCD_SET_RD;
219         LCD_DB_IN;      /* Set bus as input! */
220         LCD_DELAY_READ;
221
222 #if CONFIG_LCD_4BIT
223
224         /* Read high nibble */
225         LCD_SET_E;
226         LCD_DELAY_READ;
227         data = LCD_READ_H;
228         LCD_CLR_E;
229         LCD_DELAY_READ;
230
231         /* Read low nibble */
232         LCD_SET_E;
233         LCD_DELAY_READ;
234         data |= LCD_READ_L;
235         LCD_CLR_E;
236         LCD_DELAY_READ;
237
238 #else /* !CONFIG_LCD_4BIT */
239
240         /* Read data */
241         LCD_SET_E;
242         LCD_DELAY_READ;
243         data = LCD_READ;
244         LCD_CLR_E;
245         LCD_DELAY_READ;
246
247 #endif /* !CONFIG_LCD_4BIT */
248
249         LCD_CLR_RD;
250         LCD_DB_OUT;     /* Reset bus as output! */
251
252         return data;
253 }
254
255 /*      ___             __
256  * RS      \___________/
257  *
258  * READ __________________
259  *            _______
260  * ENA  _____/       \____
261  *
262  * DATA --<===============
263  */
264 INLINE void lcd_regWrite(uint8_t data)
265 {
266         LCD_CLR_RS;
267         lcd_dataWrite(data);
268         LCD_SET_RS;
269 }
270
271 /*      __               _
272  * RS     \_____________/
273  *          ___________
274  * READ ___/           \__
275  *            _______
276  * ENA  _____/       \____
277  *        ______      ____
278  * DATA X/      \====/
279  */
280 INLINE uint8_t lcd_regRead(void)
281 {
282         uint8_t data;
283
284         LCD_CLR_RS;
285         data = lcd_dataRead();
286         LCD_SET_RS;
287         return data;
288 }
289
290 #if CONFIG_LCD_4BIT
291
292 INLINE void lcd_mode4Bit(void)
293 {
294         LCD_CLR_RS;
295
296         LCD_WRITE_H(LCD_CMD_SETFUNC);
297         LCD_SET_E;
298         LCD_DELAY_WRITE;
299         LCD_CLR_E;
300         LCD_DELAY_WRITE;
301
302         LCD_SET_RS;
303 }
304
305 #endif /* CONFIG_LCD_4BIT */
306
307 #else /* ARCH_EMUL */
308
309 extern void Emul_LCDWriteReg(uint8_t d);
310 extern uint8_t Emul_LCDReadReg(void);
311 extern void Emul_LCDWriteData(uint8_t d);
312 extern uint8_t Emul_LCDReadData(void);
313
314 #define lcd_regWrite(d)   Emul_LCDWriteReg(d)
315 #define lcd_regRead(d)    Emul_LCDReadReg()
316 #define lcd_dataWrite(d)  Emul_LCDWriteData(d)
317 #define lcd_dataRead(d)   Emul_LCDReadData()
318
319 #endif /* ARCH_EMUL */
320
321
322 /**
323  * Wait until the LCD busy flag clears.
324  */
325 void lcd_waitBusy(void)
326 {
327         for (;;)
328         {
329                 uint8_t val = lcd_regRead();
330                 if (!(val & LCDF_BUSY))
331                         break;
332         }
333 }
334
335
336 /**
337  * Move the cursor to \a addr, only if not already there.
338  */
339 void lcd_moveTo(uint8_t addr)
340 {
341         if (addr != lcd_current_addr)
342         {
343                 lcd_waitBusy();
344                 lcd_regWrite(lcd_address(addr));
345                 lcd_current_addr = addr;
346         }
347 }
348
349
350 /**
351  * Write a value in LCD data register, waiting for the busy flag.
352  */
353 void lcd_setReg(uint8_t val)
354 {
355         lcd_waitBusy();
356         lcd_regWrite(val);
357 }
358
359 #include <cfg/debug.h>
360 /**
361  * Write the character \a c on display address \a addr.
362  *
363  * NOTE: argh, the HD44 lcd type is a bad beast: our
364  * move/write -> write optimization requires this mess
365  * because display lines are interleaved!
366  */
367 void lcd_putc(uint8_t addr, uint8_t c)
368 {
369         if (addr != lcd_current_addr)
370                 lcd_setReg(lcd_address(addr));
371
372         lcd_waitBusy();
373         lcd_dataWrite(c);
374         lcd_current_addr = addr + 1;
375
376         /* If we are at end of display wrap the address to 0 */
377         if (lcd_current_addr == LCD_COLS * LCD_ROWS)
378                 lcd_current_addr = 0;
379
380         /* If we are at the end of a row put the cursor at the beginning of the next */
381         if (!(lcd_current_addr % LCD_COLS))
382                 lcd_setReg(lcd_address(lcd_current_addr));
383 }
384
385
386 /**
387  * Remap the glyph of a character.
388  *
389  * glyph - bitmap of 8x8 bits.
390  * code - must be 0-7 for the Hitachi LCD-II controller.
391  */
392 void lcd_remapChar(const char *glyph, char code)
393 {
394         int i;
395
396         /* Set CG RAM address */
397         lcd_setReg((uint8_t)((1<<6) | (code << 3)));
398
399         /* Write bitmap data */
400         for (i = 0; i < 8; i++)
401         {
402                 lcd_waitBusy();
403                 lcd_dataWrite(glyph[i]);
404         }
405
406         /* Move back to original address */
407         lcd_setReg(lcd_address(lcd_current_addr));
408 }
409
410
411 #if 0 /* unused */
412 void lcd_remapfont(void)
413 {
414         static const char lcd_glyphs[8] =
415         {
416                 0x04, 0x0E, 0x15, 0x04, 0x04, 0x04, 0x04, 0x00 /* up arrow */
417         };
418         int i;
419
420         for (i = 0; i < 15; i++)
421                 lcd_remapChar(i, bernie_char);
422
423
424         lcd_setAddr(lcd_DefLayer, 0);
425         for (i = 0; i < 80; i++)
426                 lcd_putCharUnlocked(i);
427 }
428 #endif /* unused */
429
430 void lcd_hw_init(void)
431 {
432         lcd_bus_init();
433
434         timer_delay(50);
435
436 #if CONFIG_LCD_4BIT
437         lcd_mode4Bit();
438         timer_delay(2);
439 #endif /* CONFIG_LCD_4BIT */
440
441         lcd_regWrite(LCD_CMD_SETFUNC);
442         timer_delay(2);
443
444         lcd_regWrite(LCD_CMD_DISPLAY_ON);
445         timer_delay(2);
446
447         lcd_regWrite(LCD_CMD_CLEAR);
448         timer_delay(2);
449
450 #if !CONFIG_LCD_4BIT
451         lcd_regWrite(LCD_CMD_RESET_DDRAM); // 4 bit mode doesn't allow char reprogramming
452 #endif
453         lcd_regWrite(LCD_CMD_DISPLAYMODE);
454         timer_delay(2);
455 }
456
457 #if CONFIG_TEST
458
459 void lcd_hw_test(void)
460 {
461         lcd_regWrite(LCD_CMD_SET_DDRAMADDR | 3);
462         timer_delay(1);
463         kprintf("3 -> %02X\n", lcd_regRead());
464         timer_delay(1);
465
466         for (int i = 0; i < 10; i++)
467         {
468                 lcd_dataWrite('c');
469                 timer_delay(1);
470                 kprintf("addr = %02X\n", lcd_regRead());
471                 timer_delay(1);
472         }
473
474         lcd_regWrite(LCD_CMD_SET_DDRAMADDR | 0x4a);
475         timer_delay(1);
476         kprintf("4A -> %02X\n", lcd_regRead());
477         timer_delay(1);
478
479         lcd_regWrite(LCD_CMD_SET_DDRAMADDR | 0x52);
480         timer_delay(1);
481         kprintf("52 -> %02X\n", lcd_regRead());
482         timer_delay(1);
483
484         lcd_regWrite(LCD_CMD_SET_DDRAMADDR | 0x1F);
485         timer_delay(1);
486         kprintf("1F -> %02X\n", lcd_regRead());
487         timer_delay(1);
488 }
489
490 #endif /* CONFIG_TEST */