Friday, January 29, 2010

Adding a small HD44780 LCD display to my PC

I've always felt the need to have a small screen on my computer to show some "extra" information which I don't usually want on my screen all the time. By extra information I mean - news headlines, RSS feeds from my favorite blogs, weather updates, CPU usage information, new e-mail notifications, etc. So, to fulfill this humble need of mine, I bought a small (16x2 character) LCD screen for Rs.90 (approximately USD $2). Very inexpensive!:

(I've received an e-mail from TK Boyd! He's the man who inspired me to connect all sorts of things to my computer!)

This LCD is based on the popular Hitachi HD44780 controller. You can find lots of information about how to communicate with this LCD on the Internet. I connected it to my computer's parallel port and fixed it to my computer's case:

I created this panel by drilling holes into the case, and adding two switches (for the LCD and backlight), and a pot for contrast adjustment. The LCD is powered by 5V from the SMPS.

Here is the pinout for the LCD:

DB0-DB7 is the data bus.

'E' is the enable line. This is used to indicate the start of a transmission of a data byte to the LCD controller. When we start a transmission, this line is brought high. When transmission is complete, this line is brought low.

'RS' is the register select line. This line indicates to the LCD controller whether the data byte is to be treated as a command or as text data to be displayed on the screen. If it is high, the data sent to the LCD displayed on the screen. If it is low, the data is treated as a command.

'R/W' is the read/write line. If it is low, information can be written to the LCD controller. If it is high, data can be read from the LCD. I've kept it permanently low in my circuit.

Here's a screenshot from the HD44780's datasheet (click on the image to enlarge it):

So, this is how a typical command would be executed:
  1. Make 'RS' and 'R/W' low ('R/W' is always low in my circuit).
  2. Set 'E' high to indicate the start of the command.
  3. Make DB7-DB0 equal to binary "00000001" (decimal: 1). This is the clear display command.
  4. Make 'E' low again to execute the command (which in this case would clear the display).
In code, this would look like:

I'm using Inpout32 to access my parallel port. Another point I should mention here is that the 'E' pin on my LCD is connected to C0 on my parallel port and 'RS' is connected to C2. A weird thing I noticed about C0 is that you have to send "1" to it to make it low (and vice versa). This is why I'm sending "1" in the last line of the the code. I have no idea why this is happening. Have any clues?

To write characters on the LCD, you just have to send the ASCII code of the character to the LCD:

Creating custom characters:

Most characters on the LCD (I think there are a total of 248) are stored in what's called a CGROM. This is an acronym for "Character Generator Read Only Memory". Characters inside this memory location are pre-defined, and cannot be changed. So how do we create our own characters if we can't change anything here? Well, there's a 64-byte hunk of RAM called CGRAM in the LCD, and it is write-able! Characters on an LCD can be up to 8 pixels high, and 5 pixels wide. Each row consumes 1 byte of memory. Since there are 8 rows, one character takes up 8 bytes. So, a total of 8 custom characters can be defined in 64 bytes. Here's the pixel map of a bell pattern I created:

I have written the decimal and hex values for each row. To store this custom character in the CGRAM, I would have to go to the CGRAM address (see the "Set CGRAM address" command in the datasheet) and write these values. The address of the first byte of the first character is 64 (hex: 0x40). How in the world did I get that value? Well if you look at the command for setting the CGRAM address in the datasheet, it says we have to set DB6 to "1". The rest of the bits, DB0-DB5, determine the CGRAM address. If we were to set DB0 to DB5 "0", we would be able to set the first first byte of the first character. The complete command would be binary "1000000" (DB6-DB0). This is equal to 64 in decimal. Similarly, the address of the first bye of the second character would be 64 + 8 = 72 (remember each character consumes 8 bytes).

Here's a code sample to draw this bell character on the LCD:

I need to do some explaining here! Well, first we define the bytes for the bell character in an array. Then we set the CGRAM address to 64 (first byte of the first custom character). Then we write the values in the array to the CGRAM in the for-loop. Notice that we don't have to set the CGRAM address every time we have to go to a different byte (65,66,67 etc). The LCD controller auto increments the CGRAM address everytime we write a byte. To display this newly created character, we first have to switch back from the CGRAM to the display area. We do this by setting the DDRAM address to 128 (first character of the first line). 129 would be the second character, and so on. The second line starts at DDRAM address 192. We display the custom character by displaying ASCII code "0". ASCII codes 0 through 7 are for custom characters. They normally serve as control codes for marking the beginning of a serial transmission, but since these have no meaning to an LCD module, the designers reserved them for CGRAM characters. It took me a long time to figure that out!

I find it convenient to create custom characters on a sheet of graph paper before I begin coding:

A note on creating animations:

You can create animation by rapidly printing custom characters. If you have a custom character on your LCD, and you modify it's bytes by going to the CGRAM, the character will change. In fact, all occurrences of that character will change. Here's another cool thing. You can create more frames for your animation than the eight character limit. This is because you can load new bit patterns from your computer without having to store it in the LCD's memory. Neat!

This post is getting long, so I'll end my discourse here. :) My LCD project is a WIP right now. I'm still adding more features. Anyway, I hope you find this post helpful in your projects. Have fun!


How to control a HD44780-based Character-LCD

Defining Custom Characters

Creating custom characters tutorial

CodeProject article on controlling LCDs using C#



Prabhu said...

Long time since your last post! Anyways, just wanted to let you know that I am a regular follower and I really appreciate the work you do and blog about, very inspiring indeed (: Will surely try some of these things soon..


Anonymous said...

this is great
Ive been following your posts for a while so its been a while since your last post

any way great work i would love to this type of thing with my pc

it would help if it displayed my temp and cpu stats

(cant login :( )

Ashish Derhgawen said...

Glad you enjoy the posts guys. Yeah, I have been silent for a long time, but now you'll be hearing from me much more often. :)

Keep visiting!


Marcio Andrey Oliveira said...


I am a regular follower of this blog.

I really liked this project. Simple, creative and nice.

Keep posting.


venkata said...

dude i hv never seen a person working on electronics so much
i must say i m so lucky 2 knw abt ur blog(as a matter of fact i discovered it by mistake)
nw dat i came 2 knw abt ur blog i m goin 2 b a regular reader
i m so shokd when i found some of ur projects were wat i really wantd 2 do n i hv been tryin 2 do bt cudnt do, n u u succeeded in doin them
any way hats off 2 u

Anupam Kapoor said...

some of the control pins on the parallel port are inverted for-example: strobe (C0, pin-1), auto-feed (C1, pin-14), select (C3, pin-17).

that's the reason why you see the behavior above. as to the reason _why_ some pins are inverted, i don't really know.

Ashish Derhgawen said...

@Anupam: Thanks for the information, Anupam. :)

John said...

Hey man really ur blog is very nice and it providing good information to user.

Alpha male said...

very simply explained. It is indeed an art to read & stop new visitors with your attractive writing style. I am really impress from your posted information. Thanks for sharing.

ravispeaks said...

Happened to check your blog after a long time.Happy to find new posts:)
What's cooking next :P

buoy said...
This comment has been removed by the author.
buoy said...
This comment has been removed by the author.
buoy said...

dude u sed d second line address starts frm 192 nw my q. is if there is a second line can v create another 8 custom chars i.e 16 in all? (guess not) but can u plz xplain? bcuz i can see dat d eye dat u created uses 16 rows in all and 5 cols each...and d LCD is 2x16(2 rows and 16 cols) so hw come u created 2 eye's ? using only 8 custom chars? as 8 custom chars r xhausted while creating d 1st eye.

P.S : sry fr d long post m clearly missing out sumthing here. plz try n clear the ambiguity of my mind :P

Ashish Derhgawen said...

@buoy: The custom characters don't get erased from the memory after you use them. You can re-use them. :)

buoy said...

@ ashish : thx a ton!

The abs diet said...

One more comment for appreciation

build your own solar panel said...

thanks for this content ..bookmarked

buoy said...

dude the prob dat i am facing ryt nw is that the LCD lights up and shows a test screen like the one shown on the codeproject site after the soldering is shows all black chars in the first row...but after dat nuthing happens...

nw wen i tried this b4 it worked i mean the lcd could be cleared and i cud type on the lcd using the demo app provided on codeproject but after i made d solderings on a PCB the prob persists it only shows the test screen and nuthing happens after dat! cud u plz help m figure out wat might b going wrong!?

Thx a ton in advance!

sam said...

I actually enjoyed reading through this posting.Many thanks.


Magento Themes said...

nice job! keep it up

- John Devis
Magento Themes