if it helps had similar problem today as I needed I2C for my ATMega328P and there is no I2C support in my ASF for 8bit AVR (I am used to AVR32 UC3 platform where is all much easier) so I decided to write my own I2C lib as I found no working or reliable lib for it...
Here the I2C.h lib:
//-----------------------------------------------------------------------------
//--- I2C lib for ATMega328P ver: 1.02 ----------------------------------------
//-----------------------------------------------------------------------------
#ifndef _I2C_h
#define _I2C_h
//-----------------------------------------------------------------------------
volatile static bool _I2C_initiated=false;
void TWIStart(void)
{
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);
}
//-----------------------------------------------------------------------------
void TWIStop(void)
{
TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
cpu_delay_us(10,clk_cpu); // some recover wait
}
//-----------------------------------------------------------------------------
void TWIWrite(U8 u8data)
{
TWDR = u8data;
TWCR = (1<<TWINT)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);
}
//-----------------------------------------------------------------------------
U8 TWIReadACK(void)
{
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
while ((TWCR & (1<<TWINT)) == 0);
return TWDR;
}
//-----------------------------------------------------------------------------
U8 TWIReadNACK(void)
{
TWCR = (1<<TWINT)|(1<<TWEN);
while ((TWCR & (1<<TWINT)) == 0);
return TWDR;
}
//-----------------------------------------------------------------------------
#define TWIGetStatus (TWSR&0xF8)
//-----------------------------------------------------------------------------
void I2C_init(U32 clk_i2c) // init I2C with clk_i2c baudrate
{
_I2C_initiated=true;
// clk_i2c=clk_cpu/(16+2*TWBR*prescaler)
U32 d=((clk_cpu>>1)/clk_i2c)-8; // TWBR*prescaler
U32 p=d>>8; if (d&255!=0) p++; // prescaler = ceil(d/256)
if (p>3){ p=3; d=255; } // too low freq, set lowest possible
else if (p) d/=p;
TWSR = (d>>8)&3; // set prescaler, clear flags
TWBR = d&255; // set clock
TWCR = (1<<TWEN); // enable TWI
}
//-----------------------------------------------------------------------------
bool I2C_probe(U8 adr) // probe slave with address adr return true if present.
{
bool ret=true;
TWIStart(); if (TWIGetStatus!=0x08) ret=false; // Start OK?
TWIWrite((adr<<1)|0); if (TWIGetStatus!=0x18) ret=false; // ACK received after address+R/W passed?
TWIStop();
return ret;
}
//-----------------------------------------------------------------------------
bool I2C_write(U8 adr,U8 *dat,U8 siz) // send dat[siz] data to slave adr
{
TWIStart(); if (TWIGetStatus!=0x08) return false;
TWIWrite((adr<<1)|0); if (TWIGetStatus != 0x18) return false;
for (;siz;dat++,siz--)
{
TWIWrite(*dat); if (TWIGetStatus != 0x28) return false;
}
TWIStop();
return true;
}
//-----------------------------------------------------------------------------
U8 I2C_read(U8 adr,U8 *dat,U8 siz) // read dat[siz] data from slave adr (untested yet!!!)
{
TWIStart(); if (TWIGetStatus!=0x08) return false;
TWIWrite((adr<<1)|1); if (TWIGetStatus != 0x40) return false;
for (;siz;dat++,siz--)
{
if (siz){ *dat=TWIReadACK(); if (TWIGetStatus != 0x50) return false; }
else { *dat=TWIReadNACK(); if (TWIGetStatus != 0x58) return false; }
}
for (;siz;dat++,siz--) *dat=0;
TWIStop();
return true;
}
//-----------------------------------------------------------------------------
#endif
//-----------------------------------------------------------------------------
It requires cpu_delay_us
routine (add it with ASF wizard) and also the used CPU clock defined prior to include of this for example mine:
#define clk_cpu 16000000
#include "I2C.h"
I tested/debugged probing and sending data (with LCD1602 + I2C expander) and reading data (Honeywell Pressure Sensor) to slaves. Looks like its working well.
Here typical usage:
// init I2C to 400KHz baudrate
I2C_init(400000);
// probe first I2C device (and obtain its address)
volatile U8 LCD_I2C_adr=0;
for (LCD_I2C_adr=1;LCD_I2C_adr<128;LCD_I2C_adr++)
if (I2C_probe(LCD_I2C_adr))
break;
// send some packet
U8 packet[]={1,2,3,4,5}; // some packet
I2C_write(LCD_I2C_adr,packet,sizeof(packet));
// read some packet
I2C_read(LCD_I2C_adr,packet,sizeof(packet));
The lib itself is simple blocking without using interrupts but its a good start point...
Here example of the sensor reading:
// 015PG2A3 H 6329-03N
// 1KHz analog update
// 2KHz digital update
// preasure 14 bit
// temperature 11 bit
// power up 5ms
// I2C (adr=40) SPI Analog
// GND 1 8 NC GND 1 8 NC NC 1 8 NC
// Vcc 2 [ ] 7 NC Vcc 2 [ ] 7 NC Vcc 2 [ ] 7 NC
// SDA 3 [ ] 6 NC MISO 3 [ ] 6 NC Vout 3 [ ] 6 NC
// SCL 4 5 NC SCLK 4 5 SS GND 4 5 NC
volatile U8 sensor_I2C_adr=0; // I2C sensor 7bit address
U8 sensor_read(int32_t *t,int32_t *p) // update t,p and return status:
{ // 0 normal
int32_t a; // 1 command mode
U8 status,buf[4]; // 2 no new data yet (called too often)
I2C_read(sensor_I2C_adr,buf,4); // 3 diagnostics
status=(buf[0]>>6)&3;
a = buf[0] &63; a<<=8; a&=0xFF00;
a|= buf[1];
// preasure [0.01 Pa]
a-=0x0666; a*=689476<<1; a/=0x3999-0x0666; /*a-=689476;*/ *p=a;
a = buf[2]; a<<=3; a&=0xFFF8;
a|=(buf[3]>>5)& 7;
// temp [0.1 C]
a*=2000; a/=2047; a-=500; *t=a;
return status;
}