电子说
01不安全的CAN总线
1、如何保证ECU接收到的数据是合法的?
比如ECU1接收ECU2发出的一帧0x100报文,协议层是不会区分是ECU1发的,还是非法接入OBD的CAN盒发的?如果ECU1接收到非ECU2发出的0x100报文岂不是很危险?
2、ECU如何知道另一个节点挂死
比如ECU1还是接收ECU2发出的一帧0x100报文,但是由于某些神秘原因(程序跑飞了)导致ECU2挂死或者掉线,那ECU1如何知道此时的接收到0x100无效?
CAN通讯是一种广播形式的通讯方式,自然协议层是无法做到数据合法性的校验,这部分工作需要应用层来完成。由此就出现了RollingCounter与Checksum。这两个东西好像也是功能安全的一部分,本期只是介绍这两个东西的原理,不对功能安全做过多讨论。
一个规范的CAN矩阵协议,每一帧报文都会要求有这个两个信号。如上图,从bit52到bit55是RollingCounter,取值范围0~15,ECU没发一次自动累加,满15就归零。从bit56到bit63(byte8)都是Checksum,取值范围0x00~0xFF,用来表示该条报文的校验值。
对于判断发送报文ECU有没有挂死很简单,只要在接收端对RollingCounter进行判断,几个周期内协议栈上的buff没有得到更新则就能判断为节点异常。
对于报文合法性的判断则就要负责得多,且会有各种花样
车企制定的通讯协议时,除了制定矩阵信号外,会规定Checksum的计算方法,比如这个是采用多项式的CRC校验算法。
并且规定出需要校验的数据,有的是对矩阵的前7个byte进行校验,有的则要增加报文ID作为校验计算的输入,各种花样都有。
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * CRC8校验子程序0x1D(x8+x4+x3+x2+1) * * * * * * * * * * * * * * 参数1,uint8_t *data:需要计算的数据 * * * * * * * * * * * * * 参数1,uint16_t len:需要计算的数据字节长度 * * * * * * * * * * 返回值,uint8_t crc8:计算出的CRC值 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ uint8_t crc_8find(uint8_t *data, uint16_t len) { uint8_t crc8 = 0x00; while( len-- ) { crc8 = crc_table[crc8 ^ *data]; data++; } return crc8; }
甚至贴心的给出校验计算的伪代码,这函数写法C、C++应该都可以直接运行的。理解下上面的代码!
当ECU2发出0x100之前,会对RollingCounter累加,并且通过计算包含RollingCounter的前七个字节的CRC值,填充到数据域的第8个字节;
ECU1接收到0x100的时候使用同样的校验算法,计算数据域的前7个字节,并且与第8个字节发送源计算的CRC值进行比较。如果两者相同,则数据合法。
02CANOE仿真
接下来在CANOE的CAPL进行RollingCounter与Checksum的模拟
创建两个ECU节点,ECU1为发送节点,ECU2为接收节点。ECU2会对ECU1的节点做Checksum,如果非法数据会在log窗口中打印出来。ECU1会而这个IG是用来模拟非法的数据发送。
int GetCrcChecksum (int crc_position ,message *data) { byte checksum; byte bitIndex; byte byteIndex; byte tdata; checksum = 0x00; for (byteIndex = DBLookup(data).dlc; byteIndex >= 1; byteIndex--) { if(byteIndex-1 != crc_position) { tdata = data.byte(byteIndex-1); } else { tdata = 0; } checksum ^= tdata; for (bitIndex = 0; bitIndex < 8; bitIndex++) { if ((checksum & 0x80) != 0) { checksum = (checksum << 1) ^ 0x1D; // cb_CRC_POLY: 0x1D } else { checksum = (checksum << 1); } } } checksum &= 0xFF; return (checksum); }
CAPL计算Checksum的函数,在ECU1与ECU2里都存在
on timer Timer1 { Req.rollingCounter_0x100=LiveCount; Req.checksum_0x100=GetCrcChecksum(7,Req); output(Req); setTimer(Timer1,20); LiveCount=LiveCount+1; if(LiveCount==16) { LiveCount=0; }
ECU1的定时器函数,发送前调用GetCrcChecksum函数生成Checksum
on message RCTest1 { if(this.checksum_0x100==GetCrcChecksum(7,this)) { Rep=this; }else { write("Invaild Message"); } }
ECU2接收事件中重新计算Checksum跟发送报文里的Checksum进行比较,不同则抛出错误
按F2,开始仿真,可以监控到ECU1发送出来的rollingCounter与Checksum
在Write窗口中打印ECU2接收到的数据
在IG节点设置一个非法的报文,rollingcounter与Checksum都设置0
对非法数据进行抛出。
来源:古德曼汽车工业
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !