Solution:
Majenko properly pointed out that I was trying to write across page boundaries and either overwriting or skipping blocks of the EEPROM when I did. This updated write function checks for a page boundary and splits the write into two writes if it is going to write over one.
void writeEEPROMPage(long eeAddress) {
int writes = incr;
bool overflow = false;
//check for page overrun, adjust writes to fill page and set flag if overrun
if((eeAddress % 128 + incr) > 127) {
writes = 128 - eeAddress % 128;
overflow = true;
}
Wire.beginTransmission(EEPROM_ADR);
Wire.write((int)(eeAddress >> 8)); // MSB
Wire.write((int)(eeAddress & 0xFF)); // LSB
//Write bytes to EEPROM
Wire.write(rwBuffer, writes); //Write the data
Wire.endTransmission(); //Send stop condition
eeAddress += writes;
if(overflow) {
delay(5);
Wire.beginTransmission(EEPROM_ADR);
Wire.write((int)(eeAddress >> 8)); // MSB
Wire.write((int)(eeAddress & 0xFF)); // LSB
//Write remaining data to new page
Wire.write(rwBuffer + writes, incr - writes);
Wire.endTransmission();
eeAddress += incr - writes;
}
}
Problem:
I am currently working on a project to record sensor readings to a 24FC512 512kb I2C EEPROM and have been teaching myself to read and write properly to this chip using an Arduino Uno. I am having a problem: when I write too many bytes in one I2C cycle, some bytes of data are skipped and not written.
In theory, I believe I should be able to write up to a 128-byte page to the EEPROM and the buffer in the Arduino Wire library should allow me to write up to 32 bytes (including address) in one transmission without modification. However, when I write more than a certain number of bytes, typically 16, and then read back the data from the EEPROM, I notice that the write commands seem to have skipped part of the write.
For instance, when trying to clear pages of 20 bytes at a time using 0xF0 to fill the space I get this result in the first few lines in blocks of 20:
F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0
F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0
F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0
F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 11 22 33 44 55 66 77 88
+12 lines of F0
F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 55 66 77 88 99 AA BB CC
+12 lines of F0
F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 99 AA BB CC DD EE FF 00
+5 lines of F0
F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 11 22 33 44 55 66 77 88
The section that has not been cleared matches the original pattern in that location. When I reduce the page size to 16 bytes, the problem goes away and everything is written correctly. The uncleared pattern is also periodic after the first three lines, running 12 lines, error, 12 lines, error, 5 lines, error for the rest of the data. When tried with 30 bytes per transmission, it's periodic by 3-8-3 when organized in lines of 30. In all cases the errors have been at the end of each page of transmission.
This has become very frustrating because I'd like to get the data written to the EEPROM in as little time as possible so I can get a good sensor sample rate (currently 24 bytes per sample) and have time to run other functions. Ideally I'd be able to write every 5 ms to increase the sampling rate to the maximum supported by the EEPROM, so I need to solve this problem.
Here is the code I am using to clear and read the contents of the EEPROM. The variable incr holds the size of the writes I want to make, and pin 12 decides whether the program will read the EEPROM (high) or clear the EEPROM (low). I write data to the EEPROM with the commented out array to run my tests. It is based on example programs for EEPROM from Sparkfun.
#include <Wire.h>
#define EEPROM_ADR 0x54
const int incr = 30;
byte rwBuffer[] = {
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0
};
void setup() {
pinMode(13, OUTPUT);
pinMode(12, INPUT);
Wire.begin();
Wire.setClock(400000);
long currentSpot = 0;
if (digitalRead(12)) {
dumpEEPROM();
} else {
while (currentSpot < 64000) {
writeEEPROMPage(currentSpot);
currentSpot += incr;
delay(7);
}
}
}
void loop() {
digitalWrite(13, !digitalRead(13));
delay(500);
}
void writeEEPROMPage(long eeAddress) {
Wire.beginTransmission(EEPROM_ADR);
Wire.write((int)(eeAddress >> 8)); // MSB
Wire.write((int)(eeAddress & 0xFF)); // LSB
//Write bytes to EEPROM
Wire.write(rwBuffer, incr); //Write the data
Wire.endTransmission(); //Send stop condition
}
void dumpEEPROM() {
long i;
int j, readnum, timer = 0;
Serial.begin(115200);
timer = millis();
for (i = 0 ; i < 64000 ; i += incr) {
//Read all bytes from EERPOM
readnum = readToBuffer(i);
for (j = 0; j < incr; j++) {
if (rwBuffer[j] < 0x10)
Serial.print("0");
Serial.print(rwBuffer[j], HEX);
Serial.print(" ");
}
Serial.println();
}
timer = millis() - timer;
Serial.print("Full data dump took ");
Serial.print((float)timer / 1000, 3);
Serial.println(" seconds.");
Serial.end();
}
int readToBuffer(long address) {
int i = 0;
Wire.beginTransmission(EEPROM_ADR);
Wire.write((int)(address >> 8)); // MSB
Wire.write((int)(address & 0xFF)); // LSB
Wire.endTransmission();
Wire.requestFrom(EEPROM_ADR, incr);
for (i = 0; i < incr && Wire.available(); i++) {
rwBuffer[i] = Wire.read();
}
return i;
}