Amazon Banner

Saturday, February 2, 2013

Using a 16x2 LCD with i2c on the Raspberry Pi

I got a Raspberry Pi for Christmas, and have been playing with it ever since. For one project, I needed to display a couple of lines of text. I ordered a 16 Character x 2 Line LCD display based on an hd44780 compatible controller. From what I had read, I wanted one with an i2c i/o expander. By using i2c, the lcd can be controlled using just 4 GPIO pins from the Raspberry Pi instead of 8. I ended up ordering the SainSmart IIC LCD1602 Display from amazon for just $12.

When I received the LCD, I hooked it up to my Pi and tried using it with a tutorial I found on the raspberry pi blog. The tutorial I was following mentioned that some of the i2c expansion 'backpack' boards connected the i2c chip's pins to the lcd's pins in a different way than others, and that the code has to be modified to use it with other versions.  When I couldn't get it working, I looked through the 'documentation' sainsmart offered for download on their website and in the product description on amazon. Unfortunately, the documentation was basically just some sample code for arduino. (As it turns out, the sample arduino code didn't even work.  It was written for an old, pre 1.0 version of the arduino IDE.) I contacted sainsmart support to ask for any further documentation available and a schematic of the i2c expander board, but the best they could do is point me to a post on someone's blog about using an i2c controlled lcd with the Raspberry Pi. After examining the path of traces on the backpack board, I was able to map out which pins of the i/o expander chip were connected to which pins of the lcd:

CHIP  LCD
P0       RS
P1       RW
P2       EN
P3       BL
P4       D4
P5       D5
P6       D6
P7       D7
Armed with this information, I was able to get assistance from the lcdproc help to modify the hd44780-i2c driver used by lcdproc.  Since I had seen many posts from others with the same problem, I wanted to share the solution.  Here is the patch code code that I used to modify the hd44780 driver:


Index: server/drivers/hd44780-i2c.c =================================================================== RCS file: /cvsroot/lcdproc/lcdproc/server/drivers/hd44780-i2c.c,v retrieving revision 1.14 diff -u -r1.14 hd44780-i2c.c --- server/drivers/hd44780-i2c.c 30 Oct 2010 18:04:21 -0000 1.14 +++ server/drivers/hd44780-i2c.c 27 Jan 2013 14:39:31 -0000 @@ -76,10 +85,10 @@ void i2c_HD44780_backlight(PrivateData *p, unsigned char state); void i2c_HD44780_close(PrivateData *p); -#define RS 0x10 -#define RW 0x20 -#define EN 0x40 -#define BL 0x80 +#define RS 0x01 +#define RW 0x02 +#define EN 0x04 +#define BL 0x08 // note that the above bits are all meant for the data port of PCF8574 #define I2C_ADDR_MASK 0x7f @@ -168,43 +214,43 @@ // powerup the lcd now /* We'll now send 0x03 a couple of times, * which is in fact (FUNCSET | IF_8BIT) >> 4 */ - i2c_out(p, 0x03); + i2c_out(p, 0x30); if (p->delayBus) hd44780_functions->uPause(p, 1); - i2c_out(p, enableLines | 0x03); + i2c_out(p, enableLines | 0x30); if (p->delayBus) hd44780_functions->uPause(p, 1); - i2c_out(p, 0x03); + i2c_out(p, 0x30); hd44780_functions->uPause(p, 15000); - i2c_out(p, enableLines | 0x03); + i2c_out(p, enableLines | 0x30); if (p->delayBus) hd44780_functions->uPause(p, 1); - i2c_out(p, 0x03); + i2c_out(p, 0x30); hd44780_functions->uPause(p, 5000); - i2c_out(p, enableLines | 0x03); + i2c_out(p, enableLines | 0x30); if (p->delayBus) hd44780_functions->uPause(p, 1); - i2c_out(p, 0x03); + i2c_out(p, 0x30); hd44780_functions->uPause(p, 100); - i2c_out(p, enableLines | 0x03); + i2c_out(p, enableLines | 0x30); if (p->delayBus) hd44780_functions->uPause(p, 1); - i2c_out(p, 0x03); + i2c_out(p, 0x30); hd44780_functions->uPause(p, 100); // now in 8-bit mode... set 4-bit mode - i2c_out(p, 0x02); + i2c_out(p, 0x20); if (p->delayBus) hd44780_functions->uPause(p, 1); - i2c_out(p, enableLines | 0x02); + i2c_out(p, enableLines | 0x20); if (p->delayBus) hd44780_functions->uPause(p, 1); - i2c_out(p, 0x02); + i2c_out(p, 0x20); hd44780_functions->uPause(p, 100); // Set up two-line, small character (5x8) mode @@ -240,8 +284,8 @@ i2c_HD44780_senddata(PrivateData *p, unsigned char displayID, unsigned char flags, unsigned char ch) { unsigned char enableLines = 0, portControl = 0; - unsigned char h = (ch >> 4) & 0x0f; // high and low nibbles - unsigned char l = ch & 0x0f; + unsigned char h = ch & 0xf0; // high and low nibbles + unsigned char l = (ch << 4) & 0xf0; if (flags == RS_INSTR) portControl = 0; @@ -277,7 +321,7 @@ */ void i2c_HD44780_backlight(PrivateData *p, unsigned char state) { - p->backlight_bit = ((!p->have_backlight||state) ? 0 : BL); + p->backlight_bit = ((p->have_backlight && state) ? BL : 0); i2c_out(p, p->backlight_bit); }

A copy of the modified source code and a compiled, ready to use, copy of the driver file can be downloaded here.

The sainsmart backpack board seems to be a clone of a similar board made by DFROBOT and others, so if your i2c backpack matches the pinouts I listed above, give the modified driver for lcdproc a try.

I hope this post is able to save others from going through the trouble I did to get this LCD working.

4 comments:

  1. Hi Steve,
    your code helped me a lot. I faced the the same problem with a cheap i2c backpack for a 20x4 lcd. It has the same pinout you descripted, and with your driver I was able to get a text on the lcd with lcdproc. My problem is that the text position on the display ist not correct for line > 1. Text that should be on the second line is shown in the third with an x offset. Do you have an idea about that.
    Thanks, sull

    ReplyDelete
  2. Hi Steve,
    my fault, it was a wrong setting in the LCDd.conf....
    Thanks again, now it´s working like a charm with your code!!!!

    ReplyDelete
  3. Dear Steve,
    I have the same problem as the poster "Siggi Ullrich" in Raspberry Pi, but I have no ideal how to use the "HD44780.so" file. Could you kindly post the process step by step, including a LCD test program ?
    Thanks, Hjchen

    ReplyDelete
    Replies
    1. It's been a while since I've done any tweaking on that pi, and I don't have it with me at the moment. Going from a foggy memory and some notes, I believe you just need to copy HD44780.so to /usr/lib/lcdproc/

      Check out the project on github that I used this display in. The actual project may not be of interest to you, but if you look over the process described in the readme, it may help you get the lcd working.
      https://github.com/Aurock/Cacheberry-Pi/tree/CustomDisplay

      Also, since Siggi Ullrich mentioned that his issue boiled down to a problem with a setting in LCDd.conf , that would be the first place I suggest you check. Compare your LCDd.conf to the one I used for that project:
      https://github.com/Aurock/Cacheberry-Pi/blob/CustomDisplay/misc/LCDd.conf

      Delete