2 * HD44780 LCD Controller
4 * Copyright (c) 2009 Filip Navara
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 #include "pixel_ops.h"
31 Register Select (input only)
32 Read/Write (input only)
34 Backlight (input only) */
43 #define LCD_CLR 1 /* DB0: clear display */
44 #define LCD_HOME 2 /* DB1: return to home position */
45 #define LCD_ENTRY_MODE 4 /* DB2: set entry mode */
46 #define LCD_ENTRY_INC 2 /* DB1: increment */
47 #define LCD_ENTRY_SHIFT 1 /* DB2: shift */
48 #define LCD_ON_CTRL 8 /* DB3: turn lcd/cursor on */
49 #define LCD_ON_DISPLAY 4 /* DB2: turn display on */
50 #define LCD_ON_CURSOR 2 /* DB1: turn cursor on */
51 #define LCD_ON_BLINK 1 /* DB0: blinking cursor */
52 #define LCD_MOVE 16 /* DB4: move cursor/display */
53 #define LCD_MOVE_DISP 8 /* DB3: move display (0-> move cursor) */
54 #define LCD_MOVE_RIGHT 4 /* DB2: move right (0-> left) */
55 #define LCD_FUNCTION 32 /* DB5: function set */
56 #define LCD_FUNCTION_8BIT 16 /* DB4: set 8BIT mode (0->4BIT mode) */
57 #define LCD_FUNCTION_2LINES 8 /* DB3: two lines (0->one line) */
58 #define LCD_FUNCTION_10DOTS 4 /* DB2: 5x10 font (0->5x7 font) */
59 #define LCD_CGRAM 64 /* DB6: set CG RAM address */
60 #define LCD_DDRAM 128 /* DB7: set DD RAM address */
61 #define LCD_BUSY 64 /* DB7: LCD is busy */
67 uint8_t font5x7
[][7]={
68 /* */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00},
69 /* ! */ {0x04,0x04,0x04,0x04,0x00,0x00,0x04},
70 /* " */ {0x0A,0x0A,0x0A,0x00,0x00,0x00,0x00},
71 /* # */ {0x0A,0x0A,0x1F,0x0A,0x1F,0x0A,0x0A},
72 /* $ */ {0x04,0x0F,0x14,0x0E,0x05,0x1E,0x04},
73 /* % */ {0x18,0x19,0x02,0x04,0x08,0x13,0x03},
74 /* & */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00},
75 /* ' */ {0x0C,0x04,0x08,0x00,0x00,0x00,0x00},
76 /* ( */ {0x02,0x04,0x08,0x08,0x08,0x04,0x02},
77 /* ) */ {0x08,0x04,0x02,0x02,0x02,0x04,0x08},
78 /* * */ {0x00,0x04,0x15,0x0E,0x15,0x04,0x00},
79 /* + */ {0x00,0x04,0x04,0x1F,0x04,0x04,0x00},
80 /* , */ {0x00,0x00,0x00,0x00,0x0C,0x04,0x08},
81 /* - */ {0x00,0x00,0x00,0x1F,0x00,0x00,0x00},
82 /* . */ {0x00,0x00,0x00,0x00,0x00,0x0C,0x0C},
83 /* / */ {0x00,0x01,0x02,0x04,0x08,0x10,0x00},
84 /* 0 */ {0x0E,0x11,0x13,0x15,0x19,0x11,0x0E},
85 /* 1 */ {0x04,0x0C,0x04,0x04,0x04,0x04,0x0E},
86 /* 2 */ {0x0E,0x11,0x01,0x02,0x04,0x08,0x1F},
87 /* 3 */ {0x1F,0x02,0x04,0x02,0x01,0x11,0x0E},
88 /* 4 */ {0x02,0x06,0x0A,0x12,0x1F,0x02,0x02},
89 /* 5 */ {0x1F,0x10,0x1E,0x01,0x01,0x11,0x0E},
90 /* 6 */ {0x06,0x08,0x10,0x1E,0x11,0x11,0x0E},
91 /* 7 */ {0x1F,0x01,0x02,0x04,0x08,0x08,0x08},
92 /* 8 */ {0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E},
93 /* 9 */ {0x0E,0x11,0x11,0x0F,0x01,0x02,0x0C},
94 /* : */ {0x00,0x0C,0x0C,0x00,0x0C,0x0C,0x00},
95 /* ; */ {0x00,0x0C,0x0C,0x00,0x0C,0x04,0x08},
96 /* < */ {0x02,0x04,0x08,0x10,0x08,0x04,0x02},
97 /* = */ {0x00,0x00,0x1F,0x00,0x1F,0x00,0x00},
98 /* > */ {0x08,0x04,0x02,0x01,0x02,0x04,0x08},
99 /* ? */ {0x0E,0x11,0x01,0x02,0x04,0x00,0x04},
100 /* @ */ {0x0E,0x11,0x01,0x0D,0x15,0x15,0x0E},
101 /* A */ {0x0E,0x11,0x11,0x11,0x1F,0x11,0x11},
102 /* B */ {0x1E,0x11,0x11,0x1E,0x11,0x11,0x1E},
103 /* C */ {0x0E,0x11,0x10,0x10,0x10,0x11,0x0E},
104 /* D */ {0x1C,0x12,0x11,0x11,0x11,0x12,0x1C},
105 /* E */ {0x1F,0x10,0x10,0x1E,0x10,0x10,0x1F},
106 /* F */ {0x1F,0x10,0x10,0x1E,0x10,0x10,0x10},
107 /* G */ {0x0E,0x11,0x10,0x17,0x11,0x11,0x0F},
108 /* H */ {0x11,0x11,0x11,0x1F,0x11,0x11,0x11},
109 /* I */ {0x0E,0x04,0x04,0x04,0x04,0x04,0x0E},
110 /* J */ {0x07,0x02,0x02,0x02,0x02,0x12,0x0C},
111 /* K */ {0x11,0x12,0x14,0x18,0x14,0x12,0x11},
112 /* L */ {0x10,0x10,0x10,0x10,0x10,0x10,0x1F},
113 /* M */ {0x11,0x1B,0x15,0x15,0x11,0x11,0x11},
114 /* N */ {0x11,0x11,0x19,0x15,0x13,0x11,0x11},
115 /* O */ {0x0E,0x11,0x11,0x11,0x11,0x11,0x0E},
116 /* P */ {0x1E,0x11,0x11,0x1E,0x10,0x10,0x10},
117 /* Q */ {0x0E,0x11,0x11,0x11,0x15,0x12,0x0D},
118 /* R */ {0x1E,0x11,0x11,0x1E,0x14,0x12,0x11},
119 /* S */ {0x0F,0x10,0x10,0x0E,0x01,0x01,0x1E},
120 /* T */ {0x1F,0x04,0x04,0x04,0x04,0x04,0x04},
121 /* U */ {0x11,0x11,0x11,0x11,0x11,0x11,0x0E},
122 /* V */ {0x11,0x11,0x11,0x11,0x11,0x0A,0x04},
123 /* W */ {0x11,0x11,0x11,0x15,0x15,0x15,0x0A},
124 /* X */ {0x11,0x11,0x0A,0x04,0x0A,0x11,0x11},
125 /* Y */ {0x11,0x11,0x11,0x0A,0x04,0x04,0x04},
126 /* Z */ {0x1F,0x01,0x02,0x04,0x08,0x10,0x1F},
127 /* [ */ {0x0E,0x08,0x08,0x08,0x08,0x08,0x0E},
128 /* \ */ {0x00,0x10,0x08,0x04,0x02,0x01,0x00},
129 /* ] */ {0x0E,0x02,0x02,0x02,0x02,0x02,0x0E},
130 /* ^ */ {0x04,0x0A,0x11,0x00,0x00,0x00,0x00},
131 /* _ */ {0x00,0x00,0x00,0x00,0x00,0x00,0x1F},
132 /* ` */ {0x08,0x04,0x02,0x00,0x00,0x00,0x00},
133 /* a */ {0x00,0x00,0x0E,0x01,0x0F,0x11,0x0F},
134 /* b */ {0x10,0x10,0x10,0x16,0x19,0x11,0x1E},
135 /* c */ {0x00,0x00,0x0E,0x10,0x10,0x11,0x0E},
136 /* d */ {0x01,0x01,0x01,0x0D,0x13,0x11,0x0F},
137 /* e */ {0x00,0x00,0x0E,0x11,0x1F,0x10,0x0E},
138 /* f */ {0x06,0x09,0x08,0x1C,0x08,0x08,0x08},
139 /* g */ {0x00,0x0F,0x11,0x11,0x0F,0x01,0x0E},
140 /* h */ {0x10,0x10,0x16,0x19,0x11,0x11,0x11},
141 /* i */ {0x00,0x04,0x00,0x04,0x04,0x04,0x04},
142 /* j */ {0x02,0x00,0x06,0x02,0x02,0x12,0x0C},
143 /* k */ {0x10,0x10,0x12,0x14,0x18,0x14,0x12},
144 /* l */ {0x04,0x04,0x04,0x04,0x04,0x04,0x0F},
145 /* m */ {0x00,0x00,0x1A,0x15,0x15,0x11,0x11},
146 /* n */ {0x00,0x00,0x16,0x19,0x11,0x11,0x11},
147 /* o */ {0x00,0x00,0x0E,0x11,0x11,0x11,0x0E},
148 /* p */ {0x00,0x00,0x1E,0x11,0x1E,0x10,0x10},
149 /* q */ {0x00,0x00,0x0D,0x13,0x0F,0x01,0x01},
150 /* r */ {0x00,0x00,0x16,0x19,0x10,0x10,0x10},
151 /* s */ {0x00,0x00,0x0E,0x10,0x0E,0x01,0x1E},
152 /* t */ {0x08,0x08,0x1C,0x08,0x08,0x09,0x06},
153 /* u */ {0x00,0x00,0x11,0x11,0x11,0x13,0x0D},
154 /* v */ {0x00,0x00,0x11,0x11,0x11,0x0A,0x04},
155 /* w */ {0x00,0x00,0x11,0x11,0x15,0x15,0x0A},
156 /* x */ {0x00,0x00,0x11,0x0A,0x04,0x0A,0x11},
157 /* y */ {0x00,0x00,0x11,0x11,0x0F,0x01,0x0E},
158 /* z */ {0x00,0x00,0x1F,0x02,0x04,0x08,0x1F},
159 /* { */ {0x02,0x04,0x04,0x08,0x04,0x04,0x02},
160 /* | */ {0x04,0x04,0x04,0x04,0x04,0x04,0x04},
161 /* } */ {0x08,0x04,0x04,0x02,0x04,0x04,0x08},
164 typedef struct LCDState
{
170 uint32_t control
: 1;
172 uint32_t backlight
: 1;
174 uint8_t mode8bit
: 1;
175 uint8_t write_low
: 1;
177 uint8_t ac
; /* address counter */
178 uint8_t dispcol
; /* first visible column (display shift!) */
179 uint8_t id
: 1; /* cursor move increase(1)/decrease(0) */
180 uint8_t sh
: 1; /* shift display(1) */
181 uint8_t ddram
: 1; /* access ddram(1)/cgram(0) */
187 uint8_t two_lines
: 1;
188 uint8_t font5x10
: 1;
190 uint8_t need_update
: 1;
194 static void draw_char(DisplayState
*ds
, int x
, int y
, char ch
, uint32_t color
, uint32_t backcolor
)
200 bpp
= (ds_get_bits_per_pixel(ds
) + 7) >> 3;
201 for (line
= 0; line
< 7 * CZOOM
; line
++) {
202 d
= ds_get_data(ds
) + ds_get_linesize(ds
) * y
+ bpp
* x
;
203 if (ch
>= ' ' && (ch
- ' ') < sizeof(font5x7
)/sizeof(font5x7
[0]))
204 cdata
= font5x7
[(int)(ch
- ' ')][line
/ CZOOM
];
206 cdata
= font5x7
[0][line
/ CZOOM
];
210 for (i
= 0; i
< 5 * CZOOM
; i
++) {
211 *((uint8_t *)d
) = (cdata
& (1 << (i
/ CZOOM
))) ? color
: backcolor
;
217 for (i
= 0; i
< 5 * CZOOM
; i
++) {
218 *((uint16_t *)d
) = (cdata
& (1 << (i
/ CZOOM
))) ? color
: backcolor
;
224 for (i
= 0; i
< 5 * CZOOM
; i
++) {
225 *((uint32_t *)d
) = (cdata
& (1 << (i
/ CZOOM
))) ? color
: backcolor
;
234 static void hd44780_enable(LCDState
*s
)
237 //D(printf("CONTROL: %x\n", s->input));
238 if (s
->input
& LCD_DDRAM
) {
239 int ddram_addr
= s
->input
& ~LCD_DDRAM
;
240 if (ddram_addr
>= 0 && ddram_addr
< WIDTH
)
242 else if (ddram_addr
>= 64 && ddram_addr
< 64 + WIDTH
)
243 s
->ac
= (ddram_addr
- 64) + WIDTH
;
244 else if (ddram_addr
>= 20 && ddram_addr
< 20 + WIDTH
)
245 s
->ac
= (ddram_addr
- 20) + WIDTH
* 2;
246 else if (ddram_addr
>= 84 && ddram_addr
< 84 + WIDTH
)
247 s
->ac
= (ddram_addr
- 84) + WIDTH
* 3;
249 } else if (s
->input
& LCD_CGRAM
) {
250 s
->ac
= s
->input
& ~LCD_CGRAM
;
252 } else if (s
->input
& LCD_FUNCTION
) {
253 s
->mode8bit
= !!(s
->input
& LCD_FUNCTION_8BIT
);
254 s
->two_lines
= !!(s
->input
& LCD_FUNCTION_2LINES
);
255 s
->font5x10
= !!(s
->input
& LCD_FUNCTION_10DOTS
);
256 } else if (s
->input
& LCD_MOVE
) {
257 if (s
->input
& LCD_MOVE_DISP
) {
258 if (s
->input
& LCD_MOVE_RIGHT
) {
263 s
->dispcol
%= sizeof(s
->data
);
267 } else if (s
->input
& LCD_ON_CTRL
) {
268 s
->display
= !!(s
->input
& LCD_ON_DISPLAY
);
269 s
->cursor
= !!(s
->input
& LCD_ON_CURSOR
);
270 s
->blink
= !!(s
->input
& LCD_ON_BLINK
);
271 } else if (s
->input
& LCD_ENTRY_MODE
) {
272 s
->id
= !!(s
->input
& LCD_ENTRY_INC
);
273 s
->sh
= !!(s
->input
& LCD_ENTRY_SHIFT
);
274 } else if (s
->input
& LCD_HOME
) {
278 } else if (s
->input
& LCD_CLR
) {
279 memset(s
->data
, 32, sizeof(s
->data
));
287 s
->data
[s
->ac
] = s
->input
;
289 s
->ac
%= sizeof(s
->data
);
296 s
->dispcol
%= sizeof(s
->data
);
305 static void hd44780_set_pin(void *opaque
, int pin
, int level
)
307 LCDState
*s
= opaque
;
309 if (pin
>= 0 && pin
<= 7) {
311 if (!s
->mode8bit
&& s
->write_low
)
314 s
->input
|= 1 << pin
;
316 s
->input
&= ~(1 << pin
);
318 } else if (pin
== PIN_RS
) {
320 } else if (pin
== PIN_RW
) {
322 } else if (pin
== PIN_E
) {
323 if (!level
&& !s
->rw
) {
325 s
->write_low
= !s
->write_low
;
332 } else if (pin
== PIN_BL
) {
333 s
->backlight
= level
;
338 static void hd44780_update_display(void *opaque
)
340 LCDState
*s
= opaque
;
341 uint32_t color_segment
, color_led
;
344 if (s
->need_update
) {
355 switch (ds_get_bits_per_pixel(s
->ds
)) {
357 color_segment
= rgb_to_pixel8(0, 0, 0);
358 color_led
= rgb_to_pixel8(r
, g
, b
);
361 color_segment
= rgb_to_pixel15(0, 0, 0);
362 color_led
= rgb_to_pixel15(r
, g
, b
);
365 color_segment
= rgb_to_pixel16(0, 0, 0);
366 color_led
= rgb_to_pixel16(r
, g
, b
);
369 color_segment
= rgb_to_pixel24(0, 0, 0);
370 color_led
= rgb_to_pixel24(r
, g
, b
);
373 color_segment
= rgb_to_pixel32(0, 0, 0);
374 color_led
= rgb_to_pixel32(r
, g
, b
);
381 for (y
= 0; y
< HEIGHT
; y
++) {
382 for (x
= 0; x
< WIDTH
; x
++) {
383 draw_char(s
->ds
, x
* 5 * CZOOM
, y
* 7 * CZOOM
, s
->data
[y
* WIDTH
+ x
], color_segment
, color_led
);
388 dpy_update(s
->ds
, 0, 0, WIDTH
* CZOOM
* 5, HEIGHT
* CZOOM
* 7);
392 static void hd44780_invalidate_display(void * opaque
)
394 LCDState
*s
= opaque
;
398 static void hd44780_init(SysBusDevice
*dev
)
400 LCDState
*s
= FROM_SYSBUS(LCDState
, dev
);
404 qdev_init_gpio_in(&dev
->qdev
, hd44780_set_pin
, 12);
405 qdev_init_gpio_out(&dev
->qdev
, s
->out
, 8);
407 s
->ds
= graphic_console_init(hd44780_update_display
,
408 hd44780_invalidate_display
,
410 qemu_console_resize(s
->ds
, WIDTH
* CZOOM
* 5, HEIGHT
* CZOOM
* 7);
413 static void hd44780_register(void)
415 sysbus_register_dev("gpio,hd44780", sizeof(LCDState
),
419 device_init(hd44780_register
)