第二章:系统架构设计——车载音响系统硬件架构、软件分层架构、OTA升级模块在整个系统中的位置

好,咱们进入正题。这一章我打算聊聊系统架构。说实话,很多刚入行的朋友容易忽略架构设计,上来就写代码。我年轻时也犯过这毛病,结果项目后期改得想哭。你想想看,音响系统OTA升级,如果架构没搭好,后面全是坑。

2.1 车载音响系统硬件架构

先看硬件。车载音响系统不是你家那台蓝牙音箱,它复杂得多。我习惯把硬件分成三个域:

  • 主控域:核心SoC,跑操作系统和上层应用。常见的有高通SA8155、瑞萨R-Car系列。OTA升级的主逻辑就在这里。
  • 音频DSP域:专门处理音频算法的芯片,比如ADSP、TI的DA8xx。它负责音效、降噪、均衡器。OTA升级时,DSP固件也得跟着升。
  • 功放域:功率放大芯片,直接驱动喇叭。有些高端功放也带MCU,需要独立升级。

关键点:这三个域之间通常用I2S传音频流,用I2C/SPI传控制命令。OTA升级时,主控域通过内部总线把固件分发给DSP和功放域。

我在项目中遇到过一个问题:DSP域和主控域之间的通信协议没统一,结果升级包传一半就断了。后来我们强制要求所有域都支持A/B分区升级,才彻底解决。

2.2 软件分层架构

软件架构我建议分四层。为什么是四层?因为我在多个项目里试过三层和五层,最后发现四层最舒服。

层级 职责 OTA相关
应用层 UI交互、音源管理、设置 显示升级进度、用户确认
服务层 音频策略、设备管理、OTA服务 下载管理、版本校验、回滚逻辑
中间件层 通信协议、文件系统、安全模块 HTTPS下载、签名验证、分区管理
驱动层 硬件抽象、外设驱动 Flash擦写、看门狗、电源管理

说白了,每一层只管自己的事。应用层不知道底层怎么擦Flash,驱动层也不关心用户点了什么按钮。这种解耦在OTA升级时特别重要——你想想看,如果升级过程中应用层崩溃了,底层还能继续写分区吗?

我的习惯:在服务层单独放一个OTA Manager模块。它不依赖任何UI组件,即使屏幕黑了,升级也能跑完。我曾经在一个项目里就是因为UI线程卡死,导致升级中断,车机变砖。从那以后,OTA Manager必须独立于UI进程。

2.3 OTA升级模块在整个系统中的位置

OTA升级模块不是孤立的。它像一根线,串起了整个系统。我画个逻辑图给你看:

+-------------------+       +-------------------+
|   应用层 (UI)      | <---> |   OTA Manager     |
|  - 升级进度条      |       |  - 下载引擎       |
|  - 用户确认弹窗    |       |  - 版本比对       |
+-------------------+       |  - 回滚控制       |
        |                   +--------+----------+
        |  IPC通信                    |
        v                            v
+-------------------+       +-------------------+
|   服务层           |       |   中间件层         |
|  - 音频策略       |       |  - HTTPS下载      |
|  - 设备管理       |       |  - 签名验证       |
+-------------------+       |  - 分区管理       |
                            +--------+----------+
                                     |
                                     v
                            +-------------------+
                            |   驱动层           |
                            |  - Flash驱动      |
                            |  - 看门狗         |
                            +-------------------+

注意看,OTA Manager横跨了应用层和服务层。为什么?因为下载和校验必须在服务层做,但用户交互又离不开应用层。我建议把OTA Manager拆成两个子模块:

  • OTA Core:放在服务层,负责下载、校验、分区切换。它不依赖任何UI。
  • OTA UI:放在应用层,负责显示进度、处理用户确认。它通过IPC调用OTA Core。

避坑指南:我曾经把OTA Core和OTA UI放在同一个进程里,结果升级过程中UI卡死,Core也跟着挂了。后来我强制它们分进程运行,OTA Core跑在独立的守护进程里,即使UI崩溃,升级照常进行。

2.4 升级流程中的架构协作

一个完整的OTA升级流程,各层是怎么协作的?我拿实际项目举例:

  1. 应用层:用户点击「检查更新」,OTA UI发送请求给OTA Core。
  2. 服务层:OTA Core连接云端,下载升级包。同时通知中间件层准备存储空间。
  3. 中间件层:HTTPS下载完成后,进行签名验证。验证通过后,把固件写入备用分区。
  4. 驱动层:执行Flash擦写操作。注意,这里要关中断,防止写入过程中被打断。
  5. 回滚准备:写入完成后,OTA Core设置启动标志,指向新分区。如果下次启动失败,看门狗会触发回滚。

嗯,这里要注意:驱动层的Flash擦写是最容易出问题的环节。我建议在驱动层加一个超时保护,如果擦写超过5秒没完成,强制复位。否则一旦卡死,整个系统就瘫了。

2.5 架构设计的几个原则

最后,我总结几条原则。这些是我踩过坑后总结出来的:

  • 隔离性:OTA模块必须独立于业务逻辑。业务代码可以随便改,但OTA模块的接口不能变。
  • 原子性:升级要么全成功,要么全回滚。不能出现一半新一半旧的情况。
  • 可观测性:每一层都要有日志输出。我习惯在驱动层、中间件层、服务层各留一个日志缓冲区,升级失败时能快速定位问题。
  • 降级能力:如果升级过程中电量不足、信号丢失,系统要能优雅降级,而不是直接变砖。

一句话总结:架构设计不是为了好看,是为了让你在出问题时能睡得着觉。OTA升级模块就像汽车的刹车系统——平时用不上,但一旦需要,它必须可靠。

好,这一章就到这里。下一章我会详细讲升级包的生成和签名流程,那是另一个容易踩坑的地方。