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