4X20 LCD display with I2C Interface - Project Showcase - Netduino Forums
   
Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

4X20 LCD display with I2C Interface

LCD I2C HD4478 LCD I2C

  • Please log in to reply
36 replies to this topic

#1 AxelG

AxelG

    Advanced Member

  • Members
  • PipPipPip
  • 52 posts

Posted 04 January 2013 - 02:58 PM

My holiday present to myself this year was the following 4X20 LCD: (link)

 

It is based on the HD4478 LCD driver chip, front ended with an I2C interface, popular because it only uses two ports on your MCU.

 

The information provided by the vendor is sparse, and in some cases incorrect.  The address of the device is listed as 0X27 but is actually 0x3F.  I was able to locate the Arduino (C++) driver for the I2C interfaced version, so I translated the Arduino driver into C#.

 

I then integrated Stefan Thoolen’s (http://www.netmftoolbox.com/) MultiI2C class to drive the I2C port.

 

After playing with the timing on the initialization a bit, it seems to be working well.  I added some text management methods to the driver to simplify displaying text on four lines.  The LCD is set up in such a way that it is actually driving two 40 character lines of text, wrapped on the screen.  This made it confusing for me to keep straight, so these new methods made it easier to keep track of the text.

 

Hardware connection is a snap.  Netduino (running 4.2) connected with 5v, ground, A4, and A5 to the LCD module.  This LCD is using an I2C interface, so you need pullups on the A5 and A4 wires.  I used 2.2k (corrected to 1.8k below) resistors on the 3V3 port.  Worked great.

 

One nagging issue is that after running for some time, the screen will start displaying garbled data.  I have not been able to pinpoint the reason; maybe bus speed, bad data wires, poor power?  If I figure that out, I will post an update.***

 

Thanks to everyone on the forum for inspiration and ideas to get this working; especially Stefan for a great toolbox.  Let me know what bugs you find or ideas on improvements.

 

***UPDATE: The garbage issue is resolved: I think.

Problem was that my pullup resistors were too high of a value (changed for 1k8.  I remembered my analog circuit class I took 30 years ago and realized these resistors are part of a RC circuit that affect the signal shape on the wire.  To high = smaller slope.  There was also a small bug in the code that was not sending the initialization bytes correctly (I will upload the working copy.  I also moved some of the text management methods out of this class into a separate display class)

Attached Files



#2 JerseyTechGuy

JerseyTechGuy

    Advanced Member

  • Members
  • PipPipPip
  • 870 posts

Posted 04 January 2013 - 03:19 PM

Yep, it's a speed issue. While it could also be power or loose wired most likely it's simply I2C.  I2C is bad when it comes to LCD displays.  SPI is the safer way to go as it is much faster.  I've done extensive testing of both with the same LCD displays and I2C is just not reliable especially if there are a lot of LCD refreshes happening often.



#3 Stefan

Stefan

    Moderator

  • Members
  • PipPipPip
  • 1965 posts
  • LocationBreda, the Netherlands

Posted 04 January 2013 - 03:30 PM

From my experience:

most of the time those displays are used in 4-bit mode. To enter this mode, a small handshake is required. Now and then the display "just resets" to 8-bit mode. All data from that point on is damaged.


"Fact that I'm a moderator doesn't make me an expert in things." Stefan, the eternal newb!
My .NETMF projects: .NETMF Toolbox / Gadgeteer Light / Some PCB designs

#4 JerseyTechGuy

JerseyTechGuy

    Advanced Member

  • Members
  • PipPipPip
  • 870 posts

Posted 04 January 2013 - 03:59 PM

From my experience:

most of the time those displays are used in 4-bit mode. To enter this mode, a small handshake is required. Now and then the display "just resets" to 8-bit mode. All data from that point on is damaged.

 

Very likely the case.  I've thought about designing an 8bit I2C LCD Backpack using either the MCP23017 or PCA8575 chip as most of the Hitachi compatible display's I'm using can be 8bit or 4bit.  It takes more pins to drive it which is why most are 4bit, but if you're using an I2C backpack of sorts the extra pins are a mute point.



#5 iced98lx

iced98lx

    Advanced Member

  • Members
  • PipPipPip
  • 134 posts
  • LocationSouth Dakota

Posted 04 January 2013 - 05:24 PM

Keeping an eye on this would like a reliable i2c LCD



#6 Stefan

Stefan

    Moderator

  • Members
  • PipPipPip
  • 1965 posts
  • LocationBreda, the Netherlands

Posted 04 January 2013 - 06:08 PM

I've also considered to support the 8-bit mode with my driver, as well as a reset method to re-initialize the 4-bit mode. Maybe I will have some time for this this year ;)


"Fact that I'm a moderator doesn't make me an expert in things." Stefan, the eternal newb!
My .NETMF projects: .NETMF Toolbox / Gadgeteer Light / Some PCB designs

#7 AxelG

AxelG

    Advanced Member

  • Members
  • PipPipPip
  • 52 posts

Posted 04 January 2013 - 07:44 PM

Thanks for the quick replies.

 

I wrote a reset() method in the driver, and I have attached it to the onboard switch.  Hitting the switch resets the screen.  Seems like there should be a better way...

 

Maybe I will remove the i2c interface and go to a native HD4478 8 bit driver...



#8 AxelG

AxelG

    Advanced Member

  • Members
  • PipPipPip
  • 52 posts

Posted 04 January 2013 - 11:06 PM

I updated my code to call the reset() method every time I need to clear the LCD, and that has worked all day today with 1,000+ screen updates an no garbage.  Seems like bruit-force approach but is seems to be working for now.

 

I also posed the driver code if anyone is interested.



#9 JerseyTechGuy

JerseyTechGuy

    Advanced Member

  • Members
  • PipPipPip
  • 870 posts

Posted 05 January 2013 - 12:27 PM

I updated my code to call the reset() method every time I need to clear the LCD, and that has worked all day today with 1,000+ screen updates an no garbage.  Seems like bruit-force approach but is seems to be working for now.   I also posed the driver code if anyone is interested.
Sorry, maybe I missed something. Where was your driver code posted?

#10 AxelG

AxelG

    Advanced Member

  • Members
  • PipPipPip
  • 52 posts

Posted 05 January 2013 - 10:23 PM

I attached the driver code to the first post.



#11 tim c

tim c

    Advanced Member

  • Members
  • PipPipPip
  • 31 posts

Posted 06 January 2013 - 08:21 PM

Keeping an eye on this would like a reliable i2c LCD

I have these working just fine on a N+2:

 

https://www.adafruit.com/products/292

https://www.adafruit.com/products/198

 

I2C to a 20x4 LCD and it works great.

 

Here is a video of it working on my BoxCar:

 

http://sdrv.ms/UwMKxE

 

LMK if you want a code sample.



#12 carb

carb

    Advanced Member

  • Members
  • PipPipPip
  • 352 posts
  • LocationCrystal River, Florida

Posted 06 January 2013 - 11:26 PM

Tim,

 

We can always use good code samples.

 

I don't know if you looked at the datasheet for the chip (MCP23008) used on the I2C/SPI backpack, but it can do so much more than drive a LCD display.

 

It can be used as:

  • a general purpose i2c expander,
  • it has a power-on-reset circuit that holds the output until the chip is fully powered,
  • 8 digital I/O's,
  • I/O's can be configured for interrupt on change,
  • I/O's can be configured for a 100k pullup resistor,
  • Low current but can be used to control a relay board,
  • 8 backpacks can be used on one I2C line.

And the price is right!

 

Chuck



#13 Stefan

Stefan

    Moderator

  • Members
  • PipPipPip
  • 1965 posts
  • LocationBreda, the Netherlands

Posted 07 January 2013 - 07:28 AM

Yup! I also have a MCP23008 on my desk for future use. I'm planning to support it at some point. Although I started with the 16-pin variant because it's used more these days.


"Fact that I'm a moderator doesn't make me an expert in things." Stefan, the eternal newb!
My .NETMF projects: .NETMF Toolbox / Gadgeteer Light / Some PCB designs

#14 AxelG

AxelG

    Advanced Member

  • Members
  • PipPipPip
  • 52 posts

Posted 07 January 2013 - 06:48 PM

I have these working just fine on a N+2:

 

Here is a video of it working on my BoxCar:

 

http://sdrv.ms/UwMKxE

 

LMK if you want a code sample.

 

I would love to see the code samples!



#15 tim c

tim c

    Advanced Member

  • Members
  • PipPipPip
  • 31 posts

Posted 18 January 2013 - 02:18 PM

I was out of town on work - I'll try and put it together this weekend.

 

Tim



#16 tim c

tim c

    Advanced Member

  • Members
  • PipPipPip
  • 31 posts

Posted 22 January 2013 - 02:53 PM

Attached is a sample I just ran on my NP2 with these:

 

https://www.adafruit.com/products/292

https://www.adafruit.com/products/198

 

Connect:

• DAT (SDA) to I2C Data (SD)
• CLK (SCL) to I2C Clock (SC)
• 5V to 5V
• GND to GND

 

NOTE: I did not create the libraries used - I just got everything working on my NP2 - credit and thanks go to those who created the libraries and put in all the hard work making original code for these devices.

Attached Files



#17 ShVerni

ShVerni

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationNew York, New York

Posted 24 February 2013 - 03:39 PM

I also moved some of the text management methods out of this class into a separate display class.

 

Thanks for the great project! I was wondering if you would be able to post you display class so I can see how you implement the LCD sreen? Thanks again!



#18 AxelG

AxelG

    Advanced Member

  • Members
  • PipPipPip
  • 52 posts

Posted 25 February 2013 - 03:54 AM

Thanks for the great project! I was wondering if you would be able to post you display class so I can see how you implement the LCD sreen? Thanks again!

Sorry it has taken some time to respond, I have been working on another project and just now getting back here.  I moved the MakeTextBlock() method into a display class that is quite specific to my project and may not make much sense posted as a helper-class.

 

Essentially this method uses a four element array of strings, one fore each line of the display.  This way it is easy to handle wrapping text on the screen; pass in a large string and the string gets broken into four smaller strings broken at the space character (and optionally centered on each line)

 

I can look at breaking out a more generic class if you are still interested; but it really isn't anything that special...



#19 ShVerni

ShVerni

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationNew York, New York

Posted 25 February 2013 - 04:08 AM

Sorry it has taken some time to respond, I have been working on another project and just now getting back here.  I moved the MakeTextBlock() method into a display class that is quite specific to my project and may not make much sense posted as a helper-class.

 

Essentially this method uses a four element array of strings, one fore each line of the display.  This way it is easy to handle wrapping text on the screen; pass in a large string and the string gets broken into four smaller strings broken at the space character (and optionally centered on each line)

 

I can look at breaking out a more generic class if you are still interested; but it really isn't anything that special...

 

Thanks for getting back to me, I hope your other project is going well! I see what you're saying about a very specific application, and I'll have to get a little further with my own project before I'm sure of what I need. However, that MakTextBlock() method sounds almost exactly like what I'll be using. I'm planning to be able to essentially scroll the screen vertically for large blocks of text which would take more than four lines. I'd break the text up into an array, and then load four elements of the array at a time. Pushing a button would shift the array up or down, so the screen could "scroll" up or down along each line.

 

If you think that's too different from what you have, then don't worry about it, but if you already have something that you think could be fairly easily adapted, then I'd love to see it.



#20 AxelG

AxelG

    Advanced Member

  • Members
  • PipPipPip
  • 52 posts

Posted 27 February 2013 - 02:28 AM

I looked at my code again, and it is not what you are looking to do, but here are the display methods so you can see what I did:

 

        /// <summary>        /// Writes a string array to the device.  Truncates strings to the number of columns allowed,         ///  or wraps if enough room.        /// </summary>        /// <param name="data">string array with each element mapped to a line on the LCD.  Will         ///   truncate if too many are sent</param>        /// <param name="format">C = center each line</param>        private void write(string[] data, char format = ' ')        {            int NumRows = data.Length;            if (NumRows > _rows) NumRows = _rows;            //base.clear();            //truncate the strings, and center if needed            lock (LCDLock)            {                for (int i = 0; i < NumRows; i++)                {                    if (data[i].Length > _cols) data[i] = data[i].Substring(0, _cols);                    if(data[i].Length != 0) write(data[i], 0, (byte)i, format);                }            }        }        /// <summary>        /// Takes in a string, and breaks it up into four strings suitable for the display        /// </summary>        /// <param name="value">String to be converted</param>        /// <returns>string array containing one element for each row on the display</returns>        public string[] MakeTextBlock(string value)        {            int LastSpace = 0;            string[] TextBlock = new string[_rows];            for (int i = 0; i < _rows; i++) TextBlock[i] = "";            for (int pass = 0; pass < _rows; pass++)            {                int SegLen = _cols;                if (value.Length < LastSpace + _cols) SegLen = value.Length - LastSpace;                int ThisSpace = 0;                string part = value.Substring(LastSpace, SegLen);                ThisSpace = part.Length;                if (part.Length >= _cols)                {                    for (int i = 0; i < part.Length; i++) if (part[i] == ' ') ThisSpace = i;                }                TextBlock[pass] = part.Substring(0, ThisSpace);                LastSpace += ThisSpace + 1;                if (LastSpace >= value.Length) break;            }            return TextBlock;        }        /// <summary>        /// Write the string at a specific location, with a specific formatting        /// </summary>        /// <param name="value">Value to display</param>        /// <param name="col">Starting column </param>        /// <param name="row">Row to display</param>        /// <param name="format">C = Centered on the row</param>        private void write(string value, byte col, byte row, char format = 'c')        {            string NewString = "";            if ((format == 'c' || format == 'C') && value.Length < _cols && col == 0)            {                for (int space = 0; space < (_cols - value.Length) / 2; space++) NewString += " ";                NewString = NewString + value;            }            else NewString = value;            lock (LCDLock)            {                base.setCursor(col, row);                write(NewString);                //if (row == TEMPROW && NewString.Trim().Length != 0) LastTempLine = DateTime.Now;            }        }        /// <summary>        /// Send string to the current cursor position        /// If the string is longer than number of columns, the string will be broken        ///   up into parts and written across multiple lines.        /// </summary>        /// <param name="value">string value to display</param>        private void write(string value)        {            if (value.Length > _cols)            {                write(MakeTextBlock(value), 'c');            }            else            {                byte[] Buffer = Tools.Chars2Bytes(value.ToCharArray());                lock (LCDLock)                    base.write(Buffer);            }        }






Also tagged with one or more of these keywords: LCD I2C, HD4478, LCD, I2C

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.