【Linux CANアプリケーションプログラミング(2)】SocketCANアプリケーションプログラミングの全工程を解説(コード付き)

        Linux システムは CAN デバイスをネットワーク デバイスとして管理するため、CAN バス アプリケーション開発に関して、Linux は SocketCAN アプリケーション プログラミング インターフェイスを提供します。これにより、CAN バス通信はイーサネットとの通信に似ており、アプリケーション開発インターフェイスはより一般的になります。そして柔軟です。

        SocketCAN のデータ構造と関数のほとんどはヘッダー ファイル linux/can.h で定義されているため、<linux/can.h> ヘッダー ファイルをアプリケーション プログラムに含める必要があります。

ソケットソケットを作成する

        CAN バス ソケットの作成は標準のネットワーク ソケット操作によって行われ、ネットワーク ソケットはヘッダー ファイルで定義されます。CAN ソケットの作成方法は次のとおりです。

int sockfd = -1;

/* 创建套接字 */
sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if(0 > sockfd) {
    perror("socket error");
    exit(EXIT_FAILURE);
}

        ソケット関数について詳しく紹介しましたが、第 1 パラメータは通信ドメインの指定に使用され、SocketCan では通常、CAN 通信プロトコルとして指定されている PF_CAN に設定され、第 2 パラメータはソケットのタイプを指定します。通常、これは SOCK_RAW に設定され、3 番目のパラメータは通常 CAN_RAW に設定されます。

ソケットをCANデバイスにバインドする

        たとえば、作成したソケットを can0 にバインドする場合のサンプルコードは次のとおりです。

......

struct ifreq ifr = {0};
struct sockaddr_can can_addr = {0};
int ret;

......

strcpy(ifr.ifr_name, "can0"); //指定名字
ioctl(sockfd, SIOCGIFINDEX, &ifr);

can_addr.can_family = AF_CAN; //填充数据
can_addr.can_ifindex = ifr.ifr_ifindex;

/* 将套接字与 can0 进行绑定 */
ret = bind(sockfd, (struct sockaddr *)&can_addr, sizeof(can_addr));
if (0 > ret) {
    perror("bind error");
    close(sockfd);
    exit(EXIT_FAILURE);
}

        bind() 関数については、「Linux でのネットワーク プログラミング (1) - まずはソケットを理解する」で詳しく紹介されているので、ここでは繰り返しません。

        ここには、struct ifreq と struct sockaddr_can の 2 つの構造体があります。struct ifreq はヘッダー ファイルで定義され、struct sockaddr_can はヘッダー ファイルで定義されます。

フィルタールールを設定する

        このアプリケーションでは、フィルタリング ルールが設定されていない場合、アプリケーションはデフォルトですべての ID メッセージを受信します。アプリケーションが特定の ID メッセージのみを受信する必要がある場合 (または、すべてのメッセージを受け入れずにメッセージの送信のみを行う場合)、次の方法でフィルタリング ルールを設定できます。 setsockopt 関数。たとえば、アプリケーションが ID 0x60A と 0x60B のメッセージ フレームのみを受信する場合、ルールを満たさない他のフレームをすべて除外できます。サンプル コードは次のとおりです。

struct can_filter rfilter[2]; //定义一个 can_filter 结构体对象

// 填充过滤规则,只接收 ID 为(can_id & can_mask)的报文
rfilter[0].can_id = 0x60A;
rfilter[0].can_mask = 0x7FF;
rfilter[1].can_id = 0x60B;
rfilter[1].can_mask = 0x7FF;

// 调用 setsockopt 设置过滤规则
setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));

        struct can_filter 構造体のメンバーは、can_id と can_mask の 2 つだけです。データを送信するだけのこのアプリケーションでは、アプリケーション プログラムがすべてのメッセージを受信できない場合、カーネル内で受信キューを省略して CPU リソースの消費を削減できます。このとき、次のように、setsockopt() 関数の 4 番目のパラメーターを NULL に設定し、5 番目のパラメーターを 0 に設定できます。

setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);

データ送受信

        CANバスは、データの送受信内容が標準的なソケット通信とは少し異なり、struct can_frame構造体を用いてデータをフレームにカプセル化して通信します。構造は次のように定義されます。

struct can_frame {
     canid_t can_id; /* CAN 标识符 */
     __u8 can_dlc; /* 数据长度(最长为 8 个字节) */
     __u8 __pad; /* padding */
     __u8 __res0; /* reserved / padding */
     __u8 __res1; /* reserved / padding */
     __u8 data[8]; /* 数据 */
};

        can_id はフレーム識別子で、標準フレームの場合は can_id の下位 11 ビットを使用し、拡張フレームの場合は 0 ~ 28 ビットを使用します。can_id の 29 ビット目、30 ビット目、31 ビット目はフレームのフラグ ビットで、フレームのタイプを定義するために使用されます。

/* special address description flags for the CAN_ID */
#define CAN_EFF_FLAG 0x80000000U /* 扩展帧的标识 */
#define CAN_RTR_FLAG 0x40000000U /* 远程帧的标识 */
#define CAN_ERR_FLAG 0x20000000U /* 错误帧的标识,用于错误检查 */

/* mask */
#define CAN_SFF_MASK 0x000007FFU /* <can_id & CAN_SFF_MASK>获取标准帧 ID */
#define CAN_EFF_MASK 0x1FFFFFFFU /* <can_id & CAN_EFF_MASK>获取标准帧 ID */
#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */

        (1)、データ送信

        データの送信には write() 関数を使用します。たとえば、送信するデータ フレームには 0xA0、0xB0、0xC0 の 3 バイトのデータが含まれており、フレーム ID は 123 です。送信には次のメソッドを使用できます。

struct can_frame frame; //定义一个 can_frame 变量
int ret;

frame.can_id = 123;//如果为扩展帧,那么 frame.can_id = CAN_EFF_FLAG | 123;
frame.can_dlc = 3; //数据长度为 3
frame.data[0] = 0xA0; //数据内容为 0xA0
frame.data[1] = 0xB0; //数据内容为 0xB0
frame.data[2] = 0xC0; //数据内容为 0xC0

ret = write(sockfd, &frame, sizeof(frame)); //发送数据
if(sizeof(frame) != ret) //如果 ret 不等于帧长度,就说明发送失败
    perror("write error");

        リモート フレーム (フレーム ID が 123) を送信する場合は、次の方法で送信できます。

struct can_frame frame;

frame.can_id = CAN_RTR_FLAG | 123;

write(sockfd, &frame, sizeof(frame));

        (2)、データ受信

        データ受信は、以下に示すように、read() 関数を使用して実装されます。

struct can_frame frame;

int ret = read(sockfd, &frame, sizeof(frame));

        (3)、エラー処理

        アプリケーションプログラムはデータフレームを受信した後、can_id の CAN_ERR_FLAG ビットを判断することで、受信したフレームがエラーフレームであるかどうかを判断できます。エラーフレームの場合、can_id の他の符号ビットによりエラーの具体的な原因を判断できます。エラー フレームの符号ビットはヘッダー ファイルで定義されます。

/* error class (mask) in can_id */
#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
......
......

ループバック機能の設定

        デフォルトでは、CAN のローカル ループバック機能は有効になっており、次の方法を使用してローカル ループバック機能を無効または有効にできます。

int loopback = 0; //0 表示关闭,1 表示开启(默认)

setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));

        ローカル ループバック機能が有効な場合、送信されたすべてのフレームは CAN バス インターフェイスに対応するソケットにループバックされます。


        SocketCAN プログラミング プロセスの学習は終了しました。次のパートでは、簡単な CAN アプリケーション プログラムを作成します。

おすすめ

転載: blog.csdn.net/cj_lsk/article/details/130962936