4、PduR上行路由配置:从CAN/LIN/Ethernet到上层模块
各位同学,今天我们来聊聊PduR的上行路由。说白了,就是数据从底层总线收上来之后,怎么一步步走到Dcm(诊断通信管理器)这个“终点站”。
我个人习惯把上行路由比作“快递分拣系统”。CAN、LIN、Ethernet就像三个不同的快递入口,PduR就是那个分拣中心,而CanIf、CanTp这些模块,就是不同层级的转运站。最终,包裹(诊断请求)要送到Dcm手里。
4.1 上行路由的整体架构
先看一张图,这是我用SVG画的,帮你快速建立整体认知。
嗯,这张图很直观。数据从总线进来,经过接口层(CanIf/LinIf/EthIf),汇聚到PduR,再分发到传输层(CanTp/LinTp/SoAd),最后到达Dcm。
4.2 PduR路由表的核心配置
PduR能干活,全靠一张“路由表”。这张表告诉它:收到某个ID的报文,该往哪个模块送。
我举个例子。假设你有一个CAN诊断请求,CAN ID是0x7DF。PduR收到后,需要知道:
- 源模块:CanIf
- 目标模块:CanTp
- 路由方式:直接转发还是分段转发
配置起来大概长这样:
/* PduR路由表配置示例 */
const PduR_RoutingTableType PduR_RoutingTable[] = {
{
.PduRConfigurationId = 1,
.PduRRoutingPathGroupId = 0,
.sourcePduRef = &CanIf_RxPdu_0x7DF, /* 来自CanIf的0x7DF */
.destPduRef = &CanTp_RxPdu_DCM, /* 发往CanTp的DCM端口 */
.routingType = PDUR_DIRECT, /* 直接路由 */
.gatewayType = PDUR_GW_FORWARD /* 转发模式 */
}
};
关键点:上行路由的sourcePduRef一定是接口层(CanIf/LinIf/EthIf)的Rx PDU,destPduRef是传输层(CanTp/LinTp/SoAd)的Rx PDU。千万别搞反了。
4.3 从CanIf到PduR:第一站
数据从CAN总线进来,CanIf先收下。CanIf会做两件事:
- 硬件过滤:只接收配置好的CAN ID,其他的丢掉
- 回调通知:调用PduR的
PduR_CanIfRxIndication()函数
我记得有一次调试,发现某个报文总是收不到。查了半天,原来是CanIf的硬件过滤配置错了,把0x7DF写成了0x7FE。你想想看,这种低级错误,排查起来真的很费时间。
小技巧:调试时可以在CanIf的RxIndication回调里加个断点,确认报文是否真的到了CanIf。如果没到,问题大概率在CAN驱动或硬件过滤上。
4.4 PduR的分发逻辑
PduR收到CanIf的通知后,会查路由表。这里有两种情况:
| 路由类型 | 说明 | 典型场景 |
|---|---|---|
| PDUR_DIRECT | 直接转发,不经过缓冲 | 短报文(≤8字节) |
| PDUR_TRIGGER | 触发传输层接收 | 多帧报文(需CanTp组包) |
| PDUR_BUFFERED | 先缓冲再转发 | 跨总线网关场景 |
对于诊断请求,通常是PDUR_TRIGGER。因为诊断报文可能很长(比如UDS的34服务,要写几十个字节),需要CanTp来做拆包和组包。
配置时要注意:
- 如果报文长度≤8字节,用
PDUR_DIRECT就够了 - 如果报文长度>8字节,必须用
PDUR_TRIGGER,否则数据会丢
避坑指南:我曾经在一个项目里,把诊断请求配成了PDUR_DIRECT,结果多帧报文只收到了第一帧。排查了两天才发现是路由类型配错了。所以,诊断报文一定要用PDUR_TRIGGER。
4.5 从PduR到CanTp:组包与重组
CanTp收到PduR的触发后,开始干活。它的核心任务是:
- 接收单帧:如果只有一帧,直接提取数据
- 接收多帧:先收第一帧(FF),再收后续帧(CF),全部收齐后重组
配置CanTp时,有几个参数很关键:
/* CanTp通道配置示例 */
const CanTp_ChannelType CanTp_ChannelConfig[] = {
{
.CanTpChannelId = 0,
.CanTpRxPduId = 0, /* 对应PduR路由表中的destPduRef */
.CanTpRxBufferSize = 4096, /* 接收缓冲区大小,建议≥4096 */
.CanTpRxTimeout = 1000, /* 接收超时,单位ms */
.CanTpRxBlockSize = 0, /* 流控块大小,0表示不限制 */
.CanTpRxSeparationTime = 0 /* 连续帧间隔时间 */
}
};
嗯,这里要注意缓冲区大小。如果诊断数据很大(比如刷写Bootloader,一次可能传几MB),缓冲区太小会导致数据丢失。我个人习惯设成4096字节起步。
4.6 从CanTp到Dcm:最后一公里
CanTp把数据重组完成后,会调用Dcm的Dcm_StartOfReception()和Dcm_CopyRxData()回调。Dcm拿到完整的数据后,开始解析UDS服务。
这里有一个配置点:Dcm的DcmDslProtocolId要和CanTp的通道ID对应上。否则数据到了Dcm门口,却进不去。
/* Dcm配置示例 */
const Dcm_DslProtocolType Dcm_DslProtocolConfig[] = {
{
.DcmDslProtocolId = 0, /* 协议ID,与CanTp通道ID对应 */
.DcmDslRxPduId = 0, /* 接收PDU ID */
.DcmDslRxBufferSize = 4096, /* 接收缓冲区 */
.DcmDslFunctionalReqEnabled = TRUE /* 是否支持功能寻址 */
}
};
小提示:调试Dcm收不到数据时,先检查CanTp是否正常组包。可以在CanTp的CanTp_RxIndication()里加日志,看看数据是否完整。如果CanTp没问题,再查Dcm的协议ID配置。
4.7 多通道场景:CAN + LIN + Ethernet同时存在
现在的车,不可能只有一路CAN。可能有CAN FD、LIN、甚至车载以太网。PduR需要同时处理多路数据。
配置上,每个通道独立配置路由表。比如:
| 通道 | 源模块 | 目标模块 | 路由类型 |
|---|---|---|---|
| CAN诊断 | CanIf | CanTp | PDUR_TRIGGER |
| LIN诊断 | LinIf | LinTp | PDUR_TRIGGER |
| 以太网诊断 | EthIf | SoAd | PDUR_DIRECT |
注意,以太网诊断通常走DoIP协议,SoAd直接处理,不需要Tp层。因为以太网本身支持大数据包传输。
我做过一个项目,车上同时有CAN和以太网诊断。PduR配置了三条路由:CAN走CanTp,以太网走SoAd,还有一条是CAN到以太网的网关路由。嗯,那个配置表写了将近200行,但逻辑很清楚。
4.8 常见问题与排查思路
最后,总结几个我踩过的坑:
- 问题1:PduR收不到数据
先查CanIf的RxIndication是否被调用。如果没调用,问题在CAN驱动或硬件。如果调用了,查PduR路由表配置,看sourcePduRef是否正确。 - 问题2:CanTp组包失败
检查CanTp的缓冲区大小是否足够。如果诊断数据超过缓冲区,CanTp会报错。另外,检查流控参数(BlockSize和SeparationTime)是否合理。 - 问题3:Dcm收到数据但解析失败
大概率是Dcm的协议ID配置错了。确保DcmDslProtocolId和CanTp通道ID一致。另外,检查Dcm的RxPduId是否匹配。
核心原则:上行路由的调试,一定要从底层往上层逐级排查。先确认数据到了CanIf,再确认PduR转发了,再确认CanTp组包了,最后确认Dcm收到了。跳级排查,只会浪费时间。
好了,关于PduR上行路由的配置,今天就聊到这里。说白了,就是一张路由表 + 几个关键参数。配置对了,数据流自然就通了。
可可精品资料库