支持CAN FD的扩展模块介绍

 2020-5-27 13:57:33     作者:黄志超    
文章标签:C/C++CAN

  随着现在工控领域技术的发展,传输的数据量越来越多, CAN总线在传输速率和带宽渐渐不能满足现场的需求。为了解决这个问题,BOSCH公司推出了CAN FD(CAN with Flexible Data rate)协议,该协议可以支持更高的波特率和更长的数据位,最高波特率可达8Mbit/s,支持最长数据位为64byte,并且可以兼容传统的CAN 2.0B协议。为了在英创Linux工控主板上支持CAN FD,满足客户的需求,英创公司基于SBC8XX系列工控应用底板,设计了扩展模块ETA703。


eta703侧面.png

ETA703模块


  ETA703通过SPI总线扩展出一路支持CAN FD的CAN总线,同时将主板的串口隔离后分别转换为RS232和RS485,并将主板带有的2路CAN总线也经过隔离后引出,因此模块上一共包含3路can、3路RS485和1路RS232:


ETA703工控通讯接口接口数量简要说明
CAN-FD高速现场总线1 8Mbps最高波特率,与CAN2.0B兼容
CAN-2.0B总线2 波特率: 50kbps - 1Mbps
RS4853 自动方向切换
RS2321 3线制


  英创公司已经调试好了ETA703的驱动,用户在使用时调用modprobe eta703命令就能够加载对应的驱动文件。目前能够支持ETA703扩展模块的主板为ESM6800H/E,ESM6802和ESM7000,因为CAN FD是比较新的功能,需要这几款主板使用的较高版本的交叉工具链才能够支持。


  驱动加载成功后系统会生成新的CAN设备,因为ESMARC系列主板都板载了2路CAN总线(can0和can1),所以加载驱动后生成的设备为can2,可以使用命令ifconfig –a来查看。


  关于ETA703更加具体的参数说明可以参考ETA703的数据手册。这里主要介绍关于CAN FD的相关概念和程序接口。CAN FD的全称为CAN with Flexible Data rate,相对于传统的CAN总线协议,主要有两点变化:


  第一点从字面上就能够看出来,即传输速率是可变的,最高可达8Mbit/s。


  第二点是相比传统CAN总线的8byte数据位,CAN FD协议最长可以支持64byte数据位。


  我们可以将CAN FD看作是传统CAN 2.0B协议的升级版本,并且是向下兼容的,即能够同时支持CAN 2.0B协议。所以使用原来英创公司提供的CAN总线例程(test_socketcan)就能够在ETA703上进行正常的通讯,只是没有启用CAN FD的功能而已,关于CAN FD协议更加详细的介绍可以参考网站:https://www.can-cia.org/can-knowledge/can/can-fd/


  接下来我们介绍如何通过程序启用CAN FD协议进行通讯。其实Linux系统使用的socketcan接口已经很好的支持了CAN FD,并且同时能够兼容CAN 2.0B协议。首先来看在<linux/can.h>头文件中对于CAN和CANFD数据帧的结构体定义:


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数据帧的定义,和下面CAN FD数据帧的定义对比,可以发现是兼容的,区别只在于数据位的长度上面:


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)));
};



  关于数据位的长度,根据前面的介绍应该能够了解到CAN FD最长支持64byte的数据位,在头文件中有如下定义:


/* CAN payload length and DLC definitions according to ISO 11898-1 */
#define CAN_MAX_DLC 8
#define CAN_MAX_DLEN 8
 
/* CAN FD payload length and DLC definitions according to ISO 11898-7 */
#define CANFD_MAX_DLC 15
#define CANFD_MAX_DLEN 64


  启用CAN FD具体命令为ip link set can2 up type can bitrate 1000000 dbitrate 2000000 fd on restart-ms 100,可以看到是在原来使能CAN总线命令的基础上增加了CAN FD的波特率的设置。同时在程序中也需要调用ioctl来使能CAN FD,具体代码如下:


/* try to switch the socket into CAN FD mode */
canfd_on = 1;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on));



  在使能了CAN FD协议后,数据通讯的部分使用的函数和标准的CAN通讯是相同的,都是使用write和recv函数,驱动会根据传输的帧的长度,自动判断是否为CAN FD数据帧。所以客户在编写程序的时候,只需要注意一下填入的传输数据长度即可,这里可以利用头文件中的宏定义:


#define CAN_MTU           (sizeof(struct can_frame))
#define CANFD_MTU       (sizeof(struct canfd_frame))



  发送CAN FD数据的具体代码如下:


struct canfd_frame   frame;
frame.len = 64;
nbytes = write(s, &frame, CANFD_MTU);



  如果将frame.len的值改为8,write函数中的长度改为CAN_MTU,驱动就自动切换成CAN 2.0B协议进行发送。同样的在接收数据的时候,可以通过recv函数的返回值来判断,具体代码如下:


nbytes = recv(can_sock, &frame, CANFD_MTU, 0 );
if (nbytes < 0)
{
       perror("can raw socket read");
       break;
}
if ((size_t)nbytes == CAN_MTU) {
       maxdlen = CAN_MAX_DLEN;
}
else if ((size_t)nbytes == CANFD_MTU) {
       maxdlen = CANFD_MAX_DLEN;
}
else {
       fprintf(stderr, "read: incomplete CAN frame\n");
       return 1;
}


  从上面的代码可以看出CAN FD的编程十分简单,socketcan提供的接口完美的兼容了CAN 2.0B和CAN FD,只需要在原来标准CAN总线的例程中增加对CAN FD的使能就可以了,在数据传输时候,根据数据帧长度就能判断出类型,然后做出对应的处理。


  感兴趣的客户可以和英创的工程师联系,索取完整的例程代码。

文章标签:C/C++CAN