英创工控主板CAN FD编程及使用介绍

 2021-10-25 15:47:50     作者:黄志超    

  CAN FD (CAN with Flexible Data rate)可以认为是传统CAN总线的升级版本,主要是对传统CAN总线协议的数据长度和传输速度上做了升级。CAN FD的数据帧可以支持最长64数据字节的长度,而传统CAN总线的数据长度为8字节。在传输速度上,标称(仲裁)比特率与传统CAN总线一致,最快支持到1M比特率,但是数据比特率则取决于现场总线环境,理论可以实现高达5 Mbit/s的数据比特率,同时CAN FD也是兼容传统CAN总线的。网上也有许多介绍CAN FD的资料,有兴趣的的话可以搜索更加详细的资料查看。


  英创公司推出的ESM8000主板上带有两路支持CAN FD协议的CAN总线,另外基于ESM8000主板,英创公司还推出了支持6路CAN FD协议的CAN总线扩展方案,关于这套方案具体可以参考《ESM8000异构CPU实时应用——6路CAN-FD的实现》。不管是哪一种方案,英创公司都提供了现成驱动,将每一路CAN总线都映射为linux系统中的标准CAN设备。而用户通过标准的socketcan编程,就可以实现对每一路CAN总线的操作。Linux系统提供的socketcan已经对CAN FD提供了完整的支持,并且同时能够兼容原来的传统CAN总线,接下来我们就介绍具体的编程方法。


  在socketcan中为了支持CAN FD,需要使用到数据帧结构canfd_frame,这个数据结构中同时支持了传统CAN总线的数据帧与CAN FD的数据帧。在使能了CAN FD功能后,通过canfd_frame就可以进行收发数据。CAN总线使用的帧的结构体定义在include/linux/can.h头文件中,其中原来传统CAN总线的数据帧can_frame具体内容如下:


/* CAN payload length and DLC definitions according to ISO 11898-1 */  
#define CAN_MAX_DLC 8  
#define CAN_MAX_DLEN 8  
  
/** 
 * struct can_frame - basic CAN frame structure 
 * @can_id:  CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition 
 * @can_dlc: frame payload length in byte (0 .. 8) aka data length code 
 *           N.B. the DLC field from ISO 11898-1 Chapter 8.4.2.3 has a 1:1 
 *           mapping of the 'data length code' to the real payload length 
 * @__pad:   padding 
 * @__res0:  reserved / padding 
 * @__res1:  reserved / padding 
 * @data:    CAN frame payload (up to 8 byte) 
 */  
struct can_frame {  
    canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */  
    __u8    can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */  
    __u8    __pad;   /* padding */  
    __u8    __res0;  /* reserved / padding */  
    __u8    __res1;  /* reserved / padding */  
    __u8    data[CAN_MAX_DLEN] __attribute__((aligned(8)));  
};

 

  CAN FD协议的数据帧canfd_frame具体定义如下:


/* CAN FD payload length and DLC definitions according to ISO 11898-7 */  
#define CANFD_MAX_DLC 15  
#define CANFD_MAX_DLEN 64  
  
/** 
 * 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)));  
};

 

  通过对比很容易发现在canfd_frame中, can帧的id、can帧的数据长度以及can帧实际的数据和can_frame结构体中对应成员变量的偏移地址是完全一致的,只有数据的最大长度有所区别。所以传统CAN总线的数据帧结构体可以直接拷贝到CAN FD的数据帧结构体中,这也允许了用户通过canfd_fram结构体实现对不同结构数据帧的发送和接收。在同时存在传统CAN数据帧和CAN FD数据帧的情况下用户,也不会增加程序的复杂程度,通过判断数据长度就能够区别不同的数据帧结构。


  了解了数据帧结构体,接下来就是带入到程序中实际操作了,我们首先需要启动CAN总线,在Linux系统中可以通过ip(8)工具来进行设置。这里需要特别注意,在使用CAN FD的情况下,设备之间设置的比特率和采样点必须一致,否则会无法通信。本次测试采用了德国PEAK公司的PCAN-USB FD设备和ESM8000主板连接进行收发数据,仲裁比特率设置为250K,数据传输比特率设置为2M,如下图:


英创工控主板CAN FD编程及使用介绍.png

仲裁比特率                                                                                    数据传输比特率


  从上图可以看到,250K波特率下采样点在75%的位置,而2M波特率下采样点在80%的位置。所以在ESM8000主板上也需要做同样的设置,通过ip(8)工具可以很容易的设置,具体代码如下:


/* 关闭can0 */  
system("ifconfig can0 down");  
/* 设置仲裁波特率和数据波特率,以及对应的采样点 */  
system("ip link set can0 type can bitrate 250000 sample-point 0.75 dbitrate 2000000 dsample-point 0.8 fd on restart-ms 100");  
/* 启动can0 */  
system("ifconfig can0 up");

 

  如果实际使用中,波特率和采样点不同,只需要更改其中波特率和采样点的设置即可,主板上的驱动会根据设置的值,自动计算出对应的参数并设置到CAN总线中。然后创建socketcan的套接字,这部分代码和传统CAN总线的socketcan操作完全一致:


/* 创建套节字 */  
s = socket(PF_CAN, SOCK_RAW, CAN_RAW);  
printf( "SOCK_RAW can sockfd:%d\n", s );  
if( s < 0 )  
{  
  return -1;  
}  
  
/* 是否开启回环功能 */  
int loopback = 0; /* 0 = disabled, 1 = enabled (default) */  
setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));  
  
/* 赋值实际的设备名 */  
strcpy(ifr.ifr_name, "can0" );  
ret = ioctl(s, SIOCGIFINDEX, &ifr);  
if( ret < 0 )  
{  
  return -1;  
}  
addr.can_family = AF_CAN;  
addr.can_ifindex = ifr.ifr_ifindex;

 

  创建的CAN_RAW套接字对于CAN FD的支持默认是关闭的,可以能够通过使能CAN_RAW_FD_FRAMES这个套接字选项来增加对CAN FD的支持,当CAN_RAW_FD_FRAMES选项被使能,就可以同时支持发送can和can fd数据帧了,具体代码如下:


/* check if the frame fits into the CAN netdevice */  
if (ioctl(s, SIOCGIFMTU, &ifr) < 0) {  
    perror("SIOCGIFMTU");  
    return 1;  
}  
  
if (ifr.ifr_mtu != CANFD_MTU) {  
    printf("CAN interface is not CAN FD capable - sorry.\n");  
    return 1;  
}  
  
enable_canfd = 1;  
/* interface is ok - try to switch the socket into CAN FD mode */  
if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable_canfd, sizeof(enable_canfd))){  
    printf("error when enabling CAN FD support\n");  
    return 1;  
}  
  
/* 将can接口和can设备id绑定 */  
bind(s, (struct sockaddr *)&addr, sizeof(addr));

 

  到这里CAN设备的初始化就已经完成了,此时通过write和read函数,就可以通过对应的CAN接口进行数据通讯了,注意如果要支持CAN FD这里代入读写的数据帧结构体应该为canfd_frame。例程在这里实现了一个简单的回发功能,先读取设备的数据然后判断数据帧的类型并回发,具体代码如下:


for( i1=0; ;)  
{  
  /* 读取数据 */  
  nbytes = read(s, &frame, sizeof(struct canfd_frame));  
  if (nbytes < 0) {  
    perror("can raw socket read");  
    return 1;  
  }  
  /* 判断数据帧的类型 */  
  if (nbytes == CANFD_MTU) {  
    printf("got CAN FD frame with length %d\n", frame.len);  
    /* frame.flags contains valid data */  
    /* 这里只是简单的回发 */  
    write(s, &frame, sizeof(struct canfd_frame));  
  } else if (nbytes == CAN_MTU) {  
    printf("got legacy CAN frame with length %d\n", frame.len);  
    /* frame.flags is undefined */  
    /* 这里只是简单的回发 */  
    write(s, &frame, sizeof(struct can_frame));  
    } else {  
      fprintf(stderr, "read: invalid CAN(FD) frame\n");  
      return 1;  
    }  
}

 

  使用PCAN的测试效果如下图:


英创工控主板CAN FD编程及使用介绍.png


  上图中,上半部分为接收栏,下半部分为发送栏。可以看到我们通过PCAN同时发送了一个标准CAN帧和一个CAN FD帧,ESM8000主板收到后进行了回发,回发的也同样是一个标准CAN帧和一个CAN FD帧,通过软件上的Type上可以看到标识,CAN FD帧同时也支持了比特率切换,按照我们之前的设置,CAN FD帧的数据部分就应该以2M的比特率进行的传输,下图是通讯过程中,用示波器抓取的一次波形,可以看到前后仲裁比特率,和中间数据比特率的对比:


英创工控主板CAN FD编程及使用介绍.png


  感兴趣的客户可以联系英创的工程师获取完整的测试代码。