creating a frame buffer for 1604 and 2004 hd44780 lcd

I know that some one some where may have come up with this before but every search for it has come up blank (at least doesn’t meet my needs).

I am fairly new to C/Arduino but have written programs, scripts, and batch files in other languages.
Main proplems I am having is with assignments.

Bottoms Up Frame Buffer for X*4 HD44780 LCD displays (X = 16 or 20).
Characters are added to bottom line and lines move up
HD44780s use interlaced memory for the internal frame buffer (1-3-2-4 pattern instead of 1-2-3-4).
Original program was LCD HelloWorld Example.
Opening comments are left off.
Problem areas and personal notes included.
Arduino IDE 1.5 used to maintain compatibility with another project that can’t compile above 1.5.5.

// include the library code: #include

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// global setup
const byte lcdWidth = 20;
const byte lcdHeight = 4;
char frameBuffer[lcdHeight][lcdWidth];
byte lcdPosition = 0;

//note: I was also wanting to add
//“const char blank[lcdWidth] PROGMEM= repeated set of 32 (space) lcdWidth times”
//blank would have been created as an array of 16 or 20 spaces
//#include <avr/pgmspace.h> would have been added
//but couldn’t find any references, so I coded the following function:
//all references to blank(row) were to be: frameBuffer[row] = blank;

void blank(byte row){
//clears a frameBuffer row by writing spaces (32)
for (byte col = 0; col < lcdWidth; col++){
frameBuffer[row][col] = 32;
}
}

void render(){
//sends frameBuffer array to lcd
//in proper order to fill LCDs display ram
//probably could have used lcd.setCursor/lcd.print(frameBuffer[row]) combos
//but after assignment failures thought this best
//point to starting position (top left corner) and start fill
lcd.setCursor(0,0);
breakDown(0);
breakDown(2);
breakDown(1);
breakDown(3);
}

void breakDown(byte row){
//send a frameBuffer row to LCD screen one byte at a time
for (byte col =0; col < lcdWidth; col++){
lcd.print(frameBuffer[row][col]);
}
}

void moveUP(){
//moves all lines up by 1
//original line 0 is lost
//line 3 is cleared
//lcdPosition is reset
//*********************************************
//code not working due to following assignments
//*********************************************
frameBuffer[0] = frameBuffer[1];
frameBuffer[1] = frameBuffer[2];
frameBuffer[2] = frameBuffer[3];
blank(3);
//reset position
lcdPosition = 0;
}

void lcdChar(byte c){
//lf/cr/overflow control as well as character addition to frameBuffer[3][lcdPosition]
//other controls could be added (bell/tab/backspace?) but for now basic controls.
//line advance/overflow?
if (lcdPosition = lcdWidth || c == 10 || c == 13){
moveUP();
}
//add character?
if (c != 10 && c != 13){
frameBuffer[3][lcdPosition] = c;
lcdPosition++;
}
render();
}

void setup() {
for (byte index =0; index < lcdHeight; index++){
blank(index);
}
//*********************************************
//code not working due to following assignments
//*********************************************
//message is a line count, hello world, and filler based on 20*4 LCD for now
frameBuffer[0] = ‘1 hello, world!!!’;
frameBuffer[1] = ‘2 hello, world!!!’;
frameBuffer[2] = ‘3 hello, world!!!’;
frameBuffer[3] = ‘4 hello, world!!!’;

// set up the LCD’s number of columns and rows:
lcd.begin(lcdWidth, lcdHeight);
// Print a frame buffer to the LCD.
render();
delay(3000);
}

void loop() {
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(0, 3);
// print the number of seconds since reset:
lcd.print(millis()/1000);
}

compile errors:

myHelloWorld.ino: In function ‘void moveUP()’:
myHelloWorld:97: error: invalid array assignment
myHelloWorld:98: error: invalid array assignment
myHelloWorld:99: error: invalid array assignment
myHelloWorld.ino: In function ‘void setup()’:
myHelloWorld:128: error: incompatible types in assignment of ‘int’ to ‘char [20]’
myHelloWorld:129: error: incompatible types in assignment of ‘int’ to ‘char [20]’
myHelloWorld:130: error: incompatible types in assignment of ‘int’ to ‘char [20]’
myHelloWorld:131: error: incompatible types in assignment of ‘int’ to ‘char [20]’

As you can see the areas I am having problems with are loading the frameBuffer rows with a array of characters and having the various lines moved around. Since I am sending characters 1 byte at a time and have control of the frameBuffer size and position (lcdWidth, lcdPosition) I don’t need the extra nulls. I am trying to save as much space as possible so am also using the minimum variable sizes (everything should work with char and byte, maximum character value is 255, maximum frameBuffer length is 80 total not including pointers).

When I comment out the assignment sections in question, the sketch compiles but of course does nothing but count milliseconds (loop() function).

In BASIC (various flavors), vbscript, javascript, and a few other languages with loose and strict variable assignments I don’t have this problem. I can’t find any way to fix this as all web searches keep showing solutions for Strings/String Functions which use, need, or otherwise require nulls.

Any help is greatly appreciated.

While writing the above I was still doing research and found the memcpy function.
format: memcpy(target, source, element count);

this allowed me to fix moveUP() as such:

void moveUP(){
//moves all lines up by 1
//original line 0 is lost
//line 3 is cleared
//lcdPosition is reset
memcpy(frameBuffer[1], frameBuffer[0], lcdWidth);
memcpy(frameBuffer[2], frameBuffer[1], lcdWidth);
memcpy(frameBuffer[3], frameBuffer[2], lcdWidth);
blank(3);
//reset position
lcdPosition = 0;
}

Which only leaves the errors in setup().

Well, I finally fixed all the errors. Interesting to find that that little memcpy fixed almost all of the syntax/compile errors. Found and fixed a few logic errors (= instead of ==, output order was reversed). I also combined 2 routines into 1 (added a boolean switch to select the needed function): blank and breakDown became byteChar (true causes a selected framebuffer row to print, false clears it).

Finished code (all text):

/*
LiquidCrystal Library - Hello World

Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there, and you
can usually tell them by the 16-pin interface.

This sketch prints “Hello World!” to the LCD
and shows the time.

The circuit:

  • LCD RS pin to digital pin 12
  • LCD Enable pin to digital pin 11
  • LCD D4 pin to digital pin 5
  • LCD D5 pin to digital pin 4
  • LCD D6 pin to digital pin 3
  • LCD D7 pin to digital pin 2
  • LCD R/W pin to ground
  • 10K resistor:
  • ends to +5V and ground
  • wiper to LCD VO pin (pin 3)

Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe

This example code is in the public domain.

http://www.arduino.cc/en/Tutorial/LiquidCrystal

==========================================================================
CODE HAS BEEN MODIFIED TO WORK FOR 16x4 AND 20X4 DISPLAYs ONLY.
(At least tested on 1604 and 2004 displays)
ADDED CODE FOR FRAME BUFFER TO SUPPORT INTERLACED MEMORY ON HD44780 LCDs.
It uses a lttle more than 101 bytes of ram:
80 for frameBuffer
20 for initial message array (easily removeable)
1 for global lcdPosition
various pointers

*/

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// global setup
#define lcdWidth 20
#define lcdHeight 4
char frameBuffer[lcdHeight][lcdWidth];
byte lcdPosition = 0;
const char message[20] = {’ ', ’ ', ‘h’, ‘e’, ‘l’, ‘l’, ‘o’, ’ ', ‘w’, ‘o’, ‘r’, ‘l’, ‘d’, ‘!’, ‘!’, ‘!’, ‘!’, ‘!’, ‘!’, ‘!’};

void byteChar(byte row, boolean sendClear){
//sendClear = true: send a frameBuffer row to LCD
//sendClear = false: clears a frameBuffer row by writing spaces (32)
for (byte col = 0; col < lcdWidth; col++){
if (sendClear){
lcd.print(frameBuffer[row][col]);
} else {
frameBuffer[row][col] = 32;
}
}
}

void render(){
//sends frameBuffer array to lcd in proper order to fill LCDs display ram
//point to starting position (top left corner) and start fill
lcd.setCursor(0,0);
byteChar(0, true);
byteChar(2, true);
byteChar(1, true);
byteChar(3, true);
}

void moveUP(){
//moves all lines up by 1, original line 0 is lost, line 3 is cleared
//lcdPosition is reset
memcpy(frameBuffer[0], frameBuffer[1], lcdWidth);
memcpy(frameBuffer[1], frameBuffer[2], lcdWidth);
memcpy(frameBuffer[2], frameBuffer[3], lcdWidth);
byteChar(3, false);
//reset position
lcdPosition = 0;
}

void lcdChar(byte c){
//lf/cr/overflow control as well as character addition to frameBuffer[3][lcdPosition]
//other controls could be added (bell/tab/backspace?) but for now basic controls.
//line advance/overflow?
if (lcdPosition == lcdWidth || c == 10 || c == 13){
moveUP();
}
//add character?
if (c != 10 && c != 13){
frameBuffer[3][lcdPosition] = c;
lcdPosition++;
}
render();
}

void setup() {
//initialize frameBuffer
for (byte row =0; row < lcdHeight; row++){
byteChar(row, false);
}
//memory copy message to frameBuffer then add line numbers
//only left in to test frameBuffer output order
for (byte row = 0; row < lcdHeight; row++){
memcpy(frameBuffer[row], message, lcdWidth);
frameBuffer[row][0] = row + ‘0’;
}

// set up the LCD’s number of columns and rows:
lcd.begin(lcdWidth, lcdHeight);

//print the 4 numbered messages to lcd
render();
delay(3000);
}

void loop() {
//send a list of all printable characters to lcd
//no custom characters generated or printed.
for (byte character = 32; character <= 255; character++){
lcdChar(character);
}
}

Right, first things first.

Go and read the instructions, then go back and modify your post (use the "More --> Modify" option to the bottom right of the post) to mark up the code as such so we can review it conveniently and accurately.

CODE HAS BEEN MODIFIED TO WORK FOR 16x4 AND 20X4 DISPLAYs ONLY.
(At least tested on 1604 and 2004 displays)

I haven't read all of your code yet, and won't attempt to do so until it is properly formatted but the above part stuck out.

It should be relatively easy to modify code written for a 20x4 to work on the more common 16x2 displays. In your case, since you are adding characters from the bottom and pushing them up you will certainly have to start pushing from a different LCD address.

On the other hand 16x4 displays will require more work since their 3rd and 4th rows have different starting addresses than the 20x4 displays. By the way, the Arduino LiquidCrystal library does not account for this and does not work properly with 16x4 displays.

Things will really be interesting if you modify your code for a 16x2 and then try it with most 16x1 displays.

Don