Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

5.3 AUTOSAR CP & AP

2003 年,一间会议室里的决定

2003 年,一群欧洲 OEM 和 Tier-1 的代表坐在会议室里。议题非常直接:

“我们需要一个标准。一个汽车 ECU 软件的通用框架。”

当时的情况是一盘散沙。每个 Tier-1 供应商为每个 OEM 单独开发 ECU 软件。标致-雪铁龙的发动机控制器和大众的发动机控制器硬件可以一模一样(比如都用英飞凌 TriCore 或瑞萨 RH850),但软件完全不兼容。

同样的 CAN 驱动要重写五遍。同样的诊断栈要重写五遍。同样的安全组件(WDOG、MPU、锁步核管理)要重演五次。

这是软件成本膨胀的根本原因。 不是工程师不努力。是每一次都从零开始。

与会者同意:定义一个标准。分层解耦。硬件无关的应用层可以在不同 ECU 上复用。基础的驱动层、通信栈、操作系统封装一次,用在所有 ECU 上。

这个标准叫做 AUTOSAR(AUTomotive Open System ARchitecture)。


蓝图:AUTOSAR Classic Platform(CP)的分层宇宙

CP 针对实时性高、资源有限的 ECU——发动机、变速箱、ABS、EPS、BCM 等。OS 是 OSEK/VDX 兼容的实时内核。

想象一座大厦,从上到下五层楼:

名称角色
第 5 层应用层(SWC)独立的应用逻辑:扭矩控制、轮速计算——不碰任何硬件
第 4 层RTESWC 的“软件总线“,连接 SWC 之间的数据交换
第 3 层服务层(Services)系统状态管理、存储服务(NvM)、通信服务(DCM/COM)
第 2 层ECU 抽象层把微控制器外设抽象成高层接口——CAN Interface 在这里
第 1 层MCAL直接操作硬件寄存器——GPIO、ADC、PWM、SPI、CAN、GPT

还有一层特殊的存在——复杂驱动(Complex Drivers),用于那些不能套进标准框架的特殊硬件:轮速传感器的模拟信号调理、发动机喷油芯片的精确时序控制。

关键原则:

  • SWC 不能直接访问 BSW 或硬件。它只能通过 RTE 操作。
  • BSW 层之间有严格的单向接口——COM → PduR → CanTp → CanIf → Can。
  • 每个层只能访问它的下层。依赖方向严格向下。

站在最底层:MCAL 的 CAN 驱动

MCAL 是软件栈和硬件唯一的接触点。它直接读写外设寄存器。芯片厂商提供 MCAL——你是 ECU 集成工程师,通常不修改 MCAL 代码,但你必须理解它的接口。

一个简化的 CAN 驱动 MCAL 模块看起来是这样的:

/* Can.h - MCAL CAN驱动接口(简化) */
#ifndef CAN_H
#define CAN_H

#include "Can_Types.h"

/* 初始化CAN控制器 */
void Can_Init(const Can_ConfigType *config);

/* 发送一帧CAN报文 */
Std_ReturnType Can_Write(Can_HwHandleType hoh, const Can_PduType *pdu);

/* 接收中断回调——Can驱动调用,通知上层 */
void CanIf_RxIndication(PduIdType pduId, const PduInfoType *pduInfo);

/* 发送确认回调 */
void CanIf_TxConfirmation(PduIdType pduId);

#endif /* CAN_H */

/* Can.c - MCAL CAN驱动实现(简化) */
void Can_IrqHandler(uint8_t controller_id)
{
    Can_PduType rx_pdu;
    PduInfoType pdu_info;

    /* 从硬件CAN控制器的接收FIFO读出 */
    rx_pdu.id = CAN0->sFIFOMailBox[0].RIR >> 21;
    rx_pdu.length = CAN0->sFIFOMailBox[0].RDTR & 0x0F;

    uint32_t *src = (uint32_t *)&CAN0->sFIFOMailBox[0].RDLR;
    uint32_t *dst = (uint32_t *)rx_pdu.sdu;
    dst[0] = src[0];
    dst[1] = src[1];

    CAN0->RF0R |= CAN_RF0R_RFOM0;

    /* 匹配CAN ID到PDU ID——用配置表 */
    PduIdType pdu_id = pdu_id_from_can_id(rx_pdu.id);

    /* 构造PduInfo */
    pdu_info.SduDataPtr = rx_pdu.sdu;
    pdu_info.SduLength  = rx_pdu.length;

    /* 通知上层——这是MCAL的唯一出口 */
    CanIf_RxIndication(pdu_id, &pdu_info);
}

注意 CanIf_RxIndication——MCAL 不路由数据。它只是从硬件读出、包装好、递给上层。路由是上层的事。


追踪:一个 CAN 报文从物理总线到应用逻辑的完整旅程

假设 SWC “VehicleSpeed” 从 CAN 总线上读取车速信号。让这个报文带你走一遍 AUTOSAR 的五层大厦。

第 1 层——MCAL(CAN 驱动)

硬件 CAN 控制器的接收 FIFO 满时,触发 CAN 接收中断。MCAL 中的 Can_Irq.c 从硬件寄存器读出帧,调用 CanIf_RxIndication()——通知上层有报文到达。芯片厂商提供 MCAL,你通常不修改它。

第 2 层——CAN Interface(CanIf)

CanIf_RxIndication() 做的事:

  • 匹配接收到的 CAN ID 到配置表中定义的 PDU ID。
  • 把 CAN 帧的 8 字节数据拷贝到 PDU buffer 中。
  • 调用 PduR_RxIndication(pduId, &pduInfo)

第 3 层——PDU Router(PduR)

PDU Router 是通信栈的交换机。一条 PDU 从 CanIf 上来,PduR 根据 PDU ID 的配置决定路由到哪个上层模块。对于普通信号帧(不是诊断、不是 TP 分段),PduR 直接调用 Com_RxIndication(pduId, &pduInfo)

第 4 层——COM Stack(Com)

Com 模块负责把 PDU 的原始字节流拆包成独立信号。这是整个通信栈中最体现 AUTOSAR 设计哲学的一层——信号和帧的彻底解耦。

在裸机编程中,你直接解析 CAN 帧:

/* 裸机方式:手工位掩码提取 */
uint16_t speed_raw = (can_data[0] << 8) | can_data[1];
float speed = speed_raw * 0.05625f;

在 AUTOSAR 中,COM 从配置(ARXML)中读取每个信号的定义,在运行时自动完成信号提取:

/* Com_SignalProcessing.c - COM信号提取(简化) */
void Com_RxIndication(PduIdType pduId, const PduInfoType *pduInfo)
{
    ComPduIdType comPduId = Com_PduIdFromRouting(pduId);
    const ComSignalConfig_t *signals = Com_GetSignalsForPdu(comPduId);

    for (uint16_t i = 0; i < signals->num_signals; i++) {
        const ComSignal_t *sig = &signals->signals[i];
        uint8_t *data = pduInfo->SduDataPtr;

        /* 从PDU字节流中提取信号的原始值 */
        uint64_t raw = Com_ExtractBits(data,
                                        sig->bitPosition,
                                        sig->bitSize,
                                        sig->endianness);

        /* 物理值转换 */
        float physical = (float)raw * sig->factor + sig->offset;

        /* 更新RTE buffer */
        Rte_Write_Signal(sig->handle, &physical);
    }
}

/* 位提取函数——处理跨字节边界、大小端、符号扩展 */
uint64_t Com_ExtractBits(const uint8_t *buffer,
                          uint16_t bitPos, uint8_t bitSize,
                          Com_Endianness_t endian)
{
    /* 计算起始字节和位偏移 */
    uint16_t byteIdx = bitPos / 8;
    uint8_t  bitOff  = bitPos % 8;

    /* 按字节读取并拼接——支持跨字节边界 */
    uint64_t value = 0;
    for (uint8_t i = 0; i < (bitSize + bitOff + 7) / 8; i++) {
        value |= (uint64_t)buffer[byteIdx + i] << (i * 8);
    }
    value >>= bitOff;
    value &= (1ULL << bitSize) - 1;

    /* 符号扩展:如果最高位是1且信号是有符号的 */
    if (endian == COM_SIGNED && (value & (1ULL << (bitSize - 1)))) {
        value |= ~((1ULL << bitSize) - 1);
    }

    return value;
}

这段代码是 COM 层存在的根本理由。在一个 ECU 中可能有 500+ 个 CAN 信号,分布在 50+ 个 PDU 中。让每个 SWC 应用工程师手工解析字节流不仅低效,更是 bug 的温床——信号布局的一个 bit 偏移错误可能导致错误的数据被分发到所有订阅的 SWC 中。

COM 用配置驱动的方式实现了一劳永逸的信号提取——配置在 ARXML 中定义,COM 的生成代码根据配置自动生成,应用工程师永远不需要写位掩码。

第 5 层——RTE 和 SWC

RTE 是 SWC 看到的“世界“。对于 SWC 来说,硬件不存在。CAN 不存在。Flash 不存在。只有 RTE——一组可读取和写入的端口(Port)。

/* SWC: VehicleSpeed_Implementation.c */
#include "Rte_VehicleSpeed.h"

/* 这个Runnable由RTE周期性触发——RTE根据OS schedule表调度它 */
void VehicleSpeed_MainFunction(void)
{
    float speed = 0.0f;
    boolean over_speed = FALSE;

    /* 通过RTE读取端口——背后是COM层提供的信号值 */
    Rte_Read_SpeedValue_VehicleSpeed(&speed);

    /* 应用逻辑:超速判断 */
    if (speed > 120.0f) {
        over_speed = TRUE;
    }

    /* 通过RTE写入输出端口——RTE负责把值路由到订阅的SWC */
    Rte_Write_OverSpeedWarning_VehicleSpeed(over_speed);

    /* 如果需要触发诊断——调用RTE的客户端-服务器接口 */
    if (over_speed) {
        Rte_Call_SetEventStatus_OverSpeedMonitor(DTC_OVERSPEED, TRUE);
    }
}

SWC 的实现者不需要知道“车速信号从哪来“——它是 CAN 帧里的一个信号?是 SPI 传感器直接读的?是内部计算出来的?是另一颗芯片通过以太网传来的?SWC 不在乎。它只认 RTE 端口。这是 AUTOSAR 分层解耦的最高境界。

整个路径:物理 CAN 总线 → MCAL 中断 → CanIf 匹配 → PduR 路由 → Com 拆包 → RTE 分发 → SWC 计算。每一层只做一件事,每一层只认识相邻的层。


ARXML:AUTOSAR 的灵魂——用配置代替代码

上面的 COM 信号提取不是靠硬编码,而是靠配置。AUTOSAR 的精髓不在 C 代码里——在 ARXML 文件里。ARXML 是 AUTOSAR 的配置语言,它用 XML 描述了 ECU 软件的每一个可配置参数。

一个 CAN 信号的完整 ARXML 描述:

<!-- 信号:车速,16位无符号,Big-Endian,位偏移0 -->
<Signal name="ComSignal_VehicleSpeed">
  <bitPosition>0</bitPosition>
  <bitSize>16</bitSize>
  <endianness>BIG_ENDIAN</endianness>
  <type>UINT16</type>
</Signal>

<!-- IPDU:车速信号所属的接收PDU -->
<IPdu name="ComIPdu_VehicleSpeedPdu">
  <signalRef>/ComSignal_VehicleSpeed</signalRef>
  <direction>RECEIVE</direction>
</IPdu>

<!-- 物理值转换:raw × 0.05625 + 0.0,范围 0-350 km/h -->
<CompuMethod name="ComSignal_VehicleSpeed_CompuMethod">
  <factor>0.05625</factor>
  <offset>0.0</offset>
  <lowerLimit>0.0</lowerLimit>
  <upperLimit>350.0</upperLimit>
</CompuMethod>

这段 XML 告诉 BSW 配置生成器(如 Vector DaVinci Configurator 或 EB tresos Studio):

  • “车速信号“是一个 16 位的无符号整数,以 Big-Endian 排列,在 PDU 的第 0 位开始。
  • 物理值 = 原始值 × 0.05625。
  • 有效范围为 0-350 km/h——超出此范围的值将被 COM 标记为“无效信号“并触发 DEM(诊断事件管理器)。

BSW 配置生成器读取这段 XML,生成对应的 C 代码数据结构——ComSignal_t 结构体、信号提取参数表。 这就是 AUTOSAR 从“每个项目手写代码“到“配置驱动生成代码“的转变。你的手不再碰位掩码。你描述你想要什么,工具生成为你服务的代码。


AUTOSAR 的另一张面孔:Adaptive Platform(AP)

CP 是为经典实时 ECU 设计的。OS 是 OSEK/VDX。配置是静态的——所有任务和通信时间表在编译期就固定好了。

但汽车在变化。ADAS 域控制器、自动驾驶平台、V2X 通信单元——这些 ECU 需要:

  • 强大的算力(多核 CPU、GPU、AI 加速器)
  • 动态部署——任务可以在运行时启动或停止
  • 服务发现——SOME/IP 协议让新加入的组件被自动发现
  • OTA 升级——局部更新固件而不影响整车

AP 用 POSIX 操作系统(Linux、QNX)而不是 OSEK/VDX。支持 C++ 而不是纯 C。ARA(AUTOSAR Runtime for Adaptive)替代 RTE,提供面向服务的通信。

Application (C++) → ARA → Foundation Services → POSIX OS → Hardware

在 AP 中,SWC 之间的通信不是固定的“端口读/写“,而是服务调用(Service-Oriented Architecture, SOA)。一个 SWC 在启动时通过 SOME/IP 协议向网络中宣告自己提供的服务——例如“ADAS_SensorFusion 提供 SensorData 服务“。另一个 SWC 通过 SOME/IP 的服务发现机制找到这个服务,建立 TCP/UDP 连接,开始订阅数据流。

SOME/IP 服务发现流程:

ECU-A (提供者)                    ECU-B (消费者)
    |                                    |
    |  Offer Service (SensorData, UDP)   |
    |  ─────────────────────────────>    |  (组播到整个子网)
    |                                    |  "有人提供SensorData?我要了。"
    |                                    |  Subscribe (SensorData, TCP)
    |  <─────────────────────────────    |
    |  "好,开始给你发数据。"               |
    |  SensorData stream =============>    |

CP 是静态配置的。AP 是动态的。CP 面向高确定性——每一个 OS task 的执行周期和优先级在编译期完全固定。AP 面向高灵活性——ADAS 功能可以在车辆行驶中动态部署、动态升级、动态关联。

一辆高端车同时跑着 CP(发动机控制)和 AP(自动驾驶感知)。两个世界在同一个 CAN 网关后面共存——网关的 CP 侧处理实时 CAN/CAN-FD 报文,AP 侧通过 SOME/IP 向 ADAS 应用提供车辆状态数据。


你在 R5 核心上实打实的工作流

假设你的硬件是 SPC58NN84(PowerPC e200 核心,锁步),你需要在上面集成一个完整的 AUTOSAR CP 栈。典型的工作流是五个阶段。

阶段一:MCAL 配置。 在 Tresos Studio(EB)或 ISOLAR(Vector)中配置 MCAL 模块——CAN、SPI、GPT、ADC。这一步生成 MCAL 配置参数,告诉驱动每一个外设的具体设置。例如 CAN 控制器配置:波特率、每个邮箱的 CAN ID 过滤器、接收/发送类型。

阶段二:BSW 配置。 配置 ECU 状态管理(EcuM)、通信栈(Com/PduR/CanIf/CanTp)、诊断栈(DCM/DEM)。每个模块的参数通过 ARXML 文件定义。BSW 配置生成器把 ARXML 翻译成 C 配置结构体——你在编译时链接,而不是在运行时解析 XML。

阶段三:RTE 配置。 根据 SWC 的描述(SWC 有哪些端口,端口是发送还是接收,数据类型是什么),RTE 生成器为每个 SWC 生成 Rte_<SWC>.hRte_<SWC>.c。这些生成的文件把 SWC 的端口读写映射到 COM 的信号、NvM 的存储块、或操作系统的事件。

阶段四:SWC 编写。 应用工程师只写 SWC 的逻辑代码(C 语言或 MATLAB/Simulink 自动代码生成),不碰任何底层。SWC 的世界里只有 Rte_Read_xxx()Rte_Write_xxx()Rte_Call_xxx()——这些是 SWC 能做的唯一操作。

阶段五:系统集成。 将各部分编译链接——SWC 对象文件 + RTE 生成文件 + BSW 配置生成文件 + MCAL + OS——烧入 ECU,跑集成测试。

关键收益: 当你换另一个芯片平台时——比如从 SPC58NN84 换到英飞凌 TC399——应用 SWC 和大部分 BSW 可以复用。只需要重新做 MCAL 配置(不同的芯片不同的外设寄存器映射)和 OS 配置(不同的核数、不同的中断控制器)。这大幅降低了 Tier-1 的开发成本,也保证了跨 OEM 和跨硬件平台的软件一致性。


标准:不是束缚,是释放

在没有 AUTOSAR 的时代,每个 Tier-1 和 OEM 各自为政。好的工程师做出好的系统,但他们的知识和代码无法传承、无法复用。汽车电子软件领域像是在“小农经济“——每家都在做自己的东西,努力但低效。

AUTOSAR 做的事情是工业标准化。把 ECU 软件拆分成分层、分模块、可配置的组件——就像 ISO 把螺丝钉的螺纹规格标准化一样。这样一来:

  • 芯片厂商可以只做 MCAL,不用管上层的应用逻辑。
  • Tier-1 可以跨平台、跨 OEM 地复用应用 SWC。
  • OEM 可以在不同 Tier-1 之间竞标,不担心被锁定到某个供应商的专有平台。

标准化不是束缚创造力——是释放更大的创造力。 当你不用为每个 ECU 重新写 CAN 驱动时,你的精力可以用在更高级的问题上:混合动力模式的能量管理、ADAS 传感器融合算法、OTA 升级的安全架构。

回头看这一章走过的路:

  1. 裸机:一个人掌控一切。简单、灵活、不可扩展。
  2. RTOS:在有限资源下引入任务调度和通讯机制。扩展性提高,但需要更强的系统设计能力。
  3. AUTOSAR:工业化、标准化、跨平台复用。释放整个行业的规模效应。

你在其中任何一个阶段工作,都是这个行业文明进步的一部分。

但 ECU 不只是“跑得动“。它还要能“说话“——当它出了问题,它要能告诉维修技师:我哪里不对、什么时候不对、附带什么上下文数据。

这就是诊断。UDS——统一的诊断服务。


本篇小结

今天我们做了一件事:理解AUTOSAR如何通过工业标准化,把一个Rte_Read()调用翻译成CAN总线上的物理帧——以及这种标准化如何释放整个行业的规模效应。

关键结论:

  1. AUTOSAR不是“又一个大框架“——是ECU软件的工业标准化:把ECU软件拆成分层、分模块、可配置的组件,像ISO把螺丝钉螺纹标准化一样。CAN驱动写一次,诊断栈写一次,安全组件封装一次——跨平台复用。
  2. CP(经典平台)和AP(自适应平台)分治了汽车电子的两个世界:CP面向实时MCU(确定性延迟、静态配置),AP面向高性能MPU(POSIX OS、动态服务发现)。
  3. AUTOSAR从裸机和RTOS的“手工经济“进化到“工业经济“:应用工程师只写SWC逻辑代码,不碰任何底层。换芯片平台时只需重配MCAL——释放出来的精力可以投向更高价值的系统级问题。

下一节,第六部分——软件的灵魂。诊断让ECU能“说话“、安全让ECU不可被攻破、OTA让ECU持续进化。从DTC的浮栅电子到HSM的熔丝阵列——穿透所有抽象层。

【下集预告】

2012 年,一辆豪华车在高速上突然失去动力。熄火,溜到路边,再也打不着。维修技师接上诊断仪,读取故障码——P0001。UDS 诊断仪在 30 分钟内锁定了 2 号缸喷油器线束脱焊。没有 UDS,这辆车要被拆开一半才能找到问题。

诊断不是“拆车“,而是“对话“。UDS 为 ECU 定义了一种语言——让一个沉默的金属疙瘩能够向世界报告自己的健康状况。每一个 DTC 不是一个简单的布尔值——它是一个有生命周期的状态机,从“当前操作周期检测到故障“,到“连续 3 个周期确认的真实故障“,到“写入非易失存储“,到最终被维修技师手动清除。它的旅程横跨了物理世界(传感器返回值)、AUTOSAR 软件栈(DEM/DCM)、和维修车间(诊断仪)。

下一章,我们进入 Part 6——诊断、安全、存储。汽车不仅仅是速度和操控。它还要能自检、不被攻击、不掉失数据。