
简介本资源是一套面向本科毕业设计与嵌入式初学者的STM32健康监测系统完整开发资料聚焦心率、血氧、血压三参数实时采集与异常报警功能解决可穿戴医疗设备原型快速复现难题。压缩包共284个文件含44个C源码文件如stm32f10x_adc.c、stm32f10x_i2c.c等底层驱动、47个编译中间文件.d/.o、45个头文件.h及原理图.schdoc、工程配置.uvprojx/.uvoptx、Hex固件、元件清单.xlsx和说明文档.docx/.pdf全面覆盖硬件设计、传感器驱动MAX30102MSP20、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布局敏感新手照着参考设计打样很容易出现信号差的问题现在已经不推荐新设计使用了。MAX30102MAX30100的升级版只保留红光和红外光信号质量大幅提升封装更小功耗也更低是当前DIY血氧仪的主流选择。市面上大量现成的MAX30102模块板子自带电平转换和滤波电容接上I2C就能用非常省事。分立红外对管方案需要自己设计跨阻放大器、带通滤波器、巴特沃斯滤波光信号调理电路就要花掉一大块面积。好处是成本低、原理图可以画得很唬人但调试周期长到让人崩溃真心不建议新手碰。我的结论很直接用MAX30102模块。一颗芯片同时解决心率、血氧两个参数的采集I2C接口代码库成熟网上资料多到看不完后续出了问题也好排查。2.2 PPG信号的血氧计算R值到底怎么算血氧饱和度的原理说起来是本科生物医学工程的内容还原血红蛋白对660nm红光吸收强氧合血红蛋白对940nm红外光吸收强。当血液中的氧合比例变化时两种波长的吸收比会发生改变通过这个差异反推SpO2。具体计算用比值R法。PPG信号分成两部分直流分量DC来自组织、静脉血和毛细血管的静态吸收交流分量AC来自动脉血的脉动变化。分别算出红光的AC/DC值和红外光的AC/DC值两者相除得到RR (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 Flash20KB RAM2个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做SCLPB7做SDA。两个器件地址不同MAX30102的7位地址是0x57写地址0xAESSD1306的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.58V300mmHg对应约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); /* 模式配置0x03SpO2模式红红外同时工作 */ max30102_write_reg(0x0D, 0x03); /* SpO2配置采样率200HzADC量程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[i1] 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[i1] 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的工作电压下限。解决思路不是盲目加电容而是本文还有配套的精品资源点击获取