Linux socketcan应用编程

news2025/1/16 0:43:31

一、基本步骤

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 data
        int num_bytes = input.length() / 2; // Each byte is represented by 2 characters
        if (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 ID
        frame.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.coloradmin.cn/o/1885966.html

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

相关文章

游戏AI的创造思路-技术基础-tanh函数详解

又来搞事情&#xff0c;总想着把sigmoid函数替换成其他函数作为激活函数&#xff0c;或者找到更合适某一段训练的函数&#xff0c;所以今天来聊聊tanh函数&#xff08;谁让咱当年差点去了数学系&#xff0c;结果还是在数学系转过去计算机的&#xff09; 目录 3.9. tanh函数详解…

Springboot整合Redis以及业务工具类示例

docker安装Redis参考我另一篇博客Docker安装Redis及持久化 一、Get-Started 依赖 <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis --> <dependency><groupId>org.springframework.boot</groupId>…

轻松创建对象——简单工厂模式(Python实现)

1. 引言 大家好&#xff0c;又见面了&#xff01;今天我们要聊的是设计模式中的“万能钥匙”——简单工厂模式。想象一下&#xff0c;如果每次你都得亲自动手创建各种对象&#xff0c;不仅累得像个陀螺&#xff0c;还可能搞得一团糟。别怕&#xff0c;简单工厂模式来拯救你&am…

nextTick实现原理及使用场景

1.定义&#xff1a; nextTick是一个在Vue.js中常见的异步更新DOM的机制&#xff0c;它利用JavaScript的事件循环机制以及浏览器的渲染流程来实现延迟执行DOM更新操作。nextTick方法能够将回调函数延迟到下一个DOM更新循环之后执行&#xff0c;确保在DOM更新完成后执行某些操作…

【Linux系统】CUDA的安装

今天在安装环境时遇到报错&#xff1a; The detected CUDA version (10.1) mismatches the version that was used to compile PyTorch (11.8). Please make sure to use the same CUDA versions. 报错原因&#xff1a;安装的cuda版本不对应&#xff0c;我需要安装cuda的版本…

宠物空气净化器哪个品牌性价比高?宠物空气净器Top3品牌推荐

养猫确实给家庭带来了无尽的欢乐&#xff0c;但猫毛无处不在的问题确实让不少猫主人感到头疼。不论是长毛猫还是短毛猫&#xff0c;它们掉落的浮毛飘浮在空气中&#xff0c;不仅影响家居环境的整洁度&#xff0c;还可能成为过敏的源头。因此&#xff0c;如何高效地处理这些猫浮…

多模态融合 + 慢病精准预测

多模态融合 慢病精准预测 慢病预测算法拆解子解法1&#xff1a;多模态数据集成子解法2&#xff1a;实时数据处理与更新子解法3&#xff1a;采用大型语言多模态模型&#xff08;LLMMs&#xff09;进行深度学习分析 慢病预测更多模态 论文&#xff1a;https://arxiv.org/pdf/2406…

创新校园服务模式 跑腿小程序平台源码构建与实践 前后端分离 带完整的安装代码包以及部署教程

系统概述 本项目是一个集任务发布、接单、支付、评价于一体的跑腿服务小程序平台&#xff0c;专为高校校园设计。系统采用前后端分离架构&#xff0c;前端负责用户界面展示和交互逻辑&#xff0c;后端处理业务逻辑、数据存取等&#xff0c;两者通过API接口进行通信&#xff0c…

『手撕Vue-CLI』 添加自定义指令

添加 create 指令 在 vue-cli 中&#xff0c;create 指令是用来创建一个新的项目的&#xff0c;我实现的 nue --help 的帮助信息中只有 --version&#xff0c;--help 这两个指令&#xff0c;所以当用户使用我的 nue-cli 时&#xff0c;并不知道有 create 这个指令&#xff0c;所…

Conan安装与C++第三方环境配置保姆级图文教程(附速查字典)

目录 1 什么是Conan&#xff1f;2 Conan安装与配置3 Conan的常见操作3.1 搜索指定包3.2 安装指定包3.3 本地包管理3.4 查看项目依赖 4 Conan构建项目案例 1 什么是Conan&#xff1f; Conan是一个开源的C/C包管理器&#xff0c;用于管理和构建C/C项目所需的依赖库。传统上&…

BIOS设置与系统分区

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 目录 一BIOS 1破解密码的前提 2B…

CrossViT:用于图像分类的交叉注意多尺度Vision Transformer

提出了一种双支路Transformer来组合不同大小的图像补丁(即变压器中的令牌)以产生更强的图像特征。方法处理具有不同计算复杂度的两个独立分支的小补丁和大补丁令牌,然后这些令牌纯粹通过注意多次融合以相互补充。此外,为了减少计算量,开发了一个简单而有效的基于交叉关注的令…

98 - IDEA远程调试服务器Java程序

Java 提供了一套标准的调试协议&#xff08;JDWP - Java Debug Wire Protocol&#xff09;&#xff0c;允许调试器&#xff08;IDE&#xff09;与被调试程序&#xff08;应用&#xff09;之间进行通信。 1.服务器特定命令启动程序 在服务器上以以下命令启动Java程序 java -a…

linux 离线安装docker

测试服务器&#xff1a;银河麒麟V10 x86_64 注意&#xff1a;推荐使用国内的镜像站下载&#xff0c;因为官网不挂梯子无法访问&#xff0c;我用的是清华大学开源软件镜像站 一、下载离线包&#xff1a; 官网下载docker离线包 下载地址&#xff1a;https://download.docker.c…

老师怎样一键发布期末考试成绩?

期末考试的钟声一响&#xff0c;老师们便开始了紧张的阅卷工作。成绩出来后&#xff0c;他们又面临着一项繁琐的任务——将成绩单逐一私信给每位学生的家长。这不仅耗费了大量时间&#xff0c;也让老师们在繁忙的期末工作中倍感压力。期末老师的工作已经够多够繁琐&#xff0c;…

仪器校准的概念与定义,计量校准是什么?

仪器校准的定义&#xff0c;在之前所颁布的《国际计量学词汇 基础和通用概念及相关术语》文件中&#xff0c;已经有了明确说明&#xff0c;而该文件做了修改以后&#xff0c;在后续新的定义中&#xff0c;仪器校准具体被分为两部分&#xff0c;第一步是将被计量仪器和计量校准的…

汽车制造企业中MES管理系统还有哪些作用

在当今汽车制造业的飞速发展中&#xff0c;数字化转型已成为企业不可或缺的战略选择。在这个转型浪潮中&#xff0c;MES管理系统扮演着至关重要的角色&#xff0c;成为连接企业资源计划&#xff08;ERP&#xff09;与车间自动化系统的关键纽带。它不仅推动了生产流程的智能化、…

SpringSecurity中文文档(Servlet Persisting Authentication)

Persisting Authentication 用户第一次请求受保护的资源时&#xff0c;系统会提示他们输入凭据。提示凭据的最常见方法之一是将用户重定向到登录页。对于请求受保护资源的未经身份验证的用户&#xff0c;总结的 HTTP 交换可能如下所示: Example 1. Unauthenticated User Requ…

数据库测试数据准备厂商 Snaplet 宣布停止运营

上周刚获知「数据库调优厂商 OtterTune 宣布停止运营」。而今天下班前&#xff0c;同事又突然刷到另一家海外数据库工具商 Snaplet 也停止运营了。Snaplet 主要帮助开发团队在数据库中生成仿真度高且合规的测试数据。我们在年初还撰文介绍过它「告别手搓&#xff01;Postgres 一…