socket_can_demo

socket_can_demo

  • 1 can_filter
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#include <linux/can.h>
#include <linux/can/raw.h>

int main(int argc, char **argv)
{
	int s, i; 
	int nbytes;
	struct sockaddr_can addr;
	struct ifreq ifr;
	struct can_frame frame;

	printf("CAN Sockets Receive Filter Demo\r\n");

	if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
		perror("Socket");
		return 1;
	}

	strcpy(ifr.ifr_name, "vcan0" );
	ioctl(s, SIOCGIFINDEX, &ifr);

	memset(&addr, 0, sizeof(addr));
	addr.can_family = AF_CAN;
	addr.can_ifindex = ifr.ifr_ifindex;

	if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		perror("Bind");
		return 1;
	}

	struct can_filter rfilter[1];

	rfilter[0].can_id   = 0x550;
	rfilter[0].can_mask = 0xFF0;
	//rfilter[1].can_id   = 0x200;
	//rfilter[1].can_mask = 0x700;

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

	nbytes = read(s, &frame, sizeof(struct can_frame));

	if (nbytes < 0) {
		perror("Read");
		return 1;
	}

	printf("0x%03X [%d] ",frame.can_id, frame.can_dlc);

	for (i = 0; i < frame.can_dlc; i++)
		printf("%02X ",frame.data[i]);

	printf("\r\n");

	if (close(s) < 0) {
		perror("Close");
		return 1;
	}

	return 0;
}
  • 2 can_transmit
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#include <linux/can.h>
#include <linux/can/raw.h>

int main(int argc, char **argv)
{
	int s; 
	struct sockaddr_can addr;
	struct ifreq ifr;
	struct can_frame frame;

	printf("CAN Sockets Demo\r\n");

	if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
		perror("Socket");
		return 1;
	}

	strcpy(ifr.ifr_name, "vcan0" );
	ioctl(s, SIOCGIFINDEX, &ifr);

	memset(&addr, 0, sizeof(addr));
	addr.can_family = AF_CAN;
	addr.can_ifindex = ifr.ifr_ifindex;

	if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		perror("Bind");
		return 1;
	}

	frame.can_id = 0x555;
	frame.can_dlc = 5;
	sprintf(frame.data, "Hello");

	if (write(s, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {
		perror("Write");
		return 1;
	}

	if (close(s) < 0) {
		perror("Close");
		return 1;
	}

	return 0;
}
  • 3 can_receive
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#include <linux/can.h>
#include <linux/can/raw.h>

int main(int argc, char **argv)
{
	int s, i; 
	int nbytes;
	struct sockaddr_can addr;
	struct ifreq ifr;
	struct can_frame frame;

	printf("CAN Sockets Receive Demo\r\n");

	if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
		perror("Socket");
		return 1;
	}

	strcpy(ifr.ifr_name, "vcan0" );
	ioctl(s, SIOCGIFINDEX, &ifr);

	memset(&addr, 0, sizeof(addr));
	addr.can_family = AF_CAN;
	addr.can_ifindex = ifr.ifr_ifindex;

	if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		perror("Bind");
		return 1;
	}

	nbytes = read(s, &frame, sizeof(struct can_frame));

 	if (nbytes < 0) {
		perror("Read");
		return 1;
	}

	printf("0x%03X [%d] ",frame.can_id, frame.can_dlc);

	for (i = 0; i < frame.can_dlc; i++)
		printf("%02X ",frame.data[i]);

	printf("\r\n");

	if (close(s) < 0) {
		perror("Close");
		return 1;
	}

	return 0;
}

在虚拟机中或者在实际工控机安装了can卡
使用:

sudo ip link add vcan0 type vcan
vcan0为安装的设备名称
删除设备时使用
sudo ip link delete vcan0 type vcan
之后需要(只需在一个终端执行即可)
sudo ifconfig vcan0 up

分别打开两个终端运行发送和接收(接收的先运行),这时会在接收的终端上显示出发送的数据信息。

---------------------分割线----------------------------------------------
1-创建socket套接字
s = socket(PF_CAN, SOCK_RAW, CAN_RAW)
PF_CAN:/usr/include/x86_64-linux-gnu/bits/socket.h
     /* Protocol families.  */
#define  PF_CAN	 29	/* Controller Area Network.  */
     /* Address families.  */
#define AF_CAN	PF_CAN
/* particular protocols of the protocol family PF_CAN */
#define CAN_RAW	 1 /* RAW sockets */
-----
原始套接字的创建
int socket ( int family, int type, int protocol );
参数:
family:协议族 这里写PF_PACKET
type: 套接字类,这里写SOCK_RAW
protocol:协议类别,指定可以接收或发送的数据包类型,不能写 “0”,取值如下,注意,传参时需要用?htons() 进行字节序转换。
-----

2- sockaddr
/usr/include/x86_64-linux-gnu/bits/socket.h
sockaddr
其定义如下:
struct sockaddr {
  unsigned short  sa_family;   /* address family, AF_xxx */
  char  sa_data[14];   /* 14 bytes of protocol address */
  };
说明:
sa_family :是2字节的地址家族,一般都是“AF_xxx”的形式。//通常用的都是AF_INET。

3-Struct  ifreq
ifreq结构定义在/usr/include/net/if.h,ifreq结构用来配置ip地址,激活接口,配置MTU。
在Linux系统中获取IP地址通常都是通过ifconfig命令来实现的,然而ifconfig命令实际是通过ioctl接口与内核通信,
ifconfig命令首先打开一个socket,然后调用ioctl将request传递到内核,从而获取request请求数据。处理网络接
口的许多程序沿用的初始步骤之一就是从内核获取配置在系统中的所有接口。
struct ifreq
{
#define IFHWADDRLEN 6
 union
 {
  char ifrn_name[IFNAMSIZ];  
 } ifr_ifrn;
 union 
 {
  struct sockaddr ifru_addr;
  struct sockaddr ifru_dstaddr;
  struct sockaddr ifru_broadaddr;
  struct sockaddr ifru_netmask;
  struct  sockaddr ifru_hwaddr;
  short ifru_flags;
  int ifru_ivalue;
  int ifru_mtu;
  struct  ifmap ifru_map;
  char ifru_slave[IFNAMSIZ]; 
  char ifru_newname[IFNAMSIZ];
  void __user * ifru_data;
  struct if_settings ifru_settings;
 } ifr_ifru;
};
#define ifr_name ifr_ifrn.ifrn_name 
#define ifr_hwaddr ifr_ifru.ifru_hwaddr 
#define ifr_addr ifr_ifru.ifru_addr 
#define ifr_dstaddr ifr_ifru.ifru_dstaddr 
#define ifr_broadaddr ifr_ifru.ifru_broadaddr 
#define ifr_netmask ifr_ifru.ifru_netmask 
#define ifr_flags ifr_ifru.ifru_flags 
#define ifr_metric ifr_ifru.ifru_ivalue 
#define ifr_mtu  ifr_ifru.ifru_mtu 
#define ifr_map  ifr_ifru.ifru_map 
#define ifr_slave ifr_ifru.ifru_slave 
#define ifr_data ifr_ifru.ifru_data 
#define ifr_ifindex ifr_ifru.ifru_ivalue 
#define ifr_bandwidth ifr_ifru.ifru_ivalue    
#define ifr_qlen ifr_ifru.ifru_ivalue 
#define ifr_newname ifr_ifru.ifru_newname 
#define ifr_settings ifr_ifru.ifru_settings
------
struct ifconf 
{
  int ifc_len; /* size of buffer    */
  union 
 {
   char *ifcu_buf;     /* input from user->kernel*/
   struct  ifreq  *ifcu_req;   /* return from kernel->user*/
 } ifc_ifcu;
};
#define    ifc_buf    ifc_ifcu.ifcu_buf
#define    ifc_req    ifc_ifcu.ifcu_req

4- sockaddr_can
/**
 * struct sockaddr_can - the sockaddr structure for CAN sockets
 * @can_family:  address family number AF_CAN.
 * @can_ifindex: CAN network interface index.
 * @can_addr:    protocol specific address information
 */
struct  sockaddr_can {
          __kernel_sa_family_t can_family;
          int   can_ifindex;
         union {
              /* transport protocol class address information (e.g. ISOTP) */
             struct { canid_t rx_id, tx_id; } tp;
             /* reserved for future CAN protocols address information */
           } can_addr;
};

5-canfd_frame 
/**
 * struct canfd_frame - CAN flexible data rate frame structure
 * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
 * @len:    frame payload length in byte (0 .. CANFD_MAX_DLEN)
 * @flags:  additional flags for CAN FD
 * @__res0: reserved / padding
 * @__res1: reserved / padding
 * @data:   CAN FD frame payload (up to CANFD_MAX_DLEN byte)
 */
struct  canfd_frame {
	canid_t  can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
	__u8    len;     /* frame payload length in byte */
	__u8    flags;   /* additional flags for CAN FD */
	__u8    __res0;  /* reserved / padding */
	__u8    __res1;  /* reserved / padding */
	__u8    data[CANFD_MAX_DLEN] __attribute__((aligned(8)));
};

6-ioctl(套接字,,网络请求ifreq结构体)
(1)用户空间,ioctl的调用具有如下原型:
int ioctl(int fd, unsigned long cmd, ...);
其中fd是用户程序打开设备时使用open函数返回的文件标示符,cmd是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,这个参数的有无和cmd的意义相关。
(2)驱动空间,ioctl方法的原型如下:
int (*ioctl) (struct inode * node, struct file *filp, unsigned int cmd, unsigned long arg);
1)inode和file:ioctl的操作有可能是要修改文件的属性,或者访问硬件。要修改文件属性的话,就要用到这两个结构体了,所以这里传来了它们的指针。
2)cmd:控制命令。
3)arg:补充参数。
参数2定义在:/usr/include/linux/sockios.h
#define SIOCGIFINDEX      0x8933    /* name -> if_index mapping	*/
#define SIOGIFINDEX        SIOCGIFINDEX	       /* misprint compatibility :-)	*/
参考博客:
https://blog.csdn.net/u013029731/article/details/84947160
https://www.cnblogs.com/tdyizhen1314/p/4896689.html

7-网络编程使用到的bind()函数
int bind(int sockfd, struct sockaddr *my_addr, int addrlen)
sockfd:是由socket调用返回的文件描述符.
addrlen:是sockaddr结构的长度.
my_addr:是一个指向sockaddr的指针. 在中有 sockaddr的定义
struct sockaddr{
unisgned short as_family;
char sa_data[14];
};
不过由于系统的兼容性,我们一般不用这个头文件,而使用另外一个结构(struct sockaddr_in) 来代替.在中有sockaddr_in的定义
struct sockaddr_in{
unsigned short sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];}
我们主要使用Internet所以sin_family一般为AF_INET,sin_addr设置为INADDR_ANY表示可以和任何的主机通信,
sin_port是我们要监听的端口号.sin_zero[8]是用来填充的. bind将本地的端口同socket返回的文件描述符捆绑在一起.
成功是返回0,失败的情况和socket一样
---
bind(s, (struct sockaddr *)&addr, sizeof(addr))
参考博客:
https://blog.csdn.net/liuxingrui4p/article/details/50455376

8-sprintf 
sprintf 最常见的应用之一莫过于把整数打印到字符串中,所以,spritnf 在大多数场合可以替代
itoa。如:
//把整数123 打印成一个字符串保存在s 中。
sprintf(s, "%d", 123); //产生"123"
可以指定宽度,不足的左边补空格:
sprintf(s, "%8d%8d", 123, 4567); //产生:" 123 4567"
当然也可以左对齐:
sprintf(s, "%-8d%8d", 123, 4567); //产生:"123 4567"
也可以按照16 进制打印:
sprintf(s, "%8x", 4567); //小写16 进制,宽度占8 个位置,右对齐
sprintf(s, "%-8X", 4568); //大写16 进制,宽度占8 个位置,左对齐
----
也可以使用来赋值
在socket_can_demo中
sprintf(frame.data, "Hello");

9- 网络编程使用到的写操作
write(s, &frame, sizeof(struct can_frame));将can数据帧写到套接字s所绑定的地址上
-----
还有一对操作
//如果建立连接,将产生一个全新的套接字
if((new_fd = accept(sockfd,(struct sockaddr *)&their_addr,&sin_size))==-1)
{
perror("accept");
exit(1);
}
和
//将从客户端接收到的信息再发回客户端
if(send(new_fd,buff,strlen(buff),0)==-1)
perror("send");
close(new_fd);
exit(0);
}

10-Socket close()函数 
close(s);//s为创建的套接字
此选项指定函数close对面向连接的协议如何操作(如TCP),内核缺省close操作是立即返回,
如果有数据残留在套接口缓冲区中则系统将试着将这些数据发送给对方。
参考博客:
https://blog.csdn.net/liwenlong_only/article/details/80165839
------------------------------------------------------------------------------------------------
socket_can可以参考:https://www.beyondlogic.org/example-c-socketcan-code/
在工控机上的操作
can_tx.c和can_rx.c
首先对can*操作:
创建终端窗口tw0:
sudo ifconfig can* down
sudo ip link set can* type can bitrate 115200
sudo ifconfig can* up
然后打开两个终端窗口,这里记作tw1,tw2
tw1:运行can_rx,先进行接收,read()是阻塞性操作
tw2:运行can_tx,再运行发送;
结果:tw1中显示出can_tx发送的数据;
注:这里只在tw0中对can口进行了设置,没有再在tw1,tw2中进行同样的操作。

也可以参考:
https://blog.csdn.net/hhlenergystory/article/details/79534422
补充:
TX2是英伟达旗下为嵌入式平台人工智能应用开发出的一个硬件平台,去官网看了一看,TX1好像没有can bus功能,TX2才有,作为汽车零配件商必然选择TX2。

英伟达官方为TX2制作了专门的SDK,即jetPack。网站:https://developer.nvidia.com/embedded/jetpack

肯定是Linux系统,自带各种库,谷歌的TensorFlow,opencv啥的还有一些API比如比较重要的图像获取接口V4L2接口,等等。

好了,正题:如何实现can bus收发功能。

本来的思路是can bus作为外设,和串口还有i2c什么的一样是个字符设备或者平台设备什么的步骤应该是:

(1)找到TX2平台下的canbus驱动,insmod后找到设备文件(英伟达作为大公司应该已经写好了canbus的驱动并集成到了SDK中)。

(2)通常的外设一般有open打开设备,close关闭设备,read用来接收,write用来发送,ioctl用来配置设备,用这些函数进行应用编程即可。

由于没有TX2硬件,因此百度搜索一下看看是不是和我想的一样。。。但是好像并不是这样的。。。

百度搜索:TX2 can总线通讯,第一篇文章:http://blog.csdn.net/lybhit/article/details/78661584

得知:(1)首先canbus确实有驱动,但是用modprobe进行安装,modprobe大体上是一个insmod的升级版也是将模块载入内核,并且其驱动不止一个。同时TX2有两个canbus外设,为can0和can1。

(2)Linux下的canbus好像并不是通过设备文件进行操作的,而是通过socket!

(3)上述文章大体是采用:安装驱动->安装一个开源canbus通信app->将can0和can1的CANH和CANL相接(看出TX2并没有自带canbus收发器)->测试canbus。


总结以下几个常用的linux系统can总线操作命令:

1、CAN总线开启和关闭:ifconfig canx up/ifconfig canx down

2、CAN总线配置:

设置总线波特率:ip link set canx type can bitrate 250000

设置总线帧参数:ip link set canx type can tq 200 prop-seg 6 phase-seg1 6 phase-seg2 2 sjw 1

注:ip link set canx type can -help 命令格式中,‘|’含义为只能输入一种,‘[]’含义为可选参数。

3、查看CAN总线状态:ip -details -statistics show link canx

4、设置总线的bus-off复位:ip link set canx type can restart-ms 100

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值