rt-thread icon indicating copy to clipboard operation
rt-thread copied to clipboard

[Bug] CAN总线发送(_can_int_tx)在链路波动时会阻塞

Open unnamed2 opened this issue 6 months ago • 3 comments

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

unnamed2 avatar May 29 '25 08:05 unnamed2

https://github.com/RT-Thread/rt-thread/blob/fe1308976ee6268d50890b4df7adecb182e309e5/bsp/stm32/libraries/HAL_Drivers/drivers/drv_can.c#L707

  • sce中断有定义且有做处理
  • 请排查一下是否进入该中断,进入后的具体中断标志是什么触发的.中断处理程序有没有执行错误处理

wdfk-prog avatar May 31 '25 00:05 wdfk-prog

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错误,这时能正确唤醒被挂起的发送线程。

unnamed2 avatar May 31 '25 05:05 unnamed2

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发送产生影响

wdfk-prog avatar May 31 '25 13:05 wdfk-prog