云计算百科
云计算领域专业知识百科平台

AUTOSAR CP平台从应用SWC到CAN控制器电平:数据发送的完整旅程

从应用SWC到CAN控制器电平:数据发送的完整旅程——AUTOSAR CP平台深度解析

免责声明
本文内容仅限技术学习与交流使用。文中涉及的所有AUTOSAR配置方法、模块行为描述、代码示例和工具链建议均基于AUTOSAR Classic Platform R4.x公开标准文档及行业通用实践。文中所有案例、故事、对话场景、人物及企业名称均为完全虚构,仅用于通过具体生动的场景帮助读者理解抽象技术规范。若将文中内容应用于实际项目,请务必以AUTOSAR官方发布的最新规范为准,并结合具体芯片厂商的MCAL实现进行验证。因使用不当造成的任何损失,责任由使用者自行承担。


引子:一个刹车信号的两万公里旅程

朋友,想象你坐在驾驶座上,轻轻踩下刹车踏板。刹车踏板上有一个传感器,它把踏板的压力转换成电信号。这个信号经过ADC采样后,变成了一个数字值——比如"刹车压力=100bar"。

现在,这个数值需要从你的ECU发送到整车CAN网络上,让其他ECU(比如刹车灯控制器、ESP、仪表盘)都能接收到。但问题是:这个简单的数字,是如何从应用层的SWC代码,最终变成CAN总线上一串高低电平的?

这中间经历了多少层软件处理?每一层做了什么?数据在每一层是什么样子?这就是我们今天要探索的完整旅程。

我会带你穿过AUTOSAR CP平台的每一层模块,看它们如何把"100bar"这个数字,一步一步变成CAN总线上的电信号。这不是一次简单的代码走读,而是一次从"应用逻辑"到"物理世界"的跨界旅行。我们会看到:

  • 一个浮点数如何被压缩成二进制位
  • 多个信号如何挤进8个字节的空间
  • 一个逻辑报文如何找到正确的硬件通道
  • 一个数字电平如何变成差分信号

更重要的是,我们会看到 AUTOSAR配置在每一层如何发挥作用——没有配置,这些代码就是一堆没有灵魂的骨架。配置决定了信号如何打包、I-PDU如何路由、CAN帧如何组装。正是配置,让同一套代码能够适应不同的车型、不同的网络拓扑、不同的硬件平台。

现在,系好安全带,我们出发。

第一章:全景概览——发送旅程的"七站"

在开始详细的逐站解析之前,让我们先用一张全景图来建立整体认知。当我们从应用层SWC发送一个刹车压力信号时,数据将经过以下七个"站点":

#mermaid-svg-y8ZYduipogyD6omX{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-y8ZYduipogyD6omX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y8ZYduipogyD6omX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y8ZYduipogyD6omX .error-icon{fill:#552222;}#mermaid-svg-y8ZYduipogyD6omX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y8ZYduipogyD6omX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y8ZYduipogyD6omX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y8ZYduipogyD6omX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y8ZYduipogyD6omX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y8ZYduipogyD6omX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y8ZYduipogyD6omX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y8ZYduipogyD6omX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y8ZYduipogyD6omX .marker.cross{stroke:#333333;}#mermaid-svg-y8ZYduipogyD6omX svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y8ZYduipogyD6omX p{margin:0;}#mermaid-svg-y8ZYduipogyD6omX .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-y8ZYduipogyD6omX .cluster-label text{fill:#333;}#mermaid-svg-y8ZYduipogyD6omX .cluster-label span{color:#333;}#mermaid-svg-y8ZYduipogyD6omX .cluster-label span p{background-color:transparent;}#mermaid-svg-y8ZYduipogyD6omX .label text,#mermaid-svg-y8ZYduipogyD6omX span{fill:#333;color:#333;}#mermaid-svg-y8ZYduipogyD6omX .node rect,#mermaid-svg-y8ZYduipogyD6omX .node circle,#mermaid-svg-y8ZYduipogyD6omX .node ellipse,#mermaid-svg-y8ZYduipogyD6omX .node polygon,#mermaid-svg-y8ZYduipogyD6omX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-y8ZYduipogyD6omX .rough-node .label text,#mermaid-svg-y8ZYduipogyD6omX .node .label text,#mermaid-svg-y8ZYduipogyD6omX .image-shape .label,#mermaid-svg-y8ZYduipogyD6omX .icon-shape .label{text-anchor:middle;}#mermaid-svg-y8ZYduipogyD6omX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-y8ZYduipogyD6omX .rough-node .label,#mermaid-svg-y8ZYduipogyD6omX .node .label,#mermaid-svg-y8ZYduipogyD6omX .image-shape .label,#mermaid-svg-y8ZYduipogyD6omX .icon-shape .label{text-align:center;}#mermaid-svg-y8ZYduipogyD6omX .node.clickable{cursor:pointer;}#mermaid-svg-y8ZYduipogyD6omX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-y8ZYduipogyD6omX .arrowheadPath{fill:#333333;}#mermaid-svg-y8ZYduipogyD6omX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-y8ZYduipogyD6omX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-y8ZYduipogyD6omX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y8ZYduipogyD6omX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-y8ZYduipogyD6omX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y8ZYduipogyD6omX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-y8ZYduipogyD6omX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-y8ZYduipogyD6omX .cluster text{fill:#333;}#mermaid-svg-y8ZYduipogyD6omX .cluster span{color:#333;}#mermaid-svg-y8ZYduipogyD6omX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-y8ZYduipogyD6omX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-y8ZYduipogyD6omX rect.text{fill:none;stroke-width:0;}#mermaid-svg-y8ZYduipogyD6omX .icon-shape,#mermaid-svg-y8ZYduipogyD6omX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y8ZYduipogyD6omX .icon-shape p,#mermaid-svg-y8ZYduipogyD6omX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-y8ZYduipogyD6omX .icon-shape .label rect,#mermaid-svg-y8ZYduipogyD6omX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y8ZYduipogyD6omX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-y8ZYduipogyD6omX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-y8ZYduipogyD6omX :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

硬件层

MCAL层

ECU抽象层

服务层

RTE层

应用层

Rte_Write

Com_SendSignal

PduR_Transmit

CanIf_Transmit

CanDrv_Transmit

写寄存器

TX引脚

CAN_H/CAN_L

SWC刹车控制软件组件产生数据:100 bar

RTE运行时环境路由信号到COM

COM通信管理器信号打包为I-PDU

PduRPDU路由器路由I-PDU到CanIf

CanIfCAN接口映射I-PDU到硬件通道

CanDrvCAN驱动写入CAN控制器寄存器

CAN控制器硬件仲裁与发送

CAN收发器差分电平输出

CAN总线

这张图看起来有点抽象,没关系。下面我们用一个生活化的类比来理解。

类比:快递寄送流程

想象你要寄一个包裹(刹车压力=100bar)给朋友(其他ECU):

  • SWC(寄件人):你在家里准备好包裹,写上收件人信息。你不关心快递公司怎么运输,只关心包裹能送到。
  • RTE(快递员上门取件):快递员到你家取走包裹。他是你与快递公司之间的唯一接口。
  • COM(打包中心):包裹被送到打包中心。打包中心会把你的小包裹和其他人的小包裹一起装进一个大箱子(I-PDU),提高运输效率。
  • PduR(分拣中心):大箱子被送到分拣中心。分拣中心根据目的地决定走哪条运输线路(CAN0、CAN1、LIN、以太网)。
  • CanIf(运输调度):调度中心决定用哪辆车、哪个司机来运输。它知道哪些车可用,每辆车能装多少货。
  • CanDrv(司机):司机把箱子装上车,踩油门,开始运输。他直接操作货车(硬件寄存器)。
  • CAN控制器和收发器(货车和公路):货车在公路上行驶(CAN总线),最终到达目的地。
  • 接下来,我们就逐站深入,看看每一站的内部细节,以及AUTOSAR配置如何在这一站发挥作用。

    第二章:第一站——应用SWC:信号的"诞生地"

    2.1 SWC是什么?

    SWC(Software Component,软件组件)是AUTOSAR架构中最上层的软件单元。它就像建筑中的"房间"——每个房间有特定的功能。一个刹车控制SWC负责采集刹车踏板信号、计算刹车压力、并把这个压力值发送给其他SWC。

    SWC是硬件无关的。这意味着同一个刹车控制SWC的代码,可以运行在任何MCU上,只要它通过RTE与底层通信。这种硬件无关性使得应用层软件可以跨平台复用——这是AUTOSAR的核心价值之一。

    在AUTOSAR方法论中,SWC的设计分为两个层面:

    • VFB(Virtual Functional Bus,虚拟功能总线)层面:SWC之间通过**端口(Port)和接口(Interface)**相互连接。发送方通过P-Port(Provider Port)提供数据,接收方通过R-Port(Require Port)消费数据。
    • RTE层面:RTE将VFB层面的抽象连接映射到具体的操作系统任务和函数调用。

    刹车控制SWC的端口定义示例:

    <!– 刹车控制SWC的端口定义(ARXML片段) –>
    <SW-COMPONENT-PROTOTYPE>
    <SHORT-NAME>BrakeControl_SWC</SHORT-NAME>
    <PORTS>
    <!– 输出端口:刹车压力信号 –>
    <P-PORT-PROTOTYPE>
    <SHORT-NAME>BrakePressure_Out</SHORT-NAME>
    <PROVIDED-INTERFACE-TREF>/Interfaces/SenderReceiver_BrakeData</PROVIDED-INTERFACE-TREF>
    </P-PORT-PROTOTYPE>
    <!– 输入端口:刹车踏板传感器值 –>
    <R-PORT-PROTOTYPE>
    <SHORT-NAME>PedalSensor_In</SHORT-NAME>
    <REQUIRED-INTERFACE-TREF>/Interfaces/SenderReceiver_PedalSensor</REQUIRED-INTERFACE-TREF>
    </R-PORT-PROTOTYPE>
    </PORTS>
    </SW-COMPONENT-PROTOTYPE>

    在代码层面,SWC的Runnable看起来像这样:

    /**
    * @file BrakeControl_SWC.c
    * @brief 刹车控制SWC的Runnable实现
    *
    * @note
    * – SWC的代码是硬件无关的
    * – 所有与底层的交互都通过RTE生成的接口
    * – SWC不关心信号最终通过什么总线发送
    */

    #include "Rte_BrakeControl_SWC.h"

    /**
    * @brief 刹车控制SWC的Runnable:采集并发送刹车压力
    *
    * @note
    * – Rte_Read_BrakePedalSensor:从传感器读取踏板位置
    * – Rte_Write_BrakePressure:发送刹车压力信号
    */

    void Runnable_SendBrakePressure(void)
    {
    float pedal_position = 0.0f;
    float brake_pressure = 0.0f;

    /* 从传感器读取踏板位置(通过RTE输入端口) */
    Rte_Read_BrakePedalSensor(&pedal_position);

    /* 计算刹车压力值 */
    brake_pressure = pedal_position * CALIBRATION_GAIN;

    /* 发送刹车压力信号(通过RTE输出端口) */
    Rte_Write_BrakePressure(brake_pressure);
    }

    关键点:SWC不关心"刹车压力"这个信号最终通过什么总线(CAN/LIN/以太网)发送,不关心放在报文的哪个字节,不关心CAN ID是多少。它只关心一件事:我有数据要发出去。

    2.2 数据在SWC中的形态

    在SWC中,数据是**信号(Signal)**的形态。信号是一个有意义的物理量:

    • 刹车压力 = 100 bar
    • 车速 = 60 km/h
    • 发动机转速 = 2500 rpm

    信号的类型、范围和精度在AUTOSAR数据类型的配置中定义:

    <!– 数据类型定义:刹车压力 –>
    <IMPLEMENTATION-DATA-TYPE>
    <SHORT-NAME>BrakePressure_Type</SHORT-NAME>
    <SW-DATA-DEF-PROPS>
    <COMPU-METHOD-REF>/CompuMethods/BrakePressure_Compu</COMPU-METHOD-REF>
    <UNIT-REF>/Units/Bar</UNIT-REF>
    </SW-DATA-DEF-PROPS>
    <TYPE-EMITTER>Platform_Type</TYPE-EMITTER>
    </IMPLEMENTATION-DATA-TYPE>

    <!– 计算方法:刹车压力的物理值到原始值的转换 –>
    <COMPU-METHOD>
    <SHORT-NAME>BrakePressure_Compu</SHORT-NAME>
    <UNIT-REF>/Units/Bar</UNIT-REF>
    <COMPU-INTERNAL-TO-PHYS>
    <COMPU-SCALES>
    <COMPU-SCALE>
    <LOWER-LIMIT>0</LOWER-LIMIT>
    <UPPER-LIMIT>65535</UPPER-LIMIT>
    <COMPU-RATIONAL-COEFFS>
    <COMPU-NUMERATOR>
    <V>1</V>
    </COMPU-NUMERATOR>
    <COMPU-DENOMINATOR>
    <V>1</V>
    </COMPU-DENOMINATOR>
    </COMPU-RATIONAL-COEFFS>
    </COMPU-SCALE>
    </COMPU-SCALES>
    </COMPU-INTERNAL-TO-PHYS>
    </COMPU-METHOD>

    这个配置定义了:BrakePressure信号使用16位无符号整数存储,物理值范围0~65535 bar,精度1 bar/LSB。SWC中使用的float类型是RTE在应用层的表示,实际传输时会转换为16位整数。

    第三章:第二站——RTE:应用与BSW之间的"桥梁"

    3.1 RTE的角色

    RTE(Runtime Environment,运行时环境)是连接应用层和基础软件层的唯一桥梁。SWC不能直接调用任何BSW模块的函数——它只能通过RTE生成的接口来与外界通信。

    RTE的核心功能:

    功能说明
    接口生成 根据SWC的端口定义,为每个SWC生成Rte_Write_xxx、Rte_Read_xxx等函数
    数据路由 将SWC的输出信号路由到正确的BSW模块(如COM)
    跨分区通信 如果SWC和BSW运行在不同的OS分区,通过IOC机制跨越分区边界
    触发调度 根据配置,在数据更新时触发接收方的Runnable

    当SWC调用Rte_Write_BrakePressure(100.0f)时,RTE需要完成以下工作:

  • 识别目标:根据配置,RTE知道BrakePressure这个信号属于哪个I-PDU,由哪个BSW模块(COM)负责处理。
  • 跨分区通信:如果SWC和COM运行在不同的OS分区,RTE需要通过IOC机制跨越分区边界。
  • 调用COM接口:最终,RTE调用COM的Com_SendSignal函数,将信号值传递给COM模块。
  • RTE的配置核心——信号映射:

    在AUTOSAR的ECU配置中,有一个关键的映射关系:SWC端口 → 系统信号 → I-PDU信号。这个映射链将应用层的抽象端口与通信层的具体信号连接起来。

    <!– RTE信号映射:从SWC端口到系统信号 –>
    <SIGNAL-MAPPING>
    <SHORT-NAME>BrakePressure_Mapping</SHORT-NAME>
    <DATA-ELEMENT-IREF>
    <PORT-PROTOTYPE-REF>/ECU/BrakeControl_SWC/BrakePressure_Out</PORT-PROTOTYPE-REF>
    <DATA-ELEMENT-PROTOTYPE-REF>/Interfaces/SenderReceiver_BrakeData/BrakePressure</DATA-ELEMENT-PROTOTYPE-REF>
    </DATA-ELEMENT-IREF>
    <SYSTEM-SIGNAL-IREF>
    <SIGNAL-REF>/Signals/BrakePressure_Signal</SIGNAL-REF>
    </SYSTEM-SIGNAL-IREF>
    </SIGNAL-MAPPING>

    COMRTESWCCOMRTESWC#mermaid-svg-9Z1yAxRIzGIb2Q4s{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9Z1yAxRIzGIb2Q4s .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .error-icon{fill:#552222;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .marker.cross{stroke:#333333;}#mermaid-svg-9Z1yAxRIzGIb2Q4s svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9Z1yAxRIzGIb2Q4s p{margin:0;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9Z1yAxRIzGIb2Q4s text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-9Z1yAxRIzGIb2Q4s .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-9Z1yAxRIzGIb2Q4s #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .sequenceNumber{fill:white;}#mermaid-svg-9Z1yAxRIzGIb2Q4s #sequencenumber{fill:#333;}#mermaid-svg-9Z1yAxRIzGIb2Q4s #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .messageText{fill:#333;stroke:none;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .labelText,#mermaid-svg-9Z1yAxRIzGIb2Q4s .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .loopText,#mermaid-svg-9Z1yAxRIzGIb2Q4s .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-9Z1yAxRIzGIb2Q4s .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .noteText,#mermaid-svg-9Z1yAxRIzGIb2Q4s .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .actorPopupMenu{position:absolute;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-9Z1yAxRIzGIb2Q4s .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9Z1yAxRIzGIb2Q4s .actor-man circle,#mermaid-svg-9Z1yAxRIzGIb2Q4s line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-9Z1yAxRIzGIb2Q4s :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}Rte_Write_BrakePressure(100.0f)查配置:BrakePressure信号→ 属于I-PDU 0x100→ 由COM处理Com_SendSignal(BrakePressure_SignalID, 100.0f)E_OKE_OK

    3.2 数据在RTE中的形态

    数据经过RTE时,形态没有变化——它仍然是"BrakePressure = 100.0f"这个信号值。RTE不做任何数据转换,只是"搬运"。

    但是,RTE在搬运过程中完成了信号ID的映射:将SWC中的端口名称(BrakePressure_Out)映射到COM中的信号ID(通常是一个数字,如SIGNAL_BRAKE_PRESSURE = 0x00)。这个映射在配置阶段定义,RTE生成器将其硬编码在生成的代码中。

    第四章:第三站——COM:信号的"打包车间"

    4.1 COM的核心职责

    COM(Communication Manager)是通信栈中最核心的模块。它的核心职责是将多个信号打包成一个I-PDU(Interaction Layer Protocol Data Unit)。

    什么是I-PDU? I-PDU是一个逻辑数据包,它包含一个或多个信号。在CAN总线上,一个I-PDU最终对应一个CAN帧(8字节数据域)。

    为什么需要打包? CAN帧最多只能携带8字节数据。如果一个刹车压力信号只占2字节,而一个CAN帧有8字节,那么剩下的6字节就可以用来放其他信号(如刹车状态、方向盘转角等)。这样一帧CAN报文就能同时传输多个信号,提高总线利用率。

    4.2 COM的配置核心:I-PDU定义与信号布局

    在AUTOSAR配置中,COM模块的配置主要围绕I-PDU展开。每个I-PDU包含多个信号,每个信号有明确的位布局、字节序、精度和偏移。

    I-PDU 0x100的配置示例:

    <!– I-PDU定义 –>
    <ECUC-CONTAINER-VALUE>
    <DEFINITION-REF>/AUTOSAR/EcucDefs/Com/ComIPdu</DEFINITION-REF>
    <SHORT-NAME>ComIPdu_0x100</SHORT-NAME>
    <PARAMETER-VALUES>
    <ECUC-INTEGER-VALUE>
    <DEFINITION-REF>…/ComIPduHandleId</DEFINITION-REF>
    <VALUE>0x100</VALUE>
    </ECUC-INTEGER-VALUE>
    <ECUC-INTEGER-VALUE>
    <DEFINITION-REF>…/ComIPduSize</DEFINITION-REF>
    <VALUE>8</VALUE>
    </ECUC-INTEGER-VALUE>
    <ECUC-ENUMERATION-VALUE>
    <DEFINITION-REF>…/ComIPduDirection</DEFINITION-REF>
    <VALUE>SEND</VALUE>
    </ECUC-ENUMERATION-VALUE>
    </PARAMETER-VALUES>
    <SUB-CONTAINERS>
    <!– 信号:刹车压力 –>
    <ECUC-CONTAINER-VALUE>
    <DEFINITION-REF>/AUTOSAR/EcucDefs/Com/ComSignal</DEFINITION-REF>
    <SHORT-NAME>BrakePressure</SHORT-NAME>
    <PARAMETER-VALUES>
    <ECUC-INTEGER-VALUE>
    <DEFINITION-REF>…/ComSignalBitPosition</DEFINITION-REF>
    <VALUE>0</VALUE>
    </ECUC-INTEGER-VALUE>
    <ECUC-INTEGER-VALUE>
    <DEFINITION-REF>…/ComSignalBitSize</DEFINITION-REF>
    <VALUE>16</VALUE>
    </ECUC-INTEGER-VALUE>
    <ECUC-ENUMERATION-VALUE>
    <DEFINITION-REF>…/ComSignalEndianness</DEFINITION-REF>
    <VALUE>LITTLE_ENDIAN</VALUE>
    </ECUC-ENUMERATION-VALUE>
    <ECUC-ENUMERATION-VALUE>
    <DEFINITION-REF>…/ComSignalType</DEFINITION-REF>
    <VALUE>UINT16</VALUE>
    </ECUC-ENUMERATION-VALUE>
    </PARAMETER-VALUES>
    </ECUC-CONTAINER-VALUE>
    <!– 信号:刹车状态 –>
    <ECUC-CONTAINER-VALUE>
    <DEFINITION-REF>/AUTOSAR/EcucDefs/Com/ComSignal</DEFINITION-REF>
    <SHORT-NAME>BrakeStatus</SHORT-NAME>
    <PARAMETER-VALUES>
    <ECUC-INTEGER-VALUE>
    <DEFINITION-REF>…/ComSignalBitPosition</DEFINITION-REF>
    <VALUE>16</VALUE>
    </ECUC-INTEGER-VALUE>
    <ECUC-INTEGER-VALUE>
    <DEFINITION-REF>…/ComSignalBitSize</DEFINITION-REF>
    <VALUE>1</VALUE>
    </ECUC-INTEGER-VALUE>
    <ECUC-ENUMERATION-VALUE>
    <DEFINITION-REF>…/ComSignalType</DEFINITION-REF>
    <VALUE>BOOLEAN</VALUE>
    </ECUC-ENUMERATION-VALUE>
    </PARAMETER-VALUES>
    </ECUC-CONTAINER-VALUE>
    </SUB-CONTAINERS>
    </ECUC-CONTAINER-VALUE>

    信号布局表:

    信号名称位起始位长度字节序类型精度偏移示例值
    BrakePressure 0 16 Little Endian UINT16 1 bar/LSB 0 100 = 0x0064
    BrakeStatus 16 1 N/A Boolean 1 0 TRUE = 1

    打包后的I-PDU数据(8字节):

    Byte0: 0x64 (BrakePressure低字节)
    Byte1: 0x00 (BrakePressure高字节)
    Byte2: 0x01 (BrakeStatus在第16位,即Byte2的bit0)
    Byte3: 0x00
    Byte4: 0x00
    Byte5: 0x00
    Byte6: 0x00
    Byte7: 0x00

    4.3 COM的发送触发模式

    COM支持多种发送触发模式,决定I-PDU何时被发送。这是通过ComTxMode配置的:

    触发模式说明适用场景
    直接发送(DIRECT) 任一信号更新就立即发送 事件型信号,如刹车状态变化
    周期发送(PERIODIC) 按固定周期发送 周期性信号,如车速
    混合发送(MIXED) 信号更新时发送,但最长间隔不超过周期 既需实时性又需保底
    无(NONE) 不自动发送,由上层控制 诊断报文

    配置示例:

    <!– I-PDU的发送模式配置 –>
    <ECUC-CONTAINER-VALUE>
    <DEFINITION-REF>/AUTOSAR/EcucDefs/Com/ComIPdu/ComTxMode</DEFINITION-REF>
    <SHORT-NAME>ComTxMode_BrakePdu</SHORT-NAME>
    <PARAMETER-VALUES>
    <ECUC-ENUMERATION-VALUE>
    <DEFINITION-REF>…/ComTxModeType</DEFINITION-REF>
    <VALUE>MIXED</VALUE>
    </ECUC-ENUMERATION-VALUE>
    <ECUC-INTEGER-VALUE>
    <DEFINITION-REF>…/ComTxModePeriod</DEFINITION-REF>
    <VALUE>50</VALUE> <!– 50ms –>
    </ECUC-INTEGER-VALUE>
    </PARAMETER-VALUES>
    </ECUC-CONTAINER-VALUE>

    4.4 COM内部的数据流

    COM模块内部维护了一个I-PDU缓冲区数组。当Com_SendSignal被调用时,COM执行以下操作:

  • 查找信号描述符:根据信号ID,在配置表中查找该信号的位布局信息。
  • 物理值转换:使用配置的增益和偏移,将物理值转换为原始整数值。
  • 位写入:根据位起始、位长度、字节序,将原始整数值写入I-PDU缓冲区的对应位。
  • 发送触发检查:根据I-PDU的发送模式,判断是否需要立即发送。
  • COM打包的完整流程:

    COMRTESWC-B(刹车状态)SWC-A(刹车控制)COMRTESWC-B(刹车状态)SWC-A(刹车控制)#mermaid-svg-CbZwi6tA5D9qHSJp{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CbZwi6tA5D9qHSJp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CbZwi6tA5D9qHSJp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CbZwi6tA5D9qHSJp .error-icon{fill:#552222;}#mermaid-svg-CbZwi6tA5D9qHSJp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CbZwi6tA5D9qHSJp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CbZwi6tA5D9qHSJp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CbZwi6tA5D9qHSJp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CbZwi6tA5D9qHSJp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CbZwi6tA5D9qHSJp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CbZwi6tA5D9qHSJp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CbZwi6tA5D9qHSJp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CbZwi6tA5D9qHSJp .marker.cross{stroke:#333333;}#mermaid-svg-CbZwi6tA5D9qHSJp svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CbZwi6tA5D9qHSJp p{margin:0;}#mermaid-svg-CbZwi6tA5D9qHSJp .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-CbZwi6tA5D9qHSJp text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-CbZwi6tA5D9qHSJp .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-CbZwi6tA5D9qHSJp .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-CbZwi6tA5D9qHSJp .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-CbZwi6tA5D9qHSJp .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-CbZwi6tA5D9qHSJp #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-CbZwi6tA5D9qHSJp .sequenceNumber{fill:white;}#mermaid-svg-CbZwi6tA5D9qHSJp #sequencenumber{fill:#333;}#mermaid-svg-CbZwi6tA5D9qHSJp #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-CbZwi6tA5D9qHSJp .messageText{fill:#333;stroke:none;}#mermaid-svg-CbZwi6tA5D9qHSJp .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-CbZwi6tA5D9qHSJp .labelText,#mermaid-svg-CbZwi6tA5D9qHSJp .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-CbZwi6tA5D9qHSJp .loopText,#mermaid-svg-CbZwi6tA5D9qHSJp .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-CbZwi6tA5D9qHSJp .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-CbZwi6tA5D9qHSJp .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-CbZwi6tA5D9qHSJp .noteText,#mermaid-svg-CbZwi6tA5D9qHSJp .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-CbZwi6tA5D9qHSJp .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-CbZwi6tA5D9qHSJp .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-CbZwi6tA5D9qHSJp .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-CbZwi6tA5D9qHSJp .actorPopupMenu{position:absolute;}#mermaid-svg-CbZwi6tA5D9qHSJp .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-CbZwi6tA5D9qHSJp .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-CbZwi6tA5D9qHSJp .actor-man circle,#mermaid-svg-CbZwi6tA5D9qHSJp line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-CbZwi6tA5D9qHSJp :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}Rte_Write_BrakePressure(100.0f)Rte_Write_BrakeStatus(TRUE)Com_SendSignal(BrakePressure_SignalID, 100.0f)查信号描述符:位起始=0, 位长度=16转换:100.0 / 1.0 = 100 = 0x0064写入I-PDU Buffer[0-1] = 0x0064 (Little Endian)Com_SendSignal(BrakeStatus_SignalID, TRUE)查信号描述符:位起始=16, 位长度=1转换:TRUE = 0x01写入I-PDU Buffer[2] bit0 = 1检查发送模式:MIXED触发条件满足,准备发送

    4.5 数据在COM中的形态

    数据在COM中经历了第一次"变身"——从信号变成了I-PDU。信号是离散的、有意义的物理量;I-PDU是连续的字节流。这个变身过程是可逆的——接收端COM会执行反向操作,将I-PDU字节流解包成信号。

    第五章:第四站——PduR:I-PDU的"交通枢纽"

    5.1 PduR的核心职责

    PduR(PDU Router,PDU路由器)是通信栈的"总邮局"。它的核心职责是根据I-PDU ID查路由表,将I-PDU路由到正确的下层模块。

    PduR的设计理念是完全配置驱动。PduR代码本身不包含任何业务逻辑——它只做"查表+转发"。这使得PduR非常轻量,但灵活性极高。

    5.2 PduR的路由表配置

    PduR的路由表在AUTOSAR ECU配置中定义。每个路由表项包含:源模块、目标模块、I-PDU ID。

    配置示例:

    I-PDU ID源模块目标模块说明
    0x100 COM CanIf 通过CAN0发送
    0x101 COM CanIf 通过CAN1发送
    0x200 COM SecOC 需要安全处理(先经过SecOC再路由到CanIf)
    0x300 CanIf COM 接收方向

    <!– PduR路由表配置 –>
    <ECUC-CONTAINER-VALUE>
    <DEFINITION-REF>/AUTOSAR/EcucDefs/PduR/PduRRoutingPath</DEFINITION-REF>
    <SHORT-NAME>Routing_COM_to_CanIf_0x100</SHORT-NAME>
    <REFERENCE-VALUES>
    <ECUC-REFERENCE-VALUE>
    <DEFINITION-REF>…/PduRSourceModule</DEFINITION-REF>
    <DESTINATION-REF>/ECUC/Com</DESTINATION-REF>
    </ECUC-REFERENCE-VALUE>
    <ECUC-REFERENCE-VALUE>
    <DEFINITION-REF>…/PduRDestinationModule</DEFINITION-REF>
    <DESTINATION-REF>/ECUC/CanIf</DESTINATION-REF>
    </ECUC-REFERENCE-VALUE>
    </REFERENCE-VALUES>
    </ECUC-CONTAINER-VALUE>

    5.3 PduR的发送流程

    当COM调用PduR_Transmit(0x100, data)时,PduR:

  • 查路由表,找到目标模块(CanIf)。
  • 调用目标模块的发送接口(CanIf_Transmit)。
  • 将I-PDU数据原封不动地传递给目标模块。
  • CanIfPduRCOMCanIfPduRCOM#mermaid-svg-MIVloXAkJUnLTFux{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MIVloXAkJUnLTFux .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MIVloXAkJUnLTFux .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MIVloXAkJUnLTFux .error-icon{fill:#552222;}#mermaid-svg-MIVloXAkJUnLTFux .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MIVloXAkJUnLTFux .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MIVloXAkJUnLTFux .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MIVloXAkJUnLTFux .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MIVloXAkJUnLTFux .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MIVloXAkJUnLTFux .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MIVloXAkJUnLTFux .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MIVloXAkJUnLTFux .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MIVloXAkJUnLTFux .marker.cross{stroke:#333333;}#mermaid-svg-MIVloXAkJUnLTFux svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MIVloXAkJUnLTFux p{margin:0;}#mermaid-svg-MIVloXAkJUnLTFux .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-MIVloXAkJUnLTFux text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-MIVloXAkJUnLTFux .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-MIVloXAkJUnLTFux .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-MIVloXAkJUnLTFux .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-MIVloXAkJUnLTFux .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-MIVloXAkJUnLTFux #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-MIVloXAkJUnLTFux .sequenceNumber{fill:white;}#mermaid-svg-MIVloXAkJUnLTFux #sequencenumber{fill:#333;}#mermaid-svg-MIVloXAkJUnLTFux #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-MIVloXAkJUnLTFux .messageText{fill:#333;stroke:none;}#mermaid-svg-MIVloXAkJUnLTFux .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-MIVloXAkJUnLTFux .labelText,#mermaid-svg-MIVloXAkJUnLTFux .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-MIVloXAkJUnLTFux .loopText,#mermaid-svg-MIVloXAkJUnLTFux .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-MIVloXAkJUnLTFux .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-MIVloXAkJUnLTFux .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-MIVloXAkJUnLTFux .noteText,#mermaid-svg-MIVloXAkJUnLTFux .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-MIVloXAkJUnLTFux .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-MIVloXAkJUnLTFux .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-MIVloXAkJUnLTFux .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-MIVloXAkJUnLTFux .actorPopupMenu{position:absolute;}#mermaid-svg-MIVloXAkJUnLTFux .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-MIVloXAkJUnLTFux .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-MIVloXAkJUnLTFux .actor-man circle,#mermaid-svg-MIVloXAkJUnLTFux line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-MIVloXAkJUnLTFux :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}PduR_Transmit(0x100, I-PDU数据)查路由表:0x100 → CanIfCanIf_Transmit(CanController0, HohId_0x100, I-PDU数据)

    5.4 PduR的特殊能力——二次路由

    如果I-PDU需要经过SecOC安全处理(添加MAC),PduR会执行二次路由:

    COM → PduR → SecOC(添加MAC)→ PduR → CanIf

    这种"二次路由"机制让PduR成为通信栈的"中间件",可以在数据传输路径上插入各种处理模块(SecOC、DCM、IpduM等)。但我们的例子中,刹车压力信号没有配置SecOC保护,所以走直接路由。

    第六章:第五站——CanIf:CAN硬件通道的"调度员"

    6.1 CanIf的核心职责

    CanIf(CAN Interface,CAN接口)是ECU抽象层的模块。它的核心职责是将I-PDU映射到具体的CAN硬件通道和硬件对象。

    CanIf位于CanDrv之上,为上层提供硬件无关的发送接口。上层(PduR、COM)不需要知道CAN控制器的数量、邮箱的组织方式、硬件句柄的细节——它们只需要提供I-PDU ID和I-PDU数据,CanIf负责剩下的所有事情。

    6.2 CanIf的配置核心:HOH映射

    CanIf的核心配置是硬件对象句柄(HOH,Hardware Object Handle)的映射。每个HOH对应CAN控制器中的一个邮箱或FIFO。

    配置示例:

    I-PDU IDCAN控制器HOH IDCAN ID说明
    0x100 CAN0 HohId_0x100 0x100 标准CAN帧
    0x101 CAN0 HohId_0x101 0x101 标准CAN帧
    0x200 CAN1 HohId_0x200 0x200 扩展CAN帧

    <!– CanIf发送PDU配置 –>
    <ECUC-CONTAINER-VALUE>
    <DEFINITION-REF>/AUTOSAR/EcucDefs/CanIf/CanIfTxPduCfg</DEFINITION-REF>
    <SHORT-NAME>CanIfTxPdu_0x100</SHORT-NAME>
    <PARAMETER-VALUES>
    <ECUC-INTEGER-VALUE>
    <DEFINITION-REF>…/CanIfTxPduId</DEFINITION-REF>
    <VALUE>0x100</VALUE>
    </ECUC-INTEGER-VALUE>
    <ECUC-INTEGER-VALUE>
    <DEFINITION-REF>…/CanIfTxPduCanId</DEFINITION-REF>
    <VALUE>0x100</VALUE>
    </ECUC-INTEGER-VALUE>
    <ECUC-ENUMERATION-VALUE>
    <DEFINITION-REF>…/CanIfTxPduCanIdType</DEFINITION-REF>
    <VALUE>STANDARD</VALUE>
    </ECUC-ENUMERATION-VALUE>
    <ECUC-INTEGER-VALUE>
    <DEFINITION-REF>…/CanIfTxPduDlc</DEFINITION-REF>
    <VALUE>8</VALUE>
    </ECUC-INTEGER-VALUE>
    </PARAMETER-VALUES>
    <REFERENCE-VALUES>
    <ECUC-REFERENCE-VALUE>
    <DEFINITION-REF>…/CanIfTxPduBufferRef</DEFINITION-REF>
    <DESTINATION-REF>…/CanIfBuffer_0x100</DESTINATION-REF>
    </ECUC-REFERENCE-VALUE>
    </REFERENCE-VALUES>
    </ECUC-CONTAINER-VALUE>

    6.3 CanIf的发送流程

    CanIf收到PduR传来的发送请求后:

  • 查映射表:找到I-PDU 0x100对应的CAN控制器(CAN0)和硬件对象句柄(HohId_0x100)。
  • 获取发送缓冲区:从配置的发送缓冲区池中获取一个空闲缓冲区。
  • 拷贝数据:将I-PDU数据拷贝到发送缓冲区。
  • 调用CanDrv:调用CanDrv_Transmit(CanController0, HohId_0x100, data)。
  • CanDrvCanIfPduRCanDrvCanIfPduR#mermaid-svg-HyG8JG696DosrWFz{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HyG8JG696DosrWFz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HyG8JG696DosrWFz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HyG8JG696DosrWFz .error-icon{fill:#552222;}#mermaid-svg-HyG8JG696DosrWFz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HyG8JG696DosrWFz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HyG8JG696DosrWFz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HyG8JG696DosrWFz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HyG8JG696DosrWFz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HyG8JG696DosrWFz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HyG8JG696DosrWFz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HyG8JG696DosrWFz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HyG8JG696DosrWFz .marker.cross{stroke:#333333;}#mermaid-svg-HyG8JG696DosrWFz svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HyG8JG696DosrWFz p{margin:0;}#mermaid-svg-HyG8JG696DosrWFz .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HyG8JG696DosrWFz text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-HyG8JG696DosrWFz .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-HyG8JG696DosrWFz .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-HyG8JG696DosrWFz .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-HyG8JG696DosrWFz .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-HyG8JG696DosrWFz #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-HyG8JG696DosrWFz .sequenceNumber{fill:white;}#mermaid-svg-HyG8JG696DosrWFz #sequencenumber{fill:#333;}#mermaid-svg-HyG8JG696DosrWFz #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-HyG8JG696DosrWFz .messageText{fill:#333;stroke:none;}#mermaid-svg-HyG8JG696DosrWFz .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HyG8JG696DosrWFz .labelText,#mermaid-svg-HyG8JG696DosrWFz .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-HyG8JG696DosrWFz .loopText,#mermaid-svg-HyG8JG696DosrWFz .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-HyG8JG696DosrWFz .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-HyG8JG696DosrWFz .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-HyG8JG696DosrWFz .noteText,#mermaid-svg-HyG8JG696DosrWFz .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-HyG8JG696DosrWFz .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HyG8JG696DosrWFz .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HyG8JG696DosrWFz .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HyG8JG696DosrWFz .actorPopupMenu{position:absolute;}#mermaid-svg-HyG8JG696DosrWFz .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-HyG8JG696DosrWFz .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HyG8JG696DosrWFz .actor-man circle,#mermaid-svg-HyG8JG696DosrWFz line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-HyG8JG696DosrWFz :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}CanIf_Transmit(0x100, data)查映射:0x100 → CAN0, HohId_0x100获取发送缓冲区拷贝数据到缓冲区CanDrv_Transmit(CAN0, HohId_0x100, data)E_OKE_OK

    6.4 数据在CanIf中的形态

    数据在CanIf中仍然是I-PDU形态。CanIf不改变数据内容,只是做路由和映射。但是,CanIf在发送方向上可能执行以下操作:

    • DLC检查:确保数据长度不超过配置的DLC。
    • 发送确认管理:维护发送确认状态,当CanDrv返回发送确认时通知上层。
    • 多路复用PDU处理:如果I-PDU是多路复用的,CanIf需要根据选择器字段动态路由到不同的CAN ID。

    第七章:第六站——CanDrv:CAN控制器的"硬件操作员"

    7.1 CanDrv的核心职责

    CanDrv(CAN Driver,CAN驱动)是MCAL层的模块,负责直接操作CAN控制器的硬件寄存器。它是整个发送流程中唯一直接接触硬件的软件层。

    CanDrv的代码高度依赖于具体的CAN控制器硬件(如英飞凌AURIX的MCMCAN、NXP S32的FlexCAN、ST的bxCAN)。但AUTOSAR标准化了CanDrv的接口,使得上层模块(CanIf)可以与任何CanDrv实现配合工作。

    7.2 CanDrv的配置核心:邮箱配置

    CAN控制器通常有多个发送邮箱(如32个)。每个邮箱可以独立配置为发送或接收。CanDrv的配置决定了邮箱的组织方式。

    配置示例:

    邮箱编号方向CAN ID说明
    0~2 接收 0x200~0x202 接收邮箱
    3 发送 0x100 发送邮箱,用于刹车压力
    4 发送 0x101 发送邮箱,用于车速
    5~31 空闲 未使用

    <!– CanDrv邮箱配置(简化) –>
    <ECUC-CONTAINER-VALUE>
    <DEFINITION-REF>/AUTOSAR/EcucDefs/Can/CanHwObject</DEFINITION-REF>
    <SHORT-NAME>CanHwObject_Tx_0x100</SHORT-NAME>
    <PARAMETER-VALUES>
    <ECUC-INTEGER-VALUE>
    <DEFINITION-REF>…/CanHwObjectId</DEFINITION-REF>
    <VALUE>3</VALUE>
    </ECUC-INTEGER-VALUE>
    <ECUC-ENUMERATION-VALUE>
    <DEFINITION-REF>…/CanHwObjectType</DEFINITION-REF>
    <VALUE>TRANSMIT</VALUE>
    </ECUC-ENUMERATION-VALUE>
    <ECUC-INTEGER-VALUE>
    <DEFINITION-REF>…/CanHwObjectCanId</DEFINITION-REF>
    <VALUE>0x100</VALUE>
    </ECUC-INTEGER-VALUE>
    </PARAMETER-VALUES>
    </ECUC-CONTAINER-VALUE>

    7.3 CanDrv的发送流程

    CanDrv_Transmit的执行过程:

  • 找到空闲的发送邮箱:CAN控制器通常有多个发送邮箱,CanDrv选择一个空闲的邮箱。
  • 写入CAN ID和数据:将CAN ID(0x100)写入邮箱的ID寄存器,将8字节数据写入邮箱的数据寄存器。
  • 设置发送请求位:写入发送请求位,CAN控制器开始仲裁,等待总线空闲后发送。
  • /**
    * @file CanDrv.c
    * @brief CAN驱动发送实现(概念性代码)
    *
    * @note
    * – 该函数直接操作硬件寄存器
    * – 硬件寄存器地址在MCAL配置中定义
    */

    void CanDrv_Transmit(uint8 controller, uint8 hoh, const uint8 *data) {
    /* 1. 找到对应的发送邮箱 */
    CAN_MailboxType* mailbox = &CAN_HW[controller].Mailboxes[hoh];

    /* 2. 写入CAN ID */
    mailbox->ID = CAN_ID_0x100;

    /* 3. 写入数据长度 */
    mailbox->DLC = 8;

    /* 4. 写入数据 */
    memcpy(mailbox->Data, data, 8);

    /* 5. 设置发送请求位 */
    mailbox->TXRQ = 1;

    /* 6. CAN控制器开始仲裁与发送 */
    }

    7.4 数据在CanDrv中的形态

    数据在CanDrv中变成了硬件寄存器中的值。CAN控制器根据寄存器的内容,自动组装CAN帧并发送。

    数据形态转变:

    I-PDU字节流:[0x64, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00]

    CAN ID寄存器:0x100
    DLC寄存器:8
    数据寄存器:0x64 0x00 0x01 0x00 0x00 0x00 0x00 0x00
    发送请求位:1

    第八章:第七站——CAN控制器与收发器:从数字到电平

    8.1 CAN控制器的硬件发送流程

    CAN控制器收到CanDrv的发送请求后,自动执行以下流程:

  • 位仲裁:检查总线是否空闲。如果总线正忙,等待。
  • 帧组装:按照CAN协议格式组装帧:帧起始位(SOF)→ CAN ID → 控制位 → 数据域 → CRC校验位 → ACK位 → 帧结束位(EOF)。
  • 位填充:如果连续5个相同电平,插入一个反相电平,防止同步丢失。
  • 逐位发送:通过TX引脚向CAN收发器逐位发送。
  • CAN帧格式(标准帧):

    SOF | 11位CAN ID | RTR(1位) | IDE(1位) | DLC(4位) | 数据(0~8字节) | CRC(15位) | ACK(1位) | EOF(7位)

    8.2 CAN收发器的电平转换

    CAN收发器将CAN控制器的数字电平(TTL电平,0V/3.3V)转换为CAN总线的差分电平(CAN_H和CAN_L):

    逻辑CAN_H电压CAN_L电压差分电压
    显性(0) 3.5V 1.5V 2.0V
    隐性(1) 2.5V 2.5V 0V

    为什么使用差分信号?

    差分信号有两个优势:

  • 抗干扰能力强:外界电磁干扰会同时影响CAN_H和CAN_L,但差分电压保持不变。
  • 传输距离远:差分信号可以在长距离(最长1000米)上保持信号完整性。
  • 最终,刹车压力=100bar这个信号,变成了CAN总线上的一串差分电平信号,可以被其他ECU接收。

    第九章:完整数据流案例——从SWC到CAN总线的全流程

    以下案例为完全虚构,所有时间、地点、人物、公司均为杜撰,旨在通过一个典型场景说明数据发送的完整流程。

    虚拟案例背景:2024年,某Tier-1供应商为某自主品牌开发一款刹车控制器(Brake ECU),采用AURIX TC387 MCU,AUTOSAR CP 4.3.1平台。刹车压力信号通过CAN0发送,CAN ID=0x100。

    9.1 系统配置摘要

    信号配置:

    信号位起始位长度字节序类型精度偏移
    BrakePressure 0 16 Little Endian UINT16 1 bar/LSB 0
    BrakeStatus 16 1 N/A Boolean 1 0

    I-PDU配置:

    I-PDU ID大小发送模式信号
    0x100 8字节 MIXED(50ms周期) BrakePressure, BrakeStatus

    路由配置:

    I-PDU ID路由路径CAN控制器HOH
    0x100 COM→PduR→CanIf CAN0 HohId_0x100

    CAN配置:

    参数值
    CAN ID 0x100
    帧类型 标准帧
    DLC 8
    波特率 500 kbps
    发送邮箱 邮箱3

    9.2 完整数据流

    步骤模块数据形态数据内容
    1 SWC 信号(物理量) BrakePressure = 100.0f
    2 RTE 信号(物理量) BrakePressure = 100.0f
    3 COM I-PDU(字节流) [0x64, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00]
    4 PduR I-PDU(字节流) 同上
    5 CanIf I-PDU(字节流) 同上
    6 CanDrv 寄存器值 CAN ID=0x100, DLC=8, DATA=0x64 0x00 0x01 …
    7 CAN控制器 CAN帧 SOF + 0x100 + 控制位 + 8字节数据 + CRC + ACK + EOF
    8 CAN收发器 差分电平 CAN_H/CAN_L差分信号

    9.3 Mermaid时序图

    CAN控制器CanDrvCanIfPduRCOMRTESWCCAN控制器CanDrvCanIfPduRCOMRTESWC#mermaid-svg-XLJL9I8YRfo85amB{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XLJL9I8YRfo85amB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XLJL9I8YRfo85amB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XLJL9I8YRfo85amB .error-icon{fill:#552222;}#mermaid-svg-XLJL9I8YRfo85amB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XLJL9I8YRfo85amB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XLJL9I8YRfo85amB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XLJL9I8YRfo85amB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XLJL9I8YRfo85amB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XLJL9I8YRfo85amB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XLJL9I8YRfo85amB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XLJL9I8YRfo85amB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XLJL9I8YRfo85amB .marker.cross{stroke:#333333;}#mermaid-svg-XLJL9I8YRfo85amB svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XLJL9I8YRfo85amB p{margin:0;}#mermaid-svg-XLJL9I8YRfo85amB .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-XLJL9I8YRfo85amB text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-XLJL9I8YRfo85amB .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-XLJL9I8YRfo85amB .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-XLJL9I8YRfo85amB .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-XLJL9I8YRfo85amB .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-XLJL9I8YRfo85amB #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-XLJL9I8YRfo85amB .sequenceNumber{fill:white;}#mermaid-svg-XLJL9I8YRfo85amB #sequencenumber{fill:#333;}#mermaid-svg-XLJL9I8YRfo85amB #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-XLJL9I8YRfo85amB .messageText{fill:#333;stroke:none;}#mermaid-svg-XLJL9I8YRfo85amB .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-XLJL9I8YRfo85amB .labelText,#mermaid-svg-XLJL9I8YRfo85amB .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-XLJL9I8YRfo85amB .loopText,#mermaid-svg-XLJL9I8YRfo85amB .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-XLJL9I8YRfo85amB .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-XLJL9I8YRfo85amB .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-XLJL9I8YRfo85amB .noteText,#mermaid-svg-XLJL9I8YRfo85amB .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-XLJL9I8YRfo85amB .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-XLJL9I8YRfo85amB .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-XLJL9I8YRfo85amB .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-XLJL9I8YRfo85amB .actorPopupMenu{position:absolute;}#mermaid-svg-XLJL9I8YRfo85amB .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-XLJL9I8YRfo85amB .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-XLJL9I8YRfo85amB .actor-man circle,#mermaid-svg-XLJL9I8YRfo85amB line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-XLJL9I8YRfo85amB :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}SWC-B发送刹车状态CAN收发器将TTL电平转换为CAN_H/CAN_L差分电平读取传感器:踏板位置 50%计算刹车压力:100 barRte_Write_BrakePressure(100.0f)查映射:BrakePressure → 信号ID 0x00Com_SendSignal(SIGNAL_BRAKE_PRESSURE, 100.0f)查信号描述符:位起始=0, 位长度=16, Little Endian转换:100.0 / 1.0 = 100 = 0x0064写入I-PDU Buffer[0] = 0x64 (低字节)写入I-PDU Buffer[1] = 0x00 (高字节)Rte_Write_BrakeStatus(TRUE)Com_SendSignal(SIGNAL_BRAKE_STATUS, TRUE)转换:TRUE = 1写入I-PDU Buffer[2] bit0 = 1检查发送模式:MIXED,触发发送PduR_Transmit(0x100, I-PDU数据)查路由表:0x100 → CanIfCanIf_Transmit(CAN0, HohId_0x100, 数据)查映射:0x100 → CAN控制器0, 邮箱3CanDrv_Transmit(0, 3, 数据)写入CAN ID寄存器 = 0x100写入DLC寄存器 = 8写入数据寄存器 = 0x64 0x00 0x01 0x00 0x00 0x00 0x00 0x00设置发送请求位位仲裁 → 帧组装 → 位填充 → 逐位发送TX引脚输出TTL电平CAN总线传输完成,等待ACK

    第十章:代码模拟——完整发送流程的C实现

    现在,我们来写一个完整的C程序,模拟从SWC到CanDrv的完整发送流程。这个程序将帮助你直观地看到每一层的数据形态变化。

    /**
    * @file can_tx_full_flow.c
    * @brief 模拟AUTOSAR CP CAN报文发送的完整流程
    *
    * 本程序模拟从SWC产生信号到CAN控制器硬件发送的完整数据流:
    * SWC → RTE → COM → PduR → CanIf → CanDrv → CAN控制器
    *
    * @note
    * – 所有模块名称、信号名称、I-PDU ID均为虚构
    * – 代码结构模拟真实AUTOSAR栈的分层调用关系
    * – 编译: make clean && make
    * – 运行: ./can_tx_full_flow
    */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdint.h>
    #include <stdbool.h>

    /* ================================================================
    * 配置常量
    * ================================================================ */

    #define SIGNAL_BRAKE_PRESSURE 0x00 /**< 刹车压力信号ID */
    #define SIGNAL_BRAKE_STATUS 0x01 /**< 刹车状态信号ID */
    #define IPDU_ID_0x100 0x100 /**< I-PDU ID */
    #define CAN_ID_0x100 0x100 /**< CAN ID */
    #define CAN_CONTROLLER_0 0 /**< CAN控制器0 */
    #define CAN_MAILBOX_0x100 3 /**< 邮箱3 */

    /* ================================================================
    * 信号描述符(模拟COM的配置数据)
    * ================================================================ */

    typedef struct {
    uint8_t signal_id; /**< 信号ID */
    const char *signal_name; /**< 信号名称 */
    uint8_t byte_offset; /**< 字节偏移 */
    uint8_t bit_offset; /**< 位偏移(在字节内) */
    uint8_t bit_length; /**< 位长度 */
    float gain; /**< 增益(精度) */
    float offset; /**< 偏移 */
    } SignalDescriptor;

    static const SignalDescriptor g_signals[] = {
    { SIGNAL_BRAKE_PRESSURE, "BrakePressure", 0, 0, 16, 1.0f, 0.0f },
    { SIGNAL_BRAKE_STATUS, "BrakeStatus", 2, 0, 1, 1.0f, 0.0f },
    };
    #define NUM_SIGNALS (sizeof(g_signals) / sizeof(g_signals[0]))

    /* ================================================================
    * I-PDU缓冲区(模拟COM内部缓冲区)
    * ================================================================ */

    static uint8_t g_ipdu_buffer[8] = {0};

    /* ================================================================
    * 函数前置声明
    * ================================================================ */

    static void Rte_Write(uint8_t signal_id, float value);
    static void Com_SendSignal(uint8_t signal_id, float value);
    static void PduR_Transmit(uint16_t ipdu_id, const uint8_t *data, uint8_t length);
    static void CanIf_Transmit(uint16_t ipdu_id, const uint8_t *data, uint8_t length);
    static void CanDrv_Transmit(uint8_t controller, uint8_t mailbox,
    const uint8_t *data, uint8_t length);

    /* ================================================================
    * 模拟RTE层
    * ================================================================ */

    /**
    * @brief 模拟RTE的Rte_Write接口
    *
    * 接收SWC的信号写入请求,路由到COM模块。
    * 在真实的AUTOSAR中,此函数由RTE生成器自动生成。
    *
    * @param signal_id [in] 信号ID
    * @param value [in] 信号值(float,原始物理量)
    */

    static void Rte_Write(uint8_t signal_id, float value)
    {
    printf("[RTE] Rte_Write_%s(%.1f)\\n",
    g_signals[signal_id].signal_name, value);
    printf("[RTE] → 路由到COM\\n");
    Com_SendSignal(signal_id, value);
    }

    /* ================================================================
    * 模拟COM层
    * ================================================================ */

    /**
    * @brief 模拟COM的Com_SendSignal接口
    *
    * 将信号值根据配置的位布局打包到I-PDU缓冲区中。
    *
    * @param signal_id [in] 信号ID
    * @param value [in] 信号值(float,原始物理量)
    */

    static void Com_SendSignal(uint8_t signal_id, float value)
    {
    printf("[COM] Com_SendSignal(%s, %.1f)\\n",
    g_signals[signal_id].signal_name, value);

    const SignalDescriptor *sig = &g_signals[signal_id];

    /* 将物理值转换为整数值 */
    int32_t raw_value = (int32_t)((value sig->offset) / sig->gain);
    printf("[COM] 转换: %.1f → %d (0x%04X)\\n",
    value, raw_value, (uint16_t)raw_value);

    /* 按位布局写入I-PDU缓冲区 */
    /* 简化处理:假设little-endian,且信号从字节边界开始 */
    for (uint8_t b = 0; b < sig->bit_length; b++) {
    uint8_t bit_pos = sig->bit_offset + b;
    uint8_t byte_idx = sig->byte_offset + (bit_pos / 8);
    uint8_t bit_in_byte = bit_pos % 8;

    if (raw_value & (1 << b)) {
    g_ipdu_buffer[byte_idx] |= (1 << bit_in_byte);
    } else {
    g_ipdu_buffer[byte_idx] &= ~(1 << bit_in_byte);
    }
    }

    printf("[COM] I-PDU Buffer: ");
    for (int i = 0; i < 8; i++) printf("%02X ", g_ipdu_buffer[i]);
    printf("\\n");

    /* 检查发送触发条件(简化:所有信号写入后立即触发) */
    static uint8_t written_signals = 0;
    written_signals++;
    if (written_signals == NUM_SIGNALS) {
    printf("[COM] 触发条件满足,准备发送\\n");
    written_signals = 0;
    PduR_Transmit(IPDU_ID_0x100, g_ipdu_buffer, 8);
    }
    }

    /* ================================================================
    * 模拟PduR层
    * ================================================================ */

    /**
    * @brief 模拟PduR的PduR_Transmit接口
    *
    * 根据I-PDU ID查路由表,将I-PDU路由到正确的下层模块。
    *
    * @param ipdu_id [in] I-PDU ID
    * @param data [in] I-PDU数据
    * @param length [in] 数据长度
    */

    static void PduR_Transmit(uint16_t ipdu_id, const uint8_t *data, uint8_t length)
    {
    printf("[PduR] PduR_Transmit(0x%03X)\\n", ipdu_id);
    printf("[PduR] 查路由表:0x%03X → CanIf\\n", ipdu_id);
    CanIf_Transmit(ipdu_id, data, length);
    }

    /* ================================================================
    * 模拟CanIf层
    * ================================================================ */

    /**
    * @brief 模拟CanIf的CanIf_Transmit接口
    *
    * 根据I-PDU ID查映射表,将I-PDU映射到具体的CAN控制器和硬件邮箱。
    *
    * @param ipdu_id [in] I-PDU ID
    * @param data [in] I-PDU数据
    * @param length [in] 数据长度
    */

    static void CanIf_Transmit(uint16_t ipdu_id, const uint8_t *data, uint8_t length)
    {
    printf("[CanIf] CanIf_Transmit(0x%03X)\\n", ipdu_id);
    printf("[CanIf] 查映射表:0x%03X → CAN控制器0, HOH=%d, CAN ID=0x%03X\\n",
    ipdu_id, CAN_MAILBOX_0x100, CAN_ID_0x100);
    CanDrv_Transmit(CAN_CONTROLLER_0, CAN_MAILBOX_0x100, data, length);
    }

    /* ================================================================
    * 模拟CanDrv层
    * ================================================================ */

    /**
    * @brief 模拟CanDrv的CanDrv_Transmit接口
    *
    * 直接操作CAN控制器硬件寄存器,启动发送。
    *
    * @param controller [in] CAN控制器编号
    * @param mailbox [in] 发送邮箱编号
    * @param data [in] 数据
    * @param length [in] 数据长度
    */

    static void CanDrv_Transmit(uint8_t controller, uint8_t mailbox,
    const uint8_t *data, uint8_t length)
    {
    printf("[CanDrv] CanDrv_Transmit(控制器%d, 邮箱%d)\\n", controller, mailbox);
    printf("[CanDrv] 写入CAN ID寄存器 = 0x%03X\\n", CAN_ID_0x100);
    printf("[CanDrv] 写入DLC寄存器 = %d\\n", length);
    printf("[CanDrv] 写入数据寄存器 = ");
    for (int i = 0; i < length; i++) printf("%02X ", data[i]);
    printf("\\n");
    printf("[CanDrv] 设置发送请求位 → CAN控制器开始仲裁与发送\\n");
    printf("[CAN HW] 帧组装:SOF + CAN ID(0x%03X) + 数据 + CRC + ACK + EOF\\n",
    CAN_ID_0x100);
    printf("[CAN HW] TX引脚输出TTL电平 → CAN收发器转换为差分电平\\n");
    printf("[CAN BUS] ★ CAN帧已发送 ★\\n");
    }

    /* ================================================================
    * 主函数
    * ================================================================ */

    int main(void)
    {
    printf("========================================\\n");
    printf(" AUTOSAR CP CAN报文发送完整流程模拟\\n");
    printf("========================================\\n");
    printf("信号: 刹车压力 + 刹车状态\\n");
    printf("I-PDU: 0x100\\n");
    printf("CAN ID: 0x100\\n");
    printf("========================================\\n\\n");

    /* SWC发送刹车压力信号 */
    printf("===== SWC发送刹车压力 =====\\n");
    Rte_Write(SIGNAL_BRAKE_PRESSURE, 100.0f);
    printf("\\n");

    /* SWC发送刹车状态信号 */
    printf("===== SWC发送刹车状态 =====\\n");
    Rte_Write(SIGNAL_BRAKE_STATUS, 1.0f);
    printf("\\n");

    printf("========================================\\n");
    printf(" 模拟结束\\n");
    printf("========================================\\n");
    return 0;
    }

    Makefile

    # ============================================
    # CAN发送流程模拟 Makefile
    # ============================================

    CC = gcc
    CFLAGS = -Wall -Wextra -O2 -std=c99
    TARGET = can_tx_full_flow
    SRCS = can_tx_full_flow.c
    OBJS = $(SRCS:.c=.o)

    .PHONY: all clean run

    all: $(TARGET)

    $(TARGET): $(OBJS)
    $(CC) $(CFLAGS) -o $@ $^

    %.o: %.c
    $(CC) $(CFLAGS) -c -o $@ $<

    clean:
    rm -f $(OBJS) $(TARGET)

    run: $(TARGET)
    ./$(TARGET)

    编译与运行说明

    环境要求:GCC 4.8+(支持C99)。

    编译:

    make clean && make

    运行:

    make run

    预期输出:程序将展示从SWC调用Rte_Write到CAN控制器发送的完整流程,每一层的处理细节都被打印出来,让你清晰地看到数据在每一层的变化。

    第十一章:总结——一次“变身”的完整旅程

    朋友,通过今天的深度解析,我们完整地走过了从应用SWC到CAN控制器电平的七站旅程。

    站模块数据形态核心职责关键配置
    第1站 SWC 信号(物理量) 产生数据 端口定义、数据类型
    第2站 RTE 信号(物理量) 路由到COM 信号映射
    第3站 COM I-PDU(字节流) 信号打包 信号布局、发送模式
    第4站 PduR I-PDU(字节流) I-PDU路由 路由表
    第5站 CanIf I-PDU(字节流) 硬件通道映射 HOH映射
    第6站 CanDrv 寄存器值 硬件寄存器操作 邮箱配置
    第7站 CAN控制器+收发器 差分电平 物理发送 波特率、引脚配置

    核心结论:数据在从SWC到CAN总线的旅程中,经历了三次关键的"变身":

  • SWC→COM:从信号(100 bar)变成I-PDU字节流(0x0064)。
  • CanIf→CanDrv:从I-PDU字节流变成硬件寄存器值。
  • CAN控制器→收发器:从数字电平变成差分电平。
  • 每一次变身都由不同层次的模块完成,各层之间通过标准化接口传递数据。这种分层的设计让每一层都可以独立替换和升级——比如更换MCU时只需要替换MCAL层,应用层代码完全不变。

    AUTOSAR配置在每一层都扮演着“灵魂”的角色:

    • 没有信号布局配置,COM不知道如何打包。
    • 没有路由表配置,PduR不知道送往何方。
    • 没有HOH映射,CanIf不知道使用哪个邮箱。
    • 没有邮箱配置,CanDrv不知道往哪里写寄存器。

    配置就是那根贯穿始终的“金线”,把分散在各层的代码缝合起来,形成一个有机的整体。

    这就是AUTOSAR CP平台的核心价值:通过标准化的分层和配置机制,让应用逻辑与硬件细节彻底解耦,让同一套应用代码可以运行在任何硬件平台上,让每一个SWC都能像乐高积木一样自由组合。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » AUTOSAR CP平台从应用SWC到CAN控制器电平:数据发送的完整旅程
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!