当ECU需要传输超过8字节的诊断数据时,单帧传输显然无法满足需求。这时,ISO 15765-2标准定义的多帧传输机制就成为了汽车电子工程师必须掌握的技能。本文将带您深入CAN-FD网络,通过OSEK_TP库函数的实战应用,完整拆解从连接建立到数据交付的全过程。
在深入代码之前,我们需要明确几个关键概念在协议栈中的位置。CAN-FD的诊断通信通常分为三层:
code复制[应用层诊断服务]
|
[传输层(ISO 15765-2)]
|
[数据链路层(ISO 11898)]
|
[物理层(CAN/CAN-FD)]
OSEK_TP.dll作为传输层的实现,主要解决了以下核心问题:
使用CANoe进行测试时,推荐以下硬件配置:
关键参数设置表:
| 参数项 | 推荐值 | 说明 |
|---|---|---|
| 仲裁段波特率 | 500kbps | 标准CAN通信速率 |
| 数据段波特率 | 2Mbps | CAN-FD特有高速模式 |
| 采样点 | 80% | 提高通信稳定性 |
| FD帧格式 | 启用 | 必须开启以支持CAN-FD |
c复制variables {
const long sysTxIdentifier = 0x72E; // 诊断仪物理ID
const long sysRxIdentifier = 0x73E; // ECU物理ID
long gHandleConn1; // 连接句柄
long dbHandle; // 数据库句柄
}
on start {
InitConn();
}
InitConn() {
// 创建数据库连接
dbHandle = CanTpGetDBConnection();
if (dbHandle > 0) {
CanTpCloseConnection(dbHandle);
}
// 创建新连接
gHandleConn1 = CanTpCreateConnection(0); // 0表示正常模式
CanTpSetTxIdentifier(gHandleConn1, sysTxIdentifier);
CanTpSetRxIdentifier(gHandleConn1, sysRxIdentifier);
CanTpSetMaxCANFDFrameLength(gHandleConn1, 64); // CAN-FD最大数据长度
}
注意:在CAN-FD模式下,MaxCANFDFrameLength通常设置为64字节,这是CAN-FD相对于经典CAN(8字节)的主要优势之一。
当发送数据超过7字节(单帧最大有效载荷)时,OSEK_TP会自动启用多帧传输。首帧包含两个关键信息:
c复制void CanTp_FirstFrameInd(long connHandle, dword length) {
write("检测到首帧,总数据长度:%d 字节", length);
// 此处可添加流控参数设置逻辑
}
典型首帧结构示例:
| 字节位置 | 内容 | 说明 |
|---|---|---|
| 0 | 0x10 | PCI类型(首帧标识) |
| 1 | 0x00 | 数据长度高字节 |
| 2 | 0x64 | 数据长度低字节(100字节) |
| 3-7 | 数据 | 首帧携带的有效数据 |
接收到首帧后,接收方需要回复流控帧,包含三个关键参数:
c复制on key 'b' {
// 设置流控参数
CanTpSetBlockSize(gHandleConn1, 0x05); // 每次接收5个连续帧
CanTpSetSTmin(gHandleConn1, 20); // 最小间隔20ms
CanTpUseFlowControlFrames(gHandleConn1, 1); // 启用流控
// 发送测试数据
byte txData[100];
CanTpSendData(gHandleConn1, txData, elCount(txData));
}
流控帧参数影响对比表:
| 参数组合 | 传输效率 | 总线负载 | 适用场景 |
|---|---|---|---|
| BS=0, STmin=0 | 最高 | 最高 | 高带宽、实时性要求低 |
| BS=5, STmin=20 | 中等 | 中等 | 平衡场景 |
| BS=1, STmin=100 | 最低 | 最低 | 资源受限ECU |
在流控参数协商完成后,发送方开始传输连续帧。每个连续帧包含:
c复制void CanTp_ReceptionInd(long connHandle, byte data[]) {
dword recvSize = elcount(data);
write("收到数据:%d 字节,首字节:0x%02X", recvSize, data[0]);
// 完整数据重组逻辑应在此实现
if (recvSize == gExpectedLength) {
ProcessCompleteData(data);
}
}
连续帧传输中的常见问题及解决方案:
序列号错误:
STmin不遵守:
数据重组失败:
ISO 15765-2定义了多种超时参数,合理设置可提高通信可靠性:
c复制on key 't' {
// 设置超时参数(单位:毫秒)
CanTpSetTimeoutAr(gHandleConn1, 1000); // 地址扩展超时
CanTpSetTimeoutAs(gHandleConn1, 2000); // 发送超时
CanTpSetTimeoutBs(gHandleConn1, 3000); // 块接收超时
CanTpSetTimeoutCr(gHandleConn1, 500); // 连续帧超时
}
关键超时参数推荐值:
| 参数 | 默认值(ms) | 调整建议 |
|---|---|---|
| Ar | 1000 | 根据ECU响应速度调整 |
| As | 2000 | 大数据传输时可适当增大 |
| Bs | 3000 | 根据BS和STmin计算调整 |
| Cr | 500 | 网络质量差时可增大 |
OSEK_TP通过回调函数报告错误:
c复制void CanTp_ErrorInd(long connHandle, long error) {
switch(error) {
case 0x01:
write("错误:连接超时");
break;
case 0x02:
write("错误:无效序列号");
break;
case 0x03:
write("错误:流控参数无效");
break;
default:
write("未知错误:%d", error);
}
// 错误恢复逻辑
CanTpResetConnection(gHandleConn1);
}
常见错误代码速查表:
| 错误码 | 含义 | 可能原因 |
|---|---|---|
| 0x01 | 连接超时 | ECU未响应/网络断开 |
| 0x02 | 序列号错误 | 数据丢失/ECU实现问题 |
| 0x03 | 流控参数无效 | 不支持的BS/STmin组合 |
| 0x04 | 缓冲区溢出 | 接收方缓冲区不足 |
| 0x05 | 无效帧类型 | 协议栈实现错误 |
动态调整BS/STmin:
c复制// 根据总线负载动态调整流控参数
if (GetBusLoad() > 70%) {
CanTpSetBlockSize(gHandleConn1, 3);
CanTpSetSTmin(gHandleConn1, 30);
} else {
CanTpSetBlockSize(gHandleConn1, 8);
CanTpSetSTmin(gHandleConn1, 10);
}
填充字节优化:
c复制// 设置填充字节为0xAA便于调试
CanTpSetPadding(gHandleConn1, 0xAA);
混合模式传输:
c复制// 对小于64字节的数据使用单帧优化
if (dataLength <= 64) {
CanTpSetMaxCANFDFrameLength(gHandleConn1, 64);
} else {
CanTpSetMaxCANFDFrameLength(gHandleConn1, 8);
}
在实际项目中,我们发现当BS设置为0时,ECU可能会根据自身资源情况动态调整实际传输速率,这时配合适当的STmin设置(通常20-50ms)可以获得最佳平衡。