I built a device that logs weather data to 64kB I2C FRAM, it's FM24C512. Logging interval is 5 minutes, the amount of data written is 96 bytes. This FRAM is actually two FM24C256 in one package. Each 32kB memory bank has it's own device address. I have 2 such chips on the bus, so in total 4 of addressable banks, each has 32kB. Device addresses are 0x50, 0x51, 0x52 and 0x53.
I'm writing these 96 bytes of data with sequential write and I paid special attention to situations, where the data would overlap the 32kB address space, so my code takes care of splitting the data and switching from one memory bank (and device address) to another and saving such data in two separate steps.
Yet I noticed that every couple of hours there's garbage data. It's always one single record approximately once a day. I figured out, that it happens when the data is being saved on addresses close to end of memory bank. So I did a lot of testing and found out this:
writing a single byte at any address is OK
writing a sequence of bytes at any address except the end of bank is OK
writing a sequence of bytes to end of bank EXCEPT the very last address (32767 / 0x7FFF) is OK. (for example writing 20 bytes from 32747 to 32766)
writing a sequence of bytes, where the last byte is being written to address 32767 is NOT OK. None of the data is saved. Not just last byte, but every byte of the sequence is lost.
I'm aware that during sequential write after writing to last address the internal address pointer of the FRAM is reset to 0, but my code is issuing a STOP condition after the last byte at 32767.
Has anyone ever see such behavior? Is this expected and normal? Am I doing something wrong?
I'm going to update my code to either skip the address space from 32672 to 32767 in each bank (last 96 bytes), or to write the last byte in the bank as a single byte write.
I will take a SWAG and say you are writing to your pointer +1 or incremented it before writing. Either way I believe it is code. I use these parts a lot without any problems.
One thing I forgot to mention is that I'm not using the Wire library but SoftI2CMaster from https://github.com/felias-fogg/SoftI2CMaster, version 2.1.9 (newest). Maybe that's buggy.
Code is running on Mega2560.
Here's the code (the "framadd" variable is uint32_t and is global, it's filled with start address (0-131071 / 0x00000 - 0x1FFFF) before calling the fram_write_page:
bool fram_write_page(const byte page_len) {
bool errr = false;
uint16_t fadd = framadd & 0x7FFF; //lower 15 bits of the 32bit address
byte slaveAddress = I2C_ADDR_FM24C512; //0x50
if (framadd > 32767) {
slaveAddress++; //select second memory bank
if (framadd > 65535) {
slaveAddress++; //select third memory bank
if (framadd > 98303) {
slaveAddress++; //select fourth memory bank
}
}
}
byte wr_count = 1; //normally, write the whole page at once
byte page_len_part = page_len;
if (fadd > (0x8000 - page_len)) { // writing at the and of bank / cross boundary
page_len_part = (0x8000 - fadd) & 0b00011111; // max 31
wr_count = 0; //make two separate writes
}
byte fbi2 = 0; //fram_buff index
while (wr_count < 2) {
if (!i2c_start((slaveAddress<<1)|I2C_WRITE)) { // start transfer
errr = true;
} else {
if (!i2c_write(highByte(fadd))) {
errr = true;
} else {
if (!i2c_write(lowByte(fadd))) {
errr = true;
} else {
for (byte t = 0; t < page_len_part; t++) {
if (!i2c_write(fram_buff[fbi2])) {
errr = true;
break;
}
fbi2++;
curr_addr++;
}
i2c_stop();
framadd += page_len_part;
fadd += page_len_part;
fadd = fadd & 0x7FFF;
}
}
}
if (wr_count == 0) {
slaveAddress++; //advance to next memory bank
}
page_len_part = page_len - page_len_part; //write the 2nd part of the buffer in the next cycle
wr_count++;
}
return !errr;
}
I updated my code, replaced the hardcoded value with the constant as you suggested, but unfortunately this wasn't it.
Nice! Thanks, this saved me a couple of lines of code.
Now I really suspect the softI2c library, I am updating my code to use the Wire library, just to see if it makes any difference. I switched from Wire years ago because I had problems with I2C bus - one of the devices (a temperature or humidity sensor) was blocking the bus and that was halting the whole program, since Wire doesn't have any timeout protection against such state (at least didn't have at that time).