Linux CAN编程——SocketCAN读写CAN数据
1. SocketCAN说明
在 Linux 中,CAN(Controller Area Network)总线通过 SocketCAN 接口提供支持。SocketCAN 是 Linux 内核中实现 CAN 协议栈的一种方式,它使用标准的 socket API 来操作 CAN 设备。
SocketCAN 将 CAN 设备抽象为网络接口(如 can0
, vcan0
),并通过标准的 socket API 进行通信。它支持以下功能:
- 发送和接收 CAN 帧。
- 配置 CAN 接口(如比特率、过滤器等)。
- 支持多种 CAN 协议(如 CAN 2.0A、CAN 2.0B、CAN FD)。
2. CAN 相关头文件
在使用 SocketCAN API 时,需要包含以下头文件:
#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>
3. CAN 数据结构
(1) CAN 帧结构 (struct can_frame
)
CAN 帧是 SocketCAN 中最基本的数据单元,定义如下:
struct can_frame
{
canid_t can_id; // CAN 标识符 (11 位或 29 位)
__u8 can_dlc; // 数据长度 (0-8)
__u8 __pad; // 填充字节
__u8 __res0; // 保留字段
__u8 __res1; // 保留字段
__u8 data[8]; // 数据 (最多 8 字节)
};
(2) CAN 过滤器 (struct can_filter
)
CAN 过滤器用于过滤接收到的 CAN 帧。定义如下:
struct can_filter
{
canid_t can_id; // CAN 标识符
canid_t can_mask; // 掩码
};
4. SocketCAN API接口使用
(1) 创建 Socket
使用 socket()
函数创建一个 CAN 套接字:
/*
PF_CAN:协议族,表示 CAN。
SOCK_RAW:原始套接字,用于直接访问 CAN 帧。
CAN_RAW:使用原始 CAN 协议。
*/
int s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (s < 0)
{
perror("Socket creation failed");
return 1;
}
(2) 绑定 CAN 接口
使用 bind()
函数将套接字绑定到指定的 CAN 接口(如 can0
或 vcan0
):
struct sockaddr_can addr;
struct ifreq ifr;
// 指定 CAN 接口名称
strcpy(ifr.ifr_name, "can0");
ioctl(s, SIOCGIFINDEX, &ifr); //获取接口的索引。
// 配置地址结构
addr.can_family = AF_CAN; //地址族,设置为AF_CAN。
addr.can_ifindex = ifr.ifr_ifindex;
// 绑定CAN接口
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("Bind failed");
return 1;
}
(3) 发送 CAN 帧
使用 write()
函数发送 CAN 帧:
struct can_frame frame;
frame.can_id = 0x123; // CAN 标识符
frame.can_dlc = 2; // 数据长度
frame.data[0] = 0xDE; // 数据字节 1
frame.data[1] = 0xAD; // 数据字节 2
if (write(s, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame))
{
perror("Write failed");
return 1;