欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 社会 > Linux CAN编程——SocketCAN读写CAN数据

Linux CAN编程——SocketCAN读写CAN数据

2025/2/14 0:33:25 来源:https://blog.csdn.net/qq_33232152/article/details/145395549  浏览:    关键词: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 <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 接口(如 can0vcan0):

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;       // 数据字节 2if (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 <iostream>
#include <vector>
#include <map>
#include <queue>
#include <mutex>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 <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <sys/select.h>
#include <sys/time.h>
#include <iostream>
#include <vector>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 FDint 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,'\0',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));}}}
}}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com