CAN Linux C应用编程

由于 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函数中第一个参数用于指定通信域,在 SocketCan 中,通常将
其设置为PF_CAN,指定为CAN通信协议;第二个参数用于指定套接字的类型,通常将其设置为SOCK_RAW;第三个参数通常设置为 CAN_RAW。

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

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

......
struct 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);
}

这里出现了两个结构体: 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 结构体将数据封装成帧。结构体定义如下:

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 总线接口对应的套接字上。

CAN 数据发送实例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
int main(void)
{struct ifreq ifr = {0};struct sockaddr_can can_addr = {0};struct can_frame frame = {0};int sockfd = -1;int ret;/* 打开套接字 */sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);if(0 > sockfd) {perror("socket error");exit(EXIT_FAILURE);}/* 指定 can0 设备 */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);}/* 设置过滤规则:不接受任何报文、仅发送数据 */setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);/* 发送数据 */frame.data[0] = 0xA0;frame.data[1] = 0xB0;frame.data[2] = 0xC0;frame.data[3] = 0xD0;frame.data[4] = 0xE0;frame.data[5] = 0xF0;frame.can_dlc = 6; //一次发送 6 个字节数据frame.can_id = 0x123;//帧 ID 为 0x123,标准帧for ( ; ; ) {ret = write(sockfd, &frame, sizeof(frame)); //发送数据if(sizeof(frame) != ret) { //如果 ret 不等于帧长度,就说明发送失败perror("write error");goto out;}sleep(1); //一秒钟发送一次}out:/* 关闭套接字 */close(sockfd);exit(EXIT_SUCCESS);
}

CAN 数据接收实例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
int main(void)
{struct ifreq ifr = {0};struct sockaddr_can can_addr = {0};struct can_frame frame = {0};int sockfd = -1;int i;int ret;/* 打开套接字 */sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);if(0 > sockfd) {perror("socket error");exit(EXIT_FAILURE);}/* 指定 can0 设备 */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);}/* 设置过滤规则 *///setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);/* 接收数据 */for ( ; ; ) {if (0 > read(sockfd, &frame, sizeof(struct can_frame))) {perror("read error");break;}/* 校验是否接收到错误帧 */if (frame.can_id & CAN_ERR_FLAG) {printf("Error frame!\n");break;}/* 校验帧格式 */if (frame.can_id & CAN_EFF_FLAG) //扩展帧printf("扩展帧 <0x%08x> ", frame.can_id & CAN_EFF_MASK);else //标准帧printf("标准帧 <0x%03x> ", frame.can_id & CAN_SFF_MASK);/* 校验帧类型:数据帧还是远程帧 */if (frame.can_id & CAN_RTR_FLAG) {printf("remote request\n");continue;}/* 打印数据长度 */printf("[%d] ", frame.can_dlc);/* 打印数据 */for (i = 0; i < frame.can_dlc; i++)printf("%02x ", frame.data[i]);printf("\n");}/* 关闭套接字 */close(sockfd);exit(EXIT_SUCCESS);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/694489.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

隐私保护 AI 的演变:从协议到实际实现

近些年&#xff0c;人工智能不断发展&#xff0c;在解决日常人类任务方面变得更加高效。但与此同时&#xff0c;它增加了个人信息滥用的可能性&#xff0c;在分析和传播个人数据方面达到了前所未有的力量和速度水平。在这篇文章中&#xff0c;我想仔细研究一下人工智能系统和机…

spinalhdl,vivado,fpga

https://spinalhdl.github.io/SpinalDoc-RTD/master spinal hdl sudo apt install openjdk-17-jdk scala curl echo “deb https://repo.scala-sbt.org/scalasbt/debian all main” | sudo tee /etc/apt/sources.list.d/sbt.list echo “deb https://repo.scala-sbt.org/scal…

浅谈加密算法(对称加密、非对称加密、混合加密、数字签名、哈希函数)

1、对称加密 对称加密只有一个密钥&#xff0c;直接使用这一个密钥对信息进行加密或解密。这样子就使得对称加密解密十分高效&#xff0c;计算量也相较于非对称加密小很多&#xff0c;适合有大量数据的场合。 密钥只有一个且他一定不能泄漏。由此分发密钥&#xff0c;讲这个密钥…

SQL 练习题目(入门级)

今天发现了一个练习SQL的网站--牛客网。里面题目挺多的&#xff0c;按照入门、简单、中等、困难进行了分类&#xff0c;可以直接在线输入SQL语句验证是否正确&#xff0c;并且提供了测试表的创建语句&#xff0c;也可以方便自己拓展练习&#xff0c;感觉还是很不错的一个网站&a…

ChromeDriver | 谷歌浏览器驱动下载地址 及 浏览器版本禁止更新

在使用selenoum时&#xff0c;需要chrome浏览器的版本和chrome浏览器驱动的版本一致匹配&#xff0c;才能进行自动化测试 一、ChromeDriver驱动镜像网址 国内可以搜到的谷歌浏览器下载地址里面最新的驱动器只有114版本的CNPM Binaries Mirror 在其他博主那找到了最新版本12X的…

【PX4SimulinkGazebo联合仿真】在Simulink中使用ROS2控制无人机沿自定义圆形轨迹飞行并在Gazebo中可视化

在Simulink中使用ROS2控制无人机沿自定义圆形轨迹飞行并在Gazebo中可视化 系统架构Matlab官方例程Control a Simulated UAV Using ROS 2 and PX4 Bridge运行所需的环境配置PX4&Simulink&Gazebo联合仿真实现方法建立Simulink模型并完成基本配置整体框架各子系统实现原理…

识别图片字符-PaddleOCR

PaddleOCR 是由百度开发的一个开源光学字符识别&#xff08;OCR&#xff09;工具&#xff0c;它可以识别图片中的文本信息。然而&#xff0c;PaddleOCR 本身主要专注于文本的检测与识别 安装PaddleOCR框架 pip install paddlepaddle paddleocr 使用PaddleOCR识别图片代码 fro…

STL - hash

1、unordered系列关联式容器 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可达到O()&#xff0c;即最差情况下需要比较红黑树的高度次&#xff0c;当树中的节点非常多时&#xff0c;查询效率也不理想。最好 的查询是&#xff0c;进…

第四十一回 还道村受三卷天书 宋公明遇九天玄女-python创建临时文件和文件夹

宋江想回家请老父亲上山&#xff0c;晁盖说过几天带领山寨人马一起去。宋江还是坚持一个人去。 宋江到了宋家村&#xff0c;被两个都头和捕快们追捕&#xff0c;慌不择路&#xff0c;躲进了一所古庙。一会儿&#xff0c;听见有人说&#xff1a;小童奉娘娘法旨&#xff0c;请星主…

SpringBoot2整合支付宝进行沙箱支付

目录 1. 进入支付宝的开放平台 2. 导入Maven依赖 3. 配置application.yml文件 NATAPP.cn(内网穿透工具) 注册登录 下载 4. 后端配置 5. 测试 1. 进入支付宝的开放平台 开发平台: 支付宝开放平台 登录后,点击控制台 点击最下面的沙箱 2. 导入Maven依赖 <dependency…

频率主义线性回归和贝叶斯线性回归

整体概述 频率主义&#xff08;Frequentist&#xff09;线性回归和贝叶斯&#xff08;Bayesian&#xff09;线性回归是统计学中用于数据分析和预测的两种主要方法&#xff0c;特别是在建模关于因变量和自变量之间线性关系的上下文中。尽管两种方法都用于线性回归分析&#xff…

【LeetCode】746. 使用最小花费爬楼梯(简单)——代码随想录算法训练营Day38

题目链接&#xff1a;746. 使用最小花费爬楼梯 题目描述 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 …

Git 客户端可视化工具tortoisegit

Git 使用教程 git一点通 (kdocs.cn) 二、Git 客户端可视化工具-推荐 1.常用工具 tortoisegit 官网 https://tortoisegit.org/ 推荐 sourcetree 官网 https://www.sourcetreeapp.com/ 2.tortoisegit安装 2.1 下载安装包 2.2 下载语言包 2.3 安装 2.4 安装语言包 5.使用 5.1 新建…

C++ CRTP设计范式

CRTP&#xff08;Curiously Recurring Template Pattern&#xff09;奇异递归模板范式是一个相对少有人知的C设计范式&#xff0c;它可以实现基类指针调用派生类的函数&#xff0c;从而实现另类多态。 如&#xff1a; #include <iostream> // 基类模板&#xff0c;接受…

Sora 一款文本转视频模型

**Sora** 是一个由美国人工智能研究机构 **OpenAI** 开发的 AI 视频模型。让我们一起了解一下这个令人兴奋的项目&#xff1a; 1. **名称和含义**&#xff1a; - 在日语中&#xff0c;**Sora** 是“天空”的意思&#xff0c;引申含义还有“自由”。 - **Sora** 的官方介绍页上展…

js之事件循环

JavaScript的事件循环是它的并发模型的核心部分&#xff0c;使得JavaScript能够在单线程中处理异步操作。事件循环允许JavaScript在执行代码时&#xff0c;同时进行非阻塞的I/O操作&#xff08;如网络请求、文件操作等&#xff09;。这个概念对于理解如何高效地构建交互式Web应…

本地模拟发送、接收RabbitMQ数据

文章目录 前言一、相关文章二、相关代码1.模拟的 Channel 类2.接收消息3.模拟推送MQ数据前言 日常开发中,当线上RabbitMQ坏境还没准备好时,可在本地模拟发送、接收消息 一、相关文章 Docker安装RabbitMQ 【SpringCloud】整合RabbitMQ六大模式应用(入门到精通) Spring R…

Spring学习笔记(三)--Spring中的Bean的管理

一、什么是Bean Bean是注册到Spring容器中的Java类&#xff0c;控制反转和依赖注入都是通过Bean实现的&#xff0c;任何一个Java类都可以是一个Bean。Bean由Spring进行管理&#xff0c;可以通过xml文件对bean进行配置和管理。 二、BeanFactory接口和ApplicationContext接口&a…

利用Python实现科学式占卜

一直以来&#xff0c;中式占卜都是基于算命先生手工实现&#xff0c;程序繁琐&#xff08;往往需要沐浴、计算天时、静心等等流程&#xff09;。准备工作复杂&#xff08;通常需要铜钱等道具&#xff09;&#xff0c;计算方法复杂&#xff0c;需要纯手工计算二进制并转换为最终…

2023年12月 Python(六级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,共50分) 第1题 运行以下程序,输出的结果是?( ) class A():def __init__(self,x):self.x=x