diff --git a/nametag8_CH592/user/comm/soft_i2c_slave.c b/nametag8_CH592/user/comm/soft_i2c_slave.c index 039262a..d5e8eb7 100644 --- a/nametag8_CH592/user/comm/soft_i2c_slave.c +++ b/nametag8_CH592/user/comm/soft_i2c_slave.c @@ -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; diff --git a/nametag8_CH592/user/comm/soft_i2c_slave.h b/nametag8_CH592/user/comm/soft_i2c_slave.h index dea1800..dfb6aee 100644 --- a/nametag8_CH592/user/comm/soft_i2c_slave.h +++ b/nametag8_CH592/user/comm/soft_i2c_slave.h @@ -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,