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