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

使用can驱动会卡死在接收中断,一直产生接收中断

Open Simon-Leung opened this issue 4 years ago • 6 comments

芯片: stm32f429/bsp: stm32/工具链: rt-thread studio,RT-Thread版本-4.03 HAL_Drivers里的can驱动有点问题。

1 插拔线、振动时线松动

会导致发送出错,hcan->State = HAL_CAN_STATE_ERROR;

static int _can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t box_num)
{
    CAN_HandleTypeDef *hcan;
    hcan = &((struct stm32_can *) can->parent.user_data)->CanHandle;
    struct rt_can_msg *pmsg = (struct rt_can_msg *) buf;
    CAN_TxHeaderTypeDef txheader = {0};
    HAL_CAN_StateTypeDef state = hcan->State;

    /* Check the parameters */
    RT_ASSERT(IS_CAN_DLC(pmsg->len));

    if ((state == HAL_CAN_STATE_READY) ||
            (state == HAL_CAN_STATE_LISTENING))
    {
        /*check select mailbox  is empty */
        switch (1 << box_num)
        {
        case CAN_TX_MAILBOX0:
            if (HAL_IS_BIT_SET(hcan->Instance->TSR, CAN_TSR_TME0) != SET)
            {
                /* Change CAN state */
                hcan->State = HAL_CAN_STATE_ERROR;
                /* Return function status */
                return -RT_ERROR;
            }
            break;

2 此时因插拔线导致接收了些乱的帧或者过载

进接收中断,但进HAL_CAN_GetRxMessage因hcan->State为 HAL_CAN_STATE_ERROR退出了,邮箱有数据但就是读不出来,于是不断疯狂进中断,系统就像死机一样。

建议

上面贴的那段代码是不是不应该操作hcan->State,插拔线这种操作,CAN又不是出什么错,一旦变HAL_CAN_STATE_ERROR,所有正常库函数操作不了

Simon-Leung avatar Aug 28 '21 04:08 Simon-Leung

还有个前提,我把CAN.c的发送改了,一直死等发送成功有点坑,加了resend_cnt

rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *data, int msgs)
{
    int size;
    struct rt_can_tx_fifo *tx_fifo;
    uint8_t resend_cnt = 0;

    RT_ASSERT(can != RT_NULL);

    size = msgs;
    tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
    RT_ASSERT(tx_fifo != RT_NULL);

    while (msgs)
    {
        rt_base_t level;
        rt_uint32_t no;
        rt_uint32_t result;
        struct rt_can_sndbxinx_list *tx_tosnd = RT_NULL;

        rt_sem_take(&(tx_fifo->sem), RT_WAITING_FOREVER);
        level = rt_hw_interrupt_disable();
        tx_tosnd = rt_list_entry(tx_fifo->freelist.next, struct rt_can_sndbxinx_list, list);
        RT_ASSERT(tx_tosnd != RT_NULL);
        rt_list_remove(&tx_tosnd->list);
        rt_hw_interrupt_enable(level);

        no = ((rt_uint32_t)tx_tosnd - (rt_uint32_t)tx_fifo->buffer) / sizeof(struct rt_can_sndbxinx_list);
        tx_tosnd->result = RT_CAN_SND_RESULT_WAIT;
        if (can->ops->sendmsg(can, data, no) != RT_EOK)
        {
            /* send failed. */
            level = rt_hw_interrupt_disable();
            rt_list_insert_after(&tx_fifo->freelist, &tx_tosnd->list);
            rt_hw_interrupt_enable(level);
            rt_sem_release(&(tx_fifo->sem));
            resend_cnt++;
            if(resend_cnt < 0x7f)
            {
                continue;
            }
            else
            {
                tx_tosnd->result = RT_CAN_SND_RESULT_ERR;
            }
        }

        can->status.sndchange = 1;
        if(tx_tosnd->result == RT_CAN_SND_RESULT_WAIT)
            rt_completion_wait(&(tx_tosnd->completion), 10);

        level = rt_hw_interrupt_disable();
        result = tx_tosnd->result;
        if (!rt_list_isempty(&tx_tosnd->list))
        {
            rt_list_remove(&tx_tosnd->list);
        }
        rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
        rt_hw_interrupt_enable(level);
        rt_sem_release(&(tx_fifo->sem));

        if (result == RT_CAN_SND_RESULT_OK)
        {
            level = rt_hw_interrupt_disable();
            can->status.sndpkg++;
            rt_hw_interrupt_enable(level);

            data ++;
            msgs -= sizeof(struct rt_can_msg);
            if (!msgs) break;
        }
        else
        {
            level = rt_hw_interrupt_disable();
            can->status.dropedsndpkg++;
            rt_hw_interrupt_enable(level);
            break;
        }
    }

    return (size - msgs);
}

Simon-Leung avatar Aug 28 '21 04:08 Simon-Leung

CAN必须至少有两个设备在总线上

cazure avatar Dec 07 '21 02:12 cazure

还有个前提,我把CAN.c的发送改了,一直死等发送成功有点坑,加了resend_cnt

rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *data, int msgs)
{
    int size;
    struct rt_can_tx_fifo *tx_fifo;
    uint8_t resend_cnt = 0;

    RT_ASSERT(can != RT_NULL);

    size = msgs;
    tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
    RT_ASSERT(tx_fifo != RT_NULL);

    while (msgs)
    {
        rt_base_t level;
        rt_uint32_t no;
        rt_uint32_t result;
        struct rt_can_sndbxinx_list *tx_tosnd = RT_NULL;

        rt_sem_take(&(tx_fifo->sem), RT_WAITING_FOREVER);
        level = rt_hw_interrupt_disable();
        tx_tosnd = rt_list_entry(tx_fifo->freelist.next, struct rt_can_sndbxinx_list, list);
        RT_ASSERT(tx_tosnd != RT_NULL);
        rt_list_remove(&tx_tosnd->list);
        rt_hw_interrupt_enable(level);

        no = ((rt_uint32_t)tx_tosnd - (rt_uint32_t)tx_fifo->buffer) / sizeof(struct rt_can_sndbxinx_list);
        tx_tosnd->result = RT_CAN_SND_RESULT_WAIT;
        if (can->ops->sendmsg(can, data, no) != RT_EOK)
        {
            /* send failed. */
            level = rt_hw_interrupt_disable();
            rt_list_insert_after(&tx_fifo->freelist, &tx_tosnd->list);
            rt_hw_interrupt_enable(level);
            rt_sem_release(&(tx_fifo->sem));
            resend_cnt++;
            if(resend_cnt < 0x7f)
            {
                continue;
            }
            else
            {
                tx_tosnd->result = RT_CAN_SND_RESULT_ERR;
            }
        }

        can->status.sndchange = 1;
        if(tx_tosnd->result == RT_CAN_SND_RESULT_WAIT)
            rt_completion_wait(&(tx_tosnd->completion), 10);

        level = rt_hw_interrupt_disable();
        result = tx_tosnd->result;
        if (!rt_list_isempty(&tx_tosnd->list))
        {
            rt_list_remove(&tx_tosnd->list);
        }
        rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
        rt_hw_interrupt_enable(level);
        rt_sem_release(&(tx_fifo->sem));

        if (result == RT_CAN_SND_RESULT_OK)
        {
            level = rt_hw_interrupt_disable();
            can->status.sndpkg++;
            rt_hw_interrupt_enable(level);

            data ++;
            msgs -= sizeof(struct rt_can_msg);
            if (!msgs) break;
        }
        else
        {
            level = rt_hw_interrupt_disable();
            can->status.dropedsndpkg++;
            rt_hw_interrupt_enable(level);
            break;
        }
    }

    return (size - msgs);
}

亲测可用,大神受我一拜。

haodongnj avatar Dec 14 '21 10:12 haodongnj

CAN必须至少有两个设备在总线上

有多少个设备不重要,关键接口松动会死机,你总不能要求两根线焊死到电路板上吧

Simon-Leung avatar Dec 14 '21 11:12 Simon-Leung

RTT的CAN驱动用下来觉得问题还是不少的,需要遇到具体的问题再自己手动打补丁。后来又发现,我这边会卡在 rt_sem_take(&(tx_fifo->sem), RT_WAITING_FOREVER),秉持着只要不死等那就等不死的想法,绕过去了

haodongnj avatar Dec 24 '21 03:12 haodongnj

https://github.com/RT-Thread/rt-thread/issues/4981#issue-981722139 can发送错误后要取消相应的邮箱发送。(要不然,下次再发送时,没有空邮箱就会一直死等)。然后,在上层自己做重发机制。

yycx2016 avatar Aug 21 '22 15:08 yycx2016