nrf24l01收发程序详解_LinuxCAN编程详解
为了在TX2上对CAN使⽤,收集了各种资料,希望对我能有帮助,⾃⼰实在不会的地⽅太多了;加强。
《Linux CAN编程详解》是⼀篇百度⽂库上的⽂档,主要描述了以下内容:
1. can总线介绍及其帧类型;
2. Linux 系统中CAN 接⼝配置;mide008
3. Linux 系统中CAN 接⼝应⽤程序开发;
4. Linux 系统中CAN 接⼝编程实例
5. 总体来说,这篇⽂档,对于嵌⼊式linux can应⽤编程还是有很⼤的帮助。特别是⾥⾯关于“Linux 系统中CAN 接⼝应⽤程序开
发”的介绍,总结的很全⾯,讲述的⽐较清楚。
本⼈编写的linux socket can程序cantool(⼀个基于linux socket can机制编写的can接⼝应⽤程序),在调试CAN帧发送功能的时候,就有参考过该⽂档“5. 过滤规则设置”⼀节内容。 下⾯是该⽂档中个⼈认为⽐较有价值的内容部分。完整PDF⽂档下载地址:Linux CAN编程详解
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 总线通信近似于和以太⽹的通信,应⽤程序开发接⼝ 更加通⽤, 也更加灵活。
下⾯具体介绍使⽤ SocketCAN 实现通信时使⽤的应⽤程序开发接⼝。
(1). 初始化
SocketCAN 中⼤部分的数据结构和函数在头⽂件 linux/can.h 中进⾏了定义。 CAN 总线套接字的创建采⽤标准的⽹络套接字操作来完成。⽹络套接字在头⽂件 sys/socket.h 中定义。 套接字的初始化⽅法如下:
智能电表系统1int s;2struct sockaddr_can addr;3struct ifreq ifr;4s = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建 SocketCAN 套接
字5strcpy(ifr.ifr_name,"can0");6ioctl(s, SIOCGIFINDEX, &ifr);//指定 can0 设备7addr.can_family = AF_CAN;8addr.can_ifindex =
ifr.ifr_ifindex;9bind(s, (struct sockaddr *)&addr,sizeof(addr));//将套接字与 can0 绑定
(2). 数据发送
在数据收发的内容⽅⾯, CAN 总线与标准套接字通信稍有不同,每⼀次通信都采⽤ can_ frame 结构体将数据封装成帧。 结构体定义如下:
1struct can_frame {2canid_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)的数据,可采⽤如下⽅法进⾏发送:
1struct can_frame frame;2frame.can_id = 0x123;//如果为扩展帧,那么 frame.can_id = CAN_EFF_FLAG | 0x123;3frame.can_dlc = 1;//数据长度为 14frame.data[0] = 0xAB;//数据内容为 0xAB5int nbytes = write(s, &frame,sizeof(frame));//发送数据6if(nbytes !=sizeof(frame))//如果nbytes 不等于帧长度,就说明发送失败7printf("Errorn!");
如果要发送远程帧(标识符为 0x123),可采⽤如下⽅法进⾏发送:
1struct can_frame frame;2frame.can_id = CAN_RTR_FLAG | 0x123;3write(s, &frame,sizeof(frame));
数据接收使⽤ read 函数来完成,实现如下:
1struct can_frame frame;2int 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 结构体来实现,定义如下:
1struct can_filter {2canid_t can_id;3canid_t can_mask;4};
过滤的规则为:
接收到的数据帧的 can_id & mask == can_id & mask
通过这条规则可以在系统中过滤掉所有不符合规则的报⽂,使得应⽤程序不需要对⽆关的报⽂进⾏处理。在 can_filter 结构的 can_id 中,符号位 CAN_INV_FILTER 在置位时可以实现 can_id 在执⾏过滤前的位反转。
⽤户可以为每个打开的套接字设置多条独⽴的过滤规则,使⽤⽅法如下:
1struct can_filter rfilter[2];2rfilter[0].can_id = 0x123;3rfilter[0].can_mask = CAN_SFF_MASK;//#define CAN_SFF_MASK
0x000007FFU4rfilter[1].can_id = 0x200;5rfilter[1].can_mask = 0x700;6setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));//设置规则
在极端情况下,如果应⽤程序不需要接收报⽂,可以禁⽤过滤规则。这样的话,原始套接字就会忽略所有接收到的报⽂。在这种仅仅发送数据的应⽤中,可以在内核中省略接收队列,以此减少 CPU 资源的消耗。禁⽤⽅法如下:
自动润滑系统1setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);//禁⽤过滤规则
通过错误掩码可以实现对错误帧的过滤, 例如:
1can_err_mask_t err_mask = ( CAN_ERR_TX_TIMEOUT | CAN_ERR_BUSOFF );2setsockopt(s, SOL_CAN_RAW,
CAN_RAW_ERR_FILTER, err_mask,sizeof(err_mask));
(6). 回环功能设置
在默认情况下, 本地回环功能是开启的,可以使⽤下⾯的⽅法关闭回环/开启功能:
1int loopback = 0;// 0 表⽰关闭, 1 表⽰开启( 默认)2setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback,
sizeof(loopback));
在本地回环功能开启的情况下,所有的发送帧都会被回环到与 CAN 总线接⼝对应的套接字上。 默认情况下,发送 CAN 报⽂的套接字不想接收⾃⼰发送的报⽂,因此发送套接字上的回环功能是关闭的。可以在需要的时候改变这⼀默认⾏为:
铣刀头装配图
1int ro = 1;// 0 表⽰关闭( 默认), 1 表⽰开启2setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &ro,sizeof(ro));
Linux 系统中CAN 接⼝应⽤程序⽰例
悬空板该⽂档提供了⼀个很简单的程序⽰例,如下:
1. 报⽂发送程序
01/* 1. 报⽂发送程序 */02#include <stdio.h>03#include <stdlib.h>04#include <string.h>05#include <unistd.h>06#include
<net/if.h>07#include <sys/ioctl.h>08#include <sys/socket.h>09#include <linux/can.h>10#include <linux/can/raw.h>1112int main()13{14 int s, nbytes;15struct sockaddr_can addr;16struct ifreq ifr;17struct can_frame frame[2] = {{0}};18s = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字19strcpy(ifr.ifr_name,"can0");20ioctl(s, SIOCGIFINDEX, &ifr);//指定 can0 设备21addr.can_family =
AF_CAN;22addr.can_ifindex = ifr.ifr_ifindex;23bind(s, (struct sockaddr *)&addr,sizeof(addr));//将套接字与 can0 绑定24//禁⽤过滤规则,本进程不接收报⽂,只负责发送25setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);26//⽣成两个报⽂27frame[0].can_id = 0x11;28frame[0]. can_dlc = 1;29frame[0].data[0] ='Y';30frame[0].can_id = 0x22;31frame[0]. can_dlc = 1;32frame[0].data[0] ='N';33//循环发送两个报⽂34while(1)35{36nbytes = write(s, &frame[0],sizeof(frame[0]));//发送 frame[0]37if(nbytes !=sizeof(frame[0]))38{39 printf("Send Error frame[0]n!");40break;//发送错误,退出41}42sleep(1);43nbytes = write(s, &frame[1],sizeof(frame[1]));//发送 frame[1]44 if(nbytes !=sizeof(frame[0]))45{46printf("Send Error frame[1]n!");47break;48}49sleep(1);50}51close(s);52return0;53}
2. 报⽂过滤接收程序
01/* 2. 报⽂过滤接收程序 */02#include <stdio.h>03#include <stdlib.h>04#include <string.h>05#include <unistd.h>06#include
<net/if.h>07#include <sys/ioctl.h>08#include <sys/socket.h>09#include <linux/can.h>10#include <linux/can/raw.h>1112int main()13{14 int s, nbytes;15struct sockaddr_can addr;16struct ifreq ifr;17struct can_frame frame;18struct can_filter rfilter[1];19s = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字20strcpy(ifr.ifr_name,"can0");21ioctl(s, SIOCGIFINDEX, &ifr);//指定 can0 设备22
addr.can_family = AF_CAN;23addr.can_ifindex = ifr.ifr_ifindex;24bind(s, (struct sockaddr *)&addr,sizeof(addr));//将套接字与 can0 绑
定25//定义接收规则,只接收表⽰符等于 0x11 的报⽂26rfilter[0].can_id = 0x11;27rfilter[0].can_mask = CAN_SFF_MASK;28//设置过滤规则29setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter,sizeof(rfilter));30while(1)31{32nbytes = read(s, &frame,
sizeof(frame));//接收报⽂33//显⽰报⽂34if(nbytes > 0)35{36printf(“ID=0x%X DLC=%d data[0]=0x%Xn”, frame.can_id,37frame.can_dlc, frame.data[0]);38}39}40close(s);41return0;42}
这个⽰例程序博主并未编译测试验证。更完整的程序详见本⼈编写的linux socket can程序cantool