7. CAN总线¶
CAN总线协议(Controller Area Network), 控制器局域网总线,是德国BOSCH(博世)公司研发的一种串行通讯协议总线, 它可以使用双绞线来传输信号,是世界上应用最广泛的现场总线之一。
7.1. CAN硬件连接¶
鲁班猫3板卡40pin上的can为芯片原生接口,没有通过can收发器,使用的时候需要外接can收发器模块。
CAN收发器购买参考链接: 野火CAN收发器模块
核心板加底板形式的板卡,比如鲁班猫3IO,在底板上已经有CAN收发器,可以直接使用。
板卡和外部设备CAN接线如下:
1 2 3 | 板卡 连接 外部设备
CAN_L ------- CAN_L
CAN_H ------- CAN_H
|
7.2. CAN¶
7.2.1. 使能CAN接口¶
开启和关闭can都需要重启才能生效
开启设备树插件(这里以打开CAN0为例)
1 2 | #修改配置文件
vi /boot/uEnv/uEnv.txt
|
把
#dtoverlay=/dtb/overlay/rk3576-lubancat-can0-m2-overlay.dtbo
前面的#删除,如下图

关闭设备树插件(这里以打开CAN0为例)
在
dtoverlay=/dtb/overlay/rk3588-lubancat-can1-m1-overlay.dtbo
前面添加#,如下图

7.2.2. CAN通信测试¶
默认配置为CANFD,如果要使用CAN,需要进行切换:
1 2 | #设置can0为500Kbps的比特率,并关闭CAN FD模式
sudo ip link set can0 type can bitrate 500000 fd off
|
CAN常用指令如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 查询当前网络设备:
ifconfig -a
CAN启动:
关闭CAN:
ip link set can0 down
设置比特率500KHz:
ip link set can0 type can bitrate 500000
打印can0信息:
ip -details link show can0
启动CAN:
ip link set can0 up
CAN发送:
发送(标准帧,数据帧,ID:123,date:DEADBEEF):
cansend can0 123#DEADBEEF
发送(标准帧,远程帧,ID:123):
cansend can0 123#R
发送(扩展帧,数据帧,ID:00000123,date:DEADBEEF):
cansend can0 00000123#12345678
发送(扩展帧,远程帧,ID:00000123):
cansend can0 00000123#R
CAN接收:
开启打印,等待接收:
candump can0
|
7.2.3. 更多指令¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 关闭can设备
ip link set canX down
开启can设备
ip link set canX up
显示can设备详细信息
ip -details link show canX
接收can总线发来数据
candump canX
关闭can设备,以便配置
ifconfig canX down
设置can波特率
ip link set canX up type can bitrate 250000
发送数据
cansend canX --identifier=ID+数据
使用滤波器接收ID匹配的数据
candump canX --filter=ID:mask
|
7.3. CANFD¶
7.3.1. CANFD通信测试¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 查询当前网络设备:
ifconfig -a
CAN FD启动:
关闭CAN:
ip link set can0 down
设置仲裁段1M波特率,数据段3M波特率:
ip link set can0 type can bitrate 1000000 dbitrate 3000000 fd on
打印can0信息:
ip -details link show can0
启动CAN:
ip link set can0 up
CAN FD发送:
发送(标准帧,数据帧,ID:123,date:DEADBEEF):
cansend can0 123##1DEADBEEF
CAN FD接收:
开启打印,等待接收:
candump can0
|
7.3.2. 更多指令¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 关闭can设备
ip link set canX down
开启can设备
ip link set canX up
显示can设备详细信息
ip -details link show canX
接收can总线发来数据
candump canX
关闭can设备,以便配置
ifconfig canX down
设置仲裁段1M波特率,数据段3M波特率:
ip link set can0 type can bitrate 1000000 dbitrate 3000000 fd on
发送数据
cansend canX --identifier=ID+数据
使用滤波器接收ID匹配的数据
candump canX --filter=ID:mask
|
7.4. CAN时钟频率修改¶
修改设备树插件上的时钟频率,
kernel/arch/arm64/boot/dts/rockchip/overlay/rk3576-lubancat-canX-mY-overlay.dts
(X,Y是各自的数字)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /dts-v1/;
/plugin/;
#include <dt-bindings/clock/rk3588-cru.h>
/ {
fragment@0 {
target = <&can0>;
__overlay__ {
status = "okay";
assigned-clocks = <&cru CLK_CAN0>;
assigned-clock-rates = <100000000>;
pinctrl-names = "default";
pinctrl-0 = <&can0m2_pins>;
};
};
};
|
assigned-clock-rates可以修改。如果小于等于3Mbps,建议将can clock修改为100M,这样信号更稳定。如果高于3Mbps,则可以将时钟设置为200M。
7.5. 简单的自收发脚本¶
将can1和can2对接起来,然后执行以下脚本,收发日志保存在/home/cat/log_canx.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #!/bin/bash
ip link set can0 down
ip link set can1 down
ip link set can0 type can bitrate 1000000 dbitrate 3000000 fd on
ip link set can1 type can bitrate 1000000 dbitrate 3000000 fd on
ip link set can0 up
ip link set can1 up
#监控can数据
candump can0 >> /home/cat/log_can0.txt &
candump can1 >> /home/cat/log_can1.txt &
counter=0
while true; do
#can0发送can0 123#00000000~123#99999999数据
data=$(printf "%08d" $counter)
command="cansend can0 123##0$data"
echo $command
$command
#can1发送can0 321#00000000~321#99999999数据
command2="cansend can1 321##0$data"
echo $command2
$command2
if [ $counter -eq 99999999 ]; then
counter=0
else
counter=$((counter+1))
fi
sleep 0.01
done
|
7.6. 与CAN分析仪通信测试¶
以LubanCat-RK3576板卡的CAN0为例,根据前面介绍的方法将CAN0接口使能,然后连接CAN收发器和CAN分析仪。
CAN收发器模块以 野火小智【CAN收发器】模块 为例,
USB CAN分析仪使用: CAN分析仪
7.6.1. 硬件接线¶
1 2 3 4 5 6 | 板卡 对接 CAN收发器 CAN收发器 对接 USB CAN分析仪
CAN0_TX(11) ------ CAN_TX H ------ H
CAN0_RX(7) ------ CAN_RX L ------ L
5V ------ 5V
GND ------ GND
3.3V ------ EN
|
7.6.2. shell命令测试¶
配置can接口:
1 2 3 | ip link set can0 down
ip link set can0 type can bitrate 500000 fd off
ip link set can0 up
|
发送消息:
1 2 | #发送(标准帧,数据帧,ID:123,date:DEADBEEF)
cansend can0 123#DEADBEEF
|
接收信息:
1 2 | #开启打印,等待接收
candump can0
|
测试效果如图:

7.6.3. C程序测试¶
1 | quick_start/can/can_send.c
|
代码较长复制粘贴容易乱序,可以下载我们提供的源码 can_send.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
int main(int argc, char *argv[])
{
struct ifreq ifr = {0};
struct sockaddr_can can_addr = {0};
struct can_frame frame = {0};
unsigned char buffer[4] = {0};
int sockfd = -1;
unsigned int cnt = 0;
int ret;
// 设置CAN波特率为500000
system("ip link set can0 down");
system("ip link set can0 type can bitrate 500000 fd off");
system("ip link set can0 up");
sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if(0 > sockfd) {
perror("socket error");
exit(EXIT_FAILURE);
}
/* 指定 can0 设备 */
strcpy(ifr.ifr_name, "can0");
ioctl(sockfd, SIOCGIFINDEX, &ifr);
can_addr.can_family = AF_CAN;
can_addr.can_ifindex = ifr.ifr_ifindex;
/* 将 can0 与套接字进行绑定 */
ret = bind(sockfd, (struct sockaddr *)&can_addr, sizeof(can_addr));
if (0 > ret) {
perror("bind error");
close(sockfd);
exit(EXIT_FAILURE);
}
setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
memcpy(frame.data, buffer, sizeof(buffer));
frame.can_dlc = sizeof(buffer);
frame.can_id = 0x123;
for (;;) {
frame.data[0] = (cnt >> 24) & 0xff;
frame.data[1] = (cnt >> 16) & 0xff;
frame.data[2] = (cnt >> 8) & 0xff;
frame.data[3] = cnt & 0xff;
cnt++;
ret = write(sockfd, &frame, sizeof(frame));
if (sizeof(frame) != ret) {
perror("write error");
break;
}
usleep(10 * 1000);
}
close(sockfd);
exit(EXIT_SUCCESS);
}
|
编译并运行:
1 2 3 4 5 | #编译程序
gcc can_send.c -o can_send
#运行
sudo ./can_send
|
测试效果如下,大概每秒发送100帧数据,数据ID号为0x0123,数据每帧递增。
