1

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;
}

2 Answers 2

0

You are trying to do a page write across a page boundary.

The write control byte, word address and the first data byte are transmitted to the 24XX512 in the same way as in a byte write. But instead of generating a Stop condition, the master transmits up to 127 additional bytes, which are temporarily stored in the on-chip page buffer and will be written into memory after the master has transmitted a Stop condition. After receipt of each word, the seven lower Address Pointer bits are internally incremented by one. If the master should transmit more than 128 bytes prior to generating the Stop condition, the address counter will roll over and the previously received data will be overwritten. As with the byte write operation, once the Stop condition is received, an internal write cycle will begin

You are sending blocks of 30 bytes (incr = 30). You have 128 byte pages.

  • 128 / 30 = 4.267 writes per page. The 5th write spans pages. But, in a big bold NOTE in the datasheet:

Page write operations are limited to writing bytes within a single physical page, regardless of the number of bytes actually being written. Physical page boundaries start at addresses that are integer multiples of the page buffer size (or ‘page size’) and end at addresses that are integer multiples of [page size – 1]. If a Page Write command attempts to write across a physical page boundary, the result is that the data wraps around to the beginning of the current page (overwriting data previously stored there), instead of being written to the next page as might be expected. It is therefore necessary for the application software to prevent page write operations that would attempt to cross a page boundary.

So you write 4 blocks to the first page, then the 5th block fills in the end of the first page then goes back to the start of that page and overwrites what has already been written earlier. Your next page then starts part way through the next block skipping a chunk that you cannot now get to.

You must either do your writes in blocks of an integer division of 128 (so 16 bytes is the largest you can do) or manually detect a page boundary and split your block write into two separate writes - one to finish filling the first page and the next to start filling the next page.

1
  • Thank you for correcting my understanding of how pages work and don't work. I'm not sure what I was(n't) thinking when I read that part of the datasheet. I'll update the main post with proper code for handling page boundaries.
    – SRFirefox
    Commented Aug 27, 2017 at 16:02
0

Right after each Wire.endTransmission(); you need to insert a delay(5);. The errors you see could be write-related, and/or read related too. You have to let the EEPROM chip process the address request before you start dumping/reading.

4
  • I have the line delay(7); in the main program rather than the write function, as I intend to be able to use the writeEEPROMPage function in a program based around a timer interrupt so as to not tie up the processor when it needs to be doing other work.
    – SRFirefox
    Commented Aug 27, 2017 at 5:00
  • And you don't have it, at all, for readToBuffer(). It is not only safer, but necessary, to put it right after every Wire.endTransmission();. Try it and see if that influences things. The delay is not optional, and it's there for a reason.
    – dda
    Commented Aug 27, 2017 at 5:16
  • readToBuffer() is reading the EEPROM and to my understanding does not need to wait 5mS to then read or write to the EEPROM.
    – SRFirefox
    Commented Aug 27, 2017 at 16:28
  • You seem to know everything and still you come with questions... As I said, try it. You might learn something.
    – dda
    Commented Aug 27, 2017 at 18:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.