文章包含了AUTOSAR基础软件(BSW)中CanTp模块相关的内容详解。本文从AUTOSAR规范解析,ISOLAR-AB配置以及模块相关代码分析三个维度来帮读者清晰的认识和了解CanTp这一基础软件模块。文中涉及的ISOLAR-AB配置以及模块相关代码都是依托于ETAS提供的工具链来配置与生成的,与AUTOSAR规范之间可能会有些许的出入,但总体的功能要点与处理流程都应该是一致的。
CanTp模块作为PduR与CanIf之间的模块,它的主要功能是针对超过底层CAN帧能有容纳的数据长度(Can为8字节,CanFD为64字节)Pdu发送的时候,进行传输层的分段以及组装数据。CanTp常见的应用是将诊断涉及的相关功能模块发送/接收数据转化为包括单帧(SF)、首帧(FF)、连续帧(CF)以及流控帧(FC)格式并下发到CanIf模块,并提供整个通信过程中超时时间的判决。文中还介绍了利用CanTp模块实现多路诊断ID的配置过程,并简单的展示了CanTp模块静态代码的调试,帮助读者更好地理解CanTp模块。
目录
[SF N-SDU received and no buffer available](#SF N-SDU received and no buffer available)
[Successful SF N-PDU reception](#Successful SF N-PDU reception)
[Transmit request of SF N-SDU](#Transmit request of SF N-SDU)
[Transmit request of larger N-SDU](#Transmit request of larger N-SDU)
[Large N-SDU Reception](#Large N-SDU Reception)
AUTOSAR规范解析
CanTp是PduR和CanIf模块之间的模块(见下图)。CanTp模块的主要目的是在普通CAN帧的情况下分段和重组超过8字节或在CAN FD帧情况下超过64字节的CAN I-PDU。
PduR将AUTOSAR的标准模块COM和DCM模块的I-PDUs部署到不同的通信协议上,并通过I-PDU标识符来路由到不同的网络系统类型(如 CAN、LIN或者FlexRay),PduR同时判决是否需要使用传输协议,如果需要则路由I-PDU到CanTp。最后,当没有速率转换时,PduR模块可以执行网关功能。
Canlf向上提供了相同的机制来访问CAN总线通道,无论其位置(片内/片外)。根据CAN控制器的位置((片内/片外),它可以抽象出ECU的硬件布局和CAN驱动器的数量。因为CanTp只处理传输协议帧(即SF、FF、CF和FC PDU),CanIf必须根据N-PDU ID,将I-PDU转发到CanTp或PduR。
根据AUTOSAR基本软件架构,CanTp为以下领域提供服务:
- 数据在发送方向上的分段。
- 数据在接收方向重新组装数据。
- 数据流的控制。
- 检测分段会话中的错误。
- 传输取消接收取消。
根据现有标准制定基本软件模块规范是AUTOSAR的决定,因此AUTOSAR CAN传输层规范基于国际标准ISO-15765,这是汽车领域最常用的标准。
ISO-15765(包含四个部分),描述了两种适用的CAN传输层规范:用于OEM增强诊断的ISO-15765-2和用于OBD诊断的ISO-15765-4。关于传输层,ISO-15765-4(ISO-15765中也包括数据链路层和物理层的部分)与ISO-15765-2一致,但有一些限制/增加。为了在ISO-15765-2和ISO15765-4之间不存在不兼容问题,差异将通过CAN传输层配置来解决。
虽然CAN传输协议主要用于车辆诊断系统,但它也被开发来处理其他基于CAN并需要传输层协议的系统开发需求。下图展示了各个模块对应的ISO Layer以及PDU Name。
缩略语与概念
前缀
- I-:与AUTOSAR COM交互层相关。
- L-:与CanIf相关,相当于逻辑链路层(数据链路层的上半部分,其下半部分称为媒体介质访问控制,也就是CAN控制器)。
- N-:与CanTp相关,CAN传输层相当于OSI网络层。
缩略语
- OBD:On-Board Diagnostic。
- PDU:Protocol Data Unit。
- PduR:PDU Router。
- SDU:Service Data Unit。
- CAN L-SDU:这是CAN接口模块的SDU。它类似于CAN N-PDU,但从CAN接口模块的角度来看。
- CAN LSduId:CanIf内独一无二的ID,为了通过API与CanIf交互,上层可以引用CAN L-SDU的结构体以此进行数据传递。
- CAN N-PDU:这是CanTp的PDU。它包含唯一标识符、数据长度和数据(协议控制信息加上整个N-SDU或其中的一部分)。
- CAN N-SDU:这是CanTp的SDU。在AUTOSAR体系结构中,它是来自PduR的一组数据。
- CAN N-SDU Info Structure:这是一个CanTp内部常量结构,包含特定的CAN传输层
信息,用于处理相关CAN N-SDU的发送、接收、分段和重组。 - CAN NSduId:Can传输层中唯一的SDU标识符。它用于SDU的路由属性。为了通过其API与CAN传输层交互,上层使用CANNSduld来参考CAN N-SDU信息结构体。
- I-PDU:这是AUTOSAR COM模块的PDU。
- PDU:在分层系统中,它指的是在给某层的协议中指定的数据单元。它包含该层(SDU)的用户数据以及可能的协议控制信息。X层的PDU为其下层X-1层的SDU,即(X)-PDU =(x-1)-SDU。
- PduInfoType:该类型是指用于存储处理PDU(或SDU)收发基本信息的结构,即指向其在RAM中的有效载荷的指针及其长度(以字节为单位)。
- SDU:在分层系统中,这指的是一组数据,该数据由某层的服务用户发送,并传输给对等服务用户,同时在语义上保持不变。
- PCI:PCI是"Protocol Control Information"的缩写。这些信息对于从一个特定协议层的实例传递到另一个实例的SDU是必要的。例如,它包含源和目标信息。在传输端由协议层添加PCL,在接收端再次移除。
下图展示了PDU,SDU之间的关系。
- CAN:CAN驱动模块。
- CAN CF:CAN连续帧。
- CAN FC:CAN流控帧。
- CAN FF:CAN首帧。
- CAN SF:CAN单帧。
- DLC:数据长度(CAN PDU中描述SDU长度) 。
- Mtype:消息类型(可能值:诊断、远程诊断)。
- FS:Flow Status。可能的值有:
- CTS:Continue To Send 继续发送状态
- WAIT:等待。
- OVFLW:溢出。
- STmin:ECU发送流控帧后,连续帧之间的最大时间间隔。
- BS:块大小。ECU发送流控帧后,Tester被允许发送连续帧最大帧数据,为0可以一直发。
- CAN FD:CAN flexible data rate。
- CAN_DL:CAN frame data length。
- TX_DL:数据链路层发送数据长度。
- RX_DL:数据链路层接收数据长度。
- SF_DL:单帧数据长度
CanTp模块会对消息分段成四种类型的帧。分别是单帧(Single Frame,SF),首帧(FirstFrame,FF) ,连续帧(Consecutive Frame,CF),流控帧(Flow ControlFrame,FC),区分4种帧的关键信息在于CAN帧数据中第一Byte的前四个bit,如下图所示。流控帧是Tester发送头帧时或者在连续帧发送过程中,由ECU发送给Tester用于控制多帧发送数据速率。
- N_AI:标识消息发送者和接收者的源地址(N_SA)、目标地址(N_TA),以及消息的通信模型(N_TAtype)和可选的地址扩展名(N_AE)。
- N_As:发送方时间参数,发送方从请求发送到发送完成的时间间隔,超过这个时间发送中断,一般为70ms。
- N_Bs:发送方时间参数,首帧发送成功到流控帧接收成功的时间节点,也就是说从数据确认发送到收到流控帧的最大时间间隔,一般为150ms。
- N_Cs:发送方时间参数,接收到流控帧到发送连续帧的最大时间间隔,此处注意不是STmin的含义,一般为50ms。
- N_Ar:接收方时间参数,传输流控帧到发送方的时间,一般为70ms。
- N_Br:接收方时间参数,接收到首帧到发送流控帧的时间,一般为50ms。
- N_Cr:接收方时间参数,接受方成功发送流控帧到接收到连续帧的时间,一般为150ms。
下面的图片展示了上述的时间参数。
- S3_Tester:发送方维持非默认会话的最大时间。
- S3_Sever:接收方未接受到任何诊断报文维持在非默认会话下的时间,超时则会进入默认会话。
- P2_Client:发送方发送完请求消息后等待服务器响应超时的时间。
- P2*_Client:发送方收到否定响应码为0x78的否定响应后等待接受方发送响应的增强型超时时间设置。
- P2_Sever:接收方收到请求后发出响应的实际时间。
- P2*_Sever:接收方发送0x78否定响应到发出否定响应的实际时间。
- P3_ClientPyh:发送方在收到物理寻址(phy)的肯定响应下允许发送下一条物理寻址请求的最小时间间隔。
- P3_ClientFun:发送方在收到物理寻址(phy)的肯定响应下允许发送下一条功能寻址(fun)的最小时间间隔。
模块依赖
CanTp连接
在AUTOSAR架构的最终版本中,传输协议设施将用于传输诊断(例如OBD和UDS协议)和AUTOSAR COM I-PDUs。因此,CanTp模块能够同时处理多个连接(即并行处理多个分段会话)。
最大同时连接数是通过静态配置的。这种配置对生成的代码的复杂性和资源消耗(CPU、ROM和RAM)有重要影响,因为必须为每次同时访问保留资源(例如Rx和Tx状态机、用于处理N-PCI数据的变量等)。
为了允许用户选择哪些I-PDUs可以同时接收(或发送),每个N-SDU标识符将通过配置的CanTp"连接通道"进行内部路由。由于"连接通道"不能从外部访问,因此传输N-SDU所需的所有信息都将链接到N-SDU标识符(例如:"连接通道"号、超时、寻址格式等)。
根据MetaDataLength, N-SDU可以作为具有定义的N_Al的特定连接,或者作为通用连接,其中N_TA, N_SA和N_AE在运行时变化,而n_type, MType和寻址格式是静态定义的。
CanTp交互
下图显示了CanTp、PduR和Canlf模块之间的交互。
CanTp的上层接口提供了PduR模块的通用访问,用于传输和接收数据。该访问是通过CAN N-SDU标识符(CAN NSduld)实现的。CAN NSduld是指一个常量数据结构,它由描述CAN N-SDU的属性组成。每个CAN N-SDU特定的数据结构可能包含以下属性:N-SDU的类型(Tx或Rx),其地址格式,此消息的L-SDU标识符或其他对实现有用的属性。
处理模式
AUTOSAR通信堆栈支持轮询和事件触发模式。因此,每个通信层可以通过不同的机制从较低层接收信息并将其传播到较高层。在CAN传输层的情况下,仅支持事件触发模式。
数据一致性
为了优化通信堆栈,AUTOSAR限制了CAN传输层的缓冲容量。因此,CanTp将从上层(DCM、COM或PDU路由器--在1:1 TP路由的情况下)直接复制N-SDU载荷(包含内容)到CAN驱动程序,反之亦然。为了确保数据一致性,上层将遵守以下规则:
- 在传输时,从传输请求到接收到传输确认,N-SDU的数据有效载荷将保持不变。
- 在接收时,N-SDU的数据访问将被锁定,从接收开始到接收指示已收到。
静态配置
在运行时,CAN传输模块必须拥有管理传输连接所需的所有信息。因此,应静态配置以下属性:
- CAN N-SDU的数量
- 整个工程唯一的CAN N-SDU标识符
- 每个CAN N-SDU(Tx或Rx)的通信方向
- 每个通道的通信类别,全双工还是半双工
- 每个连接的寻址格式(普通、扩展或混合),如果是扩展寻址格式,则为N_TA值,如果是混合寻址格式,则为N_AE值。
- 针对每种连接的寻址格式(普通、扩展、混合11位、普通固定或混合29位)的不同,另外需要包含的配置(对于使用N-SDUs和MetaData的通用连接,可以省略静态寻址信息。):
- Normal: none
- Extended: N_TA
- Mixed 11 bit: N_AE
- Normal fixed: N_TA, N_SA
- Mixed 29 bit: N_TA, N_SA, N_AE
- 每个CAN N-SDU标识符的关联CAN L-SDU标识符,必要时(多帧分段会话)用于传输CAN FC N-PDU的CAN L-SDU标识符。
- 是CAN还是CANFD。
PduR关联
CAN传输层使用PDU路由器的回调函数来复制发送的数据并确认发送,发起接收,复制接收到的数据并表示接收到消息:
- PduR_CanTpRxIndication
- PduR_CanTpStartOfReception
- PduR_CanTpCopyRxData
- PduR_CanTpCopyTxData
- PduR_CanTpTxConfirmation
CanIf关联
CAN传输层使用CAN接口的以下服务来传输CAN N-PDUs:
- CanIf_Transmit
为了实现AUTOSAR通信栈一致API,基本交互相关数据已经定义了类型。这些类型被CAN传输层用来与PduR和CanIf通信。CanTp数据类型来源于Com。
功能规范说明
提供给上层的服务
初始化以及关闭:CanTp模块提供了分段、流控制传输和消息重组的服务。它的主要目的是发送和接收可能适合或不适合单个CAN帧的消息。不能装入单个CAN帧的消息被分割成多个部分,这样消息的每个部分都可以在单个CAN帧中传输。CanTp模块提供了主要以下功能:
- 初始化以及关闭(Initialization and shutdown)。
- 通信服务(Communication services)。
CanTp_Init函数初始化模块的所有全局变量,并将所有传输协议连接设置为CANTP_ON的子状态,在此状态下,既不进行分段发送,也不进行分段接收(Rx线程处于CANTP_RX_WAIT状态,Tx线程处于CANTP_TX_WAIT状态。CanTp的生命周期状态图如下:
传输请求:传输操作CanTp_Transmit()将允许上层(一般指诊断)请求使用CAN传输协议设施(分段、扩展寻址格式等)进行数据传输。功能函数CanTp_Transmit()应该是异步的,多段传输不阻塞。传输请求被接受后,如果N-SDU传输完成(成功或不成功),CanTp模块将通知其上层。
传输取消:传输是可以取消的,一个实例场景:当更高优先级的诊断协议来临的时候。此功能通过API CanTp_CancelTransmit()触发。注意,如果传输正在进行中,将在接收端产生一个超时错误。
提供给下层的服务
根据AUTOSAR通信栈规范,CanTp为CanIf模块提供以下两个回调函数:CanTp_TxConfirmation()和CanTp_RxIndication()。
传输确认:CanIf模块应调用发送确认函数,通知CanTp请求的CAN帧传输已成功执行。L-PDU标识符与调用相关联,以便识别相应的传输。当在最大时间(等于N_As)后没有收到传输确认时,CanTp模块应终止相应的会话。在收到TxConfirmation之前,N-PDU对其他并发会话仍然不可用。传输确认调用接口函数 CanTp_TxConfirmation()。
接收通知:canf模块调用接收通知函数,通知CanTp模块收到了新的CAN N-PDU帧(即传输协议帧)。接收指示可以根据CanIf配置在ISR上下文中执行。接收确认是被CanIf调用的CanTp_RxIndication()。
内部行为
CAN传输层的内部行为提供了基本机制,以实现该模块的主要目的,即在单个CAN帧或多个CAN帧中传输消息。CAN传输层的整个行为是被事件触发,因此CanTp可以直接处理来自PduR(或CANIf)的N-SDU(或L-SDU)的传输。
N-SDU接收:当CanTp模块收到SF或FF的N-PDU时,使用PduR_CanTpStartOfReception函数通知上层(PduR)。通过调用上述函数,上层将会预留一段RAM空间给N-SDU使用。CanTp将FF/SF的内容提供给PduR使用。内容在PduR_CanTpStartofReception()的参数TpSdulnfoPtr指向的空间。
接收到的数据链路层数据长度(RX_DL)由CAN帧/PDU的第一个接收到的有效载荷长度(CAN_DL)导出(CAN_DL小于等于8应该按照8字节计算)。
注意:如果上层不能使缓冲区可用,因为一个错误(例如,在网关的情况下,它可能表明到目标网络的传输会话已经中断)或资源限制(例如N-SDU长度超过上层的最大缓冲区大小),PduR_CanTpStartofReception()函数返回BUFREQ_E_NOT_OK或者BUFREQ_E_OVFL。
接收到第一帧后,如果函数PduR_CanTpStartOfReception()返回BUFREQ_E_OVFL给CanTp模块,CanTp模块将发送一个流控N-PDU,状态为溢出(FS(OVFLW)),并终止接收N-SDU。
在接收到第一帧或单帧后,如果函数PduR_CanTpStartOfReception()返回的BUFREQ_OK的可用缓冲区大小小于已经接收到的数据,CanTp模块将中止接收N-SDU,并调用PduR_CanTpRxIndication(),结果为E_NOT_OK。如果函数PduR_CanTpCopyRxData()在接收到块的最后一个连续帧后返回BUFREQ_OK,但是剩余的缓冲区不足以接收下一个块,CanTp模块将启动定时器N_Br。当定时器N_Br处于活动状态时,CanTp模块将在每次处理MainFunction期间调用PduR_CanTpCopyRxData()服务时,将传输数据长度为0,并将NULL_PTR作为数据缓冲区。如果N_Br定时器过期,而可用缓冲区的大小仍然不够大,CanTp模块将发送一个新的FC(WAIT)来暂停N-SDU的接收,并重新加载N_Br定时器。
N-SDU发送:上层通过调用CanTp_Transmit()请求传输N-SDU。CanTp_Transmit()的参数描述了CAN NSduld和要发送的完整Tx N-SDU长度。
对于不使用MetaData的特定连接,CanTp_Transmit函数应该只使用完整的SduLength信息,而不使用可用的N-SDU数据缓冲区来准备Single Frame或FirstFrame PCI。当在通用连接(带元数据的N-PDU)上调用CanTp_Transmit时,CanTp模块将根据元数据中包含的地址信息发送帧。
上层发送请求后,CanTp模块在调用PduR_CanTpCopyTxData之前启动超时N_Cs。如果在计时器过去之前没有可用的数据,CanTp模块将终止通信。上层将传输数据复制到PdulnfoType结构中。CanTp调用PduR_CanTpCopyTxData服务发送的每个帧(SF, FF和CF)。假设函数返回BUFREQ_E_BUSY, CanTp模块将稍后(具体实现)重试复制数据。
缓存策略:因为CanTp没有缓冲能力,所以要发送的N-SDU负载不会在内部复制,收到的N-PDU也不会在内部重新组装。CAN传输层直接在上层的存储区域上工作(例如PduR、DCM、COM)。要访问这些内存区域,CAN传输层使用PduR_CanTpCopyTxData()或PduR_CanTpCopyRxData()函数。因此,为了保证数据一致性,上层应该锁定该内存区域,直到出现指示。当发送缓冲区被锁定时,上层不能在缓冲区内写入数据。当接收缓冲区被锁定时,CAN传输层不能保证缓冲区的数据一致性。上层既不能读也不能写缓冲区中的数据。
下图提供了一个示例,总结了使用CAN2.0帧发送长度为50字节的帧的过程:
下面我们针对这四个步骤进行分析:
- PduR请求传输50个数据字节
- CanTp向PduR请求负载数据,并发送第一帧。
- CanTp将剩余的有效载荷数据作为连续帧的序列发送,6或7个有效载荷数据字节由上层提供复制到每个连续帧(CF)上。
- CanTp确认载荷数据的传输。
下一个图形是一个接收49字节N-SDU的示例,上层提供25字节大小的Rx缓冲区。
下面介绍以下上图涉及的步骤:
- CanIf用CanTp_RxIndication()通知新的接收。CanTp将此通知转发给PduR。
- PduR返回可用缓冲区大小为25字节,CanTp发送FlowControl向发起者发送CTS。
- CanTp向PduR提供接收到的每一帧的数据,并监控剩余的缓冲区大小。在连续第二帧之后,剩余的缓冲区大小不够下一个块(一个块为两个连续帧大小)。
- CanTp通过调用PduR_CanTpCopyRxData(),以0为数据长度和NULL_PTR作为数据指针,请求PduR剩余的缓冲区大小,并向发起者发送一个流控等待标识。直到下一个块的足够缓冲区可用。
- 当缓冲区大小最终足够下一个块时,CanTp将向发起者发送一个FlowControl CTS,并继续接收下一个连续帧块。
- 在复制完块的最后一个连续帧后,剩余的缓冲区对于下一个块来说太低了,所以CanTp再次发送等待帧并监控剩余的缓冲区大小。
- 当最后一个块的缓冲区可用时,CanTp将继续接收。
- CanTp通过调用PduR_CanTpRxIndication()通知PduR接收结束。
流控用于调整发送方以适应接收方的能力。这种传输协议的主要用途是点对点通信(即1对1通信)。
NSDuId与LSDuId的关系:一个 CAN NSduId只与一个可以用于传输SF、FF、FC和CF帧的LSduId挂钩。但是,如果将消息配置为使用扩展或混合地址格式,CanTp模块必须将每个传输段(SF、FF和CF)的第一个字节填充为N_TA(在扩展寻址)或N_AE(在混合地址)值的情况下,则NSduId也可以与N_TA或N_AE值有关。对于扩展寻址格式,FC的第一个数据字节也包含N_TA值或N_TA和N_TAtype值的唯一组合。对于混合寻址格式,FC的第一个数据字节包含N_AE值。这样,一个FC帧的CAN值与它的N_TA值(例如N_Al)或N_AE值结合起来,只能识别一个CAN值。在接收方向,将使用每个(SF, FF或CF)传输协议数据单元的第一个数据字节值来确定相关的NSDU。因此,在接收扩展寻址N-PDU时,CanTp模块需要提取N_TA值来建立相应的N-SDU。对于与N_AE值相关的混合寻址模式,应用相同的过程。总结下来如下图所示:
如果一个连接通道被分配给多个N-SDU,那么资源在不同的N-SDU之间共享,如果没有可用的连接通道,CAN传输层将拒绝传输或中止接收。 CanTp模块不能同时接收或发送具有相同标识符的N-SDU,否则接收到的帧不能分配到正确的连接。
N-PDU填充:为了保证与所有上层关于帧数据长度的要求完全兼容(例如OBD要求数据长度始终设置为8字节,而UDS没有),填充激活选项可以在每个N-SDU的预编译时配置,通过使用CanTpRxPaddingActivation用于Rx N-SDU或CanTpTxPaddingActivation用于Tx N-SDU。填充字节的值可以通过配置参数CANTP_PADDING_BYTE配置。为了防止传输未初始化的数据
,当要传输的N_PDU长度不等于ISO-11898-1:2014 DLC表中定义的离散长度值时,应进行填充。
非预期帧的处理:CAN传输层在意外的N-PDU到达时的行为是很大的取决于处理N-SDU的通信方向类型。处理非预期的是数据帧与通信方式的全双工、半双工之间也有很大的关系,如果接收到意外帧,CanTp模块应按以下表格操作。该表格规定了根据当前CanTp内部状态(CanTp状态)处理N-PDU的方式。
补充信息:网络层数据的交换由三种寻址格式支持:普通寻址、扩展寻址和混合寻址。每种寻址格式需要不同数量的CAN帧数据字节来封装与要交换的数据相关联的寻址信息。因此,在单个CAN帧内传输的数据字节数取决于所选择的寻址格式的类型。
-
Normal addressing:对于N_SA、N_TA、n_type和Mtype的每一个组合,都分配一个唯一的CAN标识符。N_PCI和N_Data放在CAN帧数据字段中。见下表。
-
Normal Fixed addressing:对于普通的固定寻址,只允许使用29位CAN标识符。下面两个表定义了地址信息(N_Al)到CAN标识符的映射,这取决于目标地址类型(n_ttype)。N_PCI和N_Data放在CAN帧数据字段中。
-
Extended addressing:对于N_SA、n_ttype和Mtype的每一个组合,都分配一个唯一的CAN标识符。N_TA位于CAN帧数据字段的第一个数据字节。N_PCI和N_Data放在CAN帧数据字段的剩余字节中。
-
Mix addressing(29位ID):混合寻址是当Mtype设置为远程诊断时使用的寻址格式。下面两个表定义了地址信息(N_Al)到29位CAN标识符方案和第一个CAN帧数据字节的映射,这取决于目标地址类型(n_ttype)。N_PCI和N_Data放在CAN帧数据字段的剩余字节中。
-
Mix addressing(11位ID):混合寻址是当Mtype设置为远程诊断时使用的寻址格式。下表定义了地址信息(N_Al)到11位CAN标识符方案的映射。对于N_SA、N_TA和n_type的每一个组合,都分配一个唯一的CAN标识符。N_AE放在CAN帧数据字段的第一个数据字节中。N_PCI和N_Data放在CAN帧数据字段的剩余字节中。
时序图
本章的目标是通过对大多数更频繁和复杂用例的描述,使理解CAN传输层变得更容易。因此,下面的图表并非详尽无遗,也不反映所有指定的API可能性。
SF N-SDU received and no buffer available
下图展示了在没有buffer可用时接收单帧的时序图。
注意,此时序图仅演示了CANTp模块的工作。然而,如果在这种接收过程中考虑整个系统,则涉及更多的模块。由于这种接收可以在CAN ISR的上下文中触发,因此CANTp操作应尽可能短。
下表展示了各环节涉及函数与相关返回值和环节描述。
Successful SF N-PDU reception
下图展示了成功接收单帧的时序图。
下表展示了各环节涉及函数与相关返回值和环节描述。
Transmit request of SF N-SDU
下图展示了发送单帧的时序图。
下表展示了各环节涉及函数与相关返回值和环节描述。
Transmit request of larger N-SDU
下面的时序图展示发送多帧数据时的时序图。
下表展示了各环节涉及函数与相关返回值和环节描述。
Large N-SDU Reception
下面的时序图展示接收多帧数据时的时序图。
下表展示了各环节涉及函数与相关返回值和环节描述。
ISOLAR-AB配置
ISOLAR-A
首先建立诊断报文的DBC。
导入DBC。
然后导入之前建立好的三帧报文。
最后执行RTE-BSW生成对应的Pdus。
CanTp
CanTpGeneral
下图为CanTpGeneral的RTA-BSW之后生成的配置截图。
下面是一些这个容器常用的配置解释:
- CanTpChangeParameterApi:打开/关闭CanTp_ChangeParameter函数,可以更改传输层的参数BS与STmin。
- CanTpDynIdSupport:支持通过N-PDU的MetaData处理动态ID。
- CanTpFlexibleDataRateSupport:是否支持CAN FD。
- CanTpGenericConnectionSupport:是否支持使用N-SDUs通过MetaData处理通用连接。需要同时开启CanTpDynIdSupport。
- CanTpPaddingByte:填充未初始化的值是多少,此处配置为0xAA。
- CanTpRbBurstQsize:突发队列的大小。如果配置此值,则启用突发模式。它同时提供最大可能的爆发。
- CanTpRbDynamicFlowControlSupport:ETAS自定义参数,在后续的FlowControl帧中启用动态BS/STmin值,AUTOSAR的策略是只有固定的值。
CanTpConfig
这里主要配置的项为CanTpMainFunctionPeriod,这个选项需要正确配置CanTpMainFunction的调用周期,因为其中很多定时器以这个为时基。
CanTpChannel
这个容器需要正确配置Channel是全双工还是单工。
CanTpRxNSdus
Rx的NSdu有两个:
- 使用功能寻址的,功能寻址接收之后无需响应,一般是正响应抑制。接收的方向是CanIf到CanTp然后是CanTp到PduR。
- 使用物理寻址的,使用物理寻址一般需要响应,毕竟物理寻址是与ECU一对一的。
功能寻址一般操作的是0x28\0x85等诊断服务。下图的物理接收同时配置了CanTpRxPyNPdu与CanTpTxFcNPdu,他们包含了接收和发送的路径。
下面介绍了这个容器的重要参数:
- CanTpRxAddressingFormat:声明支持此RxNSdu的通信寻址模式。当前配置的值为CanTpStandard,使用的是正常寻址格式。下面解释两个寻址模式(一共有五种)含义:
- BASIC ADDRESSING:CAN-TP协议的基本寻址模式称为普通寻址模式,我们通过CAN Identifier识别是普通的CAN 报文还是CAN-TP报文。也就是说将有一些特定的标识符用于 CAN-TP,如果接收到这些标识符的任何消息,则服务器将理解为此消息为TP消息。这种寻址方式的优点是可以将完整的8个字节的数据包作为数据发送。
- EXTENDED ADDRESSING:这种寻址模式是将CAN数据字段的第一个字节将用于地址的附加元素的一种 CAN-TP 寻址模式,这种寻址方式导致报文数据的有效负载减少一个字节。
- CanTpRxPaddingActivation:定义空白字节是否填充。
- CanTpRxTaType:定义接收报文类型,可以是CAN或者CAN FD的功能请求,物理请求。
记住这里RX物理寻址的配置多了一个Tp到If的路径,用于进行正/负响应。
CanTpTxNSdus
TXNSdus只需要配置物理寻址一个TXNSdus。这里我们介绍一个参数:
- CanTpTc:启用还是关闭发送取消和接收取消的开关。
多路诊断ID配置
假设需要配置多路诊断ID,例如:
- 第一路:
- 功能 RX ID:0x710
- 物理 RX ID:0x720
- 物理 TX ID:0x750
- 第二路:
- 功能 RX ID:0x610
- 物理 RX ID:0x620
- 物理 TX ID:0x650
在导入第二对诊断帧之后,在RTA-BSW之后选择对应的Pdus的引用即可。
代码解析
动态配置代码
下图为CanTp模块配置生成的动态代码。
我们针对一些常用的文件内容说明:
-
CanTp_Types.h:包括CanTp_RxPduStructType、CanTp_TxSduStructType、CanTp_RxSduStructType、CanTp_TimeOutStructType、CanTp_ParamStructType与CanTp_ConfigStructType结构体定义。
-
CanTp_Cfg.h:包含CANTP_ON、CANTP_CANFD_SUPPORT等通用配置。
-
CanTp_Cfg.c:首先包含了CanIf模块提供的CanTp用到的CanIf HandleId import以及PduR模块提供的CanTp用到的PduR HandleId import。并包含了CanTp_RxPdu,CanTp_TxSdu,CanTp_RxSdu,CanTp_TimeOut,CanTp_Param,与整体CanTp_Config的配置生成代码。
cpp/* *********************************************************************************************************************** * * Product Info * Isolar version: ISOLAR-AB 4.0.2 * Product release version: RTA-BSW 3.1.0 * *********************************************************************************************************************** */ /* Includes */ #include "CanTp.h" #include "PduR_CanTp.h" #if (!defined(PDUR_AR_RELEASE_MAJOR_VERSION) || (PDUR_AR_RELEASE_MAJOR_VERSION != 4)) #error "AUTOSAR major version undefined or mismatched" #endif #if (!defined(PDUR_AR_RELEASE_MINOR_VERSION) || (PDUR_AR_RELEASE_MINOR_VERSION != 2)) #error "AUTOSAR minor version undefined or mismatched" #endif #include "CanIf.h" #if (!defined(CANIF_AR_RELEASE_MAJOR_VERSION) || (CANIF_AR_RELEASE_MAJOR_VERSION != 4)) #error "AUTOSAR major version undefined or mismatched" #endif #if (!defined(CANIF_AR_RELEASE_MINOR_VERSION) || (CANIF_AR_RELEASE_MINOR_VERSION != 2)) #error "AUTOSAR minor version undefined or mismatched" #endif typedef uint16 CanTp_TickType; typedef uint8 CanTp_SduIdType; typedef uint8 CanTp_ChannelIdType; typedef void (*CanTp_GetSduPairType)(CanTp_SduIdType *TxSduId, CanTp_SduIdType *RxSduId, uint8 Address); struct CanTp_RxPduStructType /* Rx N-PDU to Connection Configuration Mapping Structure */ { const CanTp_SduIdType RxSduId; /* RxSdu for combination of N-PDU and N-Ta */ const CanTp_SduIdType TxSduId; /* TxSdu for combination of N-PDU and N-Sa */ const CanTp_GetSduPairType GetSduPair; }; struct CanTp_TxSduStructType /* TxConnection Configuration Structure */ { const uint8 TX_DL; /* Max CAN_DL of Tx */ const uint8 BitFields; /* 4:IsTcDisabled|3:IsPadingOn|2:IsFunctional|1:IsFcDisabled|0:IsFdEnabled */ const uint8 Address; /* Extended/Mixed Address to be used during transmission */ const uint8 AddressFormatId; /* Addressing Format Identifier */ const uint8 TimeOutId; /* Timeout Index for this Connection in CanTp_TimeOut */ const CanTp_ChannelIdType ChannelId; /* Index of RAM Channel used by this connection */ const PduIdType TxConfirmationId; /* Transmit Confirmation Id for SF/FF/CF Frame */ const PduIdType TxPduId; /* Transmit N-PDU Id for this Connection */ const PduIdType PduRPduHandleId; /* Identifier to be used for PduR Api calls */ }; struct CanTp_RxSduStructType /* RxConnection Configuration Structure */ { const uint8 BitFields; /* 3:IsPadingOn|2:IsFunctional|1:IsFcDisabled|0:IsFdEnabled */ const uint8 Address; /* Extended/Mixed Address to be used during reception */ const uint8 AddressFormatId; /* Addressing Format Identifier */ const uint8 TimeOutId; /* Timeout Index for this Connection in CanTp_TimeOut */ const uint8 ParamId; /* Rx Conn. STmin/BS/FcWaitMax Index for this connection. */ const CanTp_ChannelIdType ChannelId; /* Index of RAM Channel used by this connection */ const PduIdType TxConfirmationId; /* Transmit Confirmation Id for FC Frame */ const PduIdType TxPduId; /* Transmit FC relevant N-PDU Id for this Connection */ const PduIdType PduRPduHandleId; /* Identifier to be used for PduR Api calls */ }; struct CanTp_TimeOutStructType /* TimeOut Configuration Structure */ { const CanTp_TickType AsArTicks; const CanTp_TickType BsBrTicks; const CanTp_TickType CsCrTicks; }; struct CanTp_ParamStructType /* RxConnection Specific left overs Configuration Structure */ { const uint8 Param[2]; /* Param[0] is for STMin and Param[1] is for BS */ const uint16 FcWaitMax; }; struct CanTp_ConfigStructType { const CanTp_ChannelIdType NumberOfChannels; const CanTp_SduIdType NumberOfRxPdus; const CanTp_SduIdType NumberOfTxPdus; const CanTp_SduIdType NumberOfRxSdus; const CanTp_SduIdType NumberOfTxSdus; const struct CanTp_RxPduStructType *RxPdu; const struct CanTp_TxSduStructType *TxSdu; const struct CanTp_RxSduStructType *RxSdu; const struct CanTp_TimeOutStructType *TimeOut; const struct CanTp_ParamStructType *Param; }; /* CanIf HandleId import */ #define CANTP_IF_PDU_ID_00000 CanIfConf_CanIfTxPduCfg_Diag_VCU_Resp_Can_Network_CANNODE_ECAN #define CANTP_IF_FC_PDU_ID_00000 CanIfConf_CanIfTxPduCfg_Diag_VCU_Resp_Can_Network_CANNODE_ECAN #define CANTP_IF_FC_PDU_ID_00001 0 /* PduR HandleId import */ #define CANTP_PDUR_DEST_ID_00000 PduRConf_PduRDestPdu_Diag_VCU_Resp_Can_Network_CANNODE_ECAN_Phys_PduR2CanTp #define CANTP_PDUR_SRC_ID_00000 PduRConf_PduRSrcPdu_Diag_VCU_ReqPhsy_Can_Network_CANNODE_ECAN_CanTp2PduR #define CANTP_PDUR_SRC_ID_00001 PduRConf_PduRSrcPdu_Diag_ECAN_ReqFunc_Can_Network_CANNODE_ECAN_CanTp2PduR /* Function definitions */ #define CANTP_START_SEC_CONFIG_CODE #include "CanTp_MemMap.h" #define CANTP_STOP_SEC_CONFIG_CODE #include "CanTp_MemMap.h" /* ConfigSet Structure definitions */ #define CANTP_START_SEC_CONFIG_DATA_UNSPECIFIED #include "CanTp_MemMap.h" static const struct CanTp_RxPduStructType CanTp_RxPdu[2u] = { { 0, 0, NULL_PTR }, { 1, 2, NULL_PTR } }; static const struct CanTp_TxSduStructType CanTp_TxSdu[1u] = { { 0x8, 0x8, 0x0, 0, 0, 1, 0, CANTP_IF_PDU_ID_00000, CANTP_PDUR_DEST_ID_00000 } }; static const struct CanTp_RxSduStructType CanTp_RxSdu[2u] = { { 0x8, 0x0, 0, 1, 0, 0, 0, CANTP_IF_FC_PDU_ID_00000, CANTP_PDUR_SRC_ID_00000 }, { 0xC, 0x0, 0, 1, 0, 0, 0, CANTP_IF_FC_PDU_ID_00001, CANTP_PDUR_SRC_ID_00001 } }; static const struct CanTp_TimeOutStructType CanTp_TimeOut[2u] = { { 3, 8, 7 }, { 3, 1, 15 } }; static const struct CanTp_ParamStructType CanTp_Param[1u] = { {{ 0x32, 0x8, }, 0x0 } }; const struct CanTp_ConfigStructType CanTp_Config = { 2, 2, 1, 2, 1, &CanTp_RxPdu[0], &CanTp_TxSdu[0], &CanTp_RxSdu[0], &CanTp_TimeOut[0], &CanTp_Param[0] }; #define CANTP_STOP_SEC_CONFIG_DATA_UNSPECIFIED #include "CanTp_MemMap.h"
集成代码
下图为CanTp模块包含的集成代码,其中SchM、MemMap文件这里就不赘述了。
CanTp_Timer.h文件中包含一个内联函数CanTp_GetElapsedValue,它的作用是获取定时器当前ticks和与从上次获取到现在已用的ticks。
CanTp_CallOut.c文件中包含CanTp_XX_YY_ExternalFdSupportCallback,它将作为启动功能之前的最后一次调用。
静态代码
主要介绍常见的静态代码涉及API说明,具体详细完整的介绍读者可以参考《AUTOSAR_SWS_CANTransportLayer.pdf》以及《RTA-BSWReferenceGuide.pdf》。
- CanTp_Init/CanTp_Shutdown:此函数初始化/关闭CanTp模块。
- CanTp_Transmit:请求发送一个PDU。
- CanTp_CancelReceive:请求取消下层传输协议模块中对PDU的持续接收。
- CanTp_ChangeParameter:请求更改特定的传输协议参数(例如块大小)。
- CanTp_MainFunction:CanTp模块周期调度的MainFunction。
- CanTp_RxIndication:通知从下层通信接口模块接收到的PDU。
- CanTp_TxConfirmation:下层通信接口模块确认PDU的传输或PDU的未传输。
静态代码调试
下图通过在CanTp_RxIndication打断点,可以通过底层提供的PduInfoPtr来找到对应的数据指针,从而可以从Memory中看到对应接收到的诊断报文为02 3E 00。
下图展示了在CanTp_Transmit函数打断点,可以通过上层提供的PduInfoPtr找到发送数据的指针,并在Memory可以看到对应需要发送的报文内容。
十六宿舍 原创作品,转载必须标注原文链接。
©2023 Yang Li. All rights reserved.
欢迎关注 『十六宿舍』,大家喜欢的话,给个 👍 ,更多关于嵌入式相关技术的内容持续更新中。