我们先简单总结一下CAN的错误处理与故障界定:
1.CAN控制器记录发生在发送/接收过程中,总线数据出现错误的总数(位错误,CRC错误等)。
2.CAN控制器根据总线出错数量由低到高,依次处于主动错误状态,被动错误状态,以及总线关闭状态。
3.位于主动错误的节点,在检测到错误时,可以发送主动错误标志(6位显性位),告知总线上所有节点发生了总线的错误,之后进行正常的收发操作。保证如果总线CAN_H与CAN_L出现短路等会影响整个总线通讯的问题时,各个控制器会迅速反应。
当随着发送/接收错误总数的增加,节点将位于被动错误状态,当检测到总线发生错误的时候,将等待总线出现被动错误帧(连续6位隐性位),之后才可正常进行收发操作。保证如果总线因为线长或者节点数增大,远处的节点干扰严重,则干扰严重的节点将不会影响其余节点的正常通信。
如果发送错误总数达到了255,则进入bus-off状态,处于这种状态的节点将会与总线隔离,直到检测到128 次出现11 个连续“隐性”位后,才可以恢复错误主动状态,错误计数器 也清零。
1.Linux SocketCAN
Linux 4.17.0-RC6 内核网络部分增加了SocketCAN,用于Linux的CAN协议的一种实现。以前的嵌入式开发板的CAN驱动是以基于字符串设备的驱动注册到内核中,新的内核使用Berkeley套接字API,Linux网络堆栈并将CAN设备驱动程序作为网络接口来实现。CAN套接字API的设计与TCP / IP协议尽可能相似,以便熟悉网络编程的程序员轻松使用CAN套接字进行CAN通信。具体详细的特性介绍详见https://www.kernel.org/doc/html/latest/networking/can.html。其中章节“Network Problem Notifications”中介绍了一种CAN总线错误的记录机制,SocketCAN将所有总线上的错误包装成一个“错误帧”,注意这块的“错误帧”不是CAN总线上实际跑的错误帧,而是驱动部分将控制器或者总线上的检测的错误,包装成一个CAN帧,上报给基于网络层之上的用户程序。
我们在linux4.9内核E:\linux-4.9\linux-4.9\drivers\net\can目录下的Makefile中看到内核支持了包括SJA1000,以及赛灵思的开发板的CAN驱动支持,我们比较关心的CAN错误的定义呢,在E:\linux-4.9\linux-4.9\include\uapi\linux\can中定义了所有CAN总线可能上报给用户层的错误信息具体的关系图如下:
三个能展开的三级目录分别如下:
这其中我们举个例子来说明这个伪造的错误帧是如何产生的,以AT91的驱动为例,下面的描述均来自内核源码。首先在设备的Open函数处注册了中断处理函数at91_irq,函数在发生终端时候,判断中断类型为错误中断,调用at91_irq_err处理设备错误信息,细分中断源来定义当前新状态为bus-off、报警、主动错误状态还是被动错误状态,如果状态发生了变化,则调用at91_irq_err_state形成错误帧进行上报。假如这个时候如果原状态为主动错误,而新的状态为RX/TX错误计数达到报警状态,则会更新设备当前状态,并向上层监听端口的用户程序发送一个表示控制器错误-->RX/TX错误计数达到报警状态的错误帧。用户程序就可以知道CAN总线发生了这样的错误,并检查干扰源。
2.STM32F10x bxCAN
工业现场的总线上一般有两种设备,一种为普通的can节点设备,他们在整条总线上按需分布,反馈一些即时的信息(传感器信息或者摁键等),另一种设备一般一根CAN总线上就一台这样的设备,它负责将CAN总线上的数据转发到以太网接口,WIFI,或者zigbee等通信接口,或者它本身带有屏幕,显示各个节点上报的信息并进行统一的控制。这种类似‘网关’的设备一般会上嵌入式实时操作系统,或者linux内核裁剪一下拿QT做做界面,或者直接就安卓了。
反观线上多数的设备,一般为了压低成本等原因,会采用STM32来进行开发,而总线上的状态,往往是这些处于总线远端的设备能更好的体现,并且出问题的设备也大概率会是这些设备,但是这些设备往往没有实时操作系统,业务开发起来比较缓慢且不易多人维护,导致往往对于异常的处理不足,关注实现往往大于功能实现的效率以及质量。而其更没有Linux比较完善的官方驱动支持,如上文一样可以给用户程序主动报一些总线上的错误。所以,基于STM32开发CAN的时候,更应该借鉴Linux的驱动实现方式,对总线上的错误进行记录,方便查询。
我们首先来分析一下STM32 bxCAN的错误中断源:
由上图可知,ERRIE为错误中断的总使能位,EWGIE为错误警告中断使能,当接收/发送的错误数到达报警标准之后触发此中断,EPVIE为错误被动中断使能,当接收/发送的错误数到达被动错误标准时触发此中断,BOFIE为离线中断使能,当接收/发送的错误数到达离线标准时触发此错误,LECIE为上次错误号中断使能,当接收/发送出现错误的时候,且与上次错误不同,触发此中断,错误号根据手册能表示一下错误:000: No Error,001: Stuff Error,010: Form Error,011: Acknowledgment Error,100: Bit recessive Error,101: Bit dominant Error,110: CRC Error,111: Set by software。
在发送过程,bxCan有三个发送mailbox,STM32的库函数CAN_Transmit负责将数据放到mailbox中并触发发送(若没有空闲的mailbox则返回错误),由手册可知,可根据TME来判断mailbox是否可能,库函数CAN_TransmitStatus封装好能够直接获得当前mailbox的状态,这里建议对CAN控制器的CAN_NART配置为DISABLE,使能报文重传功能,这样报文如果发送失败,将在SCHEDULED和TRANSMIT两个状态切换,直到发送完毕,才会释放邮箱。除非你的应用需要报文发送的准确时间点进行记录,并且你的应用实时性要求也不高,能够腾出时间去处理报文重传(如果你把CAN控制器的报文重传功能去掉了,那你必然要自己实现)。从这个流程中可以看出,我们可以将发送溢出(邮箱占满),以及发送仲裁丢失作为控制器的错误记录下来。
在接收过程中,就有接收溢出的中断可供记录接收溢出错误。
综上所述,STM32F10x bxCAN提供的寄存器能够满足类似Linux的除了收发器其余的所有错误记录,通过库函数能够很方便的将CAN控制器的状态变化以及总线上的错误记录下来,从而可以分析现场CAN总线的状态。