10. CAN通信¶
本章介绍在Android中使用CAN总线与外部设备的通讯。
CAN总线协议(Controller Area Network), 控制器局域网总线,是德国BOSCH(博世)公司研发的一种串行通讯协议总线, 它可以使用双绞线来传输信号,是世界上应用最广泛的现场总线之一。
支持CAN总线的鲁班猫板卡如下:
LubanCat-1HS
LubanCat-2
LubanCat-2N
LubanCat-2IO
LubanCat-3
LubanCat-3IO
LubanCat-4-V1
LubanCat-5-V2
10.1. CAN引脚使能¶
核心板加底板款板卡 默认使能了CAN接口, 一体板款板卡 默认没有使能CAN接口
一体板款板卡 用户参考“40pin引脚对照图”章节,查看需要使用的CAN引脚, 需要修改设备树源码来使能,并重新编译镜像。
如果对设备树不熟悉的用户,请学习野火配套的《嵌入式Linux驱动开发实战指南》的 Linux设备树 章节内容。
以使能LubanCat-4-V1的CAN0_M0为例,修改SDK源码/kernel-5.10/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-4-v1.dtsi (其他板卡修改板卡对应的设备树),在设备树中添加以下节点:
1 2 3 4 5 6 7 | &can0 {
status = "okay";
assigned-clocks = <&cru CLK_CAN0>;
assigned-clock-rates = <200000000>;
pinctrl-names = "default";
pinctrl-0 = <&can0m0_pins>;
};
|
assigned-clock-rates时钟频率可以修改,RK356x和RK3588系列如果CAN的波特率1M建议修改CAN时钟到300M,信号更稳定,低于1M比特率的,时钟设置200M就可以,默认使用的是200M。RK3576系列如果CAN的波特率小于等于3Mbps,建议CAN时钟修改为100M,这样信号更稳定,如果高于3Mbps,则可以将时钟设置为200M。
提示
如果不清楚选定的引脚节点怎么写,可以参考Linux内核对应的 设备树插件源码
修改为如下图所示,如果与其他节点冲突需关闭冲突节点再添加:

参考 编译 Android 镜像 章节重新编译镜像并将编译出来的镜像烧录到板卡。
10.2. 检查CAN设备¶
可以通过以下命令查看CAN接口有没有开启,存在can0则开启成功。
1 2 3 4 5 | #进入shell终端
adb shell
#查看网络接口
ifconfig -a
|
默认CAN编号从0开始,如果使能2个CAN接口,如CAN0_M0、CAN2_M0将会顺序注册为can0、can1。
10.3. 连接CAN设备¶
以连接CAN分析仪为例, 一体板款板卡40pin上的can为芯片原生接口,没有通过can收发器,使用的时候需要外接can收发器模块, 核心板加底板款板卡底板默认带can收发器,不需要额外接。
CAN收发器购买参考链接: 野火CAN收发器模块
一体板款板卡和外部设备CAN接线如下:
1 2 3 4 5 6 | 板卡 对接 CAN收发器 CAN收发器 对接 USB CAN分析仪
CAN_TX ------ CAN_TX H ------ H
CAN_RX ------ CAN_RX L ------ L
5V ------ 5V
GND ------ GND
3.3V ------ EN
|
核心板加底板款板卡和外部设备CAN接线如下:
1 2 3 | 板卡 连接 USB CAN分析仪
CAN_L ------- CAN_L
CAN_H ------- CAN_H
|
10.4. 使用shell收发数据¶
10.4.1. 配置CAN接口¶
CAN接口会注册成网络接口,可以使用ip命令进行配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 查询当前网络设备:
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
|
10.4.2. 收发CAN数据¶
使用shell收发CAN数据需要使用can-utils工具,镜像默认集成了其中的cansend和candump工具(旧镜像没有)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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
发送数据:
cansend can0 --identifier=ID+数据
使用滤波器接收ID匹配的数据:
candump can0 --filter=ID:mask
|
如果系统没有cansend和candump命令,可访问资料网盘/6-开发软件获取can-utils-android.tar.gz, 该压缩包是野火交叉编译好的can-utils工具集,其中还包含can-utils的其他工具,如果需要也可自行集成到系统。
以添加cansend和candump工具到系统为例,解压can-utils-android.tar.gz压缩包,进入其中的usr/local/bin目录:
1 2 3 4 5 6 7 8 9 10 11 12 13 | #进入can-utils-android/usr/local/bin目录,在目录栏输入cmd,打开windows终端
cmd
#通过adb命令重新挂载板卡分区
adb root && adb remount
#将工具传送到板卡/system/bin/目录
adb push cansend /system/bin/
adb push candump /system/bin/
#添加执行权限
adb shell chmod 777 /system/bin/cansend
adb shell chmod 777 /system/bin/candump
|
以上操作完成后即可参照前面介绍的收发CAN数据步骤进行测试。
can-utils源码为以下链接,感兴趣的用户可自行研究。
10.5. 使用安卓应用收发数据¶
野火提供的安卓综合测试应用能很方便的实现收发CAN数据。
10.5.2. 实现方式¶
CAN数据收发调用jni接口方式,参考野火应用源码ebf_android_app/app/src/main/cpp/can.cpp
在Activity中声明JNI方法进行使用:
1 2 3 4 5 6 7 8 9 10 11 12 | // 打开CAN设备
public native boolean openCan(String ifname);
// 打开关闭设备
public native void closeCan();
// 发送CAN数据
public native boolean sendCanFrame(int canId, boolean isExtended, boolean isRemote, byte[] data);
// 获取CAN接口
public native List<String> getCanInterfaces();
// 接收CAN数据
public native void startReceiving();
// 停止接收CAN数据
public native void stopReceiving();
|
值得注意的是普通用户没有配置CAN接口的权限(jni接口也没有),所以需要通过root用户执行shell命令去配置CAN接口, 在野火应用源码的CanTestActivity.java中提供了executeRootCommand函数用来执行root命令, 通过executeRootCommand函数执行chmod命令配置CAN接口参数并使能。
1 | private boolean executeRootCommand(String command)
|
那么CAN发送数据的流程为:
1 2 3 4 5 6 7 8 9 10 | // 配置CAN参数并使能
boolean downResult = executeRootCommand("ip link set can0 down");
boolean baudResult = executeRootCommand("ip link set can0 type can bitrate 500000");
boolean upResult = executeRootCommand("ip link set can0 up");
// 打开CAN接口
boolean openSuccess = openCan(selectedCanInterface);
// 发送数据
boolean result = sendCanFrame(canId, isExtended, isRemote, data);
|
由于野火测试应用是不确定用户选择的CAN接口以及波特率是多少,那么必须在应用中根据用户选择调用root去配置, 如果自己的项目中明确使用哪些CAN接口以及波特率,也可以在系统shell脚本中提前配置,在Activity中省去配置的步骤。
系统自启动的shell脚本位于/system/bin/android_shell.sh,由/vendor/etc/init/init.rk3588.rc调用执行(如果是其他芯片则是[对应芯片名字].rc), 以提前设置can0波特率为500000并使能为例:
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 | #重新挂载系统
adb root && adb remount
#拉取系统中的android_shell.sh
adb pull system/bin/android_shell.sh
#电脑文本软件打开android_shell.sh,在其末尾添加权限命令
ip link set can0 down
ip link set can0 type can bitrate 500000
ip link set can0 up
#将修改后的android_shell.sh的覆盖到系统system/bin/
adb push android_shell.sh system/bin/
#重启系统
adb reboot
#查看can0状态
adb shell ip -details link show can0
#信息输出如下
3: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
link/can promiscuity 0
can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 1
bitrate 500000 sample-point 0.868
tq 20 prop-seg 42 phase-seg1 43 phase-seg2 13 sjw 1
rockchip_canfd: tseg1 1..128 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 2
clock 198000000numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
|
最终可以看到重启系统后,can0接口默认波特率为500000并且状态为up。
如需修改SDK源码来修改android_shell.sh,该文件位于SDK源码/device/rockchip/rk[具体芯片]/android_shell.sh,由SDK源码/device/rockchip/rk[具体芯片]/init.rk[具体芯片].rc中调用脚本, 由SDK源码/device/rockchip/rk[具体芯片]/device.mk中拷贝脚本进系统。
10.6. 修改CAN缓冲区¶
默认CAN缓冲区大小只有10,每秒最多发4000帧,如果帧率再高缓冲区将没有多余的空间,提示No buffer space available,增大缓冲区大小可以增加帧率上限。
执行下面命令可以查看或者修改CAN缓冲区大小:
1 2 3 4 5 6 7 8 9 10 11 | #进入shell终端
adb shell
#切换root
su root
#查看CAN缓冲区大小
cat /sys/class/net/can0/tx_queue_len
#修改CAN缓冲区大小为4096
echo 4096 > /sys/class/net/can0/tx_queue_len
|