diff --git a/bsp/stm32/libraries/HAL_Drivers/drivers/drv_hard_i2c.c b/bsp/stm32/libraries/HAL_Drivers/drivers/drv_hard_i2c.c index 48fb6a6981e..fc7634422db 100644 --- a/bsp/stm32/libraries/HAL_Drivers/drivers/drv_hard_i2c.c +++ b/bsp/stm32/libraries/HAL_Drivers/drivers/drv_hard_i2c.c @@ -10,6 +10,7 @@ * 2024-12-10 zzk597 add support for STM32F1 series * 2024-06-23 wdfk-prog Add blocking modes and distinguish POLL,INT,DMA modes * 2024-06-23 wdfk-prog Distinguish STM32 I2C timing semantics by IP generation + * 2026-04-20 wdfk-prog Stabilize async completion and recovery flow */ #include "drv_hard_i2c.h" @@ -176,8 +177,8 @@ static rt_err_t stm32_i2c_init(struct stm32_i2c *i2c_drv) } #endif /* defined(BSP_I2C_TX_USING_DMA) */ #if defined(BSP_I2C_USING_IRQ) - if ((i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_DMA_TX || i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_DMA_RX) - || (i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_INT_TX || i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_INT_RX)) + if (((i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_DMA_TX) || (i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_DMA_RX)) + || ((i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_INT_TX) || (i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_INT_RX))) { /* In the data transfer function stm32_i2c_master_xfer(), the IT transfer function HAL_I2C_Master_Seq_Transmit_IT() is used when DMA is not used, so the IT interrupt @@ -185,6 +186,8 @@ static rt_err_t stm32_i2c_init(struct stm32_i2c *i2c_drv) the rt_completion_wait() will always timeout. */ HAL_NVIC_SetPriority(i2c_drv->config->evirq_type, 2, 0); HAL_NVIC_EnableIRQ(i2c_drv->config->evirq_type); + HAL_NVIC_SetPriority(i2c_drv->config->erirq_type, 2, 0); + HAL_NVIC_EnableIRQ(i2c_drv->config->erirq_type); } #endif /* defined(BSP_I2C_USING_IRQ) */ @@ -205,7 +208,8 @@ static rt_err_t stm32_i2c_configure(struct rt_i2c_bus_device *bus) * @param handle Pointer to the HAL I2C handle. * @param msg Pointer to the RT-Thread I2C message descriptor. * @param mode HAL sequential transfer mode. - * @param timeout Timeout in RT-Thread ticks for polling transfer. + * @param timeout Timeout in milliseconds for polling transfer. + * @param async_allowed RT_TRUE when the current context allows IRQ/DMA wait. * @param need_wait Output flag set to RT_TRUE when IT/DMA path is used. * @return HAL status returned by the selected HAL receive API. * @retval HAL_OK Transfer start succeeded. @@ -216,6 +220,7 @@ static HAL_StatusTypeDef stm32_i2c_master_receive_start(struct stm32_i2c *i2c_ob struct rt_i2c_msg *msg, uint32_t mode, rt_uint32_t timeout, + rt_bool_t async_allowed, rt_bool_t *need_wait) { RT_UNUSED(i2c_obj); @@ -229,7 +234,7 @@ static HAL_StatusTypeDef stm32_i2c_master_receive_start(struct stm32_i2c *i2c_ob *need_wait = RT_FALSE; #if defined(BSP_I2C_RX_USING_DMA) - if ((i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_DMA_RX) && (msg->len >= DMA_TRANS_MIN_LEN)) + if (async_allowed && (i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_DMA_RX) && (msg->len >= DMA_TRANS_MIN_LEN)) { *need_wait = RT_TRUE; return HAL_I2C_Master_Seq_Receive_DMA(handle, (msg->addr << 1), msg->buf, msg->len, mode); @@ -237,7 +242,7 @@ static HAL_StatusTypeDef stm32_i2c_master_receive_start(struct stm32_i2c *i2c_ob #endif /* defined(BSP_I2C_RX_USING_DMA) */ #if defined(BSP_I2C_RX_USING_INT) - if (i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_INT_RX) + if (async_allowed && (i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_INT_RX)) { *need_wait = RT_TRUE; return HAL_I2C_Master_Seq_Receive_IT(handle, (msg->addr << 1), msg->buf, msg->len, mode); @@ -257,7 +262,8 @@ static HAL_StatusTypeDef stm32_i2c_master_receive_start(struct stm32_i2c *i2c_ob * @param handle Pointer to the HAL I2C handle. * @param msg Pointer to the RT-Thread I2C message descriptor. * @param mode HAL sequential transfer mode. - * @param timeout Timeout in RT-Thread ticks for polling transfer. + * @param timeout Timeout in milliseconds for polling transfer. + * @param async_allowed RT_TRUE when the current context allows IRQ/DMA wait. * @param need_wait Output flag set to RT_TRUE when IT/DMA path is used. * @return HAL status returned by the selected HAL transmit API. * @retval HAL_OK Transfer start succeeded. @@ -268,6 +274,7 @@ static HAL_StatusTypeDef stm32_i2c_master_transmit_start(struct stm32_i2c *i2c_o struct rt_i2c_msg *msg, uint32_t mode, rt_uint32_t timeout, + rt_bool_t async_allowed, rt_bool_t *need_wait) { RT_UNUSED(i2c_obj); @@ -281,7 +288,7 @@ static HAL_StatusTypeDef stm32_i2c_master_transmit_start(struct stm32_i2c *i2c_o *need_wait = RT_FALSE; #if defined(BSP_I2C_TX_USING_DMA) - if ((i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_DMA_TX) && (msg->len >= DMA_TRANS_MIN_LEN)) + if (async_allowed && (i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_DMA_TX) && (msg->len >= DMA_TRANS_MIN_LEN)) { *need_wait = RT_TRUE; return HAL_I2C_Master_Seq_Transmit_DMA(handle, (msg->addr << 1), msg->buf, msg->len, mode); @@ -289,7 +296,7 @@ static HAL_StatusTypeDef stm32_i2c_master_transmit_start(struct stm32_i2c *i2c_o #endif /* defined(BSP_I2C_TX_USING_DMA) */ #if defined(BSP_I2C_TX_USING_INT) - if (i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_INT_TX) + if (async_allowed && (i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_INT_TX)) { *need_wait = RT_TRUE; return HAL_I2C_Master_Seq_Transmit_IT(handle, (msg->addr << 1), msg->buf, msg->len, mode); @@ -386,7 +393,8 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus, struct stm32_i2c *i2c_obj; rt_bool_t is_last = RT_FALSE; uint32_t mode = 0; - rt_uint32_t timeout; + rt_uint32_t timeout_ms; + if (num == 0) { return 0; @@ -398,6 +406,7 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus, I2C_HandleTypeDef *handle = &i2c_obj->handle; RT_ASSERT(handle != RT_NULL); #if defined(BSP_I2C_USING_IRQ) + rt_bool_t need_abort = RT_FALSE; struct rt_completion *completion; completion = &i2c_obj->completion; #endif /* defined(BSP_I2C_USING_IRQ) */ @@ -405,32 +414,49 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus, for (i = 0; i < num; i++) { rt_bool_t need_wait = RT_FALSE; + const rt_bool_t scheduler_available = rt_scheduler_is_available(); + const rt_bool_t irq_disabled = rt_hw_interrupt_is_disabled(); + const rt_bool_t async_allowed = (scheduler_available && !irq_disabled); + msg = &msgs[i]; is_last = (i == (num - 1)); next_msg = is_last ? RT_NULL : &msgs[i + 1]; mode = stm32_i2c_get_xfer_mode(i, msg, next_msg, is_last); LOG_D("xfer msgs[%d] addr=0x%2x buf=0x%x len= 0x%x flags= 0x%x", i, msg->addr, msg->buf, msg->len, msg->flags); #if defined(STM32_I2C_TIMINGR_IP) - timeout = bus->timeout ? bus->timeout : 100U; + timeout_ms = bus->timeout ? (bus->timeout * 1000U + RT_TICK_PER_SECOND - 1U) / RT_TICK_PER_SECOND : 100U; #else - timeout = TIMEOUT_CALC(msg); -#endif + timeout_ms = TIMEOUT_CALC(msg); +#endif /* defined(STM32_I2C_TIMINGR_IP) */ + +#if defined(BSP_I2C_USING_IRQ) + rt_tick_t timeout_tick = rt_tick_from_millisecond(timeout_ms); + rt_completion_init(completion); +#endif /* defined(BSP_I2C_USING_IRQ) */ if (msg->flags & RT_I2C_RD) { LOG_D("xfer rec msgs[%d] hal mode = %s", i, stm32_i2c_mode_name(mode)); - ret = stm32_i2c_master_receive_start(i2c_obj, handle, msg, mode, timeout, &need_wait); + ret = stm32_i2c_master_receive_start(i2c_obj, handle, msg, mode, timeout_ms, async_allowed, &need_wait); if (ret != HAL_OK) { - LOG_E("I2C[%s] Read error(%d)!\n", bus->parent.parent.name, ret); + LOG_E("I2C[%s] Read error(%d)!", bus->parent.parent.name, ret); goto out; } #if defined(BSP_I2C_USING_IRQ) if (need_wait) { - ret = rt_completion_wait(completion, timeout); + ret = rt_completion_wait(completion, timeout_tick); if (ret != RT_EOK) { - LOG_W("I2C[%s] receive wait failed %d, timeout %d", bus->parent.parent.name, ret, timeout); + need_abort = RT_TRUE; + LOG_W("I2C[%s] receive wait failed %d, timeout %u ms", bus->parent.parent.name, ret, timeout_ms); + goto out; + } + + if (handle->ErrorCode != HAL_I2C_ERROR_NONE) + { + need_abort = RT_TRUE; + LOG_E("I2C[%s] receive failed, error code: 0x%08x", bus->parent.parent.name, handle->ErrorCode); goto out; } } @@ -439,19 +465,27 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus, else { LOG_D("xfer trans msgs[%d] hal mode = %s", i, stm32_i2c_mode_name(mode)); - ret = stm32_i2c_master_transmit_start(i2c_obj, handle, msg, mode, timeout, &need_wait); + ret = stm32_i2c_master_transmit_start(i2c_obj, handle, msg, mode, timeout_ms, async_allowed, &need_wait); if (ret != HAL_OK) { - LOG_E("I2C[%s] Write error(%d)!\n", bus->parent.parent.name, ret); + LOG_E("I2C[%s] Write error(%d)!", bus->parent.parent.name, ret); goto out; } #if defined(BSP_I2C_USING_IRQ) if (need_wait) { - ret = rt_completion_wait(completion, timeout); + ret = rt_completion_wait(completion, timeout_tick); if (ret != RT_EOK) { - LOG_W("I2C[%s] transmit wait failed %d, timeout %d", bus->parent.parent.name, ret, timeout); + need_abort = RT_TRUE; + LOG_W("I2C[%s] transmit wait failed %d, timeout %u ms", bus->parent.parent.name, ret, timeout_ms); + goto out; + } + + if (handle->ErrorCode != HAL_I2C_ERROR_NONE) + { + need_abort = RT_TRUE; + LOG_E("I2C[%s] transmit failed, error code: 0x%08x", bus->parent.parent.name, handle->ErrorCode); goto out; } } @@ -469,20 +503,20 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus, out: ret = i; /* - * On TIMINGR-based STM32 I2C IPs (currently F7/H7 in this driver), - * STOPI only enables STOP-event interrupt handling. - * It does not actively generate a STOP condition on the bus. - * - * For legacy STM32 I2C IPs, the HAL error handler already generates a - * STOP condition on AF in master/memory modes, so this driver does not - * manually issue another STOP in the AF path. - */ + * On TIMINGR-based STM32 I2C IPs (currently F7/H7 in this driver), + * STOPI only enables STOP-event interrupt handling. + * It does not actively generate a STOP condition on the bus. + * + * For legacy STM32 I2C IPs, the HAL error handler already generates a + * STOP condition on AF in master/memory modes, so this driver does not + * manually issue another STOP in the AF path. + */ if (handle->ErrorCode & HAL_I2C_ERROR_AF) { LOG_W("I2C[%s] NACK Error", bus->parent.parent.name); #if defined(STM32_I2C_TIMINGR_IP) handle->Instance->CR1 |= I2C_IT_STOPI; -#endif /* defined(STM32_I2C_TIMINGR_IP) */ +#endif /* defined(STM32_I2C_TIMINGR_IP) */ } if (handle->ErrorCode & HAL_I2C_ERROR_BERR) { @@ -491,8 +525,46 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus, handle->Instance->CR1 |= I2C_IT_STOPI; #else handle->Instance->CR1 |= I2C_CR1_STOP; -#endif /* defined(STM32_I2C_TIMINGR_IP) */ +#endif /* defined(STM32_I2C_TIMINGR_IP) */ } +#if defined(BSP_I2C_USING_IRQ) + if (need_abort && (msg != RT_NULL)) + { + if (HAL_I2C_Master_Abort_IT(handle, (msg->addr << 1)) != HAL_OK) + { + LOG_W("I2C[%s] abort start failed, state: %d, error: 0x%08x", + bus->parent.parent.name, + handle->State, + handle->ErrorCode); + } + else + { + rt_uint32_t timeout = timeout_ms; + const rt_bool_t scheduler_available = rt_scheduler_is_available(); + const rt_bool_t irq_disabled = rt_hw_interrupt_is_disabled(); + + while (HAL_I2C_GetState(handle) != HAL_I2C_STATE_READY) + { + if (timeout-- > 0) + { + if (scheduler_available && !irq_disabled) + { + rt_thread_mdelay(1); + } + else + { + rt_hw_us_delay(1000); + } + } + else + { + LOG_E("I2C[%s] timeout! state did not become READY after abort.", bus->parent.parent.name); + break; + } + } + } + } +#endif /* defined(BSP_I2C_USING_IRQ) */ return ret; #undef TIMEOUT_FREQ_KHZ @@ -690,27 +762,24 @@ void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) { - LOG_W("%s error code %d", hi2c->Instance == I2C1 ? "I2C1" - : hi2c->Instance == I2C2 ? "I2C2" - : hi2c->Instance == I2C3 ? "I2C3" -#ifdef I2C4 - : hi2c->Instance == I2C4 ? "I2C4" -#endif /* I2C4 */ - : "unknown", - hi2c->ErrorCode); + struct stm32_i2c *i2c_drv = rt_container_of(hi2c, struct stm32_i2c, handle); + + LOG_W("%s error code 0x%08x", i2c_drv->config->name, hi2c->ErrorCode); #if defined(STM32_I2C_TIMINGR_IP) - /* Send stop signal to prevent bus lock-up */ - if (hi2c->ErrorCode == HAL_I2C_ERROR_AF) + /* + * Trigger STOP handling immediately in IRQ context so the peripheral can + * leave the error state before the waiting thread starts its recovery path. + */ + if (hi2c->ErrorCode & HAL_I2C_ERROR_AF) { - LOG_W("I2C NACK Error now stoped"); hi2c->Instance->CR1 |= I2C_IT_STOPI; } - if (hi2c->ErrorCode == HAL_I2C_ERROR_BERR) + if (hi2c->ErrorCode & HAL_I2C_ERROR_BERR) { - LOG_W("I2C BUS Error now stoped"); hi2c->Instance->CR1 |= I2C_IT_STOPI; } #endif /* defined(STM32_I2C_TIMINGR_IP) */ + rt_completion_done(&i2c_drv->completion); } #ifdef BSP_USING_HARD_I2C1