在 C 中读取 CAN 总线显示 29 位 CAN ID 的 CAN ID 不正确
Reading CAN bus in C displays incorrect CAN ID for 29-bit CAN ID
我用C写了一些代码来读取CAN总线数据。当我读取 11 位 CAN ID 时一切正常。一旦我尝试读取 29 位 ID,它就会错误地显示 ID。
示例:
正在接收带有 29 位 ID 的消息:
0x01F0A020
并用
打印
printf("%X\n", frame.can_id);
它打印 81F0A020。
11 位 ID 信息
0x7DF
并用
打印出来
printf("%X\n", frame.can_id);
打印正确 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, '[=12=]', 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, '[=12=]', 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;
}
struct can_frame
的字段 can_id
包含 CAN ID 和 EFF/RTR/ERR 标志。扩展ID有29位,所以有3个空闲位用来表示3个flags。
您的示例 ID 0x01F0A020
必须是扩展帧,但 ID 0x7DF
可以作为基本帧或扩展帧发送。这些是不同的消息。要区分具有相同 ID 的基本框架或扩展框架,您需要 EFF 标志。
在您的示例中,您会看到值 0x81F0A020
,它是 ID 0x01F0A020
和 CAN_EFF_FLAG
(0x80000000U
) 的组合。
摘自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 */
...
/**
* 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)));
};
要仅获取没有标志的 ID,您应该根据 CAN_EFF_FLAG
位的值应用 CAN_SFF_MASK
或 CAN_EFF_MASK
。
示例代码:
//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));
我用C写了一些代码来读取CAN总线数据。当我读取 11 位 CAN ID 时一切正常。一旦我尝试读取 29 位 ID,它就会错误地显示 ID。
示例:
正在接收带有 29 位 ID 的消息:
0x01F0A020
并用
打印printf("%X\n", frame.can_id);
它打印 81F0A020。
11 位 ID 信息
0x7DF
并用
打印出来printf("%X\n", frame.can_id);
打印正确 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, '[=12=]', 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, '[=12=]', 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;
}
struct can_frame
的字段 can_id
包含 CAN ID 和 EFF/RTR/ERR 标志。扩展ID有29位,所以有3个空闲位用来表示3个flags。
您的示例 ID 0x01F0A020
必须是扩展帧,但 ID 0x7DF
可以作为基本帧或扩展帧发送。这些是不同的消息。要区分具有相同 ID 的基本框架或扩展框架,您需要 EFF 标志。
在您的示例中,您会看到值 0x81F0A020
,它是 ID 0x01F0A020
和 CAN_EFF_FLAG
(0x80000000U
) 的组合。
摘自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 */
...
/**
* 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)));
};
要仅获取没有标志的 ID,您应该根据 CAN_EFF_FLAG
位的值应用 CAN_SFF_MASK
或 CAN_EFF_MASK
。
示例代码:
//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));