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