一、何为 CAN?
CAN 的全称为 Controller Area Network,也就是控制局域网络,简称为 CAN。CAN 最早是由德国 BOSCH (博世) 开发的,目前已经是国际标准 (ISO 11898),是当前应用最广泛的现场总线之一。BOSCH 主要是做汽车电子的,因此 CAN 一开始主要是为汽车电子准备的,事实也是如此,CAN 协议目前已经是汽车网络的标准协议。当然了,CAN 不仅仅应用于汽车电子,经过几十年的发展,CAN 协议的高性能和高可靠性已经得到了业界的认可,目前除了汽车电子以外也广泛应用于工业自动化、医疗、工业和船舶等领域。
以汽车电子为例,汽车上有空调、车门、发动机、大量传感器等,这些部件都是通过 CAN 总线连在一起形成一个网络,车载网络结构如图所示:

图中各个单元通过 CAN 总线连接在一起,每个单元都是独立的 CAN 节点。同一个 CAN 网络中所有单元的通信速度必须一致,不同的网络之间通信速度可以不同。比如图中 125Kbps 的 CAN 网络下所有的节点速度都是 125Kbps 的,整个网络由一个网关与其他的网络连接。
CAN 的特点主要有以下几点:
①、多主控制
在总线空闲时,所有单元都可以发送消息(多主控制),而两个以上的单元同时开始发送消息时,根据标识符(Identifier 以下称为 ID)决定优先级。ID 并不是表示发送的目的地址,而是表示访问总线的消息的优先级。两个以上的单元同时开始发送消息时,对各消息 ID 的每个位进行逐个仲裁比较。仲裁获胜(被判定为优先级最高)的单元可继续发送消息,仲裁失利的单元则立刻停止发送而进行接收工作。
②、系统的柔软性
与总线相连的单元没有类似于 "地址" 的信息。因此在总线上增加单元时,连接在总线上的其它单元的软硬件及应用层都不需要改变。
③、通信速度快,距离远
最高 1Mbps(距离小于 40M),最远可达 10KM(速率低于 5Kbps)。
④、具有错误检测、错误通知和错误恢复功能
所有单元都可以检测错误(错误检测功能),检测出错误的单元会立即同时通知其他所有单元(错误通知功能),正在发送消息的单元一旦检测出错误,会强制结束当前的发送。强制结束发送的单元会不断反复地重新发送此消息直到成功发送为止(错误恢复功能)。
⑤、故障封闭功能
CAN 可以判断出错误的类型是总线上暂时的数据错误(如外部噪声等)还是持续的数据错误(如单元内部故障、驱动器故障、断线等)。由此功能,当总线上发生持续数据错误时,可将引起此故障的单元从总线上隔离出去。
⑥、连接节点多
CAN 总线是可同时连接多个单元的总线。可连接的单元总数理论上是没有限制的。但实际上可连接的单元数受总线上的时间延迟及电气负载的限制。降低通信速度,可连接的单元数增加;提高通信速度,则可连接的单元数减少。
二、CAN 电气属性
CAN 总线使用两根线来连接各个单元:CAN_H 和 CAN_L,CAN 控制器通过判断这两根线上的电位差来得到总线电平,CAN 总线电平分为显性电平和隐性电平两种。显性电平表示逻辑 "0",此时 CAN_H 电平比 CAN_L 高,分别为 3.5V 和 1.5V,电位差为 2V。隐形电平表示逻辑 "1",此时 CAN_H 和 CAN_L 电压都为 2.5V 左右,电位差为 0V。CAN 总线就通过显性和隐形电平的变化来将具体的数据发送出去,如图所示:

CAN 总线上没有节点传输数据的时候一直处于隐性状态,也就是说总线空闲状态的时候一直处于隐性。CAN 网络中的所有单元都通过 CAN_H 和 CAN_L 这两根线连接在一起,如图所示:

图中所有的 CAN 节点单元都采用 CAN_H 和 CAN_L 这两根线连接在一起,CAN_H 接 CAN_H、CAN_L 接 CAN_L,CAN 总线两端要各接一个 120Ω 的端接电阻,用于匹配总线阻抗,吸收信号反射及回拨,提高数据通信的抗干扰能力以及可靠性。
CAN 总线传输速度可达 1Mbps/S,最新的 CAN-FD 最高速度可达 5Mbps/S,甚至更高。CAN 传输速度和总线距离有关,总线距离越短,传输速度越快。
三、CAN 协议
通过 CAN 总线传输数据是需要按照一定协议进行的,CAN 协议提供了 5 种帧格式来传输数据:数据帧、遥控帧、错误帧、过载帧和帧间隔。其中数据帧和遥控帧有标准格式和扩展格式两种,标准格式有 11 位标识符 (ID),扩展格式有 29 个标识符 (ID)。这 5 种帧的用途见表:

1、数据帧
数据帧由 7 段组成:
①、帧起始,表示数据帧开始的段。
②、仲裁段,表示该帧优先级的段。
③、控制段,表示数据的字节数及保留位的段。
④、数据段,数据的内容,一帧可发送 0~8 个字节的数据。
⑤、CRC 段,检查帧的传输错误的段。
⑥、ACK 段,表示确认正常接收的段。
⑦、帧结束,表示数据帧结束的段。
数据帧结构如图所示:

上图给出了数据帧标准格式和扩展格式两种帧结构,图中 D 表示显性电平 0、R 表示隐性电平 1,D/R 表示显性或隐性,也就是 0 或 1,我们来简单分析一下数据帧的这 7 个段。
①、帧起始 帧起始很简单,标准格式和扩展格式都是由一个位的显性电平 0 来表示帧起始。
②、仲裁段 仲裁段表示帧优先级,仲裁段结构如图所示:

标准格式和扩展格式的仲裁段不同,从图可以看出,标准格式的 ID 为 11 位,发送顺序是从 ID10 到 ID0,最高 7 位 ID10~ID4 不能全为隐性 (1),也就是禁止 0X1111111XXXX 这样的 ID。扩展格式的 ID 为 29 位,基本 ID 从 ID28 到 ID18,扩展 ID 由 ID17 到 ID0,基本 ID 与标准格式一样,禁止最高 7 位都为隐性。
③、控制段
控制段由 6 个位构成,表示数据段的字节数,标准格式和扩展格式的控制段略有不同,如图所示:

图中 r1 和 r0 为保留位,保留位必须以显性电平发送。DLC 为数据长度,高位在前,DLC 段有效值范围为 0~8。
④、数据段
数据段也就是帧的有效数据,标准格式和扩展格式相同,可以包含 0~8 个字节的数据,从最高位 (MSB) 开始发送,结构如图所示:

注意,图中数据段的 0~64 为 bit,对应到字节就是 0~8 字节。
⑤、CRC 段
CRC 段保存 CRC 校准值,用于检查帧传输错误,标准格式和扩展格式相同,CRC 段结构如图所示:

从图可以看出,CRC 段由 15 位的 CRC 值与 1 位的 CRC 界定符组成。CRC 值的计算范围包括:帧起始、仲裁段、控制段、数据段,接收方以同样的算法进行计算,然后用计算得到的 CRC 值与此 CRC 段进行比较,如果不一致的话就会报错。
⑥、ACK 段
ACK 段用来确认接收是否正常,标准格式和扩展格式相同,ACK 段结构如图所示:

从图可以看出,ACK 段由 ACK 槽 (ACK Slot) 和 ACK 界定符两部分组成。发送单元的 ACK,发送 2 个隐性位,而接收到正确消息的单元在 ACK 槽(ACK Slot)发送显性位,通知发送单元正常接收结束,这个过程叫发送 ACK / 返回 ACK。发送 ACK 的是所有接收单元中接收到正常消息的单元,所谓正常消息是指不含填充错误、格式错误、CRC 错误的消息,这些接收单元既不处于总线关闭态也不处于休眠态的所有接收单元中。
⑦、帧结束
最后就是帧结束段,标准格式和扩展格式相同,帧结束段结构如图所示:

从图可以看出,帧结束段很简单,由 7 位隐性位构成。
2、遥控帧
接收单元向发送单元请求数据的时候就用遥控帧,遥控帧由 6 个段组成:
①、帧起始,表示数据帧开始的段。
②、仲裁段,表示该帧优先级的段。
③、控制段,表示数据的字节数及保留位的段。
④、CRC 段,检查帧的传输错误的段。
⑤、ACK 段,表示确认正常接收的段。
⑥、帧结束,表示数据帧结束的段。
遥控帧结构如图所示:

从图可以看出,遥控帧结构基本和数据帧一样,最主要的区别就是遥控帧没有数据段。遥控帧的 RTR 位为隐性的,数据帧的 RTR 位为显性,因此可以通过 RTR 位来区分遥控帧和没有数据的数据帧。遥控帧没有数据,因此 DLC 表示的是所请求的数据帧数据长度,遥控帧的其他段参考数据帧的描述即可。
3、错误帧
当接收或发送消息出错的时候使用错误帧来通知,错误帧由错误标志和错误界定符两部分组成,错误帧结构如图所示:

错误标志有主动错误标志和被动错误标志两种,主动错误标志是 6 个显性位,被动错误标志是 6 个隐性位,错误界定符由 8 个隐性位组成。
4、过载帧
接收单元尚未完成接收准备的话就会发送过载帧,过载帧由过载标志和过载界定符构成,过载帧结构如图所示:

过载标志由 6 个显性位组成,与主动错误标志相同,过载界定符由 8 个隐性位组成,与错误帧中的错误界定符构成相同。
5、帧间隔
帧间隔用于分隔数据帧和遥控帧,数据帧和遥控帧可以通过插入帧间隔来将本帧与前面的任何帧隔开,过载帧和错误帧前不能插入帧间隔,帧间隔结构如图所示:

图中间隔由 3 个隐性位构成,总线空闲为隐性电平,长度没有限制,本状态下表示总线空闲,发送单元可以访问总线。延迟发送由 8 个隐性位构成,处于被动错误状态的单元发送一个消息后的帧间隔中才会有延迟发送。
四、CAN 速率
CAN 总线以帧的形式发送数据,但是最终到总线上的就是 "0" 和 "1" 这样的二进制数据,这里就涉及到了通信速率,也就是每秒钟发送多少位数据,前面说了 CAN2.0 最高速度为 1Mb ps/S。对于 CAN 总线,一个位分为 4 段:
①、同步段 (SS)
②、传播时间段 (PTS)
③、相位缓冲段 1 (PBS1)
④、相位缓冲段 2 (PBS2)
这些段由 Tq (Time Quantum) 组成,Tq 是 CAN 总线的最小时间单位。帧由位构成,一个位由 4 个段构成,每个段又由若干个 Tq 组成,这个就是位时序。1 位由多少个 Tq 构成、每个段又由多少个 Tq 构成等,可以任意设定位时序。通过设定位时序,多个单元可同时采样,也可任意设定采样点。各段的作用和 Tq 数如图所示:

1 个位的构成如图所示:

图中的采样点是指读取总线电平,并将读到的电平作为位值的点。位置在 PBS1 结束处。根据这个位时序,我们就可以计算 CAN 通信的波特率了。具体计算方法,我们等下再介绍,前面提到的 CAN 协议具有仲裁功能,下面我们来看看是如何实现的。
在总线空闲态,最先开始发送消息的单元获得发送权。
当多个单元同时开始发送时,各发送单元从仲裁段的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送。实现过程,如图所示:

上图中,单元 1 和单元 2 同时开始向总线发送数据,开始部分他们的数据格式是一样的,故无法区分优先级,直到 T 时刻,单元 1 输出隐性电平,而单元 2 输出显性电平,此时单元 1 仲裁失利,立刻转入接收状态工作,不再与单元 2 竞争,而单元 2 则顺利获得总线使用权,继续发送自己的数据。这就实现了仲裁,让连续发送显性电平多的单元获得总线使用权。
五、I.MX6ULL FlexCAN 简介
I.MX6ULL 带有 CAN 控制器外设,叫做 FlexCAN,FlexCAN 符合 CAN2.0B 协议。FlexCAN 完全符合 CAN 协议,支持标准格式和扩展格式,支持 64 个消息缓冲。I.MX6ULL 自带的 FlexCAN 模块特性如下:
①、支持 CAN2.0B 协议,数据帧和遥控帧支持标准和扩展两种格式,数据长度支持 0~8 字节,可编程速度,最高 1Mbit/S。
②、灵活的消息邮箱,最高支持 8 个字节。
③、每个消息邮箱可以配置为接收或发送,都支持标准和扩展这两种格式的消息。
④、每个消息邮箱都有独立的接收掩码寄存器。
⑤、强大的接收 FIFO ID 过滤。
⑥、未使用的空间可以用作通用 RAM。
⑦、可编程的回测模式,用于进行自测。
⑧、可编程的优先级组合。
FlexCAN 支持四种模式:正常模式 (Normal)、冻结模式 (Freeze)、仅监听模式 (Listen-Only) 和回环模式 (Loop-Back),另外还有两种低功耗模式:禁止模式 (Disable) 和停止模式 (Stop)。
①、正常模式 (Normal)
在正常模式下,FlexCAN 正常接收或发送消息帧,所有的 CAN 协议功能都使能。
②、冻结模式 (Freeze)
当 MCR 寄存器的 FRZ 位置 1 的时候使能此模式,在此模式下无法进行帧的发送或接收,CAN 总线同步丢失。
③、仅监听模式 (Listen-Only)
当 CTRL 寄存器的 LOM 位置 1 的时候使能此模式,在此模式下帧发送被禁止,所有错误计数器被冻结,CAN 控制器工作在被动错误模式,此时只会接收其他 CAN 单元发出的 ACK 消息。
④、回环模式 (Loop-Back)
当 CTRL 寄存器的 LPB 位置 1 的时候进入此模式,此模式下 FlexCAN 工作在内部回环模式,一般用来进行自测。从模式下发送出来的数据流直接反馈给内部接收单元。
前面在讲解 CAN 协议的时候说过 CAN 位时序,FlexCAN 支持 CAN 协议的这些位时序,控制寄存器 CTRL 用于设置这些位时序,CTRL 寄存器中的 PRESDIV、PROPSEG、PSEG1、PSEG2 和 RJW 这 5 个位域用于设置 CAN 位时序。
PRESDIV 为 CAN 分频值,也即是设置 CAN 协议中的 Tq 值,公式如下:

f_CANCLK 为 FlexCAN 模块时钟,这个根据时钟章节设置即可,设置好以后就是一个定值,因此只需要修改 PRESDIV 即可修改 FlexCAN 的 Tq 频率值。
Tq 定了以后我们结合之前图中的各个段来看一下如何设置 FlexCAN 的速率:
**SS:**同步段 (Synchronization Segment),在 I.MX6ULL 参考手册中叫做 SYNC_SEG,此段固定为 1 个 Tq 长度,因此不需要我们去设置。
**PTS:**传播时间段 (Propagatin Segment),FlexCAN 的 CTRL 寄存器中的 PROPSEG 位域设置此段,可以设置为 0~7,对应 1~8 个 Tq。
**PBS1:**相位缓冲段 1 (Phase Buffer Segment 1),FlexCAN 的 CRTL 寄存器中的 PSEG1 位域设置此段,可以设置为 0~7,对应 1~8 个 Tq。
**PBS2:**相位缓冲段 2 (Phase Buffer Segment 2),FlexCAN 的 CRTL 寄存器中的 PSEG2 位域设置此段,可以设置为 1~7,对应 2~8 个 Tq。
**SJW:**再同步补偿宽度 (reSynchronization Jump Width),FlexCAN 的 CRTL 寄存器中的 RJW 位域设置此段,可以设置 0~3,对应 1~4 个 Tq。
FlexCAN 的 CAN 位时序如图所示:

根据图所示,SYNC+SEG+(PROP_SEG+PSEG1+2)+(PSEG2+1)就是总的 Tq,因此 FlexCAN 的波特率就是:

六、程序编写
1、修改设备树
NXP 原厂提供的设备树已经配置好了 FlexCAN 的节点信息 (FlexCAN1 和 FlexCAN2),但是我们还是要来看一下如何配置 I.MX6ULL 的 CAN1 节点。首先看一下 I.MX6ULL 的 FlexCAN 设备树绑定文档,打开Documentation/devicetree/bindings/net/can/fsl-flexcan.txt,此文档描述了 FlexCAN 节点下的相关属性信息,这里就不做介绍了,大家自行查阅。
1.1、FlexCAN1 引脚节点信息
首先肯定是 CAN1 引脚配置信息,打开imx6ull-alientek-emmc.dts,找到如下所示内容:
pinctrl_flexcan1: flexcan1grp{
fsl,pins = <
MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX 0x1b020
MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX 0x1b020
>;
};
第 3 和第 4 行将 UART3_RTS 和 UART3_CTS 这两个引脚分别复用为 FlexCAN1 的 RX 和 TX,电气属性都设置为 0x1b020。
1.2、FlexCAN1 控制器节点信息
打开imx6ull.dtsi文件,找到名为flexcan1的节点,内容如下:
flexcan1: can@02090000 {
compatible = "fsl,imx6ul-flexcan", "fsl,imx6q-flexcan";
reg = <0x02090000 0x4000>;
interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_CAN1_IPG>,
<&clks IMX6UL_CLK_CAN1_SERIAL>;
clock-names = "ipg", "per";
stop-mode = <&gpr 0x10 1 0x10 17>;
status = "disabled";
};
注意示例代码中的 flexcan1 节点不需要我们修改,这里只是告诉大家 FlexCAN1 完整节点信息。根据第 2 行的 compatible 属性就可以找到 I.MX6ULL 的 FlexCAN 驱动文件,驱动文件为drivers/net/can/flexcan.c。第 9 行的 status 属性为 disabled,所以 FlexCAN1 默认关闭的。在imx6ull-alientek-emmc.dts中添加使能 FlexCAN1 的相关操作,找到如下所示代码:
&flexcan1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_flexcan1>;
xceiver-supply = <®_can_3v3>;
status = "okay";
};
第 3 行指定 FlexCAN1 所使用的 pinctrl 节点为 pinctrl_flexcan1,也就是上面的 pinctrl 节点。
第 4 行 xceiver-supply 属性指定 CAN 收发器的电压为 3.3V。
第 5 行将 flexcan1 节点的 status 属性改为 "okay",也就是使能 FlexCAN1。
重新编译设备树,我们还需要配置 Linux 内核,使能内核里面的 FlexCAN 驱动。
2、使能 Linux 内核自带的 FlexCAN 驱动
NXP 官方提供的 linux 内核默认已经集成了 I.MX6ULL 的 FlexCAN 驱动,但是没有使能,因此我们需要配置 Linux 内核,打开 FlexCAN 驱动,步骤如下:
2.1、使能 CAN 总线
首先打开 CAN 总线子系统,在 Linux 下 CAN 总线是作为网络子系统的,配置路径如下:
-> Networking support
-> <*> CAN bus subsystem support //打开CAN总线子系统
如图所示:

2.2、使能 Freescale 系 CPU 的 FlexCAN 外设驱动
接着使能 Freescale 系 CPU 的 FlexCAN 外设驱动,配置路径如下:
-> Networking support
-> CAN bus subsystem support
-> CAN Device Drivers
-> Platform CAN drivers with Netlink support
-> <*> Support for Freescale FLEXCAN based chips //选中
配置如图所示:

配置好以后重新编译内核,然后使用新的内核和设备树启动开发板。
4、FlexCAN 测试
4.1 检查 CAN 网卡设备是否存在
使用新编译的内核和设备树启动开发板,然后输入如下命令:
ifconfig -a //查看所有网卡
前面我们说了,Linux 系统中把 CAN 总线接口设备作为网络设备进行统一管理,因此如果 FlexCAN 驱动工作正常的话就会看到 CAN 对应的网卡接口,如图所示:

从图可以看出,有一个名为 "can0" 的网卡,这个就是 I.MX6U-ALPHA 开发板上的 CAN1 接口对应的 can 网卡设备。如果使能了 I.MX6ULL 上的 FlexCAN2 的话也会出现一个名为 "can1" 的 can 网卡设备。
4.2、移植 iproute2
busybox 自带的 ip 命令并不支持对 can 的操作,因此我们需要重新移植 ip 命令,也就是 iproute2,iproute2 源码下载地址为:Index of /pub/linux/utils/net/iproute2/。
wget https://mirrors.edge.kernel.org/pub/linux/utils/net/iproute2/iproute2-4.4.0.tar.xz
解压**:**
tar -vxzf iproute2-4.4.0.tar.gz
解压完成以后会得到一个名为 "iproute2-4.4.0" 的目录,进入此目录中,打开 Makefile 并修改。在 Makefile 中找到下面这行:
CC := gcc
改为
CC:=arm-linux-gnueabihf-gcc
修改完成以后如图所示:

Makefile 修改完成以后直接使用 "make" 命令编译,编译成功以后就会在 iproute2 源码的 ip 目录下得到一个名为 "ip" 的命令,如图所示:

- 将交叉编译得到的 ip 拷贝到开发板中
首先将交叉编译到的 ip 命令拷贝到开发板中,先不要替换开发板根文件系统中原有的 ip 命令!切记!切记! 先拷贝到开发板根文件系统的其他目录里面,比如这里就拷贝到/lib/modules/4.1.15这个目录里面。拷贝完成以后在开发板上先执行一下新的 ip 命令,查看一下版本号,命令如下:
cd lib/modules/4.1.15/
./ip -V //执行新的 ip 命令,查看版本号
如果新编译的 ip 命令运行正确的话就会打印出其版本号,如图所示:

- 在开发板根文件系统中用新的 ip 命令替换原来的
注意,此步骤在开发板中执行!开发板根文件系统中原来的 ip 命令是 busybox 自带的,存放在/sbin目录下。接下来,我们使用新的 ip 命令替换原来的,在开发板中执行如下命令:
cd lib/modules/4.1.15/
cp ip /sbin/ip -f
拷贝完成以后将/lib/modules/4.1.15/目录下的 ip 命令删除掉,重启开发板,查看根文件系统是否可以正常启动!如果正常启动的话输入如下命令查看 ip 命令版本号:
ip -V //查看 ip 命令版本号
结果如图所示:

至此,iproute2 中的 ip 命令就已经移植好了,稍后的 can 测试中我们会使用 ip 命令来设置 can0 网卡的相关信息。
4.3、移植 can-utils 工具
can0 网卡已经出现了,但是工作正不正常还不知道,必须要进行数据收发测试。这里我们使用 can-utils 这个工具来对 can0 网卡进行测试,因此要先移植 can-utils 到我们的开发板根文件系统中。
wget https://github.com/linux-can/can-utils/archive/refs/tags/v2020.02.04.tar.gz -O can-utils-2020.02.04.tar.gz
解压,命令如下:
tar -vxzf can-utils-2020.02.04.tar.gz //解压
解压完成以后得到一个名为 "can-utils-2020.02.04" 的目录,这个就是 can-utils 源码,进入到此目录中,然后配置并编译,命令如下:
cd can-utils-2020.02.04 //进入 can-utils 源码目录
./autogen.sh //先执行 autogen.sh,生成配置文件 configure
./configure --target=arm-linux-gnueabihf --host=arm-linux-gnueabihf --prefix=$PWD/build --disable-static --enable-shared //配置
make //编译
make install
编译完成以后就会前面创建的 "can-utils" 目录下就会多出一个 "bin" 目录,此目录下保存着 can-utils 的各种小工具,如图所示:

将图中的所有 can-utils 小工具全部拷贝到开发板根文件系统下的/usr/bin目录下,拷贝完成以后我们就可以使用这些小工具来测试 CAN 了。
4.4、CAN 通信测试
接下来我们使用一个 USB 转 CAN 卡与 IMX6U-ALPHA 开发板进行数据收发测试。
首先设置开发板的 can0 接口,速度为 500Kbit/S,命令如下:
ip link set can0 type can bitrate 500000 //设置 can0,速率 500Kbit
ifconfig can0 up //打开 can0
candump can0 & //candump 后台接收数据
按照 USBCAN 说明手册设置好 USB CAN 卡,速度设置为 500Kbit,首先通过 USBCAN 向 ALPHA 发送数据,发送的数据如图所示:

从图可以看出,USBCAN 发送了 5 帧数据帧,帧类型为标准帧,这 5 帧数据都是一样的,帧 ID 分别为 0~4。开发板 CAN 接口工作正常的话就会接收到这 5 帧数据,接收到的数据如图所示:

从图可以看出,开发板 can0 接口接收到了 5 帧数据,和 USBCAN 发送的一致。
也可以通过开发板向 USBCAN 发送数据,输入如下命令发送数据:
cansend can0 5A1#11.22.33.44.55.66.77.88
USBCAN 接收到数据,如图所示:

觉得有帮助的话,打赏一下呗。。
