WebbotLib AVR library
WebbotLib It just does it
  C++ documentation  C documentation

i2cEEPROM.h

Provides generic support for EEPROM devices accessed over an I2C bus.
Note that these device are often advertised showing the capacity as the number of 'bits' they provide. Such as 512k bits. Since we are dealing in 8 bit bytes then you need to divide this figure by 8. So 512k bits => 64k bytes. Not sure why they are sold that way - maybe its because of 32 bit vs 16 bit vs 8 bit processors?
So why would you need one?
I2C_EEPROM_XXX.jpg
These devices are moderately cheap (say five dollars) and typically come in an 8 pin format and are therefore physically quite small but can store a moderate amount of data - so they are good for storing 'work in progress'. Your processor may come with some 'on board' EEPROM space but typically it will be quite small. So if you need more capacity then here is your solution.
The diagram shows a typical pin out for such a device but you may want to check the data sheet for the device you have purchased.
Besides a Ground and Supply voltage you will also see the SDA and SCL pins which are the heart of the I2C bus.
You will also see a write protect pin, WP, which can be connected to Ground so that the chip is 'writable'.
The only other pins are those labelled A0, A1 and A2.
In 'simple' terms these can be connected to Vcc or Gnd to modify the I2C address of the device starting at its default address of 0xC0 where the resultant address is:
1 1 0 0 a2 a1 a0 0
So "in theory" these 3 bits allow you to connect 8 devices with addresses: 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA and 0xCE.
Unfortunately, its not that simple ... although WebbotLib takes care of the complexities for you.
There are a few other things you need to check from the datasheet before you can use the device:
The address size may be 1, 2, 3 or 4 bytes (ie 8, 16, 24 or 32 bits) and is normally dictated by the number of bytes of storage:
However: you may have,say, a 1024 byte EEPROM which therefore needs 10 bits to specify an address. In this case: it uses 1 byte (8 bits) for the memory address and the remaining two bits are placed into A0 and A1 - meaning that each chip has 4 x I2C addresses as if it was 4 separate 256 byte chips. In this case the A0 and A1 pins should be left unconnected.
But don't worry:- the WebbotLib Project Designer will show you examples.
The page size is always a power of 2 - it chops the device up into a number of similar sized pages. A single write to the device must all be in the same page. This value is passed to WebbotLib when creating the device so that your code doesn't have to worry about the restriction - WebbotLib 'just does it'.
This header file has a generic MAKE macro but due to the complexities it also has a MAKE command for several standard ATmel EEPROMs.
Assuming we want to create a device called 'myEEPROM' which is an AT24C1024B and we want to use address 0xC0 then:
#include "Storage/i2cEEPROM.h"
// Create the device
I2C_EEPROM myEEPROM = MAKE_I2C_EEPROM_AT24C1024B(FALSE,FALSE); // Address = 0xC0
 
You can now read and write bytes to the EEPROM.

 

Function

 


uint8_t i2cEEPROM_readByte(SPI_EEPROM* eeprom, EEPROM_ADDR addr)

Read a single byte from the EEPROM.
The parameters specify the EEPROM device to read, and the 32 bit address of the byte in the EEPROM that you want to read.
So to read bytes 120 to 130 from the EEPROM then:
for(EEPROM_ADDR addr = 120; addr <= 130; addr++){
    uint8_t byte = spiEEPROM_readByte( &myEEPROM, addr);
}

void i2cEEPROM_writeByte(SPI_EEPROM* eeprom, EEPROM_ADDR addr, uint8_t data)

Write a single byte to the EEPROM.
The parameters specify the EEPROM device, the 32 bit address of the byte in the EEPROM that you want to write, and the value.
So to write a random value to byte 100 in the EEPROM then:
uint8_t val = random();
spiEEPROM_writeByte(&myEEPROM, 100, val);
Note that writes to the EEPROM are relatively slow. So if you are writing a sequence of bytes to an EEPROM then it is faster to use the spiEEPROM_writeBytes command to write them in one go.

void i2cEEPROM_readBytes(SPI_EEPROM* eeprom, EEPROM_ADDR addr, void* dest, size_t numBytes)

Read a sequence of bytes from the EEPROM.
The parameters specify the EEPROM device to read, the 32 bit address of the first byte in the EEPROM that you want to read, the address where you want to read the data to, and the number of bytes to read.
So to read the 11 bytes starting at 120 from the EEPROM into memory then:
// Create a buffer to store the bytes
uint8_t buffer[11];
// Read from the eeprom into buffer
spiEEPROM_readBytes(&myEEPROM, 120, buffer, 11);

void i2cEEPROM_writeBytes(SPI_EEPROM* eeprom, EEPROM_ADDR addr, const void* src, size_t numBytes)

Write a sequence of bytes to the EEPROM.
The parameters specify the EEPROM device to write to, the 32 bit address of the first byte in the EEPROM that you want to write to, the address where you want to write the data from, and the number of bytes to write.
So to write the 11 bytes to the address starting at 120 in the EEPROM from memory then:
// Create a buffer
uint8_t buffer[11];
// -- Code here puts values in the buffer --
// Write the buffer to EEPROM
spiEEPROM_writeBytes(&myEEPROM, 120, buffer, 11);
Often an EEPROM is used to store an array of values where each value is the same size. So lets create a routine to log changes for the ADC inputs. So each 'value' can be stored in the following structure:
typedef struct s_adc_log{
    TICK_COUNT time; // The current time
    uint8_t adcs[8]; // 8 adc values
} ADC_LOG;
Create a variable to hold one of these 'values':
ADC_LOG log;
Create a variable to store the current address in the EEPROM we are writing to:
EEPROM_ADDR addr; // Will be initialised to 0
Create a function to populate the data and return 'TRUE' if the data has changed:
boolean getValues(){
    boolean changed = FALSE;
    for(uint8_t n = 0; n < 8; n++){
        uint8_t val = a2dConvert8bit(ADC_NUMBER_TO_CHANNEL(n));
        if( val != log.adcs[n] ){
            // The value has changed
            log.adcs[n] = val;
            changed = TRUE;
        }
    }
    return changed;
}
Create a function to write the value to EEPROM:-
void write(TICK_COUNT time){
    // Put in the time stamp
    log.time = time;
    // Write to eeprom
    spiEEPROM_writeBytes(&myEEPROM, addr, &log, sizeof(ADC_LOG));
    // bump address for next time
    addr += sizeof(ADC_LOG);
}
In your startup code - write out the initial value:
TICK_COUNT appInitSoftware(TICK_COUNT loopStart){
    getValues();
    write(loopStart);
}
In your main loop - only write out the values if they have changed:
TICK_COUNT appControl(LOOP_COUNT loopCount, TICK_COUNT loopStart){
    if(getValues()){
        write(loopStart);
    }
}

EEPROM_ADDR i2cEEPROM_totalBytes(const SPI_EEPROM* eeprom)

Returns the total number of bytes in the EEPROM.

Valid XHTML 1.0 Transitional