I needed i2c for a 24LC512 EEPROM for an emulator project I'm working on.
I got sick of the Wire library.
It uses lots of flash/SRAM space, is slow and can't behave well using sequential R/W.
So I wrote my own low level routines for i2C access.
I don't think it uses more than 200bytes of flash.
It can read out the entire 64Kb in one pass and write 64bytes per write access.
The wire libray doesn't even come close.
Anyway, here it is.
// This is the hardware and bitstate definitions for the i2c functions
// They are defined for PORTB on an ATmega
// If you use any other pins than PORTB3-5, change these to reflect the pins used
// It is written by Jan Ostman in 2014 but free for non commercial use
#define SCLoutput asm volatile("sbi 0x04,4\n"); //define SCL as output PORTB bit4
#define SDAoutput asm volatile("sbi 0x04,5\n"); //define SDA as output PORTB bit5
#define SDAinput asm volatile("cbi 0x04,5\n"); //define SDA as input
#define setSCLhigh asm volatile("sbi 0x05,4\n"); //set SCL high
#define setSCLlow asm volatile("cbi 0x05,4\n"); //set SCL low
#define setSDAhigh asm volatile("sbi 0x05,5\n"); //set SCL high
#define setSDAlow asm volatile("cbi 0x05,5\n"); //set SCL low
#define powerPIN asm volatile("sbi 0x04,3\n"); //set flash power pin as output
#define powerON asm volatile("sbi 0x05,3\n"); //Turn on flash power
#define powerOFF asm volatile("cbi 0x05,3\n"); //Turn off flash power
#define delayHALF delayMicroseconds(3); //Constant delay for half bit time
// These are the basic low level i2c functions
void initTWI() { //Initialize the port pins to run i2c
powerPIN //Set the pin for i2c power as output
powerON //Turn on i2c power
SCLoutput //Set SCL as output
SDAoutput //Set SDA as output
setSCLhigh //Set SCL high
setSDAlow //Set SDA low
delayHALF //Wait half bit time
setSDAhigh //Set SDA high for stop condition
delayMicroseconds(20); //Wait for the starting TWI bus to settle
}
void sendTWIstart() { //Send TWI start condition
setSDAlow //Set SDA low for start
delayHALF //Wait half bit time
setSCLlow //Set SCL low for first bit
}
void sendTWIdata(char data) { //Send a byte on the TWI bus
SDAoutput //Set SDA as output
for (uint8_t shift=128;shift;shift>>=1) { //Shift out 8 bits
setSDAlow //Set SDA low for a 0 bit
if (data&shift) setSDAhigh //Set SDA high for a 1 bit
delayHALF //Wait half bit time
setSCLhigh //Set SCL low
delayHALF //Wait half bit time
setSCLlow //Set SCL low
}
setSDAlow //Set SDA low for ACK
delayHALF //Wait half bit time
setSCLhigh //Set SCL high to clock in ACK
delayHALF //Wait half bit time
setSCLlow //Set SCL low
}
uint8_t getTWIdata() { //Recieve a byte from the TWI bus
uint8_t data=0;
SDAinput //Set SDA as input
setSDAhigh //Set SDA with pullup
for (uint8_t shift=128;shift;shift>>=1) { //Shift in 8 bits
delayHALF //Wait half bit time
setSCLhigh //Set SCL high
delayHALF //Wait half bit time
if (PINB&32) data|=shift; //Get the bit
setSCLlow //Set SCL low
}
SDAoutput //Set SDA as output
setSDAlow //Set SDA low for ACK
delayHALF //Wait half bit time
setSCLhigh //Set SCL high
delayHALF //Wait half bit time
setSCLlow //Set SCL low
return data; //Return the byte
}
void sendTWIstop() { //Send TWI stop condition
SDAoutput //Set SDA as output
setSDAlow //Set SDA low for stop
setSCLhigh //Set SCL high for stop
delayHALF //Wait half bit time
setSDAhigh //Set SDA high for stop
}
void setup() {
initTWI();
sendTWIstart();
sendTWIdata(0xA0); //Write 0xAA to location 0x0000 in a 24LC256/512 EEPROM
sendTWIdata(0x00);
sendTWIdata(0x00);
sendTWIdata(0xAA);
sendTWIstop();
Serial.begin (9600);
}
void loop() {
sendTWIstart();
sendTWIdata(0xA0);
sendTWIdata(0x00);
sendTWIdata(0x00);
sendTWIstop();
sendTWIstart();
sendTWIdata(0xA1);
uint8_t temp=getTWIdata();
sendTWIstop();
Serial.print(temp,HEX); //Print back the 0xAA byte
Serial.println();
}