ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MCAL、ECU Abstraction、Service、RTE、ASW 到底怎么分工?代码到底该放哪一层?

MCAL、ECU Abstraction、Service、RTE、ASW 到底怎么分工?代码到底该放哪一层? MCAL、ECU Abstraction、Service、RTE、ASW 到底怎么分工代码到底该放哪一层上一篇我们把 AUTOSAR Classic 的完整软件架构过了一遍。如果还没看可以先看这里一张图看懂 AUTOSAR Classic 完整软件架构上一篇解决的是AUTOSAR Classic 里面到底有哪些层每一层大概有什么东西。但实际做项目的时候真正让人头疼的往往不是“MCAL 是什么”“RTE 是什么”。而是这种问题我现在要写一段代码这段代码到底应该放哪里比如来了一个非常普通的需求KL15 接在 MCU 的 P33.0 上。当 KL15 为高电平时VCU 认为整车已经上低压开始执行后续状态切换。如果是一个普通裸机项目这件事可能几分钟就写完了if(Dio_ReadChannel(KL15_PIN)STD_HIGH){VehicleStateVEHICLE_STATE_WAKEUP;}代码能不能跑当然能。问题是这段代码应该写在 ASW 里面吗如果不应该那Dio_ReadChannel()应该在哪里调用P33.0 和 KL15 的对应关系应该谁知道“KL15 有效以后进入 Wakeup”又应该谁负责这才是 AUTOSAR 分层真正开始有意思的地方。一、先别急着背层级先拆一个 KL15我们先把刚才那个需求拆开。实际硬件是KL15 │ │ 12V / 电气信号 ▼ 输入电路 │ ▼ MCU P33.0软件需要最终得到KL15 ON然后应用软件根据这个状态决定车辆进入唤醒状态乍一看这就是一个功能。但如果仔细拆会发现里面其实混了三件完全不同的事情。第一件P33.0 当前是高电平还是低电平第二件P33.0 这个输入在这块 ECU 上代表的是 KL15。第三件KL15 有效以后车辆应该做什么。这三件事看起来很接近实际上属于三个完全不同的抽象层级。比较合理的关系应该是P33.0 │ ▼ Dio │ ▼ IoHwAb / ECU Abstraction │ ▼ RTE │ ▼ VehicleState SWC把它翻译成人话Dio 我只知道 P33.0 是高还是低。 IoHwAb 我知道这个输入在 ECU 上叫 KL15。 ASW 我只关心 KL15 有没有上电。 至于 KL15 接 P33.0 还是 P20.4我不关心。这就是 AUTOSAR 分层真正想解决的问题。二、MCAL 最好不要知道“KL15”是什么先从最下面说起。P33.0 是 MCU 的一个具体 Pin。真正读取它的是 MCAL 里面的 Dio Driver。类似Dio_LevelType level;levelDio_ReadChannel(DioConf_DioChannel_P33_0);Dio Driver 知道PORT33 Pin0 Input High / Low但从软件架构上讲它最好不知道这个 Pin 是 KL15为什么因为“KL15”是 ECU 的电气功能定义。而P33.0是 MCU 的硬件资源定义。这两件事情不是一个层级。今天的原理图可能是KL15 → P33.0下一版硬件有可能变成KL15 → P20.4如果应用代码里面到处都是Dio_ReadChannel(DioConf_DioChannel_P33_0);那硬件一改应用代码也得跟着改。这就意味着应用层已经和 MCU 硬件绑死了。这显然不是 AUTOSAR 想要的结果。三、那谁负责把 P33.0 变成 KL15这时候就到了 ECU Abstraction。上一篇我们已经讲过 ECU Abstraction 是什么这里不再重复定义。这次只看它在项目里到底干什么。可以做一个非常简单的封装Std_ReturnTypeIoHwAb_GetKl15State(boolean*state){Dio_LevelType level;levelDio_ReadChannel(DioConf_DioChannel_KL15);if(levelSTD_HIGH){*stateTRUE;}else{*stateFALSE;}returnE_OK;}从这层开始软件里面第一次真正出现KL15而不是P33.0这一步非常关键。因为从现在开始上层软件看到的已经不再是 MCU 引脚。而是 ECU 的功能信号KL15 BrakeSwitch WakeupInput IgnitionState CrashInput这就是为什么叫ECU Abstraction。它抽象掉的已经不是单纯的 MCU而是这块 ECU 的具体硬件连接。四、ASW 最好连“Dio”这个单词都不要看到继续往上。现在 VehicleStateManager 需要知道 KL15 状态。如果直接这么写if(Dio_ReadChannel(DioConf_DioChannel_KL15)STD_HIGH){VehicleStateVEHICLE_STATE_WAKEUP;}短期看没什么问题。甚至很多实际项目里确实有人这么写。但从架构上看这里已经出现了一个很明显的问题ASW ↓ 直接调用 ↓ MCAL中间所有抽象层都被绕过去了。这意味着 VehicleStateManager 必须知道KL15 是一个 DIO这其实已经不应该是 ASW 关心的事情了。因为对于应用软件来说KL15 是通过 DIO 采集的和KL15 是通过 SPI SBC 读取的理论上没有区别。应用只应该看到Kl15State TRUE至于这个 TRUE 是怎么来的是硬件层的事情。所以更合理的应用代码可能只是boolean kl15State;Rte_Read_Kl15State(kl15State);if(kl15StateTRUE){VehicleStateVEHICLE_STATE_WAKEUP;}这时候 ASW 终于干净了。它只处理KL15有效 ↓ 车辆状态切换没有P33.0 Dio PORT Input Channel这些东西。五、这时候再看 RTE就容易理解多了很多人学 AUTOSAR 时最容易把 RTE 学得很玄乎。其实放到刚才这个例子里就很简单。下面已经有一个KL15 State上面的 VehicleState SWC 需要它。中间谁负责把数据接起来RTE。于是IoHwAb │ │ KL15 State ▼ RTE │ ▼ VehicleState SWC应用里面只看到Rte_Read_Kl15State(kl15State);甚至它都不知道这个数据到底是谁提供的。这就是 RTE 非常重要的一个价值让软件组件之间不需要互相知道对方的具体实现。六、那为什么不能所有东西都直接 Rte_Read这里再补一个实际项目里很容易产生的误区。有人看到这里可能会觉得那我是不是所有输入全部做成 Rte_Read 就完事了也不是。AUTOSAR 分层不是为了“所有函数外面套一个 Rte_”。真正重要的是不同的软件层只知道自己应该知道的信息。比如还是 KL15。MCAL 应该知道哪个 Port 哪个 Pin 怎么读硬件ECU Abstraction 应该知道这个 Pin 在 ECU 上是什么功能ASW 应该知道KL15 有效以后车辆怎么响应RTE 只是把这些不同世界连接起来。所以分层的重点不是 API 长什么样。而是知识边界。谁应该知道什么。谁不应该知道什么。这个思维比背 AUTOSAR 模块分类重要得多。七、再看一个 ADC问题会更明显KL15 还算简单。换一个稍微复杂一点的。比如 VCU 上有一路模拟量油门踏板传感器。硬件可能是05V ↓ MCU ADC Channel现在问一个问题ADC 采集到的 Raw Value 转成实际电压应该放哪一层再进一步电压转成 0100% 的踏板开度又应该放哪一层这两个事情其实并不一样。假设 ADC 读取Adc_ValueGroupType adcRaw;Adc_ReadGroup(...,adcRaw);得到2456这是一个纯硬件量。它属于 MCAL 能理解的世界。继续往上2456 ↓ 2.31 V这是硬件电气量转换。这类事情通常更适合放在IoHwAb / ECU Abstraction再往上2.31 V ↓ 43.5%这时候就开始接近功能定义了。比如踏板的0.5V 0% 4.5V 100%还可能包含死区 合理性判断 双路踏板校验 故障降级这些已经越来越接近应用功能。最终可能变成ADC Raw ↓ MCAL ↓ Voltage ↓ IoHwAb ↓ Pedal Position ↓ RTE ↓ TorqueControl SWC但实际项目里到底在哪里做电压转换、在哪里做百分比换算并没有一句放之四海而皆准的答案。要看公司软件架构 功能安全分区 软件复用要求 具体传感器设计但判断原则还是一样越靠近硬件的知识越往下放。越靠近车辆功能的知识越往上放。这个原则非常实用。八、CAN 更能说明“为什么不能乱跨层”再看大家最熟悉的 CAN。假设 VCU 要发送VCU_HVStatus很多人刚接触 AUTOSAR 时会看到下面几个函数Can_Write()CanIf_Transmit()Com_SendSignal()Rte_Write()然后开始混乱到底应该用哪个答案其实不是看“哪个函数能把报文发出去”。而是看你现在站在哪一层。如果你在 ASW比如 VehicleStateManager 算出来VCU_HVStatus HV_READY你应该表达的是我的应用信号变成 HV_READY 了。所以应用层更合理的是Rte_Write_VCU_HVStatus(HV_READY);它不应该知道CAN ID PDU Hardware Object CAN Controller到 COMCOM 负责的是Signal I-PDU Update Bit Timeout Signal Group Transmission Mode所以它看到的是VCU_HVStatus和这个 Signal 属于哪个 I-PDU到 PduRPduR 更不关心VCU_HVStatus 是不是高压状态它只关心这个 PDU 应该往哪里路由。到 CanIfCanIf 开始知道这个 PDU 应该走哪个 CAN Driver 哪个 Tx PDU 哪个 Controller但它依然不会直接去操作 CAN Controller 寄存器。最后到 Can Driver真正调用硬件的才是Can_Write()这时候已经到了 MCAL。所以一条完整发送链路大概是ASW │ │ Rte_Write ▼ RTE │ ▼ COM │ ▼ PduR │ ▼ CanIf │ ▼ Can Driver │ ▼ CAN Controller现在再回头看一个问题ASW 能不能直接调用 Can_Write()技术上很多情况下你确实能做到。架构上基本不应该这么干。因为一旦这么做ASW 就开始知道Hardware Object PDU Handle CAN Driver Controller应用和底层立刻耦合起来。九、Can_Write 能调用不代表你就该调用这个问题其实很值得单独说一下。实际开发中经常会出现一种情况编译能过 功能能跑 测试也没问题于是大家觉得这代码没问题。但 AUTOSAR 项目里“能不能跑”和“架构对不对”其实是两个问题。例如Dem_SetEventStatus(...);某些项目里 ASW 直接调用它可能完全能工作。但如果整个项目定义的是SWC ↓ RTE ↓ Dem那应用直接调用Dem_SetEventStatus()就相当于绕过 RTE这未必会立刻产生 Bug。但会留下几个问题软件组件失去独立性 代码复用困难 依赖关系变复杂 软件组件测试困难 架构规则越来越乱所以 AUTOSAR 项目里经常需要区分两个概念Functionally Correct 和 Architecturally Correct也就是功能上是对的不代表架构上是对的做大型 ECU 项目这个区别非常重要。十、NvM 也是一样再举一个很典型的例子。应用软件需要保存驾驶模式记忆比如用户上次选择的是SPORT下次上电以后还要恢复。最粗暴的方法ASW ↓ 直接写 Flash理论上完全可以。但如果这么做应用就必须知道Flash地址 擦写方式 Sector Page 写入时序 数据有效性 掉电保护这些全部变成应用层的负担。所以 AUTOSAR 才会设计ASW ↓ RTE ↓ NvM ↓ MemIf ↓ Fee / Ea ↓ Fls / Eep ↓ Hardware应用只想表达把这个数据保存下来。至于底下到底什么时候写 写到哪里 怎么做冗余 怎么做 Block 管理不应该由 VehicleMode SWC 来操心。十一、Service Layer 为什么看起来特别杂到这里再回头看 Service Layer理解起来会比背定义简单很多。因为这一层其实放的是 ECU 的各种“公共能力”。比如Dem Dcm NvM ComM EcuM BswM COM PduR这些模块彼此做的事情完全不一样。所以第一次看 AUTOSAR 架构图时会觉得为什么这些乱七八糟的东西都放在 Service Layer其实它们有一个共同点都不是某一个具体应用功能独享的。比如 Dem。不是只有TorqueControl需要故障管理。可能ThermalManagement VehicleState Charging EnergyManagement全部都需要。NvM 也一样。COM 也一样。ComM、EcuM 更明显。它们提供的是整个 ECU 共用的基础软件能力。所以叫 Service。从这个角度看就比死记“Service Layer 里面有哪些模块”自然很多。十二、外部芯片到底算 MCAL还是 ECU Abstraction这个问题在实际项目里非常常见。比如 ECU 上有一颗 SBCTLE8888通过 SPI 和 MCU 通信。那 TLE8888 Driver 属于 MCAL 吗一般来说不是标准 MCAL。因为 MCAL 主要面对的是 MCU 内部外设。比如SPI ADC CAN PORT PWMTLE8888 是 ECU 板子上的外部器件。它可能通过SPI Driver去访问。关系更像Application / BSW ↓ TLE8888 Driver ↓ SPI ↓ MCU QSPI这里 SPI 属于 MCAL。而 TLE8888 的控制逻辑根据具体软件架构可能被放在ECU Abstraction或者CDD十三、什么时候会用 CDDCDD 也就是Complex Device Driver。项目里经常遇到一些硬件或者功能AUTOSAR 标准模块并没有很好地覆盖。比如特殊电源芯片 特殊 AFE 定制 ASIC FPGA 复杂硬件控制 强实时功能这时候可能就会做一个 CDD。比如ASW ↓ RTE ↓ CDD_TLE8888 ↓ SPI ↓ MCU或者某些 CDD 直接访问硬件。这也是 AUTOSAR 很现实的一面。它并没有要求所有东西必须严格按照标准模块一级一级走。而是标准能够解决的尽量走标准架构。特殊问题允许通过 CDD 等方式解决。否则很多真实 ECU 根本没法做。十四、实际项目里最怕的不是跨层而是“没人知道为什么跨层”这里说一个比较实际的经验。做 AUTOSAR 项目千万不要把“不能跨层”理解成绝对铁律。真实项目一定会存在一些特殊需求 性能要求 历史代码 供应商限制 芯片限制 Bootloader接口 复杂驱动 功能安全设计导致某些调用并不完全符合最漂亮的架构图。这很正常。真正的问题不是有没有跨层。而是为什么跨层有没有明确设计。如果大家都知道这个模块因为 XX 性能原因 经架构评审允许直接访问 XX Driver。这是设计。但如果工程里面变成这里为了方便直接调一下 CanIf。 那里为了省事直接调一下 Dio。 再那里直接操作寄存器。做着做着就会发现软件分层只剩下 PPT 里的架构图。代码里面已经完全不是那么回事了。这才是最麻烦的。十五、判断一段代码放哪层我一般会问三个问题实际开发的时候我觉得没有必要把问题搞得特别复杂。看到一段代码先问三个问题。第一个问题它知道具体硬件吗比如代码里面出现P33.0 ADC Group 3 CAN Controller 1 SPI Channel 2 GTM TOM那它大概率已经非常靠近MCAL / Driver第二个问题它知道 ECU 上这个硬件是干什么的吗比如KL15 BrakeSwitch PowerRelay WakeupPin SensorSupply已经开始从MCU资源变成ECU硬件功能那通常已经到了ECU Abstraction / IoHwAb / CDD这个范围。第三个问题它是不是在决定车辆应该怎么做比如KL15有效以后是否进入Ready SOC低于多少开始限扭 什么条件允许上高压 什么时候允许快充 什么时候进入故障降级这种代码基本已经属于ASW这三个问题在实际工作中非常好用。十六、再把 KL15 从头走一遍现在重新看文章开头那个需求KL15 接在 P33.0。KL15 有效以后让 VCU 进入唤醒状态。合理的软件责任可以拆成MCAL负责读取 P33.0例如Dio_ReadChannel(...)它不管这个 Pin 为什么要读。ECU Abstraction / IoHwAb负责P33.0 ↓ KL15 State它知道这个硬件输入在这块 ECU 上代表 KL15。RTE负责把 KL15 State 送给需要它的 SWCASW负责KL15 ON ↓ Vehicle State Transition它知道KL15 有效以后整车应该怎么运行。但不知道KL15 到底接在哪个 Pin 上。这样整个软件边界就非常清楚。十七、再看一次 ADC同样MCU ADC Channel ↓ Adc Driver ↓ IoHwAb ↓ RTE ↓ Application各层关心的事情分别是Adc Driver “这个 ADC Channel 采到了多少”↓IoHwAb “这个值代表某一路 ECU 模拟输入。”↓ASW “这个传感器现在是什么状态 接下来车辆应该怎么控制”十八、再看一次 CANCAN Controller ↓ Can Driver ↓ CanIf ↓ PduR ↓ COM ↓ RTE ↓ ASW各层继续逐渐忘掉硬件细节。越往上CAN Controller Hardware Object PDU I-PDU Signal Vehicle Function你会发现这是一个很有意思的过程。AUTOSAR 从下往上其实一直在做一件事不停地把“硬件语言”翻译成“功能语言”。最下面说PORT33.0 HIGH中间变成KL15 ON最上面变成车辆应该唤醒CAN 也是一样。最下面是Controller / Mailbox / Frame中间是PDU / Signal最上面变成SOC VehicleSpeed TorqueRequest HVStatus如果从这个角度理解 AUTOSAR很多东西就不需要硬背了。十九、为什么大型 ECU 必须这么折腾看到这里还是会有人问直接写不是简单很多吗是。小项目绝对是直接写更简单。比如一个 MCU 几十个 IO 一条 CAN 几千行代码你完全可以ReadPin();ReadAdc();SendCan();很快就做完了。但是实际量产 ECU 往往是几百甚至上千个 CAN Signal 几十个诊断 DID 几百个 DTC 多路 CAN / LIN 大量 IO / ADC / PWM Bootloader 网络管理 睡眠唤醒 多核 功能安全 几十甚至上百个应用组件而且还有应用供应商 BSW供应商 MCAL供应商 整车厂 Tier1 测试团队 标定团队共同开发。这个时候真正麻烦的已经不是代码能不能写出来。而是这么多人写的代码怎么保证互相不搅在一起。AUTOSAR 分层的价值就在这里。二十、所以 MCAL、ECU Abstraction、Service、RTE、ASW 到底是什么关系看到最后其实可以不用再背一遍定义。只需要记住这一条变化硬件资源 ↓ MCAL ↓ ECU硬件功能 ↓ ECU Abstraction ↓ 公共基础能力 ↓ Service Layer ↓ RTE ↓ 车辆功能 ↓ ASW不过再次强调这不是严格的调用顺序图。真实 AUTOSAR 里面存在不同的软件路径。比如 CANASW ↓ RTE ↓ COM ↓ PduR ↓ CanIf ↓ Can而 IO 可能是ASW ↓ RTE ↓ IoHwAb ↓ DioNvM 又是ASW ↓ RTE ↓ NvM ↓ MemIf ↓ Fee ↓ Fls所以不要试图把所有 AUTOSAR 模块强行排成一条直线。真正需要理解的是每一层负责什么信息每一层不应该知道什么信息。最后上一篇我们看 AUTOSAR 架构图的时候更多是在认地图这里是 ASW 这里是 RTE 这里是 Service 这里是 ECU Abstraction 这里是 MCAL这一篇真正想说的是地图不是拿来背的。实际做项目的时候更有价值的问题应该是我现在这段代码到底属于硬件资源、ECU硬件功能还是车辆功能一旦这个问题能够想清楚后面再去看Dio Adc CanIf PduR COM NvM Dem Dcm就会轻松很多。因为它们不再是一堆缩写。你会开始知道它为什么在这里。以及它为什么不能随便跑到另外一层去。下一篇预告AUTOSAR 里的 SWC、Runnable、Port、Interface 到底是什么前面几篇我们一直在讲ASW RTE BSW下一篇开始正式进入应用软件。比如一个 SWC 到底是什么 Runnable 是不是一个普通 C 函数 Sender/Receiver Port 是怎么通信的 Client/Server 又是什么 Rte_Read、Rte_Write 到底是怎么生成出来的这些东西理解以后才算真正开始走进 AUTOSAR 应用软件开发。车控码农 · AUTOSAR 从 0 到 1尽量不背概念。从真实 ECU 开发的问题出发一点一点把 AUTOSAR 拆开。
返回列表