主要内容

使用循环冗余校验检测 CAN 帧损坏

此示例说明如何使用循环冗余校验 (CRC) 检测 CAN 帧的有效负载中的损坏值。

数据完整性和标准化 CRC 算法

CRC 用于确保各种通信和内存操作中的数据完整性。该校验基于多项式除法;具体来说,有效负载的校验值是通过将有效负载位与生成器多项式进行多项式除法得到的余数。然后将校验值追加到有效负载并传输。在接收时,接收器根据接收到的有效负载计算校验值,并将其与报文中追加的值进行比较;相等的值在一定程度上表明传输未影响数据的完整性。

在汽车应用中,该校验以标准化的方式实现,以确保 ECU(电子控制单元)之间的一致性。例如,SAE J1850 CRC8 算法基于 8 位的校验值,以及形式为 x8+x4+x3+x2+1 的生成器多项式。

在 DBC 文件中定义的 CAN 帧中的 CRC 值

此示例仅处理所谓的应用层 CRC,它由应用层处理;此 CRC 与协议层 CRC 无关,后者通常由硬件或底层驱动管理。DBC 文件不对应用层 CRC 提供任何特殊处理。如果您需要在 CAN 报文中包含 CRC 校验值,则必须在报文中保留特定位,并将 CRC 定义为 DBC 文件中的常规信号。

该示例提供文件 CAN_OBD2_CRC.dbc,该文件定义一条长度为 8 个字节的报文,在前 3 个字节中携带三个 OBD 信号,并在第八个字节中携带一个 CRC 信号。

db = canDatabase("CAN_OBD2_CRC.dbc");
struct2table(db.MessageInfo.SignalInfo)
ans=4×20 table
          Name            Comment      StartBit    SignalSize       ByteOrder        Signed     ValueType       Class      Factor    Offset    Minimum    Maximum      Units        ValueTable     Multiplexor    Multiplexed    MultiplexMode     RxNodes      Attributes    AttributeInfo
    _________________    __________    ________    __________    ________________    ______    ___________    _________    ______    ______    _______    _______    __________    ____________    ___________    ___________    _____________    __________    __________    _____________

    {'Accelerator'  }    {0×0 char}        8           8         {'LittleEndian'}    true      {'Integer'}    {'int8' }      1         0        -100        100      {0×0 char}    {0×1 struct}       false          false             0          {0×1 cell}    {0×0 cell}    {0×0 struct} 
    {'CRC'          }    {0×0 char}       56           8         {'LittleEndian'}    false     {'Integer'}    {'uint8'}      1         0           0          0      {0×0 char}    {0×1 struct}       false          false             0          {0×1 cell}    {0×0 cell}    {0×0 struct} 
    {'Engine_Torque'}    {0×0 char}        0           8         {'LittleEndian'}    false     {'Integer'}    {'uint8'}      1         0           0        100      {'rpm'   }    {0×1 struct}       false          false             0          {0×1 cell}    {0×0 cell}    {0×0 struct} 
    {'Vehicle_Speed'}    {0×0 char}       16           8         {'LittleEndian'}    false     {'Integer'}    {'uint8'}      1         0           0        100      {'km/h'  }    {0×1 struct}       false          false             0          {0×1 cell}    {0×0 cell}    {0×0 struct} 

计算 CRC 值并在传输前更新报文

CRC 值是根据有效负载计算的,而有效负载又取决于信号值。在 Simulink® 中进行处理的一种方法是在将 CRC 的输入值保持为零的情况下应用报文的常规编码,然后在打包后将 CRC 值添加到报文中,如下图中所示。

此图是模型 detectCANFrameCorruptionUsingCRC 的一部分,该模型使用如下代码中所示的采样时间、停止时间和输入数据进行参数化。

open_system("detectCANFrameCorruptionUsingCRC.slx");
Ts = 0.1;
StopTime = 10;
load("input_data.mat");

输入数据通过 Inport 模块定义,因此输入信号在 Configuration Parameters 中指定,如下所示:

CAN Pack 模块配置为输出类型为 CAN_MESSAGE_BUS 的总线。MATLAB® 函数 AddCRCToPayload 期望对应的总线类型,并通过根据前 7 个字节计算 CRC 值并将其添加到第 8 个字节来修改总线中的 Data 字段。可以使用以下调用在 MATLAB 工作区中创建总线对象:

canMessageBusType;

MATLAB 函数 AddCRCToPayload 中的校验值计算遵循标准方法;有关示例,请参阅执行循环冗余校验

此处提出的架构仅用于说明目的。特别是,在更贴近实际应用的场景中,AddCRCToPayload MATLAB Function 模块与 DBC 文件之间的耦合可以通过将这两个模块放在一个封装子系统内并根据子系统封装回调中 DBC 文件的内容对其进行参数化来管理。

报文的完整性检查

在接收时,MATLAB 函数 CheckCRC 根据有效负载计算校验值,如果该值等于报文中接收到的值,则返回 true。此布尔输出反过来用于有条件地控制 CAN Unpack 模块,因此如果检测到无效的 CRC 值,将中断传入报文的处理。

传输故障的仿真

CAN Transmit 和 CAN Receive 模块在配置为使用虚拟通道时会使用软件缓冲区对传输进行仿真,这不会损坏数据。可以通过在传输的信号报文中引入自定义故障对数据损坏过程进行仿真。

在此示例中,故障作用于 MATLAB 函数 AddCRCToPayload 的输出,并在仿真时间 t = 5 秒处激活。

故障模型图存储在模型 detectCANFrameCorruptionUsingCRC_FaultModel 中,可从 Simulink Fault Analyzer™ 界面访问,如下图所示。有效负载损坏使用 MATLAB 函数 corruptByte2 实现,该函数在有效负载的字节 2 中翻转一个随机位。

Simulink Fault Analyzer 使用额外的 XML 文件 detectCANFrameCorruptionUsingCRC_faulInfo.xml 来存储故障元数据。

在模型仿真后,对信号的检查显示,Conditional CAN Unpack 子系统的输出与(量化后的)输入在 5 秒之前匹配,然后由于 CRC 无效而降为零(默认值)。CAN Unpack 模块的输出被量化,因为 DBC 文件为信号的表示指定 8 位整数。

在上图中,CheckCRC 信号显示比故障触发器延迟一个时间步。这是由于模型中模块的执行顺序:在给定时间,CAN Receive 模块在 CAN Transmit 模块之前执行。特别是,在 t = 0 秒处,CheckCRC 信号为 false,因为 CAN Transmit 模块尚未传输任何内容,而 CAN Receive 模块接收到一个空帧,其 CRC 不匹配;正确的帧会在下一个时间步接收到。同样,当故障在 t = 5 秒处触发时,CheckCRC 信号在 t = 5.1 秒处更新,即比故障触发时间晚一个时间步。