Faster way to write to external EEPROM?

I have a 24LC256 external 256 kbit EEPROM I've been playing with. Basically, I want to write data to it as fast as possible. So far I've been able to get it to write a single byte in 5 ms, which according to the datasheet is the minimum time required to write a byte. The datasheet also says that either a byte or a page can be written in 5 ms. Now, I have no idea what a page is or how to write one to the memory, but it sounds like it may be a bit bigger than a byte, and would thus require fewer write cycles for the same amount of data. To measure the time, I'm having a pin flip high, write an arbitrary byte, then flip the pin low. Another arduino measures the resulting pulse and reports it in microseconds. The code I've been using is below.

#include <Wire.h>    
 
#define disk1 0x50    //Address of 24LC256 eeprom chip

int val = 12345;

void setup(void)
{
  Serial.begin(9600);
  Wire.begin();  
  Wire.setClock(1000000);
  DDRB = (1<<PB5);
  PORTB = (1<<PB5);
  writeEEPROM(disk1,0,123);
  //writeEEPROM(disk1, 0, lowByte(val));
  //writeEEPROM(disk1, 1, highByte(val));
  PORTB = (1>>PB5);
  byte a = readEEPROM(disk1, 0);
  byte b = readEEPROM(disk1, 1);
  Serial.print(a, DEC);
  Serial.print(" ");
  Serial.print(b, DEC);
  Serial.print(" ");
  Serial.println(word(b,a));
}
 
void loop(){}
 
void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data ) 
{
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.write(data);
  Wire.endTransmission();
 
  delay(5);
}
 
byte readEEPROM(int deviceaddress, unsigned int eeaddress ) 
{
  byte rdata = 0xFF;
 
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));   // MSB
  Wire.write((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
 
  Wire.requestFrom(deviceaddress,1);
 
  if (Wire.available()) rdata = Wire.read();
 
  return rdata;
}

So I guess my real question is, what is a page and how can I write one to the EEPROM?
~Josh

Your best source of advice is the device data sheet. I happen to have a copy for the 24LC256, wherein I very quickly discovered:

6.2 Page Write
The write control byte, word address and the first data
byte are transmitted to the 24XX256 in much the same
way as in a byte write. The exception is that instead of
generating a Stop condition, the master transmits up to
63 additional bytes, which are temporarily stored in the
on-chip page buffer, and will be written into memory
once the master has transmitted a Stop condition.

Yeah, I read that too, the problem is that I don't really know anything about protocol or sending individual bits or anything like that, I'm pretty new to all this stuff. I also saw what seems to be the control protocol or something like that, so my next goal is to figure out what it means and how to use it.

EEPROM_bits.PNG

You are already successfully sending one byte to the chip, so why is it difficult to send 63 more bytes?

Do you not understand the code you posted?

If not, the code to send the other 63 bytes would be placed between these two lines:

  Wire.write(data);
  Wire.endTransmission();

Edit: what is this line doing in the EEPROM write routine?

  delay(5);

Make CERTAIN that you understand if any calls to delay() are necessary, and if so, why and how long they should be.

Correct, I don't really understand the code I'm using, I just know it works. I did not write the code myself.

I'm not sure what that line is doing, I'll try to remove it and see what happens.

I think I understand what youre saying. So I would add the other byes like this?

Wire.write(byte1)
Wire.write(byte2)
...
Wire.write(byte64)
Wire.end transmission

I'll try that and see what happens.
Thanks for the tip.

Josh_Blackburn:
Correct, I don't really understand the code I'm using, I just know it works. I did not write the code myself.

I'm not sure what that line is doing, I'll try to remove it and see what happens.

I think I understand what youre saying. So I would add the other byes like this?

Wire.write(byte1)
Wire.write(byte2)
...
Wire.write(byte64)
Wire.end transmission

I'll try that and see what happens.
Thanks for the tip.

You need to understand what an EEPROM Write Page is, and the Arduino Wire.h library limitations.

An EEPROM can be though of as an array of memory cells in a X * Y array where the Write page is the width of the 'X' dimension. The chip contains a Write buffer that is the same size the Write Page.

The hardware in the chip always writes ONE complete page at a time. When you write a byte to the EEPROM three sequences happen:

  • First, you tell the EEPROM where the write starts (the 16bit address).
    This causes the EEPROM to decode your address into two parts, first the Page address, second the offset within that page where your changes start.
    Then it copies the current content of that decoded page from it's EEPROM array into the Write buffer.
  • Second, you send the individual bytes, which the chip then overwrites into the Page buffer. After each byte is stored in the buffer the chip's internal buffer offset counter is incremented. If this counter exceeds the maximum offset value (Write page Size), the offset is counter is set to zero (0). The next byte you send then overwrite the 0'th byte of the page. (It does NOT increment to the next higher address, on the NEXT page.)
  • When you issue the Wire.endTransmission(); call the chip then copies the current content of the Write buffer into the EEPROM array.

So, the maximum number of bytes that can be stored in a single I2C transaction is equivalent to the lessor of two limits, first, the Write page size, or the Maximum Arduino Wire transaction size (32bytes, Except the two address byte count against the Arduino limit). A standard Wire.h write transaction is limited to 30 data bytes.

If your I2C chip has a 64byte Write page this means that if you try to write 10 bytes of data starting at address 60, you will actually change bytes, 60..63, 0..5.

If you try the same write sequence at address 120, you will actually change 120..127, 64..67.

This is my code for isolating the Write page issues from raw block writes.

bool writeI2CBin(const uint8_t id,uint16_t adr, char data[],const uint16_t len,const uint8_t pageSize){
// Have to handle write page wrapping,
// 24lc512 has 128 byte 
// 24lc64 has 32 byte
/*
Serial.print("writeBin(0x");
Serial.print(id,HEX);
Serial.print(",0x");
Serial.print(adr,HEX);
Serial.print(",len=");
Serial.println(len,DEC);
*/
uint16_t bk = len;
bool abort=false;
uint8_t i;
uint16_t j=0;
uint32_t timeout;
uint16_t mask = pageSize-1;
while((bk>0)&&!abort){
 i =30;  // maximum data bytes that Wire.h can send in one transaction
 if(i>bk) i=bk;  // data block is bigger than Wire.h can handle in one transaction
 if(((adr)&~mask)!=((((adr)+i)-1)&~mask)){ // over page! block would wrap around page
  i = (((adr)|mask)-(adr))+1; // shrink the block until it stops at the end of the current page
// Serial.print("adjust i =");
// Serial.println(i,DEC);
 }
//wait for the EEPROM device to complete a prior write, or 10ms 
 timeout = millis();
 bool ready=false;
 while(!ready&&(millis()-timeout<10)){
 Wire.beginTransmission((uint8_t)id);
 ready=(Wire.endTransmission(true)==0); // wait for device to become ready!
 }
 if(!ready){ // chip either does not exist, is hung, or died
 abort=true;
 Serial.print(id,HEX);
 Serial.println(F(" chip timeout"));
 break;
 }
// Serial.print("adr=");Serial.print(*adr,HEX);
// Serial.print(":");
// start sending this current block
 Wire.beginTransmission((uint8_t)id);
 Wire.write((uint8_t)highByte(adr));
 Wire.write((uint8_t)lowByte(adr));

 bk = bk -i;
 adr = (adr) + i;

 while(i>0){
// Serial.print(" ");
// Serial.print((uint8_t)data[j],HEX);
 Wire.write((uint8_t)data[j++]);
 i--;
 }
 
 uint8_t err=Wire.endTransmission();
 if(err!=0){
 Serial.print(F("write Failure="));
 Serial.println(err,DEC);
 abort = true;
 break;
 }
// else Serial.println();
 }
return !abort;
}

Chuck.