Memory (Part 6)

Part 1234567

Previous posts on the same subject showed that it is possible to significantly increase the amount of memory for a moderate price and little hardware and software works. This post is now about the use we can do of this extra memory. The idea here is to record and then display large amounts of text on the screen of a basic 2×16 LCD unit.

letters

The use of LCD units has already been discussed here. The proposed application consists in displaying a list of pre-recorded messages. This is a two steps process: within the first phase, messages are recorded in the  EEPROM chip. In the second phase, an other application picks up the messages from the external EEPROM chip according to its need. In this way, not the least byte of Arduino’ memory is involved in message storage.

One thing to be aware of before coding is the charset of the LCD screen. Usual characters (e, 4, !, …) are encoded the same way as they are in the ASCII table but some other characters are not.

Warning : LCD units differ one from the other by means of their font maps (so as to say their tables of characters). While almost all LCD units share the ASCII codes from 32 (space) to 125 (right closing brace) , the content of other map locations may differ from unit to the other. The following tables are extracted from the datasheet of two different units from the same maker and they illustrate the changes in the font maps.  :

comparator fonts

Note: The 16 first records from the font maps are available for recording custom characters. PlainLCD contains an example of how to create and record such custom  characters.

Let’s learn how to combine the benefits from this extra memory and the use of a LCD unit with a funy application.

Have you ever heard about “Fortune” ? It runs on almost all UNIX-like (e.g. Linux, BSD, …) platforms. The purpose of Fortune is to display a random quote on request. The application is made of two separate sketches. The first sketch records quotes in the external EEPROM and the second skecth randomly picks up a quote and displays it on LCD screen.

The first sketch acts as per follow :

  • Initialize the communication with the EEPROM chip
  • Initialize the LCD display
  • Store quotes in the EEPROM chip. These quotes are split into 16 characters lines to fit the LCD screen.
  • Print the number of stored lines to give it to the second sketch

The second sketch runs the following  :

  • Initialize the communication with the EEPROM chip
  • Initialize the LCD display
  • Read 2 lines from the EEPROM chip into a buffer
  • Print this buffer onto the LCD screen
  • Go to the next line : the LCD unit will scroll vertically

No more explanations, time for coding !

Here is the sketch that stores quotes :

/*

	Example of use of PlainLCD, Liquid Crystal Display driver library
	Copyright (C) 2014 HL2 group 

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

/* Include libraries */
#include <PlainSPI.h>
#include <PlainSPI_EEPROM.h>

/* Create objects */
PlainSPI_EEPROM MEM;

uint16_t _lines = 0; /* the number of lines stored in the EEPROM */

void setup() 
{

	Serial.begin(115200);
	InitEEPROM();
}

void loop() 
{
	FillEEPROM();
	/* print the number of lines to set it in the other application */
	Serial.print(_lines);
	Serial.println();
	while(true){};
}

void InitEEPROM()
{
	/* Reset all CS pins from PORTB */
	DDRB |= 0x07; /* Set PB0:2 as output ports */
	PORTB |= 0x07; /* Set PB0:2 to high */
	MEM.InitializeSPI_EEPROM(&PORTB, PINB2, SPI_EEPROM_25LC320);
}

void FillEEPROM()
{
	WriteSentence("Lee Segall", "A man with one watch knows what time it is; a man with two watches is never quite sure. ");
	WriteSentence("Socrates", "The unexamined life is not worth living.");
	WriteSentence("Descartes", "I think therefore I am");
	WriteSentence("Nietzsche", "Without music, life would be a mistake.");
	WriteSentence("Plato", "Wise men speak because they have something to say; fools because they have to say something.");
	WriteSentence("Chinese Proverb", "when a wise man points at the moon, the fool only looks at the finger");
}

void WriteSentence(const char* author, const char* sentence)
{
	SplitAndWriteSentence(sentence);
	SplitAndWriteSentence(author);
	SplitAndWriteSentence("****************");
}

/* split a sentence in group of 16 characters and don't split words */
void SplitAndWriteSentence(const char* sentence)
{
	uint8_t lenght = strlen(sentence);
	uint16_t cursor = 0; /* count the characters */
	uint16_t charPerLine;
	uint16_t ignoredSpace = 0;
	while(cursor < lenght){
		if(sentence[cursor] == ' '){
			cursor += 1;
			lenght += 1;
			ignoredSpace += 1;
		}
		if(cursor + 16 <= lenght){
			charPerLine = 16;
			if(sentence[cursor + 16] != ' '){ /* if a word would be cut */
				uint16_t cursorCutter = cursor+16; /* count the characters */
				while((cursorCutter > cursor) && (sentence[cursorCutter] != ' ')){ /* find the start of the word */
					cursorCutter -= 1;
				}
				if(cursorCutter != cursor){ /* if the word is not too big, set the new number of char per line*/
					charPerLine = cursorCutter - cursor + 1;
				}
			}
			WriteLineToEEPROM(sentence + cursor, charPerLine); 
			cursor += charPerLine;
		} else {
			WriteLineToEEPROM(sentence + cursor, lenght - cursor - ignoredSpace);
			break; /* the last line */
		}
	}
}

void WriteLineToEEPROM(const char* line, uint8_t size)
{
	/* write the significant text to the EEPROM */
	for(uint8_t i = 0; i < size; i++){
		MEM.WriteUInt8(line[i], (_lines<<4)+i); ;
	}
	/* fill the end of the line with spaces */
	for(uint8_t i = size; i < 16; i++){
		MEM.WriteUInt8(' ', (_lines<<4)+i); ;
	}
	_lines++;
}

And here is the sketch which prints the EEPROM content  on request:

/*

	Example of use of PlainLCD, Liquid Crystal Display driver library
	Copyright (C) 2014 HL2 group 

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

/* Include libraries */
#include <PlainSPI.h>
#include <PlainSPI_EEPROM.h>
#include <PlainLCD.h> /* Load LCD library */

/* Create objects */
PlainLCD LCD; /* HD74800-compatible, 4-bit mode*/
PlainSPI_EEPROM MEM;

/* the number of lines stored in the EEPROM retrieved from the previous sketch */
uint16_t _lines = 38; 

void setup() 
{

	InitEEPROM();
	InitLCD();
}

void loop() 
{
	for(uint16_t i = 0; i < _lines-1; i++){
		for(uint8_t j = 0; j < 16; j++){
			LCD.InsertString(MEM.ReadUInt8(j + (i<<4)), j+1);
		}
		LCD.PrintBuffer(1); /* first line */
		for(uint8_t j = 0; j < 16; j++){
			LCD.InsertString(MEM.ReadUInt8(j + ((i+1)<<4)), j+1);
		}
		LCD.PrintBuffer(2); /* second line */
		delay(1500);
	}
}

void InitLCD()
{
	/* Initialize LCD: pins must belong to the same port 
	in the order data4, data5, data6, data7, data port, reg_select, chip_enable, port 
	connect the R/W pin to ground. About the screen size, if nothing is specified,
	a 2*16 characters display is assumed */
	LCD.InitializeLCD(2, 3, 4, 5, 6, 7, &PORTD);
	LCD.ClearDisplay();
}

void InitEEPROM()
{
	/* Reset all CS pins from PORTB */
	DDRB |= 0x07; /* Set PB0:2 as output ports */
	PORTB |= 0x07; /* Set PB0:2 to high */
	MEM.InitializeSPI_EEPROM(&PORTB, PINB2, SPI_EEPROM_25LC320);
}

So far, we recorded only 38 lines. Depending on the EEPROM chip that you use you could store from 64 up to 8192 lines of 16 characters :

EEPROM_LCD_lines

Well… but there is in fact a limitation ! Before the lines are recorded in the EEPROM chip, their content must be set in the code which is responsible for writing the data in the EEPROM. The size of the data is limited by the size of the flash memory from the Arduino microprocessor (32kB for Arduino UNO). We will address this limitation in a later post.

 

Next post on same subject

Leave a Reply

You must be logged in to post a comment.