复杂生物实验设备:从系统架构到嵌入式程序全流程
一、先建立正确的系统思维
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:+ 完整功能 + 外壳(产品化) │
│ │
│ 不要试图一次做对。 │
│ 先让它动起来,再让它动得好,最后让它动得久。 │
│ │
└─────────────────────────────────────────────────────────────┘