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