201 lines
3.4 KiB
C
201 lines
3.4 KiB
C
#include <hwo/i2c.h>
|
|
#include <hwo/utils.h>
|
|
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/eeprom.h>
|
|
|
|
#include <sys/mutex.h>
|
|
#include <sys/atomic.h>
|
|
#include <sys/errno.h>
|
|
|
|
struct i2c i2c;
|
|
|
|
#define TWCMD(cmd) TWCR = ((TWCR & 0x0F) | _BV(TWINT) | _BV(TWIE) | cmd)
|
|
|
|
static inline void _i2c_send_slave(uint8_t slave) { TWDR = slave; TWCMD( 0 ); };
|
|
static inline void _i2c_send_start(void) { TWCMD( _BV(TWSTA) ); };
|
|
static inline void _i2c_send_restart(void) { TWCMD( _BV(TWSTA) ); };
|
|
|
|
static void _i2c_send_stop(void) {
|
|
TWCMD( _BV(TWSTO) );
|
|
if (i2c.owner)
|
|
thread_wake(i2c.owner);
|
|
};
|
|
|
|
static inline void _i2c_send_byte(uint8_t by) {
|
|
TWDR = by;
|
|
TWCMD(0);
|
|
};
|
|
|
|
void i2c_lock(void) {
|
|
mutex_lock(&i2c.mutex);
|
|
};
|
|
void i2c_release(void) {
|
|
mutex_release(&i2c.mutex);
|
|
};
|
|
|
|
void i2c_init(uint32_t clk)
|
|
{
|
|
uint32_t twbr = ((__freq_cpu / clk) - 16) / 2;
|
|
uint8_t twps = 0;
|
|
|
|
while (twbr > 255)
|
|
{
|
|
twps++;
|
|
twbr /= 4;
|
|
};
|
|
|
|
TWBR = twbr & 0xff;
|
|
TWSR = twps;
|
|
TWCR = _BV(TWEN) | _BV(TWIE);
|
|
|
|
i2c.clock = clk;
|
|
|
|
};
|
|
|
|
int i2c_default_callback(void *p,int n,int ch){
|
|
if (ch == -1) {
|
|
if (n >= i2c.length){
|
|
return -1;
|
|
};
|
|
return i2c.buffer[ n ];
|
|
} else {
|
|
i2c.buffer[ n ] = ch;
|
|
|
|
if ((n+2) == i2c.length){
|
|
return I2C_CBR_NACK;
|
|
} else if ((n+1) >= i2c.length){
|
|
return I2C_CBR_STOP;
|
|
} else {
|
|
return I2C_CBR_CONT;
|
|
};
|
|
};
|
|
};
|
|
|
|
int i2c_transfer(uint8_t slave,uint8_t *buffer,uint8_t length)
|
|
{
|
|
MUTEXED(&i2c.mutex);
|
|
|
|
i2c.length = length;
|
|
i2c.buffer = buffer;
|
|
|
|
return i2c_transfer_ex(slave, i2c_default_callback, NULL);
|
|
};
|
|
|
|
int i2c_transfer_ex(uint8_t slave,i2c_callback callback,void *p)
|
|
{
|
|
MUTEXED(&i2c.mutex);
|
|
|
|
// Warten auf freie Hardware
|
|
while (TWCR & 0xF0){
|
|
//wait_ms(1);
|
|
yield();
|
|
};
|
|
|
|
i2c.slave = slave;
|
|
i2c.ptr = 0;
|
|
i2c.error = 0;
|
|
i2c.callback= callback;
|
|
i2c.p = p;
|
|
i2c.owner = current_thread();
|
|
|
|
{
|
|
ATOMIC
|
|
_i2c_send_start();
|
|
thread_sleep(NULL);
|
|
};
|
|
|
|
i2c.owner = NULL;
|
|
|
|
return i2c.error ? i2c.error : i2c.ptr;
|
|
};
|
|
|
|
void _i2c_cancel(void)
|
|
{
|
|
i2c.error = -EFAIL;
|
|
_i2c_send_stop();
|
|
};
|
|
|
|
void _i2c_send(void)
|
|
{
|
|
int n;
|
|
|
|
if (!i2c.callback){
|
|
i2c.error = -ENULLPTR;
|
|
_i2c_send_stop();
|
|
} else {
|
|
n = i2c.callback( i2c.p, i2c.ptr, -1 );
|
|
if (n<0){
|
|
i2c.error = ESUCCESS;
|
|
_i2c_send_stop();
|
|
} else {
|
|
i2c.ptr++;
|
|
_i2c_send_byte( n & 0xFF );
|
|
};
|
|
};
|
|
};
|
|
|
|
void _i2c_recv(void)
|
|
{
|
|
int ch = TWDR;
|
|
|
|
if (!i2c.callback){
|
|
i2c.error = -ENULLPTR;
|
|
_i2c_send_stop();
|
|
} else {
|
|
ch = i2c.callback( i2c.p, i2c.ptr, ch );
|
|
i2c.ptr++;
|
|
|
|
switch (ch){
|
|
case I2C_CBR_CONT:
|
|
TWCMD(_BV(TWEA));
|
|
break;
|
|
case I2C_CBR_NACK:
|
|
TWCMD(0);
|
|
break;
|
|
case I2C_CBR_STOP:
|
|
i2c.error = ESUCCESS;
|
|
_i2c_send_stop();
|
|
break;
|
|
default:
|
|
i2c.error = -EPARAM;
|
|
_i2c_send_stop();
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
|
|
VECT(TWI_vect){
|
|
switch (TWSR & 0xF8)
|
|
{
|
|
case 0x08: // START sent 0b00001000
|
|
case 0x10: // Repeated START sent 0b00010000
|
|
_i2c_send_slave( i2c.slave );
|
|
break;
|
|
case 0x18: // SLA+W sent + ACK 0b00011000
|
|
case 0x28: // Data sent + ACK 0b00101000
|
|
_i2c_send();
|
|
break;
|
|
case 0x20: // SLA+W sent + N-ACK 0b00100000
|
|
case 0x30: // Data sent + N-ACK 0b00110000
|
|
case 0x38: // ARB LOST / SLA+R N-ACK 0b00111000
|
|
case 0x48: // SLA+R N-ACK 0b01001000
|
|
_i2c_cancel();
|
|
break;
|
|
case 0x40: // SLA+R ACK
|
|
TWCMD(_BV(TWEA));
|
|
break;
|
|
case 0x50: // DATA rcvd + ACK
|
|
_i2c_recv();
|
|
break;
|
|
case 0x58: // Data rcvd + NACK
|
|
_i2c_recv();
|
|
break;
|
|
default:
|
|
_i2c_cancel();
|
|
i2c.error = -EUNKNOWN;
|
|
break;
|
|
};
|
|
};
|