Arduino + cheap i2c LCD Backpack
So you bought one (or more) of these i2c LCD backpacks and, despite having gone through several code examples downloaded here and there, you still can’t get it to work: glitchy backlight, meaningless random characters or… just the void.
Your brain is telling you “You got a gift from China with fake ICs” but you still want to believe there’s good in people, so you spend a little more time searching the web for someone who has your exact same problem and you end up here. How lucky!
Read along, as I tell you what I found out about these very useful little boards and got mine to work.
While on a shopping spree on EBay I ended up purchasing a couple of i2c backpacks for HD44780 based LCDs. These controller boards are based on the PCF8574* by NXP. It took me a bit to figure out why I couldn’t make them work. Hopefully this post will save someone’s time (not really sure, though, as I can be quite prolix), and teach something to some struggling beginner.
The Metal Side Of Life
Liquid Crystal Displays, from here on LCD(s), are a really useful and interesting thing to have in your lab, but out of the box you need a lot of pins (at least 6 if you only write to the display) to make them work as well as some other components and wiring on the side. In the next photo you can see one I hooked up in 4 bit mode using 2 pins for Register Control and 4 to latch data in using the LiquidCrystal library from Arduino.
That seems like a lot of pins, so I decided to finally solder one of these backpacks to control the display over the i2c port.
Using just two pins for i2c (rather than the 6/10 needed for the 4/8 bit Parallel Interface and Register Control in Write-Only configuration) can save you a lot of jumper wires as well as time, allowing you to use your precious Arduino pins for other purposes. Basically the way this works is by outsourcing the logic behaviour of those pins to an external component. The sequence of logic levels (HIGH/LOW) is pushed to the PCF8574* over its i2c interface. This way you don’t digitalWrite()/digitalRead() on your Arduino but ask another device to do it instead, using a communication protocol to control such device. But let’s not get lost in things I’ll probably cover some other time… this is not the place.
I sometimes run out of available IO on my prototypes, hence using a Serial Interface such as i2c is sort of a life-saver, especially because often my SPI port is busy doing else and would anyway require 4 pins. If you don’t need to transfer data at high rates (SPI is good for that), you can use a serial display to show debug information while you’re working on some UART Serial Communication Protocols and can’t just Serial.write() your debug info to a console. I know I could use Arduino boards with two UART interfaces but I really like the ATMega328 and 32U4, and in my specific case the Serial Protocol can be quite time critical at high speeds (more on this in the future).
Once I got one of these hooked up I immediately turned to the web to know how to control it, and there seems to be a lot of information but it’s a bit scattered around, so I gathered some resources, figured out a few kinks which made me waste a bit of time, and decided it would be a good idea to post this somewhere for future reference and to help a few fellow makers.
Setting it up is quite straight forward once you figure out how this backpack interfaces with the data and control pins of the HD44780.
The PCF8574* Remote 8-bit I/O expander for I2C-bus with interrupt is used to control the HD44780 using only the highest 4 bits of its data bus (D4, D5, D6, D7), and uses the other 4 IO pins for EN, RS, R/W as well as controlling the status of the backlight LED (which can be hardware disabled on some backpacks via means of a jumper).
Without digging too much into how the HD44780 works (there’s plenty of documentation if you look that up), I’ll try and go through how I set it up and got it to work. Let it suffice to know that this is what the blocks of pins illustrated above are used for:
Power: Main logic power (+5v) and
Contrast: Usually connected to a potentiometer voltage divider
Register Control: Set HIGH/LOW depending on desired function
Not Used [D0-D4]: Low byte section for 8 bit data mode only
Data Control: High byte section for 4 bit data mode (also used in 8 bit mode)
LED Power: Backlight voltage (+5V)
In a parallel setup the LiquidCrystal library would take care of flipping these pins HIGH or LOW to shift data into the controller. This task is taken over by the PCF8574* chip, which will transform the data pushed over i2c by a modified LiquidCrystal library into a parallel array of logic levels (bits) and latch the data in. This setup, using only 4 bits, is a few microseconds (µS) slower than using the 8 bit mode… do you really care?
Always test breakout boards and expansion modules on a breadboard BEFORE you solder them somewhere. Believe me, it’s worth your time.
Another interesting thing about these IO expanders is that they come in two flavours, hence the notation PCF8574*. This chip comes into two main versions (regardless of the package): PCF8574 and PCF8574A. The A version differs only in matter of possible i2c address range, exposing a default address of 39 (27 hex) for the former and 63 (3F hex) for the latter. As noted in the example code following, you can use the i2c_scanner utility sketch to find out which one is yours, but if you see an “A” on the chip markings you’ll know that your base address is 63 (3F hex).
While this may seem like an annoyance, if you think for a second you realise that you can have both chips on the same i2c bus and talk to them without conflicts. Even better, part of the i2c address is configurable with 3 pins, which gives you the opportunity to have 4 of each on the same bus, with 8 different addresses.
Do the math and you’ll quickly realise that you have (2 chips) * (4 addresses) * (8 IO ports) = 64 extra Input/Output pins that you can read and write from a single Arduino board, all just learning how to use i2c. In our specific case, this gives you the chance to have 8 of these displays connected to your board. Make sure you power them externally and not through the Arduino 5V pin.
I’ll write something about port expanders at some point, but now let’s move on to…
The Code Side Of Things
First thing first: you won’t be able to make this work using the standard Arduino LiquidCrystal.h library but will have to use the extended one by Francisco Malpartida which can be found here https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home#!downloading-and-installation. Once downloaded place the extracted folder in your libraries folder.
This library adds a few more classes including LiquidCrystal_I2C (which we will use) and other variants (ByVac and backpacks/displays with shift registers as controllers) with some interesting overloaded constructors. There is an example (HelloWorld_i2c.pde) which may not work out of the box for a couple of reasons:
- wrong i2c address
- confusing BACKLIGHT_PIN usage
The .pde extension will probably tell you that this example is old and hasn’t been checked in a while).
Let’s move on to understand something about these backpacks. I have not traced the schematics because it’s quite intuitive how the PCF8574* is used here.
With its 8 IO pins (“quasi-bidirectional ports” is how they are defined in the datasheet) it can control (read/write) 8 logic levels, and since the HD44780 requires at least 4 data pins for its 4 bit operation mode and 3 control pins for registers operations, it’s quite logical that 7 of these IO pins are used for data and control, and the one left out controls a MOSFET to activate the backlight (there is an SMD MOSFET on the PCB close to the LED jumper). This allows you to turn the backlight on and off with a single command
setBacklight( uint8_t value )
Any non-zero value will turn it on.
The library example HelloWorld_i2c.pde did not work with the backpack because of a different way to control the backlight using Arduino pin 13. I deduce this was coded to work with a bare chip, connecting the backlight to an Arduino physical pin via a MOSFET and controlling it with digitalWrite().
One thing that pushed me to investigate this further was the overloaded constructor (don’t get scared if you don’t know what that is) I found in the library source code. When you can’t find an answer online, checking out the code can yield great results, although, as a beginner, you may not really enjoy reading C++:
lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin,Bl_pin, POSITIVE);
Here’s my take on how to use the LiquidCrystal_I2C library and what the configuration options stand for. We’ll use the above mentioned overloaded constructor to initialise the object passing every single pin of the controller and assigning it a functionality.
Since Medium sucks at displaying code, I created a gist. Some comments are key to understanding some of the inner workings.
That’s about it. I hope you learned something new reading through my ramblings. Questions are welcome in the comments section.
See you next time :)
References
Hitachi HD44780 LCD Controller: https://en.wikipedia.org/wiki/Hitachi_HD44780_LCD_controller
Tronixlab’s tutorial PCF8574* backpacks: http://www.instructables.com/id/Using-PCF8574-backpacks-with-LCD-modules-and-Ardui/
Arduino.cc’s great list of LCD resources: http://playground.arduino.cc/Code/LCD
PCF8574 LCD backpacks (mostly knock-offs) on EBay: http://www.ebay.com/sch/i.html?_from=R40&_trksid=p2050601.m570.l1313.TR6.TRC2.A0.H1.Xpcf8574+LCD+backpack.TRS0&_nkw=pcf8574+LCD+backpack&_sacat=0
Downloads
Francisco Malpartida’s New LiquidCrystal libary: https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home
Bonus
These boards are labeled as LCD backpack, but remember that they’re a sort of breakout board for a PCF8574*, which means you can use them as an 8 port expander with your microcontroller by just writing and reading from it.
The datasheet will tell you everything you need to know, but I’ll probably write another post on using these for other purposes than driving LCDs :)