diff --git a/lib/i2c_master.c b/lib/i2c_master.c index c27172f..a65bd31 100644 --- a/lib/i2c_master.c +++ b/lib/i2c_master.c @@ -345,6 +345,76 @@ try: return to_return; } +/** wait until stop is sent and bus is released + * @param[in] i2c I²C base address + * @return I²C return code + */ +static enum i2c_master_rc i2c_master_wait_stop(uint32_t i2c) +{ + cm3_assert(I2C1 == i2c || I2C2 == i2c); + + enum i2c_master_rc to_return = I2C_MASTER_RC_NONE; // return code + // prepare timer in case the peripheral hangs on sending stop condition (see errata 2.14.4 Wrong behavior of I²C peripheral in master mode after a misplaced Stop) + systick_counter_disable(); // disable SysTick to reconfigure it + systick_set_frequency(500, rcc_ahb_frequency); // set timer to 2 ms (that should be long enough to send a stop condition) + systick_clear(); // reset SysTick (set to 0) + systick_interrupt_disable(); // disable interrupt to prevent ISR to read the flag + systick_get_countflag(); // reset flag (set when counter is going for 1 to 0) + bool timeout = false; // remember if the timeout has been reached + systick_counter_enable(); // start timer + while ((I2C_CR1(i2c) & I2C_CR1_STOP) && !(I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) && !timeout) { // wait until stop condition is accepted and cleared + timeout |= systick_get_countflag(); // verify if timeout has been reached + } + if (I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) { + to_return = I2C_MASTER_RC_BUS_ERROR; + } + while ((I2C_SR2(i2c) & I2C_SR2_MSL) && !(I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) && !timeout) { // wait until bus released (non master mode) + timeout |= systick_get_countflag(); // verify if timeout has been reached + } + if (I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) { + to_return = I2C_MASTER_RC_BUS_ERROR; + } + while ((I2C_SR2(i2c) & I2C_SR2_BUSY) && !(I2C_SR1(i2c) & (I2C_SR1_BERR)) && !timeout) { // wait until peripheral is not busy anymore + timeout |= systick_get_countflag(); // verify if timeout has been reached + } + if (I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) { + to_return = I2C_MASTER_RC_BUS_ERROR; + } + while ((0 == gpio_get(GPIO_PORT_SCL(i2c), GPIO_PIN_SCL(i2c)) || 0 == gpio_get(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c))) && !timeout) { // wait until lines are really high again + timeout |= systick_get_countflag(); // verify if timeout has been reached + } + + if (timeout) { // I2C_CR1_STOP could also be used to detect a timeout, but I'm not sure when + if (I2C_MASTER_RC_NONE == to_return) { + to_return = I2C_MASTER_RC_TIMEOUT; // indicate timeout only when no more specific error has occurred + } + I2C_CR1(i2c) |= I2C_CR1_SWRST; // assert peripheral reset + I2C_CR1(i2c) &= ~I2C_CR1_SWRST; // release peripheral reset + } + systick_counter_disable(); // we don't need to timer anymore + return to_return; +} + +enum i2c_master_rc i2c_master_stop(uint32_t i2c) +{ + cm3_assert(I2C1 == i2c || I2C2 == i2c); + + // sanity check + if (!(I2C_SR2(i2c) & I2C_SR2_BUSY)) { // release is not busy + return I2C_MASTER_RC_NONE; // bus has probably already been released + } + if (I2C_CR1(i2c) & (I2C_CR1_START | I2C_CR1_STOP)) { // ensure start or stop operations are not in progress + return I2C_MASTER_RC_START_STOP_IN_PROGESS; + } + + if (!((I2C_SR2(i2c) & I2C_SR2_TRA))) { // if we are in receiver mode + i2c_disable_ack(i2c); // disable ACK to be able to close the communication + } + + i2c_send_stop(i2c); // send stop to release bus + return i2c_master_wait_stop(i2c); +} + enum i2c_master_rc i2c_master_select_slave(uint32_t i2c, uint16_t slave, bool address_10bit, bool write) { cm3_assert(I2C1 == i2c || I2C2 == i2c); @@ -467,7 +537,7 @@ enum i2c_master_rc i2c_master_read(uint32_t i2c, uint8_t* data, size_t data_size data[i] = i2c_get_data(i2c); // read received byte } - return I2C_MASTER_RC_NONE; + return i2c_master_wait_stop(i2c); } enum i2c_master_rc i2c_master_write(uint32_t i2c, const uint8_t* data, size_t data_size) @@ -513,54 +583,6 @@ enum i2c_master_rc i2c_master_write(uint32_t i2c, const uint8_t* data, size_t da return I2C_MASTER_RC_NONE; } -enum i2c_master_rc i2c_master_stop(uint32_t i2c) -{ - cm3_assert(I2C1 == i2c || I2C2 == i2c); - - // sanity check - if (!(I2C_SR2(i2c) & I2C_SR2_BUSY)) { // release is not busy - return I2C_MASTER_RC_NONE; // bus has probably already been released - } - if (I2C_CR1(i2c) & (I2C_CR1_START | I2C_CR1_STOP)) { // ensure start or stop operations are not in progress - return I2C_MASTER_RC_START_STOP_IN_PROGESS; - } - - if (!((I2C_SR2(i2c) & I2C_SR2_TRA))) { // if we are in receiver mode - i2c_disable_ack(i2c); // disable ACK to be able to close the communication - } - - enum i2c_master_rc to_return = I2C_MASTER_RC_NONE; - // prepare timer in case the peripheral hangs on sending stop condition (see errata 2.14.4 Wrong behavior of I²C peripheral in master mode after a misplaced Stop) - systick_counter_disable(); // disable SysTick to reconfigure it - systick_set_frequency(500, rcc_ahb_frequency); // set timer to 2 ms (that should be long enough to send a stop condition) - systick_clear(); // reset SysTick (set to 0) - systick_interrupt_disable(); // disable interrupt to prevent ISR to read the flag - systick_get_countflag(); // reset flag (set when counter is going for 1 to 0) - bool timeout = false; // remember if the timeout has been reached - i2c_send_stop(i2c); // send stop to release bus - systick_counter_enable(); // start timer - i2c_send_stop(i2c); // send stop to release bus - while ((I2C_CR1(i2c) & I2C_CR1_STOP) && !(I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) && !timeout) { // wait until stop condition is accepted and cleared - timeout |= systick_get_countflag(); // verify if timeout has been reached - } - if (I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) { - to_return = I2C_MASTER_RC_BUS_ERROR; - } - while ((I2C_SR2(i2c) & I2C_SR2_MSL) && !(I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) && !timeout) { // wait until bus released (non master mode) - timeout |= systick_get_countflag(); // verify if timeout has been reached - } - if (I2C_SR1(i2c) & (I2C_SR1_BERR | I2C_SR1_ARLO)) { - to_return = I2C_MASTER_RC_BUS_ERROR; - } - - if (timeout) { - I2C_CR1(i2c) |= I2C_CR1_SWRST; // assert peripheral reset - I2C_CR1(i2c) &= ~I2C_CR1_SWRST; // release peripheral reset - } - systick_counter_disable(); // we don't need to timer anymore - return to_return; -} - enum i2c_master_rc i2c_master_slave_read(uint32_t i2c, uint16_t slave, bool address_10bit, uint8_t* data, size_t data_size) { cm3_assert(I2C1 == i2c || I2C2 == i2c); @@ -575,15 +597,19 @@ enum i2c_master_rc i2c_master_slave_read(uint32_t i2c, uint16_t slave, bool addr goto error; } if (NULL != data && data_size > 0) { // only read data if needed - rc = i2c_master_read(i2c, data, data_size); + rc = i2c_master_read(i2c, data, data_size); // read data (includes stop) if (I2C_MASTER_RC_NONE != rc) { goto error; } + } else { + i2c_master_stop(i2c); // sent stop condition } rc = I2C_MASTER_RC_NONE; // all went well error: - i2c_master_stop(i2c); // sent stop condition + if (I2C_MASTER_RC_NONE != rc) { + i2c_master_stop(i2c); // sent stop condition + } return rc; } @@ -685,8 +711,7 @@ enum i2c_master_rc i2c_master_address_write(uint32_t i2c, uint16_t slave, bool a } } - rc = I2C_MASTER_RC_NONE; // all went well error: - i2c_master_stop(i2c); // sent stop condition + rc = i2c_master_stop(i2c); // sent stop condition return rc; } diff --git a/lib/i2c_master.h b/lib/i2c_master.h index 80684ab..ce89859 100644 --- a/lib/i2c_master.h +++ b/lib/i2c_master.h @@ -30,6 +30,8 @@ enum i2c_master_rc { I2C_MASTER_RC_NOT_READY, /**< slave is not read (previous operations has been NACKed) */ I2C_MASTER_RC_NAK, /**< not acknowledge received */ I2C_MASTER_RC_BUS_ERROR, /**< an error on the I²C bus occurred */ + I2C_MASTER_RC_TIMEOUT, /**< a timeout has occurred because an operation has not completed in the expected time */ + I2C_MASTER_RC_OTHER, /** any other error (does not have to be I²C related) */ }; /** setup I²C peripheral