[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 14/14] HD44780 LCD Controller
From: |
Filip Navara |
Subject: |
[Qemu-devel] [PATCH 14/14] HD44780 LCD Controller |
Date: |
Wed, 15 Jul 09 16:52:36 Central Europe Standard Time |
Only very simple emulation of the LCD controller is included.
The controller features the standard HD44780 interface and supports character
output on four lines using a static 5x7 font and optional backlight controlled
by signal on a separate pin.
Capabilities such as custom characters, cursor and 5x10 fonts are not
implemented. Saving/loading of device state is not implemented either.
Signed-off-by: Filip Navara <address@hidden>
---
Makefile.target | 2 +-
hw/at91pes.c | 10 ++
hw/hd44780.c | 418 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 429 insertions(+), 1 deletions(-)
create mode 100644 hw/hd44780.c
diff --git a/Makefile.target b/Makefile.target
index dca5340..74d7d5e 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -668,7 +668,7 @@ obj-y += syborg_serial.o syborg_timer.o syborg_pointer.o
syborg_rtc.o
obj-y += syborg_virtio.o
obj-y += at91_aic.o at91_dbgu.o at91_pio.o at91_pit.o at91_pmc.o at91_rtt.o
obj-y += at91_rstc.o at91_intor.o at91_tc.o at91_emac.o at91pes.o
-obj-y += gpio_rotary.o gpio_keypad.o
+obj-y += gpio_rotary.o gpio_keypad.o hd44780.o
CPPFLAGS += -DHAS_AUDIO
endif
ifeq ($(TARGET_BASE_ARCH), sh4)
diff --git a/hw/at91pes.c b/hw/at91pes.c
index 544eaaf..b58cc25 100644
--- a/hw/at91pes.c
+++ b/hw/at91pes.c
@@ -95,6 +95,16 @@ static void at91pes_init(ram_addr_t ram_size,
sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xFFFDC000);
sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[16]);
+ dev = qdev_create(NULL, "gpio,hd44780");
+ qdev_init(dev);
+ for (i = 0; i < 3; i++)
+ qdev_connect_gpio_out(pioa, 15 - i, qdev_get_gpio_in(dev, 8 + i));
+ qdev_connect_gpio_out(piob, 28, qdev_get_gpio_in(dev, 11));
+ for (i = 0; i < 4; i++) {
+ qdev_connect_gpio_out(pioa, 5 + i, qdev_get_gpio_in(dev, 4 + i));
+ qdev_connect_gpio_out(dev, 4 + i, qdev_get_gpio_in(pioa, 5 + i));
+ }
+
dev = qdev_create(NULL, "gpio,keypad");
qdev_set_prop_ptr(dev, "keys", keys);
qdev_init(dev);
diff --git a/hw/hd44780.c b/hw/hd44780.c
new file mode 100644
index 0000000..fc84f9d
--- /dev/null
+++ b/hw/hd44780.c
@@ -0,0 +1,418 @@
+/*
+ * HD44780 LCD Controller
+ *
+ * Copyright (c) 2009 Filip Navara
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "console.h"
+#include "pixel_ops.h"
+
+/* pin mapping:
+ 8 Data
+ Register Select (input only)
+ Read/Write (input only)
+ Enable (input only)
+ Backlight (input only) */
+
+#define D(x) x
+
+#define PIN_RS 8
+#define PIN_RW 9
+#define PIN_E 10
+#define PIN_BL 11
+
+#define LCD_CLR 1 /* DB0: clear display */
+#define LCD_HOME 2 /* DB1: return to home position */
+#define LCD_ENTRY_MODE 4 /* DB2: set entry mode */
+#define LCD_ENTRY_INC 2 /* DB1: increment */
+#define LCD_ENTRY_SHIFT 1 /* DB2: shift */
+#define LCD_ON_CTRL 8 /* DB3: turn lcd/cursor on */
+#define LCD_ON_DISPLAY 4 /* DB2: turn display on */
+#define LCD_ON_CURSOR 2 /* DB1: turn cursor on */
+#define LCD_ON_BLINK 1 /* DB0: blinking cursor */
+#define LCD_MOVE 16 /* DB4: move cursor/display */
+#define LCD_MOVE_DISP 8 /* DB3: move display (0-> move cursor) */
+#define LCD_MOVE_RIGHT 4 /* DB2: move right (0-> left) */
+#define LCD_FUNCTION 32 /* DB5: function set */
+#define LCD_FUNCTION_8BIT 16 /* DB4: set 8BIT mode (0->4BIT mode) */
+#define LCD_FUNCTION_2LINES 8 /* DB3: two lines (0->one line) */
+#define LCD_FUNCTION_10DOTS 4 /* DB2: 5x10 font (0->5x7 font) */
+#define LCD_CGRAM 64 /* DB6: set CG RAM address */
+#define LCD_DDRAM 128 /* DB7: set DD RAM address */
+#define LCD_BUSY 64 /* DB7: LCD is busy */
+
+#define WIDTH 20
+#define HEIGHT 4
+#define CZOOM 3
+
+uint8_t font5x7[][7]={
+/* */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+/* ! */ {0x04,0x04,0x04,0x04,0x00,0x00,0x04},
+/* " */ {0x0A,0x0A,0x0A,0x00,0x00,0x00,0x00},
+/* # */ {0x0A,0x0A,0x1F,0x0A,0x1F,0x0A,0x0A},
+/* $ */ {0x04,0x0F,0x14,0x0E,0x05,0x1E,0x04},
+/* % */ {0x18,0x19,0x02,0x04,0x08,0x13,0x03},
+/* & */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+/* ' */ {0x0C,0x04,0x08,0x00,0x00,0x00,0x00},
+/* ( */ {0x02,0x04,0x08,0x08,0x08,0x04,0x02},
+/* ) */ {0x08,0x04,0x02,0x02,0x02,0x04,0x08},
+/* * */ {0x00,0x04,0x15,0x0E,0x15,0x04,0x00},
+/* + */ {0x00,0x04,0x04,0x1F,0x04,0x04,0x00},
+/* , */ {0x00,0x00,0x00,0x00,0x0C,0x04,0x08},
+/* - */ {0x00,0x00,0x00,0x1F,0x00,0x00,0x00},
+/* . */ {0x00,0x00,0x00,0x00,0x00,0x0C,0x0C},
+/* / */ {0x00,0x01,0x02,0x04,0x08,0x10,0x00},
+/* 0 */ {0x0E,0x11,0x13,0x15,0x19,0x11,0x0E},
+/* 1 */ {0x04,0x0C,0x04,0x04,0x04,0x04,0x0E},
+/* 2 */ {0x0E,0x11,0x01,0x02,0x04,0x08,0x1F},
+/* 3 */ {0x1F,0x02,0x04,0x02,0x01,0x11,0x0E},
+/* 4 */ {0x02,0x06,0x0A,0x12,0x1F,0x02,0x02},
+/* 5 */ {0x1F,0x10,0x1E,0x01,0x01,0x11,0x0E},
+/* 6 */ {0x06,0x08,0x10,0x1E,0x11,0x11,0x0E},
+/* 7 */ {0x1F,0x01,0x02,0x04,0x08,0x08,0x08},
+/* 8 */ {0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E},
+/* 9 */ {0x0E,0x11,0x11,0x0F,0x01,0x02,0x0C},
+/* : */ {0x00,0x0C,0x0C,0x00,0x0C,0x0C,0x00},
+/* ; */ {0x00,0x0C,0x0C,0x00,0x0C,0x04,0x08},
+/* < */ {0x02,0x04,0x08,0x10,0x08,0x04,0x02},
+/* = */ {0x00,0x00,0x1F,0x00,0x1F,0x00,0x00},
+/* > */ {0x08,0x04,0x02,0x01,0x02,0x04,0x08},
+/* ? */ {0x0E,0x11,0x01,0x02,0x04,0x00,0x04},
+/* @ */ {0x0E,0x11,0x01,0x0D,0x15,0x15,0x0E},
+/* A */ {0x0E,0x11,0x11,0x11,0x1F,0x11,0x11},
+/* B */ {0x1E,0x11,0x11,0x1E,0x11,0x11,0x1E},
+/* C */ {0x0E,0x11,0x10,0x10,0x10,0x11,0x0E},
+/* D */ {0x1C,0x12,0x11,0x11,0x11,0x12,0x1C},
+/* E */ {0x1F,0x10,0x10,0x1E,0x10,0x10,0x1F},
+/* F */ {0x1F,0x10,0x10,0x1E,0x10,0x10,0x10},
+/* G */ {0x0E,0x11,0x10,0x17,0x11,0x11,0x0F},
+/* H */ {0x11,0x11,0x11,0x1F,0x11,0x11,0x11},
+/* I */ {0x0E,0x04,0x04,0x04,0x04,0x04,0x0E},
+/* J */ {0x07,0x02,0x02,0x02,0x02,0x12,0x0C},
+/* K */ {0x11,0x12,0x14,0x18,0x14,0x12,0x11},
+/* L */ {0x10,0x10,0x10,0x10,0x10,0x10,0x1F},
+/* M */ {0x11,0x1B,0x15,0x15,0x11,0x11,0x11},
+/* N */ {0x11,0x11,0x19,0x15,0x13,0x11,0x11},
+/* O */ {0x0E,0x11,0x11,0x11,0x11,0x11,0x0E},
+/* P */ {0x1E,0x11,0x11,0x1E,0x10,0x10,0x10},
+/* Q */ {0x0E,0x11,0x11,0x11,0x15,0x12,0x0D},
+/* R */ {0x1E,0x11,0x11,0x1E,0x14,0x12,0x11},
+/* S */ {0x0F,0x10,0x10,0x0E,0x01,0x01,0x1E},
+/* T */ {0x1F,0x04,0x04,0x04,0x04,0x04,0x04},
+/* U */ {0x11,0x11,0x11,0x11,0x11,0x11,0x0E},
+/* V */ {0x11,0x11,0x11,0x11,0x11,0x0A,0x04},
+/* W */ {0x11,0x11,0x11,0x15,0x15,0x15,0x0A},
+/* X */ {0x11,0x11,0x0A,0x04,0x0A,0x11,0x11},
+/* Y */ {0x11,0x11,0x11,0x0A,0x04,0x04,0x04},
+/* Z */ {0x1F,0x01,0x02,0x04,0x08,0x10,0x1F},
+/* [ */ {0x0E,0x08,0x08,0x08,0x08,0x08,0x0E},
+/* \ */ {0x00,0x10,0x08,0x04,0x02,0x01,0x00},
+/* ] */ {0x0E,0x02,0x02,0x02,0x02,0x02,0x0E},
+/* ^ */ {0x04,0x0A,0x11,0x00,0x00,0x00,0x00},
+/* _ */ {0x00,0x00,0x00,0x00,0x00,0x00,0x1F},
+/* ` */ {0x08,0x04,0x02,0x00,0x00,0x00,0x00},
+/* a */ {0x00,0x00,0x0E,0x01,0x0F,0x11,0x0F},
+/* b */ {0x10,0x10,0x10,0x16,0x19,0x11,0x1E},
+/* c */ {0x00,0x00,0x0E,0x10,0x10,0x11,0x0E},
+/* d */ {0x01,0x01,0x01,0x0D,0x13,0x11,0x0F},
+/* e */ {0x00,0x00,0x0E,0x11,0x1F,0x10,0x0E},
+/* f */ {0x06,0x09,0x08,0x1C,0x08,0x08,0x08},
+/* g */ {0x00,0x0F,0x11,0x11,0x0F,0x01,0x0E},
+/* h */ {0x10,0x10,0x16,0x19,0x11,0x11,0x11},
+/* i */ {0x00,0x04,0x00,0x04,0x04,0x04,0x04},
+/* j */ {0x02,0x00,0x06,0x02,0x02,0x12,0x0C},
+/* k */ {0x10,0x10,0x12,0x14,0x18,0x14,0x12},
+/* l */ {0x04,0x04,0x04,0x04,0x04,0x04,0x0F},
+/* m */ {0x00,0x00,0x1A,0x15,0x15,0x11,0x11},
+/* n */ {0x00,0x00,0x16,0x19,0x11,0x11,0x11},
+/* o */ {0x00,0x00,0x0E,0x11,0x11,0x11,0x0E},
+/* p */ {0x00,0x00,0x1E,0x11,0x1E,0x10,0x10},
+/* q */ {0x00,0x00,0x0D,0x13,0x0F,0x01,0x01},
+/* r */ {0x00,0x00,0x16,0x19,0x10,0x10,0x10},
+/* s */ {0x00,0x00,0x0E,0x10,0x0E,0x01,0x1E},
+/* t */ {0x08,0x08,0x1C,0x08,0x08,0x09,0x06},
+/* u */ {0x00,0x00,0x11,0x11,0x11,0x13,0x0D},
+/* v */ {0x00,0x00,0x11,0x11,0x11,0x0A,0x04},
+/* w */ {0x00,0x00,0x11,0x11,0x15,0x15,0x0A},
+/* x */ {0x00,0x00,0x11,0x0A,0x04,0x0A,0x11},
+/* y */ {0x00,0x00,0x11,0x11,0x0F,0x01,0x0E},
+/* z */ {0x00,0x00,0x1F,0x02,0x04,0x08,0x1F},
+/* { */ {0x02,0x04,0x04,0x08,0x04,0x04,0x02},
+/* | */ {0x04,0x04,0x04,0x04,0x04,0x04,0x04},
+/* } */ {0x08,0x04,0x04,0x02,0x04,0x04,0x08},
+};
+
+typedef struct LCDState {
+ SysBusDevice busdev;
+ qemu_irq out[8];
+ DisplayState *ds;
+
+ uint8_t input;
+ uint32_t control : 1;
+ uint32_t rw : 1;
+ uint32_t backlight : 1;
+
+ uint8_t mode8bit : 1;
+ uint8_t write_low : 1;
+
+ uint8_t ac; /* address counter */
+ uint8_t dispcol; /* first visible column (display shift!) */
+ uint8_t id : 1; /* cursor move increase(1)/decrease(0) */
+ uint8_t sh : 1; /* shift display(1) */
+ uint8_t ddram : 1; /* access ddram(1)/cgram(0) */
+
+ uint8_t display : 1;
+ uint8_t cursor : 1;
+ uint8_t blink : 1;
+
+ uint8_t two_lines : 1;
+ uint8_t font5x10 : 1;
+
+ uint8_t need_update : 1;
+ char data[20 * 4];
+} LCDState;
+
+static void draw_char(DisplayState *ds, int x, int y, char ch, uint32_t color,
uint32_t backcolor)
+{
+ uint8_t *d;
+ uint8_t cdata;
+ int i, bpp, line;
+
+ bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
+ for (line = 0; line < 7 * CZOOM; line++) {
+ d = ds_get_data(ds) + ds_get_linesize(ds) * y + bpp * x;
+ if (ch >= ' ' && (ch - ' ') < sizeof(font5x7)/sizeof(font5x7[0]))
+ cdata = font5x7[(int)(ch - ' ')][line / CZOOM];
+ else
+ cdata = font5x7[0][line / CZOOM];
+ switch(bpp) {
+ case 1:
+ d += 5 * CZOOM;
+ for (i = 0; i < 5 * CZOOM; i++) {
+ *((uint8_t *)d) = (cdata & (1 << (i / CZOOM))) ? color :
backcolor;
+ d--;
+ }
+ break;
+ case 2:
+ d += 10 * CZOOM;
+ for (i = 0; i < 5 * CZOOM; i++) {
+ *((uint16_t *)d) = (cdata & (1 << (i / CZOOM))) ? color :
backcolor;
+ d -= 2;
+ }
+ break;
+ case 4:
+ d += 20 * CZOOM;
+ for (i = 0; i < 5 * CZOOM; i++) {
+ *((uint32_t *)d) = (cdata & (1 << (i / CZOOM))) ? color :
backcolor;
+ d -= 4;
+ }
+ break;
+ }
+ y++;
+ }
+}
+
+static void hd44780_enable(LCDState *s)
+{
+ if (s->control) {
+ //D(printf("CONTROL: %x\n", s->input));
+ if (s->input & LCD_DDRAM) {
+ int ddram_addr = s->input & ~LCD_DDRAM;
+ if (ddram_addr >= 0 && ddram_addr < WIDTH)
+ s->ac = ddram_addr;
+ else if (ddram_addr >= 64 && ddram_addr < 64 + WIDTH)
+ s->ac = (ddram_addr - 64) + WIDTH;
+ else if (ddram_addr >= 20 && ddram_addr < 20 + WIDTH)
+ s->ac = (ddram_addr - 20) + WIDTH * 2;
+ else if (ddram_addr >= 84 && ddram_addr < 84 + WIDTH)
+ s->ac = (ddram_addr - 84) + WIDTH * 3;
+ s->ddram = 1;
+ } else if (s->input & LCD_CGRAM) {
+ s->ac = s->input & ~LCD_CGRAM;
+ s->ddram = 0;
+ } else if (s->input & LCD_FUNCTION) {
+ s->mode8bit = !!(s->input & LCD_FUNCTION_8BIT);
+ s->two_lines = !!(s->input & LCD_FUNCTION_2LINES);
+ s->font5x10 = !!(s->input & LCD_FUNCTION_10DOTS);
+ } else if (s->input & LCD_MOVE) {
+ if (s->input & LCD_MOVE_DISP) {
+ if (s->input & LCD_MOVE_RIGHT) {
+ s->dispcol--;
+ } else {
+ s->dispcol++;
+ }
+ s->dispcol %= sizeof(s->data);
+ } else {
+ // ...
+ }
+ } else if (s->input & LCD_ON_CTRL) {
+ s->display = !!(s->input & LCD_ON_DISPLAY);
+ s->cursor = !!(s->input & LCD_ON_CURSOR);
+ s->blink = !!(s->input & LCD_ON_BLINK);
+ } else if (s->input & LCD_ENTRY_MODE) {
+ s->id = !!(s->input & LCD_ENTRY_INC);
+ s->sh = !!(s->input & LCD_ENTRY_SHIFT);
+ } else if (s->input & LCD_HOME) {
+ s->ac = 0;
+ s->dispcol = 0;
+ s->ddram = 1;
+ } else if (s->input & LCD_CLR) {
+ memset(s->data, 32, sizeof(s->data));
+ s->ac = 0;
+ s->dispcol = 0;
+ s->id = 1;
+ s->ddram = 1;
+ }
+ } else {
+ if (s->ddram) {
+ s->data[s->ac] = s->input;
+ s->ac++;
+ s->ac %= sizeof(s->data);
+ if (s->sh) {
+ if (s->id)
+ s->dispcol++;
+ else
+ s->dispcol--;
+ s->dispcol %= sizeof(s->data);
+ }
+ s->need_update = 1;
+ } else {
+ // ...
+ }
+ }
+}
+
+static void hd44780_set_pin(void *opaque, int pin, int level)
+{
+ LCDState *s = opaque;
+
+ if (pin >= 0 && pin <= 7) {
+ if (!s->rw) {
+ if (!s->mode8bit && s->write_low)
+ pin -= 4;
+ if (level)
+ s->input |= 1 << pin;
+ else
+ s->input &= ~(1 << pin);
+ }
+ } else if (pin == PIN_RS) {
+ s->control = !level;
+ } else if (pin == PIN_RW) {
+ s->rw = level;
+ } else if (pin == PIN_E) {
+ if (!level && !s->rw) {
+ if (!s->mode8bit) {
+ s->write_low = !s->write_low;
+ if (!s->write_low)
+ hd44780_enable(s);
+ } else {
+ hd44780_enable(s);
+ }
+ }
+ } else if (pin == PIN_BL) {
+ s->backlight = level;
+ s->need_update = 1;
+ }
+}
+
+static void hd44780_update_display(void *opaque)
+{
+ LCDState *s = opaque;
+ uint32_t color_segment, color_led;
+ int y, x, r, g, b;
+
+ if (s->need_update) {
+ if (s->backlight) {
+ r = 0;
+ g = 0xff;
+ b = 0x80;
+ } else {
+ r = 0xf0;
+ g = 0xe0;
+ b = 0xb0;
+ }
+
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 8:
+ color_segment = rgb_to_pixel8(0, 0, 0);
+ color_led = rgb_to_pixel8(r, g, b);
+ break;
+ case 15:
+ color_segment = rgb_to_pixel15(0, 0, 0);
+ color_led = rgb_to_pixel15(r, g, b);
+ break;
+ case 16:
+ color_segment = rgb_to_pixel16(0, 0, 0);
+ color_led = rgb_to_pixel16(r, g, b);
+ break;
+ case 24:
+ color_segment = rgb_to_pixel24(0, 0, 0);
+ color_led = rgb_to_pixel24(r, g, b);
+ break;
+ case 32:
+ color_segment = rgb_to_pixel32(0, 0, 0);
+ color_led = rgb_to_pixel32(r, g, b);
+ break;
+ default:
+ return;
+ }
+
+ if (s->display) {
+ for (y = 0; y < HEIGHT; y++) {
+ for (x = 0; x < WIDTH; x++) {
+ draw_char(s->ds, x * 5 * CZOOM, y * 7 * CZOOM, s->data[y *
WIDTH + x], color_segment, color_led);
+ }
+ }
+ }
+
+ dpy_update(s->ds, 0, 0, WIDTH * CZOOM * 5, HEIGHT * CZOOM * 7);
+ }
+}
+
+static void hd44780_invalidate_display(void * opaque)
+{
+ LCDState *s = opaque;
+ s->need_update = 1;
+}
+
+static void hd44780_init(SysBusDevice *dev)
+{
+ LCDState *s = FROM_SYSBUS(LCDState, dev);
+
+ s->need_update = 1;
+
+ qdev_init_gpio_in(&dev->qdev, hd44780_set_pin, 12);
+ qdev_init_gpio_out(&dev->qdev, s->out, 8);
+
+ s->ds = graphic_console_init(hd44780_update_display,
+ hd44780_invalidate_display,
+ NULL, NULL, s);
+ qemu_console_resize(s->ds, WIDTH * CZOOM * 5, HEIGHT * CZOOM * 7);
+}
+
+static void hd44780_register(void)
+{
+ sysbus_register_dev("gpio,hd44780", sizeof(LCDState),
+ hd44780_init);
+}
+
+device_init(hd44780_register)
--
1.6.3.msysgit.0
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Qemu-devel] [PATCH 14/14] HD44780 LCD Controller,
Filip Navara <=