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