I've been implementing hardware I2C in the maple library; my code should soon be visible in our github code repository on the development branch ("master" not "stable") at github.com/leaflabs/libmaple/tree/master. Once this is complete I will port the Wire arduino library over so that when using the IDE, programmers can access i2c the way they are used to. But the lower level implementation will also be available and I was wondering if anybody had any comments or requests on our implementation...
I spent a long time trying to come up with a simple but complete implementation that would allow any possible master combined message to be sent. This would require either a big data structure that would hold an arbitrary sequence of read buffers and write data, or a lot of callbacks that would wait for user code to specify what it wanted to read/write after every slave ACK. I decided to scrap this and just allow all read or all write master messages; the user code will send multiple messages to, eg, WRITE a memory location to an EEPROM chip and then READ out data sequentially; this seems to work across multiple messages for most i2c devices? This would cut down the maximum theoretical bandwidth for complicated read/write sequences, but I figure bandwidth will mostly matter for long undivided reads and writes. Any thoughts?
One feature we could add is a "soft" i2c library that would bit bang the protocol on any two open GPIO pins; this would be much slower and consume more system resources but would add a lot of flexibility; I know the soft SPI and USART implementations on the arduino get a lot of use. Is this worth our development time?
Here's what i've got now. Slave functionality is handled by first setting an address (which enables ACK) and configuring RX and TX callbacks. There are also optional BEGIN and END callbacks which get called after the slave address is matched and ACK'ed and the message ends; these could be used to configure/reset buffers or blink a status LED or whatever. The RX and TX callbacks are called byte by byte so user code will need to implement any buffering; if they are slow SCL will get stretched which is fine but kills the bandwidth. The master read/write functions set up buffers and then return; usercode can check the status of the read/write with the is_busy function. In the following i2c_num will be just a uint8 and will select the port (I2C_PORT1 or I2C_PORT2).
// freq sets the master SCL frequency; i've tested up to 800khz successfully
void i2c_init(uint32 i2c_num, uint32 freq);
void i2c_disable(uint32 i2c_num);
uint8 i2c_busy(uint32 i2c_num); // 1=still busy, 0=ready/done
// these block until not busy or timeout
void i2c_master_read(uint32 port, uint32 addr, uint32 *data, uint32 len);
void i2c_master_write(uint32 port, uint32 addr, uint32* data,
uint32 len);
void i2c_slave_set_addr(uint32 port, uint32 addr); // this enables ACK
void i2c_slave_set_begin_callback(uint32 port, void (*function)(void)); // optional
void i2c_slave_set_rx_callback(uint32 port, void (*function)(uint8*));
void i2c_slave_set_tx_callback(uint32 port, uint8 (*function)(void));
void i2c_slave_set_end_callback(uint32 port, void (*function)(void)); // optional