• 读懂AUTOSAR规范,之CanIf 发送缓冲(带实例代码)


    1. General behavior一般行为

    在CanIf范围内,传输过程始于调用CanIf_Transmit(),并在调用上层模块的回调服务()时结束。在传输过程中,CanIf、CanDrv和CAN邮箱应共同将要传输的L-PDU仅存储一次在单个位置。根据传输方法,这些位置可以是:
    • CAN硬件传输对象或
    • 如果启用了传输缓冲,则为CanIf内的传输L-PDU缓冲区。

    对于触发传输,CanIf只需存储给定L-PDU的传输请求,而不是其数据。当HTH再次空闲时,数据将通过触发传输函数及时获取。请求传输的单个Tx L-PDU永远不应存储两次。这种行为对应于CAN网络上定期通信的通常方式。
    如果启用了传输缓冲,则如果CanDrv在传输请求时拒绝了Tx L-PDU,CanIf将在CanIf Transmit L-PDU缓冲区(CanIfBufferCfg)中存储它。

    基本上,CanIf中用于缓冲Tx L-PDU的整个缓冲区由一个或多个CanIfBufferCfg(请参见CanIfBufferCfg)组成。每个CanIfBufferCfg分配给一个或多个专用的CanIfBufferHthRef(请参见CanIfBufferHthRef),并可配置为缓冲一个或多个Tx L-PDU。但是,如上所述,在CanIfBufferCfg的总数中,每个Tx L-PDU只能缓冲一个实例。

    当对应的Tx L-PDU的配置设置中启用了传输缓冲时,CanIf在L-PDU传输期间的行为与未启用传输缓冲时不同。如果禁用传输缓冲并且向CanDrv发出的传输请求失败(CAN控制器邮箱正在使用,BasicCAN),则L-PDU不会复制到CAN控制器的邮箱中,CanIf_Transmit()返回值为E_NOT_OK。如果启用了传输缓冲并且传输请求发送失败,则根据CanIfTxBuffer配置,L-PDU可以存储在CanIfTxBuffer中。在这种情况下,尽管无法执行传输,API CanIf_Transmit()返回值为E_OK。在这种情况下,CanIf通过CanIf_TxConfirmation()回调处理L-PDU的未完成传输,上层模块无需重试传输请求。

    可用的传输CanIf Tx L-PDU缓冲区的数量可以完全独立于在CAN网络描述文件中定义的用于该ECU的已使用传输L-PDU的数量进行配置。
    根据[SWS_CANIF_00835],Tx L-PDU通过CanIfBufferCfg配置容器(请参见CanIfBufferCfg)引用HTH。即使不需要传输缓冲,这也是有效的。在这种情况下,CanIfBufferCfg的缓冲区大小(请参见CanIfBufferSize)必须设置为0。然后,CanIfBufferCfg配置容器仅用于引用HTH。

    2. Buffer characteristics缓冲区特性

    CanIfTxPduBufferRef、CanIfBufferCfg、CanIfBufferHthRef和CanIfBufferSize描述了可能的CanIfBufferCfg配置。

    2.1.将L-PDU存储在发送L-PDU缓冲区中

    CanIf仅在CanDrv在调用Can_Write()时返回CAN_BUSY(参见[SWS_CANIF_00381])时,尝试将新的发送L-PDU或其发送请求仅存储在发送L-PDU缓冲区中。(参考如下代码实例)

    1. //CanIf_TxConfirmation函数
    2. if((lCanRetVal == CAN_BUSY))
    3. {
    4. /*如果启用了传输缓冲,当CanDrv在传输请求时拒绝了一个Tx L-PDU时,CanIf会将其存储在一个CanIf传输L-PDU缓冲区(CanIfTxBuffer)中。*/
    5. #if(CANIF_PUBLIC_TXBUFFERING == STD_ON)
    6. /* CanIfTxBuffer的缓冲区大小配置为大于零。*/
    7. if((lTxPduConfig_pst->CanIf_TxBufferConfigPtr->CanIfBufferSize) > 0)
    8. {
    9. /* 进入临界区。 */
    10. SchM_Enter_CanIf_TxBufAccessNoNest(); /*RR*/
    11. lCanIf_TxPduId_temp = Pdu_temp.swPduHandle;
    12. /*[SWS_CANIF_00183]*/
    13. (void)CanIf_WriteTxBuffer(lCanIf_TxPduId_temp, Pdu_temp);
    14. /* 离开临界区。 */
    15. SchM_Exit_CanIf_TxBufAccessNoNest();
    16. }
    17. #endif

    这段代码片段来自函数'CanIf_TxConfirmation',它检查写CAN硬件对象(Can_Write())的返回值`lCanRetVal`是否等于`CAN_BUSY`。如果是,它会检查是否启用了传输缓冲(`CANIF_PUBLIC_TXBUFFERING == STD_ON`)。如果启用了缓冲并且缓冲区大小大于零,则进入一个临界区(`SchM_Enter_CanIf_TxBufAccessNoNest()`)并使用`CanIf_Prv_WriteTxBuffer()`函数将传输PDU(`Pdu_temp`)写入传输缓冲区。然后退出临界区(`SchM_Exit_CanIf_TxBufAccessNoNest()`)。,


    [SWS_CANIF_00063]  如果启用了参数CanIfPublicTxBuffering(参见CanIfPublicTxBuffering),CanIf应支持在CanIf中对BasicCAN传输的CAN L-PDU进行缓冲。

    注意:在mcal中配置CanHardwareObject时要配置发送邮箱为Basic类型才可以启用CanIf的发送Buffer。


    [SWS_CANIF_00849]  对于动态发送L-PDU,还必须将CanId存储在CanIfTxBuffer中。
    [SWS_CANIF_00381]  如果启用了发送缓冲(参见[SWS_CANIF_00063]),并且对于配置为直接传输的PDU,调用Can_Write()返回CAN_BUSY,则CanIf应检查是否可以将通过Can_Write()请求传输的CanIf Tx L-PDU缓冲在CanIfTxBuffer中。
    当调用Can_Write()返回CAN_BUSY时,表示CanDrv拒绝了L-PDU的请求传输(参见[1]),因为在传输请求(Tx请求)时没有可用的空闲硬件对象。
    [SWS_CANIF_00895] d 如果被拒绝的数据长度超过配置的大小,CanIf应:
    • 缓冲配置数量的数据并丢弃其余部分
    • 并向DET的Det_ReportError服务报告开发错误代码CANIF_E_DATA_LENGTH_MISMATCH。
    [SWS_CANIF_00881]  如果启用了发送缓冲(参见[SWS_CANIF_00063]),并且对于配置为触发传输的PDU,调用Can_Write()返回CAN_BUSY,则CanIf应检查是否可以将通过Can_Write()请求传输的发送请求缓冲在CanIfTxBuffer中。
    [SWS_CANIF_00835]  当CanIf检查是否可以缓冲CanIf Tx L-PDU或发送请求(参见[SWS_CANIF_00381]、[SWS_CANIF_00881])时,只有当CanIf Tx L-PDU分配给配置了大于零的缓冲区大小(参见CanIfBufferSize)的CanIfBufferCfg时,才能进行缓冲。
    任何CanIfTxBuffer的缓冲区大小只有在启用发送缓冲时才可配置大于零。此外,只有当CanIfTxBuffer分配给FullCAN HTH(参见CanIfBufferSize)时,单个CanIfTxBuffer的缓冲区大小才可配置大于零。(参考代码实例)

    [SWS_CANIF_00836]  如果可以缓冲CanIf Tx L-PDU或发送请求,因为已分配的CanIfTxBuffer的缓冲区大小大于零(参见[SWS_CANIF_00835]),则如果CanIf Tx L-PDU或发送请求尚未在CanIfTxBuffer中缓冲,则CanIf应在已分配的CanIfTxBuffer的空闲缓冲元素中缓冲CanIf Tx L-PDU或发送请求。(参考代码实例)

    1. /*[SWS_CANIF_00836]*/
    2. /*在SDU缓冲区中搜索空闲位置。*/
    3. CanIf_GetFreeTxBufferIndex(&lSduWriteIndex, lTxBufferConfig_pst);
    4. /*获取CanId队列缓冲区的下一个索引。*/
    5. CanIf_Prv_TxBufferRam_ast[lBufferId].last_index = (((CanIf_Prv_TxBufferRam_ast[lBufferId].last_index) + 1u)&(0xFFu));
    6. lCanIdWriteIndex = CanIf_Prv_TxBufferRam_ast[lBufferId].last_index;
    7. /*如果队列已满,则将缓冲区状态设置为CANIF_TXBUFFER_FULL。*/
    8. if(lCanIdWriteIndex == ((lBufferSize - 1u)&0xFFu))
    9. {
    10. CanIf_Prv_TxBufferRam_ast[lBufferId].bufferstatus = CANIF_TXBUFFER_FULL;
    11. }
    12. /*将要写入数据的SDU缓冲区索引存储起来。*/
    13. (lCanIdBuf_p + lCanIdWriteIndex)->BufferIndex = lSduWriteIndex;
    14. /*设置已经被PDU占用的SDU缓冲区索引的标志。*/
    15. *(lDataBuf_p + ((lSduWriteIndex)*(lDataMaxLen+1u)) + 0u) = 0x00u;
    16. /*设置此PDU已经被缓冲的标志。*/
    17. CanIf_Prv_TxPduRam_ast[CanIfTxSduId].pdu_buffered_flag = TRUE;

    这段代码片段来自函数'CanIf_TxConfirmation',用于在SDU缓冲区中查找空闲位置,并将数据写入该位置。具体实现包括以下步骤:

    调用CanIf_GetFreeTxBufferIndex函数,获取SDU缓冲区中下一个可用的位置。
    更新CanId队列缓冲区的下一个索引。
    如果队列已满,则将缓冲区状态设置为CANIF_TXBUFFER_FULL。
    存储SDU缓冲区的索引,以便写入数据。
    设置SDU缓冲区索引已被占用的标志。
    设置PDU已被缓冲的标志。

    [SWS_CANIF_00068]  如果可以缓冲CanIf Tx L-PDU或发送请求,因为已分配的CanIfTxBuffer的缓冲区大小大于零(参见[SWS_CANIF_00835]),则如果Can_Write()返回CAN_BUSY时CanIf Tx L-PDU已经在CanIfTxBuffer中缓冲,则CanIf应覆盖在已分配的CanIfTxBuffer中直接传输的CanIf Tx L-PDU。(SRS_Can_01011)
    注意:对于已存储的发送请求(参见[SWS_CANIF_00068]),由于数据将由CanDrv直接捕获(使用CanIf_TriggerTransmit()),因此最新的数据将自动发送。
    如果要保留不同L-PDU的各种传输请求的顺序,则必须将上层模块的传输请求连接到先前的传输确认通知。这意味着仅当CanIf通知了先前一个L-PDU的传输确认时,上层模块才会请求后续L-PDU进行传输。
    注意:另外,传输请求的顺序可能因配置的硬件传输对象数量而异。
    [SWS_CANIF_00837]  如果缓冲区大小大于零,所有缓冲元素都忙,且使用新的L-PDU(没有相同L-PDU的其他实例已存储在缓冲区中)调用CanIf_Transmit(),则不应存储新L-PDU或其发送请求,并且CanIf_Transmit()应返回E_NOT_OK。(参考如下代码实例)

    1. /*[SWS_CANIF_00837], [SWS_CANIF_00835]*/
    2. if ( /*缓冲区已满并且传入的PDU尚未被缓冲。*/
    3. ((CanIf_Prv_TxBufferRam_ast[lBufferId].bufferstatus == CANIF_TXBUFFER_FULL)
    4. && (CanIf_Prv_TxPduRam_ast[CanIfTxSduId].pdu_buffered_flag != TRUE))
    5. /*CanIfTxBuffer已分配给FullCAN HTH。*/
    6. ||(lCanHandleType == CANIF_FULL)
    7. )
    8. {
    9. lRetValue = E_NOT_OK; /*PDU未被缓冲*/
    10. }

    这段代码来自函数'CanIf_TxConfirmation',首先检查缓冲区是否已满并且传入的PDU尚未被缓冲,或者CanIfTxBuffer已分配给FullCAN HTH。如果是这两种情况之一,则将返回值设置为E_NOT_OK,表示PDU未被缓冲。其中,CanIf_Prv_TxBufferRam_ast和CanIf_Prv_TxPduRam_ast是两个结构体数组,用于存储CanIf Tx缓冲区和TxPdu的信息。lBufferId和CanIfTxSduId是两个变量,分别用于索引CanIf_Prv_TxBufferRam_ast和CanIf_Prv_TxPduRam_ast数组。lCanHandleType是一个变量,用于存储Can处理类型的信息。 

    2.2. 清除发送L-PDU缓冲区

    [SWS_CANIF_00386] 在传输确认期间(参见[SWS_CANIF_00007]),CanIf应评估是否在分配给新的空闲硬件传输对象的CanIfTxBuffers中存储了挂起的CanIf Tx L-PDU或发送请求(参见[SWS_CANIF_00466])。

    [SWS_CANIF_00466] d 每个CanIf Tx L-PDU在配置时应静态分配给一个CanIfBufferCfg配置容器(请参见CanIfTxPduBufferRef)。c()
    [SWS_CANIF_00466]的原因:CanIf Tx L-PDUs不引用HTHs,而是引用CanIfBufferCfg,而CanIfBufferCfg又引用HTHs。


    [SWS_CANIF_00668] 如果根据[SWS_CANIF_00386]存在挂起的CanIf Tx L-PDU或发送请求,则CanIf应使用最高优先级(参见[SWS_CANIF_00070])调用Can_Write()以传输该挂起的CanIf Tx L-PDU或发送请求(分配给新的硬件传输对象之一)。
    [SWS_CANIF_00070] CanIf应按优先级顺序(参见[12])每个HTH传输存储在发送L-PDU缓冲区中的L-PDUs或发送请求。CanIf不应区分L-PDUs和发送请求。

    [SWS_CANIF_00183] 当CanIf为存储在CanIfTxBuffer中的优先级L-PDUs和发送请求调用函数Can_Write(),并且Can_Write()的返回值为E_OK时,则CanIf应在传输确认返回之前立即从发送L-PDU缓冲区中删除此L-PDU或发送请求。
    [SWS_CANIF_00183]中指定的行为简化了存储在发送L-PDU缓冲区中的新传输L-PDU的选择。

    2.3. 发送L-PDU缓冲区的初始化

    [SWS_CANIF_00387] 当调用函数CanIf_Init()时,CanIf应初始化分配给CanIf的每个发送L-PDU缓冲区。
    要防止在CAN控制器重新启动后传输旧数据,需要满足[SWS_CANIF_00387]要求。

    3. 发送L-PDU缓冲区的数据完整性

    [SWS_CANIF_00033] CanIf应保护发送L-PDU缓冲区,防止对其进行并发访问以进行发送L-PDUs和发送请求。(SRS_Can_01114)这可以通过在BSW调度程序中定义的排他区域来实现。可以配置这些排他区域,例如,在进入排他区域时禁用所有中断。BSW调度程序模块的相应服务为SchM_Enter_CanIf()和SchM_Exit_CanIf()。
    原因:对于[SWS_CANIF_00033]:不能总是避免对发送L-PDU缓冲区的抢占式访问。这样的发送L-PDU缓冲区访问,例如存储新的L-PDU或删除已传输的L-PDU可能会抢占地发生。

  • 相关阅读:
    javaEE -6(10000详解文件操作)
    到什么程度才叫精通 Linux?
    【测试开发】用例篇 · 熟悉黑盒测试用例设计方法(2)· 正交表 · 场景设计 · 常见案例练习
    基于ITIL的ITSM工具
    Vitepress搭建组件库文档(下)—— 组件 Demo
    【项目经验】Redis Sentinel从工程中下线并对业务迁移-进行中
    WebStorm 2023:让您更接近理想的开发环境 mac/win版
    vue3中的吸顶导航交互实现 | VueUse插件
    538. 把二叉搜索树转换为累加树
    电力系统规划学习笔记(长期更新)
  • 原文地址:https://blog.csdn.net/weixin_46481662/article/details/132713125