0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

冬至子 来源:汽车电子过山车 作者:欢乐皮皮峰 2023-07-17 15:51 次阅读

一.前言:

整个的AUTOSAR的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模块实现的基本函数如下:

1.jpg

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

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

    关注

    4

    文章

    91

    浏览量

    17682
  • 接收机
    +关注

    关注

    8

    文章

    1150

    浏览量

    52920
  • AUTOSAR
    +关注

    关注

    9

    文章

    334

    浏览量

    21289
  • PDU
    PDU
    +关注

    关注

    0

    文章

    92

    浏览量

    16854
  • CAN控制器
    +关注

    关注

    3

    文章

    74

    浏览量

    14951
收藏 人收藏

    评论

    相关推荐

    ZYNQ进阶:PL端UART 发送设计案例

    主要是uart发送模块的编码讲述,uart发送模块设计主要分为波特率控制计数逻辑和按位发送逻辑,其具体编码如下所示: 波特率控制计数
    的头像 发表于 11-25 17:26 3238次阅读
    ZYNQ进阶:PL端UART <b class='flag-5'>发送</b>设计案例

    USB复合设备MSC+CDC,CDC_Transmit_FS函数发送不正常的原因?

    接收不到数据。(多次尝试打开、关闭串口,打开起始会收到一些数据) 2、在接收中断中,调用CDC_Transmit_FS将收到的数据,发回给上位机正常 3、在接收中断中,设置接收数据标志,主循环中发送也正常 总结:CDC先收到数据,再发送
    发表于 03-13 07:40

    cubemx配置的串口中断发送HAL_UART_Transmit_IT不行

    为何HAL_UART_Transmit发送,改成HAL_UART_Transmit_IT就不行了cube配置的stm32f407系统其他代码均一样while(HAL_OK
    发表于 12-10 08:39

    请问怎么用HAL_UART_Transmit发送数值型数据?

    如何用HAL_UART_Transmit 发送数值型数据?我看例程,都是发送字符型数据的uint8_t TxData[10]= "01234abcde"
    发表于 02-19 07:40

    AUTOSAR_MCAL_CAN_IM.pdf提示找不到 config/CanIf.xdm怎么解决?

    在 AUTOSAR_MCAL_CAN_IM.pdf 文件中声明还需要 CanIf 插件来实现 CAN 插件。但是,当我尝试在 Tresos 上添加 CanIf 模块时,它给出了一个错误,提示找不到
    发表于 03-30 08:48

    在EB tresos 23.0.0中配置模块时出错怎么解决?

    在EB tresos 23.0.0中配置模块时出错模块“CanIf_TS_T40D2M10I3R0”(在插件“CanIf_TS_T40D2M10I3R0”中定义)定义了无法找到的模式文件
    发表于 04-11 09:06

    使用spi_device_transmit()来发送和接收数据,发送的数据不正确怎么解决?

    使用 spi_device_transmit() 来发送和接收数据。我收到的数据是正确的。就代码而言,我发送的数据不正确。在 ESP32 上我有这个循环: uint8_t tx_buffer[3
    发表于 04-12 08:44

    Verilog实现UART之二:发送模块

    为波特率的16倍,因此对clk16x计数到16时,发送D0;计数到32时,发送D1……依此类推; 2.发送模块代码: module tx_module( inputclk16x,/*trans
    发表于 02-09 09:48 658次阅读
    Verilog实现UART之二:<b class='flag-5'>发送</b>模块

    如何让接收端知道发送端将要发送的字节流长度?

    tcp会将数据量较小,且发送时间间隔较短的数据一起打包发送,那么这里所讲的时间较短是相比网络延迟来说的。比如我们两次发送间隔为0.00001s,那么网络延迟为0.001s,这个时候两次的数据就会打包
    的头像 发表于 08-19 09:33 1296次阅读
    如何让接收端知道<b class='flag-5'>发送</b>端将要<b class='flag-5'>发送</b>的字节流长度?

    基于CAN总线进行网络管理与Transceiver的关系梳理

    Transceiver1145在初始化或者handler程序中检查唤醒源,判断到上电或者CAN总线干扰事件时会通知EcuM有唤醒事件,之后EcuM通过CanIf模块调用CanTrcv_CheckWakeup检查唤醒源。
    发表于 08-23 11:19 2570次阅读
    基于CAN总线进行网络管理与Transceiver的关系梳理

    AUTOSAR通信之CanIf模块简介1

    CAN接口模块(下文简“CanIf”)位于底层CAN驱动(CanDrv)、CAN收发器(CanTrcv)和上层通信服务层(CanSm、CanNm)、CAN传输协议(CanTp)、PDU路由器(PduR)之间。它表示上层通信层的CAN驱动程序服务接口。
    的头像 发表于 02-13 14:29 3215次阅读
    AUTOSAR通信之<b class='flag-5'>CanIf</b>模块简介1

    AUTOSAR通信之CanIf模块简介2

    CAN接口模块(下文简“CanIf”)位于底层CAN驱动(CanDrv)、CAN收发器(CanTrcv)和上层通信服务层(CanSm、CanNm)、CAN传输协议(CanTp)、PDU路由器(PduR)之间。它表示上层通信层的CAN驱动程序服务接口。
    的头像 发表于 02-13 14:29 1880次阅读
    AUTOSAR通信之<b class='flag-5'>CanIf</b>模块简介2

    AUTOSAR通信之CanIf模块简介3

    CAN接口模块(下文简“CanIf”)位于底层CAN驱动(CanDrv)、CAN收发器(CanTrcv)和上层通信服务层(CanSm、CanNm)、CAN传输协议(CanTp)、PDU路由器(PduR)之间。它表示上层通信层的CAN驱动程序服务接口。
    的头像 发表于 02-13 14:29 2638次阅读
    AUTOSAR通信之<b class='flag-5'>CanIf</b>模块简介3

    EthIf模块的主要作用是什么?Ethif的常见函数接口有哪些呢?

    正如我们了解的CanIf模块一样,作为CAN收发器,CAN控制器的统一上层ECU抽象,能够让我们实现CanIf上层的应用模块与底层硬件解耦,大大增加了软件的可移植性。
    的头像 发表于 03-23 11:27 1154次阅读

    PduR模块的发送机制 PduR这个模块对什么进行了抽象呢?

    前两期,我们讲了CAN模块的发送逻辑,Canif模块的发送逻辑发送确认
    的头像 发表于 07-17 15:54 2340次阅读
    PduR模块的<b class='flag-5'>发送机制</b> PduR这个模块对什么进行了抽象呢?