Autosar CAN 报文发送 下期预告CAN_RX
overview
从各个BSW 模块的角度来说,can报文的走向可以用下图来表示。
但是实际上一看就会,一做就废。不过这张图还是很有参考价值的。下面我们从代码层面来解释每一步。
RTE之上的SWC
从SWC的角度想要发送一帧报文,其实也没有一帧报文的概念,应该说是把报文里面的一个signal进行赋值,发送(为了严谨下文默认报文为周期发送,不是触发模式)。那么SWC需要手动去调用一个RTE提供的write接口。这里比如信号(PDU)名字叫FACAILE, SWC叫做WO.
则: SWC 需要调用
Rte_Write_WO_FACAILE(VAR(uint8, AUTOMATIC) data)
那么这个接口是谁来实现呢。
这个接口是由 RTE 来实现。那么RTE 怎么实现呢。
RTE的实现
上面说到SWC 调用的RTE write接口,这个接口实现是RTE生成阶段自动生成,需要开发者手动配置。
FUNC(Std_ReturnType, RTE_CODE)
Rte_Write_WO_FACAILE(VAR(uint8, AUTOMATIC) data)
{
VAR(Std_ReturnType, AUTOMATIC) rtn = RTE_E_OK;
if ( ((VAR(StatusType, AUTOMATIC))E_OK) != Com_SendSignal(((VAR(Com_SignalIdType, AUTOMATIC))666), &data) )
{
rtn = ((VAR(Std_ReturnType, AUTOMATIC))RTE_E_COM_STOPPED);
}
/* Send complete */
return rtn;
}
这里面可以看到 RTE 实际上是接收了SWC 的数据,然后传给了 一个函数
Com_SendSignal()
这时候就回到了overview图里面的COM,即下图
COM 做了那些事情
首先我们先看一下被RTE调用的这个函数的形参以及返回值
/*
**********************************************************************************************************************
Function name : Com_SendSignal
Description : Service updates the signal object identified by SignalId with the signal
referenced by the SignalDataPtr parameter.
Parameter : idSignal_u16 -> Id of the signal.
: signalDataPtr_pcv -> The pointer to the address where the application data is available.
Return value : E_OK/COM_SERVICE_NOT_AVAILABLE/COM_BUSY
**********************************************************************************************************************
*/
uint8 Com_SendSignal(Com_SignalIdType idSignal_u16, const void * signalDataPtr_pcv )
这里面第一个参数指的是 signal ID. 第二个参数是SWC层给的数据地址。那么signal ID 是什么呢。
答:配置生成的,可以看配置生成的代码。可以直接去这个位置看。每一个tx 的signal 都会对应一个 编号。
文件名:
Com_Cfg_SymbolicNames.h
signal ID:
/* Signal IDs*/
/* Tx Signal ID*/
好,到这里清楚了传入signal id 和 数据地址。继续往下看。
函数
Com_SendSignal()
在里面判断了
- 数据有效性
- com 是否init
- signal id 是否有效
判断完之后,把参数传给了内部sendsignal函数。老规矩看返回值和形参。
/*
**********************************************************************************************************************
Function name : Com_Prv_InternalSendSignal
Description : Service updates the signal object identified by SignalId with the signal
referenced by the SignalDataPtr parameter.
This is an internal function to update the signal.
This function doesnt do DET checks, these checks are done in the API itself.
Parameter : idSignal_u16 -> Internal Id of the signal. (Relevant for post-build case)
: signalDataPtr_pcv -> The pointer to the address where the application data is available.
Return value : E_OK/COM_SERVICE_NOT_AVAILABLE
**********************************************************************************************************************
*/
uint8 Com_Prv_InternalSendSignal(Com_SignalIdType idSignal_u16, const void * signalDataPtr_pcv)
到这里面可以确定SWC的输入信息是有效的。下面来对这个signal进行操作。
首先要获取这个signal的配置参数。每一个signal都有自己对应的配置参数。这个配置参数在哪呢?
文件名:
Com_PBcfg.c
全局变量:
const Com_Prv_xTxSigCfg_tst Com_Prv_xTxSigCfg_acst[COM_NUM_TX_SIGNALS]
这里面都有什么呢?其实都是DBC 里面定义好了的内容。这里就不复制了。可以查看上面全局变量。
下面开始把signal的数据buffer copy到pdu自己的buffer里面。
/*
**********************************************************************************************************************
Function name : Com_Prv_SigBufftoIpduBuff
Description : Pack/Copy signal buffer into IPdu buffer
Parameter : idSignal_u16 -> Internal Id of the signal. (Relevant for post-build case)
: signalDataPtr_pcv -> The pointer to the address where the application data is available.
Return value : returns signal buffer
**********************************************************************************************************************
*/
然后读取一些signal的属性,进行状态赋值。比如配置的是触发模式还是周期,还是数值变化等属性进行判断,记录状态。
操作完之后进行Pdu的状态检查,这个是检查什么呢。大家都知道模式管理模块会对PDU group/ pdu 进行管理。这里检查pdu是否允许被发送。
/**********************************************************************************************************************
Function name : Com_Prv_CheckTxIPduStatus
Description : Inline function to check TxIPdu status
Parameter : idIpdu_uo - TxIPdu Id
Return value : COM_TRUE - TxIPdu is started
COM_FALSE - TxIPdu is stopped
*********************************************************************************************************************/
一切就位,又记录一些状态值,后面会用上。这时候进行进一步pdu发送。当然可能到不了进一步了,因为上面的各种检查失败,可以关注一下返回值,来判断那一步错误了。
进一步发送pdu.
/**********************************************************************************************************************
Function name : Com_Prv_ProceedToSendIpdu
Description : 1. For gateway txIpdu - set the flags here and defer the transmission till
Com_MainFunctionRouteSignals
2. For non-gateway txIPdu - trigger the IPdu only for event type or for mode change.
Parameter : IpduId_ui -> Internal Id of the TxIPdu
sendIpduFlag_st -> flags to decide ipdu transmission
Return value : None
*********************************************************************************************************************/
void Com_Prv_ProceedToSendIpdu(Com_IpduId_tuo idComTxPdu_uo, Com_SendIpduInfo_tst sendIpduFlag_st)
这一步主要是处理一下这个即将离开com的signal的pdu发送状态,以及发送模式MIXED/PERIODIC mode。这个pdu的触发标志位 会在这里面赋值。最终传给下面函数,当然如果是没有SWC一路往下走,对于周期的函数,在
Com_MainFunctionTx()
也一样会被置位。这里暂不讨论。那么到了从com发送Ipdu.
这里不得不回到最开始的总览图,因为这里的名词发生了变化,从signal 变成了Ipdu.
com的ram 全局变量。关键时刻可以直接看这里。
/* Array of Rx Ipdu Ram flags */
extern Com_RxIpduRamData_tst Com_RxIpduRam_ast[];
/* Array of Tx Ipdu Ram flags */
extern Com_TxIpduRamData_tst Com_TxIpduRam_ast[];
发送的模式,和上面这么多状态位的赋值决定com的下一步骤,这里是很复杂的,可以通过下面的表来进行查询。来知道下一步是个什么情况 仅供参考。当然我们讨论的是正常能发出去的情况。
好回到主线,com开始发送ipdu了。
**********************************************************************************************************************
Function name : Com_Prv_SendIpdu
Description : Internal function used for Transmitting Tx Ipdu's
Parameter : idTxPdu_uo - Id of the Tx ipdu
sendIpduFlag_st - flags to decide ipdu transmission
Return value : Void
**********************************************************************************************************************
void Com_Prv_SendIpdu(PduIdType idTxPdu_uo, Com_SendIpduInfo_tst sendIpduFlag_st)
传入的形参是什么呢。是pdu ID 和 这个pdu的状态位。
状态位表示的是什么呢?答:可根据下面进行以一比对。
* typedef struct \n
* { \n
* unsigned int isEventTrig_u1 : 1; Set, If the call to Com_Prv_SendIpdu is not from MainFunctionTx. \n
* unsigned int isModeChangd_u1 : 1; Set, If Mode change has happened for the Ipdu \n
* unsigned int isSigTriggered_u1 : 1; Set, If signal transfer property is triggered(1)/pending(0) \n
* unsigned int isTimeoutReq_u1 : 1; Set, If Timeout monitoring is required \n
* unsigned int ignoreRepetitions_u1 : 1; Set, If configured repetitions are to be ignored \n
* unsigned int isSwtIpduTxMode_u1 : 1;Set , If the call is made from Com_SwitchIpduTxMode \n
* } Com_SendIpduInfo_tst; \n
- 根据id 可以获取该pdu的ram 以及一些配置信息
可以查看配置生成的文件
Com_PBcfg_Common.c
- 根据上面的flag来决定是否要发送。。如果发送的话则终于发送到了PduR
PDUR 干了什么
**************************************************************************************************
* PduR_dComTransmit - Function to be invoked if DET is enable for PduR_ComTransmit
* This function is called by the Com to request a transmission.
*
* \param PduIdType id: ID of Com I-PDU to be transmitted.
* const PduInfoType * ptr: Pointer to a structure with I-PDU related data that shall be transmitted:
* data length and pointer to I-SDU buffer
*
* \retval E_OK Transmit request has been accepted
* E_NOT_OK Transmit request has not been accepted
*
* \seealso
* \usedresources
**************************************************************************************************
Std_ReturnType PduR_dComTransmit( PduIdType id, const PduInfoType * ptr)
从形参可以看出,这里包含了pdu的信息 和 pduid. 注意这里的pduid 已经不是前面的signal id. 那么则个pduid在哪里呢?
文件:
PduR_Cfg_SymbolicNames.h
For RxPaths:
PduRConf_PduRSrcPdu_<shortname of PduRSrcPdu> will be used by module which gives PduR_<LowerLayer>RxIndication callback e.g CanIf,CanTp,Low-Cdd */
For TxPaths:
PduRConf_PduRSrcPdu_<shortname of PduRSrcPdu> will be used by module which gives PduR_<UpperLayer>Transmit request e.g Com,Dcm,Up-Cdd
PduRConf_PduRDestPdu_<shortname of PduRDestPdu> will be used by module which gives PduR_<LowerLayer>TxConfirmation callback e.g CanIf,CanTp,Low-Cdd
这里对buffer 以及 ID 进行了有效性判定。后面就到了PduR需要做的事情。路由!
具体路由到哪里去,就要根据pdu本身了。下面一般的会有以下几个路径。
const PduR_loTransmitFuncType PduR_loTransmitTable[] =
{
{&PduR_RF_CanIf_Transmit_Func},
{&PduR_RF_CanTp_Transmit_Func},
{&PduR_RF_SoAdIf_Transmit_Func}
};
本文讨论走到 CanIf. 如果是诊断报文需要走到TP, 如果是以太网帧需要走到socket. 如果是其他的come 也可以,需要配置cdd_com. 这里不一一说明。
PduR_RF_CanIf_Transmit_Func
下面到了CanIf层面。
CanIf做了什么
入口函数
/* Function name : CanIf_Transmit */
/* Syntax : Std_ReturnType CanIf_Transmit(
PduIdType CanIfTxSduId,
const PduInfoType * CanIfTxInfoPtr
) */
/* Description : This service initiates a request for transmission of the CAN L-PDU specified by the
CanTxPduId and CAN related data in the L-PDU structure. */
/* Parameter : CanTxPduId, PduInfoPtr */
/* Return value : E_OK / E_NOT_OK */
/************************************************************************************************************/
#define CANIF_START_SEC_CODE
#include "CanIf_MemMap.h"
Std_ReturnType CanIf_Transmit(PduIdType CanIfTxSduId,const PduInfoType * CanIfTxInfoPtr)
首先读取CanIf的配置文件在静态代码里面是个全局指针,可以访问阅读。
文件:
CanIf_Controller.c
变量:
const CanIf_ConfigType * CanIf_Prv_ConfigSet_tpst;
获取到了配置参数,这边就可以对pdu本身进行一些检查,比如长度等等。
当所有信息检查完毕之后。向更底层调发送。
/* Function name : CanIf_XCore_LocalCore_Transmit */
/* Syntax : Std_ReturnType CanIf_XCore_LocalCore_Transmit(
PduIdType CanIfTxSduId,
const PduInfoType * CanIfTxInfoPtr
) */
/* Description : This service initiates a request for transmission of the CAN L-PDU specified by the
CanTxPduId and CAN related data in the L-PDU structure. */
/* Parameter : CanTxPduId, PduInfoPtr */
/* Return value : E_OK / E_NOT_OK */
/************************************************************************************************************/
Std_ReturnType CanIf_XCore_LocalCore_Transmit(
PduIdType CanIfTxSduId,
const PduInfoType * CanIfTxInfoPtr
)
- CanIfTxSduId 包含pduID
- PduInfoType 包含数据
通过全局变量,配置信息来获取单个pdu信息。以及pdu的状态。
CanIf_ControllerStateType CanIf_Prv_ControllerState_ast[CANIF_TOTAL_CTRLS];
/*This hold the current config set details*/
const CanIf_ConfigType * CanIf_Prv_ConfigSet_tpst;
从这里面获取最终的CanId 和 使用的Controller Id 以及传输层协议CANFD 还是 CAN. 这样才可以和硬件交互。结构体
typedef struct{
const CanIf_Cfg_CtrlConfig_tst *CanIf_CtrlConfigPtr;
Can_HwHandleType CanObjectId;
#if(CANIF_PUBLIC_TXBUFFERING == STD_ON)
CanIf_Cfg_CanHandleType_ten CanHandleType;
#endif
}CanIf_Cfg_HthConfig_tst;
当把硬件的信息,pdu的信息,以及初始化等信息都check完毕没有问题后。开始调驱动层代码。这时候就要明确给驱动层传入了什么数据。
传入了两个
/**********************************************************************************
**[SWS_Can_00429] 8.2.4 :Can_HwHandleType **
**Type : uint8, uint16 **
**Range : Standard --0..0x0FF **
**Extended :--0..0xFFFF Description: **
***********************************************************************************/
typedef struct
{
uint8 * sdu;
Can_IdType id;
PduIdType swPduHandle;
uint8 length;
}Can_PduType;
传入了这些信息,足以驱动层知道这帧报文数据哪个核,属于哪个controller,报文ID 是多少,要以CANFD 还是CAN来发送。到了mcal.
Mcal做了什么
Can_ReturnType Can_17_McmCan_Write(const Can_HwHandleType Hth,
const Can_PduType *const PduInfo)
这里面通过两个全局变量来获取CAN硬件的相关配置,和当前这帧报文的相关配置。
/* Variable to hold the pointers to variables holding core specific CAN
states */
static const Can_17_McmCan_CoreGlobalType *const
Can_17_McmCan_GblCoreState[MCAL_NO_OF_CORES]
/* Pointer to store configuration set pointer */
static const Can_17_McmCan_ConfigType *Can_17_McmCan_kGblConfigPtr;
把这些信息都传递给了更底层的函数。
static Can_17_McmCan_ReturnValueType Can_17_McmCan_lWriteMsgObj(
const Can_HwHandleType HthIndex,
const Can_PduType *const PduInfoPtr,
const Can_17_McmCan_CoreGlobalType *const CoreGlobalPtr,
const Can_17_McmCan_CoreConfigType *const CoreConfigPtr)
这里就牛逼喽。
- 检查是否有空闲的buffer
/* Copy the exact buffer element offset */
BuffElementIndex = TxObjPtr[HthIndex].CanTxBuffIndx;
注意这一步得到的buffer后面需要写道寄存器里面。
把形参传入的pdu信息 复制到临时变量里面
/*Store the Data pointer and Data length*/
TempPduInfo->sdu = PduInfoPtr->sdu;
TempPduInfo->length = PduInfoPtr->length;
/* Set buffer size with normal buffer */
TxWriteStartAddr = TxWriteStartAddr +
(CAN_17_MCMCAN_MSG_RAM_ELEMENT_SIZE_NFD *
(uint32)BuffElementIndex);
用CAN TX 的一个寄存器结构体定义一个指针,进行赋值。
typedef volatile struct _Ifx_CAN_TXMSG
{
Ifx_CAN_TXMSG_T0 T0; /**< \brief 0, Transmit Buffer 0*/
Ifx_CAN_TXMSG_T1 T1; /**< \brief 4, Transmit Buffer 1*/
Ifx_CAN_TXMSG_DB DB[64]; /**< \brief 8, Data Byte m*/
} Ifx_CAN_TXMSG;
对下面
typedef struct _Ifx_CAN_TXMSG_T0_Bits
{
Ifx_UReg_32Bit ID:29; /**< \brief [28:0] Identifier (rw) */
Ifx_UReg_32Bit RTR:1; /**< \brief [29:29] Remote Transmission Request (rw) */
Ifx_UReg_32Bit XTD:1; /**< \brief [30:30] Extended Identifier (rw) */
Ifx_UReg_32Bit ESI:1; /**< \brief [31:31] Error State Indicator (rw) */
} Ifx_CAN_TXMSG_T0_Bits;
typedef struct _Ifx_CAN_TXMSG_T1_Bits
{
Ifx_UReg_32Bit reserved_0:16; /**< \brief [15:0] \internal Reserved */
Ifx_UReg_32Bit DLC:4; /**< \brief [19:16] Data Length Code (rw) */
Ifx_UReg_32Bit BRS:1; /**< \brief [20:20] Bit Rate Switching (rw) */
Ifx_UReg_32Bit FDF:1; /**< \brief [21:21] FD Format (rw) */
Ifx_UReg_32Bit reserved_22:1; /**< \brief [22:22] \internal Reserved */
Ifx_UReg_32Bit EFC:1; /**< \brief [23:23] Event FIFO Control (rw) */
Ifx_UReg_32Bit MM:8; /**< \brief [31:24] Message Marker (rw) */
} Ifx_CAN_TXMSG_T1_Bits;
对上面寄存器赋值完成之后通过一个循环。把刚才提到的临时变量的buffer的数据,放到TxMsgBuffer
TxMsgBufferPtr->DB[DataBytePos].U = TempPduInfo->sdu[DataBytePos];
这里完结,没问题的话,数据会从CAN controller 往 收发器走了。如果一切正常的话。会有回调函数。
多谢支持下期预告CAN_RX
文章转载自公众号:汽车与基础软件