Using Arduino with an I2C EEPROM. I got my hands on an AT24C256 (256 kbit = 32 kbyte serial EEPROM). I found no library for it, so I created a small sketch with few functions to show how the i2c_eeprom_write_page and i2c_eeprom_read_byte functions work.
I am using this library: It allows the user to write & read a single byte or an array. In my project I need to use a struct to store all my data and searching on the forum I found a topic in which a user points here: So I created an example code in which I try to use this 2 functions with the previous library but I did something wrong or I need to change something else because it is failing. Values I read from eeprom are not the ones I wrote before.
Tjis is my test sketch. I am using this library: It allows the user to write & read a single byte or an array. In my project I need to use a struct to store all my data and searching on the forum I found a topic in which a user points here: So I created an example code in which I try to use this 2 functions with the previous library but I did something wrong or I need to change something else because it is failing. Values I read from eeprom are not the ones I wrote before.
You might be running into a problem with your Device's WritePage size. Depending on exactly which EEPROM you are using(brand and Model), For instance a MicroChip 24LC01B 128byte EEPROM. Which has a 8byte WritePage. Any Single Write must fit within one of the 8 byte write pages: Address: 0x00.0x07 Address: 0x08.0x0F Address: 0x10.0x17 Address: 0x18.0x1F Address: 0x20.0x27 Address: 0x28.0x2F Address: 0x30.0x37 Address: 0x38.0x3F Address: 0x40.0x47 Address: 0x48.0x4F Address: 0x50.0x57 Address: 0x58.0x5F Address: 0x60.0x67 Address: 0x68.0x6F Address: 0x70.0x77 Address: 0x78.0x7F If your structure is 10 bytes long and you use the WriteAnything Template to save it at address 0x14.
Char buffer10 = 'ABCDEFGHI'; // with appended null What you expect the EEPROM to store: EEPROM memory Map( as HexiDecimal Characters): 0x10: xx xx xx xx 41 42 43 44 0x18: 45 45 45 48 49 00 xx xx What is actually Stored: 0x10: 45 46 47 48 49 00 43 44 The way an EEPROM works, is that it has to erase the memory cells before it can change them. When you issue a Write command, you do send the address followed by the data.
Code: Wire.beginTransaction(I2CChipAddress); Wire.write((byte)address); // starting point were to store data Wire.Write((byte)firstDataByte); Wire.Write((byte)secondDataByte); Wire.Write((byte)thirdDataByte); //. Wire.Write((byte)lastDataByte); // all of these Wire.write commands actually just buffer the data Wire.endTransaction; // this command actually does all of the transmitting to the EEPROM. The EEPROM takes the first byte of the write sequence, stores it as the 'address pointer', Calculates which WritePage is going to be changed: Page=addressPoint / WritePageSize; then copies this page from the EEPROM array into the WritePageBuffer.
Then calculates the offset into the WritePageBuffer: offset= addressPointer WritePageSize; then it starts overwriting it's WritePageBuffer one byte at a time with the data it is receiving. If the offset pointer is incremented past the end of the WritePageBuffer, it restarts at the beginning of the buffer: offset=0; After the last byte is received the Chip then Erases the Complete Page in the EEPROM array, then it writes the 'new' values from the WritePageBuffer. So, to store data into an EEPROM you must break your data into pieces that fit on it's pages. For my example: Ten bytes of data, stored in addresses 0x14 through 0x1D. Two Write operations are required.
Because this address range occupies two of the 8byte Write pages. Thank you Chucktodd.
I think I understand it better now. I bought the eeprom on ebay, it is labeled as 24C01WP 8Kb. In my real project I have an struct about 240bytes.
I was thinking on a function in which I pass the whole struct and save/read it completelly. I thought it would be easier. I think I will have to read a bit about what you just explained and try to implement myself for my struct.
Draw a memory map, give positions to every variable. One more question. If I try to save a 2 bytes value (like int) or 4 bytes (like float) I have to split them into bytes and then, save each one of those bytes in each position. Then, when I want to read, I will read all bytes and join them to get my value, is this correct?
I am going to start writing a map of my memory and put all my values in their positions before coding it. Thank you Chucktodd.
I think I understand it better now. I bought the eeprom on ebay, it is labeled as 24C01WP 8Kb. In my real project I have an struct about 240bytes. I was thinking on a function in which I pass the whole struct and save/read it completelly.
I thought it would be easier. I think I will have to read a bit about what you just explained and try to implement myself for my struct. Draw a memory map, give positions to every variable. One more question. If I try to save a 2 bytes value (like int) or 4 bytes (like float) I have to split them into bytes and then, save each one of those bytes in each position. Then, when I want to read, I will read all bytes and join them to get my value, is this correct?
I am going to start writing a map of my memory and put all my values in their positions before coding it. If you wanted to send 128 bytes at a time, yes. I wrote my writeI2CBin function to use the 32 byte Wire.h limits. It only sends a maximum of 30 data bytes at a time, +2 address bytes for 32byte Wire.h Limit.
The WritePage size is used to decide when to break a write into multiple pieces. If I send a 200 byte block to the writeI2CBin function, and WritePage is 128. At a minimum, The routine will executed 7, 30byte data blocks. Depending on how the address aligns with the WritePage, an extra 1 or 2 block may be required. The routine starts by trying to send 30 bytes.
If 30 bytes aren't available, set size to available bytes. Then it check so see if a WritePage boundary would be crossed by this block. If so, it shortens the block to stop at the boundary. Then it actually writes the adjusted block. Then it updates the next block address, checks to see if it is done.
Else jumps back to 1. And sends the next block Chuck.