第一章:为什么选 STM32H743?无人船主控芯片选型实战

做无人船的第一步不是写代码,是选芯片。芯片选错了,后面全是坑。


1. 一张图看懂选型困局

2024 年,我要做一台能自主航行的无人船。需求很明确:

必须能做的事:
  ✅ 实时采集 8 路传感器数据 (GPS/IMU/雷达/LiDAR/遥控器/...)
  ✅ 运行路径规划算法 (D* Lite + DWA, 实时规划绕开障碍)
  ✅ 输出 PWM/CAN 控制推进器
  ✅ 4G 联网 + 地面站通信
  ✅ 看门狗保命 (死机了能自己重启)

可选加分:
  ○ 运行轻量 CNN 做水面目标识别
  ○ 多船协同编队
  ○ OTA 远程升级

摆在面前三条路:

方案 代表芯片 单价 上手难度
A: 单片机裸跑 STM32H743 ¥40 ★★★
B: Linux 伴机 树莓派 CM4 + STM32 ¥400 ★★★★★
C: 纯 Linux 全志 T113 / NXP i.MX ¥80 ★★★★

2. 为什么我选了方案 A(而且没后悔)

2.1 Linux 伴机方案 — 看着美好,坑太深

刚开始我也心动过方案 B——STM32 做实时控制,树莓派跑路径规划和 AI。画了个很漂亮的架构图:

┌─────────────┐    UART/SPI    ┌──────────────┐
│   树莓派 CM4  │◄──────────────►│  STM32H743   │
│               │               │              │
│ 路径规划      │               │ PID控制      │
│ 视觉识别      │               │ 传感器采集    │
│ 4G通信       │               │ 推进器输出    │
│ 地面站        │               │ 看门狗        │
└─────────────┘               └──────────────┘

在纸上画了一个月,实际动手一周就放弃了。原因:

坑 1:两块芯片之间的通信是瓶颈

路径规划需要 LiDAR 数据(240Hz 帧率、30m 距离、每个点 0.357°)。树莓派上跑完 DWA,结果通过串口传给 STM32。多了几十毫秒延迟。船以 2m/s 运动,50ms 延迟 = 10cm 误差。撞浮标只需要一次延迟。

坑 2:电源系统复杂度翻倍

两块芯片、两路供电、上电时序、掉电保护。船在海上颠簸,多一个连接点多一个故障点。

坑 3:我一个人开发,维护两套代码库

STM32 写 C、树莓派写 Python/C++。调试时两个终端来回切。每次改通信协议两边都要改。

坑 4:成本

无人船不是无人机,船体+推进器+电池已经很贵了。再加树莓派,整机成本上一个台阶。

2.2 纯 Linux 方案 — 实时性是个硬伤

全志 T113 跑 Linux + RT-Preempt 补丁,理论上能到百微秒级实时响应。但:

  • 中断延迟不确定:Linux 内核可能在任意时刻关中断,推进器的 PWM 输出必须 50Hz 精准,Linux 做不到硬件定时器的抖动水平。
  • 启动时间:Linux 从加电到飞控就绪要 5-10 秒,单片机 200ms 就起来了。船下水前通电等着很正常,但万一海上重启,10 秒够船飘出多远?
  • 没有成熟生态:Pixhawk/CUAV 的硬件生态围绕 STM32 建了十年。用 Linux 意味着传感器驱动全得自己写。

2.3 纯单片机方案 — 刚好够用的上限

STM32H743 作为 Cortex-M7 旗舰,关键参数:

CPU:     480 MHz (双发射, ~2 DMIPS/MHz)
RAM:     996 KB SRAM (DTCM 128K + AXI 512K + SRAM1/2/3/4)
Flash:     2 MB
FPU:      双精度硬浮点
DSP:      SIMD 指令集
DMAMUX:   16通道灵活路由
OS:       FreeRTOS (抢占式, 1000Hz tick)

480MHz 做路径规划够不够?

实测数据(我们的项目):

操作                       周期/秒      CPU%
─────────────────────────────────────────
EKF 导航融合                 550K      0.1%
PID + 混控 (50Hz)           200K      0.04%
D* Lite 全局规划 (1Hz)      100K      0.02%
DWA 局部规划 (10Hz, 400轨迹) 4,920K    0.89%
LiDAR Bresenham 射线 (M10)  1,100K    0.2%
雷达栅格融合 (33Hz)          825K      0.15%
8路串口 + 网络协议栈         1,500K    0.27%
FreeRTOS 内核                600K      0.11%
─────────────────────────────────────────
稳态合计                   ~9,800K     ~1.8%

只有 1.8%! 远没到 H743 的极限。

有人会说"480MHz 才用 1.8% 是因为你们算法太简单"。不是的——是因为架构上做了正确的选择

  1. 代价地图放在 DTCM。DTCM 是 CPU 的紧耦合内存,零等待周期。DWA 每秒查 12 万次栅格,如果放普通 SRAM 上,每次访问多等 3-5 个周期,CPU 占用直接翻倍。

  2. sin/cos 用查表不用 FPU。DWA 每条轨迹 30 步,每步算一次 sin 和 cos。400 条轨迹 × 30 步 × 2 = 24000 次三角函数。如果调 sinf(),每次 ~28 周期(硬件 FPU)。我们预计算了 360 条目的查表,查一次 ~5 周期。省了 5 倍。

  3. D Lite 是增量更新*。障碍物变了不用全量重搜 6400 个节点,只更新受影响的 25 个(5×5 邻域)。1Hz 重规划几乎不占 CPU。

这些优化在 Linux 代码里很难做——因为 Linux 的设计哲学是"通用",不是"极致"。


3. 真正重要的不是 CPU 频率

选 STM32H7 系列最关键的理由,其实是这三个:

3.1 内存架构

Cortex-M7 内核
     │
     ├── ITCM (64KB, 零等待)  ← CPU 直连, 跑中断服务最快
     ├── DTCM (128KB, 零等待) ← CPU 直连, 放高频数据
     │
     └── AXI 总线矩阵 ──┬── AXI SRAM (512KB)  ← DMA 缓冲区
                        ├── SRAM1 (128KB)
                        ├── SRAM2 (128KB)
                        ├── SRAM3  (32KB)
                        └── SRAM4  (64KB)

H7 有两个零等待内存(ITCM 和 DTCM),CPU 访问它们没有任何等待周期。代价地图、EKF 状态、任务栈——全部放 DTCM。DWA 每秒查 12 万次栅格,全部零等待。

ESP32 也有 520KB SRAM,但分区分得碎,而且没有 CPU 紧耦合内存。查栅格的延迟是 H743 的 3-5 倍。

3.2 DMAMUX — 8 路串口并发不打架

这是 H7 系列独有的杀手特性。在 F4 上,每个串口绑定固定的 DMA 流,串口多了必然冲突。H7 有 DMAMUX——16 个通道可以灵活路由任意外设到任意 DMA 流:

// H7: 8个串口各自独占一个 DMA 通道, 互不干扰
[COM1] = { DMA1_Stream0, DMAREQ_ID_USART1_RX,  41 },
[COM2] = { DMA1_Stream1, DMAREQ_ID_USART2_RX,  43 },
[COM3] = { DMA1_Stream2, DMAREQ_ID_USART3_RX,  45 },
[COM4] = { DMA1_Stream3, DMAREQ_ID_UART4_RX,   63 },
[COM5] = { DMA1_Stream4, DMAREQ_ID_UART5_RX,   65 },
[COM6] = { DMA1_Stream5, DMAREQ_ID_USART6_RX,  71 },
[COM7] = { DMA1_Stream6, DMAREQ_ID_UART7_RX,   79 },
[COM8] = { DMA1_Stream7, DMAREQ_ID_UART8_RX,   81 },

无人船 8 路串口同时收发——GPS (COM3, 10Hz)、雷达 (COM4, 33Hz)、LiDAR (COM6, 460800bps/240Hz帧率)、遥控器 (COM2, 50Hz)、4G 模块 (LPUART1)——没有 DMAMUX 根本扛不住。

3.3 生态

ST 的 HAL 库虽然被骂"臃肿",但对于一个人开发来说,它是救命稻草。不用手写寄存器,不用对着 3000 页参考手册查位定义。配一个 I2C 三行代码:

HAL_I2C_Init(&hi2c4);
HAL_I2C_Master_Transmit(&hi2c4, addr, data, len, timeout);

加上 Keil MDK 的调试体验——J-Link 接上就能看变量、寄存器、调用栈。一个人开发,快速定位 bug 比省几 KB 代码重要得多。


4. 为什么从 H723 升级到 H743?

我们一开始用的是 H723ZG,后来换了 H743ZITx。不是因为 CPU 不够快(都是 480MHz 级别),而是因为内存

资源 H723ZG H743ZITx
AXI SRAM 128 KB 512 KB
Flash 1 MB 2 MB
FDCAN 3 路 2 路
UART 10 路 8 路

128KB AXI SRAM 放完 DMA 缓冲 + FreeRTOS Heap + 通用数据,空间非常紧。H743 的 512KB AXI 让所有内存焦虑都消除了。外设少了(H723 多了 FDCAN3、I2C5、UART9/10),但我们用不到那些多余的。

芯片迁移花了一天。 因为我们项目里所有硬件定义集中在 cc_h7_def.h 一个文件里——换芯片只改这一个文件和 .sct 分散加载脚本。核心控制代码一行没动。


5. 选型总结

场景                             推荐方案
──────────────────────────────────────────
做产品、有团队、跑视觉             Linux 伴机 (CM4+STM32)
做科研、要发论文、不管功耗          NVIDIA Jetson
做开源、一个人、先跑稳再跑快        STM32H743 ← 我们
做低端玩具船、几块钱的成本          ESP32

我们的选择不是"最好"的,是"在当前约束下最优解"。

一个人开发 → 不要搞分布式系统(伴机方案两块芯片通信是额外的复杂度)。
先跑稳再跑快 → 不要过早优化硬件(H743 还有 98% CPU 闲置)。
要开源 → 选最大的生态系统(ST 的社区和文档是国产芯片比不了的)。


6. 本章代码:看看我们怎么初始化芯片

// main.c — 精简版
int main(void)
{
    bsp_init();              // ① 时钟 480MHz + GPIO + DMA + 外设
    path_planner_init(...);  // ② 代价地图 + D* Lite + DWA 初始化
    radar_task_init();       // ③ MR72 雷达采集任务
    lidar_task_init();       // ④ M10 LiDAR 采集任务
    // ... 创建其他 15+ 个 FreeRTOS 任务 ...
    vTaskStartScheduler();   // ⑤ 启动调度器 (永不返回)
}
// cc_h7_def.h — 换芯片只改这里
// H743 AXI SRAM: 512KB
// DTCM: 128KB
// 引脚定义全部集中在这里
#define CAN1_RX_PORT          GPIOD
#define LIDAR_UART_PORT       COM6
#define RADAR_UART_PORT       COM4

7. 踩过的坑

坑 1:H723 和 H743 的外设不一样

H723 有 3 路 FDCAN、5 路 I2C、10 路 UART。H743 只有 2 路 FDCAN、4 路 I2C、8 路 UART。我们把 H723 的代码直接拿到 H743 编译,31 个错误——全是 FDCAN3I2C5UART9USART10 不存在。

教训:换芯片前先对比数据手册的外设章节,别想当然"都是 H7 应该一样"。

坑 2:代价地图放 AXI SRAM 上 DWA 慢 3.6 倍

一开始我们把代价地图放在 AXI SRAM(因为空间大)。DWA 规划一帧从 ~0.5ms 变成 ~1.8ms——虽然看起来不多,但 DWA 跑 10Hz,每个周期只有 100ms 时间窗,突然多占了 1.3ms 会让其他任务被挤压。

换成 DTCM 后恢复正常。H7 的 DTCM 是最宝贵的资源,DWA 必须用。

坑 3:LiDAR 天气检测死锁

我们的 M10 LiDAR 每帧只有 42 个点。但传感器融合代码里有个 LIDAR_TYPICAL_MIN = 100——意思是"至少 100 个有效点才算建立了天气基线"。结果 M10 永远达不到 100,天气自适应功能彻底死锁。

改成 30 就好了。这种硬编码常量是给之前设计的 RPLIDAR S2E(2000+ 点/帧)用的,换 LiDAR 型号必须同步更新。


8. 下一章预告

第二章:从 main.c 到 FreeRTOS — 嵌入式实时系统启动全流程

bsp_init 里到底初始化了什么?15 个 FreeRTOS 任务怎么创建、怎么分配优先级、怎么避免互相踩踏?看门狗怎么监视所有任务的心跳?


本文是《从零搭建无人船控制系统》系列第一篇。项目地址:[煜坤 YuKun](开源准备中)。
作者:一个人、一台电脑、一条船。慢就是快,走稳每一步。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐