linux can接收数据出错,CAN为什么会发送失败

CAN总线调试过程中出现报文发送失败,很多工程师都对此只知其一不知其二,这里就CAN报文发送失败的问题我们来做一次探讨。

在了解CAN报文为什么会发送失败之前我们先看看一条正确的CAN报文到底应该是怎么样的,表1是一个正常标准数据帧的报文组成。

表 1 标准数据帧报文格式组成

ca4e25075b039f7c1a5886fd23686f04.png

4af5d4ea2309f269d6fbc191cdf7a959.png

图 1 标准数据帧格式

CAN总线是一种基于广播的通讯方式,为了保证总线上的每一个正常节点都能正确的接收到报文,报文的发送者要求每一个接收节点在报文发送结束前要作出应答,这也是报文里ACK存在的原因。

一帧CAN报文中ACK段长度为2个位,包含应答间隙(ACK Slot)和应答界定符(ACK Delimter)。在应答场里,发送站发送两个隐性位。当接收器正确地接收到有效的报文,接收器就会在应答间隙(ACK Slot)期间(发送ACK信号)向发送器发送一“显性”的位以示应答。

应答间隙:所有接收到匹配CRC序列(CRC SEQUENCE)的站会在应答间隙(ACK Slot)期间用一显性的位写入发送器的隐性位来作出回答。

应答界定符:ACK界定符是ACK场的第二个位,并且是一个必须为隐性的位。因此,应答间隙(ACK Slot)被两个隐性的位所包围,也就是CRC界定符(CRC Delimter)和ACK界定符(ACK Delimter)。

cbaa292707055bf6422ee970d58a1718.png

图 2 正常ACK段报文

而如果总线上没有ACK应答(即应答间隙为隐性),发送器就会发送一个错误标志,并且发送错误计数器值加8,节点就会对报文进行自动重发,若自动重发依然收不到ACK,则在发送错误计数器计数满128后(即出现16帧错误帧),由错误主动转为错误被动状态,如图3所示。

那导致ACK段出错的原因有哪些呢?

· 总线上只有一个有效节点:发送报文的节点在发送出一帧报文后会检测总线上应答间隙的状态,如果检测到应答间隙为隐性位,则表示该帧报文没有得到ACK,发送失败,需要重发,而由于发送错误计数器会在发送失败后累加,直到该节点关闭。所以,当总线上只有一个有效节点时,这个节点是发不出去数据的,因为它所发出的数据帧中的ACK Slot没有另外一个节点来填充,将永远是隐性位,这个节点会一直重发数据直到发送成功或发送被取消。

· 波特率不匹配或者节点没有初始化,导致没有ACK;

· 总线线缆短路,断路,接反;

· 高速CAN总线上接的节点不是高速CAN,而是容错低速CAN,导致不匹配。

74877d32f2507c1826ae71e43b3053a1.png

图 3 应答界定符错误帧

f27147492cb9d7897a22af33fb9ab4bc.png

图 4 没有ACK的报文

当你在调试CAN总线时出现节点发送报文失败的情况时,一定要检查是不是以上几点疏漏导致你的总线上ACK异常。而借助恰当的仪器,可以在查找CAN总线错误时事半功倍。图4即采用致远电子的CANScope来对错误帧进行标记,同时找到错误帧对应的波形来查找出错误情况。CANScope还可以对CAN总线物理层、数据链路层、应用层做一系列的测试,为CAN工程师解决测试难题。

492d10174ea62b83b28e56bc5433c302.png

图 5 CANScope测试项目

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux上使用SocketCAN可以很方便地进行CAN数据的接收和发送。以下是一个简单的例子: ``` #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <net/if.h> #include <linux/can.h> #include <linux/can/raw.h> int main(int argc, char **argv) { int s; struct sockaddr_can addr; struct can_frame frame; struct ifreq ifr; const char *ifname = "can0"; // CAN接口名称 // 创建socketCAN套接字 s = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (s < 0) { perror("socket"); return 1; } // 设置CAN接口名称 strcpy(ifr.ifr_name, ifname); ioctl(s, SIOCGIFINDEX, &ifr); // 绑定CAN接口 addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(s, (struct sockaddr *)&addr, sizeof(addr)); // 接收CAN帧 while (1) { int nbytes = read(s, &frame, sizeof(frame)); if (nbytes < 0) { perror("read"); return 1; } printf("Received CAN frame: id=0x%x, data=%02x %02x %02x %02x %02x %02x %02x %02x\n", frame.can_id, frame.data[0], frame.data[1], frame.data[2], frame.data[3], frame.data[4], frame.data[5], frame.data[6], frame.data[7]); } // 发送CAN帧 memset(&frame, 0, sizeof(frame)); frame.can_id = 0x123; frame.can_dlc = 8; strcpy(frame.data, "HelloCAN"); write(s, &frame, sizeof(frame)); close(s); return 0; } ``` 上述代码使用了SocketCAN的API,首先创建了一个socketCAN套接字,然后绑定了CAN接口。接着,使用read函数接收CAN帧,使用write函数发送CAN帧。可以根据实际需求修改CAN帧的ID、数据等内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值