1. 项目概述:当STM32遇上健康监测
去年帮朋友调试这个手环项目时,我们遇到最棘手的问题是运动状态下的信号干扰。传统光电传感器在剧烈晃动时,PPG信号会被噪声完全淹没。这个基于STM32F4的心率血氧手环方案,通过自适应滤波算法和双波长光电检测,实现了静态±1bpm、动态±3bpm的测量精度,特别加入了异常值震动报警功能,适合老年人和心血管疾病患者日常监测。
整套方案包含硬件设计、信号处理算法和低功耗优化三大模块。主控采用STM32F411CEU6,搭配MAX30102传感器模块,整套BOM成本控制在50元以内。实测显示,在100Hz采样率下,系统待机电流仅1.2mA,连续工作续航可达7天。
2. 硬件设计精要
2.1 核心器件选型
传感器模块的选择直接决定测量精度:
- MAX30102:集成红光(660nm)/红外光(880nm)双LED,自带环境光抑制,I²C接口输出原始PPG数据
- STM32F411CEU6:Cortex-M4内核,100MHz主频,自带硬件浮点单元,适合实时信号处理
- LIS3DH:三轴加速度计,用于运动补偿和跌倒检测
- 0.96寸OLED:显示实时数据,功耗仅0.08W
关键提示:MAX30102的LED驱动电流需精确配置,红光建议设置6.4mA,红外光7.6mA,可通过修改0x0C寄存器实现。
2.2 电路设计要点
电源管理是低功耗设计的核心:
c复制// 典型低功耗配置代码
void Power_Config(void)
{
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWREx_EnableUltraLowPower();
HAL_PWREx_EnableFastWakeUp();
__HAL_FLASH_SET_LATENCY(FLASH_LATENCY_1);
}
信号链路由需要特别注意:
- 光电传感器输出端添加100nF去耦电容
- I²C总线串联100Ω电阻抑制振铃
- 模拟电源走线宽度至少0.3mm
- 传感器接口ESD保护采用SMF05C TVS管
3. 算法实现解析
3.1 心率计算原理
采用时域分析法结合频域验证:
- 原始信号经过0.5-5Hz带通滤波
- 通过动态阈值检测波峰间隔
- 使用滑动窗口计算平均心率值
python复制# 伪代码示例
def calc_hr(ppg_data):
filtered = bandpass(ppg_data, 0.5, 5)
peaks = find_peaks(filtered, height=0.6*max)
intervals = np.diff(peaks)
hr = 60 / np.median(intervals) * sampling_rate
return hr
3.2 血氧饱和度算法
基于红光(R)和红外光(IR)的AC/DC分量比:
$$
R = \frac{AC_{red}/DC_{red}}{AC_{ir}/DC_{ir}} \
SpO_2 = 110 - 25 \times R
$$
实际实现时需要做温度补偿:
c复制float CalculateSpO2(float red_ac, float red_dc, float ir_ac, float ir_dc)
{
float R = (red_ac/red_dc) / (ir_ac/ir_dc);
float spo2 = 110.0 - 25.0 * R;
// 温度补偿
if(temp < 20) spo2 += 0.5*(20-temp);
return constrain(spo2, 70, 100);
}
4. 报警功能实现
4.1 异常检测逻辑
三级预警机制设计:
- 初级预警:心率连续3次超出预设范围
- 中级预警:血氧低于92%持续30秒
- 紧急报警:心率<50或>120并伴随跌倒检测
状态机实现示例:
mermaid复制stateDiagram
[*] --> Monitoring
Monitoring --> Warning: 条件1触发
Warning --> Alarm: 条件2触发
Alarm --> [*]: 用户确认
4.2 震动马达驱动
采用PWM控制震动强度:
c复制void Vibrate(uint8_t pattern)
{
TIM3->CCR1 = (pattern == STRONG) ? 900 : 500;
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
HAL_Delay(300);
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
}
5. 低功耗优化技巧
5.1 电源模式管理
运行状态分级:
| 模式 | 功耗 | 唤醒源 |
|---|---|---|
| Run | 12mA | 常开 |
| LPRun | 5mA | 定时器 |
| Sleep | 1.8mA | 外部中断 |
| Stop | 0.4mA | RTC |
配置示例:
c复制void Enter_LowPower(void)
{
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0x2000, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
}
5.2 传感器轮询策略
动态调整采样频率:
- 静止状态:10Hz采样
- 运动状态:50Hz采样
- 报警状态:100Hz采样
实测功耗对比:
| 模式 | 采样率 | 平均电流 |
|---|---|---|
| 待机 | 1Hz | 1.2mA |
| 正常 | 10Hz | 3.8mA |
| 运动 | 50Hz | 8.5mA |
6. 常见问题排查
6.1 信号质量优化
典型干扰现象及解决方案:
- 基线漂移:添加高通滤波,截止频率0.5Hz
- 运动伪影:启用加速度计补偿算法
- 环境光干扰:检查传感器贴合度,增加遮光结构
6.2 测量精度提升
校准流程建议:
- 静坐状态下采集3分钟基准数据
- 计算DC分量平均值作为基准线
- 通过已知健康数据修正算法参数
调试技巧:
- 使用ST-Link实时查看原始波形
- 修改sensor.h中的#define DEBUG_RAW 1输出原始数据
- 通过串口绘图工具验证算法效果
7. 成品测试数据
实测性能指标:
| 参数 | 测试条件 | 结果 |
|---|---|---|
| 心率精度 | 静坐 | ±1bpm |
| 血氧精度 | 室温 | ±2% |
| 响应时间 | 手指放置 | <8s |
| 报警延迟 | 异常发生 | <3s |
对比商业产品:
| 指标 | 本方案 | 某米手环5 |
|---|---|---|
| 功耗 | 1.2mA | 0.8mA |
| 成本 | ¥48 | ¥120 |
| 刷新率 | 1Hz | 0.5Hz |
| 报警功能 | 有 | 无 |
8. 生产注意事项
8.1 贴片工艺要求
关键器件焊接参数:
| 器件 | 温度曲线 | 注意事项 |
|---|---|---|
| MAX30102 | 峰值245℃ | 禁止二次回流 |
| STM32 | 260℃±5℃ | 必须氮气保护 |
| 加速度计 | 230℃max | 提前烘干 |
8.2 固件烧录流程
批量生产方案:
- 使用STLINK-V3批量编程器
- 通过SWD接口烧录
- 校验选项字节配置:
bash复制st-flash --reset read /tmp/flash.bin 0x08000000 0x10000
9. 扩展功能建议
9.1 蓝牙数据传输
添加HC-05模块实现:
c复制void BT_SendData(void)
{
uint8_t buf[20];
sprintf(buf, "HR:%d SpO2:%d", heart_rate, spo2);
HAL_UART_Transmit(&huart1, buf, strlen(buf), 100);
}
9.2 移动端APP对接
Android端数据解析示例:
java复制public void parseData(byte[] packet) {
int hr = (packet[3] & 0xFF) << 8 | (packet[4] & 0xFF);
int spo2 = packet[5] & 0xFF;
if(hr > 40 && hr < 200) {
updateUI(hr, spo2);
}
}
10. 项目优化方向
下一步改进计划:
- 加入机器学习算法提升运动抗干扰能力
- 改用STM32U5系列进一步降低功耗
- 开发硅胶腕带模具提升佩戴舒适度
- 通过FCC/CE认证准备量产
实际开发中发现,MAX30102的玻璃窗口容易积聚汗液导致测量偏差。我们在V2版本改为疏水涂层处理,同时增加接触检测功能,当传感器未贴肤时自动暂停测量,节省了约30%的功耗。