Hitachi HD44780
LCD Display Driver
for Linux

by Rudolf Szendrei

Driver - version 1.00, release date 30. October 2007.


This driver working properly only with 2 lines HD44780 compatible displays.

It was tested out fully with EW162G0.

If you experience any bug or have questions, please don't hesitate to write it: swap@inf.elte.hu

Full driver package, contains sources, binaries, etc.: >> DOWNLOAD RECENT ARCHIVE HERE <<

!!! DRIVER USAGE HOWTO !!!


  • Download the driver
  • Unpack it with mc (midnight commander) or with the tar program
  • If you like to rebuild, then
    • Install gcc if you did not installed it before
    • Make sure you installed the kernel header files with version of your kernel
    • Run: make
    • If make was successful, then you get the following kernel module file: hd44780.ko
  • To install, run: ./install_hd44780_device install
    If you did everything well, then you can see the next message on your LCD:

    HD44780 Driver Loaded! :)

  • To uninstall, run: ./install_hd44780_device remove
    If uninstall was successful, then your LCD will clear its display.

  • Testing driver: cat > /dev/hd44780_lcd

    You can write anything on it.

    ENTER makes the LCD's cursor position to the front of the 2nd line,
    TAB clears the display and repositions the cursor to the left top (init position).

  • You can simply write to the driver file /dev/hd44780_lcd

  • NOTE: Driver uses mutual exclusion! Only one application can use the LCD at a time. Driver reject the second open()
    till the the application - which opened it before - close() it.

hd44780.h - header file (core functions of the chip)

Definitions, Includes, Function prototypes


#define LCD_INSTRUCTION 0 #define LCD_DATA 1 #define LCD_WRITE 0 #define LCD_READ 1 #define WAIT_TIME 10 #include < asm/io.h > #include < linux/delay.h > void lcd_send_4_bits(char, char, int); void lcd_send_byte(char, char, int); void lcd_clear(void); void lcd_putc(char); void lcd_write(const char*, int); void lcd_gohome(void); void lcd_gotoxy(int, int); void lcd_setcharpattern(char, const char*); void lcd_init(void);

4-bits mode low-level data transmit function


void lcd_send_4_bits(char c, char rs, int Wait) { c = c << 4; outb((c | 0x04) | rs, LPT_PORT); // ENABLE (Prepare) outb((c & 0xf0) | rs, LPT_PORT); // DISABLE (Transmit) mdelay(Wait); }

Abstraction of the 8-bits mode data transmission over 4-bits mode


void lcd_send_byte(char c, char rs, int Wait) { lcd_send_4_bits( (c >> 4), rs, Wait ); lcd_send_4_bits( (c & 15), rs, Wait ); }

Basic instructions: clear, putc, cursor positioning


void lcd_clear() { lcd_send_byte( 0x01, LCD_INSTRUCTION, 5 ); } void lcd_putc(char c) { lcd_send_byte( c, LCD_DATA, 1 ); } void lcd_gohome() { lcd_send_byte( 0x02, LCD_INSTRUCTION, 5 ); } void lcd_gotoxy(int x = 0, int y = 0) { char c = 0x80 | (x & 0x3f) | (y << 6); lcd_send_byte( c, LCD_INSTRUCTION, 1); }

Function to redefine bitmap patterns of characters


void lcd_setcharpattern(char c_pos, char* pattern) { char c = 0x40 | ((c_pos & 0x3f) << 3); // SELECT CGRAM ADDRESS lcd_send_byte( c, LCD_INSTRUCTION, 1); // UPLOAD FONT PATTERN lcd_send_byte( pattern[0], LCD_DATA, 1 ); lcd_send_byte( pattern[1], LCD_DATA, 1 ); lcd_send_byte( pattern[2], LCD_DATA, 1 ); lcd_send_byte( pattern[3], LCD_DATA, 1 ); lcd_send_byte( pattern[4], LCD_DATA, 1 ); lcd_send_byte( pattern[5], LCD_DATA, 1 ); lcd_send_byte( pattern[6], LCD_DATA, 1 ); lcd_send_byte( pattern[7], LCD_DATA, 1 ); }

Display init procedure


void lcd_init(void) { int i; char init[10]; init[0] = 0x03; // INIT init[1] = 0x02; init[2] = 0x08; init[3] = 0x0C; // DISPLAY ON init[4] = 0x06; // ENTRY MODE SET outb(0, LPT_PORT); // FUNCTION SET 4 BIT INIT for (i = 0; i < 3; i++) lcd_send_4_bits( init[0], LCD_INSTRUCTION, 20 ); // FUNCTION SET 4 BIT INIT for (i = 0; i < 2; i++) lcd_send_4_bits( init[1], LCD_INSTRUCTION, 20 ); // LINES 2, 5x8 FONT lcd_send_4_bits( init[2], LCD_INSTRUCTION, 20 ); // DISPLAY ON lcd_send_byte( init[3], LCD_INSTRUCTION, 10 ); // DISPLAY CLEAR lcd_clear(); // ENTRY MODE SET lcd_send_byte( init[4], LCD_INSTRUCTION, 5 ); }

Example 1. Write text to LCD Display (in driver)


char str[] = {"Testing 1,2,3 " "It' Works ! "}; int len = strlen(str); for(int j = 0; j < len; j++) lcd_putc(str[j]); As you can see, every line contains 40 bytes in RAM.

Example 2. Position cursor to 10th character in the 2nd line (in driver)


lcd_gotoxy(10,1);

Example 3. Redefine the look of ASCII 2 character and display it (in driver)


char pattern_temp[] = { 0x0F, 0x0C, 0x0F, 0x0C, \ 0x0E, 0x11, 0x1F, 0x0E }; lcd_setcharpattern(2, (char*)pattern_temp); lcd_putc(2); This sample shows, that each character consists of 8 rows, but because the character is only 5 dot thick, we use only the 5 least important bits of a byte for each bitmap row.


hd44780.c - module source

Includes, License notice for kernel, Variables


#include < linux/kernel.h > #include < linux/module.h > #include < linux/init.h > #include < linux/tty.h > #include < linux/ioport.h > #include < linux/fs.h > #include < asm/uaccess.h > #include < linux/cdev.h > MODULE_LICENSE("GPL"); #define DEVNAME "HD44780_LCD" static char msg[200]; static dev_t dev; static struct cdev my_cdev; static int DevMajor; static int isOpened = 0; static int LPT_PORT = 0x378; /* LOOKING FOR COMMAND LINE ARGUMENTS */ module_param(LPT_PORT, int, 0);

Include LCD core driver, Function prototypes


/* INCLUDE THE DRIVER CORE */ #include "hd44780.h" /* FUNCTION PROTOTYPES */ int print_msg(char*); static int device_open(struct inode*, struct file*); static int device_release(struct inode*, struct file*); static ssize_t device_write(struct file*, const char*, size_t, loff_t*);

Define the struct and the functions for character device instantiation


/* FILE OPERATION STRUCT TO REGISTER CHARACTER DEVICE */ static struct file_operations fops = { .write = device_write, .open = device_open, .release = device_release, .owner = THIS_MODULE }; /* DEVICE OPERATIONS */ static int device_open(struct inode* in, struct file* f) { if (isOpened) return -EBUSY; isOpened = 1; return 0; } static int device_release(struct inode* in, struct file* f) { isOpened = 0; return 0; } static ssize_t device_write(struct file* f, const char* buf, \ size_t len, loff_t* off) { int i; for (i = 0; i < len; ++i) { switch(buf[i]) { case 9: lcd_clear(); break; case 10: case 13: lcd_gotoxy(0,1); break; default: lcd_putc(buf[i]); } } return len; }

Helper function to write to the current tty


/* HELPER FUNCTION TO DEBUG MESSAGES TO CONSOLE*/ int print_msg(char* str) { struct tty_struct *driver_tty = current->signal->tty; if (!driver_tty) return -ENOTTY; (driver_tty->driver->write)(driver_tty, str, strlen(str)); return 0; }

Module Init and Exit


/* MODULE INITIALISATION */ int LCD_driver_init(void) { int res; res = alloc_chrdev_region(&dev, 0, 1, DEVNAME); if (res < 0) return res; DevMajor = MAJOR(dev); cdev_init(&my_cdev, &fops); my_cdev.owner = THIS_MODULE; res = cdev_add(&my_cdev, dev, 1); if (res < 0) { unregister_chrdev_region(dev, 1); return res; } // INIT LCD lcd_init(); lcd_write("HD44780 Driver", 14); lcd_gotoxy(0,1); lcd_write("Loaded!", 7); print_msg("HD44780 LCD Driver loaded.\n\r"); sprintf(msg, "Major number: %d.\n\r", DevMajor); print_msg(msg); return 0; } /* MODULE EXIT */ void LCD_driver_exit(void) { lcd_clear(); cdev_del(&my_cdev); unregister_chrdev_region(dev, 1); sprintf(msg, "HD44780 LCD Driver unloaded.\n\r"); print_msg(msg); } module_init(LCD_driver_init); module_exit(LCD_driver_exit);


Makefile - for Kernel > 2.6.00


ifneq ($(KERNELRELEASE),) obj-m := hd44780.o else KDIR := /lib/modules/`uname -r`/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) M=$(PWD) modules endif clean: @rm -rf *~ *.o *.mod.c .*.cmd .tmp_versions


install_44780_device - Installer / Uninstaller script


#!/bin/bash module="hd44780" devname="HD44780_LCD" device="hd44780_lcd" if [ "$1" = "install" ] then echo "Installing..."; shift; insmod ./$module.ko $* major=`awk "\\$2==\"$devname\" {print \\$1}" /proc/devices` echo "Device major = $major" mknod /dev/$device c $major 0 elif [ "$1" = "remove" ] then echo "Removing..."; rm /dev/$device rmmod $module else echo "To install driver: ./$0 install"; echo "To remove driver: ./$0 remove"; fi