can总线rollingcounter_CAN总线错误分析与解决
CAN总线错误分析与解决
背景
写这篇文章是因为我看到网上介绍CAN总线错误处理的文章,清一色的都是生搬照抄教科书或是数据文档的内容,特别是国内很难找到一些有价值的内容,这让一些真正有需要的人很苦恼,包括我自己。这篇不打算对CAN的错误处理机制做进一步的探讨,而是从实际工作中碰到的具体问题来分析一些常见的CAN总线错误和解决办法。
CAN节点数据收发过程
了解CAN节点在总线上数据上的收发过程很重要,之前的一篇文章讲解了一些CAN总线的错误处理机制,但是那些都是理论上的东西,如果不深入了解CAN总线上的数据收发过程,理解那些理论的东西难免有些晦涩。
我们知道CAN总线上的每个节点往总线上发送数据的同时会同时读取总线上的数据,并与自己发送的数据作对比。
CAN信息发送成功后,在这个间隙内,接收节点可以准备要回复的信息,也就是把应答场填充为显性0,在发送时其为隐性1应答过程可能如下:当信息传输到ACK前的Del时,可以认为信息已经传输完毕,接收节点也接收到了足够的信息来检测接收的信息是否正确,所以这时接收节点就会检测信号是否正确,如果正确,就将ACK置位为显性0,注意这时,发送节点因为还在发送而接收节点又将ACK信息置位为1,所以它就会在回读时检测到ACK为0,判断接收成功。注意:这其中有个接收节点用显性覆盖隐性---覆盖ACK位的过程,覆盖+回读。
ACK前后各加一个Del,就是为了考虑到时间误差,让接收节点有足够的时间对ACK确认。这个过程说明,CAN发送是个双向互动的过程,发送节点一边发送,一边对节点进行回收确认数据正确,而接收节点也时刻接收,并在正确的时间将ACK设置为1。
CAN总线错误
CAN总线错误分别有发送和接收错误计数,计数达到一定的累计以后就会产生CAN BUS OFF, 这说明CAN总线上出现了严重的错误。如下图CAN总线产生错误后的状态转换机制:
如果出现了BUS OFF,总线上的节点需要做一些动作,例如重启CAN控制器或是重新上电,但是这些都只是一些补救措施,最根本的还是需要找到引起BUS OFF的根源。
CAN总线分析的一些工具和文档:
CAN分析仪或者逻辑分析仪
数字示波器
相关的软件debug工具
CAN控制器芯片数据手册,这很重要
硬件电路图
CAN协议文档
相关版本的Linux内核源码
CAN节点发送错误不成功
问题描述与分析
挂载在CAN总线上的一个节点向总线上发送数据不成功,用逻辑分析仪也看不到任何波形。PS: 这应该是我碰到的最坑爹的事情了。下面具体来看看怎么不成功。于是调试中断查看CAN_STATUS即CAN状态寄存器显示0xE5, 查看CPU数据手册:
CAN总线状态直接进入了BUS OFF状态,这意味着错误计数已经超限,查看CPU收发寄存器的收发错误计数显示发送错误计数TEC达到248, 接收错误计数为0;这很明显,数据压根没有发送到总线上。
再进一步查看寄存器值LEC即LAST ERROR CODE 最后一个错误代码, 显示是BIT0 ERROR:
查看上面的错误代码表可知,BIT0错误也就是在发送数据期间,虽然CAN节点设备想要发送一个显性位,也就是逻辑0,但是CAN总线同时监听到总线上的数据位为隐性位,即逻辑1。这意味着CAN core往总线上发送的数据第一位就已经出错了,压根没有将数据经过CAN收发器传送到CAN总线上。
一直在使用CAN总线的我厂和我从来没遇到这等奇事,但是由于是新的CPU的开发所以在怀疑硬件的问题的同时也在排查软件问题,但是经过一阵排查,没有发现软件上的问题。回头再分析硬件,又经过一阵排查溯源,发现CPU的CAN收发线与CAN收发气的收发线接反,直接崩溃(PS: 硬件的大哥你能不能不要坑小弟):
总结
CAN节点发送数据不成功,首先分析是不是CAN控制器本身的问题,查看CPU中的CAN core的状态寄存器,分析是否有BUS OFF, 如果存在BUS OFF, 则进一步查看具体的错误信息,是主动的错误还是被动的错,发送错误计数有没有超限,最后一次发生的错误状态是什么,查看是位填充错误还是格式错误等其他错误,然后具体问题具体分析。这种错误一般是有硬件发送线路出现问题引起,例如光隔次边不导通,发送接口接触不良等,再则是一些奇葩的错误,例如本例,收发线直接接反了,坑爹啊!
CAN Socket 的CAN节点检测到错误帧
问题描述
我们看到以下的CAN Socket日志,在38秒内的三个错误帧,但是并没有引起总线的BUS OFF,这说明总线上检测到了错误,有可能受到了干扰,也有可能是数据发送太密集导致的总线过载,但是在这38秒内出现错误,但是期间又恢复正常。
CAN ID : 0x20000004 = 10 0000 0000 0000 0000 0000 0000 0100, 即仲裁域的值。
Linux内核源码分析
因为出现此错误的是我厂的CAN控制器CPU TI 公司的AM3352, 内核版本为Linux 3.2.0
所以我们通过内核来看内核CAN错误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 */
/* 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 */
/* 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 ID : 0x20000004 = 10 0000 0000 0000 0000 0000 0000 0100, 去除最高为的1(SOFZ帧起始位?),因为仲裁位是29位,所以应该是0 0000 0000 0000 0000 0000 0000 0100 =0x00000004,既不是CAN_ERR_BUSOFF也不是CAN_ERR_BUSERROR, 而是CAN_ERR_CTRL, 即CAN控制器的问题,而我们在看data[1]描述的CAN 控制器错误类型描述:
/* error status of CAN-controller / data[1] */
#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
/* error status of CAN-controller / data[1] */
#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
/* error status of CAN-controller / data[1] */
#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
我们再看我们截取的错误帧数据报文中显示data[1] = 0x04,如下图所示:
即具体错误为:
#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
也就是说CAN 控制器接收错误计数达到了警告的级别,需要提出警告,如果再这样下去CAN控制器就要过载了,甚至会引起总线的BUS OFF.
我们再回头看内核源码对此错误的处理:产生data[1] = CAN_ERR_CRTL_RX_WARNING 错误的内核源函数为:
static int ti_hecc_error(struct net_device *ndev, int int_status,
int err_status)
static int ti_hecc_error(struct net_device *ndev, int int_status,
int err_status)
static int ti_hecc_error(struct net_device *ndev, int int_status,
int err_status)
HECC也就是TI公司高速终端CAN控制器的简称,用以上的函数描述TI CAN core的错误处理,如下,我们可以看到也就是CAN控制器接收错误计数REC大于96的时候内核就会报此错误
if (int_status & HECC_CANGIF_WLIF) { /* warning level int */
if ((int_status & HECC_CANGIF_BOIF) == 0) {
priv->can.state = CAN_STATE_ERROR_WARNING;
++priv->can.can_stats.error_warning;
cf->can_id |= CAN_ERR_CRTL;
if (hecc_read(priv, HECC_CANTEC) > 96)
cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
if (hecc_read(priv, HECC_CANREC) > 96)
cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
}
hecc_set_bit(priv, HECC_CANES, HECC_CANES_EW);
dev_dbg(priv->ndev->dev.parent, "Error Warning interrupt\n");
hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR);
}
if (int_status & HECC_CANGIF_WLIF) { /* warning level int */
if ((int_status & HECC_CANGIF_BOIF) == 0) {
priv->can.state = CAN_STATE_ERROR_WARNING;
++priv->can.can_stats.error_warning;
cf->can_id |= CAN_ERR_CRTL;
if (hecc_read(priv, HECC_CANTEC) > 96)
cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
if (hecc_read(priv, HECC_CANREC) > 96)
cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
}
hecc_set_bit(priv, HECC_CANES, HECC_CANES_EW);
dev_dbg(priv->ndev->dev.parent, "Error Warning interrupt\n");
hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR);
}
if (int_status & HECC_CANGIF_WLIF) { /* warning level int */
if ((int_status & HECC_CANGIF_BOIF) == 0) {
priv->can.state = CAN_STATE_ERROR_WARNING;
++priv->can.can_stats.error_warning;
cf->can_id |= CAN_ERR_CRTL;
if (hecc_read(priv, HECC_CANTEC) > 96)
cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
if (hecc_read(priv, HECC_CANREC) > 96)
cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
}
hecc_set_bit(priv, HECC_CANES, HECC_CANES_EW);
dev_dbg(priv->ndev->dev.parent, "Error Warning interrupt\n");
hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR);
}
总结
出现这个错误警告的原因很可能是:
此CAN总线上有干扰,导致CAN控制器发生接收错误,CAN总线上的信号经过收发器转化为差分电平信号,此时信号容易受到外界干扰,这样容易使CAN控制器发生接收错误,接收错误寄存器接收错误计数累计到一定值后会报此错误,如果错误计数达到一定程度甚至会导致总线关闭也就是BUS OFF. 如果最终确认是由于干扰引起的错误计数累计,则应该排查干扰源,然后增加抗干扰措施。
此CAN节点经过消息滤波后仍然需要接收大量的消息,导致CPU中的CAN控制器接收出错,并且错误计数达到了错误警告的上限。但是庆幸的是总线仍然没有过载,总线还可以正常收发数据,没有引起BUS OFF。但是对于一个安全可靠控制系统,这样的警告是绝对不允许的。我们需要通过一些手段去避免这样的问题出现,例如降低总线数据并发量,降低总线负载。CAN总线设备离线与错误恢复
这种问题同样很诡异,但是似乎又是比较常见的问题,这样的问题出现的情况往往比较多,例如CAN节Power off也就是电断了,总线上也就肯定监听不到此CAN节点的心跳,或是CAN总线节点没有及时发送心跳,阻塞在任务处理里,又或是此CAN节点物理接线和总线断开,等等原因很多。
我这里要说的一种情况是我厂碰到的另一种问题。
问题描述
在整个系统重启后发现CAN总线上的某一个Cortex M0设备节点丢失,而其他的设备,也是同样M0架构的MCU和相同控制软件的设备则没有出现丢失的情况。
未完待续。
can总线rollingcounter_CAN总线错误分析与解决相关推荐
- stm32如何读取并口_STM32 的并口总线的所有接口类型解决方法
STM32 的并口总线的所有接口类型解决方法 STM32的并口总线支持NOR, SRAM, PSRAM,NAND接口,本文以PSRAM为例实现并口的同步非复用, 异步复用和异步非复用操作!以STM32 ...
- FANUC机器人发生SRVO-454 CPU总线失败报警时的解决办法
FANUC机器人发生SRVO-454 CPU总线失败 报警时的解决办法 原因:放大器内部的CPU总线数据中发生了异常 处理:需要更换伺服放大器 清除报警:需要重新通电 本人碰到过一次,当时的情况是:刚 ...
- 计组高分笔记:【06】计算机组成原理概述 「单总线 | 双总线 | 三总线 | 总线仲裁 | 总线标准 | 总线定时 | 同步定时 | 异步定时 | 半同步通信 | 分离式通信」
文章目录 1. 总线概述 1.1 基本概念 1.2 总线的分类及结点结构 1.2.1 系统总线结构 1.2.1.1 单总线结构(系统总线) 1.2.1.2 三总线方式(主存总线.I/O总线.DMA总线 ...
- 计算机组成原理,计算机系统总线,总线分类、特性、性能指标、结构以及总线控制,判优控制通信控制
文章目录 总线的基本概念 总线的分类 总线特性及性能指标 总线结构 总线控制 一.总线判优控制 二.总线通信控制 总线的基本概念 一.总线是连接各个部件的信息传输线,是各个部件共享的传输介质. 二.总 ...
- 总线控制(总线判优控制、总线通信控制)
文章目录 基本概念 总线判优控制 链式查询 计数器查询 独立请求方式 总线通信控制 同步通信 异步通信 不互锁方式 半互锁方式 全互锁方式 波特率 半同步通信(同/异步结合) 分离式通信 系列文章 基 ...
- 【车载开发系列】CAN总线通信---总线报文格式
[车载开发系列]CAN总线通信-总线报文格式 CAN总线通信---总线报文格式 [车载开发系列]CAN总线通信---总线报文格式 一.什么是ISO15765 二.ISO15765的目的 三.单帧传输的 ...
- 总线控制 总线基本概念
目录 https://blog.csdn.net/weixin_45792450/article/details/109314693 总线的概念 总线的特性 总线的分类 片内总线∶芯片内部连接各元件的 ...
- pcie总线与cpci总线_CPCI总线是什么含义?
CPCI简介 Compact PCI(Compact Peripheral Component Interconnect)简称CPCI,中文又称紧凑型PCI,是国际工业计算机制造者联合会(PCI In ...
- c语言程序一些常见的不足,C语言常见错误分析及解决方法
C.C语言的应用很广泛,越来越多的程序员希望能够学好C由于C语言的编译系统对语法的检查不如其他语言那么严格,因此C语.下面将C语言中初学者最常见的错误分析如下; 1 语句书写错误 由于大部分学生都是初 ...
最新文章
- Java继承Exception自定义异常类教程以及Javaweb中用Filter拦截并处理异常
- Spring学习五(JDBC支持)
- MySQL 在 LIMIT 条件后注入
- google浏览器javascript没反应_浏览器之导航这件小事
- redis(16)--sentinel
- jQuery DataTables
- Visual Studio Code 快捷键的设置
- Python的if条件语句(常用)当为多个判定条件时
- Springboot 自定义Tomcat默认Servlet 资源路径
- html5页面值在另一个页面反显,2009年上海市高校计算机等级考试(一级A卷)及理论题参考答案...
- 一个Web报表项目的性能分析和优化实践(七):性能监测工具JavaMelody
- 多对多表的创建方式 forms组件 session与cookie
- CF1041B Buying a TV Set
- findwindow\sendmessage向第三方软件发送消息演示
- 手输Unicode字符的方法
- Java HTML导出PDF (二)
- 【ROS1】LeGO-LOAM-BOR简洁复现过程
- vue手机适配媒体查询用法@media
- 连续十日票房日冠,《人生大事》带热了电影大盘!它凭何突出重围?
- 使用 HTML、CSS 和 JavaScript 的简单模拟时钟
热门文章
- 外卖红包天天领!最高可领取 20 块
- python游戏开发步骤_【原创】python制作游戏脚本之网游寻址及64位程序的模块遍历(视频已安排)...
- Python批量为不同文件夹下的文件更名
- Java核心技术卷一 -第五章:枚举类再认识
- 28. 学历与学识的区别
- Android 酷炫的3d立体圆柱动画效果实现
- 2018中国云MSP市场,还是要看Bespin Global的
- uni-app小程序echarts中tooltip被遮盖
- 计算机二级word文字处理参考样式怎么看,计算机二级Msoffice-文字处理答案(解题步骤)资料...
- 修改 Typora 主题样式