嵌入式开发—CAN通信协议详解与应用(下)

news2024/9/22 21:22:06

书接上回:
嵌入式开发—CAN通信协议详解与应用(中)-CSDN博客

注:本文只是说明了如何进行基础的CAN收发操作,复杂CAN操作可以看这篇文章

Linux 底软开发——对CAN的详细操作(周期发送,异常检测,过滤报文)_linux can 接收过滤-CSDN博客

文章目录

    • 使用命令行工具配置CAN接口
      • 1.查看CAN接口是否存在
      • 2.设置CAN网络接口
      • 3.使用candump监听CAN总线
        • 3.1过滤CAN ID
      • 4. 使用`cansend`发送CAN帧
    • 使用Linux库函数来完成CAN通信的收发
      • 主要步骤
      • CAN数据发送的代码示例
      • 接收CAN帧的代码示例
      • 关键点解析
        • 1. `struct sockaddr_can`
        • 2. `struct ifreq`
        • 3.`struct can_frame`
      • 效果示意:

在Linux系统中,CAN(Controller Area Network)通信可以通过SocketCAN接口进行操作。SocketCAN是Linux内核为CAN总线提供的原生接口,允许使用类似于套接字(socket)的方式与CAN网络进行通信。它支持常用的CAN协议,包括标准帧和扩展帧,并且可以通过命令行工具或编程接口进行操作。

使用命令行工具配置CAN接口

1.查看CAN接口是否存在

CAN同样与网卡一样属于网络设备,因此使用ifconfig命令即可查看状态

ipconfig -a

在这里插入图片描述

2.设置CAN网络接口

(1)使用ip命令设置CAN接口(假设CAN接口名称为can0)的波特率。例如,设置波特率为500kbps:

sudo ip link set can0 type can bitrate 500000

(2)将CAN接口置于UP状态,以启用接口:

sudo ip link set up can0

(3)通过ip link命令可以查看CAN接口的状态:

ip link show can0

示例:

在这里插入图片描述

输出结果表明,can0接口已经启动(UP)并且物理连接正常(LOWER_UP),正在使用CAN协议通信。MTU为16字节,启用了回显功能,队列长度为10,队列调度算法为FIFO。

如果需要关闭CAN接口,可以使用以下命令

sudo ip link set down can0

3.使用candump监听CAN总线

candump属于can-utils是Linux中常用的CAN命令行工具,用于监听和显示CAN总线上的所有数据帧。

监听CAN总线上所有数据帧:

candump can0

该命令会显示从can0接口接收到的所有数据帧。

效果如下:

在这里插入图片描述

3.1过滤CAN ID

不同的candump版本,命令略微不同

使用 candump --help 查看具体的命令提示

root@MADC3.5-A:/app/bin# candump --help
Usage: candump [<can-interface>] [Options]
Options:
 -f, --family=FAMILY	protocol family (default PF_CAN = 29)
 -t, --type=TYPE	socket type, see man 2 socket (default SOCK_RAW = 3)
 -p, --protocol=PROTO	CAN protocol (default CAN_RAW = 1)
     --filter=id:mask[:id:mask]...
			apply filter
 -h, --help		this help
 -o <filename>		output into filename
 -d			daemonize
     --version		print version information and exit
root@MADC3.5-A:/app/bin# 

可以使用candump命令过滤特定的CAN ID,例如,只监听ID为0x123的数据帧:

过滤器的格式为id:mask,其中:

  • id 是你想过滤的CAN帧ID。
  • mask 是掩码,用来匹配ID的哪些位需要进行比较。

当数据帧的ID与指定的id按位与(AND)后与mask的结果相等时,数据帧会被显示。

在这里插入图片描述

4. 使用cansend发送CAN帧

cansend是用于发送CAN帧的工具。发送CAN帧的格式如下:

cansend can0 123#11223344556677

其中:

  • can0 是CAN接口名称。
  • 123 是CAN ID。
  • 11223344556677 是发送的数据,长度不超过8字节。

例如,发送一个ID为0x123,数据为0x11, 0x22, 0x33的CAN帧:

cansend can0 123#112233

注:有些cansend的版本并不支持这种发送命令,具体看CAN分析软件是否正常接受数据。

使用cansend --help查看具体命令格式

root@MADC3.5-A:/app/bin# cansend --help
Usage: cansend [<can-interface>] [Options] <can-msg>
<can-msg> can consist of up to 8 bytes given as a space separated list
Options:
 -i, --identifier=ID	CAN Identifier (default = 1)
 -r  --rtr		send remote request
 -e  --extended	send extended frame
 -f, --family=FAMILY	Protocol family (default PF_CAN = 29)
 -t, --type=TYPE	Socket type, see man 2 socket (default SOCK_RAW = 3)
 -p, --protocol=PROTO	CAN protocol (default CAN_RAW = 1)
 -l			send message infinite times
     --loop=COUNT	send message COUNT times
 -v, --verbose		be verbose
 -b, --brs		bit rate switch
 -h, --help		this help
     --version		print version information and exit

根据cansend工具的帮助信息,<can-msg> 部分需要以空格分隔的字节列表形式发送。也就是说,数据部分不再使用#符号连接,而是使用空格分隔每个字节。

具体用法如下

cansend can0 -i 0x321 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 --loop=10

在这里插入图片描述

此时数据显示正常,如果数据与预期不一致,请检查cansend命令支持的形式

使用Linux库函数来完成CAN通信的收发

在Linux系统编程中,发送和接收CAN数据可以通过使用原生的socket API与PF_CAN协议族来实现。

主要步骤

  1. 创建并配置CAN套接字。
  2. 绑定到特定的CAN接口(如can0)。
  3. 发送和接收CAN帧。

CAN数据发送的代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>

int main() {
    int s;  // 套接字
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame;  // CAN帧结构

    // 创建套接字
    s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (s < 0) {
        perror("socket");
        return 1;
    }

    // 指定CAN接口,例如can0
    strcpy(ifr.ifr_name, "can0");
    ioctl(s, SIOCGIFINDEX, &ifr);  // 获取接口索引

    // 绑定套接字到CAN接口
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        return 1;
    }

    // 准备要发送的CAN帧
    frame.can_id = 0x321;  // CAN ID
    frame.can_dlc = 8;     // 数据长度为8字节
    frame.data[0] = 0x11;
    frame.data[1] = 0x22;
    frame.data[2] = 0x33;
    frame.data[3] = 0x44;
    frame.data[4] = 0x55;
    frame.data[5] = 0x66;
    frame.data[6] = 0x77;
    frame.data[7] = 0x88;

    // 发送CAN帧
    if (write(s, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {
        perror("write");
        return 1;
    }

    printf("CAN frame sent\n");

    // 关闭套接字
    close(s);

    return 0;
}

接收CAN帧的代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>

int main() {
    int s;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame;

    // 创建套接字
    s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (s < 0) {
        perror("socket");
        return 1;
    }

    // 指定CAN接口,例如can0
    strcpy(ifr.ifr_name, "can0");
    ioctl(s, SIOCGIFINDEX, &ifr);  // 获取接口索引

    // 绑定套接字到CAN接口
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        return 1;
    }

    // 接收CAN帧
    while (1) {
        int nbytes = read(s, &frame, sizeof(struct can_frame));
        if (nbytes < 0) {
            perror("read");
            return 1;
        }

        // 打印CAN帧
        printf("Received CAN frame with ID: 0x%X, DLC: %d\n", frame.can_id, frame.can_dlc);
        printf("Data: ");
        for (int i = 0; i < frame.can_dlc; i++) {
            printf("%02X ", frame.data[i]);
        }
        printf("\n");
    }

    // 关闭套接字
    close(s);

    return 0;
}

编译命令:

gcc -o can_send can_send.c
gcc -o can_receive can_receive.c

关键点解析

在Linux中进行CAN(Controller Area Network)通信时,使用了以下三个重要的结构体:struct sockaddr_canstruct ifreqstruct can_frame。它们分别用于套接字地址、网络接口配置、以及CAN帧的处理。下面是对这三个结构体的详细解析。

  1. struct sockaddr_can 用于将套接字绑定到特定的CAN接口,包含地址族和接口索引。
  2. struct ifreq 用于配置和操作网络接口属性,最常见的是用于获取CAN接口的索引。
  3. struct can_frame 用于表示CAN协议中的数据帧,包含CAN ID、数据长度和实际的CAN数据。
1. struct sockaddr_can

这个结构体用于表示CAN套接字的地址,它是AF_CAN协议族(Linux中的CAN协议)的地址结构。在套接字绑定到CAN接口时,它会使用此结构体。

定义

在头文件 <linux/can.h> 中定义:

struct sockaddr_can {
    sa_family_t can_family;   // 地址族,必须是AF_CAN
    int can_ifindex;          // 网络接口索引(类似can0, can1)
    union {
        struct { canid_t rx_id, tx_id; } tp;
    };
};

成员解释

  • can_family:指定地址的协议族。在CAN通信中,can_family 设置为 AF_CAN,用于表示CAN协议。
  • can_ifindex:表示与CAN相关的网络接口索引。它通过 ioctl 获取,常见的接口有can0can1等。使用 SIOCGIFINDEX 获取索引。
  • tp(可选):仅在某些高级传输层协议(如ISO-TP)中使用。一般CAN通信不需要使用这个字段。

示例

struct sockaddr_can addr;
addr.can_family = AF_CAN;          // 使用CAN协议
addr.can_ifindex = ifr.ifr_ifindex; // 绑定到can0或can1接口
2. struct ifreq

struct ifreq 主要用于配置网络接口属性,例如获取网络接口的索引、设置设备的参数等。在CAN通信中,它通常用于获取指定接口(如can0)的索引,以便与CAN设备关联。

定义

在头文件 <net/if.h> 中定义:

struct ifreq {
    char ifr_name[IFNAMSIZ];   // 接口名称,例如 "can0"
    union {
        struct sockaddr ifr_addr;   // 用于套接字地址的各种配置
        struct sockaddr ifr_dstaddr;
        struct sockaddr ifr_broadaddr;
        struct sockaddr ifr_netmask;
        short ifr_flags;           // 接口标志,例如 IFF_UP
        int ifr_ifindex;           // 接口索引
        int ifr_metric;
        int ifr_mtu;               // 最大传输单元
        struct ifmap ifr_map;
        char ifr_slave[IFNAMSIZ];
        char ifr_newname[IFNAMSIZ];
        char *ifr_data;
    };
};

成员解释

  • ifr_name:表示网络接口的名称,例如 "can0""eth0"。这是一个字符串数组,定义接口的名称。
  • ifr_ifindex:存储网络接口的索引。通过调用 ioctl 并使用 SIOCGIFINDEX 命令可以获取接口的索引。
  • 其它字段(如ifr_flags)可以用于设置和获取接口状态,但在基本的CAN通信中不常用。

示例

struct ifreq ifr;
strcpy(ifr.ifr_name, "can0");         // 指定接口为 can0
ioctl(s, SIOCGIFINDEX, &ifr);         // 获取接口索引并存入 ifr.ifr_ifindex
3.struct can_frame

这是CAN帧的结构体,表示CAN网络上传输的数据帧。每个CAN帧包含一个标识符(CAN ID)、数据长度(DLC),以及最多8个字节的实际数据。

定义

在头文件 <linux/can.h> 中定义:

struct can_frame {
    canid_t can_id;  // 32 位 CAN ID (11 或 29 位有效位), 包含标志位
    __u8    can_dlc; // 数据长度码 (0..8)
    __u8    __pad;   // 填充
    __u8    __res0;  // 保留
    __u8    __res1;  // 保留
    __u8    data[8]; // 数据字段 (最多8字节)
};

成员解释

  • can_id:CAN帧的标识符。根据CAN协议,这个字段有11位标准ID或29位扩展ID,CAN ID可以包括额外的标志位,如远程传输请求 (RTR) 和错误标志 (ERR):

    • CAN_EFF_FLAG:表示该帧使用29位扩展ID。
    • CAN_RTR_FLAG:表示远程传输请求帧 (remote transmission request)。
    • CAN_ERR_FLAG:表示错误帧。
  • can_dlc:数据长度码,表示数据字段data中实际传输的字节数。DLC的值范围是0到8,CAN帧最多携带8字节的数据。

  • data:一个字节数组,用于存储实际传输的数据,最多可以容纳8个字节。

示例

struct can_frame frame;
frame.can_id = 0x321;     // 设置标准CAN ID
frame.can_dlc = 8;        // 数据长度为8字节
frame.data[0] = 0x11;     // 数据
frame.data[1] = 0x22;
frame.data[2] = 0x33;
frame.data[3] = 0x44;
frame.data[4] = 0x55;
frame.data[5] = 0x66;
frame.data[6] = 0x77;
frame.data[7] = 0x88;

效果示意:

发送0x321,0x111数据

在这里插入图片描述

接收数据

在这里插入图片描述

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

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

相关文章

VMware ESXi 8.0U3b 发布下载,新增功能概览

VMware ESXi 8.0U3b 发布下载&#xff0c;新增功能概览 同步发布 Dell (戴尔)、HPE (慧与)、Lenovo (联想)、Inspur (浪潮)、Cisco (思科)、Hitachi (日立)、Fujitsu (富士通)、NEC (日电) 定制版 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-8-u3/&#…

3分钟了解 跨网文件安全交换的最佳方案是什么

跨网文件交换的背景介绍 跨网文件安全交换是指在不同网络环境下&#xff08;如不同的组织、企业、机构或国家&#xff09;进行文件传输时&#xff0c;确保文件的安全性、完整性和保密性。随着信息技术的发展和网络的广泛应用&#xff0c;跨网文件交换变得越来越普遍&#xff0…

QQ频道机器人零基础开发详解(基于QQ官方机器人文档)[第五期]

QQ频道机器人零基础开发详解(基于QQ官方机器人文档)[第五期] 第五期介绍&#xff1a;频道模块之接口授权管理和发言管理 目录 QQ频道机器人零基础开发详解(基于QQ官方机器人文档)[第五期]第五期介绍&#xff1a;频道模块之接口授权管理和发言管理获取机器人在频道可用权限列表…

电磁场与电磁波 1.1矢量基础知识

1.标量 2.矢量 大小方向 用有方向的线段表示 在直角坐标系下 3.运算法则 (1)加法&#xff1a;平行四边形法则 (2)乘法&#xff1a;点乘&叉乘 点乘 两个向量a与b的内积为 ab |a||b|cos∠(a, b)&#xff0c;特别地&#xff0c;0a a0 0&#xff1b;若…

Kettle的安装与基本使用

什么是Kettle&#xff1f; Kettle最早是一个开源的ETL&#xff08;Extract-Transform-Load的缩写&#xff09;工具&#xff0c;全称为KDE Extraction, Transportation, Transformation and Loading Environment。是一个功能丰富的ETL工具&#xff0c;它允许用户轻松地进行数据抽…

老板电器让AI融入烹饪?老板数字厨电实力展示AI做饭!

AI技术的迅猛发展已经深刻地改变了众多行业的面貌&#xff0c;其影响力远远超出了计算机科学本身的范畴。在追求生产效率与自动化的道路上&#xff0c;AI无疑成为最强大的驱动力之一&#xff0c;正是在这样的背景下&#xff0c;老板电器发布的「食神」AI大模型也为老板数字厨电…

国外创意二维码应用案例:韩国Cheil特别制作“希望胶带”,帮助寻找失踪儿童!

每年&#xff0c;在全世界都有大量的儿童失踪案件发生。对于父母来说&#xff0c;仅凭一张照片、一张海报要在茫茫人海里找到失踪的孩子&#xff0c;何其艰难&#xff1f; 2020年5月&#xff0c;韩国广告公司Cheil与韩国国家警察局宣布&#xff1a;为寻找长期失踪儿童&#xf…

VBA编写代码导出和导入类、模块、窗体

功能描述 为了方便备份代码&#xff0c;运行一个函数&#xff0c;导出类、模块、窗体。当在其他vba工程使用时&#xff0c;可以运行另外一个函数&#xff0c;快速导入代码。 1、开发人员宏设置&#xff1a;信任对VBA工程对象模型的访问 2、引用 Microsoft Visual Basic for A…

【非常实用—Navicat重置 MySQL 的密码】

Navicat重置 MySQL 的密码 连接本地数据库&#xff0c;忘记原始密码停止 MySQL 服务以安全模式启动 MySQL打开新的命令行窗口重置密码停止 MySQL 并重启 连接本地数据库&#xff0c;忘记原始密码 停止 MySQL 服务 在命令行中使用以下命令停止服务&#xff08;Windows 下&#…

2024秋季学期期中考

密码 mmmd5 直接在网上找一个md5解密的网站现场来解就可以了。每一段都拼接起来就可以了。 但是的话&#xff0c;也可以直接用脚本&#xff0c;但是要用到MD5的字典。 它给了一个附件&#xff0c;可以借助此来解答。 那么我们主要来学习一下hash函数&#xff0c; 哈希函数&…

15.10 在k8s部署grafana-deployment并导入k8s大盘

本节重点介绍 : grafana deployment部署k8s大盘导入 准备yaml 部署工作 1. 修改yaml中的节点选择器标签 k8s-node01改为你自己的节点 2. 在节点上创建数据目录 mkdir -pv /data/grafana3. 部署grafana # 部署 kubectl apply -f deployment.yaml # 检查 [rootprome-mast…

CMU 10423 Generative AI:lec7、8、9(专题1:AE、VAE详解)

文章目录 1 AE&#xff08;自编码器&#xff09;1.1 自编码器干什么用的1.2 自编码器的架构图、输入输出、训练方法1.3 常见应用1.4 代码示例&#xff1a;图片的压缩存储和复原 2 VAE&#xff08;变分自编码器&#xff09;2.1 概述2.2 AE存在的问题&#xff1a;隐空间不平滑&am…

ad 明明已经给原理图库添加封装了,为什么还是看不到

不得不说&#xff0c;ad可能真的是比较庞大也不比较冗余&#xff0c;使用的过程中就会出现各种各样的问题&#xff0c;明明所有的操作都没问题&#xff0c;可是就是没能得到自己想要的&#xff0c;额 真的一言难尽 软件本身也很庞大了 各种东西可能一下子反应不过来&#xf…

谁的大一不迷茫?网络安全到底该怎么入门?

大一的新生&#xff0c;都在向我询问类似的问题&#xff1a; 网络安全要学的东西好杂&#xff0c;找不到方向了&#xff0c;迷茫了 同学们都在研究xxx&#xff0c;我要去学吗&#xff1f; 精力有限&#xff0c;我到底该学什么编程语言&#xff0c;好怕选错啊 最近下班比较早…

指针 (六)

OK&#xff0c;书接上回&#xff0c;咱们继续&#xff1a; 一 . 函数指针变量 &#xff08;1&#xff09;函数指针变量的创建 首先我们得明白&#xff0c;什么是函数指针变量呢&#xff1f;从我们之前学习过的整型指针&#xff0c;数组指针的相关知识当中&#xff0c;通过类…

【软件测试】什么叫测试?

生活中的测试 测试在生活中是处处可见的 例如&#xff1a;在商场购买衣服 外观测试&#xff1a;挑选符合个人审美的衣服试穿测试&#xff1a;选择尺码&#xff0c;试穿材质测试&#xff1a;考虑材质相关问题价格测试&#xff1a;价格是否符合预期 例如&#xff1a;在购物软件购…

【逐行注释】自适应观测协方差R的AUKF(自适应无迹卡尔曼滤波,MATLAB语言编写),附下载链接

文章目录 自适应R的UKF逐行注释的说明运行结果部分代码各模块解释 自适应R的UKF 自适应无迹卡尔曼滤波&#xff08;Adaptive Unscented Kalman Filter&#xff0c;AUKF&#xff09;是一种用于状态估计的滤波算法。它是基于无迹卡尔曼滤波&#xff08;Unscented Kalman Filter&…

简单题58-最后一个单词的长度(Java and Python)20240918

问题的描述&#xff1a; python代码&#xff1a; class Solution(object):def lengthOfLastWord(self, s):""":type s: str:rtype: int"""# 去掉字符串前后的空格s s.strip()# 分割字符串&#xff0c;获取单词列表words s.split(" "…

9.1 溪降技术:游泳

目录 9.1 游泳概述观看视频课程电子书&#xff1a;游泳防御性游泳姿势**身体姿势** 积极游泳姿势**身体姿势** 总结 9.1 游泳 概述 深潭游泳 对于峡谷探险者来说&#xff0c;游泳是一项核心技能。我们的游泳水平和自信心将在很大程度上决定我们的路线选择。在这一阶段&#xff…

开放式耳机哪个好用?开放式耳机好还是入耳式耳机好?

开放式耳机这种新型的耳机类型最近几年&#xff0c;真的受到了越来越多人的欢迎了&#xff0c;所以很多人来问我如何才能选到一款合适自己的开放式耳机&#xff0c;毕竟一款合适自己的耳机才能够用的长久&#xff0c;作为一名合格的耳机测评师&#xff0c;在这里当然要为大家答…