在 C 中读取 CAN 总线显示 29 位 CAN ID 的 CAN ID 不正确

Mar*_*ppy 2 c linux can-bus

我用 C 编写了一些代码来读取 CAN 总线数据。当我读取 11 位 CAN ID 时,一切正常。当我尝试读取 29 位 ID 时,它会错误地显示 ID。

例子:

接收带有29位ID的消息:

0x01F0A020

并打印它

printf("%X\n", frame.can_id);
Run Code Online (Sandbox Code Playgroud)

它打印81F0A020

11位ID消息

0x7DF

并打印它

printf("%X\n", frame.can_id);
Run Code Online (Sandbox Code Playgroud)

它正确打印7DF

为什么会这样呢?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/can.h>
#include <linux/can/raw.h>

#define MAX_DATA_LEN 8
#define MAX_FIELDS 23
#define MAX_FIELD_LEN 64
#include <limits.h>

char data_str[MAX_FIELDS][MAX_FIELD_LEN];
int i;

int
main(void)
{
    int s;
    int nbytes;
    struct sockaddr_can addr;
    struct can_frame frame;
    unsigned short data[MAX_FIELDS];
    int sockfd = 0;
    int bcast = 1;
    struct sockaddr_in src_addr;
    struct sockaddr_in dst_addr;
    int numbytes;

    int fa;
    struct ifreq ifr;

    fa = socket(AF_INET, SOCK_DGRAM, 0);

    ifr.ifr_addr.sa_family = AF_INET;
    strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1);

    ioctl(fa, SIOCGIFHWADDR, &ifr);
    close(fa);

    //
    if((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("Udp sockfd create failed");
        exit(1);
    }

    //Enabled broadcast mode for udp
    if((setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST,
                &bcast, sizeof bcast)) == -1)
    {
        perror("setsockopt failed for broadcast mode ");
        exit(1);
    }

    src_addr.sin_family = AF_INET;
    src_addr.sin_port = htons(8888);
    src_addr.sin_addr.s_addr = INADDR_ANY;
    memset(src_addr.sin_zero, '\0', sizeof src_addr.sin_zero);

    if(bind(sockfd, (struct sockaddr*) &src_addr, sizeof src_addr) == -1)
    {
        perror("bind");
        exit(1);
    }

    dst_addr.sin_family = AF_INET;
    dst_addr.sin_port = htons(45454);
    dst_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    memset(dst_addr.sin_zero, '\0', sizeof dst_addr.sin_zero);

    if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
        perror("Error while opening socket");
        return -1;
    }

    addr.can_family  = AF_CAN;
    addr.can_ifindex = 0;

    if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("Error in socket bind");
        return -2;
    }

    //struct can_frame frame;
    while(1)
    {
        nbytes = read(s, &frame, sizeof(struct can_frame));

        if (nbytes < 0) {
                perror("can raw socket read");
                return 1;
        }

        /* Paranoid check ... */
        if (nbytes < sizeof(struct can_frame)) {
                fprintf(stderr, "read: incomplete CAN frame\n");
                return 1;
        }

        // Print the received CAN ID
        printf("%X\n", frame.can_id);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Bod*_*odo 7

字段can_id包含struct can_frameCAN IDEFF/RTR/ERR 标志。扩展ID有29位,因此有3个空闲位用于表示3个标志。

您的示例 ID0x01F0A020必须是扩展帧,但 ID0x7DF可以作为基本帧或扩展帧发送。这些是不同的消息。要区分具有相同 ID 的基本帧或扩展帧,您需要 EFF 标志。

在您的示例中,您看到的值是 ID和( )0x81F0A020的组合。0x01F0A020CAN_EFF_FLAG0x80000000U

摘自https://github.com/torvalds/linux/blob/master/include/uapi/linux/can.h

/* special address description flags for the CAN_ID */
#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
#define CAN_ERR_FLAG 0x20000000U /* error message frame */

/* valid bits in CAN ID for frame formats */
#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
Run Code Online (Sandbox Code Playgroud)

...

/**
 * struct can_frame - basic CAN frame structure
 * @can_id:  CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
 * @can_dlc: frame payload length in byte (0 .. 8) aka data length code
 *           N.B. the DLC field from ISO 11898-1 Chapter 8.4.2.3 has a 1:1
 *           mapping of the 'data length code' to the real payload length
 * @__pad:   padding
 * @__res0:  reserved / padding
 * @__res1:  reserved / padding
 * @data:    CAN frame payload (up to 8 byte)
 */
struct can_frame {
    canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
    __u8    can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
    __u8    __pad;   /* padding */
    __u8    __res0;  /* reserved / padding */
    __u8    __res1;  /* reserved / padding */
    __u8    data[CAN_MAX_DLEN] __attribute__((aligned(8)));
};
Run Code Online (Sandbox Code Playgroud)

要仅获取没有标志的 ID,您应该应用CAN_SFF_MASKCAN_EFF_MASK取决于位的值CAN_EFF_FLAG

示例代码:

        //Print the received CAN ID 
        printf("%X\n", 
               (frame.can_id & CAN_EFF_FLAG) ? (frame.can_id & CAN_EFF_MASK)
                                             : (frame.can_id & CAN_SFF_MASK));
Run Code Online (Sandbox Code Playgroud)