基于STM32F103C8T6的心率血氧血压检测报警系统全解析
2026/8/31 3:46:07 网站建设 项目流程

简介:本资源是一套面向本科毕业设计与嵌入式初学者的STM32健康监测系统完整开发资料,聚焦心率、血氧、血压三参数实时采集与异常报警功能,解决可穿戴医疗设备原型快速复现难题。压缩包共284个文件,含44个C源码文件(如stm32f10x_adc.c、stm32f10x_i2c.c等底层驱动)、47个编译中间文件(.d/.o)、45个头文件(.h)及原理图(.schdoc)、工程配置(.uvprojx/.uvoptx)、Hex固件、元件清单(.xlsx)和说明文档(.docx/.pdf),全面覆盖硬件设计、传感器驱动(MAX30102+MSP20)、OLED显示、HC-05蓝牙通信及阈值报警逻辑实现。资源包大小16.96MB,结构清晰,模块划分明确,便于分步调试与功能移植。已有867人学习下载,配套代码可直接烧录运行,支持实物复刻与二次开发,是掌握STM32多传感器融合与医疗电子项目落地的高实用性参考方案。 做单片机项目这么多年,身边问得最多的一个问题,就是能不能给家里老人做一台能测心率、血氧、血压的监测设备。市面上几百块的家用血压计确实能测,但商品设备的数据基本都是封闭的,要连续记录、超限报警、自定义联动非常麻烦。所以我干脆基于STM32F103C8T6自己搭了一套完整的心率血氧血压检测报警系统,程序、原理图、元件清单全套整理好,从传感器选型到算法实现,所有坑都替你踩过一遍。这套系统最大的价值不是"医疗级精准",而是低成本、可复现的连续监测和异常预警。适合三类人:做毕业设计或者电赛的在校生,想给家里长辈做健康预警仪的嵌入式开发者,以及想彻底搞懂PPG光电容积脉搏波和示波法血压测量原理的硬件爱好者。

1. 这套系统能做什么:从测量参数到预警逻辑的一体化设计

1.1 三个生理参数的测量边界

先把这个系统能测什么、测到什么程度说清楚。心率通过PPG光电容积脉搏波信号计算,适用范围大概在30~240次/分;血氧饱和度通过红光和红外光的吸收比计算,有效范围在70%~100%,低于70%以后经验公式的误差会急剧增大;血压采用示波法,通过袖带压力和震荡波包络计算,测量范围在0~200mmHg,超过这个范围系统会强制放气保护。

这里必须强调一句:这套系统是学习用和日常趋势监测用的,不是医疗器械。家用电子血压计误差在±5mmHg以内都算合格,DIY系统老老实实做到±10mmHg以内,用于观察血压波动趋势是足够的,但不能作为临床诊断依据。做毕设的同学尤其要注意,在论文里一定要写清楚这个定位,别给自己挖坑。

1.2 系统架构与完整工作流程

整个系统分五个模块:STM32主控、心率血氧传感器(MAX30102)、血压测量单元(压力传感器加气泵加电磁阀)、人机交互(OLED加按键加蜂鸣器)、电源。

工作流程是这样的:上电后系统进入待机状态,OLED实时显示当前心率和血氧饱和度。用户按下"测量血压"按键后,系统自动完成充气、保压、放气、波形采集、算法计算,最后在屏幕上显示收缩压、舒张压和脉率。任意一项参数超出设定阈值,蜂鸣器鸣叫、LED闪烁,同时把本次测量结果写入EEPROM保存,方便回看历史记录。

这个流程看起来简单,但真正实现起来有几个容易被忽略的点:第一,测量血压期间心率血氧的连续监测不能中断,否则用户等一次血压读数要好几十秒,期间屏幕黑屏或者心率数据断掉,体验很差;第二,血压测量完成后的放气动作必须可靠,气泵和电磁阀的驱动逻辑要分开处理;第三,报警触发后要区分"首次报警"和"持续报警",避免蜂鸣器一直响到用户关机。后面我会逐个展开讲。

2. 传感器选型与测量原理:MAX30102和示波法血压的底层逻辑

2.1 心率血氧传感器到底怎么选:MAX30100、MAX30102还是红外对管

心率血氧测量的物理基础是光电容积脉搏波,也就是PPG。组织里的血液随心跳周期性变化,对光的吸收也随之变化,透过或反射回来的光强就会呈现周期性波动。方案上无非三条路:用集成传感器模块、用分立红外对管自己搭模拟前端、用老款集成模块。

  • MAX30100:早期常见的集成方案,内置红光、红外光和绿光LED,但芯片本身有一些硬件设计缺陷,比如I2C通信偶发卡死、PCB布局敏感,新手照着参考设计打样很容易出现信号差的问题,现在已经不推荐新设计使用了。
  • MAX30102:MAX30100的升级版,只保留红光和红外光,信号质量大幅提升,封装更小,功耗也更低,是当前DIY血氧仪的主流选择。市面上大量现成的MAX30102模块,板子自带电平转换和滤波电容,接上I2C就能用,非常省事。
  • 分立红外对管方案:需要自己设计跨阻放大器、带通滤波器、巴特沃斯滤波,光信号调理电路就要花掉一大块面积。好处是成本低、原理图可以画得很唬人,但调试周期长到让人崩溃,真心不建议新手碰。

我的结论很直接:用MAX30102模块。一颗芯片同时解决心率、血氧两个参数的采集,I2C接口,代码库成熟,网上资料多到看不完,后续出了问题也好排查。

2.2 PPG信号的血氧计算:R值到底怎么算

血氧饱和度的原理说起来是本科生物医学工程的内容:还原血红蛋白对660nm红光吸收强,氧合血红蛋白对940nm红外光吸收强。当血液中的氧合比例变化时,两种波长的吸收比会发生改变,通过这个差异反推SpO2。

具体计算用"比值R法"。PPG信号分成两部分:直流分量DC来自组织、静脉血和毛细血管的静态吸收,交流分量AC来自动脉血的脉动变化。分别算出红光的AC/DC值和红外光的AC/DC值,两者相除得到R:

R = (AC_red / DC_red) / (AC_ir / DC_ir)

然后套经验公式:

SpO2 = 110 - 25 × R

这个公式在R取值0.4到1.0之间时线性度尚可,对应血氧大约70%到100%。低于70%以后,这个线性近似就完全不可靠了,所以代码里要对计算结果做钳位处理,低于70%一律显示"--",别给用户一个假得离谱的数字。

这里提醒一句:正规医疗血氧仪用的是大规模临床校准曲线,公式系数来自上千人次的侵入式血气分析对比。DIY系统没有这个条件,所以测出来的SpO2只适合看趋势。我做实测对比过,血氧在95%以上时和正规指夹式血氧仪差1%以内,但手指冰凉、传感器没贴紧、手指晃动时误差会明显变大。

2.3 血压测量为什么选示波法:原理与关键点

血压测量方案有听诊法(柯氏音法)和示波法两类。听诊法需要袖带内埋麦克风,检测血流恢复时的柯氏音,硬件和信号处理都复杂,DIY基本不做。示波法测量的是袖带压力上的微小震荡波,实现起来简单得多,所以市面上几乎所有电子血压计都用它。

示波法的流程:先把袖带充气到收缩压以上约30mmHg(通常180mmHg),让血管完全压闭,然后电磁阀控制缓慢放气。放气过程中,随着袖带压力下降,血管从闭合到逐渐恢复血流,动脉壁搏动会在袖带气压上叠加一个微小的震荡波。这个震荡波的幅度先增大后减小,形成一个钟形包络。

关键判据是三个点:

  • 平均压MAP:震荡波幅度最大的点对应的袖带压力。
  • 收缩压:MAP之前,震荡幅度上升到最大幅度的50%~55%时对应的压力。
  • 舒张压:MAP之后,震荡幅度下降到最大幅度的75%~80%时对应的压力。

不同厂家的算法阈值略有差别,有的用45%和75%,有的用55%和85%,没有绝对标准。我实测下来,用50%和75%这个组合在成人手臂上的误差相对小,后面你可以根据自己的测量对象微调。放气速度很关键,一般控制在每心跳下降3~5mmHg,放气太快包络会被"削平",收缩压偏高、舒张压偏低,放气太慢用户手臂难受。

3. 原理图拆解:STM32最小系统、传感器接口与泵阀驱动

3.1 MCU选型和最小系统设计:为什么是F103C8T6

主控选了STM32F103C8T6,这颗芯片在这个领域算是"国民单片机"了:64脚LQFP封装,72MHz主频,64KB Flash,20KB RAM,2个I2C、3个USART、2个ADC,价格便宜货源充足,资料多到随便搜。性能完全够用,而且基于它做过毕设和项目的人太多,出了问题找参考非常方便。

最小系统要注意几个点:8MHz晶振配两个20pF负载电容,这是必须的;NRST引脚接10kΩ上拉电阻加100nF对地电容,防止复位干扰;BOOT0接10kΩ下拉电阻到地,BOOT1接10kΩ下拉,保证从Flash启动;3.3V电源在芯片每个电源引脚附近放100nF去耦电容,另外在电源入口放一个10µF钽电容或铝电解电容。SWD四线(SWDIO、SWCLK、GND、3.3V)引出排针,烧录用ST-Link,配合ST-Link Utility或者Keil直接下载,非常方便。

如果只是做功能验证,可以直接买蓝色Pill板子,几块钱一块,省去画最小系统的功夫。但如果是毕设要交原理图和PCB,建议还是自己画一遍最小系统,这部分分数比重不小,画的时候把每个引脚的用途都标注清楚,答辩也好讲。

3.2 I2C总线:MAX30102与OLED挂同一条线的注意事项

MAX30102和OLED用的都是I2C接口,我让它们挂在同一条I2C1上,PB6做SCL,PB7做SDA。两个器件地址不同,MAX30102的7位地址是0x57(写地址0xAE),SSD1306的7位地址是0x3C(写地址0x78),所以同一条总线完全没问题。

有几个细节必须注意。第一,I2C总线需要上拉电阻,很多MAX30102小板没有集成上拉,OLED板上有没有也不一定,所以主板上要预留两个4.7kΩ上拉电阻到3.3V,否则通信会偶发失败。第二,MAX30102的逻辑电平是1.8V,但市售模块基本都做了电平转换,直接接3.3V没问题,如果用的是裸芯片,就必须加电平转换电路。第三,I2C总线上多个器件时,信号线尽量短,不要超过10cm,尤其是MAX30102要放在板边方便手指按压,走线要规划好。

3.3 血压测量通道:压力传感器、气泵与电磁阀的驱动电路

血压测量这部分是原理图的重点。压力传感器我选了XGZP6847的模拟输出版本,量程0~40kPa,输出0.5~4.5V。为什么不用数字版?因为血压测量需要连续高速采样压力信号,模拟输出直接进STM32的ADC加DMA,采样率可以做到很高,处理起来灵活得多。数字版虽然免了标定,但I2C读取速度有限,而且在放气过程中要同时处理震荡波细节,数据更新率跟不上就容易丢信息。

这里有一个关键计算:XGZP6847在0mmHg时输出0.5V,在满量程40kPa(约300mmHg)时输出4.5V,灵敏度约为每mmHg 13.3mV。但STM32的ADC输入范围是0~3.3V,直接用会超出。所以需要在传感器输出后面加一个电阻分压,用两个10kΩ电阻把电压减半,这样200mmHg对应约1.58V,300mmHg对应约2.25V,都在ADC安全范围内。同时加一个100nF电容做低通滤波,滤掉高频干扰。

气泵和电磁阀的驱动不能用GPIO直接怼,必须加驱动管。我用AO3400 N-MOS管驱动气泵,S8050三极管驱动电磁阀,注意在泵和阀的线圈两端反向并联一个续流二极管(1N4148或者SS14),否则关断瞬间的反向电动势会打坏MOS管,这个坑我第一批板子就踩过,烧了三个驱动管才反应过来。

3.4 报警与电源设计:分开供电,别让气泵拖垮主控

报警电路很简单,一个被动蜂鸣器配一个8050三极管驱动,用TIM定时器产生不同频率的PWM就能发出不同音调,比有源蜂鸣器灵活。LED指示直接用GPIO串联限流电阻驱动。

电源部分要特别说:气泵启动瞬间电流能到300~500mA,如果和主控共用一条电源轨,电压跌落会把STM32拉复位。我实测过,USB供电时Blue Pill板子上的AMS1117在气泵启动瞬间输出掉到2.8V以下,主控直接重启。解决方案是电源分两路:5V进来先接一个大容量电解电容(1000µF)再分两路,一路进AMS1117转3.3V给主控和传感器,另一路直接给气泵和电磁阀,两路之间用地线隔开,必要时加磁珠或者0欧电阻做单点连接。这样气泵启动时主控电压纹波能控制在100mV以内。

4. 固件架构:STM32驱动、信号处理与算法实现

4.1 开发环境与工程结构:HAL库还是标准库

固件我用的是Keil MDK加STM32CubeMX生成工程,HAL库开发。虽然从老工程师的角度看标准库更精简,但HAL库在CubeMX里配置引脚和定时器效率高,出错的概率小,对于包含ADC DMA、I2C、定时器、PWM、USART这么多外设的工程,HAL库能省不少时间。调试工具用ST-Link V2,串口USART1通过USB转TTL接到电脑,用串口助手打印调试信息,尤其是血压测量过程中的压力曲线数据,后面调算法全靠它。

工程结构按功能拆文件:main.c管状态机主循环,max30102.c管心率血氧驱动,pressure.c管压力采集和血压算法,oled.c管显示,buzzer.c管报警,eeprom.c管历史数据存储。每个模块独立编译,不要把所有代码塞在一个main.c里,后面调试会让你想哭。

4.2 MAX30102驱动:寄存器配置与FIFO读取

MAX30102的驱动核心是寄存器配置和FIFO数据读取。初始化序列网上到处都有,我贴一份我自己验证过的配置:

uint8_t max30102_init(void) { /* 关闭所有中断 */ max30102_write_reg(0x09, 0x00); /* FIFO配置:均值2次采样,FIFO满中断 */ max30102_write_reg(0x0C, 0x07); /* 模式配置:0x03=SpO2模式,红红外同时工作 */ max30102_write_reg(0x0D, 0x03); /* SpO2配置:采样率200Hz,ADC量程4096nA */ max30102_write_reg(0x0E, 0x27); /* 红色LED电流:约7.2mA */ max30102_write_reg(0x10, 0x24); /* 红外LED电流:约7.2mA */ max30102_write_reg(0x11, 0x24); /* FIFO每次读一组样本 */ max30102_write_reg(0x12, 0x01); /* 使能FIFO满和PPG数据准备好中断 */ max30102_write_reg(0x09, 0xC0); return 0; }

LED电流这个参数很关键。设得太小,采集到的PPG信号幅度不足,波形被量化噪声淹没;设得太大,信号饱和,红外的交流分量会被削顶。我调试时的经验是:先把LED电流调到最小(0x00),然后看串口打印的红外原始波形,如果信号幅度只有几十个LSB,就逐步加大电流,直到手指轻轻放上去时波形幅度能占到ADC满量程的30%左右,这个值是手指静止状态下的最优值。不同人的皮肤颜色、指甲厚度、温度都会影响最佳电流,所以代码里最好做成自动增益,根据DC分量大小动态调整。

数据读取用中断方式,在I2C接收完成回调里读FIFO数据寄存器0x0B,一次读6个字节(红光3字节加红外3字节,取高18位)。采样率200Hz意味着每5ms一帧数据,主循环里必须在下一个中断到来之前把数据处理完,否则FIFO溢出会导致波形断裂。

4.3 数据处理流水线:滤波、峰值检测与心率计算

原始PPG信号没有直接用不了,必须先过一遍数据处理流水线。我的处理顺序是:滑动平均滤波、去直流、峰值检测、心率计算。

滑动平均用窗口9个点的均值滤波,滤掉50Hz工频和随机噪声。去直流用两种方法配合:一是高通滤波,用一阶IIR计算滑动基线,再把原始值减去基线,通带大概0.5Hz以上,这样能保留心跳交流分量;二是对慢速基线漂移(比如手指慢慢用力)做跟踪补偿。

峰值检测是心率计算的核心。最简单可靠的方法是"阈值加不应期":设定一个动态阈值,波形从下往上穿越阈值时记录为一个候选峰值,在候选峰值附近找局部最大值作为真实波峰,然后记录两次波峰的时间间隔。不应期设置为250ms,也就是最大心率240bpm,小于这个间隔的峰值直接丢弃,避免T波或者噪声被误判成心跳。

#define REFRACTORY_MS 250 #define THRESHOLD_RATIO 0.6f if (ir_value > peak_threshold && time_since_last_peak > REFRACTORY_MS) { /* 在候选峰值后寻找局部最大值 */ if (ir_value > max_candidate) { max_candidate = ir_value; max_candidate_time = current_time; } } else if (ir_value < peak_threshold && max_candidate > 0) { /* 波形回落,确认峰值 */ heart_interval_ms = max_candidate_time - last_peak_time; instant_heart_rate = 60000 / heart_interval_ms; last_peak_time = max_candidate_time; max_candidate = 0; } /* 阈值每拍更新为最大值的60% */ peak_threshold = 0.6f * max_value_in_window;

心率显示值不要直接用单拍的瞬时值,做个五拍平均,这样显示更稳定。实测下来,静止状态下和参考血氧仪对比误差在±2bpm以内,手指稍微晃动误差会到±5bpm,这个精度对家庭监测来说够用了。

4.4 SpO2计算:R值的分帧统计与校准限制

SpO2计算和心率共用同一条PPG流水线,但需要红光和红外两个通道的数据。计算时不能逐点算R值,那样噪声太大。我的做法是:每4秒为一个统计窗口,分别对红光和红外通道做峰值检测,记录每个波峰的AC幅度和波谷的DC幅度,窗口结束时取平均值,再算R值和SpO2。

R = (AC_red_avg / DC_red_avg) / (AC_ir_avg / DC_ir_avg); spo2 = 110.0f - 25.0f * R; if (spo2 > 100) spo2 = 100; if (spo2 < 70) spo2 = 0; /* 无效值 */

这里有个细节:MAX30102的FIFO里红光和红外是交替存放的,读出来之后要按帧拆开,红光和红外数据一一对应,合并成结构体再进流水线。我见过不少代码在拆帧时搞错顺序,导致红光红外波形完全不同步,SpO2算出来永远是个离谱的数,这个问题很隐蔽,排查了很久才发现是帧拆分顺序反了。

再次强调,这个110减去25R的公式是经验近似,在血氧正常段(95%以上)表现还不错,但到了90%以下误差会放大,DIY系统不要指望它做缺氧诊断,只做趋势参考。

4.5 示波法血压算法:充气、放气与波形包络提取

血压测量是一套独立的状态机,状态依次是:空闲、充气、保压、放气、算法计算、显示结果。

充气阶段:启动气泵,ADC以100Hz采样压力值,用DMA环形缓冲存储。压力达到设定值180mmHg时关闭气泵,进入保压状态约2秒,让袖带压力平稳。然后打开电磁阀开始放气,放气速度通过调整电磁阀的PWM占空比控制,目标每心跳下降3~5mmHg。这个控制逻辑我在代码里用简单的PID实现,PWM占空比基准值靠实验整定。

放气过程中,压力原始信号包含两个成分:低频的袖带压力(主要成分)和叠加在其上的高频动脉震荡波。算法上用一个带通滤波器把两者分开:0.5Hz以下作为压力基线,0.5到10Hz作为震荡波信号。震荡波在每200ms窗口内取峰值,记录对应的袖带压力,形成一个"压力-震荡幅度"的数据点序列。放气持续到压力降到50mmHg以下,关闭电磁阀,进入算法计算。

包络提取后,先找到幅度最大的点,对应MAP;然后在最大点之前找幅度首次超过50%峰值的位置,对应收缩压;在最大点之后找幅度下降到75%峰值的位置,对应舒张压。

/* 寻找包络最大值 */ map_index = argmax(envelope); map_pressure = pressure[map_index]; /* 收缩压:最大点之前第一次达到50%峰值 */ for (i = map_index - 1; i > 0; i--) { if (envelope[i] < 0.50f * max_amp && envelope[i+1] >= 0.50f * max_amp) { sys_pressure = pressure[i]; break; } } /* 舒张压:最大点之后第一次下降到75%峰值 */ for (i = map_index + 1; i < n; i++) { if (envelope[i] > 0.75f * max_amp && envelope[i+1] <= 0.75f * max_amp) { dia_pressure = pressure[i]; break; } }

这个算法的校验我做了好几轮。第一轮直接拿算法结果和水银血压计对比,发现收缩压普遍偏高10mmHg,原因是放气速度太快,包络不够完整。把放气速度降下来之后,误差缩小到±8mmHg以内。另外还在代码里加了两个保护:压力超过220mmHg立即打开电磁阀强制放气;测量时间超过120秒强制结束并放气,防止袖带长时间压迫手臂造成伤害。

5. 报警策略与交互体验:阈值、蜂鸣器与OLED界面

5.1 报警阈值怎么定:参考临床指南但保持可配置

报警是这套系统最核心的增值功能,阈值设计不能拍脑袋。我参考了成人静息状态的常见判断范围,先给了默认值,然后在设置菜单里允许用户自由修改:

参数正常范围默认报警下限默认报警上限
心率60~100 bpm50 bpm120 bpm
血氧饱和度95%~100%90%
收缩压90~140 mmHg90 mmHg140 mmHg
舒张压60~90 mmHg60 mmHg90 mmHg

报警逻辑要分级:轻度超限(比如心率105)只闪烁LED提示,重度超限(心率低于50或血氧低于90)才触发蜂鸣器持续鸣叫。这样做是为了避免老人被频繁的报警声吓到,也是实际使用体验里很重要的一点。报警触发后屏幕上会显示具体的异常参数和数值,按下任意按键可以静音,但参数仍然超限时每隔30秒会再次提示。

5.2 交互逻辑与OLED界面:三个按键搞定所有操作

人机交互用一块0.96寸OLED加三个按键:确认键、上翻键、下翻键。主界面显示心率和血氧两个大数字,下方显示上次血压测量结果。按下确认键进入菜单,菜单项包括:测血压、历史记录、阈值设置、系统信息。

OLED用SSD1306驱动库,I2C刷新率在50帧左右,显示常规数字绰绰有余。血压测量过程中要显示进度状态:"正在充气"、"正在放气"、"正在计算",让用户知道设备在工作,而不是死机了。这里有个细节,测量期间不要频繁刷新整屏,否则I2C占用总线时间过长,会影响MAX30102的数据读取。我的做法是只在状态切换时刷新整屏,测量过程中只更新中间一小块区域的数字。

历史记录可以存最近30条血压数据,写入AT24C02外部EEPROM里,断电不丢失。每条记录包含收缩压、舒张压、脉率和时间戳。时间戳靠STM32内部RTC,加一个纽扣电池座供电,这样断电后时间也不会丢。

5.3 报警与安全:硬件保护比代码保护更可靠

报警器用被动蜂鸣器,通过TIM3 PWM输出不同频率:正常测量完成响一声短音,异常报警响三声长音,按键操作响一声短音。被动蜂鸣器的好处是可以编程控制音调和时长,提示信息更丰富。

这里要单独讲一下安全保护逻辑。血压测量涉及袖带压迫,如果电磁阀故障或者放气通道堵塞,袖带压力一直降不下来,轻则用户难受,重则有安全风险。硬件上我用了常开型电磁阀,断电时阀门是打开的,意味着即使主控死机、程序跑飞,电磁阀也会自动放气,不会出现袖带一直勒着的情况。软件上再加压力上限保护和超时保护,双保险。这一点做医疗相关DIY项目一定要记住:硬件层面的失效安全设计永远比软件可靠。

6. 全套元件清单与成本核算:照着买就能打样

6.1 完整BOM清单

全套元件清单是按单台设备计算的,所有器件都能在常用电子商城买到。表中价格是参考价,散买会比套件略贵:

序号元件名称规格说明数量参考单价
1STM32F103C8T6LQFP48, 64KB Flash18元
2MAX30102模块血氧心率传感器模块115元
3XGZP6847压力传感器模拟输出, 0~40kPa110元
4微型气泵5V隔膜泵, 压力≥100kPa112元
5常开型电磁阀5V, 两位两通18元
6血压袖带成人标准袖带, 带气管110元
70.96寸OLEDSSD1306, I2C接口18元
8AT24C02EEPROM存储历史记录11元
9被动蜂鸣器5V无源蜂鸣器11元
10AO3400 N-MOS管气泵驱动10.5元
11S8050三极管电磁阀/蜂鸣器驱动20.2元
12AMS1117-3.3稳压芯片10.5元
138MHz晶振无源晶振10.5元
14按键6mm轻触开关30.2元
15电阻电容0402/0603散件套装一套10元
16排针排母2.54mm若干3元
17PCB打样5cm×8cm, 5片120元

如果不算PCB和人工,单个物料成本大约在100元左右,加上打样和邮费,一套做下来150元以内能搞定。对比市面上带蓝牙和APP的血压计,这个成本不算高,关键是自己能改代码、能加功能。

6.2 成本与替代方案:预算不够怎么办

如果预算紧张,有几个地方可以降成本。OLED可以换成0.96寸的白底蓝字版本,便宜两三块钱;AT24C02可以用STM32内部Flash模拟EEPROM存储,省掉一颗芯片,但要注意Flash擦写寿命只有1万次,存血压记录足够用;气泵如果实在买不到,可以用手动血压计的气囊代替,测量时人工捏球充气,虽然自动化程度低了,但不影响验证算法。

反过来,如果想升级,两个方向值得考虑:一是加一个ESP8266或者HC-05蓝牙模块,把数据推送到手机APP,老人戴着设备,子女在手机上实时看数据,这个功能在家庭健康场景里非常加分;二是加一个温度传感器和跌倒检测(加速度计),做成更完整的老人健康终端。硬件接口在原理图上都预留了,后续扩展就是加模块编程的事。

7. 打样调试实录:噪声、校准与稳定性那些坑

7.1 PPG信号噪声大:先查手指,再查供电,最后查布局

MAX30102的信号问题是最常见的调试噩梦。我调试时遇到的典型现象是:波形幅度忽大忽小,或者明明静止状态波形里还带一堆毛刺。排查顺序很有讲究,别一上来就改代码滤波参数。

第一,检查手指贴放。传感器要和手指皮肤贴合但不压死,手指不要悬空,最好自然放置在桌面上。手指按压过紧会阻断血流,信号反而变小;过松则环境光漏进来,造成周期性干扰。第二,检查环境光。MAX30102虽然内置了环境光消除电路,但强光直射下信号还是会变差,用黑色遮光胶带在传感器周围围一圈,效果立竿见影。第三,检查供电。LED脉冲电流能达到几十毫安,如果3.3V电压纹波大,PPG信号上会叠加明显的工频噪声。最后才是改软件滤波,把滑动平均窗口从5加到9,给信号通路加一个50Hz陷波。

还有一个容易被忽略的点:MAX30102模块的I2C地址冲突问题。如果板上还有其他器件用了0x57地址,通信就全乱了。用I2C扫描程序跑一遍,确认总线上只有0x57和0x3C两个地址,再往下调。

7.2 血压测量校准:两次校准,误差从15mmHg压到8mmHg

血压部分的校准是这套系统里最花时间的地方。XGZP6847虽然是工厂标定过的,但输出存在零位偏置和增益误差,直接用会带来10mmHg以上的系统误差。

校准分两步。第一步是零位校准:上电后在没有连接袖带、压力为大气压的状态下,读ADC值作为零位基准,每次测量前都重新采集一次零位,消掉温漂。第二步是增益校准:把设备连接到水银血压计或者一个经过验证的电子血压计上,同时给袖带加压到不同压力点(比如60、100、140、180mmHg),记录每个点的ADC值,做一元线性回归,得到ADC值到mmHg的换算系数。

校准公式我贴一下,很有用:

pressure_mmHg = (adc_raw - zero_offset) × k_gain k_gain = (P_ref2 - P_ref1) / (adc2 - adc1)

校准完之后,我用同一个人的左臂和右臂同时测量对比(左边用自研设备,右边用标准电子血压计),连续测量五组,收缩压误差从最初的15mmHg降到8mmHg以内,舒张压误差在6mmHg以内。注意,两臂血压本身就存在生理差异(通常差5~10mmHg),对比时左右手要交替一轮再取平均,才能得到比较公平的误差数据。

另外,袖带佩戴位置对测量结果影响极大。袖带下缘要在肘窝上方2~3厘米处,气管对准肱动脉,袖带松紧以能塞进一根手指为宜。测量时手臂和心脏保持同一高度,坐姿放松。这些使用说明要写进配套文档里,否则用户随便一戴,测出来的数据忽高忽低,算法再准也没用。

7.3 气泵启动导致主控复位:电源地规划比硬件堆料更重要

这个问题我在3.4节提到过,这里说下完整的排查过程。最初测试时,只要一按测量按键启动气泵,OLED就黑屏,串口打印重启信息。查了一圈,刚开始怀疑是电磁干扰触发了复位引脚,加了各种滤波电容都没用,后来用示波器量3.3V电压才真相大白:气泵启动瞬间电流拉低5V电源,AMS1117输出跟着掉到2.7V左右,低于STM32的工作电压下限。

解决思路不是盲目加电容,而是

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询