Random Quote Board

Setting Up the HM2004A Liquid Crystal Display

Gary Schafer, June 2024

A bit of a change-up from my usual lineup. This will not be a post discussing either software defined radios (SDR) or their accompanying software. I'm working on a post on capturing Russian Meteor M2 satellite signals, but that's still a work-in-progress. Instead, this post will hark back almost ten years to some posts I did in November 2014 talking about the LCM1602C LCD.

This post will cover a slightly different display, the HM2004A. This LCD is a 4-line x 20-character display. The LCM1602C is a 2-line x 16 character display. There is an important difference between the two. Further, there's a "Duh!" moment that I want to share.

The HM2004A LCD

The HM2004A liquid crystal display (LCD) is sold by Adafruit as well as several other entities. I bought these several years ago and have been playing around with them (a bit), or letting them sit on the shelf (a lot) while I played around with my SDRs instead. They're a 4 line, 20 character display using white text on a blue background. They're actually very nice LCD displays.

Top view of the HM2004A liquid crystal display
Bottom view of the HM2004A liquid crystal display

Again, this is 4-line x 20-character display, as shown below.

This is the display active, but with the contrast set so that all characters are visible. This is not how the contrast would be set if the LCD was actually being used.

Setting the Display Address

The text is displayed based on the display data random access memory (DD RAM). This is an 8-bit hex value, with the MSB set to "1", that sets where the characters will be placed on the display. Again, this display has 4 lines with 20 characters per line. The DD RAM sets whether you will start your text on the first line at the left-most character, or the third line near the center, or wherever. I cannot find a decent document covering how to set the address for the HM2004A. The closest I found was for the TC2004A LCD as offered by Adafruit.com.

DD RAM addressing map for the TC2004A-01 LCD. Note how the addresses for the first and third lines are the same. This doesn't work for the HM2004A LCD.

The HM2004A doesn't have the map addressing as shown above. Note how the first and third line addresses are the same, as are the second and fourth line addresses. I decided to use "trial and error". Based on that, I've been able to figure out the addresses, which are as follows:

Display addressing for HM2004A LCD. This was based on trial-and-error of the possible address space. Note how the first line continues onto the third, while the second line continues onto the fourth.

A couple of points. The first line continues onto the third, while the second line continues onto the fourth. The first address starts with "0x80" (hex value of 80, or a decimal value of 128). That's because, in order to set the address, this highest-order bit (DB7) must be set to "1". The values of the lowest order bits (DB3 - DB0) then go from 0000 to 1111 (0x0 to 0xF). Then the highest order bits are set to 0x9, and the lowest order bits are again run from 0x0 to 0xF. When the number increments from 0x93 to 0x94, it jumps from the end of line one down to the beginning of line 3. From there, the next increment of the higher order bits is 0xA. But the lower order bits only go up to 0x7.

Going back to the second line, the higher order bits don't start at 0xB. Rather, they start at 0xC. For lines 2 and 4, the higher order bits go from 0xC to 0xE. And, again, the lower order bits for the last value (0xE) stop at 0x7.

Which leads to the issue of "How does the system know where to place the characters if there is overflow from one line to the next?" That's a problem. It doesn't. If you write to this LCD and the characters go past the end of, say, the first line, the overflow will go down to the third line. And once they get to the end of the third line, the overflow jumps back to the beginning of the second line.

This is a demonstration of starting of writing characters at the top, left corner (address 0x80), then continuing to write over 2 1/2 lines. The overflow of the first line drops down to the beginning of the third line. And the overflow of the third line jumps back up to the second line.

So, yeah. If you're using this LCD, your software has to account for that. I imagine that the Arduino (which is what I'm using to drive the LCD here) has a library that accounts for that. I don't know. I'm not using the library because I want to ensure I understand how the LCD works before I use any "automagic" software library. That's why my Arduino programming is still at the stage of setting each line separately. (Yes, the file is quite long with a lot of "digitalWrite" statements.)

I'm aiming to use this experience to create my own functions that will do two things. First, I want to be able to continuously write text but properly go consecutively from lines 1 - 4, and not 1 to 3, 3 to 2, then 2 to 4. Second, I want to be able to do automatically word wrapping so that a word won't be split between two lines.

Setting for Four Lines

There's also the problem of the setting the function of the display for four lines. It doesn't exist. You only have the options of one or two lines. If you perform a "function set" with DB3 set to "0" (LOW), this will set the display to just one line. This will not allow you to use lines 2 or 4. You will only be able to access lines 1 and 3. If you set DB3 to "1" (HIGH), this will give you access to all of the lines, though only in the manner prescribed above.

Back to Basics: Pull-Down Resistors

When I was an engineering undergraduate, I had to build a digital circuit. The class TA told us students, quite emphatically, to make sure that any circuits that were set between HIGH and LOW (1 and 0, respectively) needed to have either pull-up or pull-down resistors connected. A pull-up resistor is a relatively high impedance (1 kΩ or higher) resistor connected between that point and the power supply voltage (say, +9 VDC). A pull-down resistor is the same, but connected to ground.

Well, me being the idiot that I can be sometimes, I didn't add these. They didn't seem to be necessary, and the circuit seemed to be working just fine without them.

Until I had to go to the TA's office to show that my circuit was working.

Yes, at that point, it didn't work as it did before. Why? Well, without those pull-up / pull-down paths, the currents could be affected by stray currents pretty much from anywhere. The TA knew what the problem was immediately. And I'll never forget the look he gave me. The best I can describe it is as "intense annoyance".

You'd think I would have learned.

This afternoon, as I was working on determining the address mapping of the HM2004A, I discovered that the system wasn't acting consistently. Sometimes the exact, same program loaded three different times would produce three, different results. That's when the face of my old TA came back to haunt remind me, "Put in some pull-down resistors." Soooo, I added 10 kΩ resistors between the RS, RW, EN, and DB7 to DB0 connections and ground. This ensured that, when the values for those various connections were set to LOW (0), if there was any stray voltage, it would be safely shunted to ground. Further, when set to HIGH (1), the resistors would have enough resistance that the current from the supply voltage to ground would be low (500 uA each, in this case). As soon as I installed those resistors, lo and behold, the circuit started working properly and consistently.

My TA would be proud. Or maybe intensely annoyed again. Let's go with both.

Here's a Random Fact...