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