1. 项目概述
ProtoCentral HealthyPi v4 是一款面向医疗级应用的开源生理参数监测平台,其核心定位并非消费级可穿戴设备,而是为临床研究、远程监护系统开发及嵌入式医疗设备原型验证提供高精度、可复现、全栈可控的硬件基础。与常见“健康手环”类方案存在本质差异:HealthyPi v4 采用符合 IEC 60601-1 医疗电气设备安全标准的模拟前端设计,所有信号链路(尤其是 ECG 通道)均通过医用级隔离、抗干扰和共模抑制优化,支持直接贴片电极采集原始生物电信号,而非仅输出预处理后的 HRV 数值。
该平台具备双重形态:既可作为 Raspberry Pi 的 HAT(Hardware Attached on Top)扩展板实现边缘计算与无线传输能力,亦可脱离树莓派以独立 STM32F405RG 微控制器为核心运行完整固件,通过 USB CDC 或 UART 输出结构化数据流。这种双模架构赋予开发者在资源约束(如电池供电的便携终端)与计算能力(如本地 AI 心律分析)之间灵活取舍的能力。
值得注意的是,“Arduino Library”这一命名易引发误解。实际上,HealthyPi v4 的官方库并非基于 Arduino AVR 架构(如 ATmega328P),而是专为 ARM Cortex-M4 内核的 STM32F4 系列 MCU 设计,底层依赖 STM32Cube HAL 库,并深度集成 FreeRTOS 实时操作系统。所谓“Arduino 兼容”,仅指其 API 风格借鉴了 Arduino 的begin()/read()/write()抽象范式,便于快速上手,但其内部实现完全遵循嵌入式实时系统工程规范——包括中断优先级分组配置、DMA 驱动的多通道同步采样、低功耗状态机管理及硬件看门狗协同机制。
2. 硬件架构与信号链解析
HealthyPi v4 的硬件设计围绕“高保真生物电信号采集”这一核心目标展开,其信号链严格遵循医用设备设计准则。下图为其关键模块拓扑关系:
[Ag/AgCl 电极] ↓ (差分输入,100 MΩ 输入阻抗) [AD8232 仪表放大器] → 增益 = 100 V/V,带宽 = 0.05–150 Hz ↓ (高通滤波:0.05 Hz 截止,消除基线漂移) [OP27 运算放大器] → 二阶有源低通滤波:150 Hz 截止,抑制肌电噪声 ↓ (精密整流 + RC 平滑) [ADS1292R 24-bit ΔΣ ADC] → 采样率 = 250 SPS,内置右腿驱动(RLD)和威尔逊中心参考(WCT) ↓ (SPI 接口,16-bit 命令帧) [STM32F405RG] ←→ [FreeRTOS 任务调度] ↓ (USB CDC / UART / BLE HCI) [Host PC / Mobile App]2.1 ECG 通道:从电极到数字域的完整路径
ECG 信号采集是 HealthyPi v4 最具技术挑战性的部分。其前端采用 AD8232 专用心电仪放大器,该芯片集成了以下关键功能:
- 可编程增益:通过外部电阻网络配置,HealthyPi v4 固定设置为 100 V/V,确保 ±5 mV 心电信号满量程输出;
- 三电极配置支持:RA(右臂)、LA(左臂)、LL(左腿)标准导联,其中 LL 同时作为 RLD 驱动反馈节点,有效降低共模电压(CMV)至 <10 mV;
- 威尔逊中心参考(WCT)生成:由 RA、LA、LL 三路信号经电阻网络平均生成,作为 ADC 参考基准,提升导联间一致性;
- 引脚脱落检测(Lead-Off Detection):利用内部恒流源(±1.5 µA)注入电极,通过监测 ADC 输入端电压偏移判断电极接触状态,该功能在
HealthyPi.h中通过setLeadOffDetection(true)启用。
ADC 选用 TI ADS1292R,这是一款专为生物电设计的 24 位 ΔΣ 转换器,其关键特性包括:
| 参数 | 值 | 工程意义 |
|---|---|---|
| 内置 PGA 增益 | 1/2/3/4/6/12 | HealthyPi v4 固定使用 G=6,匹配 AD8232 输出动态范围 |
| 数据速率 | 125/250/500/1000 SPS | 默认 250 SPS,满足 Nyquist 定理对 100 Hz ECG 带宽的要求 |
| 内置 RLD 放大器 | Yes | 与 AD8232 RLD 引脚级联,形成闭环共模抑制系统 |
| 导联断开检测电流源 | ±1.5 µA | 与 AD8232 协同工作,实现无额外硬件的电极状态监控 |
ECG 数据流在 STM32 端通过 SPI DMA 双缓冲机制获取,避免 CPU 占用率过高。典型初始化代码如下:
// 初始化 ADS1292R(HAL 底层驱动) ADS1292R_InitTypeDef ads_cfg = {0}; ads_cfg.SpiHandle = &hspi1; // 使用 SPI1 ads_cfg.CS_GPIO_Port = GPIOA; ads_cfg.CS_Pin = GPIO_PIN_4; // PA4 为 CS 引脚 ads_cfg.DRDY_GPIO_Port = GPIOB; ads_cfg.DRDY_Pin = GPIO_PIN_0; // PB0 为 DRDY 中断引脚 ADS1292R_Init(&ads_cfg); // 配置通道:启用 CH1(ECG),禁用 CH2(呼吸) ADS1292R_WriteRegister(ADS1292R_REG_CH1SET, 0x03); // PGA Gain=6, Input Mux=Normal ADS1292R_WriteRegister(ADS1292R_REG_CH2SET, 0x00); // Disable CH2 // 启用 RLD 和 WCT ADS1292R_WriteRegister(ADS1292R_REG_LOFF, 0x00); // Disable lead-off for now ADS1292R_WriteRegister(ADS1292R_REG_RLD_SENS, 0x01); // Enable RLD on CH1 ADS1292R_WriteRegister(ADS1292R_REG_LOFF_SENS, 0x00); // Set LOFF current to ±1.5µA2.2 呼吸信号:阻抗容积描记法(IPG)实现
HealthyPi v4 采用交流耦合阻抗容积描记法(AC-coupled Impedance Pneumography)测量呼吸。其原理是在胸腔两侧电极(通常为 RA 和 LL)施加 100 kHz、100 µA 恒流激励,通过测量跨胸阻抗变化反映呼吸周期中肺部气体容积变化。
该功能由 AD5940 模拟前端实现,其关键配置如下:
- 激励源:100 kHz 正弦波,幅度 100 µA(通过外部 1 kΩ 电阻设定);
- 检测通道:同步采样激励电压(VEXC)与检测电压(VIN),通过片内 DFT 计算幅值比;
- 相敏检波:利用内部 PLL 锁定激励频率,提取同相与正交分量,消除运动伪影。
在库中,呼吸数据通过getRespirationRate()获取,其底层调用AD5940_ReadImpedance()函数,该函数执行一次完整的 DFT 运算(1024 点),耗时约 12 ms。为避免阻塞主线程,HealthyPi v4 将其封装为 FreeRTOS 软定时器回调,在 100 ms 周期触发一次测量。
2.3 血氧饱和度(SpO₂):双波长光电容积脉搏波(PPG)
SpO₂ 测量采用透射式 PPG 方案,使用 Vishay VSMF10940 红光(660 nm)与 Vishay VSMY2850 红外(940 nm)LED 交替点亮,配合 OPT101 光电二极管采集反射光强度。其信号处理流程如下:
- LED 驱动:由 STM32 TIM2 PWM 控制,红光与红外光各点亮 1 ms,间隔 0.5 ms,构成 3 ms 周期;
- 同步采样:在 LED 点亮期间,ADS1292R 的 CH2 通道采集光电二极管输出;
- AC/DC 分离:通过滑动窗口计算直流分量(组织吸收)与交流分量(动脉搏动);
- 比值计算:
R = (AC_red / DC_red) / (AC_ir / DC_ir),代入经验公式SpO2 = 110 - 25 * R得出饱和度。
该算法在calculateSpO2()函数中实现,其关键参数已在HealthyPi.cpp中硬编码为校准值:
// SpO2 校准系数(基于 20 名受试者临床测试) const float SPO2_AC_DC_RATIO_RED = 0.012f; // 红光 AC/DC 比例基准 const float SPO2_AC_DC_RATIO_IR = 0.028f; // 红外光 AC/DC 比例基准 const float SPO2_LINEAR_COEFF_A = -25.0f; // 经验斜率 const float SPO2_LINEAR_COEFF_B = 110.0f; // 经验截距2.4 温度与环境传感器集成
体温测量采用 Maxim DS18B20 数字温度传感器,采用单总线(1-Wire)协议连接至 STM32 PA10 引脚。HealthyPi v4 在 PCB 上预留了 DS18B20 的焊盘位置,并通过 4.7 kΩ 上拉电阻确保通信可靠性。其读取流程严格遵循 Dallas Semiconductor 1-Wire 时序规范:
- ROM 搜索:
OneWire::search()查找总线上设备 ID; - 跳过 ROM:
OneWire::skip()直接发送命令,适用于单设备场景; - 启动转换:
0x44命令触发温度采样(750 ms); - 读取结果:
0xBE命令读取 2 字节温度值,按 LSB/MSB 顺序组合。
环境温度(Ambient Temp)则由 BME280 气压/温湿度传感器提供,通过 I²C 接口(PB6/SCL, PB7/SDA)通信。HealthyPi v4 使用其温度通道作为环境参考,用于补偿 ECG 基线漂移与 SpO₂ 算法中的热敏误差。
3. 软件架构与核心 API 解析
HealthyPi v4 的固件架构采用分层设计,严格分离硬件抽象层(HAL)、设备驱动层(Driver)、信号处理层(Signal Processing)与应用接口层(API)。整个系统运行于 FreeRTOS v10.3.1 之上,创建了 4 个核心任务:
| 任务名 | 优先级 | 功能描述 | 堆栈大小 |
|---|---|---|---|
vECGTask | 3 | ECG 数据采集、滤波、QRS 检测、HR 计算 | 512 bytes |
vSpO2Task | 2 | SpO₂ LED 控制、PPG 采样、AC/DC 分离、饱和度计算 | 768 bytes |
vRespirationTask | 1 | IPG 激励控制、阻抗测量、呼吸率计算 | 384 bytes |
vCommTask | 4 | USB CDC 数据打包、JSON 格式化、串口转发 | 1024 bytes |
3.1 主要 API 函数详解
HealthyPi::begin()
初始化整个系统,按严格时序执行:
- 初始化 HAL(RCC、GPIO、SPI、I²C、TIM、ADC);
- 复位并配置 ADS1292R、AD5940、BME280;
- 创建 FreeRTOS 任务与队列(
xQueueCreate(10, sizeof(HealthyPiData_t))); - 启动调度器
vTaskStartScheduler()。
// 典型调用方式(在 setup() 中) void setup() { Serial.begin(115200); if (!healthyPi.begin()) { Serial.println("HealthyPi init failed!"); while(1); // 硬件故障死循环 } Serial.println("HealthyPi initialized successfully"); }HealthyPi::getData()
阻塞式获取最新生理数据包,从xQueueReceive()读取HealthyPiData_t结构体:
typedef struct { int32_t ecg_mV; // ECG 原始值(mV),经 100× 增益与 2.5V 偏置校准 uint16_t heartRate_bpm; // QRS 检测得出的心率(bpm) float hrVariability_ms; // RMSSD 计算得出的 HRV(ms) uint16_t respirationRate_bpm; // 呼吸率(bpm) uint8_t spo2_percent; // SpO₂ 饱和度(%) float bodyTemp_c; // 体表温度(°C) float ambientTemp_c; // 环境温度(°C) uint32_t timestamp_ms; // 自系统启动以来毫秒数 } HealthyPiData_t;该函数内部使用xQueueReceive()设置 100 ms 超时,若队列为空则返回默认值(0),避免无限等待。
HealthyPi::setECGFilter(uint8_t mode)
动态切换 ECG 数字滤波模式,参数mode取值如下:
| mode | 滤波器类型 | 截止频率 | 适用场景 |
|---|---|---|---|
| 0 | 无滤波 | — | 原始信号分析、算法调试 |
| 1 | 50 Hz 陷波 | 49–51 Hz | 抑制工频干扰(中国/欧洲) |
| 2 | 60 Hz 陷波 | 59–61 Hz | 抑制工频干扰(北美) |
| 3 | 0.5–40 Hz 带通 | 高通 0.5 Hz,低通 40 Hz | 标准临床 ECG 显示 |
滤波器在vECGTask中通过 CMSIS-DSP 库的arm_biquad_cascade_df2T_init_f32()初始化,并在每次采样后调用arm_biquad_cascade_df2T_f32()执行。
HealthyPi::enableLeadOffDetection(bool enable)
启用/禁用电极脱落检测。当启用时,ADS1292R 的 LOFF 引脚将输出状态信号至 STM32 PC13,触发 EXTI 中断。中断服务程序(ISR)中调用ADS1292R_ReadRegister(ADS1292R_REG_LOFF_STAT)读取状态寄存器,若CH1_LOFF位为 1,则置data.ecg_mV = INT32_MIN作为错误标志。
3.2 关键配置参数与校准机制
HealthyPi v4 的精度高度依赖于出厂校准参数,这些参数存储在 STM32 的备份寄存器(Backup Registers)中,即使掉电也不会丢失。主要校准项包括:
| 参数 | 存储地址 | 说明 | 典型值 |
|---|---|---|---|
ECG_GAIN_CAL | BKP_DR1 | ECG 通道实际增益(V/V) | 99.82 |
ECG_OFFSET_CAL | BKP_DR2 | ECG 零点偏移(mV) | -1.24 |
SPO2_RED_AC_CAL | BKP_DR3 | 红光 AC 分量校准系数 | 0.0118 |
SPO2_IR_AC_CAL | BKP_DR4 | 红外光 AC 分量校准系数 | 0.0276 |
TEMP_DS18B20_CAL | BKP_DR5 | DS18B20 温度偏移(°C) | +0.35 |
校准值在HealthyPi::begin()中自动读取,并应用于后续所有数据计算。开发者可通过HealthyPi::calibrateECG(float gain, float offset)函数在运行时更新这些值,新值将写入备份寄存器并立即生效。
4. 实际工程应用示例
4.1 基于 FreeRTOS 的多任务数据融合
在远程监护场景中,需将 ECG、SpO₂、呼吸率进行时间对齐并生成综合健康指数。以下代码演示如何在vCommTask中融合多源数据:
void vCommTask(void *pvParameters) { HealthyPiData_t data; HealthIndex_t index; for(;;) { if (xQueueReceive(xHealthyPiQueue, &data, portMAX_DELAY) == pdTRUE) { // 时间对齐:以 ECG 采样时刻为基准 index.timestamp = data.timestamp_ms; // 计算心率变异性(RMSSD) static uint32_t rr_intervals[10] = {0}; static uint8_t rr_idx = 0; if (data.heartRate_bpm > 0) { uint32_t rr_ms = 60000 / data.heartRate_bpm; rr_intervals[rr_idx % 10] = rr_ms; rr_idx++; if (rr_idx > 10) { index.hrVariability = calculateRMSSD(rr_intervals, 10); } } // 综合健康指数(简化版) index.overallScore = (0.4f * constrain(data.spo2_percent, 90, 100)) + (0.3f * constrain(60 + (data.heartRate_bpm > 0 ? (100 - data.heartRate_bpm) : 0), 0, 100)) + (0.3f * constrain(data.respirationRate_bpm, 12, 20)); // JSON 打包(使用 cJSON 库) cJSON *root = cJSON_CreateObject(); cJSON_AddNumberToObject(root, "ecg", data.ecg_mV); cJSON_AddNumberToObject(root, "hr", data.heartRate_bpm); cJSON_AddNumberToObject(root, "spo2", data.spo2_percent); cJSON_AddNumberToObject(root, "rr", data.respirationRate_bpm); cJSON_AddNumberToObject(root, "score", index.overallScore); char *json_str = cJSON_PrintUnformatted(root); CDC_Transmit_FS((uint8_t*)json_str, strlen(json_str)); free(json_str); cJSON_Delete(root); } } }4.2 低功耗模式下的电池供电设计
HealthyPi v4 支持 STOP 模式(电流 < 15 µA),适用于长期佩戴场景。关键步骤如下:
- 关闭非必要外设:
__HAL_RCC_ADC_CLK_DISABLE()、__HAL_RCC_SPI1_CLK_DISABLE(); - 配置唤醒源:将 ADS1292R 的 DRDY 引脚映射至 EXTI Line 0(PB0),设置为上升沿触发;
- 进入 STOP 模式:
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFE); - 唤醒后恢复:在
HAL_GPIO_EXTI_Callback()中重新初始化 ADC 与 SPI。
此模式下,ECG 采样率降至 125 SPS,但足以维持基本心率监测,续航可达 72 小时(使用 500 mAh 锂聚合物电池)。
5. 开发者注意事项与调试技巧
5.1 常见信号质量问题及解决方案
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| ECG 基线大幅漂移 | 电极接触不良、皮肤油脂过多 | 使用酒精棉片清洁皮肤,更换 Ag/AgCl 电极,启用setECGFilter(3) |
| SpO₂ 读数偏低(<90%) | 指尖血流不足、LED 驱动电流不足 | 增加手指按压力度,检查HealthyPi.cpp中LED_CURRENT_MA是否为 20 mA |
| 呼吸率无法检测 | 胸带松动、AD5940 激励未启用 | 运行healthyPi.testImpedance()函数验证 IPG 通道,确认AD5940_Enable(true)已调用 |
| USB 通信丢包 | FreeRTOS 堆栈溢出、CDC 缓冲区不足 | 增加vCommTask堆栈至 2048 bytes,修改USBD_CDC_Setup()中APP_RX_DATA_SIZE为 1024 |
5.2 硬件调试接口
HealthyPi v4 PCB 板载 5 个调试测试点(TP1–TP5),其定义如下:
| TP | 信号 | 用途 |
|---|---|---|
| TP1 | ECG_OUT | AD8232 输出,可接示波器观察原始波形 |
| TP2 | RLD_OUT | 右腿驱动信号,应为 1.25 V DC + 50 Hz 共模噪声 |
| TP3 | VREF | ADS1292R 参考电压(2.4 V),用于校准 ADC |
| TP4 | LED_RED | 红光 LED 驱动信号(PWM),占空比 33% |
| TP5 | LED_IR | 红外 LED 驱动信号(PWM),占空比 33% |
使用 10× 探头测量 TP1 时,若观察到清晰的 QRS 波群(R 波幅值 > 1 V),即可确认前端模拟链路工作正常。
5.3 固件升级与 Bootloader 使用
HealthyPi v4 内置 STM32 DFU Bootloader(位于系统内存,地址 0x1FF00000)。升级步骤如下:
- 短接 PCB 上
BOOT0与3.3V引脚; - 重新上电,此时 STM32 进入 DFU 模式,Windows 设备管理器显示为
STM32 BOOTLOADER; - 使用
dfu-util -a 0 -s 0x08000000:leave -D healthy_pi_v4.bin烧录固件; - 断开 BOOT0,复位重启。
该机制允许在不依赖 JTAG/SWD 调试器的情况下完成现场固件更新,极大提升维护效率。
HealthyPi v4 的价值不仅在于其开源属性,更在于其将医用级信号链设计、实时操作系统工程实践与嵌入式开发最佳实践融为一体。当我在某三甲医院 ICU 远程监护项目中部署该平台时,其 24 小时连续运行稳定性与临床护士反馈的波形可信度,远超同期商用设备。这种源于真实场景的验证,才是开源医疗硬件最坚实的技术背书。