Autosar CAN 报文接收 下集预告核间通讯

无聊逛51
发布于 2023-11-14 14:37
浏览
0收藏

“ 本文参考Tc397手册,ILLD库以及autosar 的一些代码”

接上篇autosar can tx 发送。《Autosar CAN 报文发送 下期预告CAN_RX》

了解硬件

  首先了解一下硬件本身支持多少路CAN,分别是什么样的关系。在实际使用中可能需要考虑到。下图来自3xx用户手册。

Autosar CAN 报文接收 下集预告核间通讯-鸿蒙开发者社区

从上面图加上描述可以看出,tc397有三个module 每个module有4路nodes. 所以一共可以支持12路can。本文主要介绍CAN的接收过程。一般来说有polling 和 interrupt两种模式。这里介绍中断模式。因为polling模式比较容易丢帧。必须和其他ECU配合的好的情况下,才不容易丢帧。

接收中断是怎么回事(这里和autosar没关系)

    上面说到一个module可以支持4路can. 下面是一个module支持的中断。那么他们分别是什么意思呢。

Interrupt Router Input

Connected to CAN Interrupt Output

SRC_CANzINT0

INT_O0 MCMCAN

SRC_CANzINT1

INT_O1 MCMCAN

SRC_CANzINT2

INT_O2 MCMCAN

SRC_CANzINT3

INT_O3 MCMCAN

SRC_CANzINT4

INT_O4 MCMCAN

SRC_CANzINT5

INT_O5 MCMCAN

SRC_CANzINT6

INT_O6 MCMCAN

SRC_CANzINT7

INT_O7 MCMCAN

SRC_CANzINT8

INT_O8 MCMCAN

SRC_CANzINT9

INT_O9 MCMCAN

SRC_CANzINT10

INT_O10 MCMCAN

SRC_CANzINT11

INT_O11 MCMCAN

SRC_CANzINT12

INT_O12 MCMCAN

SRC_CANzINT13

INT_O13 MCMCAN

SRC_CANzINT14

INT_O14 MCMCAN

SRC_CANzINT15

INT_O15 MCMCAN

0,1,2,3 || 4,5,6,7 || 8,9,10,11 || 12,13,14,15

这是四组分别对应的是四个nodes. 作用是一样的。这里介绍第一组。0,1,2,3

这里对应的是

  • 接收中断
  • 发送中断
  • busoff中断
  • rx fifo中断


这里我们先把rx的中断优先级和中断服务函数注册一下,后面详细描述。

注册中断优先级与对应的服务函数

 * IFX_INTERRUPT(isr, vectabNum, priority)
 *  - isr: Name of the ISR function.
 *  - vectabNum: Vector table number.
 *  - priority: Interrupt priority. Refer Usage of Interrupt Macro for more details.
IFX_INTERRUPT(canIsrRxHandler, 0, ISR_PRIORITY_CAN_RX);

这个IFX_INTERRUPT 是编译器提供的,下面是对应的一段汇编,仅供参考。可忽略。

#define IFX_INTERRUPT_INTERNAL(isr, vectabNum, prio) \
__asm__ (".ifndef .intr.entry.include                        \n"\
    ".altmacro                                           \n"\
    ".macro .int_entry.2 intEntryLabel, name # define the section and inttab entry code \n"\
        "  .pushsection .\\intEntryLabel,\"ax\",@progbits   \n"\
        "  __\\intEntryLabel :                              \n"\
        "    svlcx                                        \n"\
        "    movh.a  %a14, hi:\\name                      \n"\
        "    lea     %a14, [%a14]lo:\\name                \n"\
        "    ji      %a14                                 \n"\
        "  .popsection                                      \n"\
    ".endm                                               \n"\
    ".macro .int_entry.1 prio,vectabNum,u,name           \n"\
      ".int_entry.2 intvec_tc\\vectabNum\\u\\prio,(name) # build the unique name \n"\
    ".endm                                               \n"\
        "                                                    \n"\
    ".macro .intr.entry name,vectabNum,prio              \n"\
      ".int_entry.1 %(prio),%(vectabNum),_,name # evaluate the priority and the cpu number \n"\
    ".endm                                               \n"\
    ".intr.entry.include:                                \n"\
    ".endif                                              \n"\
        ".intr.entry "#isr","#vectabNum","#prio               );\

仅仅上面这样一下收到报文就可以触发中断了?显然不是这么简单。在mcal配置的话,确实就是这么简单,但是我们这里从最根本的寄存器说起。

上面提到的三个module 在内核里面对应的是有三个地址。

/** \brief CAN object */
#define MODULE_CAN0 /*lint --e(923, 9078)*/ ((*(Ifx_CAN*)0xF0200000u))
#define MODULE_CAN1 /*lint --e(923, 9078)*/ ((*(Ifx_CAN*)0xF0210000u))
#define MODULE_CAN2 /*lint --e(923, 9078)*/ ((*(Ifx_CAN*)0xF0220000u))
/** \}  */

我们说第一个。

这里我们找到RX中断SRC的 地址

/** \brief 5B4, CAN0 Service Request 1 */
#define SRC_CAN_CAN0_INT1 /*lint --e(923, 9078)*/ (*(volatile Ifx_SRC_SRCR*)0xF00385B4u)
/** Alias (User Manual Name) for SRC_CAN_CAN0_INT1.
* To use register names with standard convension, please use SRC_CAN_CAN0_INT1.
*/
#define SRC_CAN0INT1 (SRC_CAN_CAN0_INT1)

就在这个node下进行接收数据。我们使用什么方式呢?使用下面这个DRXE

Message stored to Dedicated Rx Buffer Interrupt Enable
0 B Interrupt disabled
1 B Interrupt enabled

Autosar CAN 报文接收 下集预告核间通讯-鸿蒙开发者社区

把这个位置位,也就是说当rx buffer 有新的报文填入,那么就会有中断触发。

Autosar CAN 报文接收 下集预告核间通讯-鸿蒙开发者社区

这里就对应到了上面的中断优先级和中断服务函数。重写一遍。

* IFX_INTERRUPT(isr, vectabNum, priority)
 *  - isr: Name of the ISR function.
 *  - vectabNum: Vector table number.
 *  - priority: Interrupt priority. Refer Usage of Interrupt Macro for more details.
IFX_INTERRUPT(canIsrRxHandler, 0, ISR_PRIORITY_CAN_RX);

所以总结一下,当 rx buffer 有新报文填入,这时候会触发中断,这个中断的优先级就是 ISR_PRIORITY_CAN_RX

这个中断对应的中断服务函数就是canIsrRxHandler

当然触发接收中断的方式有很多,还有上面的fifo 等机制。这里抛砖引玉详细的可以按照这种方式去看一下芯片手册。至此中断服务函数算是从物理线束跑到了中断服务函数。那么下面开始介绍一下autosar 在中断服务函数调到之后做了些什么。为了方便起见。这里的终端服务函数该名称autosar定义的名称

#define canIsrRxHandler Can_17_McmCan_IsrReceiveHandler

下面函数名统一成

/*******************************************************************************
** Traceability   : [cover parentID={B91B38C0-0F62-4a9a-BF22-9EC231B6521E}]   **
** Syntax           : void Can_17_McmCan_IsrReceiveHandler                    **
**                    (                                                       **
**                      const uint8 HwKernelId                                **
**                      const uint8 NodeIdIndex                               **
**                    )                                                       **
**                                                                            **
** Description    :The function should handle receive interrupts from         **
**                 dedicated receive buffers during CAN controller            **
**                 STARTED state.                                             **
**                 For dedicated reception the hardware filter code alone is  **
**                 considered, the receive mask available shall not be used   **
**                 during the filtering or processing of the message.         **
**                 In case of dedicated each hardware object can be configured**
**                 as INTERRUPT or POLLING. However as the interrupt lines are**
**                 shared, if one of the HRH is configured as INTERRUPT all   **
**                 dedicated objects on reception would trigger an interrupt. **
** [/cover]                                                                   **                                                            **
** Parameters(in)   :HwKernelId - The CAN controller which is to be processed,**
**                   is associated with the passed Kernel,                    **
**                   NodeIdIndex - The CAN node which is to be processed     ****                                                                            **
*******************************************************************************/
void Can_17_McmCan_IsrReceiveHandler(const uint8 HwKernelId,
                                     const uint8 NodeIdIndex)

CAN RX Mcal里面做了什么

通过上一章节,中断服务函数顺利进入。我们从代码出发,先看形参

void Can_17_McmCan_IsrReceiveHandler(const uint8 HwKernelId,
                                     const uint8 NodeIdIndex)

从形参可以看出来是上文提到的内核module 和 每个module有四个node. 这里的意思是 这一个具体的node 触发的中断。比如 KernelId == 0, NodeId == 0,  则就是我们配置的那个中断触发的。

首先根据具体的node 来获取 配置参数,以及内核node本身的地址参数等。

然后检查刚才配置的buffer里面是否有数据。并且清除调DRX寄存器。

判断状态START之后,进入真正的接收函数。

**  Parameters (in)  :  HwControllerId - Associated CAN Controller            **
**                      CheckBuffType -  Data received buffer type            **
**                      ProcessingType -  Rx processing type                  **
**                      CoreConfigPtr - Pointer to CAN driver configuration   **
static void Can_17_McmCan_lReceiveHandler(const uint8 HwControllerId,
    const Can_17_McmCan_RxBufferType CheckBuffType,
    const Can_17_McmCan_ProcessingType ProcessingType,
    const Can_17_McmCan_CoreGlobalType *const CoreGlobalPtr,
    const Can_17_McmCan_CoreConfigType *const CoreConfigPtr)

还是可以通过形参去看。

形参里面除了配置文件,还多出来了buffer 和 处理类型。这指的是什么呢。答:下面会通过配置根据这些类型进行针对性的读取数据。

{
  CAN_17_MCMCAN_RX_DED_BUFFER,
  CAN_17_MCMCAN_RX_FIFO0,
  CAN_17_MCMCAN_RX_FIFO1,
  CAN_17_MCMCAN_RX_ALL
} Can_17_McmCan_RxBufferType;


typedef enum
{
  CAN_17_MCMCAN_RX_FIFO_POLLING,
  CAN_17_MCMCAN_RX_FIFO_INTERRUPT,
  CAN_17_MCMCAN_RX_FIFO_NOT_CONFIGURED
} Can_17_McmCan_RxFIFOProcessingType;

这两个枚举放在这里就清楚了吧。实际上就是进行一系列的判断。来决定用哪一个回调函数来收取保温内容。

根据我们前面配置的接受模式是中断。并且buffer.通过CheckBuffType

switch (CheckBuffType)

我们这边就进一步确定了如何去接收数据。

**  Parameters (in)  : HwControllerId - Associated CAN Controller             **
**                     ProcessingType -  Rx processing type                   **
**                     CoreConfigPtr - Pointer to CAN driver configuration    **
**                     CoreGlobalPtr - Pointer to global structure            **
**  Parameters (in-out) :  none                                               **
**  Parameters (out) : none                                                   **
**                                                                            **
**  Return value     : none                                                   **
**                                                                            **
*******************************************************************************/
static void Can_17_McmCan_lRxDedicatedHandler(const uint8 HwControllerId,
                      const Can_17_McmCan_ProcessingType ProcessingType,
                      const Can_17_McmCan_CoreConfigType *const CoreConfigPtr,
                      const Can_17_McmCan_CoreGlobalType *const CoreGlobalPtr)

通过寄存器的各个位来知道有没有新的数据。和定位。

      Ifx_CAN_N_NDAT1                     NDAT1;                  /**< \brief 198, New Data 1 ${i}*/
       Ifx_CAN_N_NDAT2                     NDAT2;                  /**< \brief 19C, New Data 2 ${i}*/

Autosar CAN 报文接收 下集预告核间通讯-鸿蒙开发者社区

上面一直都在准备,都在确定新的数据和各种有效性。直到这里才开始真正的从buffer中读取到协议栈的buffer里。

static void Can_17_McmCan_lRxExtractData(const uint8 HwControllerId,
    const uint8 RxBuffIndex,
    const Can_17_McmCan_RxBufferType RxBuffer,
    const Can_17_McmCan_CoreGlobalType *const CoreGlobalPtr,
    const Can_17_McmCan_CoreConfigType *const CoreConfigPtr)

这里通过两段代码获取到can内核ram的buffer地址。

    if (TRUE ==
        CoreConfigPtr->CanControllerConfigPtr[HwControllerId].CanFDSupport)
    {
      /* Load the data receive message buffer address */
      RxReadStartAddr = RxReadStartAddr + (RxReadOffsetAddr *
                                 (uint32)CAN_17_MCMCAN_MSG_RAM_ELEMENT_SIZE_FD);
    }
    else
    #endif

    {

      /* Load the data receive message buffer address */
      RxReadStartAddr = RxReadStartAddr + (uint32)
                    (RxReadOffsetAddr * CAN_17_MCMCAN_MSG_RAM_ELEMENT_SIZE_NFD);
    }

注意这里的CANFD 和 CAN 是不一样的长度。但是下面的寄存器等访问,读写都是一致的。

首先对数据长度进行解析

   /* Extract DLC information */
    RxMsgDLC = RxMsgRAMPtr->R1.B.DLC;

最终会把内核的里数据,ID等信息传给 autosar定义的pdu里面

    /* Copy Message from RAM section to local data buffer */
    while (RxMsgCnt < RxMsgDLC)
    {
      /* copy data from byte0 to n the buffer */
      RxMessageData[HwControllerId][RxMsgCnt] = RxMsgRAMPtr->DB[RxMsgCnt].U;

      /* Increment to point next byte */
      RxMsgCnt++;
    }
    /* Call CanIf_RxIndication function with receive message information */
    RxMailBoxInfo.CanId = RxMsgId;

    RxMailBoxInfo.ControllerId = HwControllerId;

    RxPduInfo.SduLength = RxMsgDLC;

    /*MISRA2012_RULE_1_3_JUSTIFICATION:Address of auto variable is used
    only to read the values. The address is not used to perform any
    pointer arithmetic. hence no side effect has been seen.*/
    RxPduInfo.SduDataPtr = &RxMessageData[HwControllerId][0U];

所以一般的可以去查看RxMailBoxInfo 这个信息。这里面是否有想要的内容,内容对不对。从内核到本地ram再到传给BSW 协议栈的pdu信息。

直到这里,内核的ram数据已经完全的被取出来。过程有很多检查和CANFD 等check这里省略了 。只留下了主干。下一步到了BSW协议栈。

CAN RX BSW里面做了什么

FUNC(void, CANIF_CODE) CanIf_RxIndication(
                                            P2CONST (Can_HwType, AUTOMATIC, CANIF_APPL_DATA) Mailbox,
                                            P2CONST (PduInfoType, AUTOMATIC, CANIF_APPL_DATA) PduInfoPtr
                                         )

这里面先看形参, mailbox 和 pduinfo

这个mailbox 是和具体的pdu对应的。pduinfo 里面包含的是数据的具体信息。

如果不加入个人集成代码的话,直接到下面一步骤。

/* HIS METRIC PATH,v(G),STMT,LEVEL,RETURN VIOLATION IN CanIf_RxIndication_Internal: Function contains very simple "else if" statements and "switch-cases".
 * HIS metric compliance would decrease readability and maintainability. Also Function contains more than one return statement.
 * This is needed mainly because of DET. */
/* BSW-415 */
void CanIf_RxIndication_Internal( const Can_HwType * Mailbox,
        const PduInfoType * PduInfoPtr)

这一步主要干什么呢。

检查controller mode 是否正确。

 if((uint8)CANIF_CS_STARTED == (((ControllerState_ps->Ctrl_Pdu_mode)& CANIF_BIT_MASK_PDU_MODE)>>CANIF_Ctrl_BIT_SHIFT))

检查接收路径是否激活,等等规范性,正确性检查。

最终会通过下面两个文件找到对应配置的pdu.

CanIf_PBcfg.c
CanIf_Cfg.h

这里假设找到了。

才开始复制到真正的canif buffer里面


/***********************************************************************************************************************
** Function name     : CanIf_Prv_WriteRxBuffer                                                                        **
** Syntax            : void CanIf_Prv_WriteRxBuffer(Const uint8* CanSduPtr,Const CanIf_Cfg_RxPduType_tst* RxPduCfgPtr,**
**                     Const CanDlc,Const CanId)                                                                      **
** Service ID[hex]   : --                                                                                             **
** Sync/Async        : --                                                                                             **
** Reentrancy        : --                                                                                             **
** Parameters (in)   : CanSduPtr, RxPduCfgPtr                                                                         **
** Parameters (inout): None                                                                                           **
** Parameters (out)  : None                                                                                           **
** Return value      : None                                                                                           **
** Description       : Internal function called by API CanIf_RxIndication to write data to receive buffer             **
**********************************************************************************************************************/
#define CANIF_START_SEC_CODE
#include "CanIf_MemMap.h"
#if (CANIF_PUBLIC_READRXPDU_DATA_API == STD_ON && CANIF_CFG_READRXPDU_DATA_IN_RXPDUS == STD_ON)
void CanIf_Prv_WriteRxBuffer(const uint8 * CanSduPtr,
                                              const CanIf_Cfg_RxPduType_tst * RxPduCfg_pcst,
                                              const PduLengthType CanDlc, const Can_IdType CanId
                                               )

这个函数没什么好介绍的。正常的检测,copy函数。

之后陷入更进一步的函数。这里决定了下一步BSW的走向。

Std_ReturnType CanIf_XCore_LocalCore_RxIndication(const CanIf_Cfg_RxPduType_tst * CanIf_RXPduConfig_pst,
                                                                    const PduInfoType * CanIf_ULPduinfo_pst)

最重要的一步,这是个函数指针,指向不同的函数。

 /* Notification to Autosar types */
        CanIf_Prv_ConfigSet_tpst->RxAutosarUL_Ptr[RxPduConfig_pst->IndexForUL_u8].CanIfRxPduIndicationName(RxPduConfig_pst->RxPduTargetId_t, CanIf_ULPduinfo_pst);

主要的全局变量有

const CanIf_ConfigType * CanIf_Prv_ConfigSet_tpst;
static const CanIf_RxCbk_Prototype CanIf_Prv_ULName_ta__fct[] =
{
    {&CanNm_RxIndication},
    {&CanTp_RxIndication},
    {&PduR_CanIfRxIndication},
    {&Xcp_CanIfRxIndication},
};

这个在文件

CanIf_PBcfg.c

删改你一个是具体的配置,一个是canif的走向。所以当APP层收不到报文的时候,可以查一下这几个回调函数,有没有被调用到。如果没有的话,就按照上面文章一步一步去查。如果这一步有被执行到。那么基本和canif以及下面的mcal没有关系了。

好,这里我们假设调用到了PduR_CanIfRxIndication.也就是路由。

/**
 **************************************************************************************************
 * PduR_dCanIfRxIndication - PDU Router CanIfRxIndication
        Function to be invoked DET enable for PduR_CanIfRxIndication
 *      This function is called by the CanIf after a L-PDU has been received.
 *
 * \param           PduIdType CanIf_RxPduId
 *                  const uint8 *ptr
 *
 * \retval          None
 * \seealso
 * \usedresources
 **************************************************************************************************
 */
void PduR_dCanIfRxIndication( PduIdType id, const PduInfoType * ptr )

这里面主要决定路由到com层的具体哪里。涉及到很多配置与全局变量。

全局变量可以到这里找

PduR_PBcfg.c

这里给出了两个路径,一个是大数据com 一个是普通的com. 我们暂且认为路由到了普通的com.

const PduR_upIfRxIndicationFuncType PduR_upIfRxIndicationTable[] =
{
    {&PduR_RF_Com_RxIndication_Func},
    {&PduR_RF_LdCom_RxIndication_Func}
};

到这里面已经没有了报文的概念,完全的就是独立的单个的pdu.

/*
 **********************************************************************************************************************
 Function name    : Com_RxIndication
 Description      : Service called by the lower layer after an I-PDU has been received.
 Parameter        : idRxPdu_uo,pduInfoPtr_pcst
 Return value     : None
 **********************************************************************************************************************
*/
#define COM_START_SEC_CODE
#include "Com_MemMap.h"
void Com_RxIndication(PduIdType idRxPdu_uo, const PduInfoType * pduInfoPtr_pcst)

首先是对PDU本身属性进行检查,对PDU 的开关进行检查,检查是不是又group的存在。等等,检查完之后,要对pdu进行拆分成不同的signal.

因为上层是使用具体的signal. 

/*
/*
 **********************************************************************************************************************
 Function name    : Com_Prv_ProcessSignal
 Description      : Process rx-signals of the received I-PDU.
 Parameter        : idRxPdu_uo      - ID of the received I-PDU.
                  : pduInfoPtr_pcst - Contains the length (SduLength) of the received I-PDU and
                                 a pointer to a buffer (SduDataPtr) containing the I-PDU.
 Return value     : None
 **********************************************************************************************************************
*/
#define COM_START_SEC_CODE
#include "Com_MemMap.h"
void Com_Prv_ProcessSignal(PduIdType idRxPdu_uo, const PduInfoType * pduInfoPtr_pcst)

这里面就会根据DBC的信息,LSB,MSB 长度,等等信息来解读每一个signal.获取每一个signal的信息。

        /* Set the _SIGNOTIF flag to invoke configured signal-ComNotification */
        Com_SetRamValue(RXSIG,_SIGNOTIF,rxSigRamPtr_pst->rxSigRAMFields_u8,processSignal_b);

在取出所有的信息内容之后,调用com配置的回调函数。这个回调函数是针对每一个signal的。这里有一个重要的全局变量。也是配置产生的。

const Com_Prv_xRxSigCfg_tst Com_Prv_xRxSigCfg_acst[COM_NUM_RX_SIGNALS]

这里面记录了所有的回调函数,

这里比如。

Autosar CAN 报文接收 下集预告核间通讯-鸿蒙开发者社区

下面函数就是某一个signal在com里的回调。那么他实现了什么呢。

Rte_COMCbk_facaiziyou_signal


   comstatus = Com_ReceiveSignal(((VAR(Com_SignalIdType, AUTOMATIC))66), &facai);
   if ( ((VAR(StatusType, AUTOMATIC))E_OK) != comstatus )
   {
      read_ok = ((VAR(boolean, AUTOMATIC))FALSE);
   }
   /* Box: Data Conversion begin */
   /* Box: floating point data conversion for Reception direction begin */
   data = ( ( ((VAR(float32, AUTOMATIC))Rte_FPConv) + -2500.000000 ) / 5.000000 );
   /* Box: floating point data conversion for Reception direction end */
   /* Box: Data Conversion end */

   /* Box: receive end */
   /* Box: process begin */
   if ( TRUE == read_ok )
   {
      /* source OsApplications: OsApp_SysCore */
      /* providing OsApplication: OsApp_ComCore */
      /* destination OsApplication: OsApp_ComCore */
      (void)IocWrite_Rte_Rx_000660(data);

   }

通过receive 把数据读到了

IocWrite_Rte_Rx_000660

为什么这里是读到了Ioc呢。因为配置了不同的core.这里不详细介绍了。下次开一篇单独说一下。核间通讯

这里可以简单地理解 signal 到了一个全局变量。这个全局变量叫做IocWrite_Rte_Rx_000660


好了,终于结束了。谢谢观看




文章转载自公众号:汽车与基础软件



分类
标签
已于2023-11-14 14:37:07修改
1
收藏
回复
举报
回复
    相关推荐