2015年4月20日 星期一

CANBUS 發送與接收使用Socket CAN

參考文件 https://www.kernel.org/doc/Documentation/networking/can.txt
中文參考 http://blog.csdn.net/yuanlulu/article/details/7220060

控制器區域網路 (Controller Area Network, 簡稱 CAN 或 CANbus)
一種通訊協定,主要應用在汽車、工控領域。
目前版本CAN 2.0A與CAN 2.0B ,B是A的擴充。2.0B 的ID長度占29bit, 2.0A 的ID長度占11bit

節點架構


CAN Controller 通常內建於MCU內(也有獨立的ex: SJA1000),會在外接Transceiver(ex:ISO1050)轉成類比訊號。

CAN封包格式


寫基本的CAN,只需要注意仲裁欄位、控制欄位、資料欄位

Field nameLength (bits)Purpose
Start-of-frame1Denotes the start of frame transmission
Identifier (green)11A (unique) identifier which also represents the message priority
Remote transmission request (RTR)1Must be dominant (0) for data frames and recessive (1) for remote request frames (see Remote Frame, below)
Identifier extension bit (IDE)1Must be dominant (0) for base frame format with 11-bit identifiers
Reserved bit (r0)1Reserved bit. Must be dominant (0), but accepted as either dominant or recessive.
Data length code (DLC) (yellow)4Number of bytes of data (0–8 bytes)[a]
Data field (red)0–64 (0-8 bytes)Data to be transmitted (length in bytes dictated by DLC field)
CRC15Cyclic redundancy check
CRC delimiter1Must be recessive (1)
ACK slot1Transmitter sends recessive (1) and any receiver can assert a dominant (0)
ACK delimiter1Must be recessive (1)
End-of-frame (EOF)7Must be recessive (1)
CAN BUS的操作可以如同使用SOCKET。
參考Linux下範例 candsend.c   candump.c
看完除了會can 的發送與接收,也可以學到命令列參數使用方法 (function: getopt_long)

以下簡單範例:
Setting :
    struct can_frame frame;
    struct ifreq ifr;
    struct sockaddr_can addr;
    char *interface = "can0";
    int family = PF_CAN, type = SOCK_RAW, proto = CAN_RAW;

    s = socket(family, type, proto)
    strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
    if (ioctl(s, SIOCGIFINDEX, &ifr)) {
        perror("CAN ioctl");
        return 1;
    }
    addr.can_family = family;
    addr.can_ifindex = ifr.ifr_ifindex;

    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("CAN bind");
        return 1;
    }
Send :
//sendcanbus();
            frame.can_id = 0x0321;
            frame.can_dlc = 4;
            frame.data[0] = 0;
            frame.data[1] = 1;
            frame.data[2] = 2;
            frame.data[3] = 3;

            ret = send(s,&frame,sizeof(frame),0);
Read :
            //dropt the timeout packets
            while(nbytes = recvfrom(s,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,&len)>=0)
            {
                printf("\r\nrecvfrom nbytes %d",nbytes);
                memcpy(b,&frame,sizeof(frame));
                for (i=0 ; i < sizeof(struct can_frame) ; i++)
                {
                    printf(" 0x%x ",b[i]);
                }
                usleep(10000);//
            }
           




實驗測試的幾點心得:
  1. send() 可以用 write() 代替,recvfrom() 可用read()代替
  2. 通訊幾次後,將BUS移除,強迫發生timeout後,再接回bus,資料會存在can controller 內TX的buffer中,並不會移除。
  3. 為了取得最新資料,利用連下recvfrom()來丟棄timeout的frame。
  4. 連下recvfrom()兩次,有機會抓到一樣的資料,所以2次recv間要sleep()一下。
  5. 查找清除can buffer的方式,好像只有關掉重啟 CAN一途
    >ifconfig can0 down
    >ifconfig can0 up
    
  6. 為了製造TX、RX比對資料錯誤的假象,將Pin CAN_H、CAN_L短路,結果CANBus 再也沒動作...
    沒動作指的是send()一直下,但MCU到Transiver之間的通訊燈亮都沒亮...一樣只有 can0 down 、 can0 up 有解。因此判斷是CAN controller掛掉而已。
  7. 自從CAN controller GG後,心想既然可以收命令,也就有可能只是進入某種狀態
    果然 進入bus-off
    $ ip -details -statistics link show can0
  8. 利用以下,enable CAN controller 自動重啟
    
    $ set can0 up type can restart-ms 100 
  9. filter 、 廣播、error mode ... CAN還有很多可以玩啊....



待續~~~~~~~~~

4 則留言:

  1. 你用哪款CAN設備產品來收發資訊?

    回覆刪除
    回覆
    1. 實作在TI AM3505 的板子上
      Datasheet 請參考 http://www.ti.com/lit/ds/sprs550f/sprs550f.pdf

      刪除
  2. 想請教一下 data 讀出來的東西到底是甚麼資料? 您寫的是(data[0]=1) ,指的是0的位置讀出來僅是1而已?

    回覆刪除
  3. 想請教一下CAN send如何實現CAN bus有關於優先權的操作?

    回覆刪除

try comments