第3章 系统架构设计:架构图、接口与功能分配
系统架构设计,说白了就是给整个项目画骨架。你想想看,没有骨架的软件就像一滩烂泥,后期想改都无从下手。我在V模型开发里摸爬滚打这么多年,最深的体会就是:架构阶段多花一周,实现阶段能省一个月。
这一章,我们就来聊聊系统架构设计的三个核心动作:画架构图、定义接口、分配功能。嗯,这三个事儿做好了,后面的开发就会顺畅很多。
3.1 系统架构图:从黑盒到白盒
我个人习惯,画架构图分两步走。第一步是黑盒视图,只看系统跟外界的交互;第二步是白盒视图,把系统内部拆开来看。
3.1.1 黑盒视图
黑盒视图关注的是:系统有哪些外部输入?输出什么?跟哪些其他系统通信?
举个例子,一个车身域控制器,它的外部接口可能包括:
- 传感器输入:门锁状态、车窗位置、雨量传感器
- 执行器输出:门锁电机、车窗电机、指示灯
- 网络通信:CAN总线、LIN总线、以太网
- 诊断接口:UDS诊断、Bootloader
关键点:黑盒视图只定义"有什么",不定义"怎么做"。这个阶段不要陷入实现细节。
3.1.2 白盒视图
白盒视图就是把黑盒拆开,看看里面有哪些模块。我一般按照功能域来划分:
- 感知层:处理传感器数据,做信号调理
- 决策层:执行控制逻辑,状态机管理
- 执行层:驱动执行器,输出PWM或数字信号
- 通信层:协议栈管理,报文收发
- 诊断层:故障检测,DTC管理
下面这张图,就是典型的白盒架构分层:
我的经验:分层架构最大的好处是解耦。我曾经在一个项目里,因为感知层和决策层混在一起,改一个传感器类型就得动整个控制逻辑,那叫一个痛苦。后来强制分层,改传感器只需要换感知层的模型,决策层纹丝不动。
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 分配原则
我个人总结了几条分配原则:
- 高内聚低耦合:一个功能尽量在一个模块里完成,减少跨模块调用
- 单一职责:一个模块只做一件事,别搞成大杂烩
- 可测试性:每个模块都要能独立测试,别依赖其他模块
- 复用性:通用的功能(比如滤波、诊断)做成公共模块,别到处复制
我的经验:功能分配的时候,一定要考虑时序约束。比如一个控制周期是10ms,那这个周期内所有功能的总执行时间不能超过10ms。我曾经在一个项目里,把两个耗时很长的功能放在同一个周期里,结果导致任务超时,系统复位。后来拆到不同周期,问题就解决了。
3.4 架构设计文档
架构设计做完,一定要写文档。我见过太多项目,架构都在工程师脑子里,人一走,架构就没了。嗯,这是个大坑。
一份完整的架构设计文档,至少包含:
- 架构图:黑盒视图和白盒视图
- 接口定义表:硬件接口和软件接口
- 功能分配矩阵:功能到模块的映射
- 时序约束:每个模块的执行周期和最大执行时间
- 关键算法说明:比如状态机、滤波算法、控制策略
避坑指南:文档不是写给别人看的,是写给你自己看的。三个月后你回头看自己的代码,如果没有文档,你可能都搞不懂当初为什么这么设计。所以,别偷懒,把文档写清楚。
好了,这一章的内容就到这里。架构设计是V模型开发的基石,基础打好了,后面的路就好走了。记住:架构设计不是一次性的工作,它需要在整个开发过程中持续迭代。别怕改,就怕不改。