4、PduR上行路由配置:从CAN/LIN/Ethernet到上层模块

各位同学,今天我们来聊聊PduR的上行路由。说白了,就是数据从底层总线收上来之后,怎么一步步走到Dcm(诊断通信管理器)这个“终点站”。

我个人习惯把上行路由比作“快递分拣系统”。CAN、LIN、Ethernet就像三个不同的快递入口,PduR就是那个分拣中心,而CanIf、CanTp这些模块,就是不同层级的转运站。最终,包裹(诊断请求)要送到Dcm手里。

4.1 上行路由的整体架构

先看一张图,这是我用SVG画的,帮你快速建立整体认知。

PduR上行路由数据流(CAN/LIN/Ethernet → Dcm) CAN Bus LIN Bus Ethernet CanIf LinIf EthIf PduR(路由分发中心) CanTp LinTp SoAd Dcm(诊断通信管理器) 注:实际路由路径取决于配置,图中为典型诊断请求路径

嗯,这张图很直观。数据从总线进来,经过接口层(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会做两件事:

  1. 硬件过滤:只接收配置好的CAN ID,其他的丢掉
  2. 回调通知:调用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的触发后,开始干活。它的核心任务是:

  1. 接收单帧:如果只有一帧,直接提取数据
  2. 接收多帧:先收第一帧(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上行路由的配置,今天就聊到这里。说白了,就是一张路由表 + 几个关键参数。配置对了,数据流自然就通了。


可可精品资料库