|
Connecting a HD44780 compatible LCD Display
to a Rabbit RCM3720
by Frank4DD, @2008 |
|
|
I recently upgraded from a BL1800 Jackrabbit system. To get networked I got
the Rabbit Ethernet Connection kit, featuring the newer and more powerful
RCM3720 Core. The old BL1800
(picture) had been running with a serialized 2x16 LCD display
BPI-216
from Parallax, which needed only 3 wires in total. When I could not find
one quickly for the RCM3720, I looked at the many parallel port LCD displays.
Well, I had ports enough on the RCM3720, so I got a HD44780-compatible type.
It turned out that the parallel-port LCD was much more difficult to connect and to program, googling around didn't help much. When I worked out the correct wiring and programming, I thought I share the knowledge to save you trouble and time... This guide has been developed with a RCM3720 RabbitCore Ethernet Development kit using the supplied development board and a Sunlike SC1602BSLB 2x16 LCD display. |
|
|
According to the specs, HD44780-compatible displays can be connected in 8-bit
and 4-bit mode. While the 4-bit mode cuts down on valuable in/out pins, it
complicates programming. So lets start with all 8 data lines connected and
see about the 4-bit shortcut later on. In addition to the 8 data lines, we
need 3 data control lines plus the obligatory 'ground' VDD and '+5V' VSS
lines with a contrast regulating V0 line. The V0 connection can simply be
'grounded' to VDD. This will set the display to full contrast and saves a
10kOhm potentiometer. I selected the free Port A for the 8 data lines. The
control lines I put on 3 of the remaining free pins of port B. Port B is
already partially used by the development board and the internal clocks.
|
|
|
With 14 lines to wire, the Sunlike SC1602BSLB display conveniently provides 2
rows of 7 soldering holes in a raster to fit a standard 14 pin wiring terminal.
We have only limited breadboard space on the Rabbit development board, so I
placed a female terminal close to the 40-pin RabbitCore breakout terminal while
soldering the male terminal to the display itself. I placed a second 'dummy'
terminal at the other end of the display using the holes for the external
backlight connections.
|
|
|
|
Once we power-up the development board, the LCD display should already
give us a sign of live by showing a row of black squares in the upper
line. Very good! This is normal for a uninitialized display. Let's
create a first test program in Dymanic 'C' named
Rabbit_RCM3720_to_HD44780LCD_example1.c
, simplified as much as possible. We are trying to display 'Hi'.
|
|
/***************************************************************************/
/* Rabbit_RCM3720_to_HD44780LCD_example1.c http://www.frank4dd.com/howto */
/* */
/* Written for a 16x2 HD44780 compatible LCD display on a Rabbit RCM3720 */
/* Ethernet Development Kit. Connected in 8bit mode with datalines going */
/* to port A while RS, E and RW are connected to Port B on B2, B3 and B4. */
/* Written and tested under Dynamic C Version 9.21 Frank4dd, @2008 */
/***************************************************************************/
#class auto
#define RSADDR 2 // Register Select port B-2
#define ENADDR 3 // Enable signal port B-3
#define RWADDR 4 // Read/Write signal port B-4
void MsDelay(unsigned long milliSeconds) {
unsigned long ul0;
ul0 = MS_TIMER; // get the current timer value
while(MS_TIMER < ul0 + milliSeconds);
}
LcdWrite(int mode, char hex) {
BitWrPortI(PBDR, &PBDRShadow, mode, RSADDR); // Choose command or data mode
BitWrPortI(PBDR, &PBDRShadow, 0, RWADDR); // Set LCD write mode
WrPortI(PADR, &PADRShadow, hex); // Set LCD command on port A
MsDelay(1);
BitWrPortI(PBDR, &PBDRShadow, 1, ENADDR); // Start sending data
MsDelay(1); // Wait 1 ms for LCD to receive
BitWrPortI(PBDR, &PBDRShadow, 0, ENADDR); // Finish transmission
MsDelay(1); // Wait 1 ms until next write
}
void main() {
brdInit(); // Enable development board
WrPortI(SPCR, NULL, 0x84); // Set Rabbit port A to output
WrPortI(PADR, &PADRShadow, 0x0); // Zero out all bits of port A
LcdWrite(0, 0x38); // Send cmd "8bit, 2 lines, 5x7 font"
LcdWrite(0, 0x06); // Send cmd "Entry mode, increment move"
LcdWrite(0, 0x10); // Send cmd "display and cursor shift"
LcdWrite(0, 0x0E); // Send cmd "display and cursor on"
LcdWrite(0, 0x01); // Send cmd "LCD clear, jump to zero"
LcdWrite(1, 0x48); // Send data 'H'
LcdWrite(1, 0x69); // Send data 'i'
} |
|
After running our test program, we should be greeted by our first two
characters.
Now we are ready to run a more advanced example program that adds many
helpful functions to control display and cursor settings:
Rabbit_RCM3720_to_HD44780LCD_example2.c
The program can map ASCII characters to the equivalent of the LCD
character map. The LCD character map has some extra symbols not
available in the standard ASCII table, like arrows and currency
symbols. HD47780 compabtible displays have a second character map
that provides additional characters for Japanese Katakana, Hiragana,
accent and formula characters.The test program allows addressing both display lines through a easy-to-use LcdWriteStr() function, as the picture to the left demonstrates. From here we return to the question on how to run with only 4 data lines instead of 8. |
|
|
In 4-bit mode, the data lines 0-3 are unused and we only need to connect data
lines 4-7. But laying off half of the data-transmitting workforce comes at
a price. Now we need to add code for splitting bytes into bits, plus
transmitting each of the "half-bytes" is twice as much work. After adjusting
example1
Rabbit_RCM3720_to_HD44780LCD_example3.c shows the extra overhead required.
The extra function LcdInit() is required to reliably initialize the 4bit mode.
|
/***************************************************************************/
/* Rabbit_RCM3720_to_HD44780LCD_example3.c http://www.frank4dd.com/howto */
/* */
/* Written for a 16x2 HD44780 compatible LCD display on a Rabbit RCM3720 */
/* Ethernet Development Kit. Connected in 4bit mode with datalines going */
/* to port A-4 to A-7 while RS, E and RW are connected to Port B on B2, B3 */
/* and B4. Written and tested under Dynamic C Version 9.21 Frank4dd, @2008 */
/***************************************************************************/
#class auto
#define RSADDR 2 // Register Select port B-2
#define ENADDR 3 // Enable signal port B-3
#define RWADDR 4 // Read/Write signal port B-4
void MsDelay(unsigned long milliSeconds) {
unsigned long ul0;
ul0 = MS_TIMER; // get the current timer value
while(MS_TIMER < ul0 + milliSeconds);
}
void ByteSplit(char byte, int bit[8]) {
int i, j;
j=0;
for(i=128; i>0; i=i/2) {
if ((byte & i) != 0) bit[j] = 1;
if ((byte & i) == 0) bit[j] = 0;
if (j == 7) break;
else j++;
}
}
LcdInit() {
BitWrPortI(PBDR, &PBDRShadow, 0, RSADDR); // Set command mode
BitWrPortI(PBDR, &PBDRShadow, 0, RWADDR); // Set LCD write mode
BitWrPortI(PADR, &PADRShadow, 1, 4); // Set port A-4
BitWrPortI(PADR, &PADRShadow, 1, 5); // Set port A-5
BitWrPortI(PADR, &PADRShadow, 0, 6); // Set port A-6
BitWrPortI(PADR, &PADRShadow, 0, 7); // Set port A-7
MsDelay(1);
BitWrPortI(PBDR, &PBDRShadow, 1, ENADDR); // Start sending data upper 4 bit
MsDelay(1); // Wait 1 ms for LCD to read
BitWrPortI(PBDR, &PBDRShadow, 0, ENADDR); // Finish transmission upper 4 bit
MsDelay(5);
BitWrPortI(PADR, &PADRShadow, 1, 4); // Set port A-4
BitWrPortI(PADR, &PADRShadow, 1, 5); // Set port A-5
BitWrPortI(PADR, &PADRShadow, 0, 6); // Set port A-6
BitWrPortI(PADR, &PADRShadow, 0, 7); // Set port A-7
MsDelay(1);
BitWrPortI(PBDR, &PBDRShadow, 1, ENADDR); // Start sending data upper 4 bit
MsDelay(1); // Wait 1 ms for LCD to read
BitWrPortI(PBDR, &PBDRShadow, 0, ENADDR); // Finish transmission upper 4 bit
MsDelay(1);
BitWrPortI(PADR, &PADRShadow, 1, 4); // Set port A-4
BitWrPortI(PADR, &PADRShadow, 1, 5); // Set port A-5
BitWrPortI(PADR, &PADRShadow, 0, 6); // Set port A-6
BitWrPortI(PADR, &PADRShadow, 0, 7); // Set port A-7
MsDelay(1);
BitWrPortI(PBDR, &PBDRShadow, 1, ENADDR); // Start sending data upper 4 bit
MsDelay(1); // Wait 1 ms for LCD to read
BitWrPortI(PBDR, &PBDRShadow, 0, ENADDR); // Finish transmission upper 4 bit
MsDelay(1);
BitWrPortI(PADR, &PADRShadow, 0, 4); // Set port A-4
BitWrPortI(PADR, &PADRShadow, 1, 5); // Set port A-5
BitWrPortI(PADR, &PADRShadow, 0, 6); // Set port A-6
BitWrPortI(PADR, &PADRShadow, 0, 7); // Set port A-7
MsDelay(1);
BitWrPortI(PBDR, &PBDRShadow, 1, ENADDR); // Start sending data upper 4 bit
MsDelay(1); // Wait 1 ms for LCD to read
BitWrPortI(PBDR, &PBDRShadow, 0, ENADDR); // Finish transmission upper 4 bit
MsDelay(1);
}
LcdWrite(int mode, char hex) {
int bits[8];
// First we split the byte into its bits, then we send the first an second half
ByteSplit(hex, bits);
BitWrPortI(PBDR, &PBDRShadow, mode, RSADDR); // Set command or data mode
BitWrPortI(PBDR, &PBDRShadow, 0, RWADDR); // Set LCD write mode
BitWrPortI(PADR, &PADRShadow, bits[3], 4); // Set port A-4
BitWrPortI(PADR, &PADRShadow, bits[2], 5); // Set port A-5
BitWrPortI(PADR, &PADRShadow, bits[1], 6); // Set port A-6
BitWrPortI(PADR, &PADRShadow, bits[0], 7); // Set port A-7
MsDelay(1);
BitWrPortI(PBDR, &PBDRShadow, 1, ENADDR); // Start sending data upper 4 bit
MsDelay(1); // Wait 1 ms for LCD to read
BitWrPortI(PBDR, &PBDRShadow, 0, ENADDR); // Finish transmission upper 4 bit
MsDelay(1);
BitWrPortI(PADR, &PADRShadow, bits[7], 4); // Set port A-4
BitWrPortI(PADR, &PADRShadow, bits[6], 5); // Set port A-5
BitWrPortI(PADR, &PADRShadow, bits[5], 6); // Set port A-6
BitWrPortI(PADR, &PADRShadow, bits[4], 7); // Set port A-7
MsDelay(1);
BitWrPortI(PBDR, &PBDRShadow, 1, ENADDR); // Start sending data lower 4 bit
MsDelay(1); // Wait 1 ms for LCD to read
BitWrPortI(PBDR, &PBDRShadow, 0, ENADDR); // Finish transmission lower 4 bit
MsDelay(1);
}
void main() {
brdInit(); // Enable development board
WrPortI(SPCR, NULL, 0x84); // Set Rabbit port A to output
WrPortI(PADR, &PADRShadow, 0x0); // Zero out all bits of port A
LcdInit();
LcdWrite(0, 0x28); // Send cmd 4bit, set 2 lines, 5x7 font
LcdWrite(0, 0x06); // Send cmd "Entry mode, increment move"
LcdWrite(0, 0x10); // Send cmd "display and cursor shift"
LcdWrite(0, 0x0E); // Send cmd "display and cursor on"
LcdWrite(0, 0x01); // Send cmd "LCD clear, jump to zero"
LcdWrite(1, 0x48); // Send data char 'H'
LcdWrite(1, 0x69); // Send data char 'i'
} |
|
To show another 4-bit example,
Rabbit_RCM3720_to_HD44780LCD_example4.c is our second program converted into 4-bit mode.
|
|
After being able to fully control the LCD, I converted the functions into
a library for easy re-use in all future programs. I created a file called
hd44780lcd.lib and I placed it in
C:\DCRABBIT_9.21\Lib\Displays. Then I added the library to Dynamic C's
library inclusion list in C:\DCRABBIT_9.21\Lib.dir.
To use the LCD functions provided in the library, add the line #use "hd44780lcd.lib" to programs. If necessary, adjust the library definitions to your ports and pins if they are different from mine (data lines port A, control lines on port B2, B3, and B4) and define the number of data lines used with #define INTERFACE 8 or #define INTERFACE 4 on top of your program. The library's default is set to 8bit. To test the library, I modified example4 to use it in Rabbit_RCM3720_to_HD44780LCD_example5.c The example program BROWSELED.C in C:\DCRABBIT_9.21\Samples\RCM3720\Tcpip provided by Rabbit Inc. is a additional modification to Rabbit_RCM3720_to_HD44780LCD_example6.c. After running it, connecting to the Rabbit through the network port, I can switch the evaluation board's LED on and off using a browser. The board's IP address and netmask is conveniently displayed and alternates with showing the LED state on the LCD display. |
|
|
Any comments, improvements and thanks are welcome to: public[at]frank4dd[dot]com,
there is much left to be perfected. Thanks to my wife and child for their patience.
Below is a selection of links and documents that helped me to extract the
information needed:
|