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