The Arduino software has an official library (LiquidCrystal Library) to control -in a very easy way- that kind of displays. Unfortunately, you need at least 6 pins from the Arduino to control one single display. Once again we'll run out of pins quickly...
So, can we use the same strategy used to control an ICM7218 with only two pins, now with the LCDDisplay? Of course, we can, but this time we'll have to port the original library.
The idea to save (digital) pins is to plug as many PCF8574 chips as you need in a I2C bus, and control it with analog 4 and 5 from Arduino with the Wire library. I recommend to read these two posts (here and here) if you are not familiar with the concept.
The porting was done easily and with good results. I did it without opening the original datasheet, just recoding the Arduino pin working to the use of the PCF8574 connected to the I2C bus. The original library was written in a very clear way.
You can find the new library in my GitHub repository, under Arduino/libraries/PCFCrystal, including some example sketches. Feel free to download and use the library. Remember that the library is provided "as is", without any kind of warranty. (It's been tested only with the two constructors presented below).
Ok, the hard work is done. You only have to use the library! The use of the library is very easy if you know the Liquid Crystal. You only have to use a different syntax for the constructor, include and initialize the Wire library, and the rest of the code is actually the same code you used with Liquid Crystal.
Let's see it in action!
Let's start with an example. I've modified the Hello World example to show the time it takes to print a single character. Below you have a video showing a similar sketch to the one you can find with the code (PCFHelloWorld). It's a bit different as I changed it after the video recording:
As you can see I'm using an Arduino Pro Mini, but this don't affect to the results.
Now lets see the wiring. There are two versions, for a 4 bits interface and a 8 bits one.
This is the schema for the 4 bits interface. In this case I use a single PCF8574 to control the 6 pins needed.
Now the schema for the 8 bits interface. As you can see in this case I need an extra PCF8574 as I need to control 10 pins.
You can note in the schemas, and see in the video too, that there is a blinking led attached to a free pin of one PCF8574. This is to demonstrate that you can use the unused pins of the PCF8574 for whatever you want without interfering the LCDDisplay.
And now the code. Let's focus in the differences with the original library.
First we need to initilize the Wire library to use I2C.
// Inclusion of Wire #include <Wire.h> void setup() { // Setup for Wire Wire.begin(); // TODO: more initialization }
Then we have to create an PCFCrystal object indicating the pins and address of the PCF(s) used.
For the 4 bits interface this is the code:
byte buffer = 0; // initialize the library with the numbers of the interface pins // rs, en, d0, d1, d2, d3, address, buffer // 4 bits PCFCrystal lcd(B00100000, B00010000, B00000001, B00000010, B00000100, B00001000, 0x20, &buffer);
As you can see, you need a byte to store the information sent to the PCF8574. Then we call the constructor passing the masks used to access the pins (rs, enable and d0 to d3) plus the byte used to store data and the address of the PCF8574 in the I2C bus. Note that you can use whichever pins you prefer from the PCF8574.
In the case of the 8 bits interface the code is similar except because you need two PCF8574 and consequently, two buffers and addresses. Note that in this case, all the data pins must be in the first PCF, and the control pins (enable and rs) must be in the second one. Here you have the code:
byte buffer = 0; byte data = 0; // initialize the library with the numbers of the interface pins // rs, en, d0, d1, d2, d3, d4, d5, d6, d7, data_address, control_address, data_buffer, control_buffer PCFCrystal lcd(B00100000, B00010000, B10000000, B01000000, B00100000, B00010000, B00000001, B00000010, B00000100, B00001000, 0x21, 0x20, &data, &buffer);
From that point, the rest of the code is the same as using a regular Liquid Crystal library. Here is the whole sample code with comments:
/* PCFCrystal Library - Hello World Demonstrates the use a 16x2 LCD display using the I2C capabilities of Arduino. This library is a porting of the original LiquidCrystal library, prepared to control the LCD display via one or two PCF8574. The LiquidCrystal and PCFLiquidCrystal libraries work with all LCD displays that are compatible with the Hitachi HD44780 driver. There are many of them out there, and you can usually tell them by the 16-pin interface. This sketch prints "Hello World!" to the LCD and shows the time and the time consumed to print a single character The circuit (4 bits interface): * LCD RS pin to PCF8574 output 5 (pin 10) * LCD Enable pin to PCF8574 output 4 (pin 9) * LCD D4 pin to PCF8574 output 0 (pin 4) * LCD D5 pin to PCF8574 output 1 (pin 5) * LCD D6 pin to PCF8574 output 2 (pin 6) * LCD D7 pin to PCF8574 output 3 (pin 7) * 10K resistor: * ends to +5V and ground * wiper to LCD VO pin (pin 3) Library PCFCrystal created on June 2010 http://ardugonic.blogspot.com Liquid Crystal: Library originally added 18 Apr 2008 by David A. Mellis library modified 5 Jul 2009 by Limor Fried (http://www.ladyada.net) example added 9 Jul 2009 by Tom Igoe modified 25 July 2009 by David A. Mellis http://www.arduino.cc/en/Tutorial/LiquidCrystal */ #include <wire.h> // include the library code: #include <pcfcrystal.h> byte buffer = 0; byte data = 0; // initialize the library with the numbers of the interface pins // rs, en, d0, d1, d2, d3, address, buffer // 4 bits PCFCrystal lcd(B00100000, B00010000, B00000001, B00000010, B00000100, B00001000, 0x20, &buffer); // 8 bits //PCFCrystal lcd(B00100000, B00010000, B10000000, B01000000, B00100000, B00010000, B00000001, B00000010, B00000100, B00001000, 0x21, 0x20, &data, &buffer); void setup() { Wire.begin(); // set up the LCD's number of rows and columns: lcd.begin(16, 2); // Print a message to the LCD. lcd.print("scnds:loop:micrs"); } long counter = 0; int led = HIGH; void loop() { counter++; // set the cursor to column 0, line 1 // (note: line 1 is the second row, since counting begins with 0): lcd.setCursor(0, 1); // print the number of seconds since reset: lcd.print(millis()/1000); // print the number of loops since reset lcd.print(":"); lcd.print(counter); // print the time to print 1 char long mics = micros(); lcd.print(":"); lcd.print(micros() - mics ); if (counter % 27 == 0) { if (led == LOW) { buffer = buffer | B01000000; led = HIGH; } else { buffer = buffer & ~B01000000; led = LOW; } Wire.beginTransmission(0x20); Wire.send(buffer); Wire.endTransmission(); } }
Perfomance
Obviously the drawback of using this approach is that you spend more time to make the operations. But how much more? I don't know, but as an estimation, this sample sketch prints in the display the time spent to write a single character. I've tested the sample with both LiquidCrystal and PCFCrystal libraries in 4 and 8 bits interface and this is the result:
- LiquidCrystal, 8 bits: 208 microseconds.
- LiquidCrystal, 4 bits: 324 microseconds.
- PCFCrystal, 8 bits: 1196 microseconds.
- PCFCrystal, 4 bits: 1664 microseconds.
This is only an orientation. When you write strings it's supposed to take comparatively less time difference. Anyway it's clear that the saving in pins is payed with a decrease of performance. Is up to you to decide witch solution is better for your project.
Great job! I was looking for something like this. Thanks.
ReplyDeleteThanks. Let me know if you have any problem.
ReplyDeletei am having problems converting to the liquidcrystal library would you be able to help me out a bit on how to convert the code to this library?
ReplyDeleteHi nickcook,
ReplyDeletesure, let me know what's your problem and I'll do my best to solve it.
i just read this whole page again and understand how to do it now i didnt notice the pcfcrystal library wasnt included in the arduino install. i will try it again and let you know if i need help. thanks for the quick response.
ReplyDeleteThanks a lot to share this...
ReplyDeleteI try to use 2 outputs not used by your code ! (VERY GREAT CODE !) and it's work... I created 2 functions in order to do this.
But, i saw in cpp file two functions : writePCF and setBit ? possible to switch on/off an output only with this 2 functions ?
Hi bop,
ReplyDeleteyes, you can use the pair of functions you mentioned to control outputs not used by the display. Or, alternatively, you can use the same schema I use in my sample to control the "external" led.
Is there a trick to getting this to work with arduino 1.0? works fine with 022 but not 1.0? thanks in advance
ReplyDeleteHi,
ReplyDeleteI haven't it tested with arduino 1.0 yet, but there shoudn't have any problem. What problem have you found?
In file included from PCFHelloWorld.cpp:45:
ReplyDeleteD:\arduino\arduino-1.0\libraries\PCFCrystal/PCFCrystal.h:6:22: error: WProgram.h: No such file or directory
In file included from PCFHelloWorld.cpp:45:
D:\arduino\arduino-1.0\libraries\PCFCrystal/PCFCrystal.h:88: error: conflicting return type specified for 'virtual void PCFCrystal::write(uint8_t)'
D:\arduino\arduino-1.0\hardware\arduino\cores\arduino/Print.h:48: error: overriding 'virtual size_t Print::write(uint8_t)'
PCFHelloWorld.cpp: In function 'void loop()':
PCFHelloWorld.pde:-1: error: 'class TwoWire' has no member named 'send'
As of Arduino 1.0, the Wire.send() function was renamed to Wire.write() for consistency with other libraries.
In file included from PCFHelloWorld.cpp:45:
D:\arduino\arduino-1.0\libraries\PCFCrystal/PCFCrystal.h:6:22: error: WProgram.h: No such file or directory
In file included from PCFHelloWorld.cpp:45:
D:\arduino\arduino-1.0\libraries\PCFCrystal/PCFCrystal.h:88: error: conflicting return type specified for 'virtual void PCFCrystal::write(uint8_t)'
D:\arduino\arduino-1.0\hardware\arduino\cores\arduino/Print.h:48: error: overriding 'virtual size_t Print::write(uint8_t)'
PCFHelloWorld.cpp: In function 'void loop()':
PCFHelloWorld.pde:-1: error: 'class TwoWire' has no member named 'send'
As of Arduino 1.0, the Wire.send() function was renamed to Wire.write() for consistency with other libraries.
Ok, I'll have to port my library...
DeleteWhen I have the chance, I'll let you know.
Thanks.
For Arduino 1.0 compliant is necessary change send() by write() and receive() by read(). Also is necessary replace WProgram.h by Arduino.h. Read the Release Notes for Arduino 1.0.
ReplyDeleteThanks for the info
DeleteHello Ardugo,
ReplyDeleteI cannot run PCFHelloWorld on Arduino 1.0
Please help
In file included from PCFHelloWorld.cpp:45:
D:\arduino\arduino-1.0\libraries\PCFCrystal/PCFCrystal.h:88: error: conflicting return type specified for 'virtual void PCFCrystal::write(uint8_t)'
D:\arduino\arduino-1.0\hardware\arduino\cores\arduino/Print.h:48: error: overriding 'virtual size_t Print::write(uint8_t)'
Best regards,
SVA
Hello,
DeleteI haven't had the chance to test the library on Arduino 1.0. Maybe the previous comment from Rafaro can help you.
Regards.
Hello,
DeleteUnfortunately,no.
I did not find neither send()nor receive(). WProgram.h was replaced by Arduino.h earlier.
Soorry.
Best regards
Hello,
ReplyDeleteI have made changes to your library so as to work with Arduino 1.0. How do I go about sending it to you?
Hi,
Deleteyou can send me your work to ardugoblog@gmail.com and I'll publish it in my Git repository, or, if you have already published it somewhere, just send me the url and I'll post a link to it here.
Thanks.
Would it be difficult to convert this project to a different chip with more I/O?
ReplyDeleteExample PIC18F25J10 $2.25 mouser
Thanks for the example.
ReplyDeleteI am using the Liquid Crystal Library with an ethernet arduino board - http://arduino.cc/en/Main/ArduinoBoardEthernet
I initialize the LCD first and then turn towards initializing the ethernet with begin() but it never works
LCd works on I2c
Code-------
lcd.init(); // initialize the lcd
// Print a message to the LCD.
lcd.backlight();
lcd.setCursor(0, 1);
lcd.print(" LCD Init OK! ");
//Initi the ethernet
Ethernet.begin(); -It never works?
Should i use Wire functions?
Sorry, I don't have the time currently to analyse your problem.
DeleteRegards.
This is a great example! I am trying to figure out how to use a MC23017 port expander. I'm extremely new to all of this - so a couple of questions:
ReplyDelete1) Will your library work with the MC23017 port expander?
2) Without reading the data sheet - how did you know how to address each pin individually - when you were instantiating the LCD object?
Hi,
Deleteabout the firs question, I'm sorry but I don't know this chip and currently I don't have the time to look at it.
About the second one, to port the library I mapped every pin used in the original library to one pin in the PCF8574. When the original library sets or resets a pin, I set or reset the corresponding in the PCF. So I don't need the original datasheet.
Regards.
Will this work for the Arduino Mega? The SCL and SDA pins are digital 21 and 20 respectively.
ReplyDeleteI haven't tested but it should work ...
DeleteHi Ardugo
ReplyDeleteI swung by github but it appears not to have been updated in 3 years.
Any luck on an update for your PCF8574 library ?
yours seems the only library i can get to work on this chip.
Thanks for your time
Rupert
Hi Rupert,
Deletesorry for this long silence, but latelly I don't have the chance to continue my electronic projects...
Anyway, I have a version of the library that Raj Goutam send me wich is compatible with the newer versions of the IDE.
If you're still interested I can send you if you send me your email.
Regards.
This comment has been removed by a blog administrator.
ReplyDeleteHi Ardugo,
ReplyDeleteI have been trying to get the library working, but stil fails, so would you like the working version you have
Thanks in advance
Hi,
Deletewhat's your email address? (If you are the same user that sent a comment on February 4, I've just sent you the last libraries I have).
Regards.