From 20c9af8becae1aeffaf27e44213e10c54aef89a3 Mon Sep 17 00:00:00 2001 From: Laurent MEUNIER Date: Thu, 15 Dec 2016 14:51:14 +0100 Subject: [PATCH 1/7] STM32: I2C unitary functions for IP V2 STM32 supported targets have 2 possible versions of I2C. This patch makes the start / stop / read and write byte work ok for IP V2. This was not working before and does not seem to be widely used. --- .../TARGET_STM32F0/common_objects.h | 1 + .../TARGET_STM32F3/common_objects.h | 1 + .../TARGET_STM32F7/common_objects.h | 1 + .../TARGET_STM32L0/common_objects.h | 1 + .../TARGET_STM32L4/common_objects.h | 1 + targets/TARGET_STM/i2c_api.c | 182 +++++++++++++----- 6 files changed, 136 insertions(+), 51 deletions(-) diff --git a/targets/TARGET_STM/TARGET_STM32F0/common_objects.h b/targets/TARGET_STM/TARGET_STM32F0/common_objects.h index 3113b59ef45..dcb6d05b0b0 100644 --- a/targets/TARGET_STM/TARGET_STM32F0/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F0/common_objects.h @@ -98,6 +98,7 @@ struct i2c_s { IRQn_Type error_i2cIRQ; uint32_t XferOperation; volatile uint8_t event; + volatile int pending_start; #if DEVICE_I2CSLAVE uint8_t slave; volatile uint8_t pending_slave_tx_master_rx; diff --git a/targets/TARGET_STM/TARGET_STM32F3/common_objects.h b/targets/TARGET_STM/TARGET_STM32F3/common_objects.h index 01ccd64eac1..544c15f34bf 100644 --- a/targets/TARGET_STM/TARGET_STM32F3/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F3/common_objects.h @@ -98,6 +98,7 @@ struct i2c_s { IRQn_Type error_i2cIRQ; uint32_t XferOperation; volatile uint8_t event; + volatile int pending_start; #if DEVICE_I2CSLAVE uint8_t slave; volatile uint8_t pending_slave_tx_master_rx; diff --git a/targets/TARGET_STM/TARGET_STM32F7/common_objects.h b/targets/TARGET_STM/TARGET_STM32F7/common_objects.h index 1a5647339a3..4d91f63db05 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32F7/common_objects.h @@ -98,6 +98,7 @@ struct i2c_s { IRQn_Type error_i2cIRQ; uint32_t XferOperation; volatile uint8_t event; + volatile int pending_start; #if DEVICE_I2CSLAVE uint8_t slave; volatile uint8_t pending_slave_tx_master_rx; diff --git a/targets/TARGET_STM/TARGET_STM32L0/common_objects.h b/targets/TARGET_STM/TARGET_STM32L0/common_objects.h index 01ccd64eac1..544c15f34bf 100644 --- a/targets/TARGET_STM/TARGET_STM32L0/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32L0/common_objects.h @@ -98,6 +98,7 @@ struct i2c_s { IRQn_Type error_i2cIRQ; uint32_t XferOperation; volatile uint8_t event; + volatile int pending_start; #if DEVICE_I2CSLAVE uint8_t slave; volatile uint8_t pending_slave_tx_master_rx; diff --git a/targets/TARGET_STM/TARGET_STM32L4/common_objects.h b/targets/TARGET_STM/TARGET_STM32L4/common_objects.h index 01ccd64eac1..544c15f34bf 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32L4/common_objects.h @@ -98,6 +98,7 @@ struct i2c_s { IRQn_Type error_i2cIRQ; uint32_t XferOperation; volatile uint8_t event; + volatile int pending_start; #if DEVICE_I2CSLAVE uint8_t slave; volatile uint8_t pending_slave_tx_master_rx; diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 3c2f3510274..2d2063eb9e5 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -341,6 +341,9 @@ void i2c_init(i2c_t *obj, PinName sda, PinName scl) { // I2C Xfer operation init obj_s->event = 0; obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME; +#ifdef I2C_IP_VERSION_V2 + obj_s->pending_start = 0; +#endif } void i2c_frequency(i2c_t *obj, int hz) @@ -440,6 +443,14 @@ i2c_t *get_i2c_obj(I2C_HandleTypeDef *hi2c){ return (obj); } +void i2c_reset(i2c_t *obj) { + struct i2c_s *obj_s = I2C_S(obj); + /* As recommended in i2c_api.h, mainly send stop */ + i2c_stop(obj); + /* then re-init */ + i2c_init(obj, obj_s->sda, obj_s->scl); +} + /* * UNITARY APIS. * For very basic operations, direct registers access is needed @@ -514,7 +525,7 @@ int i2c_byte_read(i2c_t *obj, int last) { timeout = FLAG_TIMEOUT; while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_RXNE) == RESET) { if ((timeout--) == 0) { - return -1; + return 2; } } @@ -535,7 +546,7 @@ int i2c_byte_write(i2c_t *obj, int data) { (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BTF) == RESET) && (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_ADDR) == RESET)) { if ((timeout--) == 0) { - return 0; + return 2; } } @@ -548,92 +559,161 @@ int i2c_byte_write(i2c_t *obj, int data) { } #endif //I2C_IP_VERSION_V1 #ifdef I2C_IP_VERSION_V2 + int i2c_start(i2c_t *obj) { struct i2c_s *obj_s = I2C_S(obj); - I2C_HandleTypeDef *handle = &(obj_s->handle); - I2C_TypeDef *i2c = (I2C_TypeDef *)obj_s->i2c; - int timeout; - - // Clear Acknowledge failure flag - __HAL_I2C_CLEAR_FLAG(handle, I2C_FLAG_AF); + /* This I2C IP doesn't */ + obj_s->pending_start = 1; + return 0; +} - // Wait the STOP condition has been previously correctly sent - timeout = FLAG_TIMEOUT; - while ((i2c->CR2 & I2C_CR2_STOP) == I2C_CR2_STOP){ - if ((timeout--) == 0) { - return 1; - } +int i2c_stop(i2c_t *obj) { + struct i2c_s *obj_s = I2C_S(obj); + I2C_HandleTypeDef *handle = &(obj_s->handle); + int timeout = FLAG_TIMEOUT; +#if DEVICE_I2CSLAVE + if (obj_s->slave) { + /* re-init slave when stop is requested */ + i2c_init(obj, obj_s->sda, obj_s->scl); + return 0; } +#endif + // Disable reload mode + handle->Instance->CR2 &= (uint32_t)~I2C_CR2_RELOAD; + // Generate the STOP condition + handle->Instance->CR2 |= I2C_CR2_STOP; - // Generate the START condition - i2c->CR2 |= I2C_CR2_START; - - // Wait the START condition has been correctly sent timeout = FLAG_TIMEOUT; - while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BUSY) == RESET) { + while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_STOPF)) { if ((timeout--) == 0) { - return 1; + return I2C_ERROR_BUS_BUSY; } } - return 0; -} + /* Clear STOP Flag */ + __HAL_I2C_CLEAR_FLAG(handle, I2C_FLAG_STOPF); -int i2c_stop(i2c_t *obj) { - struct i2c_s *obj_s = I2C_S(obj); - I2C_TypeDef *i2c = (I2C_TypeDef *)obj_s->i2c; + /* Erase slave address, this wiil be used as a marker + * to know when we need to prepare next start */ + handle->Instance->CR2 &= ~I2C_CR2_SADD; - // Generate the STOP condition - i2c->CR2 |= I2C_CR2_STOP; + /* In case of mixed usage of the APIs (unitary + SYNC) + * re-inti HAL state */ + if (obj_s->XferOperation != I2C_FIRST_AND_LAST_FRAME) { + i2c_init(obj, obj_s->sda, obj_s->scl); + } return 0; } int i2c_byte_read(i2c_t *obj, int last) { struct i2c_s *obj_s = I2C_S(obj); - I2C_TypeDef *i2c = (I2C_TypeDef *)obj_s->i2c; I2C_HandleTypeDef *handle = &(obj_s->handle); - int timeout; + int timeout = FLAG_TIMEOUT; + uint32_t tmpreg = handle->Instance->CR2; + char data; +#if DEVICE_I2CSLAVE + if (obj_s->slave) { + return i2c_slave_read(obj, &data, 1); + } +#endif + /* Then send data when there's room in the TX fifo */ + if ((tmpreg & I2C_CR2_RELOAD) != 0) { + while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TCR)) { + if ((timeout--) == 0) { + printf("timeout in byte_read\r\n"); + //return 2; + } + } + } + + /* Enable reload mode as we don't know how many bytes will eb sent */ + handle->Instance->CR2 |= I2C_CR2_RELOAD; + /* Set transfer size to 1 */ + handle->Instance->CR2 |= (I2C_CR2_NBYTES & (1 << 16)); + /* Set the prepared configuration */ + handle->Instance->CR2 = tmpreg; - // Wait until the byte is received timeout = FLAG_TIMEOUT; - while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_RXNE) == RESET) { + while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_RXNE)) { if ((timeout--) == 0) { - return -1; + return 2; } } - return (int)i2c->RXDR; + /* Then Get Byte */ + data = handle->Instance->RXDR; + + if (last) { + /* Disable Address Acknowledge */ + handle->Instance->CR2 |= I2C_CR2_NACK; + } + + return data; } int i2c_byte_write(i2c_t *obj, int data) { struct i2c_s *obj_s = I2C_S(obj); - I2C_TypeDef *i2c = (I2C_TypeDef *)obj_s->i2c; I2C_HandleTypeDef *handle = &(obj_s->handle); - int timeout; - - // Wait until the previous byte is transmitted - timeout = FLAG_TIMEOUT; - while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TXIS) == RESET) { - if ((timeout--) == 0) { - return 0; + int timeout = FLAG_TIMEOUT; + uint32_t tmpreg = handle->Instance->CR2; +#if DEVICE_I2CSLAVE + if (obj_s->slave) { + return i2c_slave_write(obj, (char *) &data, 1); + } +#endif + if (obj_s->pending_start) { + obj_s->pending_start = 0; + //* First byte after the start is the address */ + tmpreg |= (uint32_t)((uint32_t)data & I2C_CR2_SADD); + if (data & 0x01) { + tmpreg |= I2C_CR2_START | I2C_CR2_RD_WRN; + } else { + tmpreg |= I2C_CR2_START; + tmpreg &= ~I2C_CR2_RD_WRN; + } + /* Disable reload first to use it later */ + tmpreg &= ~I2C_CR2_RELOAD; + /* Disable Autoend */ + tmpreg &= ~I2C_CR2_AUTOEND; + /* Do not set any transfer size for now */ + tmpreg |= (I2C_CR2_NBYTES & (1 << 16)); + /* Set the prepared configuration */ + handle->Instance->CR2 = tmpreg; + } else { + /* Set the prepared configuration */ + tmpreg = handle->Instance->CR2; + + /* Then send data when there's room in the TX fifo */ + if ((tmpreg & I2C_CR2_RELOAD) != 0) { + while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TCR)) { + if ((timeout--) == 0) { + printf("timeout in byte_write\r\n"); + //return 2; + } + } } + /* Enable reload mode as we don't know how many bytes will eb sent */ + tmpreg |= I2C_CR2_RELOAD; + /* Set transfer size to 1 */ + tmpreg |= (I2C_CR2_NBYTES & (1 << 16)); + /* Set the prepared configuration */ + handle->Instance->CR2 = tmpreg; + /* Prepare next write */ + timeout = FLAG_TIMEOUT; + while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TXE)) { + if ((timeout--) == 0) { + return 2; + } + } + /* Write byte */ + handle->Instance->TXDR = data; } - i2c->TXDR = (uint8_t)data; - return 1; } #endif //I2C_IP_VERSION_V2 -void i2c_reset(i2c_t *obj) { - struct i2c_s *obj_s = I2C_S(obj); - /* As recommended in i2c_api.h, mainly send stop */ - i2c_stop(obj); - /* then re-init */ - i2c_init(obj, obj_s->sda, obj_s->scl); -} - /* * SYNC APIS */ From 70667349ee307e3880c35c73acb3a77911b5681a Mon Sep 17 00:00:00 2001 From: Laurent MEUNIER Date: Mon, 19 Dec 2016 11:30:12 +0100 Subject: [PATCH 2/7] I2C test: change slave address This avoids conflicts with slave on ci-test-shield --- features/unsupported/tests/mbed/i2c_master_slave/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/unsupported/tests/mbed/i2c_master_slave/main.cpp b/features/unsupported/tests/mbed/i2c_master_slave/main.cpp index 820daf196a1..598b872d129 100644 --- a/features/unsupported/tests/mbed/i2c_master_slave/main.cpp +++ b/features/unsupported/tests/mbed/i2c_master_slave/main.cpp @@ -22,7 +22,7 @@ #error [NOT_SUPPORTED] Target has only one I2C instance #endif -#define ADDR (0xA0) +#define ADDR (0x80) #define FREQ 100000 // ******************************************************** From d67b431f17836458850769a1fe1d052d4606bdae Mon Sep 17 00:00:00 2001 From: Laurent MEUNIER Date: Mon, 19 Dec 2016 11:31:41 +0100 Subject: [PATCH 3/7] I2C test: let slave time to get notified depending on timing and HW, there might be some delay before the master request gets notified, so better loop in while than a single call to slave.receive() --- .../tests/mbed/i2c_master_slave/main.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/features/unsupported/tests/mbed/i2c_master_slave/main.cpp b/features/unsupported/tests/mbed/i2c_master_slave/main.cpp index 598b872d129..4f487ff5071 100644 --- a/features/unsupported/tests/mbed/i2c_master_slave/main.cpp +++ b/features/unsupported/tests/mbed/i2c_master_slave/main.cpp @@ -89,11 +89,8 @@ int main() master.start(); master.write(ADDR); master.write(sent); - if(slave.receive() != I2CSlave::WriteAddressed) - { - notify_completion(false); - return 1; - } + while(slave.receive() != I2CSlave::WriteAddressed); + slave.read(&received, 1); if(sent != received) { @@ -105,11 +102,8 @@ int main() // Second transfer: slave to master master.start(); master.write(ADDR | 1); - if(slave.receive() != I2CSlave::ReadAddressed) - { - notify_completion(false); - return 1; - } + while(slave.receive() != I2CSlave::ReadAddressed); + slave.write(received); received = master.read(0); slave.stop(); From bcf82b09012d8611c76a233dceb0cbb1d1d5597d Mon Sep 17 00:00:00 2001 From: Laurent MEUNIER Date: Mon, 19 Dec 2016 11:32:18 +0100 Subject: [PATCH 4/7] I2C test: master should send stop it's master not slave that shall send STOP at the end of I2C transfer --- features/unsupported/tests/mbed/i2c_master_slave/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/unsupported/tests/mbed/i2c_master_slave/main.cpp b/features/unsupported/tests/mbed/i2c_master_slave/main.cpp index 4f487ff5071..cee77e2efeb 100644 --- a/features/unsupported/tests/mbed/i2c_master_slave/main.cpp +++ b/features/unsupported/tests/mbed/i2c_master_slave/main.cpp @@ -106,7 +106,7 @@ int main() slave.write(received); received = master.read(0); - slave.stop(); + master.stop(); notify_completion(received == sent); } From 517d0d910aae3d24851048a974dedfdab9eae0bd Mon Sep 17 00:00:00 2001 From: Laurent MEUNIER Date: Mon, 19 Dec 2016 11:32:38 +0100 Subject: [PATCH 5/7] I2C test: add L073RZ --- features/unsupported/tests/mbed/i2c_master_slave_asynch/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/features/unsupported/tests/mbed/i2c_master_slave_asynch/main.cpp b/features/unsupported/tests/mbed/i2c_master_slave_asynch/main.cpp index 014573b088c..cc889856be5 100644 --- a/features/unsupported/tests/mbed/i2c_master_slave_asynch/main.cpp +++ b/features/unsupported/tests/mbed/i2c_master_slave_asynch/main.cpp @@ -41,6 +41,7 @@ I2C master(D14, D15); // I2C_SDA, I2C_SCL defined (TARGET_DISCO_F429ZI) || \ defined (TARGET_NUCLEO_F767ZI) || \ defined (TARGET_NUCLEO_L053R8) || \ + defined (TARGET_NUCLEO_L073RZ) || \ defined (TARGET_NUCLEO_L152RE) || \ defined (TARGET_NUCLEO_L476RG) I2CSlave slave(PB_11, PB_10); From 455c2ecbeae2b6f2891925393774173864f89e96 Mon Sep 17 00:00:00 2001 From: Laurent MEUNIER Date: Tue, 3 Jan 2017 10:18:56 +0100 Subject: [PATCH 6/7] STM32: I2C: remove debug code Few debug lines were to be removed / updated. Move the printf to DEBUG_PRINTF and return the error when needed. --- targets/TARGET_STM/i2c_api.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 2d2063eb9e5..23506d11510 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -621,8 +621,8 @@ int i2c_byte_read(i2c_t *obj, int last) { if ((tmpreg & I2C_CR2_RELOAD) != 0) { while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TCR)) { if ((timeout--) == 0) { - printf("timeout in byte_read\r\n"); - //return 2; + DEBUG_PRINTF("timeout in byte_read\r\n"); + return 2; } } } @@ -688,8 +688,8 @@ int i2c_byte_write(i2c_t *obj, int data) { if ((tmpreg & I2C_CR2_RELOAD) != 0) { while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TCR)) { if ((timeout--) == 0) { - printf("timeout in byte_write\r\n"); - //return 2; + DEBUG_PRINTF("timeout in byte_write\r\n"); + return 2; } } } From 4297e3fd36c60a426377b3be946d3e97d224969e Mon Sep 17 00:00:00 2001 From: Laurent MEUNIER Date: Tue, 3 Jan 2017 18:06:19 +0100 Subject: [PATCH 7/7] STM32: I2C: i2c_byte_read return value in case of error To make clear that an error is being reported, we shall report -1, 2 being the timeout error for i2c_byte_write only. --- targets/TARGET_STM/i2c_api.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index 23506d11510..0bbc4bbad76 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -525,7 +525,7 @@ int i2c_byte_read(i2c_t *obj, int last) { timeout = FLAG_TIMEOUT; while (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_RXNE) == RESET) { if ((timeout--) == 0) { - return 2; + return -1; } } @@ -622,7 +622,7 @@ int i2c_byte_read(i2c_t *obj, int last) { while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TCR)) { if ((timeout--) == 0) { DEBUG_PRINTF("timeout in byte_read\r\n"); - return 2; + return -1; } } } @@ -637,7 +637,7 @@ int i2c_byte_read(i2c_t *obj, int last) { timeout = FLAG_TIMEOUT; while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_RXNE)) { if ((timeout--) == 0) { - return 2; + return -1; } }