476 ms is just about right for 20 Kbytes or 160 Kbits
(160 000 bits) / (400 000 bps) = 400 ms (the 76 ms is the overhead)
It's easy to modify the code
For writing sequential data to the FRAM
int i2c_write(uint8_t slave, uint16_t fram_address, uint8_t *data, uint16_t length)
{
uint8_t tmp = 0;
int rtn = 0;
// get FRAM memory address to start writing to
uint8_t MSB = (uint8_t) ( (fram_address & 0xFF00) >> 8 );
uint8_t LSB = (uint8_t) ( (fram_address & 0x00FF) >> 0 );
// send START condition
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while((TWSR & (0x08)) == 0);
for(uint8_t count=0; count<32; count++) {
asm("NOP");
}
// set MASTER WRITE ADDRESS
TWDR = slave;
TWCR = (1<<TWINT) | (1<<TWEN);
while((TWCR & 0x80) == 0);
for(uint8_t count=0; count<32; count++) {
asm("NOP");
}
// get status
tmp = TWSR;
if(tmp == 0x18) // if a SLAVE responded
{
/**********************
Address byte MSB
**********************/
TWDR = MSB;
TWCR = (1<<TWINT) | (1<<TWEN);
while((TWCR & 0x80) == 0);
tmp = TWSR;
if(tmp == 0x28) // ACKed
{
rtn++;
}
else if(tmp == 0x30) // NACKed
{
rtn = -1;
}
/**********************
Address byte LSB
**********************/
TWDR = LSB;
TWCR = (1<<TWINT) | (1<<TWEN);
while((TWCR & 0x80) == 0);
tmp = TWSR;
if(tmp == 0x28) // ACKed
{
rtn++;
}
else if(tmp == 0x30) // NACKed
{
rtn = -1;
}
// proceed to write bytes to SLAVE
for(uint16_t count=0; count<length; count++)
{
TWDR = data[count];
TWCR = (1<<TWINT) | (1<<TWEN);
while((TWCR & 0x80) == 0);
tmp = TWSR;
if(tmp == 0x28) { // ACKed
rtn++;
}
else if(tmp == 0x30) { // NACKed
rtn = -1;
break;
}
}
// send STOP
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
}
else if(tmp == 0x20) // no SLAVE response or bus error
{
// send STOP
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
rtn = -1;
}
else {
rtn = tmp;
}
return rtn;
}
int i2c_write(uint8_t slave, uint16_t fram_address, uint8_t *data, uint16_t length)
The slave address is the I2C slave write address. In your case it's 0xA0 (or in Adafruits term 0x50 << 1)
The fram_address is the starting point in the FRAM memory where you want to start writing data to. So it would be from 0x00 to 32K
This part breaks it down to the upper 8 bits and lower 8 bits
// get FRAM memory address to start writing to
uint8_t MSB = (uint8_t) ( (fram_addres & 0xFF00) >> 8 );
uint8_t LSB = (uint8_t) ( (fram_addres & 0x00FF) >> 0 );
And sends it to the FRAM memory as the starting point
/**********************
Address byte MSB
**********************/
TWDR = MSB;
TWCR = (1<<TWINT) | (1<<TWEN);
while((TWCR & 0x80) == 0);
tmp = TWSR;
if(tmp == 0x28) // ACKed
{
rtn++;
}
else if(tmp == 0x30) // NACKed
{
rtn = -1;
}
/**********************
Address byte LSB
**********************/
TWDR = LSB;
TWCR = (1<<TWINT) | (1<<TWEN);
while((TWCR & 0x80) == 0);
tmp = TWSR;
if(tmp == 0x28) // ACKed
{
rtn++;
}
else if(tmp == 0x30) // NACKed
{
rtn = -1;
}
From there, you can send as much data as you want and the FRAM will automatically increment the memory location as you send each byte
The *data is a pointer variable where your data resides in the Arduino and the length is how many bytes do you intend to transfer starting from this address
// proceed to write bytes to SLAVE
for(uint16_t count=0; count<length; count++)
{
TWDR = data[count];
TWCR = (1<<TWINT) | (1<<TWEN);
while((TWCR & 0x80) == 0);
tmp = TWSR;
if(tmp == 0x28) { // ACKed
rtn++;
}
else if(tmp == 0x30) { // NACKed
rtn = -1;
break;
}
}
Since you data store is a struct, you can just copy the address of the struct and put it into a uint8_t * data
I think this will work
uint8_t * data = &myData
Since each int variable in the struct is two bytes, the total length (in terms of bytes) would be 20
So for example, you want to save the data starting from FRAM location 0x00
uint8_t * data = &myData;
i2c_write(0xA0, 0x00, data, 20);