CAN FD (CAN with Flexible Data rate)可以認為是傳統(tǒng)CAN總線的升級版本,主要是對傳統(tǒng)CAN總線協(xié)議的數(shù)據(jù)長度和傳輸速度上做了升級。CAN FD的數(shù)據(jù)幀可以支持最長64數(shù)據(jù)字節(jié)的長度,而傳統(tǒng)CAN總線的數(shù)據(jù)長度為8字節(jié)。在傳輸速度上,標稱(仲裁)比特率與傳統(tǒng)CAN總線一致,最快支持到1M比特率,但是數(shù)據(jù)比特率則取決于現(xiàn)場總線環(huán)境,理論可以實現(xiàn)高達5 Mbit/s的數(shù)據(jù)比特率,同時CAN FD也是兼容傳統(tǒng)CAN總線的。網(wǎng)上也有許多介紹CAN FD的資料,有興趣的的話可以搜索更加詳細的資料查看。
英創(chuàng)公司推出的ESM8000主板上帶有兩路支持CAN FD協(xié)議的CAN總線,另外基于ESM8000主板,英創(chuàng)公司還推出了支持6路CAN FD協(xié)議的CAN總線擴展方案,關(guān)于這套方案具體可以參考《ESM8000異構(gòu)CPU實時應用——6路CAN-FD的實現(xiàn)》。不管是哪一種方案,英創(chuàng)公司都提供了現(xiàn)成驅(qū)動,將每一路CAN總線都映射為linux系統(tǒng)中的標準CAN設備。而用戶通過標準的socketcan編程,就可以實現(xiàn)對每一路CAN總線的操作。Linux系統(tǒng)提供的socketcan已經(jīng)對CAN FD提供了完整的支持,并且同時能夠兼容原來的傳統(tǒng)CAN總線,接下來我們就介紹具體的編程方法。
在socketcan中為了支持CAN FD,需要使用到數(shù)據(jù)幀結(jié)構(gòu)canfd_frame,這個數(shù)據(jù)結(jié)構(gòu)中同時支持了傳統(tǒng)CAN總線的數(shù)據(jù)幀與CAN FD的數(shù)據(jù)幀。在使能了CAN FD功能后,通過canfd_frame就可以進行收發(fā)數(shù)據(jù)。CAN總線使用的幀的結(jié)構(gòu)體定義在include/linux/can.h頭文件中,其中原來傳統(tǒng)CAN總線的數(shù)據(jù)幀can_frame具體內(nèi)容如下:
/* 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協(xié)議的數(shù)據(jù)幀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))); };
通過對比很容易發(fā)現(xiàn)在canfd_frame中, can幀的id、can幀的數(shù)據(jù)長度以及can幀實際的數(shù)據(jù)和can_frame結(jié)構(gòu)體中對應成員變量的偏移地址是完全一致的,只有數(shù)據(jù)的最大長度有所區(qū)別。所以傳統(tǒng)CAN總線的數(shù)據(jù)幀結(jié)構(gòu)體可以直接拷貝到CAN FD的數(shù)據(jù)幀結(jié)構(gòu)體中,這也允許了用戶通過canfd_fram結(jié)構(gòu)體實現(xiàn)對不同結(jié)構(gòu)數(shù)據(jù)幀的發(fā)送和接收。在同時存在傳統(tǒng)CAN數(shù)據(jù)幀和CAN FD數(shù)據(jù)幀的情況下用戶,也不會增加程序的復雜程度,通過判斷數(shù)據(jù)長度就能夠區(qū)別不同的數(shù)據(jù)幀結(jié)構(gòu)。
了解了數(shù)據(jù)幀結(jié)構(gòu)體,接下來就是帶入到程序中實際操作了,我們首先需要啟動CAN總線,在Linux系統(tǒng)中可以通過ip(8)工具來進行設置。這里需要特別注意,在使用CAN FD的情況下,設備之間設置的比特率和采樣點必須一致,否則會無法通信。本次測試采用了德國PEAK公司的PCAN-USB FD設備和ESM8000主板連接進行收發(fā)數(shù)據(jù),仲裁比特率設置為250K,數(shù)據(jù)傳輸比特率設置為2M,如下圖:
仲裁比特率 數(shù)據(jù)傳輸比特率
從上圖可以看到,250K波特率下采樣點在75%的位置,而2M波特率下采樣點在80%的位置。所以在ESM8000主板上也需要做同樣的設置,通過ip(8)工具可以很容易的設置,具體代碼如下:
/* 關(guān)閉can0 */ system("ifconfig can0 down"); /* 設置仲裁波特率和數(shù)據(jù)波特率,以及對應的采樣點 */ 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");
如果實際使用中,波特率和采樣點不同,只需要更改其中波特率和采樣點的設置即可,主板上的驅(qū)動會根據(jù)設置的值,自動計算出對應的參數(shù)并設置到CAN總線中。然后創(chuàng)建socketcan的套接字,這部分代碼和傳統(tǒng)CAN總線的socketcan操作完全一致:
/* 創(chuàng)建套節(jié)字 */ s = socket(PF_CAN, SOCK_RAW, CAN_RAW); printf( "SOCK_RAW can sockfd:%d\n", s ); if( s < 0 ) { return -1; } /* 是否開啟回環(huán)功能 */ 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;
創(chuàng)建的CAN_RAW套接字對于CAN FD的支持默認是關(guān)閉的,可以能夠通過使能CAN_RAW_FD_FRAMES這個套接字選項來增加對CAN FD的支持,當CAN_RAW_FD_FRAMES選項被使能,就可以同時支持發(fā)送can和can fd數(shù)據(jù)幀了,具體代碼如下:
/* 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設備的初始化就已經(jīng)完成了,此時通過write和read函數(shù),就可以通過對應的CAN接口進行數(shù)據(jù)通訊了,注意如果要支持CAN FD這里代入讀寫的數(shù)據(jù)幀結(jié)構(gòu)體應該為canfd_frame。例程在這里實現(xiàn)了一個簡單的回發(fā)功能,先讀取設備的數(shù)據(jù)然后判斷數(shù)據(jù)幀的類型并回發(fā),具體代碼如下:
for( i1=0; ;) { /* 讀取數(shù)據(jù) */ nbytes = read(s, &frame, sizeof(struct canfd_frame)); if (nbytes < 0) { perror("can raw socket read"); return 1; } /* 判斷數(shù)據(jù)幀的類型 */ if (nbytes == CANFD_MTU) { printf("got CAN FD frame with length %d\n", frame.len); /* frame.flags contains valid data */ /* 這里只是簡單的回發(fā) */ 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 */ /* 這里只是簡單的回發(fā) */ write(s, &frame, sizeof(struct can_frame)); } else { fprintf(stderr, "read: invalid CAN(FD) frame\n"); return 1; } }
使用PCAN的測試效果如下圖:
上圖中,上半部分為接收欄,下半部分為發(fā)送欄。可以看到我們通過PCAN同時發(fā)送了一個標準CAN幀和一個CAN FD幀,ESM8000主板收到后進行了回發(fā),回發(fā)的也同樣是一個標準CAN幀和一個CAN FD幀,通過軟件上的Type上可以看到標識,CAN FD幀同時也支持了比特率切換,按照我們之前的設置,CAN FD幀的數(shù)據(jù)部分就應該以2M的比特率進行的傳輸,下圖是通訊過程中,用示波器抓取的一次波形,可以看到前后仲裁比特率,和中間數(shù)據(jù)比特率的對比:
感興趣的客戶可以聯(lián)系英創(chuàng)的工程師獲取完整的測試代碼。
成都英創(chuàng)信息技術(shù)有限公司 028-8618 0660