qemu-devel
[Top][All Lists]
Advanced

[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






reply via email to

[Prev in Thread] Current Thread [Next in Thread]