From 0751932dd302c06b0e8e9b95321415a1679d76d0 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 20 Nov 2022 17:46:30 -0800 Subject: [PATCH 1/4] Rethink STM32 I2C v2 HAL --- targets/TARGET_STM/TARGET_STM32F0/objects.h | 33 -- targets/TARGET_STM/TARGET_STM32F3/objects.h | 35 -- targets/TARGET_STM/TARGET_STM32F7/objects.h | 33 -- targets/TARGET_STM/TARGET_STM32G0/objects.h | 35 -- targets/TARGET_STM/TARGET_STM32G4/objects.h | 35 -- targets/TARGET_STM/TARGET_STM32H7/objects.h | 33 -- targets/TARGET_STM/TARGET_STM32L0/objects.h | 35 -- targets/TARGET_STM/TARGET_STM32L4/objects.h | 35 -- targets/TARGET_STM/TARGET_STM32L5/objects.h | 33 -- targets/TARGET_STM/TARGET_STM32U5/objects.h | 33 -- targets/TARGET_STM/TARGET_STM32WB/objects.h | 33 -- targets/TARGET_STM/TARGET_STM32WL/objects.h | 33 -- targets/TARGET_STM/device.h | 1 + targets/TARGET_STM/i2c_api.c | 454 +++++++++++++++----- targets/TARGET_STM/stm_i2c_api.h | 97 +++++ 15 files changed, 443 insertions(+), 515 deletions(-) create mode 100644 targets/TARGET_STM/stm_i2c_api.h diff --git a/targets/TARGET_STM/TARGET_STM32F0/objects.h b/targets/TARGET_STM/TARGET_STM32F0/objects.h index dbdadf96263..c8188eb4e2c 100644 --- a/targets/TARGET_STM/TARGET_STM32F0/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F0/objects.h @@ -77,39 +77,6 @@ struct serial_s { #endif }; -struct i2c_s { - /* The 1st 2 members I2CName i2c - * and I2C_HandleTypeDef handle should - * be kept as the first members of this struct - * to ensure i2c_get_obj to work as expected - */ - I2CName i2c; - I2C_HandleTypeDef handle; - uint8_t index; - int hz; - PinName sda; - PinName scl; - IRQn_Type event_i2cIRQ; - IRQn_Type error_i2cIRQ; - uint32_t XferOperation; - volatile uint8_t event; - volatile int pending_start; - int current_hz; -#if DEVICE_I2CSLAVE - uint8_t slave; - volatile uint8_t pending_slave_tx_master_rx; - volatile uint8_t pending_slave_rx_maxter_tx; - uint8_t *slave_rx_buffer; - volatile uint16_t slave_rx_buffer_size; - volatile uint16_t slave_rx_count; -#endif -#if DEVICE_I2C_ASYNCH - uint32_t address; - uint8_t stop; - uint8_t available_events; -#endif -}; - struct analogin_s { ADC_HandleTypeDef handle; PinName pin; diff --git a/targets/TARGET_STM/TARGET_STM32F3/objects.h b/targets/TARGET_STM/TARGET_STM32F3/objects.h index d69e9e7b6fc..c95c440c87b 100644 --- a/targets/TARGET_STM/TARGET_STM32F3/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F3/objects.h @@ -90,41 +90,6 @@ struct serial_s { #endif }; -struct i2c_s { - /* The 1st 2 members I2CName i2c - * and I2C_HandleTypeDef handle should - * be kept as the first members of this struct - * to ensure i2c_get_obj to work as expected - */ - I2CName i2c; - I2C_HandleTypeDef handle; - uint8_t index; - int hz; - PinName sda; - PinName scl; - int sda_func; - int scl_func; - IRQn_Type event_i2cIRQ; - IRQn_Type error_i2cIRQ; - uint32_t XferOperation; - volatile uint8_t event; - volatile int pending_start; - int current_hz; -#if DEVICE_I2CSLAVE - uint8_t slave; - volatile uint8_t pending_slave_tx_master_rx; - volatile uint8_t pending_slave_rx_maxter_tx; - uint8_t *slave_rx_buffer; - volatile uint16_t slave_rx_buffer_size; - volatile uint16_t slave_rx_count; -#endif -#if DEVICE_I2C_ASYNCH - uint32_t address; - uint8_t stop; - uint8_t available_events; -#endif -}; - struct dac_s { DACName dac; PinName pin; diff --git a/targets/TARGET_STM/TARGET_STM32F7/objects.h b/targets/TARGET_STM/TARGET_STM32F7/objects.h index ef8dc2919e8..ed253af45d5 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F7/objects.h @@ -108,39 +108,6 @@ struct serial_s { #endif }; -struct i2c_s { - /* The 1st 2 members I2CName i2c - * and I2C_HandleTypeDef handle should - * be kept as the first members of this struct - * to ensure i2c_get_obj to work as expected - */ - I2CName i2c; - I2C_HandleTypeDef handle; - uint8_t index; - int hz; - PinName sda; - PinName scl; - IRQn_Type event_i2cIRQ; - IRQn_Type error_i2cIRQ; - uint32_t XferOperation; - volatile uint8_t event; - volatile int pending_start; - int current_hz; -#if DEVICE_I2CSLAVE - uint8_t slave; - volatile uint8_t pending_slave_tx_master_rx; - volatile uint8_t pending_slave_rx_maxter_tx; - uint8_t *slave_rx_buffer; - volatile uint16_t slave_rx_buffer_size; - volatile uint16_t slave_rx_count; -#endif -#if DEVICE_I2C_ASYNCH - uint32_t address; - uint8_t stop; - uint8_t available_events; -#endif -}; - struct analogin_s { ADC_HandleTypeDef handle; PinName pin; diff --git a/targets/TARGET_STM/TARGET_STM32G0/objects.h b/targets/TARGET_STM/TARGET_STM32G0/objects.h index 55dea741cef..0621162292f 100644 --- a/targets/TARGET_STM/TARGET_STM32G0/objects.h +++ b/targets/TARGET_STM/TARGET_STM32G0/objects.h @@ -89,41 +89,6 @@ struct serial_s { #endif }; -struct i2c_s { - /* The 1st 2 members I2CName i2c - * and I2C_HandleTypeDef handle should - * be kept as the first members of this struct - * to ensure i2c_get_obj to work as expected - */ - I2CName i2c; - I2C_HandleTypeDef handle; - uint8_t index; - int hz; - PinName sda; - PinName scl; - int sda_func; - int scl_func; - IRQn_Type event_i2cIRQ; - IRQn_Type error_i2cIRQ; - uint32_t XferOperation; - volatile uint8_t event; - volatile int pending_start; - int current_hz; -#if DEVICE_I2CSLAVE - uint8_t slave; - volatile uint8_t pending_slave_tx_master_rx; - volatile uint8_t pending_slave_rx_maxter_tx; - uint8_t *slave_rx_buffer; - volatile uint16_t slave_rx_buffer_size; - volatile uint16_t slave_rx_count; -#endif -#if DEVICE_I2C_ASYNCH - uint32_t address; - uint8_t stop; - uint8_t available_events; -#endif -}; - struct flash_s { /* nothing to be stored for now */ uint32_t dummy; diff --git a/targets/TARGET_STM/TARGET_STM32G4/objects.h b/targets/TARGET_STM/TARGET_STM32G4/objects.h index 647c45253d8..183cf54165b 100644 --- a/targets/TARGET_STM/TARGET_STM32G4/objects.h +++ b/targets/TARGET_STM/TARGET_STM32G4/objects.h @@ -88,41 +88,6 @@ struct serial_s { #endif }; -struct i2c_s { - /* The 1st 2 members I2CName i2c - * and I2C_HandleTypeDef handle should - * be kept as the first members of this struct - * to ensure i2c_get_obj to work as expected - */ - I2CName i2c; - I2C_HandleTypeDef handle; - uint8_t index; - int hz; - PinName sda; - PinName scl; - int sda_func; - int scl_func; - IRQn_Type event_i2cIRQ; - IRQn_Type error_i2cIRQ; - uint32_t XferOperation; - volatile uint8_t event; - volatile int pending_start; - int current_hz; -#if DEVICE_I2CSLAVE - uint8_t slave; - volatile uint8_t pending_slave_tx_master_rx; - volatile uint8_t pending_slave_rx_maxter_tx; - uint8_t *slave_rx_buffer; - volatile uint16_t slave_rx_buffer_size; - volatile uint16_t slave_rx_count; -#endif -#if DEVICE_I2C_ASYNCH - uint32_t address; - uint8_t stop; - uint8_t available_events; -#endif -}; - struct dac_s { DACName dac; PinName pin; diff --git a/targets/TARGET_STM/TARGET_STM32H7/objects.h b/targets/TARGET_STM/TARGET_STM32H7/objects.h index cba64709dfd..aa2bf285ef1 100644 --- a/targets/TARGET_STM/TARGET_STM32H7/objects.h +++ b/targets/TARGET_STM/TARGET_STM32H7/objects.h @@ -97,39 +97,6 @@ struct serial_s { #endif }; -struct i2c_s { - /* The 1st 2 members I2CName i2c - * and I2C_HandleTypeDef handle should - * be kept as the first members of this struct - * to ensure i2c_get_obj to work as expected - */ - I2CName i2c; - I2C_HandleTypeDef handle; - uint8_t index; - int hz; - PinName sda; - PinName scl; - IRQn_Type event_i2cIRQ; - IRQn_Type error_i2cIRQ; - uint32_t XferOperation; - volatile uint8_t event; - volatile int pending_start; - int current_hz; -#if DEVICE_I2CSLAVE - uint8_t slave; - volatile uint8_t pending_slave_tx_master_rx; - volatile uint8_t pending_slave_rx_maxter_tx; - uint8_t *slave_rx_buffer; - volatile uint16_t slave_rx_buffer_size; - volatile uint16_t slave_rx_count; -#endif -#if DEVICE_I2C_ASYNCH - uint32_t address; - uint8_t stop; - uint8_t available_events; -#endif -}; - struct analogin_s { ADC_HandleTypeDef handle; PinName pin; diff --git a/targets/TARGET_STM/TARGET_STM32L0/objects.h b/targets/TARGET_STM/TARGET_STM32L0/objects.h index 8ed217b88e3..4becf27ff63 100644 --- a/targets/TARGET_STM/TARGET_STM32L0/objects.h +++ b/targets/TARGET_STM/TARGET_STM32L0/objects.h @@ -91,41 +91,6 @@ struct serial_s { #endif }; -struct i2c_s { - /* The 1st 2 members I2CName i2c - * and I2C_HandleTypeDef handle should - * be kept as the first members of this struct - * to ensure i2c_get_obj to work as expected - */ - I2CName i2c; - I2C_HandleTypeDef handle; - uint8_t index; - int hz; - PinName sda; - PinName scl; - int sda_func; - int scl_func; - IRQn_Type event_i2cIRQ; - IRQn_Type error_i2cIRQ; - uint32_t XferOperation; - volatile uint8_t event; - volatile int pending_start; - int current_hz; -#if DEVICE_I2CSLAVE - uint8_t slave; - volatile uint8_t pending_slave_tx_master_rx; - volatile uint8_t pending_slave_rx_maxter_tx; - uint8_t *slave_rx_buffer; - volatile uint16_t slave_rx_buffer_size; - volatile uint16_t slave_rx_count; -#endif -#if DEVICE_I2C_ASYNCH - uint32_t address; - uint8_t stop; - uint8_t available_events; -#endif -}; - #if DEVICE_FLASH struct flash_s { /* nothing to be stored for now */ diff --git a/targets/TARGET_STM/TARGET_STM32L4/objects.h b/targets/TARGET_STM/TARGET_STM32L4/objects.h index 136a582b318..d41e927384a 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/objects.h +++ b/targets/TARGET_STM/TARGET_STM32L4/objects.h @@ -87,41 +87,6 @@ struct serial_s { #endif }; -struct i2c_s { - /* The 1st 2 members I2CName i2c - * and I2C_HandleTypeDef handle should - * be kept as the first members of this struct - * to ensure i2c_get_obj to work as expected - */ - I2CName i2c; - I2C_HandleTypeDef handle; - uint8_t index; - int hz; - PinName sda; - PinName scl; - int sda_func; - int scl_func; - IRQn_Type event_i2cIRQ; - IRQn_Type error_i2cIRQ; - uint32_t XferOperation; - volatile uint8_t event; - volatile int pending_start; - int current_hz; -#if DEVICE_I2CSLAVE - uint8_t slave; - volatile uint8_t pending_slave_tx_master_rx; - volatile uint8_t pending_slave_rx_maxter_tx; - uint8_t *slave_rx_buffer; - volatile uint16_t slave_rx_buffer_size; - volatile uint16_t slave_rx_count; -#endif -#if DEVICE_I2C_ASYNCH - uint32_t address; - uint8_t stop; - uint8_t available_events; -#endif -}; - struct flash_s { /* nothing to be stored for now */ uint32_t dummy; diff --git a/targets/TARGET_STM/TARGET_STM32L5/objects.h b/targets/TARGET_STM/TARGET_STM32L5/objects.h index c06bdd71711..ed7d2a8dbee 100644 --- a/targets/TARGET_STM/TARGET_STM32L5/objects.h +++ b/targets/TARGET_STM/TARGET_STM32L5/objects.h @@ -97,39 +97,6 @@ struct serial_s { #endif }; -struct i2c_s { - /* The 1st 2 members I2CName i2c - * and I2C_HandleTypeDef handle should - * be kept as the first members of this struct - * to ensure i2c_get_obj to work as expected - */ - I2CName i2c; - I2C_HandleTypeDef handle; - uint8_t index; - int hz; - PinName sda; - PinName scl; - IRQn_Type event_i2cIRQ; - IRQn_Type error_i2cIRQ; - uint32_t XferOperation; - volatile uint8_t event; - volatile int pending_start; - int current_hz; -#if DEVICE_I2CSLAVE - uint8_t slave; - volatile uint8_t pending_slave_tx_master_rx; - volatile uint8_t pending_slave_rx_maxter_tx; - uint8_t *slave_rx_buffer; - volatile uint16_t slave_rx_buffer_size; - volatile uint16_t slave_rx_count; -#endif -#if DEVICE_I2C_ASYNCH - uint32_t address; - uint8_t stop; - uint8_t available_events; -#endif -}; - struct flash_s { /* nothing to be stored for now */ uint32_t dummy; diff --git a/targets/TARGET_STM/TARGET_STM32U5/objects.h b/targets/TARGET_STM/TARGET_STM32U5/objects.h index a5e3d328e43..69e9c5bc21a 100644 --- a/targets/TARGET_STM/TARGET_STM32U5/objects.h +++ b/targets/TARGET_STM/TARGET_STM32U5/objects.h @@ -97,39 +97,6 @@ struct serial_s { #endif }; -struct i2c_s { - /* The 1st 2 members I2CName i2c - * and I2C_HandleTypeDef handle should - * be kept as the first members of this struct - * to ensure i2c_get_obj to work as expected - */ - I2CName i2c; - I2C_HandleTypeDef handle; - uint8_t index; - int hz; - PinName sda; - PinName scl; - IRQn_Type event_i2cIRQ; - IRQn_Type error_i2cIRQ; - uint32_t XferOperation; - volatile uint8_t event; - volatile int pending_start; - int current_hz; -#if DEVICE_I2CSLAVE - uint8_t slave; - volatile uint8_t pending_slave_tx_master_rx; - volatile uint8_t pending_slave_rx_maxter_tx; - uint8_t *slave_rx_buffer; - volatile uint16_t slave_rx_buffer_size; - volatile uint16_t slave_rx_count; -#endif -#if DEVICE_I2C_ASYNCH - uint32_t address; - uint8_t stop; - uint8_t available_events; -#endif -}; - struct flash_s { /* nothing to be stored for now */ uint32_t dummy; diff --git a/targets/TARGET_STM/TARGET_STM32WB/objects.h b/targets/TARGET_STM/TARGET_STM32WB/objects.h index 515846edcbb..a3b501e8961 100644 --- a/targets/TARGET_STM/TARGET_STM32WB/objects.h +++ b/targets/TARGET_STM/TARGET_STM32WB/objects.h @@ -80,39 +80,6 @@ struct serial_s { #endif }; -struct i2c_s { - /* The 1st 2 members I2CName i2c - * and I2C_HandleTypeDef handle should - * be kept as the first members of this struct - * to ensure i2c_get_obj to work as expected - */ - I2CName i2c; - I2C_HandleTypeDef handle; - uint8_t index; - int hz; - PinName sda; - PinName scl; - IRQn_Type event_i2cIRQ; - IRQn_Type error_i2cIRQ; - uint32_t XferOperation; - volatile uint8_t event; - volatile int pending_start; - int current_hz; -#if DEVICE_I2CSLAVE - uint8_t slave; - volatile uint8_t pending_slave_tx_master_rx; - volatile uint8_t pending_slave_rx_maxter_tx; - uint8_t *slave_rx_buffer; - volatile uint16_t slave_rx_buffer_size; - volatile uint16_t slave_rx_count; -#endif -#if DEVICE_I2C_ASYNCH - uint32_t address; - uint8_t stop; - uint8_t available_events; -#endif -}; - struct flash_s { /* nothing to be stored for now */ uint32_t dummy; diff --git a/targets/TARGET_STM/TARGET_STM32WL/objects.h b/targets/TARGET_STM/TARGET_STM32WL/objects.h index a8fd7b93d32..d3c601b3513 100644 --- a/targets/TARGET_STM/TARGET_STM32WL/objects.h +++ b/targets/TARGET_STM/TARGET_STM32WL/objects.h @@ -83,39 +83,6 @@ struct serial_s { #endif }; -struct i2c_s { - /* The 1st 2 members I2CName i2c - * and I2C_HandleTypeDef handle should - * be kept as the first members of this struct - * to ensure i2c_get_obj to work as expected - */ - I2CName i2c; - I2C_HandleTypeDef handle; - uint8_t index; - int hz; - PinName sda; - PinName scl; - IRQn_Type event_i2cIRQ; - IRQn_Type error_i2cIRQ; - uint32_t XferOperation; - volatile uint8_t event; - volatile int pending_start; - int current_hz; -#if DEVICE_I2CSLAVE - uint8_t slave; - volatile uint8_t pending_slave_tx_master_rx; - volatile uint8_t pending_slave_rx_maxter_tx; - uint8_t *slave_rx_buffer; - volatile uint16_t slave_rx_buffer_size; - volatile uint16_t slave_rx_count; -#endif -#if DEVICE_I2C_ASYNCH - uint32_t address; - uint8_t stop; - uint8_t available_events; -#endif -}; - struct flash_s { /* nothing to be stored for now */ uint32_t dummy; diff --git a/targets/TARGET_STM/device.h b/targets/TARGET_STM/device.h index a2fd83c54e1..b30041afd11 100644 --- a/targets/TARGET_STM/device.h +++ b/targets/TARGET_STM/device.h @@ -36,6 +36,7 @@ #define DEVICE_ID_LENGTH 24 #include "objects.h" +#include "stm_i2c_api.h" #if DEVICE_USTICKER #include "us_ticker_defines.h" diff --git a/targets/TARGET_STM/i2c_api.c b/targets/TARGET_STM/i2c_api.c index d83292a60af..d883eeced8c 100644 --- a/targets/TARGET_STM/i2c_api.c +++ b/targets/TARGET_STM/i2c_api.c @@ -525,6 +525,9 @@ void i2c_init_internal(i2c_t *obj, const i2c_pinmap_t *pinmap) #ifdef I2C_IP_VERSION_V2 obj_s->current_hz = obj_s->hz; +#else + // I2C Xfer operation init + obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME; #endif #if DEVICE_I2CSLAVE @@ -534,12 +537,8 @@ void i2c_init_internal(i2c_t *obj, const i2c_pinmap_t *pinmap) obj_s->pending_slave_rx_maxter_tx = 0; #endif - // 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 + STM_I2C_SET_STATE(obj_s, STM_I2C_IDLE); } void i2c_deinit_internal(i2c_t *obj) @@ -823,11 +822,14 @@ int i2c_stop(i2c_t *obj) // Generate the STOP condition i2c->CR1 |= I2C_CR1_STOP; - /* In case of mixed usage of the APIs (unitary + SYNC) - * re-init HAL state - */ - if (obj_s->XferOperation != I2C_FIRST_AND_LAST_FRAME) { - i2c_init_internal(obj, NULL); + obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME; + + // Wait until condition is generated + int timeout = FLAG_TIMEOUT; + while (__HAL_I2C_GET_FLAG(&obj_s->handle, I2C_FLAG_BUSY)) { + if ((timeout--) == 0) { + return -1; + } } return 0; @@ -887,11 +889,75 @@ int i2c_byte_write(i2c_t *obj, int data) #endif //I2C_IP_VERSION_V1 #ifdef I2C_IP_VERSION_V2 +/** + * Return whether the given state is a state where we can start a new I2C transaction with the + * STM32 HAL. + */ +inline bool i2c_is_ready_for_transaction_start(stm_i2c_state state) +{ + // Note: We can safely send a transaction start in the middle of any single byte operation; this creates a + // repeated start. + + return state == STM_I2C_IDLE || STM_I2C_PENDING_START == state + || state == STM_I2C_SB_READ_IN_PROGRESS || state == STM_I2C_SB_WRITE_IN_PROGRESS; +} + +/** + * If currently in a single-byte operation, special reset logic is needed to get the peripheral + * ready to repeated-start another transaction. This function performs those steps if they are needed. + */ +static void prep_for_restart_if_needed(struct i2c_s *obj_s) { + if (obj_s->state == STM_I2C_SB_READ_IN_PROGRESS || obj_s->state == STM_I2C_SB_WRITE_IN_PROGRESS) { + // Force an end to the current operation by setting bytes remaining to 0 and RELOAD to false. + // Without this, the I2C peripheral seems to refuse to send another start condition as it + // thinks the previous operation is still running. + uint32_t cr2_val = obj_s->handle.Instance->CR2; + cr2_val &= ~(I2C_CR2_RELOAD_Msk); + cr2_val &= ~(I2C_CR2_NBYTES_Msk); + obj_s->handle.Instance->CR2 = cr2_val; + + // Wait until the hardware ends the transfer and sets the transfer complete flag. + int timeout = FLAG_TIMEOUT; + while (!__HAL_I2C_GET_FLAG(&obj_s->handle, I2C_FLAG_TC)) { + if ((timeout--) == 0) { + break; + } + } + } +} + +/** + * Generate the STM32 HAL "xferOptions" value based on the current state and whether we are going to send a + * STOP at the end of the current transaction. + */ +static uint32_t get_hal_xfer_options(struct i2c_s *obj_s, bool stop) { + if (obj_s->state == STM_I2C_SB_READ_IN_PROGRESS || obj_s->state == STM_I2C_SB_WRITE_IN_PROGRESS) { + if(stop) { + // Generate restart condition and stop at end + return I2C_OTHER_AND_LAST_FRAME; + } else { + // Generate restart condition but don't send STOP + return I2C_OTHER_FRAME; + } + } else { + if(stop) { + // Generate start condition and stop at end + return I2C_FIRST_AND_LAST_FRAME; + } else { + return I2C_LAST_FRAME; + } + } +} + int i2c_start(i2c_t *obj) { struct i2c_s *obj_s = I2C_S(obj); - /* This I2C IP doesn't */ - obj_s->pending_start = 1; + + prep_for_restart_if_needed(obj_s); + + // The I2C peripheral in this chip cannot issue a start condition without also sending the first address byte. + // So, this function just has to set a flag, and we will send the actual start condition later. + obj_s->state = STM_I2C_PENDING_START; return 0; } @@ -899,7 +965,7 @@ int i2c_stop(i2c_t *obj) { struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); - int timeout = FLAG_TIMEOUT; + int timeout; #if DEVICE_I2CSLAVE if (obj_s->slave) { /* re-init slave when stop is requested */ @@ -908,20 +974,51 @@ int i2c_stop(i2c_t *obj) } #endif - // Ensure the transmission is started before sending a stop - if ((handle->Instance->CR2 & (uint32_t)I2C_CR2_RD_WRN) == 0) { - timeout = FLAG_TIMEOUT; - while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TXIS)) { + if(obj_s->state == STM_I2C_IDLE) + { + // We get here if we got a NACK earlier and the operation aborted itself. + // The hardware will generate a stop condition, so wait for that to happen. + timeout = BYTE_TIMEOUT/8; + while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_STOPF)) { if ((timeout--) == 0) { - return I2C_ERROR_BUS_BUSY; + DEBUG_PRINTF("timeout in i2c_byte_write waiting for I2C_FLAG_STOPF\r\n"); + return 2; } } + + __HAL_I2C_CLEAR_FLAG(handle, I2C_FLAG_STOPF); + + return 0; + } + else if(!(obj_s->state == STM_I2C_SB_READ_IN_PROGRESS || obj_s->state == STM_I2C_SB_WRITE_IN_PROGRESS)) + { + // Cannot use single-byte functions while a transaction is in progress. + return 3; } // Generate the STOP condition handle->Instance->CR2 = I2C_CR2_STOP; - timeout = FLAG_TIMEOUT; + // Unfortunately, the STM32 I2C peripheral seems to be unable to generate a STOP condition immediately after the address + // byte. Simply setting CR2 to STOP (the normal procedure if at least one data byte has been sent) does not work + // -- the peripheral will hang forever and leave the bus in a bad state. STMicro seems to passively acknowledge + // this in their code (via their I2C_Flush_TXDR() HAL function). + // As the lesser of two evils, we will have to write out another byte here so that we can recover the bus, + // even though that might cause unintended behavior in some cases. + // Note: It *is* possible to do a zero-data-byte I2C transaction on these devices, but you have to use + // the transaction API, not the single-byte one. + if (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TXIS) != RESET) + { + // If TXIS is set, that means we have just sent the address byte and not any data bytes yet. + handle->Instance->TXDR = 0x00U; + /* Flush TX register if not empty */ + if (__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TXE) == RESET) + { + __HAL_I2C_CLEAR_FLAG(handle, I2C_FLAG_TXE); + } + } + + timeout = BYTE_TIMEOUT; while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_STOPF)) { if ((timeout--) == 0) { return I2C_ERROR_BUS_BUSY; @@ -931,7 +1028,7 @@ int i2c_stop(i2c_t *obj) /* Clear STOP Flag */ __HAL_I2C_CLEAR_FLAG(handle, I2C_FLAG_STOPF); - /* Erase slave address, this wiil be used as a marker + /* Erase slave address, this will be used as a marker * to know when we need to prepare next start */ handle->Instance->CR2 &= ~I2C_CR2_SADD; @@ -941,11 +1038,8 @@ int i2c_stop(i2c_t *obj) */ i2c_sw_reset(obj); - /* In case of mixed usage of the APIs (unitary + SYNC) - * re-init HAL state */ - if (obj_s->XferOperation != I2C_FIRST_AND_LAST_FRAME) { - i2c_init_internal(obj, NULL); - } + // This function restores the I2C to IDLE state. + obj_s->state = STM_I2C_IDLE; return 0; } @@ -954,7 +1048,6 @@ int i2c_byte_read(i2c_t *obj, int last) { struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); - int timeout = FLAG_TIMEOUT; uint32_t tmpreg = handle->Instance->CR2; char data; #if DEVICE_I2CSLAVE @@ -962,7 +1055,15 @@ int i2c_byte_read(i2c_t *obj, int last) return i2c_slave_read(obj, &data, 1); } #endif + + if(obj_s->state != STM_I2C_SB_READ_IN_PROGRESS) + { + // Must be in a read operation in order to read! + return -1; + } + /* Then send data when there's room in the TX fifo */ + int timeout = FLAG_TIMEOUT; if ((tmpreg & I2C_CR2_RELOAD) != 0) { while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TCR)) { if ((timeout--) == 0) { @@ -1009,8 +1110,15 @@ int i2c_byte_write(i2c_t *obj, int data) return i2c_slave_write(obj, (char *) &data, 1); } #endif - if (obj_s->pending_start) { - obj_s->pending_start = 0; + + if(!(obj_s->state == STM_I2C_SB_WRITE_IN_PROGRESS || obj_s->state == STM_I2C_PENDING_START)) + { + // Must either be ready to send address or ready to write data bytes + return 3; + } + + if (obj_s->state == STM_I2C_PENDING_START) + { //* First byte after the start is the address */ tmpreg |= (uint32_t)((uint32_t)data & I2C_CR2_SADD); if (data & 0x01) { @@ -1023,24 +1131,34 @@ int i2c_byte_write(i2c_t *obj, int data) 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 transfer size to 1 */ + tmpreg |= (I2C_CR2_NBYTES & (1 << I2C_CR2_NBYTES_Pos)); /* 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) { - DEBUG_PRINTF("timeout in i2c_byte_write\r\n"); - return 2; - } + // Wait until we get the result for the address byte. + // The hardware clears the I2C_CR2_START bit when the start condition has been sent. + timeout = BYTE_TIMEOUT; + while (handle->Instance->CR2 & I2C_CR2_START) { + if ((timeout--) == 0) { + return 2; } } - /* Enable reload mode as we don't know how many bytes will eb sent */ + + // Set to read or write based on the address + obj_s->state = data & 0x1 ? STM_I2C_SB_READ_IN_PROGRESS : STM_I2C_SB_WRITE_IN_PROGRESS; + + } else { + + if(obj_s->state == STM_I2C_SB_READ_IN_PROGRESS) + { + // Cannot do a write in a read transaction! + return 3; + } + + tmpreg = handle->Instance->CR2; + + /* Enable reload mode as we don't know how many bytes will be sent */ tmpreg |= I2C_CR2_RELOAD; /* Set transfer size to 1 */ tmpreg |= (I2C_CR2_NBYTES & (1 << 16)); @@ -1050,11 +1168,32 @@ int i2c_byte_write(i2c_t *obj, int data) timeout = FLAG_TIMEOUT; while (!__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TXE)) { if ((timeout--) == 0) { + DEBUG_PRINTF("timeout in i2c_byte_write waiting for I2C_FLAG_TXE\r\n"); return 2; } } /* Write byte */ handle->Instance->TXDR = data; + + // Wait until we get the result for that byte + // Since we set NBYTES to 1 and RELOAD to 1, the Transfer Complete Reload flag will set when this byte has transferred. + // Since we set AUTOEND to 0, the MCU will pause the SCL clock from then until we write another byte. + timeout = BYTE_TIMEOUT; + while (!(__HAL_I2C_GET_FLAG(handle, I2C_FLAG_TCR) || __HAL_I2C_GET_FLAG(handle, I2C_FLAG_AF))) { + if ((timeout--) == 0) { + DEBUG_PRINTF("timeout in i2c_byte_write waiting for byte complete\r\n"); + return 2; + } + } + } + + // If I2C_FLAG_AF is set, we got a NACK, and the hardware is currently generating a stop. + // Otherwise, we got an ACK. + if(__HAL_I2C_GET_FLAG(handle, I2C_FLAG_AF)) + { + __HAL_I2C_CLEAR_FLAG(handle, I2C_FLAG_AF); + obj_s->state = STM_I2C_IDLE; + return 0; // NACK } return 1; @@ -1068,8 +1207,8 @@ int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) { struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); - int count = I2C_ERROR_BUS_BUSY, ret = 0; - uint32_t timeout = 0; + int count = I2C_ERROR_BUS_BUSY; + uint32_t xferOptions; #if defined(I2C_IP_VERSION_V1) // Trick to remove compiler warning "left and right operands are identical" in some cases uint32_t op1 = I2C_FIRST_AND_LAST_FRAME; @@ -1088,17 +1227,17 @@ int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) obj_s->XferOperation = I2C_NEXT_FRAME; } } + xferOptions = obj_s->XferOperation; #elif defined(I2C_IP_VERSION_V2) - if ((obj_s->XferOperation == I2C_FIRST_FRAME) || (obj_s->XferOperation == I2C_FIRST_AND_LAST_FRAME) || (obj_s->XferOperation == I2C_LAST_FRAME)) { - if (stop) { - obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME; - } else { - obj_s->XferOperation = I2C_FIRST_FRAME; - } - } else { - // should not happend - error("I2C: abnormal case should not happend"); + if(!i2c_is_ready_for_transaction_start(obj_s->state)) + { + // I2C peripheral is not ready to start a new transaction + return I2C_ERROR_INVALID_USAGE; } + + prep_for_restart_if_needed(obj_s); + + xferOptions = get_hal_xfer_options(obj_s, stop); #endif obj_s->event = 0; @@ -1108,10 +1247,11 @@ int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) */ i2c_ev_err_enable(obj, i2c_get_irq_handler(obj)); - ret = HAL_I2C_Master_Seq_Receive_IT(handle, address, (uint8_t *) data, length, obj_s->XferOperation); + int ret = HAL_I2C_Master_Seq_Receive_IT(handle, address, (uint8_t *) data, length, xferOptions); if (ret == HAL_OK) { - timeout = BYTE_TIMEOUT_US * (length + 1); + STM_I2C_SET_STATE(obj_s, STM_I2C_TR_WRITE_IN_PROGRESS); + uint32_t timeout = BYTE_TIMEOUT_US * (length + 1); /* transfer started : wait completion or timeout */ while (!(obj_s->event & I2C_EVENT_ALL) && (--timeout != 0)) { wait_us(1); @@ -1121,13 +1261,28 @@ int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) if ((timeout == 0) || (obj_s->event != I2C_EVENT_TRANSFER_COMPLETE)) { DEBUG_PRINTF("TIMEOUT or error in i2c_read\r\n"); + + // Pass up the error code indicating a NACK + if(obj_s->event | I2C_EVENT_ERROR_NO_SLAVE) + { + count = I2C_ERROR_NO_SLAVE; + } + /* re-init IP to try and get back in a working state */ i2c_init_internal(obj, NULL); + STM_I2C_SET_STATE(obj_s, STM_I2C_IDLE); + } else { count = length; + + // If we requested repeated start, go into PENDING_START state so the user can call write_byte(). + // Otherwise, we are now IDLE. + STM_I2C_SET_STATE(obj_s, stop ? STM_I2C_IDLE : STM_I2C_PENDING_START); } } else { DEBUG_PRINTF("ERROR in i2c_read:%d\r\n", ret); + + STM_I2C_SET_STATE(obj_s, STM_I2C_IDLE); } return count; @@ -1137,8 +1292,8 @@ int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) { struct i2c_s *obj_s = I2C_S(obj); I2C_HandleTypeDef *handle = &(obj_s->handle); - int count = I2C_ERROR_BUS_BUSY, ret = 0; - uint32_t timeout = 0; + int count = I2C_ERROR_BUS_BUSY; + uint32_t xferOptions; #if defined(I2C_IP_VERSION_V1) // Trick to remove compiler warning "left and right operands are identical" in some cases @@ -1158,27 +1313,29 @@ int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) obj_s->XferOperation = I2C_NEXT_FRAME; } } + xferOptions = obj_s->XferOperation; #elif defined(I2C_IP_VERSION_V2) - if ((obj_s->XferOperation == I2C_FIRST_FRAME) || (obj_s->XferOperation == I2C_FIRST_AND_LAST_FRAME) || (obj_s->XferOperation == I2C_LAST_FRAME)) { - if (stop) { - obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME; - } else { - obj_s->XferOperation = I2C_FIRST_FRAME; - } - } else { - // should not happend - error("I2C: abnormal case should not happend"); + if(!i2c_is_ready_for_transaction_start(obj_s->state)) + { + // I2C peripheral is not ready to start a new transaction + return I2C_ERROR_INVALID_USAGE; } + + prep_for_restart_if_needed(obj_s); + + xferOptions = get_hal_xfer_options(obj_s, stop); #endif obj_s->event = 0; i2c_ev_err_enable(obj, i2c_get_irq_handler(obj)); - ret = HAL_I2C_Master_Seq_Transmit_IT(handle, address, (uint8_t *) data, length, obj_s->XferOperation); + int ret = HAL_I2C_Master_Seq_Transmit_IT(handle, address, (uint8_t *) data, length, xferOptions); - if (ret == HAL_OK) { - timeout = BYTE_TIMEOUT_US * (length + 1); + if (ret == HAL_OK) + { + STM_I2C_SET_STATE(obj_s, STM_I2C_TR_WRITE_IN_PROGRESS); + uint32_t timeout = BYTE_TIMEOUT_US * (length + 1); /* transfer started : wait completion or timeout */ while (!(obj_s->event & I2C_EVENT_ALL) && (--timeout != 0)) { wait_us(1); @@ -1188,13 +1345,27 @@ int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) if ((timeout == 0) || (obj_s->event != I2C_EVENT_TRANSFER_COMPLETE)) { DEBUG_PRINTF(" TIMEOUT or error in i2c_write\r\n"); + + // Pass up the error code indicating a NACK + if(obj_s->event | I2C_EVENT_ERROR_NO_SLAVE) + { + count = I2C_ERROR_NO_SLAVE; + } + /* re-init IP to try and get back in a working state */ i2c_init_internal(obj, NULL); + STM_I2C_SET_STATE(obj_s, STM_I2C_IDLE); + } else { count = length; + + // If we requested repeated start, go into PENDING_START state so the user can call write_byte(). + // Otherwise, we are now IDLE. + STM_I2C_SET_STATE(obj_s, stop ? STM_I2C_IDLE : STM_I2C_PENDING_START); } } else { DEBUG_PRINTF("ERROR in i2c_write\r\n"); + STM_I2C_SET_STATE(obj_s, STM_I2C_IDLE); } return count; @@ -1207,28 +1378,48 @@ void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) struct i2c_s *obj_s = I2C_S(obj); #if DEVICE_I2C_ASYNCH - /* Handle potential Tx/Rx use case */ - if ((obj->tx_buff.length) && (obj->rx_buff.length)) { #if defined(I2C_IP_VERSION_V1) + if ((obj->tx_buff.length) && (obj->rx_buff.length)) { if (obj_s->stop) { obj_s->XferOperation = I2C_LAST_FRAME; } else { obj_s->XferOperation = I2C_NEXT_FRAME; } + HAL_I2C_Master_Seq_Receive_IT(hi2c, obj_s->address, (uint8_t *)obj->rx_buff.buffer, obj->rx_buff.length, obj_s->XferOperation); + } else #elif defined(I2C_IP_VERSION_V2) - if (obj_s->stop) { - obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME; - } else { - obj_s->XferOperation = I2C_FIRST_FRAME; + if(obj_s->state == STM_I2C_ASYNC_WRITE_IN_PROGRESS) + { + if(obj->rx_buff.length > 0) + { + // This is a write-then-read transaction, switch to reading. + uint32_t xferOptions = obj_s->stop ? I2C_FIRST_AND_LAST_FRAME : I2C_FIRST_FRAME; + HAL_I2C_Master_Seq_Receive_IT(hi2c, obj_s->address, (uint8_t *) obj->rx_buff.buffer, obj->rx_buff.length, + xferOptions); + obj_s->state = STM_I2C_ASYNC_READ_IN_PROGRESS; + } + else + { + // Write-only async transaction, we're done. + obj_s->event |= I2C_EVENT_TRANSFER_COMPLETE; + + if(!obj_s->stop && !(obj_s->event & I2C_EVENT_ERROR_NO_SLAVE)) + { + // If the transaction was successful and we did a repeated start, update state appropriately. + obj_s->state = STM_I2C_PENDING_START; + } + else + { + obj_s->state = STM_I2C_IDLE; + } } -#endif - - HAL_I2C_Master_Seq_Receive_IT(hi2c, obj_s->address, (uint8_t *)obj->rx_buff.buffer, obj->rx_buff.length, obj_s->XferOperation); } else +#endif #endif { - /* Set event flag */ - obj_s->event = I2C_EVENT_TRANSFER_COMPLETE; + // Set event flag. Note: We still get the complete callback even if an error was encountered, + // so use |= to preserve any error flags. + obj_s->event |= I2C_EVENT_TRANSFER_COMPLETE; } } @@ -1239,9 +1430,24 @@ void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) struct i2c_s *obj_s = I2C_S(obj); #ifdef I2C_IP_VERSION_V1 hi2c->PreviousState = I2C_STATE_NONE; +#elif defined(I2C_IP_VERSION_V2) + if(obj_s->state == STM_I2C_ASYNC_READ_IN_PROGRESS) + { + if(!obj_s->stop && !(obj_s->event & I2C_EVENT_ERROR_NO_SLAVE)) + { + // If the transaction was successful and we did a repeated start, update state appropriately. + obj_s->state = STM_I2C_PENDING_START; + } + else + { + obj_s->state = STM_I2C_IDLE; + } + } #endif - /* Set event flag */ - obj_s->event = I2C_EVENT_TRANSFER_COMPLETE; + + // Set event flag. Note: We still get the complete callback even if an error was encountered, + // so use |= to preserve any error flags. + obj_s->event |= I2C_EVENT_TRANSFER_COMPLETE; } void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) @@ -1265,6 +1471,15 @@ void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) /* Keep Set event flag */ event_code = (I2C_EVENT_TRANSFER_EARLY_NACK) | (I2C_EVENT_ERROR_NO_SLAVE); } + + // If we only got a NACK, no reason to call the cavalry + if(handle->ErrorCode == HAL_I2C_ERROR_AF) + { + STM_I2C_SET_STATE(obj_s, STM_I2C_IDLE); // Hardware stops the transaction when it gets a NACK + obj_s->event = event_code; + return; + } + DEBUG_PRINTF("HAL_I2C_ErrorCallback:%d, index=%d\r\n", (int) hi2c->ErrorCode, obj_s->index); /* re-init IP to try and get back in a working state */ @@ -1524,11 +1739,15 @@ void i2c_transfer_asynch(i2c_t *obj, const void *tx, size_t tx_length, void *rx, obj_s->address = address; obj_s->stop = stop; + // For async I2C interrupts, we need to use a somewhat odd structure. We redirect the ISR to call a C++ function, + // I2C::irq_handler_asynch(). That function then sends the IRQ back down to the HAL layer, and also + // does some RTOS stuff to wake up any waiting threads. i2c_ev_err_enable(obj, handler); + int halRet = HAL_OK; +#if defined(I2C_IP_VERSION_V1) /* Set operation step depending if stop sending required or not */ if ((tx_length && !rx_length) || (!tx_length && rx_length)) { -#if defined(I2C_IP_VERSION_V1) // Trick to remove compiler warning "left and right operands are identical" in some cases uint32_t op1 = I2C_FIRST_AND_LAST_FRAME; uint32_t op2 = I2C_LAST_FRAME; @@ -1546,39 +1765,56 @@ void i2c_transfer_asynch(i2c_t *obj, const void *tx, size_t tx_length, void *rx, obj_s->XferOperation = I2C_NEXT_FRAME; } } -#elif defined(I2C_IP_VERSION_V2) - if ((obj_s->XferOperation == I2C_FIRST_FRAME) || (obj_s->XferOperation == I2C_FIRST_AND_LAST_FRAME) || (obj_s->XferOperation == I2C_LAST_FRAME)) { - if (stop) { - obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME; - } else { - obj_s->XferOperation = I2C_FIRST_FRAME; - } - } else { - // should not happend - error("I2C: abnormal case should not happend"); - } -#endif + if (tx_length > 0) { - HAL_I2C_Master_Seq_Transmit_IT(handle, address, (uint8_t *)tx, tx_length, obj_s->XferOperation); + halRet = HAL_I2C_Master_Seq_Transmit_IT(handle, address, (uint8_t *)tx, tx_length, obj_s->XferOperation); } - if (rx_length > 0) { - HAL_I2C_Master_Seq_Receive_IT(handle, address, (uint8_t *)rx, rx_length, obj_s->XferOperation); + else { // RX + halRet = HAL_I2C_Master_Seq_Receive_IT(handle, address, (uint8_t *)rx, rx_length, obj_s->XferOperation); } } else if (tx_length && rx_length) { - /* Two steps operation, don't modify XferOperation, keep it for next step */ -#if defined(I2C_IP_VERSION_V1) + // Trick to remove compiler warning "left and right operands are identical" in some cases uint32_t op1 = I2C_FIRST_AND_LAST_FRAME; uint32_t op2 = I2C_LAST_FRAME; if ((obj_s->XferOperation == op1) || (obj_s->XferOperation == op2)) { - HAL_I2C_Master_Seq_Transmit_IT(handle, address, (uint8_t *)tx, tx_length, I2C_FIRST_FRAME); + halRet = HAL_I2C_Master_Seq_Transmit_IT(handle, address, (uint8_t *)tx, tx_length, I2C_FIRST_FRAME); } else if ((obj_s->XferOperation == I2C_FIRST_FRAME) || (obj_s->XferOperation == I2C_NEXT_FRAME)) { - HAL_I2C_Master_Seq_Transmit_IT(handle, address, (uint8_t *)tx, tx_length, I2C_NEXT_FRAME); + halRet = HAL_I2C_Master_Seq_Transmit_IT(handle, address, (uint8_t *)tx, tx_length, I2C_NEXT_FRAME); } + } #elif defined(I2C_IP_VERSION_V2) - HAL_I2C_Master_Seq_Transmit_IT(handle, address, (uint8_t *)tx, tx_length, I2C_FIRST_FRAME); + + prep_for_restart_if_needed(obj_s); + + uint32_t xferOptions = get_hal_xfer_options(obj_s, stop); + + if(!i2c_is_ready_for_transaction_start(obj_s->state)) + { + // I2C peripheral is not ready to start a new transaction + return; + } + + if ((tx_length && !rx_length) || (!tx_length && rx_length)) { + + if (tx_length > 0) { + obj_s->state = STM_I2C_ASYNC_WRITE_IN_PROGRESS; + halRet = HAL_I2C_Master_Seq_Transmit_IT(handle, address, (uint8_t *)tx, tx_length, xferOptions); + } + else { // RX + obj_s->state = STM_I2C_ASYNC_READ_IN_PROGRESS; + halRet = HAL_I2C_Master_Seq_Receive_IT(handle, address, (uint8_t *)rx, rx_length, xferOptions); + } + + } else if (tx_length && rx_length) { + obj_s->state = STM_I2C_ASYNC_WRITE_IN_PROGRESS; + halRet = HAL_I2C_Master_Seq_Transmit_IT(handle, address, (uint8_t *) tx, tx_length, xferOptions); + } #endif + + if(halRet != HAL_OK) { + DEBUG_PRINTF("Error %d trying to start async I2C transaction.\n", halRet); } } @@ -1598,15 +1834,13 @@ uint32_t i2c_irq_handler_asynch(i2c_t *obj) uint8_t i2c_active(i2c_t *obj) { - struct i2c_s *obj_s = I2C_S(obj); - I2C_HandleTypeDef *handle = &(obj_s->handle); - if (handle->State == HAL_I2C_STATE_READY) { - return 0; - } else { - return 1; - } +#ifdef I2C_IP_VERSION_V2 + return !i2c_is_ready_for_transaction_start(obj_s->state); +#else + return obj_s->handle.State != HAL_I2C_STATE_READY; +#endif } void i2c_abort_asynch(i2c_t *obj) @@ -1619,6 +1853,8 @@ void i2c_abort_asynch(i2c_t *obj) uint16_t Dummy_DevAddress = 0x00; HAL_I2C_Master_Abort_IT(handle, Dummy_DevAddress); + + STM_I2C_SET_STATE(obj_s, STM_I2C_IDLE); } #if MBED_CONF_TARGET_I2C_TIMING_VALUE_ALGO diff --git a/targets/TARGET_STM/stm_i2c_api.h b/targets/TARGET_STM/stm_i2c_api.h new file mode 100644 index 00000000000..e9191ebf89b --- /dev/null +++ b/targets/TARGET_STM/stm_i2c_api.h @@ -0,0 +1,97 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2022 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef MBED_STM_I2C_API_H +#define MBED_STM_I2C_API_H + +#include "i2c_device.h" + +#include + +/** + * State of the I2C peripheral. + * Note: SB stands for Single Byte, TR stands for Transaction + */ +typedef enum stm_i2c_state +{ + STM_I2C_IDLE, + STM_I2C_PENDING_START, // This state is created either by calling start(), or by completing a transaction with the repeated start flag + STM_I2C_SB_READ_IN_PROGRESS, + STM_I2C_SB_WRITE_IN_PROGRESS, + STM_I2C_TR_READ_IN_PROGRESS, + STM_I2C_TR_WRITE_IN_PROGRESS, + STM_I2C_ASYNC_READ_IN_PROGRESS, + STM_I2C_ASYNC_WRITE_IN_PROGRESS +} stm_i2c_state; + +#ifdef I2C_IP_VERSION_V2 + +/** + * Extended I2C structure containing STM-specific data + */ +struct i2c_s { + /* The 1st 2 members I2CName i2c + * and I2C_HandleTypeDef handle should + * be kept as the first members of this struct + * to ensure i2c_get_obj to work as expected + */ + I2CName i2c; + I2C_HandleTypeDef handle; + uint8_t index; + int hz; + PinName sda; + PinName scl; + IRQn_Type event_i2cIRQ; + IRQn_Type error_i2cIRQ; + volatile stm_i2c_state state; + + /// Used to pass events (the I2C_EVENT_xxx defines) from ISRs to the main thread + volatile uint8_t event; + + int current_hz; +#if DEVICE_I2CSLAVE + uint8_t slave; + volatile uint8_t pending_slave_tx_master_rx; + volatile uint8_t pending_slave_rx_maxter_tx; + uint8_t *slave_rx_buffer; + volatile uint16_t slave_rx_buffer_size; + volatile uint16_t slave_rx_count; +#endif +#if DEVICE_I2C_ASYNCH + /// Address that the current asynchronous transaction is talking to + uint32_t address; + + /// If true, send a stop at the end of the current asynchronous transaction + bool stop; + + /// Specifies which events (the I2C_EVENT_xxx defines) can be passed up to the application from the IRQ handler + uint8_t available_events; +#endif +}; + +#endif + +// Macro that can be used to set the state of an I2C object. +// Compiles to nothing for IP v1 +#ifdef I2C_IP_VERSION_V2 +#define STM_I2C_SET_STATE(i2c_s, new_state) i2c_s->state = new_state +#else +#define STM_I2C_SET_STATE(i2c_s, new_state) (void)i2c_s +#endif + +#endif //MBED_STM_I2C_API_H From 63fbaaf41dfac64929f0bed344738f78ada9fd12 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 27 Nov 2022 21:15:52 -0800 Subject: [PATCH 2/4] Add documentation for I2C_EVENT macros --- hal/include/hal/i2c_api.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/hal/include/hal/i2c_api.h b/hal/include/hal/i2c_api.h index 9a7198581d2..83489102c4d 100644 --- a/hal/include/hal/i2c_api.h +++ b/hal/include/hal/i2c_api.h @@ -35,10 +35,36 @@ * * @{ */ + +/** + * Indicates that an unspecified error has occurred in the transfer. This usually means + * either an internal error in the Mbed MCU's I2C module, or something like an arbitration loss. + * Does not indicate a NACK. + */ #define I2C_EVENT_ERROR (1 << 1) + +/** + * Indicates that the slave did not respond to the address byte of the transfer. + */ #define I2C_EVENT_ERROR_NO_SLAVE (1 << 2) + +/** + * Indicates that the transfer completed successfully. + */ #define I2C_EVENT_TRANSFER_COMPLETE (1 << 3) + +/** + * Indicates that a NACK was received after the address byte, but before the requested number of bytes + * could be transferred. + * + * Note: Not every manufacturer HAL is able to make a distinction between this flag and #I2C_EVENT_ERROR_NO_SLAVE. + * On a NACK, you might conceivably get one or both of these flags. + */ #define I2C_EVENT_TRANSFER_EARLY_NACK (1 << 4) + +/** + * Use this macro to request all possible I2C events. + */ #define I2C_EVENT_ALL (I2C_EVENT_ERROR | I2C_EVENT_TRANSFER_COMPLETE | I2C_EVENT_ERROR_NO_SLAVE | I2C_EVENT_TRANSFER_EARLY_NACK) /**@}*/ From 0c2ee4f142ae2cbefd329b950131f1d315b311d6 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sun, 27 Nov 2022 21:18:40 -0800 Subject: [PATCH 3/4] Add some additional I2C error codes --- hal/include/hal/i2c_api.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hal/include/hal/i2c_api.h b/hal/include/hal/i2c_api.h index 83489102c4d..415445c07e2 100644 --- a/hal/include/hal/i2c_api.h +++ b/hal/include/hal/i2c_api.h @@ -87,7 +87,8 @@ typedef struct i2c_s i2c_t; enum { I2C_ERROR_NO_SLAVE = -1, - I2C_ERROR_BUS_BUSY = -2 + I2C_ERROR_BUS_BUSY = -2, + I2C_ERROR_INVALID_USAGE = -3 ///< Invalid usage of the I2C API, e.g. by mixing single-byte and transactional function calls. }; typedef struct { @@ -255,7 +256,7 @@ int i2c_byte_read(i2c_t *obj, int last); * * @param obj The I2C object * @param data Byte to be written - * @return 0 if NAK was received, 1 if ACK was received, 2 for timeout. + * @return 0 if NAK was received, 1 if ACK was received, 2 for timeout, or 3 for other error. */ int i2c_byte_write(i2c_t *obj, int data); From d59c34b69bbe4f5f95f1c97586adc2e99be6209b Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Sat, 3 Dec 2022 23:19:30 -0800 Subject: [PATCH 4/4] Fix compile error on static pinmap targets --- targets/TARGET_STM/stm_i2c_api.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/targets/TARGET_STM/stm_i2c_api.h b/targets/TARGET_STM/stm_i2c_api.h index e9191ebf89b..7ae17f4b83b 100644 --- a/targets/TARGET_STM/stm_i2c_api.h +++ b/targets/TARGET_STM/stm_i2c_api.h @@ -82,6 +82,11 @@ struct i2c_s { /// Specifies which events (the I2C_EVENT_xxx defines) can be passed up to the application from the IRQ handler uint8_t available_events; #endif + +#if STATIC_PINMAP_READY + int sda_func; + int scl_func; +#endif }; #endif