This is a small parallel port char driver for printing text on a 16x2 lcd module connected at parallel port of a PC. I did this as a part of learning linux kernel-module programming. May be this could be considered as a hello world device driver.
As we know, everything in Linux is treated as a file, even hardware devices like serial ports, hard disks, and scanners. In order to access these devices, a special file called a device node has to be present. All device nodes are stored in the /dev directory. Here, my 16x2 lcd connected to parallel port is also treated as a file and is accessed via a device node....
PROCEDURE:
1> save lcd.c to a new folder
2>create Makefile in the same folder
3>type "make" to build the driver module led.ko
4>Setup the 16x2 lcd as in circuit diagram and power on the 5v supply to lcd.
5>Insert the module using "sudo insmod lcd.ko"
5>To see the kernel message, type dmesg or use tty3 or 4 to see it directly on screen
6>Now if every thing is all right, then we could see a text "DRIVER INSERTED" on the second row of 16x2 lcd.
7>Now create a node any where, with major number 61.
eg: "sudo mknod /dev/my_lcd c 61 0"
TESTING:
Screenshot of test and result:
----------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------
A good tutorial about kernel module programming is available at
http://tldp.org/LDP/lkmpg/2.6/html/index.html
INTRODUCTION:
As we know, everything in Linux is treated as a file, even hardware devices like serial ports, hard disks, and scanners. In order to access these devices, a special file called a device node has to be present. All device nodes are stored in the /dev directory. Here, my 16x2 lcd connected to parallel port is also treated as a file and is accessed via a device node....
Now, after connecting the 16x2 lcd to parallel port (as in my circuit diagram), and then inserting the driver module, a message "DRIVER INSERTED" is displayed on the 16x2 LCD. Later, if we make a character special file (node) with major number as 61, and if we write any string to it (echo HELLO > file), then it will be displayed on the 16x2 LCD. Now, if we write a long string to it, then it is displayed on the lcd by scrolling it from bottom to top until the string is displayed completely ...
CIRCUIT DIAGRAM:
(i forgot to draw a connection between the ground of lcd and parallel port, don't miss it if any one is trying to do this. pin 25 to 18 is the ground, can select any one or all together)
DRIVER CODE - lcd.c : ('tested ok' in linux kernel 3.0):
#include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/proc_fs.h> #include <linux/fcntl.h> #include <linux/ioport.h> #include <asm/system.h> #include <asm/uaccess.h> #include <asm/io.h> #include <linux/delay.h> MODULE_LICENSE("Dual BSD/GPL"); #define LCD(x) outb((c = ((0x0f & x) | (c & 0xf0) )), 0x378) #define RS(x) outb((c = (((~(1 << 4)) & c) | (x << 4))), 0x378) #define EN(x) outb((c = (((~(1 << 5)) & c) | (x << 5))), 0x378) #define en 0b00010000 #define rs 0b00100000 #define OUT(x) outb(x, 0x378) #define MAXSIZE 17 static char lcd_buffer1[17]; static unsigned char c; static char d_buf[MAXSIZE]; static int port; static char lcd_space = ' '; static int major = 61; static int pport_open(struct inode *inode, struct file *filp); static int pport_close(struct inode *inode, struct file *filp); static ssize_t pport_read(struct file *filp, char *buf, size_t count, loff_t *f_pos); static ssize_t pport_write(struct file *filp, char *buf, size_t count, loff_t *f_pos); static void lcd_strobe(void); static void data(unsigned char); static void cmd(unsigned char); static void clear(void); static void lcd_init(void); static void printlcd(char *); static struct file_operations fops = { open: pport_open, read: pport_read, write: pport_write, release: pport_close }; static int pport_open(struct inode *inode, struct file *filp) { return 0; } static int pport_close(struct inode *inode, struct file *filp) { return 0; } static ssize_t pport_write(struct file *filp, char *buf, size_t count, loff_t *f_pos) { if(count < MAXSIZE) { copy_from_user(d_buf, buf, count); d_buf[count] = 0; printlcd(d_buf); *f_pos += count; return count; } else { copy_from_user(d_buf, buf, MAXSIZE - 1); d_buf[MAXSIZE - 1] = 0; printlcd(d_buf); *f_pos += MAXSIZE - 1; return MAXSIZE - 1; } } static ssize_t pport_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { return 0; } int init_module(void) { int a; a = register_chrdev(major, "registered_pport_61", &fops); if(a < 0) { printk(KERN_ALERT "error: can't register major number %d\n", major); return a; } port = check_region(0x378, 1); if(port) printk(KERN_ALERT "pport cannot reserve 0x378\n"); request_region(0x378, 1, "registered_pport_61"); //lcd setup// outb(0, 0x378); udelay(10000); lcd_init(); udelay(10000); printlcd("DRIVER INSERTED "); return 0; } void cleanup_module(void) { printk(KERN_ALERT "pport module is going to terminate\n"); printlcd("DRIVER REMOVED "); unregister_chrdev(major, "registered_pport_61"); clear(); } static void lcd_strobe(void) { EN(1); udelay(1); EN(0); udelay(1); } static void data(unsigned char data) { RS(1); udelay(40); LCD(data >> 4); lcd_strobe(); LCD(data); lcd_strobe(); udelay(10); RS(0); udelay(10); } static void cmd(unsigned char command) { RS(0); udelay(40); LCD(command >> 4); lcd_strobe(); LCD(command); lcd_strobe(); } static void clear(void) { cmd(1); udelay(2000); } static void lcd_init(void) { cmd(0x30); cmd(0x30); cmd(0x28); cmd(0x0c); clear(); cmd(0x6); } static void printlcd(char *p) { static int count = 0; count = 0; clear(); cmd(0x80); while(lcd_buffer1[count]) data(lcd_buffer1[count++]); count = 0; cmd(0xc0); while(*p) { if((*p != '\n') && (*p != '\t')) { lcd_buffer1[count++] = *p; data(*p); } else { data(lcd_space); lcd_buffer1[count++] = lcd_space; } p++; } lcd_buffer1[16] = 0; msleep(2000); }
Makefile:
obj-m += lcd.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
1> save lcd.c to a new folder
2>create Makefile in the same folder
3>type "make" to build the driver module led.ko
4>Setup the 16x2 lcd as in circuit diagram and power on the 5v supply to lcd.
5>Insert the module using "sudo insmod lcd.ko"
5>To see the kernel message, type dmesg or use tty3 or 4 to see it directly on screen
6>Now if every thing is all right, then we could see a text "DRIVER INSERTED" on the second row of 16x2 lcd.
7>Now create a node any where, with major number 61.
eg: "sudo mknod /dev/my_lcd c 61 0"
TESTING:
- Type "echo HELLO WORLD > /dev/my_lcd" . This should print HELLO WORLD on the lcd.
- type "date > /dev/my_lcd" and observe the date on lcd.
- Type "echo THIS IS A LARGE STRING CONTAINING MORE THAN 32 CHARACTERS. I WANT TO SEE HOW IT IS DISPLAYED ON THE LCD > /dev/my_lcd" and see the long string on the lcd.
- Type "ls > /dev/my_lcd" as root and observe contents on the folder on the lcd.
- Type "story.txt > /dev/my_lcd" as root and read the story on LCD. :-)
- Now, we can also try to display some thing using a user program...
Screenshot of test and result:
----------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------
A good tutorial about kernel module programming is available at
http://tldp.org/LDP/lkmpg/2.6/html/index.html
Good tutorial, very informative.
ReplyDeleteHow about USB driver? It would be more fun.
thanks
Ilms
Good job. I have learnt a lot with this tutorial. Well done !!
ReplyDeleteNice tutorial.. :)
ReplyDeletethanks
can u make me a video of the test please
ReplyDeleteNo pude lograr la compilaciĆ³n con make.
ReplyDeleteque puede ser?
hi, thanks for the tutorial.
ReplyDeleteI try it with pic16f877a, and i used usb serial port in my laptop.
But i failed in step 3 (3>type "make" to build the driver module led.ko )
could you please show me the details / step by step to do the third step? *i'm beginner with this
Thanks
Thanx for Help nice blogg
ReplyDeleteThanks,
ReplyDeleteAm abgnr in lnx
ths s ma fst pgm...........thnx alot
well done...
ReplyDeletekeep going ...
very much helpful to beginners like me..
Thank u.. :)
asm/system.h no such file or directory compilation terminated
ReplyDeletecan any one help me regarding circuit diagram of this.I have made it but data is not coming on LCD.
ReplyDeletewhile running make command it gives following error....plz help what is this error for
ReplyDeletelcd16x2$ make
make -C /lib/modules/3.13.0-92-generic/build M=/home/quanta/Desktop/Embedded refrences/lcd16x2 modules
make[1]: Entering directory `/usr/src/linux-headers-3.13.0-92-generic'
Makefile:614: Cannot use CONFIG_CC_STACKPROTECTOR: -fstack-protector not supported by compiler
Makefile:614: *** missing separator. Stop.
make[1]: Leaving directory `/usr/src/linux-headers-3.13.0-92-generic'
make: *** [all] Error 2
loved your writing style. your blog is amazing. Have been going through some of your posts, will def. recommend to others.Mahatma Gandhi Marathi Quotes
ReplyDelete