One of the major problems plaguing the RasPi is the lack of GPIO, and the weird placement and random numbered GPIO pins broken out, making any code using them much more disorganized than if consecutive GPIO pins had been used. In addition to the inconvinience of using the GPIO, the LCD uses 5v, while the RasPi is strict 3.3v. In order to prevent any chance of damage to my Pi, and to solve the problem, I opted to use a PCF8574 I2C 8 bit IO expander. This makes it easy enough to use the bottom four bits of the expander for LCD data, and the next 3 bits for RS, R/W, and EN. To control the LCD this way, I wrote up a nice library to get things working on the lcd. In addition to this, I added a few extra bits to the library so that anyone can add more devices to it, and wrote a class for the tmp102 so we can have some live data to display.
The Raspberry Pi wired up to a serial bridge, PCF8574 Port Expander, and a tmp102 12bit temperature sensor |
This library makes it very simple to use both these devices with the RasPi. Before using the library, you must load the i2c-dev module. At the time at which I started this project, the default Raspberry Pi linux tree was outdated and did not include spi and i2c userspace drivers.
After you get your new kernel installed, load the i2c-dev module, and place pylcd.py in whichever directory your working in, then we can move on to a simple sample program.
Links:
https://gist.github.com/2962884 The libraries somewhat regularly updated gist
I'd just buy a FTDI usb to 5v TTL adapter and a sparkfun serial LCD along with 0.1" header inserts. (Assuming you dont have any laying around)
ReplyDeleteGet a pen and rearrange the default pinout on the FTDI connector to match the LCD (you need to swap a couple wires) and then plug it into the USB. It should all work without the io expander, i2c drivers, or soldering
This was mainly done since not everyone has a serial backpack for an LCD, or want to take up a USB port. While your suggestion would work fine, this was both a personal exercise with using i2c-dev in userspace and to have a cheap and quick way to use the LCD. I would think an io expander or shift register would be more available on hand than ordering a serial backpack. Thanks for the feedback.
DeleteHi!
ReplyDeleteCould you add the schematics for this project? I want to know how did you achieve the voltage translation between the RPi and the PCF8574.
Thanks in advance
There's no need for a voltage translation, the PCF8574 is actually going from the 3.3v i2c on the RasPi to the 3.5v on the HD44780 LCD.
Deletelost what I had written. Anyway, most hd44780's can accept (VCC*0.7) voltage logic input. I can control a 5v lcd with a msp430 running at 3.3v pin output. The Lcd still needs 5v, but the logic does not. Just don't pull r/w high, don't put it into read mode. Most people tie r/w to ground anyway.
ReplyDeleteI did use your same setup. Msp430 -> pcf8574 -> lcd. No need for level translations.
Even if voltage translation isn't needed, in my experience, working with linux GPIO is worse than working with linux i2c, especially since the RasPi doesn't have 7 consecutive numbered GPIO, i can just write to the GPIO state registers and easily have it working, and i would have to deal with sysfs in python. This way, i can be sure my RasPi is safe, even if a bit unnecessary, and use multiple LCDs and devices on the i2c port, so I can save the GPIO for something else.
DeleteFirstly, good work :) I've been hoping someone would provide some Python code to do this.
ReplyDeleteUnfortunately, I can't seem to get it to work. I've got a HD44780 with an I2C backpack (uses a PCA8574). Whenever I run the code it just causes the backlight to flicker and the cursor to move erratically.
I've confirmed the LCD works on my Arduino using the Wire library. Any ideas where I might be going wrong?
Do you have a schematic for this backpack. My code assumes that the first 4 bits are attached to the 4,5,6,7 data pins on the lcd. The next 3 bits (4,5,6), should be attached in the same order to 4-CLK/EN, 5-R/W, 6-RS. If the schematic is different for you, i'll try making the code a bit more modular for different pinouts.
DeleteI don't have the schematic, but I've been examining the traces and it looks like pins 9,10,11,12 of the 8574 are connected to 11,12,13,14 on the LCD. 4,5,6 are connected to 4,5,6 of the LCD as you suggest.
DeleteOk, I'll go into the code and change some things. It seems your backpack is doing to opposite order that i am doing, so I'll have to fiddle with the code a bit to get it working.
DeleteMine: P0-P3 --> LCD 11,12,13,14; P4-P6 --> LCD 4,5,6
Yours: P0-P2 --> LCD 4,5,6; P4-P6 --> LCD 11,12,13,14
I just added the feature to switch between the three most common pinouts (other pinouts seem unlikely to occur).
DeleteI managed to test this but it still isn't working. There is some progress though - I can see a character or two on the display, but the backlight still flashes. I tried it on all three reverse modes (I believe I should be using mode 1).
DeleteThe backpack I am using is a Chinese knock-off of this one:
http://www.dfrobot.com/image/data/DFR0175/I2C%20LCD%20Backpack%20schematic.pdf
The schematic irv posted appears to match the pinout of my backpack, and I seem to be getting similar results. The lcd starts with the backlight on, the first time I try to send data to it with this, the backlight goes off. I get a few 0's on the first line of the lcd, but never anything that resembles what I was trying to display. Looking at the different options you've made available, I'm not sure I understand them. Which option would fit for a board with these connections?
DeleteLCD CHIP
RS P0
RW P1
E P2
D4 P4
D5 P5
D6 P6
D7 P7
One other question - Why is it you only list 3 of the data pins, both in your 7/1/12 3:44am comment, and in the code comments describing the "reverse" options? I see P4,P5,P6, but not P7.
Thanks!
I ended up getting this lcd working by using lcdproc and a modified driver. More information and the modified driver can be found here: http://www.neighborgeek.net/2013/02/using-16x2-lcd-with-i2c-on-raspberry-pi.html
DeleteExcellent, thank you. I will try it out later today and let you know how it goes :)
ReplyDeleteHere is my C++ HD44780 library for RaspberryPi. Easy to use.
ReplyDeletehttps://plus.google.com/101070371711981569549/posts/4EqiRHJnwXD
or
https://gitorious.org/rpi-gpio
https://gitorious.org/rpi-hd44780
Finally something "real" IMHO. Great C++ library that one can use and hope for a real time response of the hardware... Thanks
DeleteDo you have a better picture or schematic of your wiring?
ReplyDeleteI'm not exactly sure how everything fits together in your picture.
Also, would it be possible to use different GPIO-pins (instead of 0 and 1) for i2c?
Did you run the PCF8574 off 5V? With your own pullups on SDA/SCL and to what voltage? Or did you run the 8574 on 3.3V and assume the 44780 would accept 3.2V as a high?
ReplyDeletethanks...
I REALLY appreciate the work here! I am having a tought time getting this to work. I am using the LCD2004 daughterboard (same basic design as above) with a reverse=0. I get flashing on the LCD, but nothing else. Can you provide more detail on the commands sent?
ReplyDeleteMy library should have a few different modes for it, try them out. I can't find anything on the pinout of the daughterboard so it's hard for to give much more help
DeleteI am running the same backpack knock off as irv075. Same problems also - flickering, then nothing. Once in a while a character.
DeleteCould the timing of the strobe be causing problems?
thanks for the help!
I've given up and bought the components to make my own backpack based on the PCF8574. What is strange is that the knock-off works perfectly with the Arduino library...
ReplyDeleteI am almost there. I read someplace, not sure where, that the Pi could be running too fast for some peripherals designed for the Arduino. Unfortunately the article implies that the kernel would need to be recompiled (or portions) to slow it down. Not something I am willing to do.
DeleteToday I intend to unsolder the daughter board and try a direct method with the GPIO.
Interesting, i experienced no such flickering during my tests.it may be possible that the kernels gpio and SPI/I2C handling may have changed since I've used this. If you want I can post a schematic of a functioning setup.
DeleteI got it working! Here is the code:
Deletehttps://github.com/thorrak/pi-libs/blob/master/pylcd.py
https://github.com/thorrak/pi-libs/blob/master/pylcd_test.py
I added some additional commenting, and changed a few things as well to make it work with backpacks with even crappier pinouts. This is unfortunately my first time dabbling in Python so I guarantee there are easier ways of accomplishing this. Feel free to rewrite anything, republish, etc.
Great, i've made an actual github repo for this, if you could please fork my repo then add your changes and send a pull request so I can see all your changes in detail and review them before adding them to the library. https://github.com/tech2077/PyLCD
DeleteThanks a lot for this.
ReplyDeleteI do not own RasPI, but I am using a low-cost router with OpenWRT, lots of i2c devices connected to i2c-tiny-usb adapter and this would make a nice way how to display the information.
Thanks again.
Hi,
ReplyDeletejust a short question. I have this LCD with I2C-board and do find it at 0x27 (using i2cdetect) on bus 0. But, when I connect the 5v, GND, SCL and SDA to the display, it starts to flicker. Moreover, I cannot succeed with using any of the code given here (neither Thorraks).
How do you guys wire this? Am I missing something?
What revision of raspberry pi do you have?
DeleteYour script works fine but is it possible to tell us , how we can use own made characters?
ReplyDeleteThe HD44780 lcd's can be programmed in CGRAM from 0 to 7.
In your code, if i read correct, you already have that part in it, but no where i can find a example for it! the code what i mean is.:
/ add custom characters (0 - 7)
def lcd_load_custon_chars(self, fontdata):
self.lcd_device.bus.write(0x40);
for char in fontdata:
for line in char:
self.lcd_write_char(line)
Best Regards.
Pascal.
It takes a 8x8 list of bytes that hold pixel data for the char, this is an untested feature as I never had a need for it, but if it works for you great.
DeleteI can't use that futere because no one tryed it and there is no example that can be found on the internet of it.
DeleteSo my qoustion was if you have a code for us with a example of it.
Because there are symbols that for example can be used thats not in the standard symbol list of a HD44780 lcd.
Hello.
DeleteAll work fine, but how to use custom characters?
Thanks.
Thanx for ur post nd ur article, today I just tell u about a biggest retailar in electronics, ya @Sargam Electronics is the biggest Retail Chain in Electronics in Delhi. Sargam deals with big brands like Samsung, Sony, Lg, Toshiba, Sansui, Hitachi, Voltas, Godrej, Azure, Nikon, Whirlpool, Dell, HP, Acer and many more brands. @Sargam Electronics offers you to buy many electronics products like laptop,mobile,camera,washing machine,refrigerator,air conditionar and multiple electronics products at huge discount.
ReplyDeleteDear SPAMMER, the page cannot be found!
DeleteThanks for this. You saved me a bunch of time. However, I think I found a bug. I've got a type of LCD/i2c config which you call "reverse == 1". When I use that, it sends two different strobe signals, because you have this (forgive the underscores... blogspot is stripping out my indenting spaces):
ReplyDeletedef lcd_strobe(self):
__if self.reverse == 1:
____self.lcd_device.write((self.lcd_device.read() | 0x04))
____self.lcd_device.write((self.lcd_device.read() & 0xFB))
__if self.reverse == 2:
____self.lcd_device.write((self.lcd_device.read() | 0x01))
____self.lcd_device.write((self.lcd_device.read() & 0xFE))
__else:
____self.lcd_device.write((self.lcd_device.read() | 0x10))
____self.lcd_device.write((self.lcd_device.read() & 0xEF))
If reverse == 1, the first *AND* third blocks get executed because the third block is executed in the case that reverse != 2. What I added, on my end, was an else before the 2nd 'if'...
def lcd_strobe(self):
__if self.reverse == 1:
____self.lcd_device.write((self.lcd_device.read() | 0x04))
____self.lcd_device.write((self.lcd_device.read() & 0xFB))
__else:
____if self.reverse == 2:
______self.lcd_device.write((self.lcd_device.read() | 0x01))
______self.lcd_device.write((self.lcd_device.read() & 0xFE))
____else:
______self.lcd_device.write((self.lcd_device.read() | 0x10))
_____self.lcd_device.write((self.lcd_device.read() & 0xEF))
Works a little better. :) Of course, there's probably a way to do it with case statements, but I don't know any more about Python than I absolutely have to.
I'll go and fix this on the gist, a nice addition of elif to it should work. Thanks for the contribution.
DeleteSame problem exists in lcd_write_char(), incidentally. Now my LCD works. Hooo boy! You should have *seen* the crazy stuff I was seeing with 2 sets of commands being sent to my LCD. :)
ReplyDeleteOkay... more fixes...
ReplyDeleteTurns out that the lcd_load_custom_chars() didn't work for me. It was calling self.lcd_device.write(0x40) when it *should* have been calling self.lcd_write(0x40) so that the command would be properly written in two half-byte nibbles:
____def lcd_load_custom_chars(self, fontdata):
#_______self.lcd_device.write(0x40);
________self.lcd_write(0x40);
________for char in fontdata:
____________for line in char:
________________self.lcd_write_char(line)
Also, while I was at it (because I was messing with custom chars so that I could make interesting logos), I needed to be able to place the cursor anywhere I wanted, so I wrote:
row_offsets = [ 0x80, 0xC0, 0x94, 0xD4 ]
def lcd_locate_cursor(self, row=1, col=1):
____addr = self.row_offsets[row - 1] + col - 1
____self.lcd_write(addr)
Now, in lcd_puts(), you can replace the:
def lcd_puts(self, string, line):
____if line == 1:
________self.lcd_write(0x80)
____if line == 2:
________self.lcd_write(0xC0)
____if line == 3:
________self.lcd_write(0x94)
____if line == 4:
________self.lcd_write(0xD4)
____for char in string:
________self.lcd_putc(char)
... with just ...
def lcd_puts(self, string, line):
____self.lcd_locate_cursor(line)
____for char in string:
________self.lcd_putc(char)
Man... I just can't stop tweaking. I've now added some thread-sync stuff so that you can now use this with multi-threaded code, and you don't have to worry about the i2c commands getting all interleaved. I'll stop posting code in the comments, because it's getting a little overboard. If you're interested in the code, let me know...
ReplyDeleteJoe, can you post a working code of the custom chars? every body look for it aorund the internet, did you have a full working code? please :(
ReplyDeleteI'll get it posted soon. I'm in the middle of a big overhaul of this, actually. I noticed that Matthew has this on Github, so I'll probably send him a pull-request once I have it working (and once I figure out how to *send* a pull request). Some improvements include:
ReplyDelete- Thread-safety (you can have multiple threads all sending stuff to the LCD and it won't cause problems with the signaling to the chip)
- Backlight control
- "Reverse" code is all moved to two methods which handle all of the moving around of the RS, Ena, R/W, Backlight, and data bits.
- "Test" method which turns on each bit, one at a time, so that you can use a volt-meter to see which of your LCD pins each bit corresponds to. Then, you can figure out which "reverse" mode to use, or if you need to make your own.
I did the same using C programming. Anybody wants to try it out? http://karunadheera.com/index.php/archives/194
ReplyDeleteYou should however have Gordon's wiringPi - http://wiringpi.com/download-and-install/ first.
I don' know if ur code will work with IIC/I2C/TWI/SPI module with 20x4 lcd that i bought from ebay.
DeleteCan this code will work that i bought from ebay? It is IIC/I2C/TWI/SPI Serial Interface2004 20X4 Character LCD Module Display Blue
ReplyDeleteThanks!
I deleted the I2c portion of the module because I use Adafruit's Python_I2C module. The line " self.lcd_device = i2c_device(addr, port)" gives error
ReplyDeleteTraceback (most recent call last):
File "test.py", line 5, in
lcd=Hc44870.lcd(0x27,0)
File "/home/root/Hc44870.py", line 31, in __init__
self.lcd_device = i2c(addr, port)
NameError: global name 'i2c' is not defined
I not sure what properly should be here.
Jim
The code above doesn't work. So I switched to http://www.recantha.co.uk/blog/?p=4849
DeleteIt loaded right away without error problem.Copied and pasted
working good I used indentation 4, using python 3.2.3
Btw, I don't used AdaFruit library,
I'm very happy with recantha's blog. I suggest u try this recantha's site.
U missed something
ReplyDeleteself.lcd_device = i2c(addr, port)
change to:
self.lcd_device = i2c_device(addr, port)
supraDecember 11, 2013 at 7:44 PM
DeleteThanks for responding! You said
U missed something
self.lcd_device = i2c(addr, port)
change to:
self.lcd_device = i2c_device(addr, port)
I get the same error. I wo7uld really like to use Adafruit's I2C python code to use some of the other methods it has. I had changed the line as you saw as an attempt to make things work. The only other change I made was to delete the i2c_device lines. I had already loaded the Python_I2C module in my code.
Jim
Did u added import Hc44870.py to test.py?
DeleteI couldn't find code from AdaFruits
Python_I2C is a library and test.py is main program to test it?
DeleteDid id u added import Python_I2C in test.py?
Thanks a million for this. This took me so long to get going on my beaglebone black. I have an i2c lcd backpack that I bought from ebay that doesn't work with anything else, and I couldn't find easy code to modify to make it work properly. I went through probably 50 sites before I found your sample code.
ReplyDeleteMan i just wanted to thank you for the code you provide here , it help me a lot to make work this lcd with my java app , now u show me the way , almost everythings work.
ReplyDeleteyour code is clear , clean and work perfectly, Good job !
ok, good idea, it can help me
ReplyDeletei like them
it is very good
LCD Panel
LCD Module
LCD screen
Arrow Hobby
Freewing RC jet