一、背景:嵌入式开发不只是点灯和调包

在很多人的印象里,嵌入式开发要么是“点灯、串口、SPI、I2C”,要么是“拿开发板跑 Demo”,再复杂一点就是 RTOS 多任务、通信协议和底层驱动。

但在医疗器械、生命科学仪器和实验室自动化设备中,嵌入式系统面对的往往不是一个简单外设,而是一整套复杂的机电控制系统。

一台典型的 IVD 仪器或科学仪器中,可能同时包含:

  • 多个步进电机、柱塞泵、注射泵、隔膜泵;

  • 多路电磁阀、旋转阀、切换阀;

  • 压力、液位、温度、位置等传感器;

  • 光学检测、温控、运动轴、通信接口;

  • 上位机流程控制、参数配置和日志分析系统。

在这类系统中,嵌入式工程师真正面对的问题,已经不是“怎么把某个外设驱动起来”,而是:

如何把复杂硬件、动作流程、状态管理、异常处理和数据分析组织成一个长期可维护、可复用、可扩展的控制平台。

这也是趋盛科技在医疗自动化和科学仪器控制领域持续探索的方向:将传统高度依赖项目定制的嵌入式控制系统,逐步抽象为可配置、可编排、可分析的平台化控制系统。


二、传统开发模式的痛点:每个项目都从底层重新来一遍

以一台典型的医疗自动化设备为例,设备内部通常包含以下模块:

  • 加样针运动控制;

  • 注射泵定量吸排液;

  • 多通阀、电磁阀液路切换;

  • 温控模块;

  • 光学检测模块;

  • 压力、液位、位置等传感器;

  • 上位机通信;

  • 故障检测与恢复机制。

传统项目开发中,通常会按功能模块拆分代码:

motor_driver.c
valve_driver.c
pump_driver.c
sensor_driver.c
protocol.c
workflow.c
error_handler.c

这种模式在单个项目中是可行的,但随着产品数量增加,问题会逐渐暴露出来。

1. 驱动复用困难

不同项目使用的泵、阀、电机型号不同,即使控制逻辑相似,也往往需要重新写一套适配代码。

例如:

  • 某项目使用 A 厂家的柱塞泵;

  • 另一个项目换成 B 厂家的注射泵;

  • 第三个项目需要同时控制多个泵和多个阀;

  • 后续产品又增加了压力反馈、液位检测和异常保护。

底层驱动、通信协议、参数配置、初始化逻辑都可能发生变化。结果就是每个项目都要重新开发、重新调试、重新验证。

2. 业务流程和底层控制耦合严重

在医疗自动化设备中,真正复杂的往往不是单个电机转多少步,而是完整流程。

例如一次液路动作可能包含:

打开进液阀 → 注射泵吸液 → 关闭进液阀 → 打开出液阀 → 注射泵排液 → 等待压力稳定 → 触发检测

如果这些动作全部写死在 C 代码中,后期修改流程就必须改固件、重新编译、重新烧录,甚至重新做测试验证。

对于研发阶段来说,这会极大降低调试效率。
对于产品迭代来说,也会增加维护成本和质量风险。

3. 异常处理越来越复杂

医疗设备和科学仪器不能只考虑“正常流程能跑通”,还必须处理大量异常情况:

  • 电机堵转;

  • 阀未打开或响应延迟;

  • 压力异常;

  • 液路堵塞;

  • 气泡干扰;

  • 传感器数据漂移;

  • 上位机通信中断;

  • 运动过程被人为暂停或复位。

如果没有统一的状态管理和异常处理机制,项目越做越大,代码就越容易变成“补丁堆叠”。


三、平台化思路:把硬件抽象成“虚拟组件”

趋盛的核心思路不是把每个项目都写成一个完全独立的固件,而是先建立一个通用控制平台。

这个平台把复杂硬件抽象成一类类标准化的“虚拟组件”:

  • 电机组件;

  • 注射泵组件;

  • 柱塞泵组件;

  • 电磁阀组件;

  • 旋转阀组件;

  • 温控组件;

  • 压力传感器组件;

  • 光学检测组件;

  • 通信组件;

  • 流程组件。

每个组件背后仍然对应真实的嵌入式驱动,但对上层应用来说,它们不再是散乱的底层接口,而是具有统一参数、统一状态和统一动作模型的可配置模块。

例如,一个注射泵组件可以抽象为:

typedef struct {
    uint8_t id;
    uint32_t max_position;
    uint32_t current_position;
    uint32_t speed;
    uint8_t status;
    uint8_t error_code;
} PumpComponent;

typedef enum {
    PUMP_IDLE,
    PUMP_BUSY,
    PUMP_ERROR,
    PUMP_TIMEOUT
} PumpStatus;

上层不需要关心底层到底是步进电机、丝杆机构,还是某种串口协议控制的泵,只需要调用统一动作接口:

pump_move_to(pump_id, target_position, speed);
pump_aspirate(pump_id, volume);
pump_dispense(pump_id, volume);
pump_stop(pump_id);
pump_get_status(pump_id);

这样做的好处是:

底层硬件可以变化,但上层流程逻辑尽量不变。

为了让控制系统从“单项目定制开发”走向“可配置、可复用、可扩展”的平台化架构,可以将系统抽象为上位机编排层、平台软件层、控制运行时层、驱动组件层和硬件执行层。

图1 平台化控制系统总体架构图

图1展示了平台化控制系统的总体关系:
上位机侧完成设备流程编排、参数配置、运行监控和日志分析;TrendView、TrendCompile、TrendOptimize 分别承担流程编排、板卡/驱动配置和运行数据优化;控制板卡侧负责任务调度、状态机管理、通信管理和异常处理;底层则连接步进电机、注射泵、电磁阀、传感器、光学模块和温控模块等执行单元。


四、从“写代码”到“配置系统”

在传统模式中,嵌入式开发者需要手动完成:

  1. 选择 MCU;

  2. 配置 IO;

  3. 编写驱动;

  4. 配置通信;

  5. 编写业务流程;

  6. 编写状态机;

  7. 调试异常处理;

  8. 对接上位机。

趋盛平台将其中大量重复性工作做成配置化流程。

1. 硬件配置层

通过板卡配置工具,开发者可以定义:

  • 哪个端口连接哪个电机;

  • 哪个 IO 控制哪个阀;

  • 哪个 ADC 采集哪个传感器;

  • 哪个串口连接哪个模块;

  • 每个执行器的默认参数;

  • 每个传感器的采样周期和报警阈值。

配置完成后,系统可以生成对应的硬件映射和基础固件配置。

这一步的价值在于:

硬件资源不再散落在代码中,而是形成清晰、可视化、可复用的工程配置。

2. 驱动适配层

对于泵、阀、电机、传感器等常见器件,平台沉淀成熟驱动库。

不同项目只需要选择对应组件类型和参数,而不必每次从零开始写驱动。

例如:

组件类型:注射泵
控制方式:步进电机
行程范围:0-60000 step
默认速度:3000 step/s
加速度:8000 step/s²
零位检测:光电开关
异常检测:超时 + 堵转 + 压力异常

这些参数可以通过配置文件或上位机工具下发,而不是写死在代码中。

3. 流程编排层

医疗自动化设备中大量动作是流程型逻辑。
例如一次加样动作可能包含:

针移动到样本位
下降到吸样高度
打开吸液阀
注射泵吸样
关闭吸液阀
针移动到反应杯
打开排液阀
注射泵排样
清洗针外壁
复位

如果每一个动作都由嵌入式工程师写死在代码中,调试和维护都会很困难。

平台化系统采用图形化流程编排思路,将底层动作封装成标准指令节点,上层可以通过可视化方式组合流程。

例如:

MoveAxis(X, 12000)
MoveAxis(Z, 3500)
OpenValve(V1)
PumpAspirate(P1, 50uL)
CloseValve(V1)
MoveAxis(X, 30000)
OpenValve(V2)
PumpDispense(P1, 50uL)
CloseValve(V2)

这让嵌入式工程从“写死业务逻辑”,逐渐转向“提供可靠、可复用的动作能力”。


五、关键技术实现:多执行器协同调度

在真实设备中,多个动作并不是简单串行执行。为了提高效率,很多任务需要并行或半并行执行。

例如:

  • X 轴移动时,注射泵可以提前准备;

  • 温控模块可以在液路动作期间持续稳定;

  • 光学检测可以等待液体稳定后自动触发;

  • 清洗流程可以和下一个样本准备流程交叠。

这就需要一个多执行器调度机制。

1. 任务模型

可以把每个动作抽象成一个任务:

typedef struct {
    uint16_t task_id;
    uint8_t component_id;
    uint8_t command;
    uint32_t param1;
    uint32_t param2;
    uint32_t timeout_ms;
    uint8_t dependency_count;
    uint16_t dependencies[8];
    uint8_t status;
} ControlTask;

每个任务包含:

  • 执行对象;

  • 动作命令;

  • 参数;

  • 超时时间;

  • 依赖关系;

  • 当前状态。

2. 依赖调度

例如:

Task 1:X轴移动到样本位
Task 2:Z轴下降
Task 3:打开阀V1
Task 4:注射泵吸液
Task 5:关闭阀V1

其中 Task 2 依赖 Task 1,Task 4 依赖 Task 3。

但温控任务、压力采集任务、通信任务可以并行运行。

调度器需要根据依赖关系判断哪些任务可以立即执行,哪些任务必须等待。

3. 状态机设计

每个任务可以采用统一状态机:

READY → RUNNING → DONE
              ↘ ERROR
              ↘ TIMEOUT
              ↘ ABORTED

这样做可以避免不同驱动各自处理状态,导致系统行为不一致。

4. 阀动作延迟与压力反馈控制伪代码

在液路控制中,阀动作不是瞬时完成的。
如果阀还没有完全打开,注射泵就开始吸液或排液,可能会造成瞬时阻力异常、压力波动,甚至导致加样量偏差。

因此,系统中需要把阀延迟、泵动作、压力稳定判断和异常处理放入统一状态机中。

下面是一段简化后的伪代码示例:

typedef enum {
    STEP_OPEN_INLET_VALVE,
    STEP_WAIT_VALVE_READY,
    STEP_PUMP_ASPIRATE,
    STEP_CLOSE_INLET_VALVE,
    STEP_OPEN_OUTLET_VALVE,
    STEP_PUMP_DISPENSE,
    STEP_WAIT_PRESSURE_STABLE,
    STEP_TRIGGER_DETECT,
    STEP_ERROR
} ProcessStep;

typedef struct {
    ProcessStep step;
    uint32_t timestamp;
    uint32_t valve_delay_ms;
    uint32_t stable_time_ms;
    uint32_t timeout_ms;
    uint32_t aspirate_volume_ul;
    uint32_t dispense_volume_ul;
    uint32_t aspirate_speed;
    uint32_t dispense_speed;
    float pressure_threshold;
} ProcessContext;

void process_run(ProcessContext *ctx)
{
    switch (ctx->step) {
    case STEP_OPEN_INLET_VALVE:
        valve_open(V1);
        ctx->timestamp = millis();
        ctx->step = STEP_WAIT_VALVE_READY;
        break;

    case STEP_WAIT_VALVE_READY:
        if (millis() - ctx->timestamp >= ctx->valve_delay_ms) {
            pump_aspirate(P1,
                          ctx->aspirate_volume_ul,
                          ctx->aspirate_speed);
            ctx->step = STEP_PUMP_ASPIRATE;
        }
        break;

    case STEP_PUMP_ASPIRATE:
        if (pump_is_done(P1)) {
            valve_close(V1);
            valve_open(V4);
            ctx->timestamp = millis();
            ctx->step = STEP_OPEN_OUTLET_VALVE;
        }
        break;

    case STEP_OPEN_OUTLET_VALVE:
        if (millis() - ctx->timestamp >= ctx->valve_delay_ms) {
            pump_dispense(P1,
                          ctx->dispense_volume_ul,
                          ctx->dispense_speed);
            ctx->step = STEP_PUMP_DISPENSE;
        }
        break;

    case STEP_PUMP_DISPENSE:
        if (pump_is_done(P1)) {
            ctx->timestamp = millis();
            ctx->step = STEP_WAIT_PRESSURE_STABLE;
        }
        break;

    case STEP_WAIT_PRESSURE_STABLE:
        if (pressure_is_stable(ctx->pressure_threshold,
                               ctx->stable_time_ms)) {
            trigger_detect();
            ctx->step = STEP_TRIGGER_DETECT;
        } else if (millis() - ctx->timestamp > ctx->timeout_ms) {
            ctx->step = STEP_ERROR;
        }
        break;

    case STEP_TRIGGER_DETECT:
        log_info("Detection triggered.");
        break;

    case STEP_ERROR:
        emergency_stop();
        valve_close_all();
        log_error("Pressure not stable or valve timeout.");
        break;
    }
}

这段伪代码体现了几个关键点:

  • 阀打开后增加延迟等待;

  • 泵吸液和排液由状态机控制;

  • 压力稳定后才触发检测;

  • 超时或压力异常时进入安全处理;

  • 所有动作均可记录日志,便于后续分析。

5. 压力反馈闭环微调

在部分高精度液体输送场景中,仅靠步数或速度参数并不能完全保证输送稳定性。
当液体黏度、管路阻力、阀响应时间发生变化时,可以引入压力反馈,对泵速度进行微调。

简化逻辑如下:

float pressure_feedback_adjust(float target_pressure,
                               float current_pressure,
                               float base_speed)
{
    float error = target_pressure - current_pressure;
    float adjust = 0.8f * error;

    float new_speed = base_speed + adjust;

    if (new_speed > MAX_PUMP_SPEED) {
        new_speed = MAX_PUMP_SPEED;
    }

    if (new_speed < MIN_PUMP_SPEED) {
        new_speed = MIN_PUMP_SPEED;
    }

    return new_speed;
}

这类反馈控制可以用于:

  • 检测液路堵塞;

  • 判断阀响应异常;

  • 识别气泡或液路阻力变化;

  • 在允许范围内微调泵速;

  • 提高排液稳定性和重复性。


六、工程实践:以液路控制为例

在 IVD 和生命科学仪器中,液路控制是非常典型的复杂嵌入式场景。

一个常见液路单元可能包含:

  • 一个注射泵;

  • 多个电磁阀;

  • 一个压力传感器;

  • 多个液路通道;

  • 一个反应池或检测池;

  • 清洗液和废液通路。

一次完整动作可能是:

打开进液阀
注射泵后退吸液
关闭进液阀
打开出液阀
注射泵前进排液
关闭出液阀
等待压力稳定
触发检测

表面看只是几个动作,但真正工程化时会遇到很多问题。

图2 液路控制单元示意图

图2展示了一个典型液路控制单元。
样本从样本瓶经过进液阀进入注射泵,再通过压力传感器进入检测池或反应池,最终经排液阀进入废液瓶。清洗液可以通过独立阀路进入主流路,实现清洗、排空和检测前稳压。

这类液路单元虽然结构并不复杂,但对控制系统提出了较高要求:

1. 阀响应延迟

电磁阀并不是指令发出后立即完成动作。
如果泵动作过早启动,就可能出现阀尚未完全打开、液体阻力异常、压力波动等问题。

因此系统需要支持动作间延迟:

OpenValve(V1)
Delay(20ms)
PumpAspirate(P1, 50uL)

这个延迟不应该散落在业务代码里,而应该作为组件参数或流程节点参数进行统一管理。

2. 压力反馈

在高精度液体输送中,仅依靠步数或时间并不一定能保证液体稳定输送。
压力传感器可以用于判断:

  • 是否堵塞;

  • 是否有气泡;

  • 阀是否打开异常;

  • 液路是否存在阻力变化;

  • 排液过程是否稳定。

平台可以将压力反馈作为流程判断条件:

如果压力超过阈值 → 停止泵 → 关闭阀 → 上报异常
如果压力稳定在目标范围 → 继续下一步检测

3. 参数可调

不同液体黏度、管路长度、泵规格、阀响应特性都会影响动作参数。
如果参数都写死在固件里,现场调试会非常低效。

平台化系统可以把关键参数配置化:

  • 吸液速度;

  • 排液速度;

  • 阀动作延迟;

  • 压力阈值;

  • 稳定等待时间;

  • 超时时间;

  • 回零速度;

  • 异常重试次数。

这些参数可以在上位机或配置工具中调整,减少反复修改固件的成本。

4. 调试数据闭环

平台化控制系统不仅要能执行流程,还要能分析流程。

例如在一次排液过程中,系统可以同时记录:

  • 阀开启时间;

  • 泵启动时间;

  • 泵停止时间;

  • 压力变化曲线;

  • 压力稳定时间;

  • 异常报警点;

  • 当前参数版本;

  • 当前流程节点。

图3 运行调试与优化分析界面示意

图3展示了一个典型的运行调试与优化分析思路。
左侧是流程执行日志,中间是压力/时序曲线,右侧是状态机和关键参数。通过日志、压力曲线和状态机联合分析,可以快速判断问题来源:

  • 如果阀开启后压力迟迟不上升,可能是阀响应延迟或流路未导通;

  • 如果泵启动后压力瞬间过高,可能是液路堵塞或阀未完全打开;

  • 如果压力曲线波动过大,可能是气泡、管路弹性或速度参数不匹配;

  • 如果压力稳定时间过长,可能需要优化泵速、阀延时或稳压等待时间。

这种分析方式使调试从“凭经验猜测”转向“基于日志和传感器数据定位问题”。


七、平台化带来的价值

1. 对嵌入式工程师的价值

平台化不是削弱嵌入式工程师的价值,而是让工程师从重复劳动中解放出来。

过去大量时间花在:

  • 重复写驱动;

  • 重复调 IO;

  • 重复改流程;

  • 重复处理通信协议;

  • 重复定位状态异常。

平台化之后,工程师可以更多关注:

  • 驱动稳定性;

  • 调度机制;

  • 实时性;

  • 安全机制;

  • 异常恢复;

  • 系统架构;

  • 工程复用。

这才是嵌入式全栈能力真正的体现。

2. 对产品研发的价值

对于医疗器械和科学仪器企业来说,平台化控制系统可以显著提升研发效率:

  • 新产品开发周期缩短;

  • 旧项目经验可以复用;

  • 不同产品之间硬件和软件架构统一;

  • 现场调试更方便;

  • 后续维护成本降低;

  • 质量体系和验证过程更清晰。

尤其对于小型研发团队和初创企业而言,平台化控制系统可以帮助其把有限资源集中在核心检测原理、试剂体系、算法和临床应用上,而不是长期陷入底层控制系统反复开发。

3. 对行业的价值

医疗自动化设备正在从单一功能设备,走向模块化、平台化和多项目联检。

这意味着嵌入式系统也必须升级:

从单板控制 → 多模块协同
从固定流程 → 可配置流程
从手写驱动 → 驱动库复用
从项目定制 → 平台化开发
从经验调试 → 数据化优化

这不仅是软件架构变化,也是嵌入式开发模式的变化。


八、趋盛的实践方向:Trend 系列平台

围绕上述思路,趋盛科技构建了面向医疗自动化和科学仪器的 Trend 系列平台,主要包括 TrendCompile、TrendView 和 TrendOptimize。

1. TrendCompile:板卡与驱动配置

TrendCompile 负责完成硬件资源、端口、驱动和器件参数的配置。

它解决的是:

这套设备有哪些硬件?每个硬件连接在哪里?应该用什么驱动和参数?

通过配置化方式,把硬件连接关系、驱动参数、执行器类型统一管理,减少大量重复固件适配工作。

2. TrendView:流程可视化编排

TrendView 负责把底层执行器动作组织成完整工艺流程。

它解决的是:

设备要按什么顺序执行动作?哪些动作可以并行?哪些动作需要等待条件?

对于注射泵、阀、运动轴、传感器、检测模块等组件,可以通过图形化方式进行流程编排,便于研发调试和方案验证。

3. TrendOptimize:运行分析与优化

TrendOptimize 面向运行数据分析。

它解决的是:

系统执行过程中是否稳定?动作是否存在延迟?压力、位置、时间参数是否需要优化?

通过采集运行日志、传感器数据和执行指令,可以帮助工程师分析流程瓶颈和异常原因。


九、一个典型开发流程

基于平台化模式,一个新设备的控制系统开发流程可以变成:

1. 确认设备功能和执行器清单
2. 选择控制板卡和扩展模块
3. 配置电机、泵、阀、传感器等组件
4. 生成基础固件和通信接口
5. 在可视化工具中编排动作流程
6. 下载流程并联机调试
7. 采集运行数据
8. 根据压力、时序、位置等数据优化参数
9. 固化稳定版本
10. 进入产品验证和量产导入

与传统模式相比,这种流程的核心变化是:

嵌入式工程不再只交付一份固件,而是交付一套可持续演进的控制平台。


十、思考:嵌入式全栈的未来不只是“更底层”,也是“更系统”

很多人理解嵌入式高手,往往想到:

  • 会写驱动;

  • 会看寄存器;

  • 会调 RTOS;

  • 会分析总线波形;

  • 会优化中断和 DMA;

  • 会处理内存和实时性问题。

这些能力当然重要。

但在复杂行业设备中,真正的全栈嵌入式能力,还包括:

  • 对硬件系统的抽象能力;

  • 对业务流程的建模能力;

  • 对异常状态的管理能力;

  • 对跨项目复用的架构能力;

  • 对上位机、固件、硬件和工艺流程的协同能力;

  • 对产品长期维护和迭代的工程判断能力。

医疗自动化设备、科学仪器、工业装备、汽车电子等领域,都会越来越需要这种系统级嵌入式能力。

未来的嵌入式开发,不会只是“Ctrl+C 调包”,也不会只是“拿电烙铁焊板子”。

它会越来越像一套完整的工程系统:

硬件可配置
驱动可复用
流程可编排
状态可追踪
数据可分析
异常可恢复
平台可演进

这也是趋盛科技希望推动的方向:

让复杂自动化设备的控制系统开发,从一次次重复造轮子,走向模块化、平台化和可持续复用。


结语

嵌入式开发的硬核,不只体现在某一段底层代码写得多漂亮,也体现在能否把复杂系统长期稳定地跑起来。

对于医疗器械和科学仪器这样的行业场景来说,每一个泵、每一个阀、每一个传感器、每一个状态机,最终都服务于一个更大的目标:

让设备更稳定,让研发更高效,让产品更快落地。

从写驱动,到搭系统;
从项目开发,到平台复用;
从单点控制,到全栈架构。

这或许正是嵌入式开发者下一阶段真正值得证明的硬实力。

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐