一.前言:
通信模块可以大致分为三大功能:数据发送功能,数据接收功能,状态切换和管理功能。
我们的系列按照这样的功能排序和自底向上的模块排序,每次只介绍其中的一个模块的其中一个功能的实现,所以在每篇文章中对于模块不会事无巨细的介绍所有它所具备的功能,而是选择性的介绍三大功能中的其中一个。BSW往上的所有模块示例,为最大程度了解其设计思路以及避免侵权风险,将会采用遵循AUTOSAR架构的非商业代码作为示例,其部分细节不保证完全遵循AUTOSAR最新的标准,不保证所有功能的具备,敬请理解。
二.Canif模块及其发送函数CanIf_Transmit
在上节的文章中介绍了CAN模块的发送底层逻辑,Can_Write函数的介绍。Can_Write函数已经对CAN驱动进行了抽象。抽象为了HOH供上层使用。而它的上层就是CanIf层.在CanIf模块中,主要实现的功能如下:
1.传输请求(Transmit request)
传输请求功能的实现主体函数是CanIf_Transmit,这个函数内部调用Can_Write进行报文的发送
2.传输确认(Transmit confirmation)
传输确认功能的实现主体函数是CanIf_TxConfirmation,这个函数在CAN发送完成后被调用,作用是调用更上层的发送确认回调函数通知更上层的各个模块对应的PDU已发送成功。
3.接收提示(Reception indication)
传输确认功能的实现主体函数是CanIf_RxIndication,这个函数在CAN发送完成后被调用,作用是调用更上层的发送确认回调函数通知更上层的各个模块对应的PDU已发送成功。
4.CAN控制器模式切换(Controller mode control)
controller模式切换的功能实现主体函数是CanIf_SetControllerMode,涉及到模式切换的功能后面专题详解
5.PDU模式切换(PDU mode control)
PDU模式切换的功能实现主体函数是CanIf_SetPduMode,涉及到模式切换的功能后面专题详解
整个CanIf模块实现的基本函数如下:
本文侧重介绍CanIf模块的发送功能。CanIf的报文发送功能由CanIf_Transmit函数实现,此函数原型如下:
Std_ReturnType CanIf_Transmit(PduIdType CanTxPduId,
const PduInfoType *PduInfoPtr)
此函数会调用MCAL的Can模块中的Can_Write函数进行报文的发送。在上节介绍Can_Write函数的时候,说过其传入的参数是以下:
- HOH
- CAN id:报文ID
- length:报文长度
- sdu:报文数据
- swPduHandle :tx_confirm的ID
而CanIf层的传入参数变为了:
- CanTxPduId,
- PduInfoPtr这个参数包括了SduDataPtr和SduLength
所以CanTxPduId对Can_Write函数所需的HOH,CAN ID,swPduHandle做了抽象。我们基本也可以推断出CanIf的TxPDU所需要配置的主要内容了。
三.Canif模块的发送配置解析
以下例子用于说明CanIf层的tx配置。
Canif涉及到发送报文的主要配置结构体如下:
const CanIf_InitConfigType CanIfInitConfig =
{
.CanIfConfigSet = 0,
.CanIfNumberOfCanRxPduIds = sizeof(CanIfRxPduConfigData)/sizeof(CanIf_RxPduConfigType),
.CanIfNumberOfCanTXPduIds = sizeof(CanIfTxPduConfigData)/sizeof(CanIf_TxPduConfigType),
.CanIfNumberOfDynamicCanTXPduIds = 0,
// Containers
.CanIfHohConfigPtr = CanIfHohConfigData,
.CanIfRxPduConfigPtr = CanIfRxPduConfigData,
.CanIfTxPduConfigPtr = CanIfTxPduConfigData,
};
我们以下主要关注:
1.HOH的配置结构体CanIfHohConfigData,
2.发送PDU配置结构体CanIfTxPduConfigData
1.HOH的配置结构体CanIfHohConfigData,
对于HOH的配置结构体CanIfHohConfigData的结构如下:
const CanIf_InitHohConfigType CanIfHohConfigData[] = {
{
#if(CANIF_CONTROL_CAN_DRIVER ==STD_ON)
.CanConfigSet = &CanConfigSetData,
#endif
.CanIfHrhConfig = CanIfHrhConfigData_Hoh_1,
.CanIfHthConfig = CanIfHthConfigData_Hoh_1,
},
};
其中配置了发送的HTH和接收的HRH。对于发送的HTH配置如下:
const CanIf_HthConfigType CanIfHthConfigData_Hoh_1[] =
{
{
.CanIfHthType = CAN_BASIC,
.CanIfCanControllerIdRef = CANIF_Channel_1,
.CanIfHthIdSymRef = HOH_3_UDSTX_Node,
},
{
.CanIfHthType = CAN_BASIC,
.CanIfCanControllerIdRef = CANIF_Channel_1,
.CanIfHthIdSymRef = HOH_3_NMTX_Node,
},
{
.CanIfHthType = CAN_BASIC,
.CanIfCanControllerIdRef = CANIF_Channel_1,
.CanIfHthIdSymRef = HOH_3_XCPTX_Node,
},
{
.CanIfHthType = CAN_BASIC,
.CanIfCanControllerIdRef = CANIF_Channel_1,
.CanIfHthIdSymRef = HOH_3_EcuTestNode_CanCluster,
},
};
主要就是引用了上节文章例子中介绍的CAN模块配置的四个HOH。
2.发送PDU配置结构体数组CanIfTxPduConfigData
这个结构体数组有所有的发送PDU配置,每个PDU都是一个结构体成员,其中的一个成员配置示例如下:
const CanIf_TxPduConfigType CanIfTxPduConfigData[] = {
{
.CanIfTxConfrimPduId = CANTP_PDU_ID_UDS_PHYS_TX,
.CanIfCanTxPduIdCanId = 0x7ea,
.CanIfCanTxPduIdDlc = 8,
.CanIfCanTxPduType = CANIF_PDU_TYPE_STATIC,
#if ( CANIF_READTXPDU_NOTIFY_STATUS_API == STD_ON )
.CanIfReadTxPduNotifyStatus = false,
#endif
.CanIfTxPduIdCanIdType = CANIF_CAN_ID_TYPE_11,
.CanIfUserTxConfirmation = CanTp_TxConfirmation,
.CanIfCanTxPduHthRef = &CanIfHthConfigData_Hoh_1[0],
.PduIdRef = NULL,
},
.....
}
关键的参数解释如下:
- CanIfTxConfrimPduId :用于为swPduHandle 复值,向对应的TxConfirm函数传入参数。
- CanIfCanTxPduIdCanId:对应PDU的报文ID
- CanIfCanTxPduIdDlc:对应PDU的报文长度
- CanIfUserTxConfirmation:发送确认回调函数
- CanIfCanTxPduHthRef:发送此PDU要使用的HOH
类似上节的结尾说到的抽象。这些配置元素打包成一个结构体数组元素,
CanIf_Transmit需要传入的CanTxPduId,即代表这个配置结构体数组的数组下标。用来索引到其抽象的对象属性。说起来比较枯燥,以下是CanIf_Transmit的实现函数:
Std_ReturnType CanIf_Transmit(PduIdType CanTxPduId,
const PduInfoType *PduInfoPtr)
{
Can_PduType canPdu;
const CanIf_TxPduConfigType *txEntry;
CanIf_ControllerModeType csMode;
CanIf_ChannelGetModeType pduMode;
VALIDATE(CanIf_Global.initRun, CANIF_TRANSMIT_ID, CANIF_E_UNINIT );
VALIDATE((PduInfoPtr != 0), CANIF_TRANSMIT_ID, CANIF_E_PARAM_POINTER );
// Get the controller from L-PDU handle
txEntry = CanIf_FindTxPduEntry(CanTxPduId);
if (txEntry == 0)
{
VALIDATE(FALSE, CANIF_TRANSMIT_ID, CANIF_E_INVALID_TXPDUID);
return E_NOT_OK;
}
CanIf_Arc_ChannelIdType channel = txEntry- >CanIfCanTxPduHthRef- >CanIfCanControllerIdRef;
// Get and verify the controller mode
if (CanIf_GetControllerMode(channel, &csMode) == E_NOT_OK){
return E_NOT_OK;
}
if (csMode != CANIF_CS_STARTED){ // CANIF_161
return E_NOT_OK;
}
// Get and verify the PDU channel mode control
if (CanIf_GetPduMode(channel, &pduMode) == E_NOT_OK){
return E_NOT_OK;
}
if ((pduMode != CANIF_GET_TX_ONLINE) && (pduMode != CANIF_GET_ONLINE)){
return E_NOT_OK;
}
canPdu.id = txEntry- >CanIfCanTxPduIdCanId;
canPdu.length = PduInfoPtr- >SduLength;
canPdu.sdu = PduInfoPtr- >SduDataPtr;
canPdu.swPduHandle = CanTxPduId;
Can_ReturnType rVal = Can_Write(txEntry- >CanIfCanTxPduHthRef- >CanIfHthIdSymRef, &canPdu);
if (rVal == CAN_NOT_OK){
return E_NOT_OK;
}
if (rVal == CAN_BUSY) // CANIF 082, CANIF 161
{
// Tx buffering not supported so just return.
return E_NOT_OK;
}
return E_OK;
}
注意到其中CanIf_Transmit的传入参数CanTxPduId的使用方式:
txEntry = CanIf_FindTxPduEntry(CanTxPduId);
CanIf_FindTxPduEntry的函数原型如下
static const CanIf_TxPduConfigType * CanIf_FindTxPduEntry(PduIdType id)
{
if (id >= CanIf_ConfigPtr- >InitConfig- >CanIfNumberOfCanTXPduIds) {
return NULL;
} else {
return &CanIf_ConfigPtr- >InitConfig- >CanIfTxPduConfigPtr[id];
}
}
就是以CanIf_Transmit的传入参数CanTxPduId为下标,找到对应的CanIfTxPduConfigData的数组成员。并获取其属性,对Can_Write函数的传入参数进行配置。调用Can_Write函数进行发送。
四.发送确认函数:CanIf_TxConfirmation
CanIf_TxConfirmation是由Can模块底层驱动在PDU传输完成后调用的。之前讲到Can_Write函数的其中一个传入参数:swPduHandle是用来在底层标记传输的PDU ID,在更新MessageBuffer前记住PDU对应的swPduHandle参数,在对应的PDU发出去后,底层驱动函数调用CanIf_TxConfirmation传入swPduHandle。
而我们的CanIf_TxConfirmation实现如下:
void CanIf_TxConfirmation(PduIdType canTxPduId)
{
VALIDATE_NO_RV(CanIf_Global.initRun, CANIF_TXCONFIRMATION_ID, CANIF_E_UNINIT)
VALIDATE_NO_RV(canTxPduId < CanIf_ConfigPtr- >InitConfig- >CanIfNumberOfCanTXPduIds, CANIF_TXCONFIRMATION_ID, CANIF_E_PARAM_LPDU);
const CanIf_TxPduConfigType* entry =
&CanIf_ConfigPtr- >InitConfig- >CanIfTxPduConfigPtr[canTxPduId];
if (entry- >CanIfUserTxConfirmation != NULL)
{
CanIf_ChannelGetModeType mode;
CanIf_GetPduMode(entry- >CanIfCanTxPduHthRef- >CanIfCanControllerIdRef, &mode);
if ((mode == CANIF_GET_TX_ONLINE) || (mode == CANIF_GET_ONLINE)
|| (mode == CANIF_GET_OFFLINE_ACTIVE) || (mode == CANIF_GET_OFFLINE_ACTIVE_RX_ONLINE) )
{
entry- >CanIfUserTxConfirmation(entry- >CanIfTxPduId); /* CANIF053 */
}
}
return;
}
在这个函数中,会直接向上文CanIfTxPduConfigData配置的CanIfUserTxConfirmation中传入swPduHandle。
而在CanIf_Transmit中,swPduHandle又是由CanIfTxPduConfigData配置的CanIfTxConfrimPduId决定的。所以CanIfTxConfrimPduId会作为参数传入对应的CanIfUserTxConfirmation。
这期的介绍就到这,本期介绍了CanIf主要实现的功能,主要函数,主要的发送配置以及CanIf_Transmit,CanIf_TxConfirmation的机制,可以了解CanIf做了更进一步的抽象,将HOH进一步抽象为了PDU。各个AUTOSAR架构的代码实现并不一致,文中所有的函数实现和配置思路仅作参考。
-
CAN通信
+关注
关注
5文章
93浏览量
17798 -
接收机
+关注
关注
8文章
1177浏览量
53375 -
AUTOSAR
+关注
关注
10文章
349浏览量
21441 -
PDU
+关注
关注
0文章
93浏览量
16944 -
CAN控制器
+关注
关注
3文章
74浏览量
14995
发布评论请先 登录
相关推荐
评论