检索引脚复用情况 IOMUX
查 CAN0 所有复用组定义:
rg -n "can0|can0m[0-3]_pins|can0_rx|can0_tx" \
kernel/arch/arm64/boot/dts/rockchip/rk3576-pinctrl.dtsi
查 CAN0 复用组有没有被引用:
rg -n "can0m0_pins|can0m1_pins|can0m2_pins|can0m3_pins" \
kernel/arch/arm64/boot/dts/rockchip/
查 can0m0 对应 GPIO2_A0/A1 是否被占用:
rg -n "gpio2 RK_PA0|gpio2 RK_PA1|GPIO2_A0|GPIO2_A1|RK_PA0|RK_PA1" \
kernel/arch/arm64/boot/dts/rockchip/rk3576*.dts*
板子运行后查实际 pinmux:
cat /sys/kernel/debug/pinctrl/*/pinmux-pins | grep -i can
查 GPIO 是否被驱动占用:
cat /sys/kernel/debug/gpio
设备树
三层关系:
rk3576.dtsi 定义 SoC 里有 CAN0 控制器,但默认 disabled
rk3576-pinctrl.dtsi 定义 CAN0 可以复用到哪些 GPIO 引脚
rk3576-test1.dtsi 板级文件,决定这块板子到底启用哪个外设、用哪组引脚
所以我们要写的是:在 rk3576-test1.dtsi 里启用 &can0,并指定它使用哪组 pinctrl。
一、先看 SoC 里 CAN0 怎么定义
查命令:
rg -n "can0:|rockchip,rk3576-can|CLK_CAN0|SRST_CAN0" \
kernel/arch/arm64/boot/dts/rockchip/rk3576.dtsi
芯片层级设备树:
can0: can@2ac00000 {
compatible = "rockchip,rk3576-can";
reg = <0x0 0x2ac00000 0x0 0x1000>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru CLK_CAN0>, <&cru HCLK_CAN0>;
clock-names = "baudclk", "apb_pclk";
resets = <&cru SRST_CAN0>, <&cru SRST_H_CAN0>;
reset-names = "can", "can-apb";
status = "disabled";
};
逐行意思:
can0: can@2ac00000 {
这是 CAN0 控制器节点。can0: 是 label,后面板级文件可以用 &can0 引用它。
compatible = "rockchip,rk3576-can";
告诉内核用哪个 CAN 驱动匹配这个硬件。
reg = <0x0 0x2ac00000 0x0 0x1000>;
CAN0 寄存器地址,驱动会映射这段寄存器。
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
CAN0 中断号。
clocks = <&cru CLK_CAN0>, <&cru HCLK_CAN0>;
clock-names = "baudclk", "apb_pclk";
CAN0 需要两个时钟,一个用于 CAN 波特率,一个用于 APB 总线访问。
resets = <&cru SRST_CAN0>, <&cru SRST_H_CAN0>;
reset-names = "can", "can-apb";
CAN0 的复位控制。
status = "disabled";
默认关闭。SoC 公共文件里通常都这样写,板级文件再决定是否开启。
板级设备树开启can0:
在 [rk3576-test1.dtsi](/home/user/kernel/arch/arm64/boot/dts/rockchip/rk3576-test1.dtsi) 里加:
&can0 {
assigned-clocks = <&cru CLK_CAN0>;
assigned-clock-rates = <200000000>;
pinctrl-names = "default";
pinctrl-0 = <&can0m0_pins>;
status = "okay";
};
逐行解释:
&can0 {
引用 rk3576.dtsi 里已经定义好的 can0: can@2ac00000 节点。不是新建 CAN0,而是修改已有节点。
assigned-clocks = <&cru CLK_CAN0>;
指定要配置 CAN0 的工作时钟。
assigned-clock-rates = <200000000>;
把 CAN0 时钟设置为 200MHz。Rockchip 同系列 rk3576-industry.dtsi 里也是这样写的。
pinctrl-names = "default";
表示默认状态使用下面这组 pinctrl。
pinctrl-0 = <&can0m0_pins>;
指定 CAN0 使用 can0m0_pins,也就是 GPIO2_A0 做 RX,GPIO2_A1 做 TX。
status = "okay";
真正启用 CAN0。没有这一行,前面写了 pinctrl 也不会启用设备。
验证
编译 DTB:
cd kernel
make ARCH=arm64 rk3576-test1-v10-linux.dtb
如果编译报错,常见问题是:
Label or path can0m0_pins not found
说明 pinctrl label 没包含进来或名字写错。
syntax error
多半是分号、尖括号、花括号漏了。
板子启动后查:
dmesg | grep -i can
ip link show
如果成功,一般能看到 can0。
再看 pinmux:
cat /sys/kernel/debug/pinctrl/*/pinmux-pins | grep -i can
如果 ip link show 没有 can0,继续查内核配置:
zcat /proc/config.gz | grep CAN
至少要确认 CAN 框架和 Rockchip CAN 驱动打开。
编译内核
cd /home/user
./build.sh lunch
./build.sh kernel
ADB检测CAN是否开启
ifconfig -a
ip -details -statistics link show can0
ip -details -statistics link show can1
CAN 常用命令接口
- 查询当前网络设备:
ifconfig -a
- CAN启动:
关闭CAN:
ip link set can0 down
设置比特率500KHz:
ip link set can0 type can bitrate 500000
打印can0信息:
ip -details -statistics 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
检测各种模式的CAN
普通 CAN:
ip link set can0 down
ip link set can0 type can bitrate 500000
ip link set can0 up
candump can0
cansend can0 123#DEADBEEF
CAN FD 不带 BRS:
ip link set can0 down
ip link set can0 type can bitrate 1000000 dbitrate 3000000 fd on
ip link set can0 up
candump can0
cansend can0 123##0DEADBEEF
CAN FD 带 BRS:
ip link set can0 down
ip link set can0 type can bitrate 1000000 dbitrate 3000000 fd on
ip link set can0 up
candump can0
cansend can0 123##1DEADBEEF
回环测试,普通 CAN:
ip link set can0 down
ip link set can0 type can bitrate 500000 loopback on
ip link set can0 up
candump can0
cansend can0 123#DEADBEEF
回环测试,CAN FD:
ip link set can0 down
ip link set can0 type can bitrate 1000000 dbitrate 3000000 fd on loopback on
ip link set can0 up
candump can0
cansend can0 123##1DEADBEEF
监听模式:
ip link set can0 down
ip link set can0 type can bitrate 500000 listen-only on
ip link set can0 up
candump can0
错误帧抓取:
ip link set can0 down
ip link set can0 type can bitrate 500000 berr-reporting on
ip link set can0 up
candump -e can0
单次发送:
ip link set can0 down
ip link set can0 type can bitrate 500000 one-shot on
ip link set can0 up
cansend can0 123#DEADBEEF