目录
前言
一、OSEK_TP.dll
1. 加载 OSEK_TP.dll 文件
2. 常用函数介绍
3. 回调函数介绍
4. 修改诊断字节未使用的默认值
5. 修改流控帧参数
二、CANoeILNLVector.dll
1. 加载 CANoeILNLVector.dll 文件
2. IL 层控制函数
3. IL 层故障注入
4. 仿真 Counter 和 CRC
1. applILTxPending 函数
2. 定时器实现
总结
前言
CANoe 软件通过内置的模型库进一步拓展了 CANoe 的功能,每个模型库都是由一系列函数组成的,封装成一个 DLL 文件。用户可以在 CANoe 软件或者 CAPL 程序中调用这些 DLL,从而实现特定的功能。不同的总线类型,不同的协议类型,有不同的模型库,常用的总线标准库有 CANoeIL,OSEK NM, AUTOSAR NM,OSEK TP,SOME IP IL,DoIP等,本文主要讲述 OSEK TP 和 CANoeIL。
一、OSEK_TP.dll
在不用诊断描述文件的情况下,用户可以基于 CANoe 内置的 OSEK_TP.dll 动态链接库文件实现 CAN 诊断通信,该 DLL 内封装了大量的于 CAN 传输协议相关的函数。
1. 加载 OSEK_TP.dll 文件
用户可以在网络节点配置的 Components 选项中加载 OSEK_TP.dll 文件,如下:
常用的 dll 文件的路径,都在 CANoe 软件安装目录下的 Exec32 文件夹中,默认路径是:C:\Program Files\Vector CANoe 15\Exec32;
加载成功之后,能够在 CAPL Functions 窗口看到 osek_tp.dll 的函数库:
2. 常用函数介绍
long CanTpCreateConnection(dword addrMode):根据地址模式创建一个连接,如果入参是 0, 表示使用标准帧,如果入参是 1,表示使用扩展帧;
long CanTpSetRxIdentifier(long connHandle, dword rxID):设置用于接收协议数据消息的 CANID;
long CanTpSetTxIdentifier(long connHandle, dword txID):设置用于发送协议数据消息的CANID;
long CanTpSetMaxCANFDFrameLength(long connHandle, dword maxCANFDFrameLength):如果是 CANFD 总线,建立通信前调用这个函数,传入参数的最小值是 8,最大值是 64;
long CanTpSendData(long connHandle, byte txData[], dword txSize):发送诊断数据,如果接收到的 ECU 响应数据是多帧数据,接收到首帧之后就会自动发送流控帧。
示例代码:
variables { long dbHandle; } on key 'a' { byte txDataBuffer[8] = {0x19, 0x04, 0x00, 0x00, 0x01, 0x01}; long i; CanTpSendData(dbHandle, txDataBuffer, 6); } on start { initConn(0x613, 0x693); } void initConn(dword txId, dword rxId) { dbHandle = CanTpCreateConnection(0); // 0 表示标准帧, 1 表示扩展帧 CanTpSetTxIdentifier(dbHandle, txId); CanTpSetRxIdentifier(dbHandle, rxId); CanTpSetMaxCANFDFrameLength(dbHandle, 64); }3. 回调函数介绍
用户在使用 OSEK_TP.dll 进行通信时,必须在 CAPL 程序中定义回调函数,来反馈发送诊断数据和接收数据。
void CanTp_ErrorInd(long connHandle, long error):连接发生错误时,触发该函数;
void CanTp_FirstFrameInd(long connHandle, dword length):诊断数据发送第一帧成功后,会调用该函数;
void CanTp_PreSend(long handle, word msgDLC[], byte data[]):在每次预定发送 CAN 消息时,都会调用该函数;
void CanTp_ReceptionInd(dword connHandle, dword count):诊断数据发送完毕且成功,会调用该函数;
void CanTp_TxTimeoutInd(long handle, word dlc[], byte msgData[]):如果 CAN 消息不能在超时时间 Ar 或 As 内发送,则调用该函数;
示例代码:
// 回调函数 void CanTp_SendCon(long connHandle, dword count) { write("Transmission of %d byte on connection %d successful", count, connHandle); }4. 修改诊断字节未使用的默认值
long CanTpGetPadding(long connHandle):获取默认填充字节;
long CanTpSetPadding(long connHandle, long paddingValue):设置默认填充字节;
5. 修改流控帧参数
long CanTpSetBlockSize(long connHandle, dword blockSize):设置 BS 参数;
long CanTpSetSTmin(long connHandle, dword STmin):设置 STmin;
二、CANoeILNLVector.dll
DBC 文件中定义的报文和信号发送到总线(CAN Bus)之前,要经过交互层,用户通过 CAPL 程序对报文和信号进行读写操作,也需要经过交互层。因此用户可以通过交互层模型库 CANoeILNLVector.dll 观察总线上的数据变化,也可以根据需要控制报文的发送,修改信号数值,设置报文发送周期等。
1. 加载 CANoeILNLVector.dll 文件
如果在 DBC 文件中配置了 NodeLayerModules 为 CANoeILNLVector.dll,则可以在模块的 Components 看到自动加载的库文件:
2. IL 层控制函数
CANoeILNLVector.dll 中集合了丰富的函数,包括控制、故障注入、报文处理、信号处理等函数;
网络节点函数:
ILControlInit():初始化 IL 层,只能在 on preStart 中调用该函数,调用后周期报文发送停止,信号不可设置;
ILControlStart():报文开始周期发送,可设置信号值;
ILControlStop():报文停止周期发送,不可设置信号值;
ILControlWait():报文停止周期发送,可设置信号值;
ILControlResume():重新开始周期发送报文;
这些函数在哪个节点下,就控制哪个节点的报文,不需要传参;
on preStart { ILControlInit(); writeLineEx(-3, 0, "init IL layer, no message was sent."); } on key 'a' { ILControlStart(); writeLineEx(-3, 0, "start sending engine message."); } on key 'b' { ILControlStop(); writeLineEx(-3, 0, "stop sending engine message."); }测试节点函数:
ILNodeControlStart():报文开始周期发送,可设置信号值;
ILNodeControlStop():报文停止周期发送,不可设置信号值;
ILNodeControlWait():报文停止周期发送,可设置信号值;
ILNodeControlResume():重新开始周期发送报文;
void MainTest() { writeLineEx(-3, 0, "ILNodeControlStop: stop light node message."); ILNodeControlStop("Light"); testWaitForTimeout(200); writeLineEx(-3, 0, "ILNodeControlStart: start light node message"); ILNodeControlStart("Light"); testWaitForTimeout(200); }3. IL 层故障注入
网络节点函数:
long ILFaultInjectionDisableMsg(dbMsg msg):禁止发送报文;
long ILFaultInjectionEnableMsg(dbMsg msg):开始发送报文;
long ILFaultInjectionResetMsgCycleTime(dbMsg msg):重置报文周期为 DBC 中定义的值;
long ILFaultInjectionResetMsgDlc(dbMsg msg):重置报文 DLC 为 DBC 中定义的值;
long ILFaultInjectionSetMsgCycleTime(dbMsg msg, double value):设置报文周期;
long ILFaultInjectionSetMsgDlc(dbMsg msg, dword dlc):设置报文 DLC;
long ILFaultInjectionResetAllFaultInjections():重置所有的故障注入设置;
on key 'c' { writeLineEx(-3, 0, "stop sending message EngineState"); ILFaultInjectionDisableMsg(lookupMessage("EngineState")); } on key 'd' { writeLineEx(-3, 0, "start sending message EngineState"); ILFaultInjectionEnableMsg(lookupMessage("EngineState")); } on key 'e' { writeLineEx(-3, 0, "set message EngineState cycle time to 500ms"); ILFaultInjectionSetMsgCycleTime(lookupMessage("EngineState"), 500); } on key 'f' { writeLineEx(-3, 0, "reset message EngineState cycle time to 100ms"); ILFaultInjectionResetMsgCycleTime(lookupMessage("EngineState")); } on key 'g' { writeLineEx(-3, 0, "set message EngineState dlc to 8"); ILFaultInjectionSetMsgDlc(lookupMessage("EngineState"), 8); } on key 'h' { writeLineEx(-3, 0, "reset message EngineState dlc to 2"); ILFaultInjectionResetMsgDlc(lookupMessage("EngineState")); } on key 'i' { writeLineEx(-3, 0, "reset all fault injection conditions"); ILFaultInjectionResetAllFaultInjections(); }测试节点函数:
long TestDisableMsg(dbMsg msg):禁止发送报文;
long TestEnableMsg(dbMsg msg):开始发送报文;
long TestResetMsgCycleTime(dbMsg msg):重置报文周期为 DBC 中定义的值;
long TestResetMsgDlc(dbMsg msg):重置报文 DLC 为 DBC 中定义的值;
long TestSetMsgCycleTime(dbMsg msg, float newCycleTime):设置报文周期;
long TestSetMsgDlc(dbMsg msg, dword dlc):设置报文 DLC;
long TestResetAllFaultInjections(dbNode aNode):重置所有的故障注入设置;
testcase case2() { writeLineEx(-3, 0, "stop sending message EngineState"); testDisableMsg(lookupMessage("EngineState")); testWaitForTimeout(1000); writeLineEx(-3, 0, "start sending message EngineState"); testEnableMsg(lookupMessage("EngineState")); testWaitForTimeout(1000); writeLineEx(-3, 0, "set message EngineState cycle time to 500ms"); testSetMsgCycleTime(lookupMessage("EngineState"), 500); testWaitForTimeout(1000); writeLineEx(-3, 0, "reset message EngineState cycle time to 100ms"); testResetMsgCycleTime(lookupMessage("EngineState")); testWaitForTimeout(1000); writeLineEx(-3, 0, "set message EngineState dlc to 8"); testSetMsgDlc(lookupMessage("EngineState"), 8); testWaitForTimeout(1000); writeLineEx(-3, 0, "reset message EngineState dlc to 2"); testResetMsgDlc(lookupMessage("EngineState")); testWaitForTimeout(1000); writeLineEx(-3, 0, "reset all fault injection conditions"); testResetAllFaultInjections(lookupNode("Engine")); testWaitForTimeout(1000); }4. 仿真 Counter 和 CRC
CAN 总线上,两个节点通信,假设 A 节点给 B 节点发送一帧重要的报文,除了必要的信号数据,还会发送 Counter 和 CRC 信号。B 节点会根据约定的加密算法计算 CRC 和 Counter,并且与接收到的值进行比对,如果比对失败,则认为 A 节点发送的信号被篡改,将会上报故障。
1. applILTxPending 函数
CANoe 会根据 DBC 的定义,按照默认值发送仿真报文。仿真报文被发送到总线之前会经过回调函数 applILTxPending,用户可以在这个函数中计算报文的 CRC 和 Counter,然后将报文发送到总线上。
回调函数的实现思路:
根据定义好的算法,计算 Counter 和 CRC,将 计算好的值填充到报文的指定位置;
dword applILTxPending(long aId, dword aDlc, byte data[]) { if(aId == 0x100) { byte payload[64]; int i; byte tempCounter; tempCounter = data[1] & 0x0F; // 假设第1字节的低4位表示 counter 信号 tempCounter = (tempCounter + 1) % 16; data[1] = (data[1] & 0xF0) | tempCounter; for(i = 1; i < aDlc; i++) // 假设第0字节表示CRC { payload[i - 1] = data[i]; } data[0] = crc8(payload, aDlc - 1); } return 1; }下面提供一种 CRC 的实现方式,具体的实现方式需要结合企业中实际的定义:
byte crc8(byte data[], long length) { byte tCRC; long i, j; tCRC = 0xFF; for(i = 0; i < length; i++) { tCRC ^= data[i]; for(j = 0; j < 8; j++) { if((tCRC & 0x80) != 0) { tCRC <<= 1; tCRC ^= 0x1D; } else { tCRC <<= 1; } } } return ~tCRC; }2. 定时器实现
实现思路:
先初始化报文的 ID,channel等,再使用定时器持续发送报文。发送之前根据约定好的算法,先计算 counter 和 CRC,再填充到报文的指定位置,最后发送报文。
因为 CAPL 中定义的变量都是静态变量,因此每次发送之后变量的值都会保持上次值,而不需要通过回调函数获取到上次发送的值,因此能够实现。
variables { msTimer msgTimer; byte counter; dword msgId; byte channel; } on key '1' { msgId = 0x100; channel = 1; setTimerCyclic(msgTimer, 500); } on timer msgTimer { byte i; message * msg; byte simMsg[8]; msg.id = msgId; msg.can = channel; msg.DataLength = 8; msg.FDF = 0; counter = (counter + 1) % 16; simMsg[0] = counter; msg.byte(1) = counter; msg.byte(0) = crc8(simMsg, 7); output(msg); }总结
本文介绍了 CANoe 两个常用模型库的用法:OSEK_TP.dll 用于在没有诊断描述文件时实现 CAN 诊断通信,核心是建立连接、设置收发 ID 并发送数据,同时实现必要的回调函数;CANoeILNLVector.dll 工作在交互层,可用于控制报文发送、注入故障,并通过applILTxPending回调或定时器仿真 Counter 和 CRC。
掌握这两个库的函数与回调机制,即可快速搭建 CAN 诊断通信与交互层仿真测试环境。