第一次接触CAN总线是在一个工业控制项目里,当时需要让RK3399开发板通过MCP2515芯片与PLC通信。作为嵌入式工程师,我发现很多新手会被CAN总线的专业术语吓到,其实它的核心逻辑特别像我们日常用的微信群——每个设备都是群成员,可以主动发消息(帧),也能接收别人发的消息,而且不需要群主协调。
MCP2515这颗芯片相当于给Linux系统装了个"CAN网卡",它通过SPI接口与主控连接。我常跟团队新人说:"把它想象成USB转串口芯片,只不过转换的是CAN协议"。内核自带的mcp251x驱动已经帮我们处理了大部分脏活累活,我们要做的就像配置WiFi模块一样,告诉系统三件事:
实际接线时最容易踩的坑是时钟线。有次我死活调不通,后来发现是SCK线走了15cm的飞线,SPI时钟频率降到1MHz才稳定。建议使用长度小于10cm的导线,双面板的话最好做阻抗匹配。硬件连接可以参考这个清单:
在RK3399上配置设备树就像给新员工办入职手续,得把工位(寄存器地址)、工作设备(时钟源)、紧急联系方式(中断)都交代清楚。下面这个配置模板我用了不下20次,关键参数都加了注释:
dts复制mcp251x_clk: mcp251x-clk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <20000000>; // 必须20MHz晶振
};
&spi1 {
status = "okay";
max-freq = <48000000>; // SPI控制器上限
pinctrl-names = "default";
pinctrl-0 = <&spi1_clk &spi1_tx &spi1_rx &spi1_cs0>;
mcp2515: can@0 {
status = "okay";
compatible = "microchip,mcp2515";
reg = <0>; // 片选0
clocks = <&mcp251x_clk>;
interrupt-parent = <&gpio2>;
interrupts = <24 IRQ_TYPE_EDGE_FALLING>; // GPIO2_D0
spi-max-frequency = <10000000>; // 实测10MHz最稳
vdd-supply = <&vcc3v3_sys>; // 3.3V电源
xceiver-supply = <&vcc3v3_sys>; // CAN收发器供电
};
};
有次客户要求用8MHz晶振,结果CAN波特率死活上不到1Mbps。后来用逻辑分析仪抓波形,发现8MHz下时序余量不足。血泪教训:时钟必须用20MHz!配置完后记得用这些命令检查:
bash复制# 查看SPI控制器是否识别
ls /sys/bus/spi/devices/spi1.0
# 检查CAN接口
ip -d link show can0
默认配置就像给跑车限速60km/h,根本发挥不出MCP2515的实力。经过多次压力测试,我总结出这些黄金参数:
默认发送队列只有10个帧,工业现场经常爆出"No buffer space available"。修改mcp251x.c驱动:
c复制// 在probe函数里增加
netdev->tx_queue_len = 1000; // 发送队列深度
netdev->flags |= IFF_ECHO; // 回显发送帧
通过dmesg发现SPI传输经常超时,添加DMA支持后性能提升3倍:
dts复制&spi1 {
dmas = <&dmac_peri 12>, <&dmac_peri 13>;
dma-names = "tx", "rx";
};
计算CAN波特率时,这个公式从没让我失望过:
code复制波特率 = 晶振频率 / (2 × (BRP + 1) × (Tseg1 + Tseg2 + 1))
推荐配置(20MHz晶振):
用ip link set can0 type can bitrate 1000000设置后,一定要用示波器测量实际波形。有次发现误差2.3%,原因是PCB走线太长引入了延迟。
上周又遇到个诡异问题:CAN帧偶尔丢失。最后发现是电源问题,这里分享我的排错清单:
SPI通信异常
spidev_test -D /dev/spidev1.0 -v测试SPI回路/proc/interrupts确认中断计数是否增加CAN链路不稳定
bash复制# 监控错误计数
cat /proc/net/can/stats
# 实时查看帧
candump can0
硬件诊断三板斧
有个经典案例:客户现场电磁干扰强,我在CANH/CANL之间加了100pF电容,再套上磁环,误码率立刻从10^-4降到10^-7。