|
|
|
@ -1,5 +1,6 @@
|
|
|
|
|
|
|
|
|
|
#include "i2c.h" |
|
|
|
|
#include "interrupt.h" |
|
|
|
|
|
|
|
|
|
#define I2CBASE 0xE001C000 |
|
|
|
|
|
|
|
|
@ -21,6 +22,11 @@
|
|
|
|
|
|
|
|
|
|
#define SI (IREG(I2CONSET) & SIFLAG) |
|
|
|
|
|
|
|
|
|
void __attribute__((interrupt("IRQ"))) i2c_interrupt_handler(void); |
|
|
|
|
|
|
|
|
|
struct i2c_transaction *i2c_transaction; |
|
|
|
|
int i2c_bytes; |
|
|
|
|
|
|
|
|
|
void init_i2c(void) |
|
|
|
|
{ |
|
|
|
|
IREG(I2CONSET) = 0x40; /* Enable I2C ready for Master Tx */ |
|
|
|
@ -32,17 +38,7 @@ void init_i2c(void)
|
|
|
|
|
IWREG(I2SCLL) = 73; /* ~100kHz */ |
|
|
|
|
IWREG(I2SCLH) = 73; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int i2cstat; |
|
|
|
|
|
|
|
|
|
int i2c_wait(void) |
|
|
|
|
{ |
|
|
|
|
int stat; |
|
|
|
|
while (!SI); |
|
|
|
|
stat = IREG(I2STAT); |
|
|
|
|
i2cstat = stat; |
|
|
|
|
return stat; |
|
|
|
|
interrupt_register(I2C0, i2c_interrupt_handler); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int i2c_conreg(void) |
|
|
|
@ -55,61 +51,110 @@ int i2c_statreg(void)
|
|
|
|
|
return IREG(I2STAT); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool i2c_send_start(void) |
|
|
|
|
bool i2c_busy(void) |
|
|
|
|
{ |
|
|
|
|
IREG(I2CONCLR) = STOFLAG | STAFLAG; |
|
|
|
|
IREG(I2CONSET) = STAFLAG; |
|
|
|
|
switch (i2c_wait()) { |
|
|
|
|
case 0x08: |
|
|
|
|
case 0x10: |
|
|
|
|
return TRUE; |
|
|
|
|
default: |
|
|
|
|
i2c_send_stop(); |
|
|
|
|
return FALSE; |
|
|
|
|
} |
|
|
|
|
return i2c_transaction != NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool i2c_send_address(int addr, bool write) |
|
|
|
|
bool i2c_start_transaction(struct i2c_transaction *t) |
|
|
|
|
{ |
|
|
|
|
return i2c_send_data((addr<<1) + (write?0:1)); |
|
|
|
|
if (i2c_transaction) |
|
|
|
|
return FALSE; |
|
|
|
|
|
|
|
|
|
i2c_transaction = t; |
|
|
|
|
*(t->result) = I2C_IN_PROGRESS; |
|
|
|
|
|
|
|
|
|
/* Paranoia in case we left things in a bad state */ |
|
|
|
|
IREG(I2CONCLR) = STOFLAG | STAFLAG; |
|
|
|
|
|
|
|
|
|
/* Set it all going */ |
|
|
|
|
IREG(I2CONSET) = STAFLAG; |
|
|
|
|
|
|
|
|
|
return TRUE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool i2c_send_data(unsigned int data) |
|
|
|
|
void __attribute__((interrupt("IRQ"))) i2c_interrupt_handler(void) |
|
|
|
|
{ |
|
|
|
|
IREG(I2DAT) = data; |
|
|
|
|
IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG; |
|
|
|
|
switch (i2c_wait()) { |
|
|
|
|
case 0x18: |
|
|
|
|
case 0x28: |
|
|
|
|
case 0x40: |
|
|
|
|
return TRUE; |
|
|
|
|
default: |
|
|
|
|
i2c_send_stop(); |
|
|
|
|
return FALSE; |
|
|
|
|
int stat = IREG(I2STAT); |
|
|
|
|
|
|
|
|
|
if (!i2c_transaction) { |
|
|
|
|
IREG(I2CONSET) = STOFLAG; |
|
|
|
|
IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; |
|
|
|
|
interrupt_clear(); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool i2c_receive_data(unsigned int *data, bool last) |
|
|
|
|
{ |
|
|
|
|
if (!last) |
|
|
|
|
IREG(I2CONSET) = AAFLAG; |
|
|
|
|
IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG | (last?AAFLAG:0); |
|
|
|
|
switch (i2c_wait()) { |
|
|
|
|
case 0x50: |
|
|
|
|
case 0x58: |
|
|
|
|
*data = IREG(I2DAT); |
|
|
|
|
return TRUE; |
|
|
|
|
default: |
|
|
|
|
i2c_send_stop(); |
|
|
|
|
return FALSE; |
|
|
|
|
switch (stat) { |
|
|
|
|
|
|
|
|
|
case 0x08: /* START transmitted */ |
|
|
|
|
case 0x10: /* repeated START transmitted */ |
|
|
|
|
IREG(I2DAT) = i2c_transaction->address; |
|
|
|
|
IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG; |
|
|
|
|
i2c_bytes = 0; |
|
|
|
|
break; |
|
|
|
|
case 0x18: /* SA+W transmitted, ACK received */ |
|
|
|
|
case 0x28: /* data transmitted, ACK received */ |
|
|
|
|
if (i2c_bytes < i2c_transaction->bytes) { |
|
|
|
|
IREG(I2DAT) = i2c_transaction->data[i2c_bytes++]; |
|
|
|
|
IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG; |
|
|
|
|
} else { |
|
|
|
|
*(i2c_transaction->result) = I2C_SUCCESS; |
|
|
|
|
if (i2c_transaction->next) { |
|
|
|
|
i2c_transaction = i2c_transaction->next; |
|
|
|
|
i2c_bytes = 0; |
|
|
|
|
IREG(I2CONSET) = STAFLAG; |
|
|
|
|
IREG(I2CONCLR) = STOFLAG | AAFLAG | SIFLAG; |
|
|
|
|
} else { |
|
|
|
|
i2c_transaction = NULL; |
|
|
|
|
IREG(I2CONSET) = STOFLAG; |
|
|
|
|
IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 0x50: /* data received, ACK returned */ |
|
|
|
|
i2c_transaction->data[i2c_bytes++] = IREG(I2DAT); |
|
|
|
|
/* fall through */ |
|
|
|
|
|
|
|
|
|
case 0x40: /* SA+R transmitted, ACK received */ |
|
|
|
|
if (i2c_bytes < (i2c_transaction->bytes-1)) { |
|
|
|
|
IREG(I2CONSET) = AAFLAG; |
|
|
|
|
IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG; |
|
|
|
|
} else { |
|
|
|
|
IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG
|
|
|
|
|
| AAFLAG; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 0x58: /* data received, NACK returned */ |
|
|
|
|
i2c_transaction->data[i2c_bytes] = IREG(I2DAT); |
|
|
|
|
*(i2c_transaction->result) = I2C_SUCCESS; |
|
|
|
|
if (i2c_transaction->next) { |
|
|
|
|
i2c_transaction = i2c_transaction->next; |
|
|
|
|
i2c_bytes = 0; |
|
|
|
|
IREG(I2CONSET) = STAFLAG; |
|
|
|
|
IREG(I2CONCLR) = STOFLAG | AAFLAG | SIFLAG; |
|
|
|
|
} else { |
|
|
|
|
i2c_transaction = NULL; |
|
|
|
|
IREG(I2CONSET) = STOFLAG; |
|
|
|
|
IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 0x20: /* SA+W transmitted, NACK received */ |
|
|
|
|
case 0x30: /* data transmitted, NACK received */ |
|
|
|
|
case 0x48: /* SA+R transmitted, NACK received */ |
|
|
|
|
case 0x38: /* arbitration lost during SA+W or data */ |
|
|
|
|
case 0x00: /* bus error */ |
|
|
|
|
*(i2c_transaction->result) = I2C_FAIL; |
|
|
|
|
i2c_transaction = NULL; |
|
|
|
|
IREG(I2CONSET) = STOFLAG; |
|
|
|
|
IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
/* We don't handle slave mode */ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void i2c_send_stop(void) |
|
|
|
|
{ |
|
|
|
|
IREG(I2CONCLR) = STAFLAG | AAFLAG; |
|
|
|
|
IREG(I2CONSET) = STOFLAG; |
|
|
|
|
IREG(I2CONCLR) = SIFLAG; |
|
|
|
|
/* Don't think we need to wait here. Could be wrong. */ |
|
|
|
|
interrupt_clear(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|