08 - RK3568 CAN1 控制器修复与验证(2026-08-29)¶
状态:
[BSP-6.1 CAN CONTROLLER RUNTIME VERIFIED]已验证 GEC V11 的 RK3568 CAN1 硬件控制器由
rockchip_canfd驱动注册为 SocketCAN 网络接口can0,可配置为 500 kbit/s,控制器时钟为 200 MHz。尚未取得 loopback 帧或外部节点实际收发证据,因此本页不写成“CAN 物理总线完全跑通”。
结论¶
本次故障的主因不是 CAN1 节点缺失,而是 BSP 6.1 的专用 defconfig 没有启用匹配 RK3568 的控制器驱动:
compatible = "rockchip,rk3568-can-2.0"
driver = drivers/net/can/rockchip/rockchip_canfd.c
Kconfig = CONFIG_CANFD_ROCKCHIP
初始系统只有 CAN 协议栈日志,没有控制器 probe,也没有 SocketCAN 接口:
加入 CONFIG_CANFD_ROCKCHIP=y,补齐 CAN1 的 200 MHz assigned clock,并重新编译烧写后,板端出现:
配置 500 kbit/s 后:
can state ERROR-ACTIVE (berr-counter tx 0 rx 0)
bitrate 500000 sample-point 0.875
rockchip_canfd: tseg1 1..128 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 2
clock 200000000
这证明控制器、驱动、时钟、pinctrl 和 SocketCAN 注册链已经成立。
官方 EVB 与 GEC 的差异¶
官方 EVB 没有板级 CAN override¶
核查 BSP 6.1 当前源码:
rk3568-evb.dtsi no board-level CAN override
rk3568-evb1-ddr4-v10.dtsi no board-level CAN override
rk3568-evb1-ddr4-v10-linux.dts no board-level CAN override
因此不存在一段“原厂 EVB 已启用 CAN”的节点可以直接复制。rk356x.dtsi 只提供 SoC 控制器资源,默认状态仍是 disabled;具体启用哪个控制器、哪组 pinmux,必须由板级 DTS 决定。
SoC 层 CAN1¶
BSP 6.1 rk356x.dtsi 中的控制器定义:
can1: can@fe580000 {
compatible = "rockchip,rk3568-can-2.0";
reg = <0x0 0xfe580000 0x0 0x1000>;
interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru CLK_CAN1>, <&cru PCLK_CAN1>;
clock-names = "baudclk", "apb_pclk";
resets = <&cru SRST_CAN1>, <&cru SRST_P_CAN1>;
reset-names = "can", "can-apb";
tx-fifo-depth = <1>;
rx-fifo-depth = <6>;
status = "disabled";
};
GEC 板级层只需覆盖板级差异,不修改共享的 rk356x.dtsi。
Pinmux 不能照搬其它板卡¶
BSP 提供两组 CAN1 引脚:
| pinctrl | RX | TX | mux |
|---|---|---|---|
can1m0_pins |
GPIO1_A0 | GPIO1_A1 | function 3 |
can1m1_pins |
GPIO4_C2 | GPIO4_C3 | function 3 |
LubanCat 某些板型使用 can1m1_pins,GEC 当前板级定义使用 can1m0_pins。这代表不同 PCB 接线,不是 m1 比 m0 更“标准”。GEC 的 m0 选择来自既有板级定义和 5.10 迁移基础,本轮保持不变;除非原理图、PCB 或连通性测量提供相反证据,不应照搬其它板卡改成 m1。
修改前¶
板级节点已经启用 CAN1,但没有显式指定工作时钟:
更关键的是,当时 .config 为:
CONFIG_CAN_DEV=y
CONFIG_CAN_CALC_BITTIMING=y
# CONFIG_CAN_ROCKCHIP is not set
# CONFIG_CANFD_ROCKCHIP is not set
所以 CAN core、RAW、BCM 和 gateway 可以注册,但没有驱动去匹配 fe580000.can。
根因:5.10 与 6.1 的配置符号不同¶
| 内核路线 | RK3568 CAN 控制器配置 |
|---|---|
| Rockchip BSP 5.10 | CONFIG_CAN_RK3568 |
| Rockchip BSP 6.1 | CONFIG_CANFD_ROCKCHIP |
BSP 6.1 Kconfig 中没有 CONFIG_CAN_RK3568。把 5.10 的符号原样写进 6.1 defconfig,不会编出 RK3568 CAN 控制器驱动;重新生成 .config 时该无效符号会被静默丢弃。
CONFIG_CAN_ROCKCHIP 也不是本板所需选项:它对应旧的 rockchip,can-1.0。当前 rockchip,rk3568-can-2.0 由 rockchip_canfd.c 匹配,其 Makefile 映射是:
虽然配置名中包含 CANFD,它正是当前 BSP 6.1 为 RK3568 can-2.0 节点提供的驱动,不能只凭名称判断并删除。
最终配置¶
GEC 板级 DTS¶
当前 rk3568-evb1-gec-v11.dtsi:
&can1 {
compatible = "rockchip,rk3568-can-2.0";
assigned-clocks = <&cru CLK_CAN1>;
assigned-clock-rates = <200000000>;
pinctrl-names = "default";
pinctrl-0 = <&can1m0_pins>;
status = "okay";
};
compatible 在 SoC 节点中已经存在,板级重复写不是驱动匹配成功的关键;本轮真正关键的是 CONFIG_CANFD_ROCKCHIP=y。assigned clock 让位时序基准明确固定为 200 MHz,板端 ip -details 已验证该频率生效。
GEC 专用 defconfig¶
维护配置当前至少包含:
加载 defconfig 后,最终 .config 展开为:
CONFIG_CAN=y
CONFIG_CAN_RAW=y
CONFIG_CAN_BCM=y
CONFIG_CAN_GW=y
CONFIG_CAN_DEV=y
CONFIG_CAN_NETLINK=y
CONFIG_CAN_CALC_BITTIMING=y
CONFIG_CANFD_ROCKCHIP=y
编译和烧写¶
cd /home/hyl/lubancat-linux-sdk/kernel-6.1
make ARCH=arm64 rockchip_rk3568_gec_linux_defconfig
make ARCH=arm64 olddefconfig
grep -E '^CONFIG_CAN|CANFD_ROCKCHIP|CAN_RK3568' .config
cd /home/hyl/lubancat-linux-sdk
./build.sh kernel
按照当前 SDK 流程烧写新生成的 boot.img。CAN 驱动和 DTS 都包含在内核启动产物中,只更新用户空间文件不会让控制器出现。
专用 defconfig 在 SDK 内核仓库中曾处于未跟踪状态。执行 savedefconfig 或覆盖文件前应先备份并确认产物包含 CONFIG_CANFD_ROCKCHIP=y,否则旧的根目录 defconfig 可能覆盖新配置。
板端验证¶
1. 控制器是否注册¶
只看到 PF_CAN、RAW、BCM 等日志,不能证明控制器已注册;必须看到 SocketCAN 网络接口。
2. 配置 500 kbit/s¶
本板只启用了一个 CAN 控制器,它注册出的第一个网络接口是 can0:
ip link set can0 down
ip link set can0 type can bitrate 500000
ip link set can0 up
ip -details -statistics link show can0
本轮板端输出确认:
这里的 ERROR-ACTIVE 是 CAN 控制器的正常主动错误状态名称,不等于“当前发生错误”。但在没有实际发送和接收帧时,零错误计数也不能单独证明外部收发器和总线接线正常。
为什么 DTS 叫 can1,系统却出现 can0¶
两个名字属于不同命名空间:
当前只启用 DTS 的 can1,它仍会按网络接口注册顺序获得 can0。因此板端命令应使用实际 ip link show 中的 can0,不要根据 DTS label 猜接口名。
当前证据边界¶
| 层级 | 证据 | 状态 |
|---|---|---|
| CAN 协议栈 | PF_CAN、RAW、BCM、gateway 注册 | [VERIFIED] |
| 控制器驱动 | rockchip_canfd 与 can0 出现 |
[VERIFIED] |
| 时钟与位时序 | 200 MHz、500 kbit/s、sample point 0.875 | [VERIFIED] |
| 控制器 link up | can0 为 UP,LOWER_UP |
[VERIFIED] |
| 内部 loopback 帧 | 尚无 candump/cansend 输出 |
[PENDING] |
| 板载 CAN 收发器 | 尚无示波器或外部节点证据 | [PENDING] |
| 物理总线双向收发 | 尚无双节点帧记录 | [PENDING] |
LVGL CAN Test 报错定位¶
板端 LVGL 测试页面进入 CAN Test 时出现:
此前只有运行输出,无法确定失败参数。补充应用源码后,根因已经确认在 can_init():
system("ip link set can0 down");
system("ip link set can0 type can bitrate 500000 dbitrate 500000 fd on");
system("ip link set can0 up");
中间命令要求内核同时启用 CAN-FD 和数据段波特率。ip 通过 netlink 把该请求发给 CAN 驱动,驱动发现当前控制器模式不支持 CAN_CTRLMODE_FD,返回 -EOPNOTSUPP,用户空间便打印 Operation not supported。
为什么驱动名称有 CANFD,硬件却不能 fd on¶
当前 DTS:
rockchip_canfd.c 的 compatible 映射:
probe 时不同 mode 声明不同能力。只有 ROCKCHIP_CANFD_MODE 包含:
RK3568 当前进入 ROCKCHIP_RK3568_CAN_MODE 分支,支持的是:
不包含 CAN_CTRLMODE_FD。因此:
CONFIG_CANFD_ROCKCHIP是编译这个多模式 Rockchip 驱动的配置名;rockchip_canfd.c是同时承载经典 CAN 与 CAN-FD 路径的源文件名;- 它们都不代表每个 compatible 对应的硬件一定支持 CAN-FD;
- 当前 RK3568 节点按经典 CAN 使用,单帧数据长度最大 8 字节;
dbitrate是 CAN-FD 数据阶段参数,在经典 CAN 中没有意义。
这也解释了为什么 CAN 控制器仍能以 500 kbit/s UP:失败的是应用额外请求的 FD 模式,不是 CAN1 驱动、DTS、时钟或经典 CAN bit timing。
修正后的 can_init()¶
static void can_init(void)
{
int ret;
ret = system("ip link set can0 down");
if (ret != 0)
eprint("failed to set can0 down\n");
ret = system("ip link set can0 type can bitrate 500000");
if (ret != 0)
eprint("failed to configure can0 bitrate\n");
ret = system("ip link set can0 up");
if (ret != 0)
eprint("failed to set can0 up\n");
}
核心修改是删除:
产品代码还应避免直接依赖 system() 的模糊返回值,或至少使用 <sys/wait.h> 的 WIFEXITED/WEXITSTATUS 解码退出状态。当前片段先保持原应用结构,只让错误可见。
LVGL 接收过滤器问题¶
应用的接收初始化与 thread_proc() 都包含:
rfilter[0].can_id = -1; // 0x22
rfilter[0].can_mask = CAN_SFF_MASK;
setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_FILTER,
&rfilter, sizeof(rfilter));
这段代码与注释不一致。can_id_t 是无符号 CAN ID 类型,赋值 -1 后所有 bit 都为 1,其中包括 CAN_INV_FILTER。Linux 6.1 会先保存反向过滤标志,再按 mask 预处理 ID;接收时使用:
所以当前配置不是“接收全部”,也不是“只接收 0x22”,而是反向排除低 11 位等于 0x7FF 的帧。大多数普通 ID 看起来仍能收到,这会让错误更隐蔽。
接收全部普通 CAN 帧¶
最简单的方式是不覆盖 CAN_RAW socket 的默认普通帧过滤行为;若希望显式配置:
struct can_filter rfilter = {
.can_id = 0,
.can_mask = 0,
};
if (setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_FILTER,
&rfilter, sizeof(rfilter)) < 0) {
eprint("failed to set CAN_RAW_FILTER\n");
}
这不包含 CAN error message frame;错误帧订阅由 CAN_RAW_ERR_FILTER 单独控制,本测试页面当前不需要混入错误帧。
只接收标准数据帧 ID 0x22¶
struct can_filter rfilter = {
.can_id = 0x22,
.can_mask = CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG,
};
把 CAN_EFF_FLAG 和 CAN_RTR_FLAG 纳入 mask,可避免把扩展帧或 RTR 帧误当成标准数据帧 0x22。如果测试目标是接收页面自身发送的 0x11,过滤 ID 应改成 0x11,或直接接收全部。
修正后的 can_recv_init() 示例¶
下面以“接收全部”为例,并补齐最基本的错误处理:
static s32 can_recv_init(void)
{
struct timeval timeout = {0, 20000};
struct sockaddr_can addr;
struct ifreq ifr;
struct can_filter rfilter = {
.can_id = 0,
.can_mask = 0,
};
can_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (can_fd < 0) {
eprint("failed to create CAN_RAW socket\n");
return -1;
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, "can0", IFNAMSIZ - 1);
if (ioctl(can_fd, SIOCGIFINDEX, &ifr) < 0)
goto err;
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(can_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
goto err;
if (setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_FILTER,
&rfilter, sizeof(rfilter)) < 0)
goto err;
if (setsockopt(can_fd, SOL_SOCKET, SO_RCVTIMEO,
&timeout, sizeof(timeout)) < 0)
goto err;
return 0;
err:
eprint("failed to initialize can0 receive socket\n");
close(can_fd);
can_fd = -1;
return -1;
}
thread_proc() 中复制了一份相同的 socket/filter 初始化代码,即使当前线程创建被注释,也应同步修正或抽成一个公共函数,避免以后重新启用线程时旧问题复现。
应用层其它观察¶
- 下拉框列出
can0/can1,但发送和接收代码都硬编码"can0",当前选择不会改变实际接口。 socket、ioctl、bind、setsockopt和多数system()原来没有检查错误,UI 可能在初始化失败后继续运行,造成“页面打开但功能异常”的假象。- 发送结构使用
struct can_frame、DLC=8,符合经典 CAN;若改成超过 8 字节必须同时更换struct canfd_frame和具备 FD 能力的硬件,当前 RK3568 路径不支持。 - 发送 socket 主动禁用了自己的接收过滤,接收由另一个 socket 完成。要做单机自发自收,应启用控制器
loopback on,或使用真实外部 CAN 节点提供 ACK 与回帧。
LVGL 修复后的验证¶
重新编译应用后,先确认不再请求 FD:
预期只显示经典 CAN 500 kbit/s,不应显示 FD data bitrate。然后执行:
- 打开 CAN Test 页面,确认不再出现
Operation not supported。 - 点击 Send,使用外部节点或
candump can0观察 ID0x11、DLC 8 和数据内容。 - 从外部节点发送 ID
0x22,确认 Receive 区显示正确;若配置接收全部,也测试其它 ID。 - 记录
ip -details -statistics link show can0的 packet 与 error counter。 - 双向收发成功后,再升级物理总线状态。
当前仓库只找到 docs/development/strings rk356x-demo_good_board_backup.md 中的命令字符串,没有找到这份 LVGL CAN 页面对应的可编辑 C 源文件。因此本轮完成的是根因和修正方案落地,实际应用仍标记 [APP FIX PENDING]。
经典 CAN 手工重配复验¶
板端最初在接口仍为 UP 时执行:
返回:
这不是硬件或 500 kbit/s 不受支持,而是 SocketCAN 要求修改 bit timing 前先停止接口。按正确顺序执行后全部成功:
ip link set can0 down
ip link set can0 type can bitrate 500000
ip link set can0 up
ip -details -statistics link show can0
新增实测结果:
can0: <NOARP,UP,LOWER_UP,ECHO>
can state ERROR-ACTIVE (berr-counter tx 0 rx 0)
bitrate 500000 sample-point 0.875
tq 50 prop-seg 17 phase-seg1 17 phase-seg2 5 sjw 1
rockchip_canfd: tseg1 1..128 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 2
clock 200000000
bus-off 0
判定:经典 CAN 的 down -> configure -> up 流程再次验证通过;500 kbit/s、200 MHz 时钟和当前 ERROR-ACTIVE 状态成立。
同一输出还包含累计统计:
error-warn/error-pass 是接口运行期间进入过相应状态的累计事件,不等于当前仍处于 warning/passive;当前状态行和 berr-counter 才描述即时状态,且 bus-off 为 0。RX bytes 恰好等于 packets x 8,但尚无 CAN ID 和 payload 输出,不能仅凭计数宣布物理总线帧已验证。
输出中的以下字段明显不符合正常 netdev 规模:
它们更像板端 ip 工具对当前内核 netlink 扩展属性的解码或格式兼容问题,不应解释成系统真的创建了数十万条队列。后续应直接读取 sysfs 统计并用 candump 核对真实帧:
for item in rx_packets rx_bytes rx_errors rx_dropped \
tx_packets tx_bytes tx_errors tx_dropped; do
printf '%-12s ' "$item"
cat "/sys/class/net/can0/statistics/$item"
done
candump -L can0
只有 candump 给出 CAN ID、DLC 和 payload,或另一节点明确收到本板发出的帧,才能把这些 RX/TX 计数升级为帧级和物理总线证据。
下一步:内部回环¶
板端需要 can-utils 的 candump 和 cansend:
ip link set can0 down
ip link set can0 type can bitrate 500000 loopback on
ip link set can0 up
candump can0 &
CANDUMP_PID=$!
cansend can0 123#DEADBEEF
sleep 1
kill "$CANDUMP_PID"
预期接收:
该实验通过后只能升级为 [CONTROLLER LOOPBACK VERIFIED],仍不代表板载物理收发器和外部总线已验证。
下一步:双节点物理总线¶
- 准备两个 CAN 节点,双方波特率均设为 500 kbit/s。
- 连接
CAN_H对CAN_H、CAN_L对CAN_L,并连接参考地。 - 确认板卡是否已带终端电阻;总线两端各保留一个 120 Ω 终端,避免重复叠加。
- 接收端运行
candump can0。 - 发送端运行
cansend can0 123#DEADBEEF。 - 交换发送和接收方向,再验证一次。
- 保存
ip -details -statistics link show can0,确认无持续增长的 error counter 和 BUS-OFF。
只有双向帧收发成功,才能把状态升级为 [CAN PHYSICAL BUS VERIFIED]。
常见误判¶
| 误判 | 正确判断 |
|---|---|
看见 NET: Registered PF_CAN 就认为 CAN 已通 |
这只代表协议栈;还要检查 ip link 是否出现 CAN netdev |
6.1 继续使用 CONFIG_CAN_RK3568 |
该符号在当前 6.1 Kconfig 中不存在,应使用 CONFIG_CANFD_ROCKCHIP |
CONFIG_CANFD_ROCKCHIP 只服务 CAN-FD,传统 CAN 不能用 |
当前 BSP 的 rk3568-can-2.0 就由该驱动匹配 |
| LubanCat 使用 m1,所以 GEC 也应改 m1 | pinmux 由 PCB 接线决定;GEC 当前使用 m0 |
DTS label 是 can1,接口就必须叫 can1 |
SocketCAN 接口名由网络设备注册顺序决定 |
ERROR-ACTIVE 表示故障 |
它是 CAN 正常运行状态之一;需结合 error counter、收发帧和 BUS-OFF 判断 |