当前位置: 首页 > news >正文

CAN(控制器局域网络)协议详解

CAN (Controller Area Network) 是什么?

CAN(控制器局域网络) 是一种用于设备之间通信的串行通信协议,最初由 Bosch 公司在 1980 年代开发,用于汽车内部控制系统。它是一种 多主总线通信系统,具有高可靠性和实时性,广泛应用于汽车、工业控制、医疗设备等领域。


CAN 的工作原理:

  • 数据传输方式: 基于 差分信号传输,即通过两根数据线 CAN_H(高电平)和 CAN_L(低电平)传输数据。

  • 拓扑结构: 通常为 总线拓扑,多个节点(设备)共享一条总线。

  • 多主架构: 任何节点都可以主动发送数据,不存在主从关系。

  • 优先级控制: CAN 消息帧中的 标识符(ID) 用于确定优先级,ID 数值越小,优先级越高。


CAN 帧结构:

CAN 数据帧主要分为以下几种类型:

  1. 数据帧 (Data Frame): 用于发送数据。

  2. 远程帧 (Remote Frame): 请求数据。

  3. 错误帧 (Error Frame): 报告总线上发生的错误。

  4. 过载帧 (Overload Frame): 指示接收节点需要更多时间处理数据。

典型的 CAN 数据帧 结构如下:

字段位数描述
Start of Frame1起始帧标志
Identifier11/29消息标识符
Control Field6控制字段(数据长度)
Data Field0-8字节数据字段
CRC Field15校验码
ACK Field2应答字段
End of Frame7帧结束标志

CAN 波特率:

CAN 总线支持多种波特率,常见波特率如下:

  • 125 kbps

  • 250 kbps

  • 500 kbps

  • 1 Mbps

1 Mbps 是传统 CAN 的最高速率。
如果波特率设置不一致,不同节点之间将无法正常通信。


CAN 协议版本:

  1. CAN 2.0A: 11 位标识符(标准帧)。

  2. CAN 2.0B: 29 位标识符(扩展帧)。

  3. CAN FD (Flexible Data-rate): 增加了数据传输速率(最高 8 Mbps)和数据长度(8 到 64 字节)。

CAN 的配置命令、调试方法或编程示例

1. Linux 中的 CAN 配置命令

Linux 内核从 2.6.25 版本开始内置了 SocketCAN,它提供了标准化的 CAN 网络接口,可以像网络接口一样操作 CAN 设备。

前提:

  • 内核必须启用 CAN 模块:can.kocan_raw.kovcan.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

相关文章:

  • Confusion2(Python反序列化+JWT)
  • 【前端】【JavaScript】【总复习】四万字详解JavaScript知识体系
  • 【数据结构】栈
  • 【MyBatis-8】MyBatis对象关联查询详解:高效处理复杂关系映射
  • Altium Designer AD如何输出PIN带网络名的PDF装配图
  • 内存中的“BANK”
  • 深入理解Python逻辑判断、循环与推导式(附实战案例)
  • Shell脚本与Xshell的使用、知识点、区别及原理
  • 演员评论家算法
  • 缺乏需求变更的影响评估,如何降低项目风险
  • Linux操作系统之进程(一):进程属性与进程创建
  • 交流充电桩IEC 61851-1和IEC 61851-21-2标准测试项目
  • MySQL推荐书单:从入门到精通
  • gradle--问答
  • 在 STM32 上使用 register 关键字
  • 如何访问云相关的api
  • python数据分析常用的10个核心库
  • 题海拾贝:P1833 樱花
  • vue3项目创建-配置-elementPlus导入-路由自动导入
  • 滑动窗口——水果成篮
  • 法学联合书单|法庭上的妇女
  • 陕西一村民被冒名贷款40余万续:名下已无贷款,将继续追责
  • 工人日报:“鼠标手”被纳入职业病,劳动保障网越织越密
  • 行知读书会|换一个角度看见社会
  • 电影路演,虚幻狂欢?
  • 重庆三峡学院回应“中标价85万设备网购300元”:已终止采购