Skip to content

Commit 06edda0

Browse files
committed
fix[STM32][I2C]: stabilize async completion and recovery flow for hard i2c
enable I2C error irq when async transfer paths are used fall back to polling when scheduler or interrupt context cannot wait for completion complete the wait path on HAL error callback and abort the transfer on async timeout or error allow INT and DMA mode flags to coexist and keep recovery behavior aligned across STM32 I2C IPs
1 parent 3550a8b commit 06edda0

1 file changed

Lines changed: 110 additions & 42 deletions

File tree

bsp/stm32/libraries/HAL_Drivers/drivers/drv_hard_i2c.c

Lines changed: 110 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* 2024-12-10 zzk597 add support for STM32F1 series
1111
* 2024-06-23 wdfk-prog Add blocking modes and distinguish POLL,INT,DMA modes
1212
* 2024-06-23 wdfk-prog Distinguish STM32 I2C timing semantics by IP generation
13+
* 2026-04-20 wdfk-prog Stabilize async completion and recovery flow
1314
*/
1415

1516
#include "drv_hard_i2c.h"
@@ -176,15 +177,17 @@ static rt_err_t stm32_i2c_init(struct stm32_i2c *i2c_drv)
176177
}
177178
#endif /* defined(BSP_I2C_TX_USING_DMA) */
178179
#if defined(BSP_I2C_USING_IRQ)
179-
if ((i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_DMA_TX || i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_DMA_RX)
180-
|| (i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_INT_TX || i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_INT_RX))
180+
if (((i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_DMA_TX) || (i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_DMA_RX))
181+
|| ((i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_INT_TX) || (i2c_drv->i2c_dma_flag & RT_DEVICE_FLAG_INT_RX)))
181182
{
182183
/* In the data transfer function stm32_i2c_master_xfer(), the IT transfer function
183184
HAL_I2C_Master_Seq_Transmit_IT() is used when DMA is not used, so the IT interrupt
184185
must be enable anyway, regardless of the DMA configuration, otherwise
185186
the rt_completion_wait() will always timeout. */
186187
HAL_NVIC_SetPriority(i2c_drv->config->evirq_type, 2, 0);
187188
HAL_NVIC_EnableIRQ(i2c_drv->config->evirq_type);
189+
HAL_NVIC_SetPriority(i2c_drv->config->erirq_type, 2, 0);
190+
HAL_NVIC_EnableIRQ(i2c_drv->config->erirq_type);
188191
}
189192
#endif /* defined(BSP_I2C_USING_IRQ) */
190193

@@ -205,7 +208,8 @@ static rt_err_t stm32_i2c_configure(struct rt_i2c_bus_device *bus)
205208
* @param handle Pointer to the HAL I2C handle.
206209
* @param msg Pointer to the RT-Thread I2C message descriptor.
207210
* @param mode HAL sequential transfer mode.
208-
* @param timeout Timeout in RT-Thread ticks for polling transfer.
211+
* @param timeout Timeout in milliseconds for polling transfer.
212+
* @param async_allowed RT_TRUE when the current context allows IRQ/DMA wait.
209213
* @param need_wait Output flag set to RT_TRUE when IT/DMA path is used.
210214
* @return HAL status returned by the selected HAL receive API.
211215
* @retval HAL_OK Transfer start succeeded.
@@ -216,6 +220,7 @@ static HAL_StatusTypeDef stm32_i2c_master_receive_start(struct stm32_i2c *i2c_ob
216220
struct rt_i2c_msg *msg,
217221
uint32_t mode,
218222
rt_uint32_t timeout,
223+
rt_bool_t async_allowed,
219224
rt_bool_t *need_wait)
220225
{
221226
RT_UNUSED(i2c_obj);
@@ -229,15 +234,15 @@ static HAL_StatusTypeDef stm32_i2c_master_receive_start(struct stm32_i2c *i2c_ob
229234
*need_wait = RT_FALSE;
230235

231236
#if defined(BSP_I2C_RX_USING_DMA)
232-
if ((i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_DMA_RX) && (msg->len >= DMA_TRANS_MIN_LEN))
237+
if (async_allowed && (i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_DMA_RX) && (msg->len >= DMA_TRANS_MIN_LEN))
233238
{
234239
*need_wait = RT_TRUE;
235240
return HAL_I2C_Master_Seq_Receive_DMA(handle, (msg->addr << 1), msg->buf, msg->len, mode);
236241
}
237242
#endif /* defined(BSP_I2C_RX_USING_DMA) */
238243

239244
#if defined(BSP_I2C_RX_USING_INT)
240-
if (i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_INT_RX)
245+
if (async_allowed && (i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_INT_RX))
241246
{
242247
*need_wait = RT_TRUE;
243248
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
257262
* @param handle Pointer to the HAL I2C handle.
258263
* @param msg Pointer to the RT-Thread I2C message descriptor.
259264
* @param mode HAL sequential transfer mode.
260-
* @param timeout Timeout in RT-Thread ticks for polling transfer.
265+
* @param timeout Timeout in milliseconds for polling transfer.
266+
* @param async_allowed RT_TRUE when the current context allows IRQ/DMA wait.
261267
* @param need_wait Output flag set to RT_TRUE when IT/DMA path is used.
262268
* @return HAL status returned by the selected HAL transmit API.
263269
* @retval HAL_OK Transfer start succeeded.
@@ -268,6 +274,7 @@ static HAL_StatusTypeDef stm32_i2c_master_transmit_start(struct stm32_i2c *i2c_o
268274
struct rt_i2c_msg *msg,
269275
uint32_t mode,
270276
rt_uint32_t timeout,
277+
rt_bool_t async_allowed,
271278
rt_bool_t *need_wait)
272279
{
273280
RT_UNUSED(i2c_obj);
@@ -281,15 +288,15 @@ static HAL_StatusTypeDef stm32_i2c_master_transmit_start(struct stm32_i2c *i2c_o
281288
*need_wait = RT_FALSE;
282289

283290
#if defined(BSP_I2C_TX_USING_DMA)
284-
if ((i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_DMA_TX) && (msg->len >= DMA_TRANS_MIN_LEN))
291+
if (async_allowed && (i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_DMA_TX) && (msg->len >= DMA_TRANS_MIN_LEN))
285292
{
286293
*need_wait = RT_TRUE;
287294
return HAL_I2C_Master_Seq_Transmit_DMA(handle, (msg->addr << 1), msg->buf, msg->len, mode);
288295
}
289296
#endif /* defined(BSP_I2C_TX_USING_DMA) */
290297

291298
#if defined(BSP_I2C_TX_USING_INT)
292-
if (i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_INT_TX)
299+
if (async_allowed && (i2c_obj->i2c_dma_flag & RT_DEVICE_FLAG_INT_TX))
293300
{
294301
*need_wait = RT_TRUE;
295302
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,
386393
struct stm32_i2c *i2c_obj;
387394
rt_bool_t is_last = RT_FALSE;
388395
uint32_t mode = 0;
389-
rt_uint32_t timeout;
396+
rt_uint32_t timeout_ms;
397+
390398
if (num == 0)
391399
{
392400
return 0;
@@ -398,39 +406,56 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus,
398406
I2C_HandleTypeDef *handle = &i2c_obj->handle;
399407
RT_ASSERT(handle != RT_NULL);
400408
#if defined(BSP_I2C_USING_IRQ)
409+
rt_bool_t need_abort = RT_FALSE;
401410
struct rt_completion *completion;
402411
completion = &i2c_obj->completion;
403412
#endif /* defined(BSP_I2C_USING_IRQ) */
404413
LOG_D("xfer start %d megs", num);
405414
for (i = 0; i < num; i++)
406415
{
407416
rt_bool_t need_wait = RT_FALSE;
417+
const rt_bool_t scheduler_available = rt_scheduler_is_available();
418+
const rt_bool_t irq_disabled = rt_hw_interrupt_is_disabled();
419+
const rt_bool_t async_allowed = (scheduler_available && !irq_disabled);
420+
408421
msg = &msgs[i];
409422
is_last = (i == (num - 1));
410423
next_msg = is_last ? RT_NULL : &msgs[i + 1];
411424
mode = stm32_i2c_get_xfer_mode(i, msg, next_msg, is_last);
412425
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);
413426
#if defined(STM32_I2C_TIMINGR_IP)
414-
timeout = bus->timeout ? bus->timeout : 100U;
427+
timeout_ms = bus->timeout ? bus->timeout : 100U;
415428
#else
416-
timeout = TIMEOUT_CALC(msg);
429+
timeout_ms = TIMEOUT_CALC(msg);
417430
#endif
431+
#if defined(BSP_I2C_USING_IRQ)
432+
rt_tick_t timeout_tick = rt_tick_from_millisecond(timeout_ms);
433+
rt_completion_init(completion);
434+
#endif /* defined(BSP_I2C_USING_IRQ) */
418435
if (msg->flags & RT_I2C_RD)
419436
{
420437
LOG_D("xfer rec msgs[%d] hal mode = %s", i, stm32_i2c_mode_name(mode));
421-
ret = stm32_i2c_master_receive_start(i2c_obj, handle, msg, mode, timeout, &need_wait);
438+
ret = stm32_i2c_master_receive_start(i2c_obj, handle, msg, mode, timeout_ms, async_allowed, &need_wait);
422439
if (ret != HAL_OK)
423440
{
424-
LOG_E("I2C[%s] Read error(%d)!\n", bus->parent.parent.name, ret);
441+
LOG_E("I2C[%s] Read error(%d)!", bus->parent.parent.name, ret);
425442
goto out;
426443
}
427444
#if defined(BSP_I2C_USING_IRQ)
428445
if (need_wait)
429446
{
430-
ret = rt_completion_wait(completion, timeout);
447+
ret = rt_completion_wait(completion, timeout_tick);
431448
if (ret != RT_EOK)
432449
{
433-
LOG_W("I2C[%s] receive wait failed %d, timeout %d", bus->parent.parent.name, ret, timeout);
450+
need_abort = RT_TRUE;
451+
LOG_W("I2C[%s] receive wait failed %d, timeout %u ms", bus->parent.parent.name, ret, timeout_ms);
452+
goto out;
453+
}
454+
455+
if (handle->ErrorCode != HAL_I2C_ERROR_NONE)
456+
{
457+
need_abort = RT_TRUE;
458+
LOG_E("I2C[%s] receive failed, error code: 0x%08x", bus->parent.parent.name, handle->ErrorCode);
434459
goto out;
435460
}
436461
}
@@ -439,19 +464,27 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus,
439464
else
440465
{
441466
LOG_D("xfer trans msgs[%d] hal mode = %s", i, stm32_i2c_mode_name(mode));
442-
ret = stm32_i2c_master_transmit_start(i2c_obj, handle, msg, mode, timeout, &need_wait);
467+
ret = stm32_i2c_master_transmit_start(i2c_obj, handle, msg, mode, timeout_ms, async_allowed, &need_wait);
443468
if (ret != HAL_OK)
444469
{
445-
LOG_E("I2C[%s] Write error(%d)!\n", bus->parent.parent.name, ret);
470+
LOG_E("I2C[%s] Write error(%d)!", bus->parent.parent.name, ret);
446471
goto out;
447472
}
448473
#if defined(BSP_I2C_USING_IRQ)
449474
if (need_wait)
450475
{
451-
ret = rt_completion_wait(completion, timeout);
476+
ret = rt_completion_wait(completion, timeout_tick);
452477
if (ret != RT_EOK)
453478
{
454-
LOG_W("I2C[%s] transmit wait failed %d, timeout %d", bus->parent.parent.name, ret, timeout);
479+
need_abort = RT_TRUE;
480+
LOG_W("I2C[%s] transmit wait failed %d, timeout %u ms", bus->parent.parent.name, ret, timeout_ms);
481+
goto out;
482+
}
483+
484+
if (handle->ErrorCode != HAL_I2C_ERROR_NONE)
485+
{
486+
need_abort = RT_TRUE;
487+
LOG_E("I2C[%s] transmit failed, error code: 0x%08x", bus->parent.parent.name, handle->ErrorCode);
455488
goto out;
456489
}
457490
}
@@ -469,20 +502,20 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus,
469502
out:
470503
ret = i;
471504
/*
472-
* On TIMINGR-based STM32 I2C IPs (currently F7/H7 in this driver),
473-
* STOPI only enables STOP-event interrupt handling.
474-
* It does not actively generate a STOP condition on the bus.
475-
*
476-
* For legacy STM32 I2C IPs, the HAL error handler already generates a
477-
* STOP condition on AF in master/memory modes, so this driver does not
478-
* manually issue another STOP in the AF path.
479-
*/
505+
* On TIMINGR-based STM32 I2C IPs (currently F7/H7 in this driver),
506+
* STOPI only enables STOP-event interrupt handling.
507+
* It does not actively generate a STOP condition on the bus.
508+
*
509+
* For legacy STM32 I2C IPs, the HAL error handler already generates a
510+
* STOP condition on AF in master/memory modes, so this driver does not
511+
* manually issue another STOP in the AF path.
512+
*/
480513
if (handle->ErrorCode & HAL_I2C_ERROR_AF)
481514
{
482515
LOG_W("I2C[%s] NACK Error", bus->parent.parent.name);
483516
#if defined(STM32_I2C_TIMINGR_IP)
484517
handle->Instance->CR1 |= I2C_IT_STOPI;
485-
#endif /* defined(STM32_I2C_TIMINGR_IP) */
518+
#endif /* defined(STM32_I2C_TIMINGR_IP) */
486519
}
487520
if (handle->ErrorCode & HAL_I2C_ERROR_BERR)
488521
{
@@ -491,8 +524,46 @@ static rt_ssize_t stm32_i2c_master_xfer(struct rt_i2c_bus_device *bus,
491524
handle->Instance->CR1 |= I2C_IT_STOPI;
492525
#else
493526
handle->Instance->CR1 |= I2C_CR1_STOP;
494-
#endif /* defined(STM32_I2C_TIMINGR_IP) */
527+
#endif /* defined(STM32_I2C_TIMINGR_IP) */
528+
}
529+
#if defined(BSP_I2C_USING_IRQ)
530+
if (need_abort && (msg != RT_NULL))
531+
{
532+
if (HAL_I2C_Master_Abort_IT(handle, (msg->addr << 1)) != HAL_OK)
533+
{
534+
LOG_W("I2C[%s] abort start failed, state: %d, error: 0x%08x",
535+
bus->parent.parent.name,
536+
handle->State,
537+
handle->ErrorCode);
538+
}
539+
else
540+
{
541+
rt_uint32_t timeout = timeout_ms;
542+
const rt_bool_t scheduler_available = rt_scheduler_is_available();
543+
const rt_bool_t irq_disabled = rt_hw_interrupt_is_disabled();
544+
545+
while (HAL_I2C_GetState(handle) != HAL_I2C_STATE_READY)
546+
{
547+
if (timeout-- > 0)
548+
{
549+
if (scheduler_available && !irq_disabled)
550+
{
551+
rt_thread_mdelay(1);
552+
}
553+
else
554+
{
555+
rt_hw_us_delay(1000);
556+
}
557+
}
558+
else
559+
{
560+
LOG_E("I2C[%s] timeout! state did not become READY after abort.", bus->parent.parent.name);
561+
break;
562+
}
563+
}
564+
}
495565
}
566+
#endif /* defined(BSP_I2C_USING_IRQ) */
496567

497568
return ret;
498569
#undef TIMEOUT_FREQ_KHZ
@@ -690,27 +761,24 @@ void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
690761

691762
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
692763
{
693-
LOG_W("%s error code %d", hi2c->Instance == I2C1 ? "I2C1"
694-
: hi2c->Instance == I2C2 ? "I2C2"
695-
: hi2c->Instance == I2C3 ? "I2C3"
696-
#ifdef I2C4
697-
: hi2c->Instance == I2C4 ? "I2C4"
698-
#endif /* I2C4 */
699-
: "unknown",
700-
hi2c->ErrorCode);
764+
struct stm32_i2c *i2c_drv = rt_container_of(hi2c, struct stm32_i2c, handle);
765+
766+
LOG_W("%s error code 0x%08x", i2c_drv->config->name, hi2c->ErrorCode);
701767
#if defined(STM32_I2C_TIMINGR_IP)
702-
/* Send stop signal to prevent bus lock-up */
703-
if (hi2c->ErrorCode == HAL_I2C_ERROR_AF)
768+
/*
769+
* Trigger STOP handling immediately in IRQ context so the peripheral can
770+
* leave the error state before the waiting thread starts its recovery path.
771+
*/
772+
if (hi2c->ErrorCode & HAL_I2C_ERROR_AF)
704773
{
705-
LOG_W("I2C NACK Error now stoped");
706774
hi2c->Instance->CR1 |= I2C_IT_STOPI;
707775
}
708-
if (hi2c->ErrorCode == HAL_I2C_ERROR_BERR)
776+
if (hi2c->ErrorCode & HAL_I2C_ERROR_BERR)
709777
{
710-
LOG_W("I2C BUS Error now stoped");
711778
hi2c->Instance->CR1 |= I2C_IT_STOPI;
712779
}
713780
#endif /* defined(STM32_I2C_TIMINGR_IP) */
781+
rt_completion_done(&i2c_drv->completion);
714782
}
715783

716784
#ifdef BSP_USING_HARD_I2C1

0 commit comments

Comments
 (0)