CAN(控制器局域网络)协议详解
✅ CAN (Controller Area Network) 是什么?
CAN(控制器局域网络) 是一种用于设备之间通信的串行通信协议,最初由 Bosch 公司在 1980 年代开发,用于汽车内部控制系统。它是一种 多主总线通信系统,具有高可靠性和实时性,广泛应用于汽车、工业控制、医疗设备等领域。
✅ CAN 的工作原理:
-
数据传输方式: 基于 差分信号传输,即通过两根数据线
CAN_H
(高电平)和CAN_L
(低电平)传输数据。 -
拓扑结构: 通常为 总线拓扑,多个节点(设备)共享一条总线。
-
多主架构: 任何节点都可以主动发送数据,不存在主从关系。
-
优先级控制: CAN 消息帧中的 标识符(ID) 用于确定优先级,ID 数值越小,优先级越高。
✅ CAN 帧结构:
CAN 数据帧主要分为以下几种类型:
-
数据帧 (Data Frame): 用于发送数据。
-
远程帧 (Remote Frame): 请求数据。
-
错误帧 (Error Frame): 报告总线上发生的错误。
-
过载帧 (Overload Frame): 指示接收节点需要更多时间处理数据。
典型的 CAN 数据帧 结构如下:
字段 | 位数 | 描述 |
---|---|---|
Start of Frame | 1 | 起始帧标志 |
Identifier | 11/29 | 消息标识符 |
Control Field | 6 | 控制字段(数据长度) |
Data Field | 0-8字节 | 数据字段 |
CRC Field | 15 | 校验码 |
ACK Field | 2 | 应答字段 |
End of Frame | 7 | 帧结束标志 |
✅ CAN 波特率:
CAN 总线支持多种波特率,常见波特率如下:
-
125 kbps
-
250 kbps
-
500 kbps
-
1 Mbps
1 Mbps
是传统 CAN 的最高速率。
如果波特率设置不一致,不同节点之间将无法正常通信。
✅ CAN 协议版本:
-
CAN 2.0A: 11 位标识符(标准帧)。
-
CAN 2.0B: 29 位标识符(扩展帧)。
-
CAN FD (Flexible Data-rate): 增加了数据传输速率(最高 8 Mbps)和数据长度(8 到 64 字节)。
✅ CAN 的配置命令、调试方法或编程示例
1. Linux 中的 CAN 配置命令
Linux 内核从 2.6.25 版本开始内置了 SocketCAN,它提供了标准化的 CAN 网络接口,可以像网络接口一样操作 CAN 设备。
前提:
-
内核必须启用 CAN 模块:
can.ko
、can_raw.ko
、vcan.ko
等。 -
硬件设备如 USB-CAN 模块或 PCI-CAN 卡需要安装驱动。
1.1 启用 CAN 模块
首先,加载 CAN 模块:
sudo modprobe can
sudo modprobe can_raw
sudo modprobe can_dev
1.2 配置 CAN 接口
假设 CAN 设备节点为 can0
,以 500 kbps 波特率为例:
# 关闭 CAN 接口
sudo ip link set can0 down# 设置波特率并启动
sudo ip link set can0 type can bitrate 500000
sudo ip link set can0 up
查看 CAN 接口状态:
ip -details link show can0
1.3 模拟 CAN 接口 (vcan)
如果没有硬件设备,可以使用 vcan 虚拟 CAN 接口:
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set vcan0 up
1.4 发送与接收 CAN 数据
发送数据帧:
cansend can0 123#1122334455667788
123
:CAN 标识符(ID)
1122334455667788
:8 字节数据
接收数据帧:
candump can0
2. CAN 调试方法
2.1 使用 candump
监听 CAN 数据
candump can0
显示类似以下输出:
can0 123 [8] 11 22 33 44 55 66 77 88
-
123
:CAN ID -
[8]
:数据长度 -
11 22 33 44 55 66 77 88
:数据内容
2.2 发送测试数据:
模拟发送数据:
cansend can0 321#DEADBEEF
-
321
:CAN ID -
DEADBEEF
:4 字节数据
2.3 过滤特定 CAN ID:
监听特定 CAN ID(例如 321
):
candump can0,321:7FF
-
321:7FF
:表示仅显示 ID 为321
的帧。
3. CAN 编程示例:C语言与 SocketCAN
示例:发送和接收 CAN 数据帧
示例 1:发送 CAN 数据帧
send_can.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>int main() {int sock;struct sockaddr_can addr;struct ifreq ifr;struct can_frame frame;// 创建套接字sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);if (sock < 0) {perror("Socket");return 1;}// 设置 CAN 设备名称strcpy(ifr.ifr_name, "can0");ioctl(sock, SIOCGIFINDEX, &ifr);// 绑定套接字addr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;bind(sock, (struct sockaddr *)&addr, sizeof(addr));// 设置 CAN 数据帧frame.can_id = 0x123;frame.can_dlc = 8;frame.data[0] = 0x11;frame.data[1] = 0x22;frame.data[2] = 0x33;frame.data[3] = 0x44;frame.data[4] = 0x55;frame.data[5] = 0x66;frame.data[6] = 0x77;frame.data[7] = 0x88;// 发送数据帧if (write(sock, &frame, sizeof(frame)) != sizeof(frame)) {perror("Write");return 1;}printf("CAN 数据发送成功\n");close(sock);return 0;
}
编译并运行:
gcc -o send_can send_can.c
sudo ./send_can
示例 2:接收 CAN 数据帧
recv_can.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>int main() {int sock;struct sockaddr_can addr;struct ifreq ifr;struct can_frame frame;// 创建套接字sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);if (sock < 0) {perror("Socket");return 1;}// 设置 CAN 设备名称strcpy(ifr.ifr_name, "can0");ioctl(sock, SIOCGIFINDEX, &ifr);// 绑定套接字addr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;bind(sock, (struct sockaddr *)&addr, sizeof(addr));printf("等待 CAN 数据帧...\n");// 循环接收数据while (1) {int nbytes = read(sock, &frame, sizeof(frame));if (nbytes < 0) {perror("Read");return 1;}printf("接收 CAN ID: 0x%03X DLC: %d Data: ", frame.can_id, frame.can_dlc);for (int i = 0; i < frame.can_dlc; i++) {printf("%02X ", frame.data[i]);}printf("\n");}close(sock);return 0;
}
编译并运行:
gcc -o recv_can recv_can.c
sudo ./recv_can
调试方法:
-
开启调试日志:
sudo ip link set can0 up type can bitrate 500000 sudo ip link set can0 up dmesg | grep can
-
查看网络流量:
candump can0
-
发送测试数据:
cansend can0 123#1122334455667788