复杂生物实验设备:从系统架构到嵌入式程序全流程
一、先建立正确的系统思维
1.1 面对的不是"一块 PCB",而是一个"系统"
生物实验设备(示例:自动液体处理工作站)
功能分解:
┌─────────────────────────────────────────────────────────┐
│ ① XYZ 三轴运动(步进/伺服电机 × 3) │
│ ② 移液头(微量泵 / 注射泵,精度 0.1μL) │
│ ③ 样品抓取(夹爪 / 真空吸头) │
│ ④ 温控模块(PCR 加热块 4~99°C,精度 ±0.1°C) │
│ ⑤ 制冷模块(帕尔贴 TEC,4°C 保存) │
│ ⑥ 液位检测(电容/超声/压力) │
│ ⑦ 条码/RFID 识别(样品管标识) │
│ ⑧ 视觉定位(相机,对位) │
│ ⑨ 人机界面(触摸屏 / 上位机 PC) │
│ ⑩ 通信(USB / Ethernet / WiFi / RS485) │
│ ⑪ 安全(急停、门锁、漏液检测) │
│ ⑫ 电源管理(24V / 12V / 5V / 3.3V / ±15V) │
└─────────────────────────────────────────────────────────┘
如果全部塞在一块 PCB 上:
· 板子尺寸 400mm × 300mm+
· 信号类型混杂(电机驱动大电流 + μV 级传感器 + 高速数字)
· 一个模块故障 → 整块板报废
· 调试地狱(示波器探头都伸不进去)
· 迭代成本极高(改一个电阻 → 重新打样整块大板)
→ 必须模块化!
1.2 核心设计哲学
复杂系统 = 主控板(大脑)+ 功能子板(器官)+ 背板/线缆(血管/神经)
┌─────────────────────────────────────────────────────────────┐
│ │
│ 不是"一块大 PCB" │
│ 不是"每个功能一块独立 PCB + 飞线" │
│ 而是"分层分级的模块化 PCB 系统" │
│ │
│ Level 0: 系统(整机) │
│ Level 1: 子系统(运动 / 流体 / 温控 / 感知 / 人机) │
│ Level 2: 功能模块 PCB(电机驱动板 / 温控板 / 传感板) │
│ Level 3: 器件(芯片 / 接插件 / 无源元件) │
│ │
└─────────────────────────────────────────────────────────────┘
二、系统架构设计(硬件拓扑)
2.1 总体拓扑图
┌─────────────────────┐
│ 上位机 PC / 云端 │
│ (实验方案/数据管理) │
└──────────┬──────────┘
│ Ethernet / USB / WiFi
▼
┌──────────────────────────────────────────────────────────────────┐
│ 主控板(Mother Board) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 主 MCU │ │ 以太网 │ │ USB │ │ SD卡 │ │
│ │ STM32H7 │ │ PHY芯片 │ │ Host/Dev │ │ 数据记录 │ │
│ │ /i.MX RT │ │ │ │ │ │ │ │
│ └────┬─────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │
│ ┌────┴─────────────────────────────────────────────────┐ │
│ │ 总线 / 接口 │ │
│ │ · SPI × 2(高速传感器/Flash) │ │
│ │ · I²C × 2(低速传感器/EEPROM) │ │
│ │ · UART × 4(子板通信/调试) │ │
│ │ · CAN × 2(电机驱动/分布式I/O) │ │
│ │ · PWM × 6(电机/加热/泵) │ │
│ │ · ADC × 8(温度/压力/液位) │ │
│ │ · GPIO × 20(限位/急停/指示灯) │ │
│ │ · 24V 电源输入 + DC-DC(5V/3.3V) │ │
│ └────┬──────┬──────┬──────┬──────┬──────┬─────────────┘ │
│ │ │ │ │ │ │ │
└────────┼──────┼──────┼──────┼──────┼──────┼────────────────────┘
│ │ │ │ │ │
┌────▼──┐┌──▼───┐┌─▼──┐┌─▼──┐┌──▼──┐┌──▼───┐
│电机 ││电机 ││温控││流体││传感 ││人机 │
│驱动板 ││驱动板││板 ││控制││采集 ││接口板│
│(X轴) ││(Y/Z) ││ ││板 ││板 ││ │
└───────┘└──────┘└────┘└────┘└─────┘└──────┘
子板A 子板B 子板C 子板D 子板E 子板F
每块子板 = 一块独立 PCB(4~6层,50~100mm 见方)
通过 排线/板对板连接器/CAN总线 与主控板连接
2.2 为什么必须分板?
| 维度 |
单板设计 |
模块化分板 |
| 尺寸 |
400×300mm+,打样贵、贴片难 |
每块 ≤100×80mm,标准工艺 |
| 信号完整性 |
电机 PWM 干扰 ADC,数字干扰模拟 |
物理隔离,干扰最小化 |
| 散热 |
热源集中,热管理困难 |
热源分散,各自散热 |
| 调试 |
一个故障查整块板 |
换板排查,分钟级定位 |
| 迭代 |
改一个电路 → 重新打样整板(¥2000+) |
只改子板(¥200) |
| 维护 |
现场维修 = 换整板 |
换子板,5 分钟 |
| 复用 |
无 |
电机驱动板可用于其他项目 |
| 并行开发 |
一人画完整板 |
多人同时画不同子板 |
| 成本 |
大板良率低,报废贵 |
小板良率高,报废便宜 |
2.3 分板原则
分板决策树:
这个功能是否需要物理隔离?
│
├─ 是(大电流/高压/高温/敏感模拟)→ 必须独立子板
│ 例:电机驱动(3A+)、TEC 驱动(10A+)、加热(100W+)
│
├─ 信号类型是否与其他模块冲突?
│ ├─ 是(μV 模拟 vs 数字 PWM)→ 独立子板
│ │ 例:温度采集(热电偶 μV)vs 步进电机 PWM
│ └─ 否 → 可以合并
│
├─ 是否需要独立散热?
│ ├─ 是 → 独立子板(加散热片/风扇)
│ └─ 否 → 可合并
│
├─ 是否需要独立更换/维护?
│ ├─ 是 → 独立子板
│ └─ 否 → 可合并
│
└─ 接口是否标准化?
├─ 是(CAN / RS485 / SPI)→ 独立子板,总线连接
└─ 否(紧耦合,如 MCU 与 DDR)→ 同一板
2.4 本例的分板方案
┌─────────────────────────────────────────────────────────────┐
│ 板卡清单(BOM of Boards) │
│ │
│ ① 主控板(Main Control Board) │
│ · MCU(STM32H743 / i.MX RT1170) │
│ · 以太网 PHY(LAN8720 / RTL8201) │
│ · USB Host/Device │
│ · SD 卡槽 │
│ · QSPI Flash(固件存储) │
│ · 电源入口(24V → 5V → 3.3V) │
│ · 总线接口(CAN×2, UART×4, SPI×2, I²C×2) │
│ · 板对板连接器 / 排针(连子板) │
│ · 尺寸:100×80mm,4 层 │
│ │
│ ② 电机驱动板 × 2(Motor Driver Board) │
│ · 步进驱动(TMC2209 / DRV8825)× 2~3 轴 │
│ · 或伺服驱动(CAN 总线伺服) │
│ · 限位开关输入 │
│ · 编码器输入 │
│ · CAN / UART 接口(接收主控指令) │
│ · 尺寸:80×60mm,4 层 │
│ │
│ ③ 温控板(Temperature Control Board) │
│ · 加热驱动(MOSFET + PID,100W) │
│ · TEC 驱动(H 桥,帕尔贴制冷/加热) │
│ · 温度采集(PT100 / NTC / 热电偶,24bit ADC) │
│ · 过温保护(硬件比较器,独立于 MCU) │
│ · 尺寸:80×60mm,4 层 │
│ │
│ ④ 流体控制板(Fluid Control Board) │
│ · 注射泵驱动(步进电机 + 丝杆) │
│ · 电磁阀驱动(12V/24V,多路) │
│ · 压力传感器(0~100kPa,检测堵塞/液位) │
│ · 气泡检测(光学) │
│ · 尺寸:80×60mm,4 层 │
│ │
│ ⑤ 传感器采集板(Sensor Acquisition Board) │
│ · 多路 ADC(16~24bit,Σ-Δ) │
│ · 液位检测(电容式) │
│ · 条码/RFID 模块接口 │
│ · 环境传感器(温湿度) │
│ · 模拟前端(仪表放大器、滤波) │
│ · 尺寸:60×50mm,4 层(模拟/数字分区) │
│ │
│ ⑥ 人机接口板(HMI Interface Board) │
│ · 触摸屏接口(RGB / LVDS / MIPI) │
│ · 或:UART 连独立 HMI 屏(如迪文/淘晶驰) │
│ · 蜂鸣器、LED 指示灯 │
│ · 按键/旋钮 │
│ · 尺寸:按屏幕定,2~4 层 │
│ │
│ ⑦ 电源分配板(Power Distribution Board)(可选) │
│ · 24V 输入 → 各路分配 │
│ · 保险丝 / 断路器 │
│ · EMI 滤波 │
│ · 急停继电器 │
│ · 尺寸:100×40mm,2 层 │
│ │
│ 连接方式: │
│ · 主控 ↔ 电机驱动:CAN 总线(4 线:H/L/GND/+5V) │
│ · 主控 ↔ 温控:UART + PWM(或 CAN) │
│ · 主控 ↔ 流体:UART + GPIO │
│ · 主控 ↔ 传感:SPI / I²C │
│ · 主控 ↔ HMI:UART / USB │
│ · 电源:24V 总线(粗线)→ 各子板本地 DC-DC │
└─────────────────────────────────────────────────────────────┘
三、主控 MCU 选型
3.1 选型决策
需求分析:
· 3 轴运动控制(PWM + 编码器,或 CAN 伺服)
· 多路 PID 温控(采样率 ≥ 10Hz,精度 0.1°C)
· 多路 ADC(温度/压力/液位)
· 通信(Ethernet + USB + CAN + UART + SPI + I²C)
· HMI(触摸屏驱动)
· 文件系统(SD 卡,实验数据记录)
· 实时性(电机控制 < 1ms,温控 < 100ms)
· 未来扩展(可能加视觉、加轴)
候选方案:
| MCU |
主频 |
内核 |
外设 |
适合度 |
价格 |
| STM32H743 |
480MHz |
Cortex-M7 |
全外设、双CAN、ETH、USB HS |
★★★★★ |
~40元 |
| STM32F407 |
168MHz |
Cortex-M4 |
够用,单CAN |
★★★★ |
~20元 |
| i.MX RT1170 |
1GHz |
Cortex-M7 + M4 |
极丰富、MIPI、GPU |
★★★★★(高端) |
~60元 |
| ESP32-S3 |
240MHz |
Xtensa 双核 |
WiFi/BLE、USB |
★★★(通信强,实时弱) |
~15元 |
| GD32H759 |
600MHz |
Cortex-M7 |
国产替代 STM32H7 |
★★★★ |
~30元 |
| Linux(i.MX6/8) |
1GHz+ |
Cortex-A |
全功能 |
★★★★(非实时) |
~80元+ |
3.2 推荐方案
方案 A(主流,推荐):
主控:STM32H743VIT6
· 480MHz Cortex-M7,单精度 FPU + 双精度 FPU
· 1MB RAM,2MB Flash
· 2× CAN-FD,2× USB(HS+FS),1× Ethernet MAC
· 3× SPI,4× UART,4× I²C
· 3× ADC(16bit,3.6MSPS)
· 多路 PWM(高级定时器,互补输出,死区)
· 足够跑 FreeRTOS + 所有外设 + 文件系统 + TCP/IP
方案 B(高端,需要屏幕/视觉):
主控:i.MX RT1170(Cortex-M7 1GHz + Cortex-M4 400MHz)
· M7 跑应用逻辑
· M4 跑电机控制(硬实时)
· 内置 GPU + MIPI-DSI(直接驱触摸屏)
· 适合带 7 寸屏 + 视觉的复杂设备
方案 C(极致实时,多轴伺服):
主控:STM32H7 + FPGA(或专用运动控制芯片)
· MCU 做逻辑/通信/HMI
· FPGA 做多轴伺服环(μs 级)
· 适合 6 轴以上、μs 级同步
3.3 主控板原理图框图
┌─────────────────────────────────────────────────────────────────┐
│ 主控板原理图框图(STM32H743) │
│ │
│ 电源入口 │
│ 24V ──→ [TVS] ──→ [保险丝] ──→ [DC-DC 5V/3A] ──→ 5V 总线 │
│ │ │
│ [LDO 3.3V/1A] ──→ 3.3V(MCU/数字)│
│ [LDO 1.8V] ──→ 1.8V(DDR/PHY) │
│ [LDO 1.2V] ──→ 1.2V(MCU 内核) │
│ │
│ MCU 最小系统 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ STM32H743VIT6 │ │
│ │ │ │
│ │ VDD × 5 ──→ 3.3V(每对 VDD/VSS 放 100nF) │ │
│ │ VDDA ──→ 3.3V(模拟电源,独立 LDO + LC 滤波) │ │
│ │ VCAP × 3 ──→ 10μF(内核稳压输出) │ │
│ │ BOOT0 ──→ 10K 下拉(正常从 Flash 启动) │ │
│ │ NRST ──→ 100nF + 10K 上拉 + 复位按钮 │ │
│ │ HSE ──→ 25MHz 晶振 + 2×20pF │ │
│ │ LSE ──→ 32.768KHz 晶振(RTC) │ │
│ │ VBAT ──→ CR2032 纽扣电池(RTC 保持) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 通信外设 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ ETH PHY │ │ USB HS │ │ CAN FD │ │ QSPI │ │
│ │ LAN8720 │ │ USB3300 │ │ TJA1043 │ │ Flash │ │
│ │ RMII │ │ ULPI │ │ ×2 路 │ │ W25Q256 │ │
│ │ + 网络变 │ │ + ESD │ │ + 共模 │ │ 32MB │ │
│ │ 压器 │ │ │ │ 电感 │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ 模拟前端 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ ADC 输入(3 路 × 16bit) │ │
│ │ · 温度(NTC/PT100 → 分压/桥路 → 运放 → ADC) │ │
│ │ · 压力(0~5V 传感器 → RC 滤波 → ADC) │ │
│ │ · 电流(采样电阻 → 差分放大 → ADC) │ │
│ │ · 每路:TVS + RC 滤波 + 运放缓冲 + ESD │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ 对外接口(连子板) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ · CAN 接口 × 2(4pin 排针:CANH/CANL/GND/5V) │ │
│ │ · UART 接口 × 4(6pin:TX/RX/RTS/CTS/GND/3.3V) │ │
│ │ · SPI 接口 × 2(8pin:CLK/MOSI/MISO/CS×2/GND/3V3)│ │
│ │ · I²C 接口 × 2(4pin:SCL/SDA/GND/3.3V) │ │
│ │ · PWM 输出 × 6(排针) │ │
│ │ · GPIO × 20(限位/急停/指示) │ │
│ │ · 24V 电源输出(给子板供电) │ │
│ │ · 5V 电源输出(给子板逻辑供电) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ 调试接口 │
│ · SWD(4pin:SWDIO/SWCLK/GND/3.3V) │
│ · UART 调试口(TX/RX/GND) │
│ · BOOT 跳线(ISP 模式) │
│ · 状态 LED × 4(电源/运行/通信/错误) │
│ · 复位按钮 │
└─────────────────────────────────────────────────────────────────┘
四、PCB 层数、叠层与布局
4.1 层数选择
| 板卡 |
层数 |
理由 |
| 主控板 |
4 层(推荐)或 6 层 |
MCU + 以太网 + USB + 多路模拟,需要完整地平面 |
| 电机驱动板 |
4 层 |
大电流走线 + PWM 信号,需要地平面隔离 |
| 温控板 |
4 层 |
功率 MOSFET + 精密模拟(ADC),需隔离 |
| 流体控制板 |
4 层 |
电磁阀驱动(感性负载)+ 压力传感 |
| 传感器采集板 |
4 层(或 6 层) |
精密模拟,需要完整地平面和屏蔽 |
| HMI 接口板 |
2~4 层 |
简单数字信号 |
| 电源分配板 |
2 层 |
纯功率分配,无高速信号 |
4.2 四层板叠层设计
标准 4 层叠层(1.6mm 板厚):
Layer 1(TOP) ── 信号层(元件面)
─────────────────────────────────────
Prepreg(7628) ── ~0.2mm
─────────────────────────────────────
Layer 2(GND) ── 完整地平面 ← 关键!不要割裂!
─────────────────────────────────────
Core(FR4) ── ~1.0mm
─────────────────────────────────────
Layer 3(PWR) ── 电源层(3.3V / 5V / 24V 分区)
─────────────────────────────────────
Prepreg(7628) ── ~0.2mm
─────────────────────────────────────
Layer 4(BOTTOM) ── 信号层(底层元件/走线)
铜厚:
· 信号层(L1/L4):1 oz(35μm)
· 电源/地层(L2/L3):1 oz(35μm)
· 大电流板(电机驱动):2 oz(70μm)或更厚
阻抗控制(如果需要):
· 单端 50Ω:线宽 ~0.2mm(4mil),介质 0.2mm
· 差分 90Ω(USB):线宽 0.15mm,间距 0.15mm
· 差分 100Ω(Ethernet):线宽 0.12mm,间距 0.15mm
4.3 六层板叠层(主控板,如果信号复杂)
6 层叠层(1.6mm):
L1(TOP) ── 信号(高速:USB/ETH/QSPI)
L2(GND1) ── 完整地平面
L3(SIG1) ── 信号(MCU 引脚扇出)
L4(PWR) ── 电源层(3.3V/1.8V/1.2V 分区)
L5(GND2) ── 完整地平面
L6(BOTTOM) ── 信号(低速:UART/GPIO/连接器)
优势:
· 高速信号(L1)紧邻地平面(L2)→ 回流路径短
· MCU 扇出(L3)两侧都有参考平面
· 电源层(L4)与地(L5)形成平板电容 → 去耦
4.4 布局原则(以主控板为例)
┌─────────────────────────────────────────────────────────────┐
│ 主控板布局(俯视图,100×80mm) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 电源区(左上角) │ │
│ │ · 24V 输入端子 │ │
│ │ · DC-DC(5V)+ 电感 + 电容 │ │
│ │ · LDO(3.3V/1.8V/1.2V) │ │
│ │ · 远离模拟区! │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MCU 区(中心) │ │
│ │ · STM32H743(LQFP100 / TFBGA100) │ │
│ │ · 去耦电容紧贴每个 VDD 引脚 │ │
│ │ · 晶振紧贴 HSE 引脚(< 10mm) │ │
│ │ · 复位电路靠近 NRST │ │
│ │ · BOOT 跳线 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ 高速通信区(右侧) │ │ 模拟区(右下角) │ │
│ │ · 以太网 PHY+变压器 │ │ · ADC 输入 │ │
│ │ · USB(PHY+ESD) │ │ · 运放/仪表放大 │ │
│ │ · QSPI Flash │ │ · 滤波 RC │ │
│ │ · 差分对等长走线 │ │ · 独立地平面区域 │ │
│ │ · 远离电源和电机驱动│ │ · 远离数字信号 │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 接口区(板边缘) │ │
│ │ · CAN 排针 × 2 │ │
│ │ · UART 排针 × 4 │ │
│ │ · SPI/I²C 排针 │ │
│ │ · SWD 调试口 │ │
│ │ · USB 口、网口(如果板载) │ │
│ │ · 所有连接器靠边,方便插拔 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 布局铁律: │
│ ① 电源 → MCU → 外设 → 接口(信号流向) │
│ ② 模拟区与数字区物理分隔(≥ 20mm 或地平面隔离) │
│ ③ 高速信号(USB/ETH)靠近 MCU,走线短 │
│ ④ 去耦电容紧贴 IC 电源引脚(< 2mm) │
│ ⑤ 晶振下方不走其他信号(挖空内层) │
│ ⑥ 大电流路径(电源)走线宽(≥ 1mm/A) │
│ ⑦ 连接器靠板边,丝印标注清晰 │
└─────────────────────────────────────────────────────────────┘
4.5 电机驱动板布局要点
┌─────────────────────────────────────────────────────────────┐
│ 电机驱动板布局(80×60mm,4层,2oz 铜) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 功率区(左半) │ │
│ │ · 24V 输入(大端子/大过孔阵列) │ │
│ │ · MOSFET / 驱动 IC(TMC2209 / DRV8825) │ │
│ │ · 续流二极管 │ │
│ │ · 大电解电容(100~470μF,储能) │ │
│ │ · 采样电阻(电流检测) │ │
│ │ · 铜皮加宽(≥ 2mm/A),多过孔散热 │ │
│ │ · 散热焊盘 → 大面积铜皮 → 散热片 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 信号区(右半) │ │
│ │ · MCU(如果板载独立控制)或 CAN 收发器 │ │
│ │ · 编码器接口(差分输入) │ │
│ │ · 限位开关输入(光耦隔离) │ │
│ │ · CAN / UART 接口 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 关键: │
│ · 功率地与信号地:单点连接(星形接地) │
│ · PWM 走线远离模拟信号 │
│ · 电机输出线(大电流)走顶层/底层宽铜皮 │
│ · 内层完整地平面(不要割裂!) │
└─────────────────────────────────────────────────────────────┘
4.6 温控板布局要点(精密模拟 + 大功率)
· 加热 MOSFET(100W)→ 板子一端,大面积散热铜皮
· TEC H 桥(10A)→ 另一端
· 温度采集(PT100 / 热电偶)→ 板子中间,远离热源
· 24bit ADC(ADS1256)→ 紧邻传感器输入
· 模拟地平面完整,数字地单点连接
· 加热 PWM 走线 ≠ 温度信号走线(物理距离 ≥ 15mm)
· 传感器输入走差分线,包地处理
五、原理图模块化设计方法
5.1 层次化原理图(Hierarchical Schematic)
在 Altium / KiCad / 立创EDA 中:
顶层(Top Sheet):
┌─────────────────────────────────────────────────────────┐
│ Project: BioLab_Workstation │
│ Sheet: Top_Level.SchDoc │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ [MCU ]│ │ [Power ]│ │ [ETH ]│ │
│ │ _Core ]│ │ _Supply ]│ │ _PHY ]│ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ [USB ]│ │ [CAN ]│ │ [ADC ]│ │
│ │ _Interface]│ │ _Transceiver]│ │ _FrontEnd]│ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ [PWM ]│ │ [GPIO ]│ │ [Debug ]│ │
│ │ _Output ]│ │ _Connectors]│ │ _Interface]│ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ 每个方框 = 一个子原理图(Sheet Symbol) │
│ 方框的引脚 = 跨模块信号(Net Label / Port) │
└─────────────────────────────────────────────────────────┘
子原理图(Sub Sheet):
MCU_Core.SchDoc:
· STM32H743 + 去耦 + 晶振 + 复位 + BOOT
· 引脚通过 Port 引出(PA0 → Port "ADC_CH0")
Power_Supply.SchDoc:
· 24V → DC-DC → LDO
· 电源输出通过 Port 引出(Port "VCC_3V3")
ADC_FrontEnd.SchDoc:
· 运放 + 滤波 + 保护
· 输入通过 Port 引入(Port "ADC_CH0")
· 输出连 MCU 的 ADC 引脚
5.2 模块化设计的好处
① 并行设计:
· 你画 MCU 核心 + 通信
· 搭档画电源 + 驱动
· 另一人画模拟前端
· 最后合并顶层
② 复用:
· "MCU_Core" 模块可用于下一个项目(换 MCU 型号即可)
· "Power_Supply" 模块通用(改参数)
· "CAN_Transceiver" 模块直接复制
③ 可读性:
· 每张子图 < 50 个元件 → 一屏看完
· 顶层图 = 系统框图 → 一目了然
④ 错误隔离:
· 一个模块改错 → 只影响该模块
· DRC/ERC 分模块检查 → 定位快
⑤ 评审方便:
· 电源工程师只看 Power_Supply
· 硬件负责人看 Top_Level
5.3 跨模块连接方式
| 方式 |
工具 |
适用 |
| Net Label(网络标签) |
Altium / KiCad |
同名标签自动连接(如 "VCC_3V3") |
| Port(端口) |
Altium Sheet Symbol |
层次化,方向明确(Input/Output/Bidirectional) |
| Power Port |
Altium / KiCad |
电源网络(GND、VCC)全局连接 |
| Harness(线束) |
Altium |
一组信号打包(如 SPI:CLK+MOSI+MISO+CS) |
| Off-sheet Connector |
KiCad |
跨页连接 |
5.4 原理图设计规范
每张原理图必须包含:
✓ 标题栏(项目名、板名、版本、日期、作者)
✓ 修订记录(Rev A → B → C,改了什么)
✓ 页码(Page 1 of 8)
✓ 去耦电容(每个 IC 的每个 VDD 引脚)
✓ 未用引脚处理(输出悬空,输入接上/下拉)
✓ 测试点(关键信号留 TP)
✓ 丝印标注(接口定义、极性、电压)
命名规范:
· 电阻:R1, R2...(按模块分区:R101~R199 = 电源区)
· 电容:C1, C2...
· IC:U1, U2...
· 连接器:J1, J2...(或 P1, P2)
· 网络名:VCC_3V3, GND, CAN1_H, SPI1_CLK, ADC_TEMP
· 不用 "NET1" "N$1" 这种自动命名!
六、子板与主控板的连接设计
6.1 连接方式选择
| 方式 |
适用 |
优点 |
缺点 |
| 排线(IDC/杜邦) |
低速信号(UART/GPIO) |
便宜、灵活、易更换 |
不可靠、易松脱 |
| 板对板连接器 |
子板叠在主控板上 |
紧凑、可靠 |
占高度、对位精度 |
| DB 连接器 / 航空插头 |
子板远离主控(机箱内) |
牢固、屏蔽好 |
体积大、贵 |
| CAN 总线(4线) |
电机驱动、分布式 I/O |
抗干扰、多节点、远距 |
需要协议栈 |
| RS485(4线) |
传感器、温控 |
简单、远距 |
半双工 |
| SPI/I²C 排线 |
近距离子板 |
简单 |
距离短(<30cm) |
| 以太网(RJ45) |
子板 = 独立控制器 |
标准、高速 |
成本高 |
6.2 本例连接方案
主控板 ←→ 电机驱动板A(X轴):CAN 总线(4pin 航空插头)
主控板 ←→ 电机驱动板B(Y/Z轴):CAN 总线(4pin 航空插头)
主控板 ←→ 温控板:UART + PWM(6pin 排线,<30cm)
主控板 ←→ 流体控制板:UART + GPIO(10pin 排线)
主控板 ←→ 传感器采集板:SPI(8pin 排线,<20cm)
主控板 ←→ HMI 板:UART / USB(4pin / USB 线)
电源分配板 → 所有子板:24V + GND(粗线,端子排)
CAN 总线拓扑:
主控板 ──┬── 电机驱动板A(Node ID=1)
├── 电机驱动板B(Node ID=2)
└── 温控板(Node ID=3,可选)
· 两端各 120Ω 终端电阻
· 屏蔽双绞线
· 共地
6.3 连接器选型
| 信号类型 |
推荐连接器 |
型号示例 |
| CAN / RS485 |
4pin 航空插头 / JST XH |
M12-4pin / B4B-XH-A |
| UART / GPIO |
2.54mm 排针 + 杜邦 / JST PH |
— |
| SPI / I²C |
2.0mm 排针(防反插) |
JST SH |
| 电源(24V/5A+) |
5.08mm 接线端子 / 航空插头 |
KF301 / M12-5pin |
| 板对板 |
0.5mm FPC / 1.27mm 排针 |
— |
| USB |
Type-C / Micro-B |
— |
| 以太网 |
RJ45(带变压器) |
HR911105A |
七、嵌入式程序设计:从零到一
7.1 软件架构总览
┌─────────────────────────────────────────────────────────────┐
│ 嵌入式软件分层架构(STM32H7 + FreeRTOS) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Layer 5: 应用层(Application) │ │
│ │ · 实验流程状态机(吸液→移动→排液→清洗→...) │ │
│ │ · 温控 PID 算法 │ │
│ │ · 运动规划(梯形/S曲线加减速) │ │
│ │ · 用户交互(HMI 协议解析) │ │
│ │ · 数据记录(SD 卡文件系统) │ │
│ │ · 安全监控(急停/超时/异常) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↕ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Layer 4: 中间件(Middleware) │ │
│ │ · FreeRTOS(任务调度、队列、信号量、定时器) │ │
│ │ · FatFs(文件系统) │ │
│ │ · LwIP / FreeRTOS+TCP(以太网协议栈) │ │
│ │ · USB Stack(Host/Device) │ │
│ │ · CANopen / 自定义 CAN 协议 │ │
│ │ · Modbus RTU(RS485 通信) │ │
│ │ · 日志系统(printf → UART / 文件) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↕ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Layer 3: 设备驱动(Device Drivers) │ │
│ │ · motor_drv.c(步进/伺服,PWM+编码器) │ │
│ │ · temp_drv.c(ADC 采集 + 标定) │ │
│ │ · heater_drv.c(PWM 加热 + MOSFET) │ │
│ │ · tec_drv.c(H 桥驱动) │ │
│ │ · pump_drv.c(注射泵步进控制) │ │
│ │ · valve_drv.c(电磁阀 GPIO) │ │
│ │ · sensor_drv.c(压力/液位/气泡) │ │
│ │ · can_drv.c(CAN 收发) │ │
│ │ · uart_drv.c(UART DMA 收发) │ │
│ │ · spi_drv.c(SPI 外设) │ │
│ │ · i2c_drv.c(I²C 外设) │ │
│ │ · eth_drv.c(以太网 MAC+PHY) │ │
│ │ · usb_drv.c(USB) │ │
│ │ · sdio_drv.c(SD 卡) │ │
│ │ · gpio_drv.c(限位/急停/LED) │ │
│ │ · wdt_drv.c(看门狗) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↕ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Layer 2: 硬件抽象层(HAL / BSP) │ │
│ │ · STM32 HAL 库(ST 官方) │ │
│ │ · 或 LL 库(轻量,直接寄存器) │ │
│ │ · BSP(Board Support Package): │ │
│ │ bsp.c / bsp.h(板级初始化:时钟/引脚/外设) │ │
│ │ · system_stm32h7xx.c(时钟树配置) │ │
│ │ · startup_stm32h743.s(启动文件) │ │
│ │ · linker.ld(链接脚本:Flash/RAM 分区) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↕ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Layer 1: 硬件(Hardware) │ │
│ │ · STM32H743 芯片 │ │
│ │ · 外部晶振、Flash、PHY、传感器... │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
7.2 从零到一:开发流程
Phase 0: 环境搭建(Day 1)
─────────────────────────────────
· 安装 IDE:STM32CubeIDE(免费,基于 Eclipse)
或 Keil MDK / IAR(付费)
或 VS Code + PlatformIO / CMake + arm-none-eabi-gcc
· 安装 STM32CubeMX(图形化配置工具)
· 准备硬件:主控板 + ST-Link 调试器 + 串口线
· 新建工程 → 点亮 LED → 串口打印 "Hello"
· 确认:编译 → 下载 → 运行 → 调试 全链路通
Phase 1: BSP 板级支持包(Day 2-3)
─────────────────────────────────
· 时钟树配置(CubeMX):
HSE 25MHz → PLL → 480MHz(CPU)
→ 240MHz(AHB)
→ 120MHz(APB1/APB2)
· GPIO 初始化(所有引脚模式/速度/上下拉)
· UART 初始化(调试口 115200,DMA 接收)
· 看门狗初始化(IWDG,2s 超时)
· SysTick 配置(1ms 节拍)
· 验证:LED 闪烁 + 串口回显
Phase 2: 外设驱动逐个打通(Day 4-10)
─────────────────────────────────
按优先级:
① GPIO(限位开关、急停、LED)→ 中断测试
② UART(DMA + 空闲中断,收发帧)→ 与 HMI 通信
③ ADC(多通道 DMA 采集)→ 读温度/压力
④ PWM(高级定时器,互补+死区)→ 驱动加热/MOSFET
⑤ SPI(DMA)→ 读外部 ADC / Flash
⑥ I²C → 读 EEPROM / 传感器
⑦ CAN(FDCAN)→ 与电机驱动板通信
⑧ USB(Device CDC)→ 虚拟串口调试
⑨ Ethernet(LwIP)→ TCP 通信
⑩ SDIO + FatFs → SD 卡读写
每个驱动:
· 写 xxx_drv.h(接口定义)
· 写 xxx_drv.c(实现)
· 写 test_xxx.c(单元测试)
· 在板子上验证通过 → 下一个
Phase 3: RTOS 集成(Day 11-13)
─────────────────────────────────
· CubeMX 中启用 FreeRTOS
· 创建任务:
- Task_Motor(优先级 5,周期 1ms):运动控制
- Task_Temp(优先级 4,周期 100ms):温控 PID
- Task_Comm(优先级 3):通信处理(CAN/UART/ETH)
- Task_HMI(优先级 2):人机交互
- Task_Safety(优先级 6,最高):安全监控
- Task_Logger(优先级 1,最低):日志/数据记录
· 任务间通信:
- 队列(Queue):HMI 命令 → 应用层
- 信号量(Semaphore):ADC 采集完成通知
- 事件组(EventGroup):多条件同步
- 互斥锁(Mutex):共享资源保护(SD 卡、UART)
· 验证:多任务并行运行,无死锁,CPU 负载 < 70%
Phase 4: 应用层开发(Day 14-30)
─────────────────────────────────
· 运动控制模块:
- 梯形加减速算法
- 多轴插补(直线/圆弧)
- 回零(Home)流程
- 软限位 + 硬限位
· 温控模块:
- PID 算法(增量式/位置式)
- 自整定(继电器法)
- 过温保护(硬件 + 软件双重)
· 流体控制模块:
- 注射泵控制(体积 → 步数换算)
- 吸液/排液/清洗流程
- 气泡检测 + 堵塞检测
· 实验流程状态机:
- 解析实验方案(JSON / 自定义协议)
- 步进执行(Step → Step → ...)
- 异常处理 + 恢复
· HMI 协议:
- 定义通信协议(帧头+长度+命令+数据+CRC)
- 解析上位机/触摸屏指令
- 上报状态/数据/报警
Phase 5: 联调与优化(Day 31-45)
─────────────────────────────────
· 全系统联调(所有子板 + 主控 + 机械)
· 时序优化(节拍分析)
· 压力测试(连续运行 24h)
· 异常注入测试(拔线、断电、急停、超温)
· EMC 预测试(如有条件)
· 固件版本管理(Git)
Phase 6: 量产准备(Day 46-60)
─────────────────────────────────
· Bootloader(OTA 升级)
· 生产测试固件(一键自检)
· 参数校准流程(温度/压力/位置)
· 序列号 / 配置参数写入 Flash
· 文档(通信协议、接口定义、校准手册)
7.3 代码结构(工程目录)
BioLab_Workstation/
│
├── Core/
│ ├── Inc/
│ │ ├── main.h
│ │ ├── bsp.h ← 板级支持
│ │ ├── FreeRTOSConfig.h
│ │ └── app_config.h ← 全局配置(引脚定义、参数)
│ └── Src/
│ ├── main.c ← 初始化 + 启动 RTOS
│ ├── bsp.c ← 时钟/GPIO/外设初始化
│ ├── system_stm32h7xx.c
│ └── stm32h7xx_it.c ← 中断服务
│
├── Drivers/
│ ├── CMSIS/ ← ARM 核心定义
│ ├── STM32H7xx_HAL_Driver/ ← ST HAL 库
│ └── BSP/ ← 板级驱动
│ ├── motor_drv.c / .h
│ ├── temp_drv.c / .h
│ ├── heater_drv.c / .h
│ ├── tec_drv.c / .h
│ ├── pump_drv.c / .h
│ ├── valve_drv.c / .h
│ ├── sensor_drv.c / .h
│ ├── can_drv.c / .h
│ ├── uart_drv.c / .h
│ ├── spi_drv.c / .h
│ ├── i2c_drv.c / .h
│ ├── eth_drv.c / .h
│ ├── usb_drv.c / .h
│ ├── sdio_drv.c / .h
│ ├── gpio_drv.c / .h
│ └── wdt_drv.c / .h
│
├── Middleware/
│ ├── FreeRTOS/ ← RTOS 内核
│ ├── FatFs/ ← 文件系统
│ ├── LwIP/ ← TCP/IP 协议栈
│ ├── USB/ ← USB 协议栈
│ ├── CANopen/ ← CAN 应用层协议
│ └── Modbus/ ← Modbus RTU
│
├── Application/
│ ├── app_motion.c / .h ← 运动控制(加减速、插补)
│ ├── app_temp_ctrl.c / .h ← 温控 PID
│ ├── app_fluid.c / .h ← 流体控制(吸液/排液)
│ ├── app_experiment.c / .h ← 实验流程状态机
│ ├── app_hmi.c / .h ← HMI 协议解析
│ ├── app_safety.c / .h ← 安全监控
│ ├── app_logger.c / .h ← 数据记录
│ └── app_calibration.c / .h ← 校准
│
├── Protocol/
│ ├── protocol_hmi.c / .h ← HMI 通信协议
│ ├── protocol_can.c / .h ← CAN 应用协议
│ └── protocol_modbus.c / .h ← Modbus 寄存器映射
│
├── Utils/
│ ├── pid.c / .h ← PID 算法
│ ├── ramp.c / .h ← 加减速算法
│ ├── crc.c / .h ← CRC 校验
│ ├── ring_buffer.c / .h ← 环形缓冲区
│ ├── log.c / .h ← 日志系统
│ └── utils.c / .h ← 通用工具
│
├── Bootloader/ ← OTA 升级(独立工程)
│ ├── bootloader.c
│ └── ymodem.c
│
├── Test/
│ ├── test_motor.c
│ ├── test_temp.c
│ ├── test_adc.c
│ └── test_can.c
│
├── Docs/
│ ├── 通信协议文档.md
│ ├── 接口定义.md
│ ├── 校准手册.md
│ └── 变更日志.md
│
├── STM32H743VITx_FLASH.ld ← 链接脚本
├── Makefile / CMakeLists.txt ← 构建系统
├── .ioc ← CubeMX 工程文件
└── README.md
7.4 关键代码示例
main.c(启动流程)
#include "main.h"
#include "bsp.h"
#include "FreeRTOS.h"
#include "task.h"
#include "app_motion.h"
#include "app_temp_ctrl.h"
#include "app_fluid.h"
#include "app_hmi.h"
#include "app_safety.h"
#include "app_logger.h"
/* 任务句柄 */
TaskHandle_t hTask_Motor, hTask_Temp, hTask_Comm;
TaskHandle_t hTask_HMI, hTask_Safety, hTask_Logger;
int main(void)
{
/* 1. HAL 初始化(SysTick、NVIC 优先级分组) */
HAL_Init();
/* 2. 板级初始化(时钟树、GPIO、外设) */
BSP_Init();
/* 3. 驱动初始化 */
GPIO_DRV_Init();
UART_DRV_Init(UART_DEBUG, 115200);
ADC_DRV_Init();
PWM_DRV_Init();
CAN_DRV_Init(CAN1, 500000);
SPI_DRV_Init(SPI1);
I2C_DRV_Init(I2C1);
WDT_DRV_Init(2000); /* 2s 看门狗 */
/* 4. 中间件初始化 */
FatFs_Init();
LwIP_Init();
USB_Init();
/* 5. 应用初始化 */
App_Motion_Init();
App_TempCtrl_Init();
App_Fluid_Init();
App_HMI_Init();
App_Safety_Init();
/* 6. 创建 RTOS 任务 */
xTaskCreate(Task_Motor, "Motor", 512, NULL, 5, &hTask_Motor);
xTaskCreate(Task_Temp, "Temp", 256, NULL, 4, &hTask_Temp);
xTaskCreate(Task_Comm, "Comm", 512, NULL, 3, &hTask_Comm);
xTaskCreate(Task_HMI, "HMI", 512, NULL, 2, &hTask_HMI);
xTaskCreate(Task_Safety, "Safety", 256, NULL, 6, &hTask_Safety);
xTaskCreate(Task_Logger, "Logger", 512, NULL, 1, &hTask_Logger);
/* 7. 启动调度器(不会返回) */
vTaskStartScheduler();
while (1) {} /* 不应到达这里 */
}
温控 PID 任务
/* app_temp_ctrl.c */
#include "app_temp_ctrl.h"
#include "temp_drv.h"
#include "heater_drv.h"
#include "tec_drv.h"
#include "pid.h"
#include "FreeRTOS.h"
#include "task.h"
static PID_t pid_heater;
static float target_temp = 37.0f; /* 目标温度 °C */
static float current_temp = 25.0f;
void App_TempCtrl_Init(void)
{
PID_Init(&pid_heater,
2.5f, /* Kp */
0.08f, /* Ki */
0.5f, /* Kd */
100.0f, /* 输出上限(PWM 占空比 %) */
0.0f, /* 输出下限 */
0.1f); /* 采样周期 dt(秒)= 100ms */
/* 从 Flash/EEPROM 读取校准参数 */
Temp_DRV_LoadCalibration();
}
void Task_Temp(void *arg)
{
TickType_t lastWake = xTaskGetTickCount();
const TickType_t period = pdMS_TO_TICKS(100); /* 100ms 周期 */
for (;;)
{
/* 1. 采集温度(多路 ADC,取均值) */
current_temp = Temp_DRV_ReadAverage(TEMP_CH_BLOCK, 8);
/* 2. PID 计算 */
float output = PID_Calculate(&pid_heater, target_temp, current_temp);
/* 3. 输出驱动 */
if (output > 0)
{
Heater_DRV_SetPWM((uint16_t)(output * 10)); /* 0~1000 */
TEC_DRV_Off();
}
else
{
Heater_DRV_Off();
TEC_DRV_SetPWM((uint16_t)(-output * 10)); /* 制冷 */
}
/* 4. 过温保护(软件层,硬件层独立) */
if (current_temp > 105.0f)
{
Heater_DRV_Off();
TEC_DRV_Off();
App_Safety_ReportAlarm(ALARM_OVERTEMP, current_temp);
}
/* 5. 上报数据(给 HMI / 日志) */
App_HMI_UpdateTemp(current_temp, target_temp, output);
/* 6. 精确周期等待 */
vTaskDelayUntil(&lastWake, period);
}
}
运动控制(梯形加减速)
/* app_motion.c */
#include "app_motion.h"
#include "motor_drv.h"
#include "ramp.h"
typedef struct {
float pos; /* 当前位置(mm) */
float vel; /* 当前速度(mm/s) */
float target_pos; /* 目标位置 */
float max_vel; /* 最大速度 */
float accel; /* 加速度 */
float decel; /* 减速度 */
uint8_t axis; /* 轴号 */
uint8_t state; /* 状态:IDLE/ACCEL/CRUISE/DECEL/DONE */
} MotionAxis_t;
static MotionAxis_t axis[3]; /* X, Y, Z */
void App_Motion_MoveTo(uint8_t axis_id, float target_mm, float vel, float acc)
{
MotionAxis_t *a = &axis[axis_id];
a->target_pos = target_mm;
a->max_vel = vel;
a->accel = acc;
a->decel = acc;
a->state = MOTION_ACCEL;
}
/* 1ms 周期调用(在 Task_Motor 或定时器中断中) */
void App_Motion_Update_1ms(void)
{
for (int i = 0; i < 3; i++)
{
MotionAxis_t *a = &axis[i];
float dt = 0.001f;
float dist_to_target = a->target_pos - a->pos;
float decel_dist = (a->vel * a->vel) / (2.0f * a->decel);
switch (a->state)
{
case MOTION_IDLE:
break;
case MOTION_ACCEL:
a->vel += a->accel * dt;
if (a->vel >= a->max_vel)
{
a->vel = a->max_vel;
a->state = MOTION_CRUISE;
}
if (fabsf(dist_to_target) <= decel_dist)
a->state = MOTION_DECEL;
break;
case MOTION_CRUISE:
if (fabsf(dist_to_target) <= decel_dist)
a->state = MOTION_DECEL;
break;
case MOTION_DECEL:
a->vel -= a->decel * dt;
if (a->vel <= 0 || fabsf(dist_to_target) < 0.01f)
{
a->vel = 0;
a->pos = a->target_pos;
a->state = MOTION_DONE;
}
break;
case MOTION_DONE:
break;
}
/* 更新位置 */
a->pos += a->vel * dt * (dist_to_target >= 0 ? 1.0f : -1.0f);
/* 输出脉冲(速度 → 频率) */
uint32_t pulse_freq = (uint32_t)(fabsf(a->vel) * STEPS_PER_MM);
Motor_DRV_SetFrequency(a->axis, pulse_freq);
Motor_DRV_SetDirection(a->axis, dist_to_target >= 0 ? DIR_CW : DIR_CCW);
}
}
HMI 通信协议
/* protocol_hmi.c */
/*
帧格式:
┌──────┬──────┬──────┬──────────┬──────┬──────┐
│ 0xAA │ LEN │ CMD │ DATA[0~N]│ CRC16│ 0x55 │
│ 帧头 │ 长度 │ 命令 │ 数据 │ 校验 │ 帧尾 │
└──────┴──────┴──────┴──────────┴──────┴──────┘
CMD 定义:
0x01 = 读取状态
0x02 = 设置目标温度
0x03 = 启动实验
0x04 = 停止/急停
0x05 = 手动移动(轴+位置)
0x06 = 读取温度数据
0x07 = 报警确认
0x80 = 状态上报(设备→HMI)
0x81 = 温度上报
0x82 = 报警上报
*/
void Protocol_HMI_Parse(uint8_t *buf, uint16_t len)
{
if (buf[0] != 0xAA || buf[len-1] != 0x55) return; /* 帧头帧尾 */
if (CRC16_Check(buf, len) != 0) return; /* CRC 校验 */
uint8_t cmd = buf[2];
uint8_t *data = &buf[3];
switch (cmd)
{
case CMD_SET_TEMP:
{
float temp;
memcpy(&temp, data, 4);
if (temp >= 4.0f && temp <= 99.0f)
App_TempCtrl_SetTarget(temp);
break;
}
case CMD_START_EXPERIMENT:
App_Experiment_Start();
break;
case CMD_ESTOP:
App_Safety_EmergencyStop();
break;
case CMD_MOVE_AXIS:
{
uint8_t axis = data[0];
float pos;
memcpy(&pos, &data[1], 4);
App_Motion_MoveTo(axis, pos, 50.0f, 200.0f);
break;
}
}
}
安全监控任务
/* app_safety.c */
void Task_Safety(void *arg)
{
for (;;)
{
/* 1. 急停按钮(硬件中断 + 软件轮询双重) */
if (GPIO_DRV_Read(PIN_ESTOP) == 0) /* 常闭,断开=触发 */
{
EmergencyStop_All();
}
/* 2. 看门狗喂狗(所有任务正常才喂) */
if (AllTasks_Healthy())
{
WDT_DRV_Feed();
}
/* 3. 通信超时检测 */
if (HMI_CommTimeout() > 5000) /* 5s 无通信 */
{
App_Motion_StopAll();
Heater_DRV_Off();
}
/* 4. 温度超限(独立于温控任务) */
float t = Temp_DRV_ReadQuick(TEMP_CH_BLOCK);
if (t > 110.0f)
{
Heater_DRV_Off(); /* 直接关,不等 PID */
TEC_DRV_Off();
Alarm_Trigger(ALARM_CRITICAL_OVERTEMP);
}
/* 5. 漏液检测 */
if (GPIO_DRV_Read(PIN_LEAK_SENSOR) == 1)
{
App_Fluid_StopAll();
Alarm_Trigger(ALARM_LEAK);
}
vTaskDelay(pdMS_TO_TICKS(10)); /* 10ms 轮询 */
}
}
void EmergencyStop_All(void)
{
/* 立即执行,不等任务调度 */
Motor_DRV_DisableAll(); /* 关电机使能 */
Heater_DRV_Off(); /* 关加热 */
TEC_DRV_Off(); /* 关 TEC */
Pump_DRV_Stop(); /* 停泵 */
Valve_DRV_CloseAll(); /* 关阀 */
GPIO_DRV_Write(PIN_ALARM_OUT, 1); /* 报警输出 */
/* 通知所有任务进入安全状态 */
xEventGroupSetBits(hSafetyEvent, BIT_ESTOP);
}
7.5 调试方法
| 阶段 |
工具 |
方法 |
| 裸机调试 |
ST-Link + SWD |
断点、单步、寄存器查看 |
| 串口调试 |
UART + printf |
打印变量、状态、时间戳 |
| RTOS 调试 |
FreeRTOS + Trace(SEGGER SystemView) |
任务切换时序、CPU 负载 |
| 逻辑分析 |
逻辑分析仪(Saleae / DSLogic) |
SPI/I²C/CAN/UART 波形 |
| 模拟信号 |
示波器 |
PWM 波形、ADC 噪声、电源纹波 |
| 通信调试 |
CAN 分析仪(PCAN / CANable) |
CAN 报文抓包 |
| 网络调试 |
Wireshark + 网线 |
TCP/IP 抓包 |
| 长期监控 |
SD 卡日志 |
记录温度/位置/状态,事后分析 |
| 生产测试 |
专用测试固件 |
一键自检所有 I/O、外设 |
7.6 常见坑与对策
| 坑 |
表现 |
对策 |
| 栈溢出 |
随机崩溃、HardFault |
增大任务栈、用 uxTaskGetStackHighWaterMark() 监控 |
| 优先级反转 |
低优先级任务卡住高优先级 |
用互斥锁(Mutex)而非二值信号量 |
| 中断中调用 RTOS API |
崩溃 |
中断中只用 FromISR 后缀的 API |
| ADC 噪声大 |
温度跳动 ±2°C |
硬件:RC 滤波 + 屏蔽线;软件:多次采样 + 中值滤波 |
| CAN 通信丢包 |
电机偶尔不动 |
检查终端电阻、波特率匹配、总线负载 < 70% |
| Flash 写入磨损 |
运行数月后参数丢失 |
用 EEPROM / FRAM,或 Flash 磨损均衡 |
| 看门狗复位 |
设备偶尔重启 |
确保所有路径都喂狗,长操作分段喂狗 |
| 电源纹波 |
ADC 读数不稳、通信误码 |
电源去耦(100nF+10μF)、模拟/数字地分离 |
| 堆栈共享 |
多任务同时用 printf → 乱码 |
每个任务独立缓冲区,或用互斥锁保护 |
八、总结
┌─────────────────────────────────────────────────────────────┐
│ │
│ 复杂系统 PCB 设计的核心原则: │
│ │
│ ① 分而治之 │
│ 一块大板 → 主控板 + N 块功能子板 │
│ 每块板 < 100×80mm,4 层,独立可测可换 │
│ │
│ ② 一个大脑 │
│ 一颗强力 MCU(STM32H7 / i.MX RT)做总控 │
│ 子板可以是"哑设备"(只接收 PWM/CAN 指令) │
│ 也可以有自己的小 MCU(做本地闭环) │
│ │
│ ③ 总线连接 │
│ CAN(电机/分布式)+ UART(温控/流体) │
│ + SPI/I²C(传感器)+ Ethernet(上位机) │
│ │
│ ④ 原理图层次化 │
│ 顶层 = 系统框图 │
│ 子图 = 功能模块(< 50 个元件/张) │
│ 跨模块用 Port / Net Label │
│ │
│ ⑤ PCB 布局 │
│ 4 层起步(信号-地-电源-信号) │
│ 模拟/数字/功率 物理分区 │
│ 地平面完整不割裂 │
│ 高速信号紧邻参考平面 │
│ │
│ ⑥ 嵌入式软件 │
│ 分层:HAL → 驱动 → 中间件 → 应用 │
│ RTOS 多任务(运动/温控/通信/安全/日志) │
│ 模块化代码(每个功能一个 .c/.h) │
│ 先跑通外设 → 再加 RTOS → 再写应用 │
│ │
│ ⑦ 迭代思维 │
│ V0.1:主控板 + LED + 串口(验证最小系统) │
│ V0.2:+ 一路电机 + 一路温度(验证驱动) │
│ V0.3:+ 全部子板(验证系统集成) │
│ V1.0:+ 完整功能 + 外壳(产品化) │
│ │
│ 不要试图一次做对。 │
│ 先让它动起来,再让它动得好,最后让它动得久。 │
│ │
└─────────────────────────────────────────────────────────────┘