(六)CAN总线通讯

文章目录

  • CAN总线回环测试
    • 第一种基于板载CAN测试
      • 第一步确认板载是否支持
      • 第二步关闭 CAN 接口将 CAN 接口置于非活动状态
      • 第三步 配置 CAN 接口
        • 第一步 设置 CAN 接口比特率
        • 第二步 设置 CAN 启用回环模式
        • 第三步 启用 CAN 接口
      • 第四步 测试CAN总线回环
        • 捕获 CAN 消息
        • 发送 CAN 消息
    • 第二种基于Linux库的原生开发
      • 1. 确认硬件支持
      • 2. 配置 CAN 接口
      • 3. 使用 SocketCAN API 编写应用程序
        • 创建 CAN 套接字
        • 绑定 CAN 接口
        • 发送和接收 CAN 消息
        • 示例代码
      • 4. 应用程序测试


CAN总线回环测试

第一种基于板载CAN测试

第一步确认板载是否支持

确保你的硬件支持 CAN,并且已经正确安装了驱动程序。可以通过以下命令检查是否检测到了 CAN 设备:

ip link show type can

可以看到板载自带了can0和can1.

请添加图片描述

can0 和 can1:是接口的名称,分别表示系统中的第一个和第二个 CAN 接口。

<NOARP,ECHO>:
NOARP:表示该接口不使用 ARP(地址解析协议)。CAN 总线不需要 ARP 因为它不是基于 IP 的网络。
ECHO:可能启用了回环模式,发送的消息会被自己的 CAN 控制器重新接收。这里不确定是否启用了,可以用指令ip -details link show 查看到:

请添加图片描述

或者单看can0,can1的状态,以can0为例子:

ip -details link show can0

请添加图片描述

mtu 16:最大传输单元(Maximum Transmission Unit),对于 CAN 接口来说MTU 通常是 16 字节,这是因为 CAN 消息的最大有效载荷是 8 字节,加上一些额外的头部信息。

qdisc noop:队列调度算法(Queueing Discipline),这里使用的是 noop,即空操作队列调度器。这意味着没有任何包调度策略被应用,所有数据包将直接传递而不会排队。

state DOWN:当前接口状态为关闭(DOWN),意味着接口未激活,无法进行通信。要激活接口,可以使用 ip link set can0 up 或 ip link set can1 up。

mode DEFAULT:表示接口的工作模式,默认情况下是标准模式。

group default:指明这个接口属于哪个组,default 是默认组。

qlen 10:队列长度(Queue Length),表示可以排队等待处理的数据包数量,在这里是 10。

链路类型
link/can:表明这是一个 CAN 类型的接口。


第二步关闭 CAN 接口将 CAN 接口置于非活动状态

如果没有激活的情况,直接跳过此步

ip link set can0 down

请添加图片描述


第三步 配置 CAN 接口

第一步 设置 CAN 接口比特率
ip link set can0 type can bitrate 500000

ip link set:这是一个用来配置网络接口的命令。
can0:指定要配置的网络接口名称,这里是 can0。
type can:指明该接口是 CAN 类型的。
bitrate 500000:设置 CAN 总线的数据传输速率(比特率)为 500 kbps。这个值应该根据你的硬件支持和需求进行调整。

设置完可以借助指令查看:

ip -details link show can0

请添加图片描述


第二步 设置 CAN 启用回环模式

指令:

ip link set can0 type can loopback on

loopback on:启用回环模式。在这种模式下,所有从 CAN 控制器发出的消息都会被重新送回到同一个控制器,而不会真正发送到物理总线上。这对于测试非常有用,因为它允许你在不连接任何其他设备的情况下验证软件是否正常工作。

请添加图片描述

设置完同样可以借助指令查看:

ip -details link show can0

请添加图片描述


第三步 启用 CAN 接口

指令:

ip link set can0 up

请添加图片描述

启动成功后同样可以借助指令查看:

ip -details link show can0

请添加图片描述


第四步 测试CAN总线回环

捕获 CAN 消息

使用 nohup 和 candump 捕获消息并记录到文件

nohup candump can0 > log.txt &

nohup:使程序在用户退出终端后继续运行。这对于长时间运行的任务很有用。
candump:这是 Linux 下的一个工具,用于捕获并显示 CAN 消息。它会实时监听指定的 CAN 接口,并输出接收到的消息。
can0:指定要监听的 CAN 接口。
log.txt:将 candump 的输出重定向到一个名为 log.txt 的文件中,而不是打印到屏幕上。
&:将命令放入后台执行,这样可以在同一终端窗口中继续输入其他命令。

请添加图片描述

发送 CAN 消息

标准数据帧发8位数据:

cansend can0 0B000123#00.00.00.00.00.00.00.01

#后面每一位数据不加.也行,注意别发错了

cansend:这是 Linux 下的一个工具,用于向指定的 CAN 接口发送 CAN 消息。
can0:指定要使用的 CAN 接口。

0B000123#00.00.00.00.00.00.00.01:这是要发送的 CAN 消息格式。
具体来说:0B000123是 CAN ID,其中 0B 表示标准帧格式(11位ID),000123是具体的 ID 值。
#分隔符,后面跟着的是数据字段。00.00.00.00.00.00.00.01 是数据字段的内容,表示 8 字节的数据。每个字节用两位十六进制数表示。

请添加图片描述

其实都用默认,就是标准数据帧:

cansend can0 123#00.00.00.00.00.00.00.02

请添加图片描述

数据也可以发0-8任意字节:

cansend can0 234#88

请添加图片描述

第二种基于Linux库的原生开发

在 Linux 下进行 CAN 总线应用开发时,通常需要通过系统调用和特定的套接字 API 来与 CAN 接口交互。Linux 内核提供了一个叫做 SocketCAN 的子系统,它使得 CAN 通信可以像普通的网络编程一样使用标准的 BSD 套接字接口来实现。下面是进行 CAN 应用开发的基本步骤:

1. 确认硬件支持

ip link show type can

加粗样式
可以看到板载自带了can0和can1.

请添加图片描述


2. 配置 CAN 接口

先关闭can接口

ip link set can0 down

配置波特率:

ip link set can0 type can bitrate 500000

如果使用回环如下操作,不使用或者使用正常模式就跳过此步,我这里采用回环测试因此执行此步骤:

ip link set can0 type can loopback on

启动can接口

ip link set can0 up

至此所有配置完成


3. 使用 SocketCAN API 编写应用程序

创建 CAN 套接字

在 C/C++ 中,可以通过 socket() 函数创建一个 CAN 套接字。例如:

#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>int s;
if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {perror("Socket creation failed");return -1;
}

绑定 CAN 接口

接下来,将套接字绑定到具体的 CAN 接口(如 can0)。这可以通过 bind() 函数完成:

struct ifreq ifr;
struct sockaddr_can addr;addr.can_family = AF_CAN;
strcpy(ifr.ifr_name, "can0");if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {perror("Interface index get failed");close(s);return -1;
}addr.can_ifindex = ifr.ifr_ifindex;if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {perror("Bind to CAN interface failed");close(s);return -1;
}

发送和接收 CAN 消息
struct can_frame frame;// 构造要发送的 CAN 帧
frame.can_id = 0x123; // CAN ID
frame.can_dlc = 8;    // 数据长度码(DLC),表示数据域的字节数
memset(frame.data, 0x01, frame.can_dlc); // 设置数据域内容// 发送 CAN 帧
if (write(s, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {perror("Write CAN frame failed");close(s);return -1;
}// 接收 CAN 帧
if (read(s, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {perror("Read CAN frame failed");close(s);return -1;
}printf("Received CAN ID=%x, DLC=%d\n", frame.can_id, frame.can_dlc);
for (int i = 0; i < frame.can_dlc; ++i)printf("Data[%d]: %02x\n", i, frame.data[i]);

示例代码

下面是一段完整的示例代码,展示了如何创建 CAN 套接字、绑定接口、发送和接收消息:

这里我开启了线程,父线程用来写,子线程用来读;代码被屏蔽掉的那部分是,禁止回环模式的,为了防止配置的时候关闭回环失败,因此软件上再关闭一次。

#include "can_config.h"#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <libgen.h>
#include <getopt.h>
#include <limits.h>#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <net/if.h>#include <linux/can.h>
#include <linux/can/raw.h>int main()
{int s; // 就是fdint n_read = 0; // 读取到的数据个数//创建 CAN 套接字if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {perror("Socket creation failed");return -1;}printf("socket ok ========================\n");/*// 禁用回环模式(如果需要)int loopback = 0; // 0 表示关闭回环模式if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback)) < 0) {perror("Failed to disable loopback mode");close(s);return -1;}// 禁用监听自己的消息(可选)int recv_own_msgs = 0; // 0 表示关闭接收自己的消息if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &recv_own_msgs, sizeof(recv_own_msgs)) < 0) {perror("Failed to disable receiving own messages");close(s);return -1;}*/// 绑定 CAN 接口struct ifreq ifr;struct sockaddr_can addr;addr.can_family = AF_CAN;strcpy(ifr.ifr_name, "can0");if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {perror("Interface index get failed");close(s);return -1;}addr.can_ifindex = ifr.ifr_ifindex;if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {perror("Bind to CAN interface failed");close(s);return -1;}printf("bind ok ========================\n");// 构造要发送的 CAN 帧struct can_frame wirte_frame;struct can_frame recive_frame;wirte_frame.can_id = 0x123; // CAN IDwirte_frame.can_dlc = 8;    // 数据长度码(DLC),表示数据域的字节数memset(wirte_frame.data, 0x01, wirte_frame.can_dlc); // 设置数据域内容int data = 0x01;//开启线程 一个接收一个发送;__pid_t pid;pid = fork();//返回的pid号 父进程是正数id号码,子进程是0// 父进程 write  子进程接收if(pid>0){printf("in farther ok ========================\n");while(1){// 发送 CAN 帧  间隔3秒写一次if (write(s, &wirte_frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {perror("Write CAN frame failed");close(s);return -1;}sleep(3);memset(wirte_frame.data,++data, wirte_frame.can_dlc); // 设置数据域内容}	}else if(pid==0){printf("in son ok ========================\n");while(1){// 接收 CAN 帧if ((n_read = read(s, &recive_frame, sizeof(struct can_frame)) )!= sizeof(struct can_frame)) {perror("Read CAN frame failed");close(s);return -1;}if(n_read>0){printf("Received CAN ID=%x, DLC=%d\n", recive_frame.can_id, recive_frame.can_dlc);for (int i = 0; i < recive_frame.can_dlc; ++i)printf("Data[%d]: %02x\n", i, recive_frame.data[i]);}}}else{perror("fork error\n");}close(s);return 0 ;
}

4. 应用程序测试

先用交叉编译工具编译:

arm-linux-gnueabi-gcc mycan.c -o mycan

请添加图片描述


远程发送到板子:

scp ./mycan root@192.168.1.101:/root/zhua

请添加图片描述

执行:

请添加图片描述

请添加图片描述

请添加图片描述

适当改一改代码:

请添加图片描述

再测试:

请添加图片描述

这里有个小问题就是write一直写,FIFO文件队列会满,然后就是溢出报错,因此要学习错误处理等操作。

所以最后关于帧格式,位同步,仲裁,错误处理等可以深入了解。推荐在学习stm32的时候学习寄存器配置开发,可以深入的了解到。

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

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

相关文章

【微服务】4、服务保护

微服务架构与组件介绍 单体架构拆分&#xff1a;黑马商城早期为单体架构&#xff0c;后拆分为微服务架构。跨服务调用与组件使用 服务拆分后存在跨服务远程调用&#xff0c;如下单需查询商品信息&#xff0c;使用openfeign组件解决。服务间调用关系复杂&#xff0c;需维护服务…

SQL从入门到实战

学前须知 sqlzoo数据介绍 world nobel covid ge game、goal、eteam teacher、dept movie、casting、actor 基础语句 select&from SELECT from WORLD Tutorial - SQLZoo 基础查询select单列&多列&所有列&别名应用 例题一 SELECT name, continent, population …

QML学习(八) Quick中的基础组件:Item,Rectangle,MouseArea说明及使用场景和使用方法

上一篇中我们从设计器里可以看到Qt Quick-Base中有几大基础组件&#xff0c;如下图&#xff0c;这篇文章先介绍下Item&#xff0c;Rectangle&#xff0c;MouseArea这三个的说明及使用场景和使用方法 Item Item 是 QML 中所有可视元素的基类&#xff0c;是一个非常基础和通用的…

设计模式(1)——面向对象和面向过程,封装、继承和多态

文章目录 一、day11. 什么是面向对象2. 面向对象的三要素&#xff1a;继承、封装和多态2.1 封装**2.1.1 封装的概念****2.1.2 如何实现封装****2.1.3 封装的底层实现**2.1.4 为什么使用封装&#xff1f;&#xff08;好处&#xff09;**2.1.5 封装只有类能做吗&#xff1f;结构体…

Java到底是值传递还是引用传递????

在搞懂这个问题之前, 我们要首先了解什么是值传递, 什么是引用传递? 值传递: 传递的是数据的副本&#xff0c;修改副本不会影响原始数据。引用传递: 传递的是数据的引用&#xff08;地址&#xff09;&#xff0c;修改引用会直接影响原始数据. 也就是说&#xff0c;值传递和引…

VLMs之Agent之CogAgent:《CogAgent: A Visual Language Model for GUI Agents》翻译与解读

VLMs之Agent之CogAgent&#xff1a;《CogAgent: A Visual Language Model for GUI Agents》翻译与解读 导读&#xff1a;这篇论文介绍了CogAgent&#xff0c;一个专注于图形用户界面 (GUI) 理解和导航的视觉语言模型 (VLM)。这篇论文提出了一种新的视觉语言模型 CogAgent&#…

23.行号没有了怎么办 滚动条没有了怎么办 C#例子

新建了一个C#项目&#xff0c;发现行号没有了。 想把行号调出来&#xff0c;打开项目&#xff0c;选择工具>选项> 如下图&#xff0c;在文本编辑器的C#里有一个行号&#xff0c;打开就可以了 滚动条在这里&#xff1a;

Element-plus表单总结

表单包含输入框&#xff0c;单选框&#xff0c;下拉选择&#xff0c;多选框等用户输入的组件。输入表单&#xff0c;您可以收集、验证和提交数据。 经典表单 最基础的表单包括各种输入表单项&#xff0c;比如input、select、radio、checkbox等。 在每一个form组件中&#xff0…

在K8S上部署OceanBase的最佳实践

在K8S上部署OceanBase的最佳实践 目录 1. 背景与选型 1.1 为什么选择OB1.2 为什么选择ob-operator实现OB on K8S 2. 部署实操 2.1 环境准备2.2 安装 ob-operator2.3 配置 OB 集群2.4 配置 OBProxy 集群2.5 Headless Service 和 CoreDNS 配置2.6 监控与运维 2.6.1 Promethues部…

关于FPGA中添加FIR IP核(采用了GOWIN EDA)

文章目录 前言一、IP核二、MATLAB文件三、导出系数COE文件1.设计滤波器2.用官方的matlab代码或者直接用文本文件 四、进行模块化设计源文件 前言 FIR滤波器的特点是其输出信号是输入信号的加权和&#xff0c;权值由滤波器的系数决定。每个系数代表了滤波器在特定延迟位置上的“…

部署:上传项目代码 配置数据库

一、上传代码 1、使用git 可以使用Git Clone。使用前&#xff0c;在服务器上也要创建秘钥对。这里的密钥对&#xff0c;是专门用来读取Git仓库的。 在宝塔上&#xff0c;点击终端。进来后&#xff0c;运行 ssh-keygen还是一路回车&#xff0c;密钥对就建好了。 接着用命令…

【竞技宝】CS2:HLTV2024职业选手排名TOP8-broky

北京时间2025年1月7日,HLTV年度选手排名正在持续公布中,今日凌晨正式公布了今年的TOP8为FAZE战队的broky。 选手简介 broky是一位来自拉脱维亚的职业CS选手,现年23岁。2018年7月,broky获得了FPL资格,连续几季在榜上前5。他的首次赛场留名是跟随拉脱维亚本土战队Wolsung出征BES…

英伟达 RTX 5090 显卡赋能医疗大模型:变革、挑战与展望

一、英伟达 RTX 5090 与 RTX 4090 技术参数对比 1.1 核心架构与制程工艺 在探讨英伟达 RTX 4090 与 RTX 5090 的差异时&#xff0c;核心架构与制程工艺无疑是最为关键的基础要素&#xff0c;它们从根本上决定了两款显卡的性能上限与应用潜力。 1.1.1 核心架构差异 RTX 4090…

【Cesium】自定义材质,添加带有方向的滚动路线

【Cesium】自定义材质&#xff0c;添加带有方向的滚动路线 &#x1f356; 前言&#x1f3b6;一、实现过程✨二、代码展示&#x1f3c0;三、运行结果&#x1f3c6;四、知识点提示 &#x1f356; 前言 【Cesium】自定义材质&#xff0c;添加带有方向的滚动路线 &#x1f3b6;一、…

unity学习12:地图相关的一些基础2, 增加layer种草种树

目录 参考学习 1 地图设置 1.1 上次制作的地图&#xff0c;稍微加点地形完善下. 1.2 调整下camera 1.3 摄像机camera的移动速度 1.4 地图属性&#xff0c;terrain settings 1.5 但是&#xff0c;地图看起来像沙漠一样&#xff0c;很单调 2 paint terrain / paint textu…

游戏引擎学习第77天

仓库: https://gitee.com/mrxiao_com/2d_game 回顾昨天的 bug 今天我们继续开发进度&#xff0c;进行调试昨天代码的问题&#xff0c;主要是关于如何跟踪玩家和敌人在世界中的高度位置。虽然我们做的是一款 2D 游戏&#xff0c;但我们希望能够处理多层的房间&#xff0c;玩家…

【微服务】7、分布式事务

在分布系统中&#xff0c;一个业务由多个服务合作完成&#xff0c;每个服务有自己的事务&#xff0c;多个事务需同时成功或失败&#xff0c;这样的事务称为分布式事务。 其中每个服务的事务叫分支事务&#xff0c;整个业务的统一事务叫全局事务。 分布式事务相关知识讲解 课程引…

【pyqt】(四)Designer布局

布局 之前我们利用鼠标拖动的控件的时候&#xff0c;发现一些部件很难完成对齐这些工作&#xff0c;pyqt为我们提供的多种布局功能不仅可以让排版更加美观&#xff0c;还能够让界面自适应窗口大小的变化&#xff0c;使得布局美观合理。最常使用的三种布局就是垂直河子布局、水…

30天开发操作系统 第 12 天 -- 定时器

前言 定时器(Timer)对于操作系统非常重要。它在原理上却很简单&#xff0c;只是每隔一段时间(比如0.01秒)就发送一个中断信号给CPU。幸亏有了定时器&#xff0c;CPU才不用辛苦地去计量时间。……如果没有定时器会怎么样呢?让我们想象一下吧。 假如CPU看不到定时器而仍想计量时…

大数据-268 实时数仓 - ODS层 将 Kafka 中的维度表写入 DIM

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; Java篇开始了&#xff01; MyBatis 更新完毕目前开始更新 Spring&#xff0c;一起深入浅出&#xff01; 目前已经更新到了&#xff1a; H…