Saturday, June 27, 2009

Storing strings in EEPROM, byte by careful byte

By popular demand (meaning somebody asked :), in this article I will show how I store ASCII characters(1) in an I2C EEPROM, and then how I fetch them for display purpose.

(1) In this article, “character” and “byte” have the same meaning, since one ASCII character can be stored in a byte.

I used this solution in Wise Clock, where 32KB (aprox 16 book pages) of text(2) is displayed line by line, as a sequence of quotations.
(2) "Text" is a sequence of ASCII characters.
Obviously, before the text is retrieved from EEPROM, the text must be written, once, to EEPROM. So, the very first step is to load the text into the EEPROM. To do this, I use two pieces of software:
  • one sketch (running on Arduino) that reads, from the serial port, byte by byte, and then writes it into the EEPROM;

void loop()
{
if (isWrite)
{
if (Serial.available() > 0)
{
// read next character from COM port;
byte incomingByte = Serial.read();
if (isCommand && (incomingByte == 'r' || incomingByte == 'l'))
{
isWrite = false;
Serial.println("read eprom");
switch (incomingByte)
{
case 'r':
isReadLineByLine = false;
break;
case 'l':
isReadLineByLine = true;
break;
}
}
else
{
isCommand = false;
if (incomingByte == '\\')
{
// don't write the backslash;
isCommand = true;
}
else
{
// the following debug lines (serial.print...) take about 25 more ms than the write itself (10 ms);
// it is a good idea to comment them out if we transfer the content (32K) of a whole file;
/*
Serial.print("write to addr ");
Serial.print(crtWriteAddress);
Serial.print(" byte ");
Serial.println(incomingByte, BYTE);
*/
writeNextByte(incomingByte);
}
}
}
}

  • one application (running on PC and already presented here) that reads the characters from a text file and sends them to serial port, byte by byte.
Once in the EEPROM, the text can be retrieved when required. In Wise Clock, I chose to read the text from the EEPROM one line at a time. Since each line ends with a CR (“carriage return”), ASCII character 13, to get a line of text I read characters until a CR is found


else // reading the eeprom;
{
if (isReadLineByLine)
{
// read a whole line into the message buffer;
lastReadByte = readNextByte();
while (lastReadByte != 13 && lastReadByte != 0xFF)
{
*msgBufferPtr++ = lastReadByte;
lastReadByte = readNextByte();
}
*msgBufferPtr++ = 0;
Serial.println(msgBuffer);
msgBufferPtr = &msgBuffer[0]; // reset buffer pointer;
delay(50);
}
else
{
if (0xFF != lastReadByte)
{
Serial.print("read from addr ");
Serial.print(crtReadAddress);
Serial.print(" byte ");
lastReadByte = readNextByte();
Serial.print(lastReadByte, BYTE);
Serial.print(" (");
Serial.print(lastReadByte, DEC);
Serial.println(")");
delay(50);
}
}
}
}
If you want to cut an paste this code, you may need the following lines as well:
// I2C Bus address of 24LC256 EEPROM;
// if more than one eeprom, they will have different addresses (h/w configured);
#define I2C_ID 0x50
// global address of the last written byte;
unsigned int crtWriteAddress = 0;
// global address of the last read byte;
unsigned int crtReadAddress = 0;
boolean isWrite = true;
byte lastReadByte = 0;
// current line string is built by copying each character to the position of the pointer, then advancing the pointer;
char msgBuffer[200] = {0};
char* msgBufferPtr = &msgBuffer[0];
boolean isCommand = false;
boolean isReadLineByLine = false;
void writeByte(int i2cId, unsigned int eeaddress, byte data)
{
int rdata = data;
Wire.beginTransmission(i2cId);
Wire.send((int)(eeaddress >> 8)); // Address High Byte
Wire.send((int)(eeaddress & 0xFF)); // Address Low Byte
Wire.send(rdata);
Wire.endTransmission();
delay(10); // NEED THIS DELAY!
}
void writeNextByte(byte data)
{
writeByte(I2C_ID, crtWriteAddress, data);
crtWriteAddress++;
}
byte readByte(int i2cId, unsigned int eeaddress)
{
byte rdata = 0xFF;
Wire.beginTransmission(i2cId);
Wire.send((int)(eeaddress >> 8)); // Address High Byte
Wire.send((int)(eeaddress & 0xFF)); // Address Low Byte
Wire.endTransmission();
Wire.requestFrom(i2cId, 1);
if (Wire.available()) rdata = Wire.receive();
return rdata;
}
byte readNextByte()
{
byte rdata = readByte(I2C_ID, crtReadAddress);
crtReadAddress++;
return rdata;
}
(The last 4 I2C functions are copied and adapted from playground.)

1 comment: