1. 程式人生 > >ARM40-A5應用程式——CAN匯流排的傳送和接收

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