RT-Thread CAN驱动问题

本文分析了RT-Thread操作系统中CAN驱动存在的问题,主要集中在当CAN总线出现故障时,驱动的稳定性下降。在数据发送过程中,如果发送失败,可能导致死循环。此外,长时间等待发送完成的线程可能会被无限期挂起,消耗资源。错误中断频繁触发也可能影响程序的正常运行。解决方案包括增加错误处理机制,避免死循环,并优化中断服务。作者建议在遇到此类问题时,深入分析异常现象,寻找根本原因。
摘要由CSDN通过智能技术生成

在比较早的时候就发现RT-Thread的CAN驱动存在一些问题。当时并没有去记录,最近又因为这个驱动问题搞的一个头两个大。因为产品已经发到国外了,就算进行升级也是比较麻烦的。

RT-Thread的CAN驱动在CAN没有出错的时候是可以正常工作的,一旦CAN总线出现一问题,这个驱动就变得不那么稳定了。我觉得在CAN总线发生故障状态下,这个驱动会出现问题的点有以下几个:

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;

    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));
            continue;
        }

        can->status.sndchange = 1;
        rt_completion_wait(&(tx_tosnd->completion), RT_WAITING_FOREVER);

        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);
}
static rt_err_t rt_can_open(struct rt_device *dev, rt_uint16_t oflag)
{
    struct rt_can_device *can;
    char tmpname[16];
    RT_ASSERT(dev != RT_NULL);
    can = (struct rt_can_device *)dev;

    CAN_LOCK(can);

    /* get open flags */
    dev->open_flag = oflag & 0xff;
    if (can->can_rx == RT_NULL)
    {
        if (oflag & RT_DEVICE_FLAG_INT_RX)
        {
            int i = 0;
            struct rt_can_rx_fifo *rx_fifo;

            rx_fifo = (struct rt_can_rx_fifo *) rt_malloc(sizeof(struct rt_can_rx_fifo) +
                      can->config.msgboxsz * sizeof(struct rt_can_msg_list));
            RT_ASSERT(rx_fifo != RT_NULL);

            rx_fifo->buffer = (struct rt_can_msg_list *)(rx_fifo + 1);
            rt_memset(rx_fifo->buffer, 0, can->config.msgboxsz * sizeof(struct rt_can_msg_list));
            rt_list_init(&rx_fifo->freelist);
            rt_list_init(&rx_fifo->uselist);
            rx_fifo->freenumbers = can->config.msgboxsz;
            for (i = 0;  i < can->config.msgboxsz; i++)
            {
                rt_list_insert_before(&rx_fifo->freelist, &rx_fifo->buffer[i].list);
#ifdef RT_CAN_USING_HDR
                rt_list_init(&rx_fifo->buffer[i].hdrlist);
                rx_fifo->buffer[i].owner = RT_NULL;
#endif
            }
            can->can_rx = rx_fifo;

            dev->open_flag |= RT_DEVICE_FLAG_INT_RX;
            /* open can rx interrupt */
            can->ops->control(can, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_RX);
        }
    }

    if (can->can_tx == RT_NULL)
    {
        if (oflag & RT_DEVICE_FLAG_INT_TX)
        {
            int i = 0;
            struct rt_can_tx_fifo *tx_fifo;

            tx_fifo = (struct rt_can_tx_fifo *) rt_malloc(sizeof(struct rt_can_tx_fifo) +
                      can->config.sndboxnumber * sizeof(struct rt_can_sndbxinx_list));
            RT_ASSERT(tx_fifo != RT_NULL);

            tx_fifo->buffer = (struct rt_can_sndbxinx_list *)(tx_fifo + 1);
            rt_memset(tx_fifo->buffer, 0,
                    can->config.sndboxnumber * sizeof(struct rt_can_sndbxinx_list));
            rt_list_init(&tx_fifo->freelist);
            for (i = 0;  i < can->config.sndboxnumber; i++)
            {
                rt_list_insert_before(&tx_fifo->freelist, &tx_fifo->buffer[i].list);
                rt_completion_init(&(tx_fifo->buffer[i].completion));
                tx_fifo->buffer[i].result = RT_CAN_SND_RESULT_OK;
            }

            rt_sprintf(tmpname, "%stl", dev->parent.name);
            rt_sem_init(&(tx_fifo->sem), tmpname, can->config.sndboxnumber, RT_IPC_FLAG_FIFO);
            can->can_tx = tx_fifo;

            dev->open_flag |= RT_DEVICE_FLAG_INT_TX;
            /* open can tx interrupt */
            can->ops->control(can, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_TX);
        }
    }

    can->ops->control(can, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_CAN_INT_ERR);

#ifdef RT_CAN_USING_HDR
    if (can->hdr == RT_NULL)
    {
        int i = 0;
        struct rt_can_hdr *phdr;

        phdr = (struct rt_can_hdr *) rt_malloc(can->config.maxhdr * sizeof(struct rt_can_hdr));
        RT_ASSERT(phdr != RT_NULL);
        rt_memset(phdr, 0, can->config.maxhdr * sizeof(struct rt_can_hdr));
        for (i = 0;  i < can->config.maxhdr; i++)
        {
            rt_list_init(&phdr[i].list);
        }

        can->hdr = phdr;
    }
#endif

    if (!can->timerinitflag)
    {
        can->timerinitflag = 1;

        rt_timer_start(&can->timer);
    }

    CAN_UNLOCK(can);

    return RT_EOK;
}
  1. RT-Thread的CAN驱动在将数据写入发送邮箱的时候如果出错,会形成死循环.。
    如果if (can->ops->sendmsg(can, data, no) != RT_EOK) 出现问题,会continue回到while,如果一直不成功,就一直循环下去成为死循环。之前我在处理这个问题的时候是进行计数,如果写入失败将做出一定的处理,而不是一直在这里死循环。
  2. 将数据写入发送邮箱之后,会以FOREVER的形式等在完成量上rt_completion_wait(&(tx_tosnd->completion), RT_WAITING_FOREVER);,如果一直发送失败,这个完成量无法执行则会将调用发送的线程一直挂起,这个线程如果有其它的事务需要处理将导致一些问题。
  3. open函数默认开启了CAN的错误中断,如果CAN一直发送一直出错,程序将一直进行错误中断服务函数。这将消耗大量的运算资源,甚至整个程序都得不到正常的运行。

当然,这些问题都是有办法解决的,只是在不了解这些潜在问题的情况下如果遇到问题将很难找到问题所在。
到目前为止好像官方都没有提交有效解决这些问题的代码,嗯……只能自己改巴改巴了。
RT-Thread社区也有人对一些问题提出解决方法。例如https://club.rt-thread.org/ask/article/3034.html,其实这些问题更难的是如何发现,像出现以上问题程序并不会跑崩,这就需要深入分析出现的异常现象是什么导致的,找到问题,解决办法总是有的。

RT-Thread作品秀】基于RT-Thread的CAN数据采集终端作者:tlled 概述在测试产品中,需要监听CAN通信上的数据,之前通过CAN适配器连接到电脑,在上位机上监听,但是在户外测试使用PC机不是很方便,使用ART-PI板卡外接显示屏,来显示要查看的设备总线的数据,以图形的方式显示出来,方便查看,我这次要实现的功能是显示显示十个测距传感器的距离,以条形的方式显示每个距离,同时显示测距传感器中最近的一个距离以数字的方式显示。 开发环境硬件:RT-Thread ART-PI STM32H750开发板,DIY 7寸电容触摸显示屏和CAN转换器 RT-Thread版本:RT-Thread 4.0.3 开发工具及版本:RT-Thread Studio 版本2.0.0 RT-Thread使用情况概述内核部分:线程创建,信号量,消息队列 组件部分:串口,CAN,I2C,LCD 软件包部分:FT6236驱动,peripheral_samples中的can_sample 例程,TouchGFX 4.15 其他:无 硬件框架硬件框图如下: 硬件说明: 1、LCD显示屏部分是根据ART-PI显示接口,绘制的转接驱动板,按照7寸的硬件驱动要求,绘制硬件电路设计电路PCB板。 2、显示触摸屏使用I2C协议的电容触摸屏。 3、CAN驱动是使用的mcp2551收发器芯片转接小板 软件框架说明软件总体流程图: 软件部分说明: 修改LCD和触摸屏驱动程序,能够正常显示和触摸。 使用TouchGFX组件,设计显示的界面。 创建消息队列,用于将CAN通信接收到的消息发送给TouchGFX组件,实现数据传输。 CAN通信接收处理。 软件模块说明演示效果https://www.bilibili.com/video/BV1bi4y1w74V/ 代码地址https://gitee.com/gtizhanghui/art-pi-prj 比赛感悟这次项目是在RT-Thread Studo软件创建ART-PI板卡对应的例程上进行修改的,也是第一次使用这个软件创建项目工程到下载板卡上运行,相比之前使用其他的编译工具,这个软件更方便,功能也更强大,可以直接在软件包里面找到相应设备的驱动直接可以应用到项目中,外设组件和设备驱动也可以应用,确实很方面。 这次项目中使用的TouchGFX与板卡之间的数据交互的实现,花费了较多的时间,通过网上查看资料,通过论坛的帮助,最终解决了问题,收获了不少。 CAN通信部分使用的例程修改的,这部分还好,就是使用硬件滤波功能时,会有报错提示。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值