[Bug] CAN总线发送(_can_int_tx)在链路波动时会阻塞
RT-Thread Version
5.1.0
Hardware Type/Architectures
STM32F407VET6
Develop Toolchain
RT-Thread Studio
Describe the bug
在CAN总线上设备掉电时发送CAN报文,STM32F407会产生SCE中断(错误码并非RT_CAN_BUS_ACK_ERR),drv_can中没有调用rt_hw_can_isr来执行
rt_completion_done(&(tx_fifo->buffer[no].completion));
这会导致 _can_int_tx 中
rt_completion_wait(&(tx_tosnd->completion), RT_WAITING_FOREVER)
永远不会返回。我尝试将他改成
if(RT_EOK != rt_completion_wait(&(tx_tosnd->completion), 100))
{
/* send timeout. */
level = rt_hw_interrupt_disable();
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
rt_hw_interrupt_enable(level);
rt_sem_release(&(tx_fifo->sem));
// rt_kprintf("CAN send timeout, no %d\n", no);
goto err_ret;
}
来解决,不知道是否正确
Other additional context
No response
https://github.com/RT-Thread/rt-thread/blob/fe1308976ee6268d50890b4df7adecb182e309e5/bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c#L707
- sce中断有定义且有做处理
- 请排查一下是否进入该中断,进入后的具体中断标志是什么触发的.中断处理程序有没有执行错误处理
rt-thread/bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c
Line 707 in [fe13089](/RT-Thread/rt-thread/commit/fe1308976ee6268d50890b4df7adecb182e309e5) static void _can_sce_isr(struct rt_can_device *can)`` * sce中断有定义且有做处理
* 请排查一下是否进入该中断,进入后的具体中断标志是什么触发的.中断处理程序有没有执行错误处理
感谢回复。
在我使用的版本中, _can_sce_isr的代码是直接写在CAN1_SCE_IRQHandler中的,经过对比,他们的实现上的差距并不算很大。
经过排查,在设备掉电瞬间发送报文会产生SCE中断,此时ESR寄存器的值为0x1800013(在被读取之后变更为0x0800003), 对应着RT_CAN_BUS_BIT_PAD_ERR,新版_can_sce_isr中和我使用的旧版本中对此错误的处理是一致的,仅仅对计数+1以记录错误,随后并未调用rt_hw_can_isr来进行处理,这也是导致rt_completion_done(&(tx_fifo->buffer[no].completion));无法返回的原因。
在我的案例中,设备掉电之后,我尝试发送CAN报文会正常产生RT_CAN_BUS_ACK_ERR错误,这时能正确唤醒被挂起的发送线程。
rt-thread/bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c
Line 707 in [fe13089](/RT-Thread/rt-thread/commit/fe1308976ee6268d50890b4df7adecb182e309e5) static void _can_sce_isr(struct rt_can_device *can)``
- sce中断有定义且有做处理
* 请排查一下是否进入该中断,进入后的具体中断标志是什么触发的.中断处理程序有没有执行错误处理感谢回复。
在我使用的版本中, _can_sce_isr的代码是直接写在CAN1_SCE_IRQHandler中的,经过对比,他们的实现上的差距并不算很大。
经过排查,在设备掉电瞬间发送报文会产生SCE中断,此时ESR寄存器的值为0x1800013(在被读取之后变更为0x0800003), 对应着RT_CAN_BUS_BIT_PAD_ERR,新版_can_sce_isr中和我使用的旧版本中对此错误的处理是一致的,仅仅对计数+1以记录错误,随后并未调用rt_hw_can_isr来进行处理,这也是导致rt_completion_done(&(tx_fifo->buffer[no].completion));无法返回的原因。
在我的案例中,设备掉电之后,我尝试发送CAN报文会正常产生RT_CAN_BUS_ACK_ERR错误,这时能正确唤醒被挂起的发送线程。
- 那你能参考这里关于错误处理的代码提个pr吗?
https://github.com/RT-Thread/rt-thread/blob/fe1308976ee6268d50890b4df7adecb182e309e5/bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c#L724-L731
- 应该加入这样的代码就可以了,可以用去验证一下效果.我把
RT_CAN_BUS_ACK_ERR错误处理的逻辑直接copy到RT_CAN_BUS_BIT_PAD_ERR错误处理上了.
case RT_CAN_BUS_BIT_PAD_ERR:
if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_RQCP0))
{
if (!__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK0))
{
rt_hw_can_isr(can, RT_CAN_EVENT_TX_FAIL | 0 << 8);
}
SET_BIT(hcan->Instance->TSR, CAN_TSR_RQCP0);
}
else if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_RQCP1))
{
if (!__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK1))
{
rt_hw_can_isr(can, RT_CAN_EVENT_TX_FAIL | 1 << 8);
}
SET_BIT(hcan->Instance->TSR, CAN_TSR_RQCP1);
}
else if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_RQCP2))
{
if (!__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK2))
{
rt_hw_can_isr(can, RT_CAN_EVENT_TX_FAIL | 2 << 8);
}
SET_BIT(hcan->Instance->TSR, CAN_TSR_RQCP2);
}
else
{
if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TERR0))/*IF AutoRetransmission = ENABLE,ACK ERR handler*/
{
SET_BIT(hcan->Instance->TSR, CAN_TSR_ABRQ0);/*Abort the send request, trigger the TX interrupt,release completion quantity*/
}
else if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TERR1))
{
SET_BIT(hcan->Instance->TSR, CAN_TSR_ABRQ1);
}
else if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TERR2))
{
SET_BIT(hcan->Instance->TSR, CAN_TSR_ABRQ2);
}
}
break;
- 你采用的方案,仅仅是增加的超时的错误返回而已.并不是真正的错误处理
- 或许应该对SCE的所有错误都进行一下上述逻辑的处理.都进行一下,在没有开启
AutoRetransmission时,对于TX发送导致进入的SCE中断执行进入rt_hw_can_isr函数执行错误处理. - 其他错误因为很少遇到,并不清楚会不会对tx发送产生影响