Linux 系统中CAN 接口配置

在 Linux 系统中, CAN 总线接口设备作为网络设备被系统进行统一管理。在控制台下, CAN 总线的配置和以太网的配置使用相同的命令。

在控制台上输入命令:

ifconfig –a

可以得到以下结果:

在上面的结果中, eth0 设备为以太网接口, can0和can1 设备为两个 CAN 总线接口。接下来使用 ip 命令来配置 CAN 总线的位速率:

ip link set can0 type cantq 125 prop-seg 6phase-seg1 7 phase-seg2 2 sjw 1

也可以使用 ip 命令直接设定位速率:

ip link set can0 type can bitrate 125000

当设置完成后,可以通过下面的命令查询 can0 设备的参数设置:

ip -details link show can0

当设置完成后,可以使用下面的命令使能 can0 设备:

ifconfig can0 up

使用下面的命令取消 can0 设备使能:

ifconfig can0 down

在设备工作中,可以使用下面的命令来查询工作状态:

ip -details -statistics link show can0

Linux 系统中CAN 接口应用程序开发

由于系统将 CAN 设备作为网络设备进行管理,因此在 CAN 总线应用开发方面, Linux 提供了SocketCAN 接口,使得 CAN 总线通信近似于和以太网的通信,应用程序开发接口 更加通用, 也更加灵活。

此外,通过 https://gitorious.org/linux-can/can-utils 网站发布的基于 SocketCAN 的 can-utils 工具套件, 也可以实现简易的 CAN 总线通信。

下面具体介绍使用 SocketCAN 实现通信时使用的应用程序开发接口。

(1). 初始化

SocketCAN 中大部分的数据结构和函数在头文件 linux/can.h 中进行了定义。 CAN 总线套接字的创建采用标准的网络套接字操作来完成。网络套接字在头文件 sys/socket.h 中定义。 套接字的初始化方法如下:

1

int s;

2

struct sockaddr_can addr;

3

struct ifreq ifr;

4

s = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建 SocketCAN 套接字

5

strcpy(ifr.ifr_name, "can0" );

6

ioctl(s, SIOCGIFINDEX, &ifr);//指定 can0 设备

7

addr.can_family = AF_CAN;

8

addr.can_ifindex = ifr.ifr_ifindex;

9

bind(s, (struct sockaddr *)&addr, sizeof(addr)); //将套接字与 can0 绑定

(2). 数据发送

在数据收发的内容方面, CAN 总线与标准套接字通信稍有不同,每一次通信都采用 can_ frame 结构体将数据封装成帧。 结构体定义如下:

1

struct can_frame {

2

canid_t can_id;//CAN 标识符

3

__u8 can_dlc;//数据场的长度

4

__u8 data[8];//数据

5

};

can_id 为帧的标识符, 如果发出的是标准帧, 就使用 can_id 的低 11 位; 如果为扩展帧, 就使用 0~ 28 位。 can_id 的第 29、 30、 31 位是帧的标志位,用来定义帧的类型,定义如下:

1

#define CAN_EFF_FLAG 0x80000000U //扩展帧的标识

2

#define CAN_RTR_FLAG 0x40000000U //远程帧的标识

3

#define CAN_ERR_FLAG 0x20000000U //错误帧的标识,用于错误检查

数据发送使用 write 函数来实现。 如果发送的数据帧(标识符为 0x123)包含单个字节(0xAB)的数据,可采用如下方法进行发送:

1

struct can_frame frame;

2

frame.can_id = 0x123;//如果为扩展帧,那么 frame.can_id = CAN_EFF_FLAG | 0x123;

3

frame.can_dlc = 1; //数据长度为 1

4

frame.data[0] = 0xAB; //数据内容为 0xAB

5

int nbytes = write(s, &frame, sizeof(frame)); //发送数据

6

if(nbytes != sizeof(frame)) //如果 nbytes 不等于帧长度,就说明发送失败

7

printf("Error\n!");

如果要发送远程帧(标识符为 0x123),可采用如下方法进行发送:

1

struct can_frame frame;

2

frame.can_id = CAN_RTR_FLAG | 0x123;

3

write(s, &frame, sizeof(frame));

(3). 数据接收

数据接收使用 read 函数来完成,实现如下:

1

struct can_frame frame;

2

int nbytes = read(s, &frame, sizeof(frame));

当然, 套接字数据收发时常用的 send、 sendto、 sendmsg 以及对应的 recv 函数也都可以用于 CAN总线数据的收发。

(4). 错误处理

当帧接收后,可以通过判断 can_id 中的 CAN_ERR_FLAG 位来判断接收的帧是否为错误帧。 如果为错误帧,可以通过 can_id 的其他符号位来判断错误的具体原因。

错误帧的符号位在头文件 linux/can/error.h 中定义。

(5). 过滤规则设置

在数据接收时,系统可以根据预先设置的过滤规则,实现对报文的过滤。过滤规则使用 can_filter 结构体来实现,定义如下:

1

struct can_filter {

2

canid_t can_id;

3

canid_t can_mask;

4

};

过滤的规则为:

接收到的数据帧的 can_id  & mask == can_id & mask

通过这条规则可以在系统中过滤掉所有不符合规则的报文,使得应用程序不需要对无关的报文进行处理。在 can_filter 结构的 can_id 中,符号位 CAN_INV_FILTER 在置位时可以实现 can_id 在执行过滤前的位反转。

用户可以为每个打开的套接字设置多条独立的过滤规则,使用方法如下:

1

struct can_filter rfilter[2];

2

rfilter[0].can_id = 0x123;

3

rfilter[0].can_mask = CAN_SFF_MASK; //#define CAN_SFF_MASK 0x000007FFU

4

rfilter[1].can_id = 0x200;

5

rfilter[1].can_mask = 0x700;

6

setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));//设置规则

在极端情况下,如果应用程序不需要接收报文,可以禁用过滤规则。这样的话,原始套接字就会忽略所有接收到的报文。在这种仅仅发送数据的应用中,可以在内核中省略接收队列,以此减少 CPU 资源的消耗。禁用方法如下:

1

setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); //禁用过滤规则

通过错误掩码可以实现对错误帧的过滤, 例如:

1

can_err_mask_t err_mask = ( CAN_ERR_TX_TIMEOUT | CAN_ERR_BUSOFF );

2

setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, err_mask, sizeof(err_mask));

(6). 回环功能设置

在默认情况下, 本地回环功能是开启的,可以使用下面的方法关闭回环/开启功能:

1

int loopback = 0; // 0 表示关闭, 1 表示开启( 默认)

2

setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));

在本地回环功能开启的情况下,所有的发送帧都会被回环到与 CAN 总线接口对应的套接字上。 默认情况下,发送 CAN 报文的套接字不想接收自己发送的报文,因此发送套接字上的回环功能是关闭的。可以在需要的时候改变这一默认行为:

1

int ro = 1; // 0 表示关闭( 默认), 1 表示开启

2

setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &ro, sizeof(ro));

Linux 系统中CAN 接口应用程序示例

该文档提供了一个很简单的程序示例,如下:

1. 报文发送程序

01

/* 1. 报文发送程序 */

02

#include

03

#include

04

#include

05

#include

06

#include

07

#include

08

#include

09

#include

10

#include

11

12

int main()

13

{

14

int s, nbytes;

15

struct sockaddr_can addr;

16

struct ifreq ifr;

17

struct can_frame frame[2] = {{0}};

18

s = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字

19

strcpy(ifr.ifr_name, "can0" );

20

ioctl(s, SIOCGIFINDEX, &ifr); //指定 can0 设备

21

addr.can_family = AF_CAN;

22

addr.can_ifindex = ifr.ifr_ifindex;

23

bind(s, (struct sockaddr *)&addr, sizeof(addr));//将套接字与 can0 绑定

24

//禁用过滤规则,本进程不接收报文,只负责发送

25

setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);

26

//生成两个报文

27

frame[0].can_id = 0x11;

28

frame[0]. can_dlc = 1;

29

frame[0].data[0] = 'Y';

30

frame[0].can_id = 0x22;

31

frame[0]. can_dlc = 1;

32

frame[0].data[0] = 'N';

33

//循环发送两个报文

34

while(1)

35

{

36

nbytes = write(s, &frame[0], sizeof(frame[0])); //发送 frame[0]

37

if(nbytes != sizeof(frame[0]))

38

{

39

printf("Send Error frame[0]\n!");

40

break; //发送错误,退出

41

}

42

sleep(1);

43

nbytes = write(s, &frame[1], sizeof(frame[1])); //发送 frame[1]

44

if(nbytes != sizeof(frame[0]))

45

{

46

printf("Send Error frame[1]\n!");

47

break;

48

}

49

sleep(1);

50

}

51

close(s);

52

return 0;

53

}

2. 报文过滤接收程序

01

/* 2. 报文过滤接收程序 */

02

#include

03

#include

04

#include

05

#include

06

#include

07

#include

08

#include

09

#include

10

#include

11

12

int main()

13

{

14

int s, nbytes;

15

struct sockaddr_can addr;

16

struct ifreq ifr;

17

struct can_frame frame;

18

struct can_filter rfilter[1];

19

s = socket(PF_CAN, SOCK_RAW, CAN_RAW); //创建套接字

20

strcpy(ifr.ifr_name, "can0" );

21

ioctl(s, SIOCGIFINDEX, &ifr); //指定 can0 设备

22

addr.can_family = AF_CAN;

23

addr.can_ifindex = ifr.ifr_ifindex;

24

bind(s, (struct sockaddr *)&addr, sizeof(addr)); //将套接字与 can0 绑定

25

//定义接收规则,只接收表示符等于 0x11 的报文

26

rfilter[0].can_id = 0x11;

27

rfilter[0].can_mask = CAN_SFF_MASK;

28

//设置过滤规则

29

setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));

30

while(1)

31

{

32

nbytes = read(s, &frame, sizeof(frame)); //接收报文

33

//显示报文

34

if(nbytes > 0)

35

{

36

printf(“ID=0x%X DLC=%d data[0]=0x%X\n”, frame.can_id,

37

frame.can_dlc, frame.data[0]);

38

}

39

}

40

close(s);

41

return 0;

42

}

这个示例程序博主并未编译测试验证

linux can命令详解,Linux CAN编程详解相关推荐

  1. linux每日命令(26):Linux文件属性详解

    Linux 文件或目录的属性主要包括:文件或目录的节点.种类.权限模式.链接数量.所归属的用户和用户组.最近访问或修改的时间等内容.具体情况如下: 命令: ls -lih 输出: [root@loca ...

  2. linux cp命令 前面,盘点Linux命令之Linux cp命令使用大全

    Linux命令有很多,其中Linux cp命令常用的一种,Linux cp命令如何使用,下面IT培训网将为您盘点有关Linux命令之Linux cp命令大全. Linux命令之Linux cp命令详解 ...

  3. linux用户命令快捷链接,linux简单命令

    linux系统命令是最基础也是最重要的部分,下面由学习啦小编为大家整理了linux简单命令的相关知识,希望对大家有帮助! linux简单命令一.文件和目录 家目录为 /home/user 或者 - 目 ...

  4. linux每日命令(30):Linux 用户及用户组相关文件、命令详解

    阅读目录(Content) 一. 用户.用户组概念及其文件结构详解 二. 常用的用户.用户组shell命令 用户相关命令 useradd userdel usermod passwd 用户组相关命令 ...

  5. linux rz命令的全称,linux rz命令详解

    Linux中rz命令和sz命令都可用于文件传输,而rz命令主要用于文件的上传,下面由学习啦小编为大家整理了linux rz命令的相关知识,希望大家喜欢! linux rz命令 说明 rz命令可以批量上 ...

  6. linux top命令看磁盘,linux top命令详解

    linux top命令详解 下面详细介绍它的使用方法. top - 01:06:48 up 1:22, 1 user, load average: 0.06, 0.60, 0.48 Tasks: 29 ...

  7. linux tar命令解析(未完成)(压缩解压)(tar指令)(十分之坑,千万不要在windows上解压带有软链接的压缩文件,会把软链接搞没了!软链接丢失、软链接失效)

    文章目录 示例 man 1 tar 示例 注意 20220926 如何打包指定目录的文件 20230129 用解压tgz的命令解压tar还解压不了... 20230328 自己注意啊,好几次把压缩.t ...

  8. linux帮助命令和用法,Linux命令帮助及history命令的使用

    1.Linux命令帮助的获取详解 在Linux中获取命令帮助时,内部命令和外部命令的获取方式是有区别的: 即 (1)内部命令:#help COMMAND ?            #man bash ...

  9. linux su命令在哪里,Linux su命令

    本人以前一直习惯直接使用root,很少使用su,前几天才发现su与su -命令是有着本质区别的! 大部分Linux发行版的默认账户是普通用户,而更改系统文件或者执行某些命令,需要root身份才能进行, ...

  10. 2022非常全的软件测试linux常用命令全集,linux面试题及参考答案

    一.前言: 作为一名软件测试工程师,我相信大部分的人都和Linux打过交道,因为我们的服务器一般都是装的Linux操作系统,包括各种云服务器也都是用的Linux,目前主流是CentOS7,那么对于一个 ...

最新文章

  1. TCP和UDP 粘包 消息保护边界
  2. 如何用python画出中国地图-用Python画一个中国地图
  3. 2014 北京邀请赛ABDHJ题解
  4. 文献学习(part34)
  5. Patrick Wyatt:代码没问题 程序却有bug?
  6. 小程序开发(10)-之热力图解决方案、手绘图
  7. linux 智联 网卡设置,Linux初学者DNS配置指南(四)配置Bind常见问题
  8. NOIP2015普及组第1题 45 金币 方法三(python3实现)
  9. Windows 8 下使用 ScrollViewer 替代 GridView
  10. 前端小报 - 201812 月刊
  11. 查看 mysql端口 和进程_mysql 端口号(怎么查看mysql的端口号)
  12. Android 官方独立 adb / fastboot 工具包
  13. CTF|逆向工程软件之IDA
  14. linux版本qq的安装
  15. HTML如何实现简单登录页面
  16. win10官方iso下载
  17. 白盒测试方法的简单理解(通俗易懂)
  18. 教你从零做起谷歌Adsense。开户,过审核,过pin码达到稳定收益
  19. 一个工作了三年的社会人士与即将毕业的研究生对话:应届生什么时候准备找工作,如何准备?
  20. 7-66 华氏温度转换为摄氏温度

热门文章

  1. 小红书KOC和KOL这两者怎样进行投放?
  2. linux 文件末尾追加内容
  3. 小程序发送 request请求失败 提示不在合法域名列表中的解决方法
  4. 京东股权众筹投后总结和反思
  5. IDEA 配置 JDK 源码
  6. 【日记】 使用 zip4j 实现压缩包加密
  7. JSON和list之间的转换
  8. mysql 集群搭建(Centos7) for Galera
  9. 计算机专业的高级称呼,软考高级和中级全称~~
  10. 微信小程序开发 - 视图与逻辑