Select Git revision
i2c_master.h
-
Anders Blomdell authoredAnders Blomdell authored
i2c_master.h 8.49 KiB
#ifndef __I2C_MASTER_H__
#define __I2C_MASTER_H__
#include <util/atomic.h>
#include <util/twi.h>
enum i2c_op {
I2C_R_OP=0x01,
I2C_W_FLASH_OP,
I2C_W_RAM_OP,
I2C_W_1_OP,
I2C_W_2_OP,
I2C_W_3_OP,
I2C_CALL_OP,
I2C_CHAIN_OP,
I2C_END_OP,
};
struct i2c_transcation {
enum i2c_op op:8;
union {
struct i2c_read {
unsigned char address;
unsigned char count;
void volatile *data;
} read;
struct i2c_write_flash {
unsigned char address;
unsigned char count;
const volatile unsigned __flash char *data;
} write_flash;
struct i2c_write_ram {
unsigned char address;
unsigned char count;
const volatile unsigned char *data;
} write_ram;
struct i2c_write {
unsigned char address;
const volatile unsigned char data[3];
} write;
struct i2c_callback {
unsigned char (*function)(const volatile void *context);
const volatile void *context;
} callback;
struct i2c_chain {
const __flash struct i2c_transcation *transaction;
} chain;
};
};
static volatile struct i2c_ctx {
const __flash struct i2c_transcation *transaction;
// unsigned char length;
unsigned char pos;
char status;
} i2c_ctx;
#define I2C_READ(ADDR, COUNT, DATA) \
{.op=I2C_R_OP,{.read={.address=ADDR,.count=COUNT, .data=DATA}}}
#define I2C_WRITE_FLASH(ADDR, COUNT, DATA) \
{.op=I2C_W_FLASH_OP,{.write_flash={.address=ADDR,.count=COUNT,.data=DATA}}}
#define I2C_WRITE_RAM(ADDR, COUNT, DATA) \
{.op=I2C_W_RAM_OP,{.write_ram={.address=ADDR,.count=COUNT,.data=DATA}}}
#define I2C_WRITE_1(ADDR, DATA0) \
{.op=I2C_W_1_OP,{.write={.address=ADDR, \
.data[0]=DATA0}}}
#define I2C_WRITE_2(ADDR, DATA0, DATA1) \
{.op=I2C_W_2_OP,{.write={.address=ADDR, \
.data[0]=DATA0, \
.data[1]=DATA1}}}
#define I2C_WRITE_3(ADDR, DATA0, DATA1, DATA2) \
{.op=I2C_W_3_OP,{.write={.address=ADDR, \
.data[0]=DATA0, \
.data[1]=DATA1, \
.data[2]=DATA2}}}
#define I2C_CALLBACK(FUNC, CONTEXT) \
{.op=I2C_CALL_OP,{.callback={.function=FUNC,.context=CONTEXT}}}
#define I2C_CHAIN(TRANSACTION) \
{.op=I2C_CHAIN_OP,{.chain={.transaction=TRANSACTION}}}
#define I2C_END() \
{.op=I2C_END_OP,{}}
static unsigned char i2c_chain(
const __flash struct i2c_transcation *transaction)
{
i2c_ctx.transaction = transaction;
i2c_ctx.pos = 0;
i2c_ctx.status = 0;
return 0;
}
static unsigned char i2c_start(
const __flash struct i2c_transcation *transaction)
{
i2c_chain(transaction);
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN) | (1<<TWIE);
return 0;
}
/* Return number of bytes in current transaction */
static unsigned char i2c_count()
{
struct i2c_transcation tr = i2c_ctx.transaction[i2c_ctx.pos];
switch (tr.op) {
case I2C_CALL_OP: return 0; // ERROR
case I2C_R_OP: return tr.read.count;
case I2C_W_FLASH_OP: return tr.write_flash.count;
case I2C_W_RAM_OP: return tr.write_ram.count;
case I2C_W_1_OP: return 1;
case I2C_W_2_OP: return 2;
case I2C_W_3_OP: return 3;
case I2C_CHAIN_OP: return 0; // ERROR
case I2C_END_OP: return 0; // ERROR
}
return 0;
}
static unsigned char i2c_get_data(unsigned char index)
{
struct i2c_transcation tr = i2c_ctx.transaction[i2c_ctx.pos];
switch (tr.op) {
case I2C_CALL_OP: return 0; // ERROR
case I2C_R_OP: return 0; // ERROR
case I2C_W_FLASH_OP: return tr.write_flash.data[index];
case I2C_W_RAM_OP: return tr.write_ram.data[index];
case I2C_W_1_OP: return tr.write.data[index];
case I2C_W_2_OP: return tr.write.data[index];
case I2C_W_3_OP: return tr.write.data[index];
case I2C_CHAIN_OP: return 0; // ERROR
case I2C_END_OP: return 0; // ERROR
}
return 0;
}
static void i2c_put_data(unsigned char index, unsigned char data)
{
struct i2c_transcation tr = i2c_ctx.transaction[i2c_ctx.pos];
switch (tr.op) {
case I2C_CALL_OP: break;
case I2C_R_OP: {
if (index < tr.read.count) {
((unsigned char *)tr.read.data)[index] = data;
}
} break;
case I2C_W_FLASH_OP: break;
case I2C_W_RAM_OP: break;
case I2C_W_1_OP: break;
case I2C_W_2_OP: break;
case I2C_W_3_OP: break;
case I2C_CHAIN_OP: break;
case I2C_END_OP: break;
}
}
static char i2c_callback()
{
while (i2c_ctx.status == 0) {
enum i2c_op op = i2c_ctx.transaction[i2c_ctx.pos].op;
if (op == I2C_END_OP) {
break;
} else if (i2c_ctx.transaction[i2c_ctx.pos].op == I2C_CALL_OP) {
struct i2c_callback cb = i2c_ctx.transaction[i2c_ctx.pos].callback;
i2c_ctx.status = cb.function(cb.context);
if (i2c_ctx.status == 0) {
i2c_ctx.pos++;
continue;
}
} else if (i2c_ctx.transaction[i2c_ctx.pos].op == I2C_CHAIN_OP) {
i2c_chain(i2c_ctx.transaction[i2c_ctx.pos].chain.transaction);
continue;
} else {
break;
}
}
return i2c_ctx.status;
}
static char i2c_next()
{
#if 0
putstr("POS: ");
puthex(i2c_ctx.pos);
putstr(" OP: ");
puthex( i2c_ctx.transaction[i2c_ctx.pos].op);
putstr("\r\n");
#endif
if (i2c_ctx.status != 0) {
return i2c_ctx.status;
}
if (i2c_ctx.transaction[i2c_ctx.pos].op != I2C_END_OP) {
i2c_ctx.pos++;
i2c_callback();
}
if (i2c_ctx.status != 0) {
return i2c_ctx.status;
}
return i2c_ctx.transaction[i2c_ctx.pos].op != I2C_END_OP;
}
static char i2c_more(unsigned char index)
{
if (i2c_ctx.status != 0) {
return i2c_ctx.status;
}
return index < i2c_count();
}
static char i2c_idle()
{
char result = 1;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
if (i2c_ctx.transaction &&
i2c_ctx.transaction[i2c_ctx.pos].op != I2C_END_OP) {
result = 0;
}
}
return result;
}
SIGNAL(TWI_vect)
{
// unsigned char twsr = TWSR;
unsigned char twcr = 0x00;
static unsigned char data_pos = 0x00;
// Consume callback operations
if (i2c_callback() != 0) {
// putstr("Error\r\n");
}
switch (TW_STATUS) {
case TW_START:
case TW_REP_START:
{
switch (i2c_ctx.transaction[i2c_ctx.pos].op) {
case I2C_CALL_OP:
case I2C_CHAIN_OP:
case I2C_END_OP:
{
// ERROR
} break;
case I2C_R_OP: {
// send SLA+R
TWDR = (i2c_ctx.transaction[i2c_ctx.pos].read.address << 1) | 1;
} break;
case I2C_W_FLASH_OP: {
// send SLA+W
TWDR = (i2c_ctx.transaction[i2c_ctx.pos].write_flash.address << 1);
} break;
case I2C_W_RAM_OP: {
// send SLA+W
TWDR = (i2c_ctx.transaction[i2c_ctx.pos].write_ram.address << 1);
} break;
case I2C_W_1_OP:
case I2C_W_2_OP:
case I2C_W_3_OP: {
// send SLA+W
TWDR = (i2c_ctx.transaction[i2c_ctx.pos].write.address << 1);
} break;
}
twcr = 0;
data_pos = 0;
} break;
case TW_MT_SLA_ACK:
case TW_MT_DATA_ACK:
{
// Send next byte
if (i2c_more(data_pos)) {
TWDR = i2c_get_data(data_pos);
twcr = 0;
data_pos++;
} else {
if (i2c_next()) {
// Repeated start
twcr = (1 << TWSTA);
} else {
// Done
twcr = (1 << TWSTO);
}
}
} break;
case TW_MR_SLA_ACK:
{
if (i2c_more(data_pos + 1)) {
// Receive byte, send ACK
twcr = (1<<TWEA);
} else {
// Receive last byte, send NOT ACK
twcr = 0;
}
} break;
case TW_MR_DATA_ACK:
case TW_MR_DATA_NACK:
{
// Data received:
i2c_put_data(data_pos, TWDR);
data_pos++;
if (i2c_more(data_pos+1)) {
// Receive byte, send ACK
twcr = (1<<TWEA);
} else if (i2c_more(data_pos)) {
// Receive last byte, send NOT ACK
twcr = 0;
} else if (i2c_next()) {
// Send repeated START
twcr = (1 << TWSTA);
} else {
// Send STOP
twcr = (1<<TWSTO);
}
} break;
default: {
i2c_ctx.status = TW_STATUS | 0x01;
twcr = (1<<TWSTO);
// Done
#if 0
putstr("TWSR: ");
puthex(TW_STATUS);
putstr(" TWCR: ");
puthex(twcr);
putstr("\r\n");
#endif
} break;
}
#if 0
putstr("TWSR: ");
puthex(TW_STATUS);
putstr(" TWCR: ");
puthex(twcr);
putstr("\r\n");
#endif
TWCR = twcr | (1<<TWINT) | (1<<TWEN) |(1<<TWIE);
}
#endif