CAN总线报文数据一致性校验

电子说

1.3w人已加入

描述

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

通讯协议

对非法数据进行抛出。

来源:古德曼汽车工业

   审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分