3. Simulink建模规范与可代码生成模型准则

系统架构设计,说白了就是给整个项目画骨架。你想想看,没有骨架的软件就像一滩烂泥,后期想改都无从下手。我在V模型开发里摸爬滚打这么多年,最深的体会就是:架构阶段多花一周,实现阶段能省一个月。

这一章,我们就来聊聊系统架构设计的三个核心动作:画架构图、定义接口、分配功能。嗯,这三个事儿做好了,后面的开发就会顺畅很多。

3.1 系统架构图:从黑盒到白盒

我个人习惯,画架构图分两步走。第一步是黑盒视图,只看系统跟外界的交互;第二步是白盒视图,把系统内部拆开来看。

3.1.1 黑盒视图

黑盒视图关注的是:系统有哪些外部输入?输出什么?跟哪些其他系统通信?

举个例子,一个车身域控制器,它的外部接口可能包括:

  • 传感器输入:门锁状态、车窗位置、雨量传感器
  • 执行器输出:门锁电机、车窗电机、指示灯
  • 网络通信:CAN总线、LIN总线、以太网
  • 诊断接口:UDS诊断、Bootloader

关键点:黑盒视图只定义"有什么",不定义"怎么做"。这个阶段不要陷入实现细节。

3.1.2 白盒视图

白盒视图就是把黑盒拆开,看看里面有哪些模块。我一般按照功能域来划分:

  • 感知层:处理传感器数据,做信号调理
  • 决策层:执行控制逻辑,状态机管理
  • 执行层:驱动执行器,输出PWM或数字信号
  • 通信层:协议栈管理,报文收发
  • 诊断层:故障检测,DTC管理

下面这张图,就是典型的白盒架构分层:

系统白盒架构分层图 感知层(信号调理、传感器融合) 输入:开关信号、模拟量、脉冲信号 → 输出:标准化数值 决策层(控制逻辑、状态机) 输入:感知数据 → 输出:控制指令 执行层(驱动、PWM、诊断) 输入:控制指令 → 输出:执行器信号 通信层(CAN/LIN/以太网协议栈) 输入:网络报文 → 输出:应用层数据 诊断层(UDS、DTC管理、Bootloader)

我的经验:分层架构最大的好处是解耦。我曾经在一个项目里,因为感知层和决策层混在一起,改一个传感器类型就得动整个控制逻辑,那叫一个痛苦。后来强制分层,改传感器只需要换感知层的模型,决策层纹丝不动。

3.2 硬件与软件接口定义

接口定义,是架构设计里最容易出问题的地方。我见过太多项目,硬件和软件各干各的,等到联调的时候才发现接口对不上。嗯,那场面,真是鸡飞狗跳。

3.2.1 硬件接口定义

硬件接口,主要定义MCU引脚跟外部器件的连接关系。我建议用一张表格来管理:

信号名称 引脚号 方向 电气特性 备注
DOOR_LOCK_CTRL PA0 输出 3.3V CMOS, 推挽 高电平锁门
WINDOW_POS_SENS PA1 输入 0-5V 模拟量 ADC通道0
CAN_TX PB6 输出 CAN 2.0B CAN1控制器
CAN_RX PB7 输入 CAN 2.0B CAN1控制器

避坑指南:我曾经在一个项目里,硬件工程师把引脚方向标反了,软件按输出配置,结果一上电就把引脚烧了。所以,接口定义表一定要双方签字确认,别嫌麻烦。

3.2.2 软件接口定义

软件接口,主要定义模块之间的数据交换。在Simulink里,我习惯用数据字典来管理接口。每个接口包含:

  • 接口名称:比如 DoorLockCmd
  • 数据类型:uint8、boolean、single等
  • 取值范围:0-255、true/false等
  • 初始值:上电后的默认值
  • 单位:V、mA、N·m等

举个例子,一个门锁控制接口的定义:

% 数据字典条目示例
DoorLockCmd = Simulink.Signal;
DoorLockCmd.DataType = 'uint8';
DoorLockCmd.InitialValue = '0';
DoorLockCmd.Min = 0;
DoorLockCmd.Max = 2;
DoorLockCmd.Description = '门锁控制指令:0-解锁,1-上锁,2-保持';

关键点:接口定义要精确到每一位。比如一个字节的报文,哪几位是状态,哪几位是使能,都要写清楚。模糊的接口定义,是后期bug的温床。

3.3 系统功能分配

功能分配,就是把需求文档里的功能,分配到具体的模块和组件上。我一般用功能分配矩阵来做这件事。

3.3.1 功能分配矩阵

这个矩阵的行是功能,列是模块。每个单元格里写这个功能在模块里的实现方式。

功能 感知层 决策层 执行层 通信层
门锁控制 读取门锁开关状态 状态机判断锁/解锁 驱动门锁电机 上报门锁状态
车窗控制 读取车窗位置传感器 防夹算法、位置控制 驱动车窗电机 接收遥控指令
故障诊断 检测传感器短路/断路 记录DTC、降级策略 — 响应诊断请求

3.3.2 分配原则

我个人总结了几条分配原则:

  1. 高内聚低耦合:一个功能尽量在一个模块里完成,减少跨模块调用
  2. 单一职责:一个模块只做一件事,别搞成大杂烩
  3. 可测试性:每个模块都要能独立测试,别依赖其他模块
  4. 复用性:通用的功能(比如滤波、诊断)做成公共模块,别到处复制

我的经验:功能分配的时候,一定要考虑时序约束。比如一个控制周期是10ms,那这个周期内所有功能的总执行时间不能超过10ms。我曾经在一个项目里,把两个耗时很长的功能放在同一个周期里,结果导致任务超时,系统复位。后来拆到不同周期,问题就解决了。

3.4 架构设计文档

架构设计做完,一定要写文档。我见过太多项目,架构都在工程师脑子里,人一走,架构就没了。嗯,这是个大坑。

一份完整的架构设计文档,至少包含:

  • 架构图:黑盒视图和白盒视图
  • 接口定义表:硬件接口和软件接口
  • 功能分配矩阵:功能到模块的映射
  • 时序约束:每个模块的执行周期和最大执行时间
  • 关键算法说明:比如状态机、滤波算法、控制策略

避坑指南:文档不是写给别人看的,是写给你自己看的。三个月后你回头看自己的代码,如果没有文档,你可能都搞不懂当初为什么这么设计。所以,别偷懒,把文档写清楚。

好了,这一章的内容就到这里。架构设计是V模型开发的基石,基础打好了,后面的路就好走了。记住:架构设计不是一次性的工作,它需要在整个开发过程中持续迭代。别怕改,就怕不改。