CAN通信

C言語でCANを使う


Raspberry PiとOrangePi PCでCAN通信ができるようになりました。(インストールはこちら)
そこで、C言語を使ってCAN通信を行う方法を紹介します。
can-utilsのソースがこちらに 公開されています。
以下の手順でソースからビルドすることができます。
$ sudo apt install autoconf libtool
$ git clone https://github.com/linux-can/can-utils.git
$ cd can-utils
$ ./autogen.sh
$ ./configure
$ make

ソースを見るとsocket関数を使って送受信できることが分かりました。
LANとの違いはbind()に指定する定数と、送信データをcanfd_frame構造体に変換してwriteしています。
他はLAN通信と同じ手順です。



cansend.cを参考にして、最も簡単な送信処理を作りました。
parse_canframe関数で、送信データをcanfd_frame構造体に変換しています。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#include <linux/can.h>
#include <linux/can/raw.h>

#include "lib.h"

#define DEBUG 1

int main(int argc, char **argv)
{
        int s; /* can raw socket */
        int required_mtu;
        struct sockaddr_can addr;
        struct canfd_frame frame;
        struct ifreq ifr;


        /* parse CAN frame */
        required_mtu = parse_canframe("123#0011223344556677", &frame);
        if(DEBUG)printf("required_mtu=%d\n",required_mtu);

        /* open socket */
        if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
                perror("socket");
                return 1;
        }

        strncpy(ifr.ifr_name, "can0", IFNAMSIZ - 1);
        ifr.ifr_name[IFNAMSIZ - 1] = '\0';
        ifr.ifr_ifindex = if_nametoindex(ifr.ifr_name);
        if (!ifr.ifr_ifindex) {
                perror("if_nametoindex");
                return 1;
        }

        memset(&addr, 0, sizeof(addr));
        addr.can_family = AF_CAN;
        addr.can_ifindex = ifr.ifr_ifindex;

        /* disable default receive filter on this RAW socket */
        /* This is obsolete as we do not read from the socket at all, but for */
        /* this reason we can remove the receive list in the Kernel to save a */
        /* little (really a very little!) CPU usage.                          */
        setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);

        if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
                perror("bind");
                return 1;
        }

        /* send frame */
        if(DEBUG) {
          printf("can_id:%02x\n",frame.can_id);
          printf("len:%d\n",frame.len);
          int i;
          for(i=0;i<frame.len;i++) {
            printf("data[%d]:%02x\n",i,frame.data[i]);
          }
        }
        if (write(s, &frame, required_mtu) != required_mtu) {
                perror("write");
                return 1;
        }

        close(s);

        return 0;
}



受信処理もcandumpを参考にすれば作ることができます。
candumpは沢山オプションが有るので解析が少し面倒です。

続く...