I2Cmem.c
#include "i2cmem.h"
#ifdef EEPROM
//----------General I2C functions-------------------------------------------------------------------------
volatile u08 I2Cerr;
void init_i2c()
{
TWBR=(u08)(((float)F_CPU/(2.0*(float)SCL))-8.0);//sets the correct clock rate defined as SCL
} //we dont need to enable the hardware - its done in write
void i2cstart()
{
u16 timeout=1;
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); //send start
while (!(TWCR & (1<<TWINT)) && timeout)
timeout++; //wait for a start to be transmitted
if(!timeout)
I2Cerr|=16; //timeout out waiting for start error
TWCR = (1<<TWEN ); //clear the start condition
if (((TWSR & 0xF8) != TW_START)&&((TWSR & 0xF8) != TW_REP_START))
I2Cerr|=1; //error
}
void i2cstop()
{
u16 timeout=1;
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
while (!(TWCR & (1<<TWSTO)) && timeout) //wait for stop to be sent
timeout++;
if(!timeout)
I2Cerr|=64; //timeout waiting for stop error
}
void i2cwrite(u08 c)
{
u16 timeout=1;
TWDR = c; //load the data
TWCR = (1<<TWINT)|(1<<TWEN);
while (!(TWCR & (1<<TWINT)) && timeout) //wait for it to be sent
timeout++;
if(!timeout)
I2Cerr|=32; //timeout out waiting for write complete error
if ( ((TWSR&0xF8)!=TW_MT_SLA_ACK)&&((TWSR&0xF8)!=TW_MR_SLA_ACK)&&((TWSR&0xF8)!=TW_MT_DATA_ACK) )
I2Cerr|=2; //error
}
u08 i2cread(u08 ak)
{
u16 timeout=1;
TWCR=ak;
while ((!(TWCR & (1<<TWINT))) && timeout)
timeout++; //wait for it to be recieved
if(!timeout)
I2Cerr|=8; //timeout out waiting for data response error
if(((TWSR & 0xF8) !=TW_MR_DATA_ACK) && ((TWSR & 0xF8) !=TW_MR_DATA_NACK))
I2Cerr|=4;
return TWDR;
}
//----------EEPROM specific stuff-------------------------------------------------------------------------
//Designed for 12 byte records atm
void set_address(u32 * address) //Sets an address from a pointer - can select the correct physical device but reqires idle bus
{
i2cstart();
i2cwrite(SLA_W|(((*address)>>15)&0x02)); //the block
i2cwrite((u08)((*address)>>8)); //address write MSB
i2cwrite((u08)(*address)); //address write LSB
}
void write_data(u08 * c, u08 datasize, u32 * address)//The address will be incremented as the data is written
{
u08 n; //up to 256 bytes can be written at once
for(n=0;n<datasize;n++,(*address)++)
{
if(!((u08)*address)) //check the address for overflow for the 8 LSB (Atmel)
{
i2cstop(); //triggers a page write
_delay_loop_2( (u16) ( (float)F_CPU*0.006/4.0 ) );//delay 6 ms for page write
set_address(address); //set the new address
}
i2cwrite(c[n]); //write the data
}
}
void read_data(u08 * destination, u08 datasize, u32 * address)//Reads a block of data from some address
{
u08 n;
datasize--;
i2cstart();
i2cwrite(SLA_R|((u08)((*address)>>15)&0x02)); //set read to the correct block
for(n=0;n<datasize;n++) //the eeproms address pointer should have been setup already
{
destination[n]=i2cread(_AK_);
(*address)++; //increment our pointer
/*if(!(u16)(*address)) //we reached the end of the block
{
i2cstop();
set_address(address);
}This isn't needed on the Atmel chips*/
}
destination[datasize]=i2cread(_NAK_); //send NAK for the last byte recieved
i2cstop();
(*address)++;
}
u32 findtop() //Find the address of record number where we enter painted eeprom
{
u32 top=12*((u32)1<<13); //half way through rounded down to 12
u08 n,endcond=TRUE;
u16 place=1<<13;
for(n=14;endcond;) //start by probing half way through - place is in records
{
set_address(&top); //the probe address
if(n)
n--; //need to decr here to get 1 byte resolution
else
endcond=FALSE; //terminate the loop after completion //need to decr here to get 1 byte resolution
i2cstart(); //read the slave
i2cwrite(SLA_R);
if(i2cread(_NAK_)==MAGIC_NUMBER) //see if its painted
place&=~(1<<n); //clear the nth bit in the place holder
if(n)
place|=(1<<(n-1)); //set the next bit
i2cstop();
if(place>10922)
place=10922;
top = 12*( (u32)place ); //place is in units of 12
}
set_address(&top);
i2cstart();
i2cwrite(SLA_R);
if(i2cread(_NAK_)!=MAGIC_NUMBER) //check the location to make sure its blank
place++;
top = 12*( (u32)place );
return top;
}
void painteeprom() //Paints every 12th location with the magic number
{
u08 magic=MAGIC_NUMBER; //magic is our painting value
u32 n=0;
for(;n<131072;n++/*n+=12*/) //this writes every (12th) byte
{
/*
set_address(&n);
i2cwrite(magic); //write the data
if((u08)n>243) //our next n will be across a page boundary
{
i2cstop();
_delay_loop_2( (u16) ( (float)F_CPU*0.006/4.0 ) );//delay 6 ms for page write
}*/
if(!((u08)n)) //this code is for writing every byte
{
i2cstop();
_delay_loop_2( (u16) ( (float)F_CPU*0.006/4.0 ) );//delay 6 ms for page write
set_address(&n);
}
i2cwrite(magic);
wdt_reset();
}
i2cstop();
}
#endif
I2Cmem.h
#include <util/twi.h> //I2C registers
#include <util/delay_basic.h> //wait delay loop for page write
#include "avrlibtypes.h"
#define SLA_W 0b10100000 //microchip 24LC515
#define SLA_R 0b10100001
#define SCL 100000
#define _AK_ (1<<TWINT)|(1<<TWEN)|(1<<TWEA) //for setting up I2C read with AK and NAK from master
#define _NAK_ (1<<TWINT)|(1<<TWEN)
#define MAGIC_NUMBER 255 //painting value
void init_i2c();
void i2cstart();
void i2cstop();
void i2cwrite(u08 c);
u08 i2cread(u08 AK);
void set_address(u32* address);
void write_data(u08 * c, u08 datasize, u32*address);
void read_data(u08 * destination, u08 datasize, u32*address);
u32 findtop();
void painteeprom();