diff --git a/targets/TARGET_STM/TARGET_STM32F4/i2c_api.c b/targets/TARGET_STM/TARGET_STM32F4/i2c_api.c index ce49be26e6..857847c627 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/i2c_api.c +++ b/targets/TARGET_STM/TARGET_STM32F4/i2c_api.c @@ -29,6 +29,7 @@ */ #include "mbed_assert.h" #include "i2c_api.h" +#include "platform/wait_api.h" #if DEVICE_I2C @@ -41,6 +42,18 @@ not remain stuck if the I2C communication is corrupted. */ #define FLAG_TIMEOUT ((int)0x1000) #define LONG_TIMEOUT ((int)0x8000) +/* Timeout values are based on core clock and I2C clock. + The BYTE_TIMEOUT is computed as twice the number of cycles it would + take to send 10 bits over I2C. Most Flags should take less than that. + This is for immediate FLAG or ACK check. +*/ +#define BYTE_TIMEOUT ((SystemCoreClock / handle->Init.ClockSpeed) * 2 * 10) +/* Timeout values based on I2C clock. + The BYTE_TIMEOUT_US is computed as 3x the time in us it would + take to send 10 bits over I2C. Most Flags should take less than that. + This is for complete transfers check. +*/ +#define BYTE_TIMEOUT_US ((SystemCoreClock / handle->Init.ClockSpeed) * 3 * 10) #if DEVICE_I2C_ASYNCH #define I2C_S(obj) (struct i2c_s *) (&((obj)->i2c)) @@ -266,8 +279,8 @@ void i2c_frequency(i2c_t *obj, int hz) MBED_ASSERT((hz > 0) && (hz <= 400000)); // wait before init - timeout = LONG_TIMEOUT; - while ((__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BUSY)) && (timeout-- != 0)); + timeout = BYTE_TIMEOUT; + while ((__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BUSY)) && (--timeout != 0)); // I2C configuration handle->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; @@ -307,7 +320,7 @@ inline int i2c_start(i2c_t *obj) { // Wait the STOP condition has been previously correctly sent // This timeout can be avoid in some specific cases by simply clearing the STOP bit - timeout = FLAG_TIMEOUT; + timeout = BYTE_TIMEOUT; while ((handle->Instance->CR1 & I2C_CR1_STOP) == I2C_CR1_STOP) { if ((timeout--) == 0) { return 1; @@ -318,7 +331,7 @@ inline int i2c_start(i2c_t *obj) { handle->Instance->CR1 |= I2C_CR1_START; // Wait the START condition has been correctly sent - timeout = FLAG_TIMEOUT; + timeout = BYTE_TIMEOUT; while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_SB) == RESET) { if ((timeout--) == 0) { return 1; @@ -448,7 +461,7 @@ int i2c_byte_read(i2c_t *obj, int last) { } // Wait until the byte is received - timeout = FLAG_TIMEOUT; + timeout = BYTE_TIMEOUT; while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_RXNE) == RESET) { if ((timeout--) == 0) { return -1; @@ -467,7 +480,7 @@ int i2c_byte_write(i2c_t *obj, int data) { handle->Instance->DR = (uint8_t)data; // Wait until the byte (might be the address) is transmitted - timeout = FLAG_TIMEOUT; + timeout = BYTE_TIMEOUT; while ((__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TXE) == RESET) && (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BTF) == RESET) && (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_ADDR) == RESET)) { @@ -493,8 +506,8 @@ void i2c_reset(i2c_t *obj) { handle->Instance = (I2C_TypeDef *)(obj_s->i2c); // wait before reset - timeout = LONG_TIMEOUT; - while ((__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BUSY)) && (timeout-- != 0)); + timeout = BYTE_TIMEOUT; + while ((__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BUSY)) && (--timeout != 0)); if (obj_s->i2c == I2C_1) { __I2C1_FORCE_RESET(); @@ -615,17 +628,20 @@ int i2c_slave_read(i2c_t *obj, char *data, int length) { I2C_HandleTypeDef *handle = &(obj_s->handle); int count = 0; int ret = 0; + uint32_t timeout = 0; /* Always use I2C_NEXT_FRAME as slave will just adapt to master requests */ ret = HAL_I2C_Slave_Sequential_Receive_IT(handle, (uint8_t *) data, length, I2C_NEXT_FRAME); - if(ret != HAL_OK) { - count = 0; - } else { - count = length; - } + if(ret == HAL_OK) { + timeout = BYTE_TIMEOUT_US * length; + while(obj_s->pending_slave_rx_maxter_tx && (--timeout != 0)) { + wait_us(1); + } - while(obj_s->pending_slave_rx_maxter_tx); + if(timeout != 0) + count = length; + } return count; } @@ -635,17 +651,20 @@ int i2c_slave_write(i2c_t *obj, const char *data, int length) { I2C_HandleTypeDef *handle = &(obj_s->handle); int count = 0; int ret = 0; + uint32_t timeout = 0; /* Always use I2C_NEXT_FRAME as slave will just adapt to master requests */ ret = HAL_I2C_Slave_Sequential_Transmit_IT(handle, (uint8_t *) data, length, I2C_NEXT_FRAME); - if(ret != HAL_OK) { - count = 0; - } else { - count = length; - } + if(ret == HAL_OK) { + timeout = BYTE_TIMEOUT_US * length; + while(obj_s->pending_slave_tx_master_rx && (--timeout != 0)) { + wait_us(1); + } - while(obj_s->pending_slave_tx_master_rx); + if(timeout != 0) + count = length; + } return count; }