added bus state tracking, hopefully fixed some clock stretching on soft i2c slave
This commit is contained in:
parent
52435ea3f0
commit
a7fb413655
|
@ -17,7 +17,7 @@
|
|||
|
||||
|
||||
#define I2CSS_ERR_TIMEOUT 10 // timeout in bit-times before i2c slave releases control
|
||||
#define I2CSS_IDLE_TIMEOUT 3 // idle timeout required in bit-times before i2c slave can be addressed again
|
||||
#define I2CSS_IDLE_TIMEOUT 4 // idle timeout required in bit-times before i2c slave can be addressed again
|
||||
|
||||
|
||||
// todo: use with struct instead of hard-coding to allow for multiple slaves, but make it fast too
|
||||
|
@ -49,9 +49,18 @@
|
|||
|
||||
|
||||
|
||||
enum {
|
||||
BUS_STATE_IDLE,
|
||||
BUS_STATE_BUSY
|
||||
};
|
||||
|
||||
|
||||
|
||||
static uint16_t bit_time;
|
||||
static uint16_t idle_timeout = 0;
|
||||
|
||||
static uint8_t bus_state = BUS_STATE_IDLE;
|
||||
|
||||
|
||||
|
||||
static inline int8_t i2css_wait_for_scl_hi()
|
||||
|
@ -64,7 +73,7 @@ static inline int8_t i2css_wait_for_scl_hi()
|
|||
|
||||
static inline int8_t i2css_wait_for_scl_lo()
|
||||
{
|
||||
uint8_t old_data = SDA_GET();
|
||||
uint32_t old_data = SDA_GET();
|
||||
int16_t timeout = I2CSS_ERR_TIMEOUT * bit_time;
|
||||
|
||||
timeout--; // compensation
|
||||
|
@ -96,8 +105,12 @@ static inline int8_t i2css_ack(uint16_t bit_time)
|
|||
|
||||
int8_t i2css_idle_init(struct I2CSoftSlave *slave)
|
||||
{
|
||||
// on ch592, timer supports falling to falling edge counting
|
||||
// we could use this to interrupt after enough idle time, but...
|
||||
// the pins were misplaced at some point, lol. oops.
|
||||
|
||||
// set up a hardware timer to tick our idle timer
|
||||
// todo
|
||||
|
||||
// idle_timeout = slave->bit_time * I2CSS_IDLE_TIMEOUT;
|
||||
}
|
||||
|
||||
|
@ -134,16 +147,15 @@ int8_t i2css_read(struct I2CSoftSlave *slave)
|
|||
return I2CSS_NO_READ_FUNCTION;
|
||||
}
|
||||
|
||||
// clock low for first bit / stretch, get data to send to master
|
||||
SCL_OUTLO();
|
||||
dat = slave->rd_cb();
|
||||
|
||||
// release clock, wait for clock to be low by the master
|
||||
SCL_IN_HI();
|
||||
// wait for clock to go low
|
||||
ret = i2css_wait_for_scl_lo();
|
||||
if (ret == I2CSS_SCL_TIMEOUT) return I2CSS_READ_TIMEOUT;
|
||||
if (ret) return ret;
|
||||
|
||||
// then stretch the clock while we prepare data
|
||||
SCL_OUTLO();
|
||||
dat = slave->rd_cb();
|
||||
|
||||
// send data to master
|
||||
for (i = 8; i; i--) {
|
||||
if (dat &= 0x80) { SDA_IN_HI(); }
|
||||
|
@ -151,6 +163,9 @@ int8_t i2css_read(struct I2CSoftSlave *slave)
|
|||
|
||||
dat <<= 1;
|
||||
|
||||
// release clock if it was held high
|
||||
SCL_IN_HI();
|
||||
|
||||
if (i2css_wait_for_scl_hi()) return I2CSS_READ_ERROR;
|
||||
|
||||
ret = i2css_wait_for_scl_lo();
|
||||
|
@ -184,6 +199,11 @@ int8_t i2css_write(struct I2CSoftSlave *slave)
|
|||
return I2CSS_NO_WRITE_FUNCTION;
|
||||
}
|
||||
|
||||
// wait for clock to go low
|
||||
ret = i2css_wait_for_scl_lo();
|
||||
if (ret == I2CSS_SCL_TIMEOUT) return I2CSS_WRITE_TIMEOUT;
|
||||
if (ret) return ret;
|
||||
|
||||
// get data from master
|
||||
for (i = 8; i; i--) {
|
||||
if (i2css_wait_for_scl_hi()) return I2CSS_WRITE_ERROR;
|
||||
|
@ -208,14 +228,6 @@ int8_t i2css_write(struct I2CSoftSlave *slave)
|
|||
SCL_IN_HI();
|
||||
if (i2css_wait_for_scl_hi()) return I2CSS_WRITE_ERROR;
|
||||
|
||||
// wait for clock to go low
|
||||
ret = i2css_wait_for_scl_lo();
|
||||
if (ret == I2CSS_SCL_TIMEOUT) return I2CSS_WRITE_TIMEOUT;
|
||||
if (ret) return ret;
|
||||
|
||||
// then stretch
|
||||
SCL_OUTLO();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -262,9 +274,15 @@ static inline int8_t i2css_process(struct I2CSoftSlave *slave)
|
|||
if (ret) return ret;
|
||||
|
||||
// stretch clock while preparing slave to receive
|
||||
ret = i2css_wait_for_scl_lo();
|
||||
if (ret == I2CSS_SCL_TIMEOUT) return I2CSS_SCL_NEVER_WENT_LOW;
|
||||
if (ret) return ret;
|
||||
SCL_OUTLO();
|
||||
|
||||
// we're good to go now. do initial prep for data transfer
|
||||
if (slave->start_cb) slave->start_cb();
|
||||
|
||||
// then transfer data
|
||||
switch (rx_addr & 1) {
|
||||
case 0: { // master is writing to us
|
||||
do {
|
||||
|
@ -274,14 +292,15 @@ static inline int8_t i2css_process(struct I2CSoftSlave *slave)
|
|||
}
|
||||
case 1: { // master is reading from us
|
||||
do {
|
||||
// stretch clock
|
||||
SCL_OUTLO();
|
||||
ret = i2css_read(slave);
|
||||
} while (!ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SDA_IN_HI();
|
||||
SCL_IN_HI();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -291,6 +310,8 @@ int8_t i2css_blocking(struct I2CSoftSlave *slave)
|
|||
int8_t ret;
|
||||
int8_t start;
|
||||
|
||||
bus_state = BUS_STATE_BUSY;
|
||||
|
||||
do {
|
||||
ret = i2css_process(slave);
|
||||
start = 0;
|
||||
|
@ -298,6 +319,11 @@ int8_t i2css_blocking(struct I2CSoftSlave *slave)
|
|||
// determine which
|
||||
// high = stop, low = start
|
||||
start = SDA_GET() ? 0 : 1;
|
||||
|
||||
// if stop detected, bus is idle NOW
|
||||
if (!start) {
|
||||
bus_state = BUS_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
} while (start);
|
||||
|
||||
|
@ -311,25 +337,21 @@ int8_t i2css_start(struct I2CSoftSlave *slave)
|
|||
int8_t ret;
|
||||
uint8_t scl;
|
||||
|
||||
if (idle_timeout) {
|
||||
if (bus_state != BUS_STATE_IDLE) {
|
||||
// we're still waiting for bus idle time before we reply to requests
|
||||
// this will now reset the timeout
|
||||
if (idle_timeout < I2CSS_IDLE_TIMEOUT) {
|
||||
idle_timeout = I2CSS_IDLE_TIMEOUT;
|
||||
}
|
||||
i2css_idle_init(slave);
|
||||
|
||||
return I2CSS_IN_TIMEOUT;
|
||||
return I2CSS_BUS_BUSY;
|
||||
}
|
||||
|
||||
// idle our clock and check for start/stop
|
||||
// tristate our clock (should be already) and check for start/stop
|
||||
SCL_IN_HI();
|
||||
scl = SCL_GET();
|
||||
|
||||
// module will use this bit time for this communication
|
||||
bit_time = slave->bit_time;
|
||||
|
||||
// if SCL is high, this may be a start condition.
|
||||
if (scl) {
|
||||
bit_time = slave->bit_time;
|
||||
ret = i2css_blocking(slave);
|
||||
} else ret = I2CSS_BUS_BUSY;
|
||||
|
||||
|
|
|
@ -36,12 +36,13 @@ enum I2CSoftSlave_Error {
|
|||
I2CSS_ADDR_READ_ERROR = -2, // could be timeout, start or stop condition detected
|
||||
I2CSS_ADDR_READ_TIMEOUT = -3,
|
||||
I2CSS_ADDR_READ_LASTBIT_TIMEOUT = -4,
|
||||
I2CSS_ACK_ERROR = -5, // could be timeout, start or stop condition detected
|
||||
I2CSS_ACK_TIMEOUT = -6,
|
||||
I2CSS_READ_ERROR = -7,
|
||||
I2CSS_READ_TIMEOUT = -8,
|
||||
I2CSS_WRITE_ERROR = -9,
|
||||
I2CSS_WRITE_TIMEOUT = -10,
|
||||
I2CSS_SCL_NEVER_WENT_LOW = -5,
|
||||
I2CSS_ACK_ERROR = -6, // could be timeout, start or stop condition detected
|
||||
I2CSS_ACK_TIMEOUT = -7,
|
||||
I2CSS_READ_ERROR = -8,
|
||||
I2CSS_READ_TIMEOUT = -9,
|
||||
I2CSS_WRITE_ERROR = -10,
|
||||
I2CSS_WRITE_TIMEOUT = -11,
|
||||
I2CSS_NO_READ_FUNCTION = -32,
|
||||
I2CSS_NO_WRITE_FUNCTION = -33,
|
||||
I2CSS_SCL_TIMEOUT = -126,
|
||||
|
|
Loading…
Reference in New Issue