Linux socketcan应用编程

一、基本步骤

1、打开并绑定到 CAN 套接字

        在执行任何操作之前,第一步是创建一个套接字。此函数接受三个参数 – 域/协议系列 (PF_CAN)、套接字类型(原始或数据报)和套接字协议。如果成功,该函数将返回文件描述符。

int s;if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {perror("Socket");return 1;
}

        接下来,我们必须检索要使用的接口名称(can0、can1、vcan0 等)的接口索引。为此,我们发送一个 I/O 控制调用,并传递一个包含接口名称的 ifreq 结构:

struct ifreq ifr;strcpy(ifr.ifr_name, "vcan0" );
ioctl(s, SIOCGIFINDEX, &ifr);

有了接口索引,我们现在可以将套接字绑定到 CAN 接口:

struct sockaddr_can addr;memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {perror("Bind");return 1;
}

2、发送帧

        要发送CAN帧,必须初始化can_frame结构并用数据填充它。基本的can_frame结构在 include/linux/can.h 中定义,如下所示:

struct can_frame {canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */canid_t can_id; /* 32 位 CAN_ID + EFF/RTR/ERR 标志 */__u8 can_dlc; /* 以字节为单位的帧有效负载长度 (0 .. 8) */__u8 __pad; /*填充*/__u8 __res0; /* 保留 / 填充 */__u8 __res1; /* 保留 / 填充 */__u8    data[8] __attribute__((aligned(8)));
};

        下面我们初始化一个 ID 为 0x555 的 CAN 帧,一个包含 “hello” 的 5 个字节的有效负载,并使用 write() 系统调用发送它:

struct can_frame frame;frame.can_id = 0x555;
frame.can_dlc = 5;
sprintf(frame.data, "Hello");if (write(s, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {perror("Write");return 1;
}

3、读取帧

要读取帧,请初始化can_frame并调用 read() 系统调用。这将阻塞,直到帧可用:

int nbytes;
struct can_frame frame;nbytes = read(s, &frame, sizeof(struct can_frame));if (nbytes < 0) {perror("Read");return 1;
}printf("0x%03X [%d] ",frame.can_id, frame.can_dlc);for (i = 0; i < frame.can_dlc; i++)printf("%02X ",frame.data[i]);printf("\r\n");

在上面的示例中,我们显示 ID、数据长度代码 (DLC) 和有效负载。

4、设置过滤器

        除了读取之外,您可能还希望过滤掉不相关的CAN帧。这发生在驱动程序级别,这比在用户模式应用程序中读取每一帧更有效。

        若要设置筛选器,请初始化单个can_filter结构或结构数组,然后填充can_id和can_mask。调用 setsockopt():

struct can_filter rfilter[1];rfilter[0].can_id   = 0x550;
rfilter[0].can_mask = 0xFF0;
//rfilter[1].can_id   = 0x200;
//rfilter[1].can_mask = 0x700;setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));

5、关闭套接字

最后,如果不再需要套接字,关闭:

if (close(s) < 0) {perror("Close");return 1;
}

完整代码:

#include <iostream>
#include <thread>
#include <cstring>
#include <unistd.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <iomanip>// // CAN配置函数,设置波特率等参数
// void configureCan(int sock, const char *interface_name) {
//     // 设置波特率
//     std::string set_bitrate_command = "ip link set " + std::string(interface_name) + " type can bitrate 500000";
//     system(set_bitrate_command.c_str());//     // 再次打开CAN接口
//     std::string up_command = "ifconfig " + std::string(interface_name) + " up";
//     system(up_command.c_str());
// }// CAN初始化函数,创建套接字并绑定
int initCanSocket(const char *interface_name) {int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);if (sock < 0) {perror("Socket creation failed");return -1;}struct sockaddr_can addr;struct ifreq ifr;std::strcpy(ifr.ifr_name, interface_name);if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {perror("IOCTL failed");close(sock);return -1;}addr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {perror("Binding failed");close(sock);return -1;}return sock;
}// 处理发送CAN帧的函数
void sendCanFrames(int sock, const char *interface_name) {struct sockaddr_can addr;struct ifreq ifr;struct can_frame frame;std::memset(&ifr, 0, sizeof(ifr));std::strcpy(ifr.ifr_name, interface_name);ioctl(sock, SIOCGIFINDEX, &ifr);addr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;while (true) {// 从用户输入获取数据std::string input;std::cout << "Enter CAN frame data (8 bytes in hexadecimal): ";std::getline(std::cin, input);// Convert user input to CAN frame dataint num_bytes = input.length() / 2; // Each byte is represented by 2 charactersif (num_bytes > 8) {std::cerr << "Error: Input data exceeds maximum length of 8 bytes.\n";continue;}frame.can_dlc = num_bytes;for (int i = 0; i < num_bytes; ++i) {std::string byte_string = input.substr(2*i, 2);frame.data[i] = std::stoi(byte_string, 0, 16); // Convert hexadecimal string to integer}// 示例CAN IDframe.can_id = 0x123;// 发送CAN帧ssize_t nbytes = write(sock, &frame, sizeof(struct can_frame));if (nbytes != sizeof(struct can_frame)) {perror("write");break;}printf("Sent CAN frame on interface %s\n", interface_name);// 发送下一帧前睡眠一段时间,可以根据需求调整usleep(1000000); // 1秒}
}// CAN接收函数,以线程方式接收CAN帧
void receiveCanFrames(int sock, const char *interface_name) {struct can_frame frame;while (true) {ssize_t nbytes = read(sock, &frame, sizeof(struct can_frame));if (nbytes > 0) {std::cout << "Received CAN frame on " << interface_name << std::endl;std::cout << "Data: ";for (int i = 0; i < frame.can_dlc; ++i) {std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(frame.data[i]) << " ";}std::cout << std::endl;// 假设CAN数据在frame.data中,长度为frame.can_dlc// // 解析CAN帧数据,示例中假设前两个字节是命令// if (frame.can_dlc >= 2) {//     uint8_t command_byte1 = frame.data[0];//     uint8_t command_byte2 = frame.data[1];//     // 根据接收到的命令字节执行不同的命令行操作//     if (command_byte1 == 0x00 && command_byte2 == 0x11) {//         // 执行命令 dvr_test -c 4 0 4 8 12//         char *args[] = { (char*)"dvr_test", (char*)"-c", (char*)"4",(char*)"0", (char*)"4", (char*)"8", (char*)"12", NULL };//         execvp(args[0], args);//     } else if (command_byte1 == 0x22 && command_byte2 == 0x33) {//         // 执行命令 dvr_test -c 4 12 4 8 0//         char *args[] = { (char*)"dvr_test", (char*)"-c", (char*)"4",(char*)"12", (char*)"4", (char*)"8", (char*)"0", NULL };//         execvp(args[0], args);//     } else {//         std::cout << "Unhandled command bytes: " << std::hex << (int)command_byte1 << " " << (int)command_byte2 << std::endl;//         // 如果有其他命令需要处理,可以继续添加判断和执行操作//     }// } else {//     std::cout << "Invalid CAN frame data length" << std::endl;// }}}
}// 主函数,负责整体流程控制
int main() {int sock_awlink0, sock_awlink1;const char *can_interface_awlink0 = "awlink0";const char *can_interface_awlink1 = "awlink1";// 初始化CAN接口套接字sock_awlink0 = initCanSocket(can_interface_awlink0);sock_awlink1 = initCanSocket(can_interface_awlink1);if (sock_awlink0 < 0 || sock_awlink1 < 0) {std::cerr << "Failed to initialize CAN sockets." << std::endl;return 1;}// 配置CAN接口// configureCan(sock_awlink0, can_interface_awlink0);// configureCan(sock_awlink1, can_interface_awlink1);// 创建并启动发送线程std::thread send_thread_awlink0(sendCanFrames, sock_awlink0, can_interface_awlink0);std::thread send_thread_awlink1(sendCanFrames, sock_awlink1, can_interface_awlink1);// 创建并启动接收线程std::thread receive_thread_awlink0(receiveCanFrames, sock_awlink0, can_interface_awlink0);std::thread receive_thread_awlink1(receiveCanFrames, sock_awlink1, can_interface_awlink1);// 主线程等待发送线程和接收线程结束send_thread_awlink0.join();send_thread_awlink1.join();receive_thread_awlink0.join();receive_thread_awlink1.join();// 关闭套接字close(sock_awlink0);close(sock_awlink1);return 0;
}

二、测试

1、按照正常步骤开启CAN

2、编译后拷贝到开发板,修改执行权限,运行

发送数据:

然后可以通过can分析仪工具TCANLINPro查看发来的数据

接收数据:

通过TCANLINPro发送数据

 开发板接受相应数据:

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

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

相关文章

Linux多线程【线程互斥】

文章目录 Linux线程互斥进程线程间的互斥相关背景概念互斥量mutex模拟抢票代码 互斥量的接口初始化互斥量销毁互斥量互斥量加锁和解锁改进模拟抢票代码&#xff08;加锁&#xff09;小结对锁封装 lockGuard.hpp 互斥量实现原理探究可重入VS线程安全概念常见的线程不安全的情况常…

实习总结 --- 内部平台使用

常用术语 CR CR–标准问题分类管理平台&#xff1a;由业务类型-角色-国家-品类-Page定义。 FAQSOP FAQ是端上用户自助的第一道关口&#xff0c;在引导用户进行自助解决上起关键作用 SOP是指标准作业程序&#xff0c;客服SOP是针对用户遇到的具体问题场景&#xff0c;给客服…

鸿蒙OS开发者高级学习第2课:自由流转(含习题答案)

自由流转两种形态&#xff1a;相继使用&#xff08;跨端迁移&#xff09;&#xff1b;同时使用&#xff08; 多端协同&#xff09; 习题&#xff1a;

DCU整体硬件架构

DCU整体硬件架构 DCU整体硬件架构 首先&#xff0c;DCU通过PCI-E总线与CPU处理器相连&#xff0c;它是CPU主机系统的一个硬件扩展&#xff0c;其存在的目的是为了对程序某些模块或者函数进行加速。虽然DCU是原硬件系统的一个扩展&#xff0c;接受CPU调度指挥&#xff0c;但是在…

轻松配置,无需重复操作:PyCharm新建项目后,如何让当前新建项目使用既有虚拟环境

1、点击右上角的设置按钮 2、点击Settings 3、点击profect 4、点击python Interprter&#xff0c;这个是python解释器 5、点击 add interpreter&#xff0c;这个是增加python解释器 6、再点击add Local interpreter 7、选择第一个Virtualenv Environment,然后选择Existin…

rufus-4.5 制作 Clonezilla(再生龙)启动盘报syslinux-6.04下载错误(很实用)

1、官网下载rufus 官网下载rufus-4.5&#xff0c;下载地址&#xff1a;https://rufus.ie/downloads/ 2、下载再生龙&#xff08;Clonezilla&#xff09; 下载最新版本&#xff1a; Clonezilla live 版本: 3.1.2-22&#xff1a;https://sourceforge.net/projects/clonezill…

Docker拉取失败,利用 Git将 Docker镜像重新打 Tag 推送到阿里云等其他公有云镜像仓库里

目录 一、开通阿里云容器镜像服务 二、Git配置 三、去DockerHub找镜像 四、编写images.txt文件 ​五、演示 六、其他注意事项 最近一段时间 Docker 镜像一直是 Pull 不下来的状态&#xff0c;想直连 DockerHub 是几乎不可能的。更糟糕的是&#xff0c;很多原本可靠的国内…

Springboot3本地编译exe文件(实现快速启动仅需200ms)

1. 准备好grallvm版本的JDK jdk17以上 &#xff08;springboot3最低支持jdk17&#xff09; grallvm-jdk17 Download GraalVM 下载界面 2. 配置maven 3.9.x 及以上 maven 3.9.8 Maven – Download Apache Maven 3.创建SpringBoot项目 3.1 项目所需依赖 记得选择这俩个进…

免费可视化工具助力旅游数据分析

在这个数据驱动的时代&#xff0c;旅游行业正以前所未有的速度转型升级&#xff0c;从传统的资源导向转变为精准服务与个性化体验为核心。面对海量的旅游数据&#xff0c;如何高效、直观地挖掘其价值&#xff0c;成为旅游企业提升竞争力、优化游客体验的关键。 在过去&#xff…

uniapp中实现瀑布流 短视频页面展示

直接上干货 第一部分为结构 <swiper class"list" :currentindex change"swiperchange" scrolltolower"onReachBottom"><swiper-item style"overflow: scroll;" v-for"(item,index) in 2" :key"index"&g…

FastApi中的常见请求类型

FastApi中的常见请求类型 后端开发语言中&#xff0c;我钟情于node&#xff0c;高效的异步处理真是让我眼前一亮&#xff0c;同时&#xff0c;简单易懂的语法也让我非常倾心 但是但是&#xff0c;因为考虑要写一个深度学习算法的后端接口&#xff0c;所以不得不选用python作为…

【MySQL】数据库——主从复制和读写分离

一、MySQL读写分离 1.概念&#xff1a; 读写分离&#xff0c;基本的原理是让主数据库处理事务性增、改、删操作&#xff08;insert、update、delete&#xff09;&#xff0c;而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。 …

每日复盘-20240702

今日关注&#xff1a; 20240702 六日涨幅最大: ------1--------301388--------- 欣灵电气 五日涨幅最大: ------1--------301388--------- 欣灵电气 四日涨幅最大: ------1--------301388--------- 欣灵电气 三日涨幅最大: ------1--------301388--------- 欣灵电气 二日涨幅最…

OpenSSH RCE (CVE-2024-6387) | 附poc | 小试

Ⅰ 漏洞描述 OpenSSH 远程代码执行漏洞(CVE-2024-6387)&#xff0c;该漏洞是由于OpenSSH服务器 (sshd) 中的信号处理程序竞争问题&#xff0c;未经身份验证的攻击者可以利用此漏洞在Linux系统上以root身份执行任意代码。 Ⅱ 影响范围 8.5p1 < OpenSSH < 9.8p1 但OpenSS…

Java--创建对象内存分析

1.如图所示&#xff0c;左边为一个主程序&#xff0c;拥有main方法&#xff0c;右边定义了一个Pet类&#xff0c;通过debug不难看出&#xff0c;当启动该方法时&#xff0c;有以下该步骤 1.运行左边的实例化Pet类对象 2.跳转至右边定义Pet类的语句 3.跳回至左边获取Pet类基本属…

PyInstaller exe文件报错

文章目录 包找不到的问题去掉黑窗口 包找不到的问题 遇到的问题 : 打包好了之后exe文件报错: 没有找到这个文件 1.当时打包的 有这个文件main.spec 打开它找到hiddenimports ,填上差的包 2, 删除build和dist 3,在当前命令行下执行pyinstaller main.spec打包生成exe 去掉黑…

3.js - 深度测试、深度写入、深度函数

md&#xff0c;艹&#xff0c;这玩意得理解&#xff0c;只看代码不管事 效果图 代码 // ts-nocheck// 引入three.js import * as THREE from three// 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls// 导入lil.gui import { GUI } …

入门PHP就来我这(纯干货)05

~~~~ 有胆量你就来跟着路老师卷起来&#xff01; -- 纯干货&#xff0c;技术知识分享 ~~~~ 路老师给大家分享PHP语言的知识了&#xff0c;旨在想让大家入门PHP&#xff0c;并深入了解PHP语言。 1 数组及创建 数组&#xff0c;顾名思义&#xff0c;本质上就是一系列数据的组合…

Studying-代码随想录训练营day26| 491.递增子序列、46.全排列、47.全排列 II、51.N皇后、37.解数独、回溯总结

第26天&#xff0c;回溯part04&#xff0c;昨天休息复习总结回溯内容&#xff0c;&#x1f4aa;(ง •_•)ง&#x1f4aa; 目录 491.递增子序列 46.全排列 47.全排列 II 51.N皇后 37.解数独 回溯总结 491.递增子序列 文档讲解&#xff1a;代码随想录递增子序列 视频讲…

[数据库原理]数据库设计(er图)

xtu期末是机试&#xff0c;所以图形表示有点不同 实体之间的关系&#xff1a; 多对多&#xff1a;可以生成一个新的关系模型一对一&#xff1a;两边都要关联一对多、多对一 &#xff1a;一的主键可以作为多的外键 如有错误&#xff0c;欢迎指正&#xff01;&#xff01;&#x…