查看板子上的CAN设备是否使能:

android system vendor通讯 android can通信_#include


如果查询到上图所示,说明CAN设备(也可称为CAN“网卡”)是可以工作的,否则需要做好底层设备树及驱动配置。将板子上CAN接口(该板子只有1路CAN)的CAN_H和CAN_L分别接到USBCAN盒的其中一路CAN输入的CAN_H和CAN_L上,尝试从开发板发送数据到USBCAN上位机以及从上位机通过USBCAN发送数据到开发板。Linux系统将CAN当作网络设备进行统一管理,所以CAN应用程序仍然采用经典的socket通信那一套。开发板发送数据的应用代码如下。需要注意的是,对于CAN协议族,socket应该使用原始套接字SOCK_RAW。因为仅有发送,所以通过setsockopt设置了过滤规则为不接收任何报文。发送的内容是每帧8个字节的数据:0xAB 0xBC 0xCD 0xDE 0xEF 0xFA 0xAB 0xBC,ID为0x123,发送周期为1s。

#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}; // CAN套接字地址信息
	struct can_frame frame = {0}; // CAN报文帧
	int sockfd; // CAN套接字描述符
	int ret;

	if((sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0){ // PFCAN即CAN协议族,使用原始套接字
		perror("socket error");
		exit(EXIT_FAILURE);
	}

	strcpy(ifr.ifr_name, "can0"); // 指定网卡名称为开发板的can0设备
	if((ioctl(sockfd, SIOCGIFINDEX, &ifr)) != 0){ // 获取网络接口
		perror("ioctl error");
		exit(EXIT_FAILURE);
	}

	//printf("ifr.ifr_ifindex: %d", ifr.ifr_ifindex);
	
	can_addr.can_family = AF_CAN; // CAN地址簇
	can_addr.can_ifindex = ifr.ifr_ifindex; // 网卡序号
	if((ret = bind(sockfd, (struct sockaddr *)&can_addr, sizeof(can_addr))) < 0){
		perror("bind error");
		close(sockfd);
		exit(EXIT_FAILURE);
	}

	setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); // 设置过滤规则为不接收任何报文、仅发送数据

	frame.can_id = 0x123; // 帧ID为0x123(标准帧)
	frame.can_dlc = 8; // 一次发送8个字节
	frame.data[0] = 0xAB;
	frame.data[1] = 0xBC;
	frame.data[2] = 0xCD;
	frame.data[3] = 0xDE;
	frame.data[4] = 0xEF;
	frame.data[5] = 0xFA;
	frame.data[6] = 0xAB;
	frame.data[7] = 0xBC;	
	for(;;){
		if((ret = write(sockfd, &frame, sizeof(frame))) != sizeof(frame)){ // 发送
			perror("write error");
			goto out;
		}
		sleep(1);
	}
out:
	close(sockfd);
	exit(EXIT_SUCCESS);
}

编译后在开发板上运行,可以看到USBCAN的上位机接收结果如图,可见USBCAN上位机成功接收到了来自开发板的CAN报文。

android system vendor通讯 android can通信_#include_02


开发板上CAN接收数据的应用代码如下。需要注意的是,由于是接收数据,这里设置了三组报文ID过滤,分别是0x123、0x234和0x345,掩码设置的都是0x7FF,也就是说设置开发板的CAN仅接收ID为0x123或0x234或0x345的报文,其它ID不会被接收。接收到数据后根据报文ID中的一些特殊位,可以进行错误帧、扩展帧、远程帧等类型的判断。

#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};
	struct can_filter filters[3];
	int sockfd;
	int i;
	int ret;

	if((sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0){
		perror("socket error");
		exit(EXIT_FAILURE);
	}

	strcpy(ifr.ifr_name, "can0");
	if((ioctl(sockfd, SIOCGIFINDEX, &ifr)) != 0){
		perror("ioctl error");
		exit(EXIT_FAILURE);
	}

	can_addr.can_family = AF_CAN;
	can_addr.can_ifindex = ifr.ifr_ifindex;
	if((ret = bind(sockfd, (struct sockaddr *)&can_addr, sizeof(can_addr))) < 0){
		perror("bind error");
		close(sockfd);
		exit(EXIT_FAILURE);
	}

	filters[0].can_id = 0x123;
	filters[1].can_id = 0x234;
	filters[2].can_id = 0x345;
	for(i = 0;i < 3;i++)
		filters[i].can_mask = 0x7FF; // ID掩码
	setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, &filters, sizeof(filters)); // 设置接收过滤

	for(;;){
		if((read(sockfd, &frame, sizeof(struct can_frame))) < 0){
			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> \n", frame.can_id & CAN_EFF_MASK);
		else
			printf("标准帧 <0x%03x> \n", frame.can_id & CAN_SFF_MASK);

		if(frame.can_id & CAN_RTR_FLAG){ // 校验远程帧
			printf("remote request\n");
			continue;
		}

		printf("can_dlc: [%d] \n", frame.can_dlc);
		for(i = 0;i < frame.can_dlc;i++)
			printf("data: %02x", frame.data[i]);
		printf("\n");
	}

	close(sockfd);
	exit(EXIT_SUCCESS);
}

编译后在开发板上运行。尝试在USBCAN上位机发送若干种ID及类型的报文:

android system vendor通讯 android can通信_linux_03


开发板侧的接收情况如下。很明显可见,只有上面提到的那三种ID的报文才被接收了,并正确获取了帧类型。

android system vendor通讯 android can通信_网络_04