CanIf发送逻辑CanIf_Transmit机制及CanIf发送配置解析

电子说

1.3w人已加入

描述

一.前言:

整个的AUTOSAR的CAN通信模块的层次如图:

CAN控制器

通信模块可以大致分为三大功能:数据发送功能,数据接收功能,状态切换和管理功能。

我们的系列按照这样的功能排序和自底向上的模块排序,每次只介绍其中的一个模块的其中一个功能的实现,所以在每篇文章中对于模块不会事无巨细的介绍所有它所具备的功能,而是选择性的介绍三大功能中的其中一个。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模块实现的基本函数如下:

CAN控制器

本文侧重介绍CanIf模块的发送功能。CanIf的报文发送功能由CanIf_Transmit函数实现,此函数原型如下:

Std_ReturnType CanIf_Transmit(PduIdType CanTxPduId,
                        const PduInfoType *PduInfoPtr)

此函数会调用MCAL的Can模块中的Can_Write函数进行报文的发送。在上节介绍Can_Write函数的时候,说过其传入的参数是以下:

  1. HOH
  2. CAN id:报文ID
  3. length:报文长度
  4. sdu:报文数据
  5. swPduHandle :tx_confirm的ID

而CanIf层的传入参数变为了:

  1. CanTxPduId,
  2. 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架构的代码实现并不一致,文中所有的函数实现和配置思路仅作参考。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分