I've been playing with a 16 char, 2 line 1602A LCD display using the HD44780 module written by Gordon (3 years ago!). While the module provides basic functionality to write characters to the display, there is a lot of interesting functionality in the HD44780 driver chip that isn't implemented, so I started enhancing the module to add some of this functionality.
Also, found a bug... the HD44780 has a busy condition while its working on an instruction, and the busy flag needs to be checked before committing another operation. You can best see this manifest by writing to the display, then clearing and immediately writing again... the second "print" call will almost always screw up and you'll get junk in the display (at least for the 1602A display I'm playing with). I added code to fix this, requiring the ability to read from the HD44780, something not implemented in the current module.
This is a Work In Progress, so the additional functionality in the attached module is not fully baked; for instance, all of it is implemented only for the 4-bit parallel interface. I haven't addressed the I2C connection option. Still, if you want to play around with it, here it is, attached.
lcd = require("HD44780").connect(rs,en,d4,d5,d6,d7[,rw]); //this is being pulled locally from my modules folder in the project directory
The params in the connect statement above correspond directly the the pin names on the HD44780 chip. What's new here is the addition of the R/!W pin at the end. In wiring the display up, this needs to be connected to an actual pin on the MCU rather than simply grounded, as read operations are now supported. It's at the end so it can be left out, and the driver will be backwards compatible with code written for the older version (with the bug fix, though).
While the display is 16 characters wide, the internal buffer in the 44780 is actually 40 chars per line. The display is actually a 16 character "window" on the underlying 40 character buffer. You can send a 'string' parameter up to length 40, and it will be written into the display buffer (called DDRAM), with the first 16 characters displayed. There are 44780 instructions to shift the display window left or right in DDRAM to display different parts of it.
The optional 'scroll' parameter above if 'true' will cause the display to scroll left automatically when a string longer than 16 characters is written, until the last character is displayed. It then pauses for a bit, resets back to the beginning of the string, and repeats.
The interval between single character shifts is by default 150ms (looked pretty readable to me), but can be set as desired with the 'sdly' property, like this:
The code above takes effect immediately (if scrolling), and would move twice as fast as the default.
This will shift the display window left or right by 'count' character positions. Direction is specified by the sign of 'count'.
buf = lcd.read(addr,len,cg)
Reads from either DDRAM (display buffer) or CGRAM (character generator RAM), and returns a Uint8Array of length 'len'. 'addr' is the starting address in the selected RAM buffer to read from. For DDRAM, the first line starts at addr 0, second line at 0x40. CGRAM is a completely different animal that I won't address in this post. Example:
lcd.print("this is a test of the 2 line display");
buf = lcd.read(8,16,false);
After this code, the display would show "this is a test o", and buf would contain [97, 32, 116, 101, 115, 116, 32, 111, 102, 32, 116, 104, 101, 32, 50, 32] ("a test of the 2 ").
Waits for the busy flag to clear after an operation is executed. This is really only useful if you are doing low-level things by directly writing to the control register. Used internally in the module, you probably don't ever need this because it's already accounted for in read/write operations (this was the bug fix).
Finally, all the pins are now stored as properties on the instance, with the names shown above in the connect call. So, for example, the Register Select pin can be accessed as obj.rs; the set of 4 data pins can be access as an array with obj.data. This makes it easy to program directly at a low level while still using the high-level functions (like 'print') on an HD44780 instance.