SocketCan 应用编程

2023-05-16

SocketCan 应用编程

由于 Linux 系统将 CAN 设备作为网络设备进行管理,因此在 CAN 总线应用开发方面,Linux 提供了SocketCAN 应用编程接口,使得 CAN 总线通信近似于和以太网的通信,应用程序开发接口更加通用,也更加灵活。

SocketCAN 中大部分的数据结构和函数在头文件 linux/can.h 中进行了定义,所以,在我们的应用程序中一定要包含<linux/can.h>头文件。

创建 socket 套接字

CAN 总线套接字的创建采用标准的网络套接字操作来完成,网络套接字在头文件<sys/socket.h>中定义。

创建 CAN 套接字的方法如下:

int sockfd = -1; 
 
/* 创建套接字 */ 
sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW); 

if(0 > sockfd) { 
 perror("socket error"); 
 exit(EXIT_FAILURE); 

} 

socket 函数在 30.2.1 小节中给大家详细介绍过,第一个参数用于指定通信域,在Socket Can中,通常将其设置为PF_CAN,指定为CAN通信协议;第二个参数用于指定套接字的类型,通常将其设置为SOCK_RAW;第三个参数通常设置为 CAN_RAW。

将套接字与 CAN 设备进行绑定

譬如,将创建的套接字与 can0 进行绑定,示例代码如下所示:

truct ifreq ifr = {0}; 

struct sockaddr_can can_addr = {0}; 

int ret; 

strcpy(ifr.ifr_name, "can0"); //指定名字 

ioctl(sockfd, SIOCGIFINDEX, &ifr); 
 
can_addr.can_family = AF_CAN; //填充数据 

can_addr.can_ifindex = ifr.ifr_ifindex; 
 

/* 将套接字与 can0 进行绑定 */ 
ret = bind(sockfd, (struct sockaddr *)&can_addr, sizeof(can_addr)); 

if (0 > ret) { 
 perror("bind error"); 
 close(sockfd); 
 exit(EXIT_FAILURE); 
} 

bind()函数在 30.2.2 小节中给大家详细介绍过,这里不再重述!

这里出现了两个结构体:struct ifreq 和 struct sockaddr_can,其中 struct ifreq 定义在<net/if.h>头文件中,而 struct sockaddr_can 定义在<linux/can.h>头文件中,这些结构体就不给大家解释了,有兴趣的自己去查,笔者也不太懂!

设置过滤规则

在我们的应用程序中,如果没有设置过滤规则,应用程序默认会接收所有 ID 的报文;如果我们的应用程序只需要接收某些特定 ID 的报文(亦或者不接受所有报文,只发送报文),则可以通过 setsockopt 函数设置过滤规则,譬如某应用程序只接收 ID 为 0x60A 和 0x60B 的报文帧,则可将其它不符合规则的帧全部给过滤掉,示例代码如下所示:

struct can_filter rfilter[2]; //定义一个 can_filter 结构体对象 

 

// 填充过滤规则,只接收 ID 为(can_id & can_mask)的报文 

rfilter[0].can_id = 0x60A; 
rfilter[0].can_mask = 0x7FF; 
rfilter[1].can_id = 0x60B; 
rfilter[1].can_mask = 0x7FF; 
 

// 调用 setsockopt 设置过滤规则 

setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)); 

struct can_filter 结构体中只有两个成员,can_id 和 can_mask。
如果应用程序不接收所有报文,在这种仅仅发送数据的应用中,可以在内核中省略接收队列,以此减少CPU 资源的消耗。此时可将 setsockopt()函数的第 4 个参数设置为 NULL,将第 5 个参数设置为 0,如下所示:

setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); 

数据发送/接收

在数据收发的内容方面,CAN 总线与标准套接字通信稍有不同,每一次通信都采用 struct can_frame 结构体将数据封装成帧。结构体定义如下:

示例代码 31.2.1 struct can_frame 结构体

struct can_frame { 
 canid_t can_id; /* CAN 标识符 */ 
 __u8 can_dlc; /* 数据长度(最长为 8 个字节) */ 
 __u8 __pad; /* padding */ 
 __u8 __res0; /* reserved / padding */ 
 __u8 __res1; /* reserved / padding */ 
 __u8 data[8]; /* 数据 */ 

}; 

can_id 为帧的标识符,如果是标准帧,就使用 can_id 的低 11 位;如果为扩展帧,就使用 0~28 位。can_id 的第 29、30、31 位是帧的标志位,用来定义帧的类型,定义如下:

/* special address description flags for the CAN_ID */ 

#define CAN_EFF_FLAG 0x80000000U /* 扩展帧的标识 */ 
#define CAN_RTR_FLAG 0x40000000U /* 远程帧的标识 */ 
#define CAN_ERR_FLAG 0x20000000U /* 错误帧的标识,用于错误检查 */ 

 
/* mask */ 

#define CAN_SFF_MASK 0x000007FFU /* <can_id & CAN_SFF_MASK>获取标准帧 ID */ 
#define CAN_EFF_MASK 0x1FFFFFFFU /* <can_id & CAN_EFF_MASK>获取标准帧 ID */ 
#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */ 

(1)、数据发送

对于数据发送,使用 write()函数来实现,譬如要发送的数据帧包含了三个字节数据 0xA0、0xB0 以及0xC0,帧 ID 为 123,可采用如下方法进行发送:

struct can_frame frame; //定义一个 can_frame 变量 

int ret; 
 
frame.can_id = 123;//如果为扩展帧,那么 frame.can_id = CAN_EFF_FLAG | 123; 
frame.can_dlc = 3; //数据长度为 3 
frame.data[0] = 0xA0; //数据内容为 0xA0 
frame.data[1] = 0xB0; //数据内容为 0xB0 
frame.data[2] = 0xC0; //数据内容为 0xC0 
 
ret = write(sockfd, &frame, sizeof(frame)); //发送数据 

if(sizeof(frame) != ret) //如果 ret 不等于帧长度,就说明发送失败 
 perror("write error"); 

如果要发送远程帧(帧 ID 为 123),可采用如下方法进行发送:

struct can_frame frame; 
 
frame.can_id = CAN_RTR_FLAG | 123; 
 
write(sockfd, &frame, sizeof(frame)); 

(2)、数据接收

数据接收使用 read()函数来实现,如下所示:

struct can_frame frame; 
 

int ret = read(sockfd, &frame, sizeof(frame)); 

(3)、错误处理

当应用程序接收到一帧数据之后,可以通过判断 can_id 中的 CAN_ERR_FLAG 位来判断接收的帧是否为错误帧。如果为错误帧,可以通过 can_id 的其他符号位来判断错误的具体原因。错误帧的符号位在头文件<linux/can/error.h>中定义。

/* error class (mask) in can_id */ 

#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */ 
#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */ 
#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */ 
#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */ 
#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */ 
#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */ 
#define CAN_ERR_BUSOFF 0x00000040U /* bus off */ 
#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */ 
#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */ 

回环功能设置

在默认情况下,CAN 的本地回环功能是开启的,可以使用下面的方法关闭或开启本地回环功能:

int loopback = 0; //0 表示关闭,1 表示开启(默认) 
 
setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback)); 

在本地回环功能开启的情况下,所有的发送帧都会被回环到与 CAN 总线接口对应的套接字上。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SocketCan 应用编程 的相关文章

  • C语言中将字符串拆分再进行拼接

    我们有时候需要对于字符串进行操作 xff0c 主要用到strcat和strtok两个函数 xff0c 因此记录下这次的操作方式以便之后查阅 span class token macro property span class token d
  • 并行编程实现矩阵乘法优化

    实现四种矩阵乘法程序 xff0c 并对比运行效率 1 xff09 串行算法 2 xff09 Catch优化 3 xff09 SSE版本 4 xff09 分片策略 span class token macro property span cl
  • c++的函数reserve()和unique()和sort()

    函数reserve span class token comment vector reserve span span class token macro property span class token directive keywor
  • 关于c中代码加 ‘\‘ 进行换行的说明

    我们在c与c 43 43 中经常会遇到一种情况就是加 进行换行来保持代码整体结构一致的使用情况 xff0c 那么具体来说换行的规则是什么 xff0c 这里进行一下记录 span class token macro property span
  • git的命令总结

    先把清单列出来git cheat sheet git 命令总结 git的init和git clonegit add和git commit 提交二连git checkout 反向操作git reset 回退HEAD指针git revert 同
  • 宏定义中的可变参数 __VA_ARGS__ 用法 与 #和##的用法

    首先了解一下可变参数 span class token macro property span class token directive keyword include span span class token string lt st
  • C++结构体简单链表原理解释

    对结构体简单链表原理的简单解释 xff0c 程序如下 xff1a struct lianbiao int no string name struct lianbiao next lianbiao head 61 nullptr tail 6
  • linux小连招

    Linux命令目录 查看当前shell的种类find命令查找文件 查看当前shell的种类 查看当前发行版可以使用的shell xff1a chao 64 chao span class token function cat span et
  • 侵略性奶牛(对于二分的总结)

    题目 Farmer John has built a new long barn with N 2 lt 61 N lt 61 100 000 stalls The stalls are located along a straight l
  • To Fill or Not to Fill(贪心算法)

    题目描述 有了高速公路 xff0c 开车从杭州到任何其他城市都很容易 但由于汽车的油箱容量有限 xff0c 我们必须不时地在路上找到加油站 不同的加油站可能会给出不同的价格 你被要求仔细设计最便宜的路线去 输入描述 对于每个测试实例 第一行
  • cmake语法

    目录 基本语法命令行projectadd executabletarget sourcessetfileadd librarymessagetarget link librariestarget include directoriesfin
  • 《C++个人学习笔记》使用cout或cin显示未定义标识符

    在vs自动生成的c 43 43 项目中 xff0c 初次使用cout或cin报未定义标识符错误 xff0c 是由于未声明命名空间的原因 解决方法 xff1a 在头文件中声明全局命名空间 加入 using namespace std 表示使用
  • rosbag error

    rosbag play 报错 error 1 md5sum span class token punctuation span span class token constant ERROR span span class token pu
  • CMake实践(三)静态库和动态库构建

    本节的任务是 xff1a 建立一个静态库和动态库 xff0c 提供HelloFunc函数供其他程序使用 xff0c HelloFunc函数向终端输出Hello World字符串 安装头文件和共享库 1 准备工作 在 backup cmake
  • checksum算法详细的计算方法、实现思路与python校验验证

    1 checksum是什么 xff1f Checksum xff1a 电脑 总和检验码 xff0c 校验和 在数据处理和数据通信领域中 xff0c 用于校验目的的一组数据项的和 这些数据项可以是数字或在计算检验总和过程中看作数字的其它字符串
  • java循环标号

    在Java中 xff0c 要想跳出多重循环 xff0c 可以在外面的循环语句前定义一个标号 xff0c 然后在里层循环体的代码中使用带有标号的break 语句 xff0c 即可跳出外层循环 switch xff08 continue和bre
  • SQL Server安装提示【需要microsoft.NET Framework 3.5 Service Pack 1】

    问题 我在自己电脑安装SQL Server 2014 的时候遇到了这个问题 xff0c SQL Server安装提示 需要microsoft NET Framework 3 5 Service Pack 1 xff1a 解决方法 1 打开控
  • zcu102_12_Standalone以太网数据的UDP传输

    本文在PS的Standalone程序中使用lwip库通过UDP协议实现网络数据传输 包含UDP Server和UDP Client两种形式 本文主要参考lwip的SDK内部说明和工程模板 xff0c xapp1026 xapp1306 SD
  • SQL Server 2014无法连接到服务器之解决方法

    问题如图所示 xff1a 解决方法 1 打开SQL server 配置管理器 gt SQL server 网络配置 gt MSSQLSERVER的协 xff0c 将SQLEXPRESS协议中的Named Pipes改为已启用 xff1a 2
  • C语言实现顺序表删除多余元素并统计移动次数

    题目 xff1a 假设顺序表L中的元素按从小到大的次序排列 xff0c 设计算法以删除表中重复的元素 用C写 xff0c 对顺序表 xff08 1 1 2 2 2 3 4 5 5 5 6 6 7 7 8 8 8 9 xff09 模拟执行本算

随机推荐