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