Linux CAN编程——SocketCAN读写CAN数据
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
#include
#include
#include
#include
#include
#include
#include
#include
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;
}
(4) 接收 CAN 数据
使用 read()
函数接收一帧 CAN 数据:
struct can_frame frame;
int nbytes = read(s, &frame, sizeof(struct can_frame));
if (nbytes < 0)
{
perror("Read failed");
return 1;
}
(5) 设置 CAN 过滤器
使用 setsockopt()
函数设置 CAN 过滤器:
struct can_filter rfilter[1];
rfilter[0].can_id = 0x123; // 只接收 ID 为 0x123 的帧
rfilter[0].can_mask = 0xFFF; // 掩码
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
(6) 关闭 Socket
使用 close()
函数关闭套接字:
close(s);
5. CAN FD 支持
CAN FD(Flexible Data Rate)是 CAN 协议的扩展,支持更高的数据传输速率和更大的数据帧(最多 64 字节)。使用 CAN FD 时,需要启用 CAN_RAW_FD_FRAMES
选项:
int enable_canfd = 1;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable_canfd, sizeof(enable_canfd));
CAN FD 帧使用 struct canfd_frame
结构体:
struct canfd_frame
{
canid_t can_id; // CAN 标识符
__u8 len; // 数据长度 (0-64)
__u8 flags; // 标志位
__u8 __res0; // 保留字段
__u8 __res1; // 保留字段
__u8 data[64]; // 数据 (最多 64 字节)
};
6. CAN数据读取代码
- 支持can以及can fd
- 设置can过滤器
- 发送can数据
- 读取can数据
- 利用select+mutex+queue,循环阻塞读取can数据,并将数据线程安全的放入队列中,供其他子线程程序使用
can.hpp
#ifndef CAN_H
#define CAN_H
#include
#include
#include
#include
#include
namespace canTransportData {
class CanHandle
{
public:
struct CanRecvDataStruct
{
std::mutex receive_mutex;//互斥锁
std::queue<std::pair<uint32_t/*can id*/,std::vector<uint8_t>/*can data*/>> can_data; //读取的can数据队列,pair的key为canid,value为can数据
};
public:
explicit CanHandle();
~CanHandle();
bool openCanDevice(std::string can_node_name);//打开can设备
bool setCanFilter(std::map<uint32_t/*can id*/,uint32_t /*can mask*/> can_filter);//设置can过滤器
bool sendCanData(uint32_t &can_id,std::vector<uint8_t> &data);//发送can数据
bool readCanData(uint32_t &can_id,std::vector<uint8_t> &data);//读取can数据
void receiveCanData(CanRecvDataStruct &can_recv_data);//获取can数据,使用select阻塞循环读取
private:
int m_sockfd;
};
}
#endif // CAN_H
can.cpp
#include "can.hpp"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace canTransportData {
CanHandle::CanHandle()
{
m_sockfd=-1;
}
CanHandle::~CanHandle()
{
if(m_sockfd!=-1)
{
close(m_sockfd);
m_sockfd=-1;
}
}
bool CanHandle::openCanDevice(std::string can_node_name)
{
// 创建套接字
m_sockfd = socket(AF_CAN, SOCK_RAW, CAN_RAW);
if(m_sockfd<0)
{
std::cerr<<"create can socket error"<<std::endl;
return false;
}
// 指定 CAN 设备
struct ifreq ifr;
strcpy(ifr.ifr_name, can_node_name.c_str());
if(ioctl(m_sockfd, SIOCGIFINDEX, &ifr)<0)
{
std::cerr<<"ioctl SIOCGIFINDEX error"<<std::endl;
return false;
}
//打开CAN FD
int canfd_enable=1;
if(setsockopt(m_sockfd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_enable, sizeof(canfd_enable))<0)
{
std::cerr<<"can setsockopt can fd error"<<std::endl;
return false;
}
// 绑定套接字
struct sockaddr_can addr;
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if(bind(m_sockfd, (struct sockaddr *)&addr, sizeof(addr))<0)
{
std::cerr<<"can socket bind error"<<std::endl;
return false;
}
return true;
}
bool CanHandle::setCanFilter(std::map<uint32_t,uint32_t> can_filter)
{
if(can_filter.empty())
{
return false;
}
size_t filter_size=sizeof(struct can_filter)*(can_filter.size());
std::cout<<"filter_size:"<<can_filter.size()<<std::endl;
struct can_filter *filters=(struct can_filter*)malloc(filter_size);
std::map<uint32_t,uint32_t>::iterator itor;
int index=0;
for(itor=can_filter.begin();itor!=can_filter.end();itor++)
{
filters[index].can_id=itor->first;
filters[index].can_mask=itor->second;
index++;
}
if(setsockopt(m_sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, &filters, filter_size)<0)
{
std::cerr<<"can setsockopt set filter error"<<std::endl;
return false;
}
return true;
}
bool CanHandle::sendCanData(uint32_t &can_id, std::vector<uint8_t> &data)
{
ssize_t nbytes;
struct canfd_frame fd_frame;
fd_frame.can_id = can_id;
if(fd_frame.can_id>CAN_SFF_MASK)
{
fd_frame.can_id = fd_frame.can_id | CAN_EFF_FLAG;
}
for(int index=0;index<data.size();index++)
{
fd_frame.data[index]=data[index];
}
nbytes = write(m_sockfd, &fd_frame, sizeof(fd_frame));
if(nbytes != sizeof(fd_frame))
{
std::cerr<<"can socket send data error"<<std::endl;
return false;
}
return true;
}
bool CanHandle::readCanData(uint32_t & can_id, std::vector<uint8_t>& data)
{
ssize_t nbytes;
struct canfd_frame fd_frame;
memset(&fd_frame,' ',sizeof(fd_frame));
nbytes = read(m_sockfd, &fd_frame, sizeof(fd_frame));
if (nbytes > 0)
{
can_id=fd_frame.can_id;
if(can_id&0xCFFFFFFF >CAN_EFF_FLAG)
{
can_id=can_id&CAN_EFF_MASK;
}
else
{
can_id=can_id&CAN_SFF_MASK;
}
std::cout<<std::hex<<can_id<<" ["<<(int)fd_frame.len<<"] ";
for(int index=0;index<fd_frame.len;index++)
{
data.push_back(fd_frame.data[index]);
std::cout<<(int)fd_frame.data[index]<<" ";
}
std::cout<<std::dec<<std::endl;
return true;
}
return false;
}
void CanHandle::receiveCanData(CanRecvDataStruct &can_recv_data)
{
int ret;
fd_set readfds;
struct timeval tv;
while(1)
{
// 设置timeout为5秒
tv.tv_sec = 5;
tv.tv_usec = 0;
// 初始化readfds集合
FD_ZERO(&readfds);
FD_SET(m_sockfd, &readfds);
ret=select(m_sockfd + 1, &readfds, NULL, NULL, &tv);
if (ret < 0)
{
std::cerr<<"can socket select error:"<<errno<<std::endl;
return;
}
else if (ret == 0)
{
std::cerr<<"can socket select timeout"<<std::endl;
continue;
}
else
{
if(FD_ISSET(m_sockfd, &readfds))
{
uint32_t can_id;
std::vector<uint8_t> data;
readCanData(can_id,data);
std::lock_guard<std::mutex> lock(can_recv_data.receive_mutex);
can_recv_data.can_data.push(std::make_pair(can_id,data));
}
}
}
}
}