ARM40-A5應用程式——CAN匯流排的傳送和接收
ARM40-A5應用程式——CAN匯流排的傳送和接收
版權宣告:本文為博主原創文章,允許轉載。
ARM40-A5系列板卡共有2路隔離CAN匯流排,CAN匯流排的引腳定義見《ARM40-A5指南——CAN匯流排介面與測試》。
一、shell中CAN匯流排的接收與傳送
1.1、硬體接線與配置
將CAN0的TX與CAN1的TX通過100R電阻連線,CAN0的RX和CAN1的RX通過100R電阻連線(直接相連亦可),然後:
ifconfig can0 down ip link set can0 type can bitrate 250000 //配置can0的波特率為250kbps ifconfig can0 up ifconfig can1 down ip link set can1 type can bitrate 250000 //配置can0的波特率為250Kbps ifconfig can1 up
2.2、shell中接收與傳送
[email protected]:~# candump can1 & //將CAN1設定為接收
[email protected]:~# cansend can0 5A1#11.2233.44556677.88 // CAN0發資料
can1 5A1 [8] 11 22 33 44 55 66 77 88
[email protected]:~# candump can0 & //將CAN0設定為接收
[email protected] :~# cansend can1 5A1#11.2233.44556677.88 // CAN1發資料
can0 5A1 [8] 11 22 33 44 55 66 77 88
二、CAN匯流排傳送和接收的C原始碼
2.1、CAN匯流排傳送
這個測試程式要用USB CAN II除錯盒或者睿芯CAN轉串列埠(USB串列埠)做接收,傳送到PC機上。
/* * test_can_recv.c *ARM40傳送,PC接收 */ #include <stdio.h> #include <string.h> #include <sys/socket.h> #include <linux/can.h> #include <linux/can/raw.h> #include <linux/socket.h> #include <linux/net.h> #include <linux/sockios.h> #include <linux/if.h> int main( int argc,char* argv[] ) { int i, ret; int nbytes; int family = PF_CAN, type = SOCK_RAW, proto = CAN_RAW; char *interface = "can0"; int can_fd = -1; struct sockaddr_can addr; struct ifreq ifr; struct can_frame frame_rev; printf("SocketCAN Test V1.0\n"); can_fd = socket(family, type, proto); // 建立SocketCAN 套接字 printf("SOCK_RAW can sockfd:%d\n", can_fd); if( can_fd < 0 ) { perror("socket"); return can_fd; } int loopback = 0; // 0 = disabled, 1 = enabled (default) setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback)); strcpy(ifr.ifr_name, interface); // 指定 can0 裝置 strcpy(ifr.ifr_name, "can0" ); ret = ioctl(can_fd, SIOCGIFINDEX, &ifr); // 指定 can0 裝置 if(ret) { perror("ioctl:interface"); goto abort; } addr.can_family = family; addr.can_ifindex = ifr.ifr_ifindex; ret = bind(can_fd, (struct sockaddr *)&addr, sizeof(addr)); // 將套接字與 can0 繫結 if (ret) { perror("bind\n"); goto abort; } // 設定過濾規則,只接收表示符等於 0x123 的報文,如果沒有這段,則接受所有報文 struct can_filter rfilter; rfilter.can_id = 0x123; rfilter.can_mask = CAN_SFF_MASK; // 0x000007FFU setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)); //設定過濾規則 /* * 如果應用程式不需要接收報文,可以禁用過濾規則。這樣的話,原始套接字就會忽略所有接收到的報文。 * 在這種僅僅傳送資料的應用中,可以在核心中省略接收佇列,以此減少 CPU 資源的消耗。 * 禁用方法如下: * setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); //禁用過濾規則 */ memset(&frame_rev,0x0,sizeof(frame_rev)); while(1) { nbytes = read(can_fd, &frame_rev, sizeof(frame_rev)); //接收報文 if(nbytes < 0){ perror("read"); goto abort; } if(nbytes < sizeof(struct can_frame)){ printf("read:incomplete can frame\n"); goto abort; } printf("ID=0x%X,DLC=%d",frame_rev.can_id,frame_rev.can_dlc); //顯示報文 for(i=0; i<frame_rev.can_dlc; i++) printf(",data[%d]=%x", i,frame_rev.data[i]); printf("\n"); memset(&frame_rev,0x0,sizeof(frame_rev)); } close(can_fd); return 0; abort: close(can_fd); return ret; }
測試結果為:
[email protected]:~# ./test_can0_recv
SocketCAN Test V1.0
SOCK_RAW can sockfd:3 // PC上傳送資料,ARM40上可以接收到
ID=0x123,DLC=8,data[0]=0,data[1]=1,data[2]=2,data[3]=3,data[4]=4,data[5]=5,data[6]=6,data[7]=7
ID=0x123,DLC=8,data[0]=0,data[1]=1,data[2]=2,data[3]=3,data[4]=4,data[5]=5,data[6]=6,data[7]=7
2.2、CAN匯流排接收
這個測試程式要用USB CAN II除錯盒或者睿芯CAN轉串列埠(USB串列埠)接到PC機上,PC傳送,ARM40接收。
/*
*test_can_send.c
*ARM40接收,PC傳送
*/
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/sockios.h>
#include <linux/if.h>
int main( int argc,char* argv[] )
{
int i, ret;
int nbytes;
int family = PF_CAN, type = SOCK_RAW, proto = CAN_RAW;
char *interface = "can1";
int can_fd = -1;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame_send;
printf("SocketCAN Test V1.0\n");
can_fd = socket(family, type, proto); // 建立SocketCAN 套接字
printf("SOCK_RAW can sockfd:%d\n", can_fd);
if( can_fd < 0 )
{
perror("socket");
return can_fd;
}
int loopback = 0; // 0 = disabled, 1 = enabled (default)
setsockopt(can_fd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));
strcpy(ifr.ifr_name, interface); // 指定 can0 裝置 strcpy(ifr.ifr_name, "can0" );
ret = ioctl(can_fd, SIOCGIFINDEX, &ifr); // 指定 can0 裝置
if(ret)
{
perror("ioctl:SIOCGIFINDEX");
goto abort;
}
addr.can_family = family;
addr.can_ifindex = ifr.ifr_ifindex;
ret = bind(can_fd, (struct sockaddr *)&addr, sizeof(addr)); // 將套接字與 can0 繫結
if(ret) {
perror("bind\n");
goto abort;
}
/* 傳送一個位元組的情況
frame_send.can_id = 0x00;
frame_send.can_dlc = 1;
frame_send.data[0] = 'Y';
*/
frame_send.can_id = 0x123;
frame_send.can_dlc = 8; // 傳送8個位元組,一次最多發8個位元組
for(i=0; i<8; ++i) {
frame_send.data[i] = i;
}
//strncpy(frame_send.data,"12345678",8);
nbytes = write(can_fd, &frame_send, sizeof(frame_send)); //傳送
if(nbytes <0 ){
perror("write");
goto abort;
}
// 打印發送的位元組
printf("write %d bytes frame:can_id=0x%x,can_dlc=%d\n", i,frame_send.can_id,frame_send.can_dlc);
for(i=0; i<frame_send.can_dlc; i++)
printf(",data[%d]=%x", i,frame_send.data[i]);
printf("\n");
close(can_fd);
return 0;
abort:
close(can_fd);
return ret;
}
測試結果為:
[email protected]:~# ./test_can1_send
SocketCAN Test V1.0
SOCK_RAW can sockfd:3 // PC上收到的資料
write 8 bytes frame:can_id=0x123,can_dlc=8
,data[0]=0,data[1]=1,data[2]=2,data[3]=3,data[4]=4,data[5]=5,data[6]=6,data[7]=7
[email protected]:~#
參考文章:
Linux核心 Documentation/networking/can.txt
Low Level CAN Framework Application Programmers Interface
Linux核心Socket CAN中文文件
https://blog.csdn.net/yuanlulu/article/details/7220060
Linux socket CAN程式設計示例
https://blog.csdn.net/jirryzhang/article/details/79417986
Linux CAN程式設計詳解
https://blog.csdn.net/reille/article/details/49980469
linux can 匯流排socket介面測試使用
http://blog.chinaunix.net/uid-13889805-id-3072479.html
CAN介面測試方法
http://www.embedsky.com/index.php?g=home&m=news&a=show&id=62
https://github.com/linux-can/can-utils
薈聚計劃:共商 共建 共享 Grant