怎么做福利视频网站,c 微网站开发,门户网站建设自查整改报告,网页版qq登录入口版qq账号登录界面由于 Linux 系统将 CAN 设备作为网络设备进行管理#xff0c;因此在 CAN 总线应用开发方面#xff0c; Linux 提供了SocketCAN 应用编程接口#xff0c;使得 CAN 总线通信近似于和以太网的通信#xff0c;应用程序开发接口更加通用#xff0c;也更加灵活。 SocketCAN 中大…由于 Linux 系统将 CAN 设备作为网络设备进行管理因此在 CAN 总线应用开发方面 Linux 提供了SocketCAN 应用编程接口使得 CAN 总线通信近似于和以太网的通信应用程序开发接口更加通用也更加灵活。 SocketCAN 中大部分的数据结构和函数在头文件 linux/can.h 中进行了定义所以在我们的应用程序中一定要包含linux/can.h头文件。
创建socket套接字
CAN 总线套接字的创建采用标准的网络套接字操作来完成 网络套接字在头文件sys/socket.h中定义。 创建 CAN 套接字的方法如下
int sockfd-1;
/* 创建套接字 */
sockfdsocket(PF_CAN,SOCK_RAW,CAN_RAW);
if(0sockfd){perror(socket error);exit(EXIT_FAILURE);
}socket函数中第一个参数用于指定通信域在 SocketCan 中通常将 其设置为PF_CAN指定为CAN通信协议第二个参数用于指定套接字的类型通常将其设置为SOCK_RAW第三个参数通常设置为 CAN_RAW。
将套接字与 CAN 设备进行绑定
将创建的套接字与 can0 进行绑定示例代码如下所示
......
struct ifreq ifr {0};
struct sockaddr_can can_addr {0};
int ret;
......
strcpy(ifr.ifr_name, can0); //指定名字
ioctl(sockfd, SIOCGIFINDEX, ifr);
can_addr.can_family AF_CAN; //填充数据
can_addr.can_ifindex ifr.ifr_ifindex;
/* 将套接字与 can0 进行绑定 */
ret bind(sockfd, (struct sockaddr *)can_addr, sizeof(can_addr));
if (0 ret) {perror(bind error);close(sockfd);exit(EXIT_FAILURE);
}这里出现了两个结构体 struct ifreq 和 struct sockaddr_can其中 struct ifreq 定义在net/if.h头文件中而 struct sockaddr_can 定义在linux/can.h头文件中。
设置过滤规则
在我们的应用程序中如果没有设置过滤规则应用程序默认会接收所有 ID 的报文如果我们的应用程序只需要接收某些特定 ID 的报文亦或者不接受所有报文只发送报文则可以通过 setsockopt 函数设置过滤规则 譬如某应用程序只接收 ID 为 0x60A 和 0x60B 的报文帧则可将其它不符合规则的帧全部给过滤掉 示例代码如下所示
struct can_filter rfilter[2]; //定义一个 can_filter 结构体对象
// 填充过滤规则只接收 ID 为(can_id can_mask)的报文
rfilter[0].can_id 0x60A;
rfilter[0].can_mask 0x7FF;
rfilter[1].can_id 0x60B;
rfilter[1].can_mask 0x7FF;
// 调用 setsockopt 设置过滤规则
setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, rfilter, sizeof(rfilter));struct can_filter 结构体中只有两个成员 can_id 和 can_mask。 如果应用程序不接收所有报文 在这种仅仅发送数据的应用中可以在内核中省略接收队列以此减少CPU 资源的消耗。 此时可将 setsockopt()函数的第 4 个参数设置为 NULL将第 5 个参数设置为 0如下所示
setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);数据发送/接收
在数据收发的内容方面 CAN 总线与标准套接字通信稍有不同每一次通信都采用 struct can_frame 结构体将数据封装成帧。结构体定义如下
struct can_frame {
canid_t can_id; /* CAN 标识符 */
__u8 can_dlc; /* 数据长度最长为 8 个字节 */
__u8 __pad; /* padding */
__u8 __res0; /* reserved / padding */
__u8 __res1; /* reserved / padding */
__u8 data[8]; /* 数据 */
};can_id 为帧的标识符如果是标准帧就使用 can_id 的低 11 位如果为扩展帧就使用 028 位。 can_id 的第 29、 30、 31 位是帧的标志位用来定义帧的类型定义如下
/* special address description flags for the CAN_ID */
#define CAN_EFF_FLAG 0x80000000U /* 扩展帧的标识 */
#define CAN_RTR_FLAG 0x40000000U /* 远程帧的标识 */
#define CAN_ERR_FLAG 0x20000000U /* 错误帧的标识用于错误检查 */
/* mask */
#define CAN_SFF_MASK 0x000007FFU /* can_id CAN_SFF_MASK获取标准帧 ID */
#define CAN_EFF_MASK 0x1FFFFFFFU /* can_id CAN_EFF_MASK获取标准帧 ID */
#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */(1)、数据发送 对于数据发送使用 write()函数来实现譬如要发送的数据帧包含了三个字节数据 0xA0、 0xB0 以及 0xC0帧 ID 为 123可采用如下方法进行发送
struct can_frame frame; //定义一个 can_frame 变量
int ret;
frame.can_id 123;//如果为扩展帧那么 frame.can_id CAN_EFF_FLAG | 123;
frame.can_dlc 3; //数据长度为 3
frame.data[0] 0xA0; //数据内容为 0xA0
frame.data[1] 0xB0; //数据内容为 0xB0
frame.data[2] 0xC0; //数据内容为 0xC0
ret write(sockfd, frame, sizeof(frame)); //发送数据
if(sizeof(frame) ! ret) //如果 ret 不等于帧长度就说明发送失败
perror(write error);如果要发送远程帧(帧 ID 为 123)可采用如下方法进行发送
struct can_frame frame;
frame.can_id CAN_RTR_FLAG | 123;
write(sockfd, frame, sizeof(frame));(2)、数据接收 数据接收使用 read()函数来实现如下所示
struct can_frame frame;
int ret read(sockfd, frame, sizeof(frame));(3)、错误处理 当应用程序接收到一帧数据之后可以通过判断 can_id 中的 CAN_ERR_FLAG 位来判断接收的帧是否为错误帧。如果为错误帧可以通过 can_id 的其他符号位来判断错误的具体原因。错误帧的符号位在头文 件linux/can/error.h中定义。
/* error class (mask) in can_id */
#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
......回环功能设置
在默认情况下 CAN 的本地回环功能是开启的可以使用下面的方法关闭或开启本地回环功能
int loopback 0; //0 表示关闭 1 表示开启(默认)
setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, loopback, sizeof(loopback));在本地回环功能开启的情况下所有的发送帧都会被回环到与 CAN 总线接口对应的套接字上。
CAN 数据发送实例
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include sys/ioctl.h
#include sys/socket.h
#include linux/can.h
#include linux/can/raw.h
#include net/if.h
int main(void)
{struct ifreq ifr {0};struct sockaddr_can can_addr {0};struct can_frame frame {0};int sockfd -1;int ret;/* 打开套接字 */sockfd socket(PF_CAN, SOCK_RAW, CAN_RAW);if(0 sockfd) {perror(socket error);exit(EXIT_FAILURE);}/* 指定 can0 设备 */strcpy(ifr.ifr_name, can0);ioctl(sockfd, SIOCGIFINDEX, ifr);can_addr.can_family AF_CAN;can_addr.can_ifindex ifr.ifr_ifindex;/* 将 can0 与套接字进行绑定 */ret bind(sockfd, (struct sockaddr *)can_addr, sizeof(can_addr));if (0 ret) {perror(bind error);close(sockfd);exit(EXIT_FAILURE);}/* 设置过滤规则不接受任何报文、仅发送数据 */setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);/* 发送数据 */frame.data[0] 0xA0;frame.data[1] 0xB0;frame.data[2] 0xC0;frame.data[3] 0xD0;frame.data[4] 0xE0;frame.data[5] 0xF0;frame.can_dlc 6; //一次发送 6 个字节数据frame.can_id 0x123;//帧 ID 为 0x123,标准帧for ( ; ; ) {ret write(sockfd, frame, sizeof(frame)); //发送数据if(sizeof(frame) ! ret) { //如果 ret 不等于帧长度就说明发送失败perror(write error);goto out;}sleep(1); //一秒钟发送一次}out:/* 关闭套接字 */close(sockfd);exit(EXIT_SUCCESS);
}CAN 数据接收实例
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include sys/ioctl.h
#include sys/socket.h
#include linux/can.h
#include linux/can/raw.h
#include net/if.h
int main(void)
{struct ifreq ifr {0};struct sockaddr_can can_addr {0};struct can_frame frame {0};int sockfd -1;int i;int ret;/* 打开套接字 */sockfd socket(PF_CAN, SOCK_RAW, CAN_RAW);if(0 sockfd) {perror(socket error);exit(EXIT_FAILURE);}/* 指定 can0 设备 */strcpy(ifr.ifr_name, can0);ioctl(sockfd, SIOCGIFINDEX, ifr);can_addr.can_family AF_CAN;can_addr.can_ifindex ifr.ifr_ifindex;/* 将 can0 与套接字进行绑定 */ret bind(sockfd, (struct sockaddr *)can_addr, sizeof(can_addr));if (0 ret) {perror(bind error);close(sockfd);exit(EXIT_FAILURE);}/* 设置过滤规则 *///setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);/* 接收数据 */for ( ; ; ) {if (0 read(sockfd, frame, sizeof(struct can_frame))) {perror(read error);break;}/* 校验是否接收到错误帧 */if (frame.can_id CAN_ERR_FLAG) {printf(Error frame!\n);break;}/* 校验帧格式 */if (frame.can_id CAN_EFF_FLAG) //扩展帧printf(扩展帧 0x%08x , frame.can_id CAN_EFF_MASK);else //标准帧printf(标准帧 0x%03x , frame.can_id CAN_SFF_MASK);/* 校验帧类型数据帧还是远程帧 */if (frame.can_id CAN_RTR_FLAG) {printf(remote request\n);continue;}/* 打印数据长度 */printf([%d] , frame.can_dlc);/* 打印数据 */for (i 0; i frame.can_dlc; i)printf(%02x , frame.data[i]);printf(\n);}/* 关闭套接字 */close(sockfd);exit(EXIT_SUCCESS);
}