RT-Thread系列08——CAN设备(CAN收发)

====>>> 文章汇总(有代码汇总) <<<====

目标:使用CAN接口实现收发功能。

  • RT-Thread studio,版本: 2.2.6,不一样其实区别也不大
  • RT-Thread:标准版,4.0.3版本
  • 芯片包版本:0.1.9
  • 开发板:正点原子战舰开发板,主控STM32F103ZET6。

1. 硬件准备

  • 硬件设备:CANelyst-Ⅱ,设备的 CAN_L 与开发板的 L 连接,设备的 CAN_H 与开发板的 H 连接。
  • 软件:USB_CAN TOOL,切记切记要安装驱动,否则会一直显示未找到设备。

可以先按照软件上方目录 -> 设备操作 -> USBCAN测试工具中的操作指引,将硬件两个端口连起来,测试硬件是否正常运行,然后在搞软件。

常见问题:

  1. 设备一直未找到(无法连接)。打开电脑设备管理器看CAN设备是否正常识别,如果未识别,说明没安装驱动。安装驱动方法参考:软件上方目录 -> 帮助 -> 帮助文档 -> 2. USB驱动安装与卸载说明书.pdf

2. CAN测试

第一步:在 RT-Thread Settings 中 -> 组件 -> 设备驱动程序 -> CAN设备驱动程序,勾选上 -> 使能CAN硬件过滤器,也先勾选上。

在这里插入图片描述
第二步:添加驱动程序。在RT-Thread Studio 软件的安装目录下:D:\RT-ThreadStudio\repo\Extract\RT-Thread_Source_Code\RT-Thread\4.0.3\bsp\stm32\libraries\HAL_Drivers\

找到drv_can.c放到我们工程的drivers文件夹,找到drv_can.h文件放到我们工程的drivers\include文件夹。

在这里插入图片描述
第三步:添加以下宏定义到board.h文件中,这里使用 CAN1 进行测试。注意:下面这部分宏定义都是自己添加的,默认是没有关于CAN接口的宏定义的。

/*-------------------------- CAN CONFIG BEGIN --------------------------*/

#define BSP_USING_CAN
#define BSP_USING_CAN1
/*#define BSP_USING_CAN2*/

/*-------------------------- CAN CONFIG END --------------------------*/

在这里插入图片描述
第四步:从左侧窗口打开cubemx,勾选上CAN接口,引脚是对的就可以,参数配置可以不用管。然后关闭cubemx界面,生成代码后刷新左侧目录界面。即可在cubemx文件夹中的stm32f1xx_hal_msp.c文件中看到CAN的引脚初始化代码。

在这里插入图片描述

此时如果直接编译,然后在Shell窗口输入list_device命令,就已经能看到can1设备了。

第五步:编写测试代码,程序运行后,打开CAN-USB软件并连接设备,波特率设置1000k,然后在Shell窗口输入can_test命令,即可从CAN-USB软件中看到开发板发送的消息。也可以使用CAN-USB软件发送消息,在Shell窗口可看到接收的消息。

/*
 * 程序清单:这是一个 CAN 设备使用例程
 * 例程导出了 can_test 命令到控制终端
 * 命令调用格式:can_test can1
 * 命令解释:命令第二个参数是要使用的 CAN 设备名称,为空则使用默认的 CAN 设备
 * 程序功能:通过 CAN 设备发送一帧,并创建一个线程接收数据然后打印输出。
*/

#include <rtthread.h>
#include "rtdevice.h"

#define CAN_DEV_NAME       "can1"      /* CAN 设备名称 */

static struct rt_semaphore rx_sem;     /* 用于接收消息的信号量 */
static rt_device_t can_dev;            /* CAN 设备句柄 */

/* 接收数据回调函数 */
static rt_err_t can_rx_call(rt_device_t dev, rt_size_t size)
{
    /* CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    rt_sem_release(&rx_sem);

    return RT_EOK;
}

static void can_rx_thread(void *parameter)
{
    int i;
    rt_err_t res;
    struct rt_can_msg rxmsg = {0};

    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(can_dev, can_rx_call);

#ifdef RT_CAN_USING_HDR
    struct rt_can_filter_item items[5] =
    {
        RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, 0, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x100~0x1ff,hdr 为 - 1,设置默认过滤表 */
        RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, 0, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x300~0x3ff,hdr 为 - 1 */
        RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, 0, 0x7ff, RT_NULL, RT_NULL), /* std,match ID:0x211,hdr 为 - 1 */
        RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL),                  /* std,match ID:0x486,hdr 为 - 1 */
        {0x555, 0, 0, 0, 0x7ff, 7,}                                       /* std,match ID:0x555,hdr 为 7,指定设置 7 号过滤表 */
    };
    struct rt_can_filter_config cfg = {5, 1, items}; /* 一共有 5 个过滤表 */
    /* 设置硬件过滤表 */
    res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg);
    RT_ASSERT(res == RT_EOK);
#endif

    while (1)
    {
        /* hdr 值为 - 1,表示直接从 uselist 链表读取数据 */
        rxmsg.hdr = -1;
        /* 阻塞等待接收信号量 */
        rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        /* 从 CAN 读取一帧数据 */
        rt_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg));
        /* 打印数据 ID 及内容 */
        rt_kprintf("ID:%x", rxmsg.id);
        for (i = 0; i < 8; i++)
        {
            rt_kprintf("%2x", rxmsg.data[i]);
        }

        rt_kprintf("\n");
    }
}

int can_test(int argc, char *argv[])
{
    struct rt_can_msg msg = {0};
    rt_err_t res;
    rt_size_t  size;
    rt_thread_t thread;
    char can_name[RT_NAME_MAX];

    if (argc == 2)
    {
        rt_strncpy(can_name, argv[1], RT_NAME_MAX);
    }
    else
    {
        rt_strncpy(can_name, CAN_DEV_NAME, RT_NAME_MAX);
    }

    /* 查找 CAN 设备 */
    can_dev = rt_device_find(can_name);
    if (!can_dev)
    {
        rt_kprintf("find %s failed!\n", can_name);
        return RT_ERROR;
    }

    /* 初始化 CAN 接收信号量 */
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);

    /* 以中断接收及中断发送方式打开 CAN 设备 */
    res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
    RT_ASSERT(res == RT_EOK);

    /* 创建数据接收线程 */
    thread = rt_thread_create("can_rx", can_rx_thread, RT_NULL, 1024, 25, 10);
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    else
    {
        rt_kprintf("create can_rx thread failed!\n");
    }

    msg.id = 0x78;              /* ID 为 0x78 */
    msg.ide = RT_CAN_STDID;     /* 标准格式 */
    msg.rtr = RT_CAN_DTR;       /* 数据帧 */
    msg.len = 8;                /* 数据长度为 8 */

    /* 待发送的 8 字节数据 */
    msg.data[0] = 0x00;
    msg.data[1] = 0x11;
    msg.data[2] = 0x22;
    msg.data[3] = 0x33;
    msg.data[4] = 0x44;
    msg.data[5] = 0x55;
    msg.data[6] = 0x66;
    msg.data[7] = 0x77;
    /* 发送一帧 CAN 数据 */
    size = rt_device_write(can_dev, 0, &msg, sizeof(msg));
    if (size == 0)
    {
        rt_kprintf("can dev write data failed!\n");
    }

    // 更改后再发送十次
    for(rt_uint8_t send_ind = 0; send_ind < 10; send_ind++)
    {
        rt_thread_mdelay(1000);

        msg.data[0] = msg.data[0] + 0x01;
        msg.data[1] = msg.data[1] + 0x01;
        msg.data[2] = msg.data[2] + 0x01;
        msg.data[3] = msg.data[3] + 0x01;
        msg.data[4] = msg.data[4] + 0x01;
        msg.data[5] = msg.data[5] + 0x01;
        msg.data[6] = msg.data[6] + 0x01;
        msg.data[7] = msg.data[7] + 0x01;
        /* 发送一帧 CAN 数据 */
        size = rt_device_write(can_dev, 0, &msg, sizeof(msg));
        if (size == 0)
        {
            rt_kprintf("can dev write data failed!\n");
        }
    }

    return res;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(can_test, can device sample);

3. 常见问题

我这里测试F1的板子,没有出现问题,但是使用F4的板子就会出现在CAN接口写函数处卡死的问题。

RTT官网有如下解决办法,需要修改CAN的驱动文件:CAN通信写函数卡死

这里附上一个已经修改好的CAN驱动文件,直接替换掉原有的can_drv.c就好了。

修改后的CAN驱动文件,无需积分,直接下载

4. CAN波特率分析

4.1 波特率计算方法

CAN总线的波特率:是指每秒钟传输的位数,通常以Kbps(千位/秒)来表示。
如:500K 即表示 500 000 bit/s

影响CAN通信的参数有以下几个,其中最后一个不参与到波特率的计算。

  1. 时钟频率;
  2. 分频系数;
  3. 同步时间段,SS;
  4. 相位缓冲段1,BS1;定义采样点的位置。其值可以编程为1到16个时间单元,但也可以被自动延长,以补偿因为网络中不同节点的频率差异所造成的相位的正向漂移。
  5. 相位缓冲段2,BS2。定义发送点的位置。其值可以编程为1到8个时间单元,但也可以被自动缩短以补偿相位的负向漂移。
  6. 重新同步跳跃宽度,SJW。定义了在每位中可以延长或缩短多少个时间单元的上限。其值可以编程为1到4个时间单元。

波特率计算方式为:
波特率 = 时钟频率 / 分频系数 / (同步时间段 + BS1 + BS2)
其中,
1. 同步时间段固定为 1TS。
2. BS1:1TS - 16 TS.
3. BS2:1TS - 8TS.

举例:

比如,STM32F103芯片的CAN设备挂在 APB1 时钟上。将时钟设置为 36Mhz。
在这里插入图片描述

CAN设备的波特率计算如下,可以看到是符合上面公式的算法的。
在这里插入图片描述

再比如,STM32F427芯片的CAN设备挂在 APB1 时钟上。将时钟设置为 45Mhz。
在这里插入图片描述

CAN设备的波特率计算如下,可以看到依旧是符合上面公式的算法的。
在这里插入图片描述

4.2 RT-Thread中波特率定义

在 CAN 设备的驱动文件中,根据不同的芯片提供了不同波特率设置的表格。这个表格就是根据上面的公式计算出来的。
在这里插入图片描述

但是这个表格并不是很全。
比如,F4芯片的这个波特率设置表,是按照 45Mhz 的时钟频率写的参数,但是对于比如STM32F407的芯片而言,APB1的时钟最大为42Mhz,显然计算出来肯定是不一样的。

因此,当时钟频率不同的时候,可以自己更改一下上面的表格。以下是时钟频率为42M时使用的,有需要的可以试试(第3节的配置文件里面有)。

static const struct stm32_baud_rate_tab can_baud_rate_tab[] =
{
    {CAN1MBaud, (CAN_SJW_2TQ | CAN_BS1_9TQ  | CAN_BS2_4TQ | 3)},
    {CAN800kBaud, (CAN_SJW_2TQ | CAN_BS1_8TQ  | CAN_BS2_4TQ | 4)},
    {CAN500kBaud, (CAN_SJW_2TQ | CAN_BS1_9TQ  | CAN_BS2_4TQ | 6)},
    {CAN250kBaud, (CAN_SJW_2TQ | CAN_BS1_9TQ  | CAN_BS2_4TQ | 12)},
    {CAN125kBaud, (CAN_SJW_2TQ | CAN_BS1_9TQ  | CAN_BS2_4TQ | 24)},
    {CAN100kBaud, (CAN_SJW_2TQ | CAN_BS1_9TQ  | CAN_BS2_4TQ | 30)},
    {CAN50kBaud, (CAN_SJW_2TQ | CAN_BS1_9TQ  | CAN_BS2_4TQ | 60)},
    {CAN20kBaud, (CAN_SJW_2TQ | CAN_BS1_9TQ  | CAN_BS2_4TQ | 150)},
    {CAN10kBaud, (CAN_SJW_2TQ | CAN_BS1_9TQ  | CAN_BS2_4TQ | 300)}
};
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
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通信部分使用的例程修改的,这部分还好,就是使用硬件滤波功能时,会有报错提示。
概述 随着科技的发展,数字仪表的应用越来越广泛,逐渐替代传统型机械仪表。本应用就是基于ART-Pi开发板,使用RT-Thread系统设计开发的,面向工程机械行业的数字仪表。本应用通过CAN总线采集发动机和控制器参数,将发动机转速、冷却液温度、燃油液位等参数直观的显示出来。 开发环境硬件:ART-Pi+自制扩展板+自制屏幕 RT-Thread版本:4.0.3 开发工具及版本:RT-Thread Studio 1.1.5 RT-Thread使用情况概述程序基于ART-Pi开发板模板工程创建,BSP版本为1.0.1,RT-Thread版本为4.0.3。 程序中使用的组件包括UART设备驱动、CAN设备驱动、I2C设备驱动、PIN设备驱动、ADC设备驱动等,使用touchgfx库和gt9147软件包,其中对touchgfx库文件和gt9147软件包根据硬件资源进行适当修改。硬件方面使用了GPIO、UART、I2C、SDRAM、LCD、FDCAN、ADC等。 硬件框架 硬件结构框图如上图所示,核心板为ART-Pi开发板,板载SPI flash,SDRAM和RGB888接口。RGB888接口与LCD显示屏连接,用于显示数据;LED指示系统运行状态,系统运行后,LED以2Hz频率闪烁;CAN模块通过扩展板与ART-Pi连接,使用TJA1050 CAN收发器,与其它CAN设备进行数据交互;ART-Pi接收CAN模块数据,并传输给LCD,同时根据当前档位,发送转速控制数据,控制发动机转速。(程序中所有CAN数据帧均为模拟ID,与实际ID不同) 软件框架说明系统读取ADC数值,根据ADC数值发送转速控制数据,外部CAN设备接收到转速控制数据后,控制发动机转速,并将转速发出,系统接收外部CAN设备发送转速、水温、油位等数据,与ADC档位一起传输到LCD数据传输模块,LCD数据传输模块将档位、转速、水温、油位数据传输到LCD显示模块,将数据显示到屏幕上。 软件模块说明ADC模块:adc_thread_entry为ADC模块采集进程,间隔100ms采集电位计AD值。 CAN处理模块:CAN处理模块分为接收模块和发送模块。can_rx_thread为CAN接收进程,接收发动机转速、燃油液位和水温数据。can_tx_thread为CAN发送进程,根据ADC模块采集电位计AD值转换成的档位,向外发送控制转速。 LCD数据处理模块:LCD数据处理模块使用信号队列向LCD发送档位、转速、水温、燃油液位数据。LCD数据处理模块作为ART-Pi硬件与LCD显示之间的一个桥梁,将ART-Pi数据传输给touchgfx的model,实现硬件与touchgfx数据交互。 LCD显示模块: Touchgfx使用MVP架构实现和硬件的双向交互。如下图所示,Model提供数据,View负责显示,Presenter负责逻辑的处理。 程序中,在Model模块的Model::tick函数中接收消息队列数据,当当前显示页面为仪表盘页面时,将数据变化传输到Presenter,再将数据发送给View将数据显示处理。 界面进入仪表显示界面后,会将页面更新通知到Model,使Model中的数据更新有效,使页面在进入仪表盘页面时转速、水温、油位等数据及时更新。 演示效果视频: 视频内容说明:视频中左侧为CAN分析仪,主要用于数据对比显示,对比显LCD显示数据和CAN数据差异;右下角为串口转CAN上位机(下称上位机),用于与ART-Pi进行CAN数据交互,模拟CAN控制器。ART-Pi上面背有一个扩展板,使用一个系统运行指示LED,一个电位计和一路CAN。系统启动后,LED开始闪烁。电位计用于调整档位,ART-Pi根据档位发送转速控制帧数据,上位机接收到ART-Pi发送转速后,按设定转速发出转速;同时上位机可以发出水温和油位数据显示到LCD屏幕上。上位机无数据发出后3秒,仪表数据清零,指针复位。 比赛感悟本次比赛是我第一次接触RT-Thread系统,通过近一段时间学习,逐渐掌握了一些RT-Thread系统的工作方式,了解其运行机制。通过RT-Thread Studio进行简单的配置,即可让系统运行起来,减少了很多系统方面的设置,对初学者比较容易上手。 网站的文档中心就是一个学习的宝库,里面涉及内容广泛、详细,并配有详细的示例说明,初学者基本可以只通过文档就可以把单片机基本外设功能实现,对初学者学习能起到很大的作用。社区论坛有大量的开发者在使用中提出的问题,通过问题检索,方便查找自己在学习中遇到的问题,同时官方提供的讨论群非常活跃,回答问题及时,大大的赞。RT-Thread提供了各类最新、最流行的软件包,可以很方便的添加到工程中,对项目快速开发提供了很大的帮助。 最后感谢主办方、承办方、赞助商提供了

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值