RK3568开发笔记-socketCan编程

目录

前言

一、socket can创建?

二、系统can节点设置

三、can过滤器设置

四、can数据发送

五、can数据接收

总结


前言

CAN是ControllerArea Network(控制器局域网)的缩写。CAN通信协议在1986年由德国电气商博世公司所开发,主要面向汽车的通信系统。现已是ISO国际标准化的串行通信协议。根据不同的距离、不同的网络,可配置不同的速度,最高速度为1MBit/s。can总线协议的内容相对来说比较多,在此不做详细介绍,本文主要介绍socket can的使用方法。

一、socket can创建

Socket CAN采用的即是原始套接字,我们通过采用AF_CAN协议来创建一个socket can原始套接字,创建代码如下所示:

    struct sockaddr_can tCanAddr;
    struct ifreq ifr;

    iCanFd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    strcpy(ifr.ifr_name, pcCanName);
	/* 获取网络接口 */
    iRet = ioctl(iCanFd, SIOCGIFINDEX, &ifr);
    if (iRet < 0)
    {
       perror("can init:");
       close(iCanFd);
       return 0;
    }
    tCanAddr.can_family = AF_CAN;
    tCanAddr.can_ifindex = ifr.ifr_ifindex;
    
    bind(iCanFd, (struct sockaddr *)&tCanAddr, sizeof(tCanAddr));

二、系统can节点设置

使用ip link对linux can设备节点进行波特率参数设置,本文采用linux命令设置方式,分别对socket can 和 socket canfd进行参数设置,如下所示:

    /* 关闭can设备节点 */
    sprintf(acCmd, "ip link set %s down", pcCanName);
    system(acCmd);
    usleep(1000);
    
    /* socket can/canfd波特率设置 */
	//socket can设置
	if(iCanType == E_CAN)
    	sprintf(acCmd, "ip link set %s type can bitrate %d", pcCanName, iSpeed);
	else	//socket canfd设置
		sprintf(acCmd, "ip link set %s type can bitrate %d dbitrate %d fd on", pcCanName, iSpeed, iSpeed);
		
    system(acCmd);
    usleep(1000);
    
    /* 开启can设备节点 */
    sprintf(acCmd, "ip link set %s up", pcCanName);
    system(acCmd);
    usleep(1000);

三、can过滤器设置

socket can可以使用can_filter过滤器设置指定接收can id的数据报文,根据我们的需求读取can总线上指定id数据,具体设置如下函数接口所示:

int CAN_SetFilter(int iCanfd, int iCanId)
{
	int iCanFdOn = 1;
	struct can_filter rfilter;

	if(iCanId > 0)
	{
		rfilter.can_id   = iCanId;
    	rfilter.can_mask = CAN_SFF_MASK;
		setsockopt(iCanfd, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));	
	}
	else
	{
		/* To disable the reception of CAN frames on the selected CAN_RAW socket: */
		setsockopt(iCanfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);	
	}
	/* support canfd */
	setsockopt(iCanfd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &iCanFdOn, sizeof(iCanFdOn));
	
	return 0;
}

四、can数据发送

socket can can_frame结构体是应用程序与内核驱动交互的结构体。内核驱动与CAN硬件交互之后,BUS上传输的通信协议由下文的通信协议指定。

/*
* 扩展格式识别符由 29 位组成。其格式包含两个部分:11 位基本 ID、18 位扩展 ID。
* Controller Area Network Identifier structure
*
* bit 0-28     : CAN识别符 (11/29 bit)
* bit 29     : 错误帧标志 (0 = data frame, 1 = error frame)
* bit 30     : 远程发送请求标志 (1 = rtr frame)
* bit 31     :帧格式标志 (0 = standard 11 bit, 1 = extended 29 bit)
*/
typedef __u32 canid_t;

struct can_frame { 
     canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
      __u8    can_dlc; /* 数据长度: 0 .. 8 */
      __u8    data[8] __attribute__((aligned(8))); 
 };

基于Linux Socket CAN的原始套接字,利用can_frame格式,一次最大发送8个字节数据。当然,也可以使用ISOTP格式,一次可以发送64字节。 下述函数接口可以发送标准帧、扩展帧,远程帧、数据帧。数据内容应用可以自行定义,发送函数接口如下所示:

int CAN_SendMsg(int iCanFd, T_CAN_MSG *ptCanMsg)
{
    struct can_frame tFrame;
    int iDataLen = 0;
    int iRet = 0;
    
    if ((iCanFd <= 0) || (NULL == ptCanMsg))
    {
       return 0;	
    }
    	
    memset(&tFrame, 0, sizeof(tFrame));
    if (ptCanMsg->cLen < 0)
    {
        iDataLen = 0;	
    }
    else if (ptCanMsg->cLen > 64)
    {
        iDataLen = 64;	
    }
    else
    {
        iDataLen = ptCanMsg->cLen;
    }
    
    tFrame.can_id = ptCanMsg->uiCobId;
    if (CAN_TYPE_EXTENDED == ptCanMsg->cIde)
    {
        tFrame.can_id &= CAN_EFF_MASK;
        tFrame.can_id |= CAN_EFF_FLAG;
    }
    else
    {
        tFrame.can_id &= CAN_SFF_MASK;
    }

	/* attention:canfd has no remote frames */
    if (CAN_FRAMETYPE_RTR == ptCanMsg->cRtr)
    {
        tFrame.can_id |= CAN_RTR_FLAG;
    }
    else
    {
        tFrame.can_id &= ~CAN_RTR_FLAG;
    }
    
    tFrame.can_dlc = iDataLen;

    if (iDataLen > 0)
    {
        memcpy(tFrame.data, ptCanMsg->acData, iDataLen);
    }
    
    iRet = write(iCanFd, &tFrame, sizeof(tFrame));
    
    //nbytes = sendto(sock_fd, &frame, sizeof(struct can_frame), 0, (struct sockaddr*)&addr, sizeof(addr));

    return iRet;
}

五、can数据接收

根据CAN通信协议,可以收到3种类型数据:数据帧,远程请求帧,错误帧。数据帧,远程请求帧包括标准格式和扩展格式,错误帧没有区分。 所有收到的数据,都是struct can_frame结构体表示,接收API接口函数定义如下所示: 

int CAN_RecvMsg(int iCanFd, T_CAN_MSG *ptCanMsg)
{
    struct can_frame tFrame;
    int iRet = 0;
    
    if ((iCanFd <= 0) || (NULL == ptCanMsg))
    {
       return 0;	
    }
    
    memset(&tFrame, 0, sizeof(tFrame));
    //iLen = recvfrom(iCanFd, &tFrame, sizeof(struct can_frame), 0, (struct sockaddr *)&addr, &len);
    iRet = read(iCanFd, &tFrame, sizeof(tFrame));
    if (iRet < 0)
    {
        return iRet;	
    }
    
    if (tFrame.can_id & CAN_EFF_FLAG)
    {
        ptCanMsg->cIde = CAN_TYPE_EXTENDED;
        ptCanMsg->uiCobId = tFrame.can_id & CAN_EFF_MASK;
    }
    else
    {
        ptCanMsg->cIde = CAN_TYPE_STANDARD;
        ptCanMsg->uiCobId = tFrame.can_id & CAN_SFF_MASK;
    }
    
    if (tFrame.can_id & CAN_RTR_FLAG)
    {
        ptCanMsg->cRtr = CAN_FRAMETYPE_RTR;
    }
    else
    {
        ptCanMsg->cRtr = CAN_FRAMETYPE_NORMAL;
    }
    
    ptCanMsg->cLen = tFrame.can_dlc;
    memcpy(ptCanMsg->acData, tFrame.data, tFrame.can_dlc);
    
    return iRet;
}

总结

本文主要对socket can网络编程进行api接口封装,在linux操作系统下可以直接调用,完整api接口函数库在下面链接下载:

https://download.csdn.net/download/qq_18376583/86815135

你可能感兴趣的:(RK3568开发笔记,1024程序员节,c语言,架构,linux,后端)