news 2026/8/31 17:17:57

基于STM32的示波法电子血压计设计与实现全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于STM32的示波法电子血压计设计与实现全流程解析

简介:本资源是一套完整的基于STM32的脉搏电子血压计高分毕业设计项目,面向电子信息、嵌入式系统及相关专业本科生,解决课程设计、期末大作业及毕业设计中硬件驱动开发、生理信号采集与算法实现等核心实践难题。压缩包共161个文件,含35个C源码(主控逻辑与传感器驱动)、34个H头文件(外设配置与函数声明)、22个编译中间文件(.d)、21个调试符号文件(.crf)及Keil工程文件(uvproj、uvopt等),总大小仅2.22MB,结构规范、模块清晰,涵盖ADC采样、FFT脉搏分析、袖带加压控制、OLED人机交互等完整功能链。已有182人学习下载,项目经实机严格调试,代码全程中文注释,含系统初始化、血压计算模型、数据存储与显示等关键模块,可直接部署运行,无需额外适配,特别适合嵌入式初学者快速掌握STM32外设协同开发与医疗电子系统集成方法。 做毕业设计选嵌入式方向,最容易“出彩但不作死”的题目,其实不是追热门的人工智能,也不是看起来复杂的四轴无人机,而是一台踏踏实实能做出来、能测出真实数据的医用小设备——基于STM32的脉搏电子血压计。

这项目麻雀虽小五脏俱全:压力传感器、信号调理电路、ADC采样、数字滤波、症状识别算法、气泵阀控制、人机交互,一个不落。STM32在这类项目里几乎是标配,网上相关源码也确实不少,但很多同学下载下来根本跑不通,或者跑通了测出来的数据完全没法看。今天就把我从整体架构到信号处理算法,再到调试阶段踩过的坑,完整捋一遍。这篇东西既适合正在准备毕设答辩的在校生,也适合想低成本入门嵌入式医疗设备开发的工程师参考。

1. 这个项目到底在做什么:一条完整测压链路的设计思路

1.1 示波法测血压的原理,以及为什么选它

电子血压计的核心不是“测血压”,而是“通过袖带压力变化反推血压”。目前主流方案有两种:听诊法(柯氏音法)和示波法(振荡法)。

听诊法在临床上被当作金标准,但它是靠人耳分辨袖带放气过程中血流冲击血管壁产生的“柯氏音”来判定收缩压和舒张压。难点在于需要拾音传感器、需要精确找到第一声和最后一声音,算法上极度依赖特征检测,做不好就容易误判。毕设里做这个方案风险不小。

示波法的思路完全不同:袖带在放气过程中,内部压力不仅会缓慢下降,还会叠加一个随心跳搏动的小信号。这个“脉搏波”的幅度会随着袖带压力变化走一条先增后减的曲线。当袖带压力高到完全压闭血管时,脉搏波幅值几乎为零;压力逐渐降低时,血流冲击血管壁的力度增大,脉搏波幅度上升;当袖带压力正好等于平均动脉压时,血管壁处于最“松弛”状态,脉搏波幅度达到峰值;压力继续下降后,血管完全开放,脉搏波幅度减小并趋于稳定。

通过分析脉搏波包络线,可以找出三个关键点:包络峰值对应的压力是平均动脉压,峰值之前幅度上升到最大值的50%左右对应的压力是收缩压,峰值之后幅度下降到最大值的70%左右对应的压力是舒张压。

这个算法看似简单,但做出来之后,我最大的感受是:它不需要高精度拾音传感器,不需要复杂的时频分析,只要压力传感器线性度足够、ADC采样率够、放气速度控制得当,就能得到稳定可读的结果。对于嵌入式新手来说,这几乎是性价比最高的方案。

1.2 STM32在这个项目里的角色定位

STM32扮演的角色通俗讲就是“大脑加管家”。它既要控制气泵充气、电磁阀放气,又要以固定频率读取压力传感器的ADC值,还要在采集过程中实时处理数据,最终把测量结果送到OLED屏幕上显示。

为什么选STM32而不是51单片机或者Arduino?答案很实在:51的处理能力和外设资源太紧张,做20Hz以上的AD采样加上滤波算法就已经很费劲;Arduino虽然上手快,但ADC精度和定时器资源无法精细控制,而且教授和评委对这种“拼积木”式的开发方式通常不太认可。STM32F103系列主频72MHz、12位ADC、多个定时器、丰富的中断资源,应付这种数据采集加实时处理的任务绰绰有余,而且资料极为丰富,遇到问题一搜就能找到答案。

我用的是STM32F103C8T6这颗经典芯片,成本低、引脚够用、功耗也不高。核心板加外围电路一共不到三十块钱成本,对毕设预算非常友好。

2. 硬件部分:传感器选型与驱动电路,踩坑从这里开始

2.1 压力传感器的选型:国产与进口的取舍

血压计对压力传感器的要求集中在两方面:量程和线性度。人体血压范围一般在0到200mmHg,袖带充气时压力会更高一些,所以我选择量程在0到300mmHg以上的传感器比较稳妥,留出余量。

市面上常见的方案有几种,我放在一张表里对比:

传感器型号输出方式量程优点缺点
MPX5050GP模拟电压输出0~375mmHg集成度高、线性度好、经典方案价格略高、体积稍大
XGZP6847I2C/模拟输出可选0~300mmHg国产、便宜、容易买到一致性一般,需要标定
血压计专用传感器模块模拟电压输出0~300mmHg厂家已校准、直接可用成本较高,资料少

我最后选了MPX5050GP,原因是它的输出电压范围是0.2V到4.7V,对应0到375mmHg,线性误差在全量程范围内不超过2.5%,而且自带温度补偿。这个传感器的原理是内部集成了一个压阻式压力桥和放大电路,外部供电5V即可,输出阻抗较低,可以直接驱动ADC输入引脚。

实际画板子的时候有个小经验:压力传感器的气管接口方向和PCB布局方向一定要提前规划好,不然装进外壳之后气嘴方向和袖带气管对不上,就得重新飞线,很狼狈。

2.2 气泵、电磁阀、电源:容易被忽视的“外围设备”

很多人把注意力放在传感器和单片机上,结果三个月后卡在气泵上。这个项目里气泵和电磁阀才是真正的“耗材级”部件,选型不当直接导致整机测量失败。

气泵负责给袖带充气。我用的是一种微型隔膜泵,额定电压5V,最大输出气压能到300mmHg以上。启动电流比较大,能达到400mA左右,所以不能用STM32的GPIO直接驱动,必须通过三极管或MOS管扩流。我用的方案是NPN三极管2N2222加续流二极管,配合一个PWM通道控制充气速度。

电磁阀负责放气。常见的有两种:两通开关阀和比例阀。开关阀只有“开”和“关”两种状态,控制简单,但放气速度没法精确调节;比例阀可以通过PWM占空比控制开度,从而控制放气速率,但价格贵一些。我一开始图便宜用了开关阀,结果放气速度太快,一个心跳周期内压力降幅太大,包络线采样点稀疏到无法正确找到峰值。后来换成比例阀,配合PWM慢慢放气,数据才稳定下来。

电源树也要引起重视。整个系统里有数字电路(STM32)、模拟电路(传感器放大)、感性负载(气泵、电磁阀),如果不做隔离,气泵启动瞬间会把电源电压拉低,导致模拟电路输出抖动。我的做法是:主电源用两节18650锂电池串联经过降压模块稳定到5V,再用AMS1117把5V降到3.3V给STM32供电。模拟部分和数字部分的地线在PCB上单点汇合,避免形成地环路。

2.3 信号调理电路:传感器的微弱信号如何进ADC

压力传感器输出的模拟电压虽然已经是放大后的,但还是要经过一级低通滤波才能送入STM32的ADC。原因是传感器输出中包含了高频噪声,直接进ADC会导致采样值跳动明显。

我用了一阶RC低通滤波,R取1kΩ,C取0.1μF,截至频率约1.6kHz。这个频率远高于脉搏波频率(正常人心率范围是0.5Hz到3Hz),所以对有用信号完全没有影响,但对高频毛刺的抑制非常明显。如果想做得更讲究,可以加一级由运算放大器构成的二阶低通滤波器,比如Butterworth结构,截至频率设置在20Hz左右。

ADC参考电压也很关键。STM32内部有VREF引脚,如果直接用3.3V电源做参考电压,电源纹波会被直接引入到转换结果中。我使用了一个高精度基准电压源芯片REF3033,把3.3V参考电压做得干干净净。这一改动效果非常显著,ADC的跳动范围直接从几十个LSB降到了两三个LSB。

ADC配置方面,我使用定时器触发ADC采样,频率设置为200Hz,也就是每5ms采一次。这个频率经过实际测试,既能捕捉到脉搏波的细节形状,又不会让CPU忙于处理中断。采样使用了DMA方式,ADC转换完成的数据自动搬运到内存数组中,不占CPU时间。这样主循环可以专心处理控制逻辑和算法运算。

3. 软件实现:从ADC采集到血压算法全流程

3.1 程序整体架构与状态机设计

嵌入式程序最忌讳把所有逻辑堆在一个大while循环里,血压计程序尤其如此。我用一个有限状态机来组织整个测量流程,状态之间的切换非常清晰。

状态定义如下:

  • IDLE(空闲):等待启动按键,此时气泵和电磁阀都关闭
  • INFLATE(充气):启动气泵,持续检测袖带压力,升压到预设值后停止
  • MEASURE(测量):关闭气泵,打开比例阀控制放气速度,同步记录压力和脉搏波
  • FINISH(结束):完成测量,计算并显示结果,快速放气
  • ERROR(错误):检测到超时或异常压力,安全放气后回到空闲状态

状态机的好处在于:每个状态内的代码职责清晰,后续加功能(比如存储多组记录、蓝牙上传数据)时不会牵一发动全身。而且答辩的时候老师问起代码结构,状态机这个思路本身就是评分亮点。

充气预设值是动态计算的。我设定默认目标压力为180mmHg,但在充气过程中会实时检测脉搏波是否开始出现。如果压力还没到180mmHg就已经出现明显的脉搏波,说明用户血压可能较低,可以提前停止充气,避免袖带过度压迫用户手臂。这是从商用血压计上学到的策略,虽然不是所有电子血压计都这么做,但确实能提升使用体验。

3.2 数字滤波与脉搏波提取

ADC采到的原始数据是一个一维序列,里面混合了两种成分:一条缓慢下降的基线,也就是袖带压力本身;还有一条叠加在基线上的波动,也就是脉搏波。要把这两者分离,我用的是“低通滤波加减法”的思路,简单有效。

第一步,用滑动平均滤波对原始ADC序列做平滑处理,窗口大小设为5个点(对应25ms),用来去除随机噪声。第二步,再用一阶低通数字滤波器提取基线,滤波系数a取0.2,这样基线跟随比较快,能实时反映袖带压力变化。第三步,原始信号减去基线信号,得到的就是包含脉搏波的交流分量。

脉搏波虽然出来了,里面还是混着一些呼吸引起的慢波和肌肉抖动的干扰。算血压之前还需要再做一次带通滤波。我用了一个简单的二阶IIR带通滤波器,通带设置为0.5Hz到8Hz。下限滤掉呼吸干扰,上限滤掉高频肌肉伪差。

说到这里必须提醒一下:IIR滤波器的系数计算对新手来说有点门槛,别自己硬推导,直接用MATLAB的fdatool或者在线滤波器设计工具生成系数。我当年直接在MATLAB里设计好系数,然后把它填入C语言数组里,省了不少时间。注意浮点运算在STM32F103上虽然能跑,但速度慢,而且占用Flash很大。我的做法是先用浮点算好系数,再把整个滤波器实现改为定点化,牺牲一点动态范围换取运行速度。

3.3 示波法算法的具体实现与参数标定

脉搏波提取出来之后,接下来的工作就是找包络线。我在代码里维护了一个结构体数组,每个元素包含一个压力值和对应的脉搏波幅度。具体实现是:对每个放气阶段的脉搏波信号取极值,把相邻峰值的差值作为该压力点的脉搏波幅度。

关键算法代码如下,这个函数直接完成了从压力数组和幅度数组到收缩压、舒张压的映射:

// 示波法核心算法:寻找包络峰值并计算收缩压与舒张压 // pressure[]: 袖带压力数据,单位mmHg // amplitude[]: 对应压力点的脉搏波幅度 // count: 数据点数量 // sbp: 输出收缩压 // dbp: 输出舒张压 uint8_t oscillometric_alg(uint16_t *pressure, uint16_t *amplitude, uint16_t count, uint16_t *sbp, uint16_t *dbp) { uint16_t max_idx = 0; for (uint16_t i = 1; i < count; i++) { if (amplitude[i] > amplitude[max_idx]) { max_idx = i; } } // 平均压直接取包络峰值对应压力 uint16_t map_pressure = pressure[max_idx]; // 收缩压:从峰值往前找,幅度首次降到峰值50%的位置 uint16_t sbp_thr = amplitude[max_idx] / 2; uint16_t sbp_idx = 0; for (int i = max_idx; i > 0; i--) { if (amplitude[i] <= sbp_thr) { sbp_idx = i; break; } } // 舒张压:从峰值往后找,幅度首次降到峰值70%的位置 uint16_t dbp_thr = amplitude[max_idx] * 7 / 10; uint16_t dbp_idx = 0; for (uint16_t i = max_idx; i < count; i++) { if (amplitude[i] <= dbp_thr) { dbp_idx = i; break; } } *sbp = pressure[sbp_idx]; *dbp = pressure[dbp_idx]; return 0; }

这段代码隐藏了一个细节:阈值系数0.5和0.7不是随便写的。它们对应的是商用血压计中最常用的固定比例系数,但不同人群的血压特征不一样,比如老年人血管弹性较差,包络比阈值应该适当调整。我在实际调试中发现,0.5和0.7的默认值在20到30岁的测试者身上表现良好,但对中老年测试者,收缩压阈值取0.45更准确。

标定的方法是:用一台水银血压计或商用电子血压计作为参照,分别测量同一人多个时间点的血压,记录自己的算法输出和参照输出的差值,然后做线性回归,修正阈值参数。毕设论文里如果能把这个标定过程写进去,内容的含金量会立刻不一样。

算法还有一个边界情况要处理:如果放气过快,包络线峰值附近的采样点太少,有可能峰值被漏掉或者位置偏移。所以我在算法前会做一次插值处理,用三次样条把脉搏波幅度序列插值成两倍密度的数组,这样峰值定位的精度就有保障了。

4. 调试日记:我在这个项目里踩过的坑与排查思路

4.1 ADC数值飘得离谱,先检查电源和地

第一次把硬件焊好上电的时候,我遇到的情况是:传感器静止不动,ADC读数却在500到1200之间大幅跳动。当时第一反应是传感器坏了,后来用万用表量了一下传感器输出引脚,发现模拟电压确实也在跳,但幅度没有ADC读数那么夸张。

排查了一圈,问题出在两个地方。第一个是电源:我用USB供电,USB的5V经过AMS1117降到3.3V,但AMS1117后面没有加足够大的滤波电容。气泵不工作时还好,气泵一转,电流骤增,电源上就会叠加一个很大的纹波。解决方案是在3.3V和GND之间加了一个470μF的电解电容和一个0.1μF的陶瓷电容,纹波立刻降下来了。第二个是参考电压:直接用VDD做VREF,芯片内部没有隔离,电源纹波直接进入ADC。解决方案是换用REF3033基准源,这个改动效果立竿见影。

所以调试ADC类的项目,先别急着调软件,拿示波器看看电源轨的纹波、看看传感器输出端是否有毛刺,往往能省下几个小时的时间。

4.2 脉搏波时有时无,问题可能出在袖带和放大

软件写好之后,第一次完整测量发现脉搏波提取结果非常不稳定:有时候能看到明显的脉搏波,有时候整段数据清一色都是直线。我仔细检查了代码逻辑,排除了算法问题,最后发现是袖带的问题。

电子血压计袖带和普通医用袖带略有区别,里面其实是一根连接气管的橡胶气囊。气囊通过气管与压力传感器相连,袖带绑在手臂上充气后,气囊内的空气压力变化会通过气管传到传感器。如果袖带绑得太松,气囊和手臂之间存在空隙,血管搏动就无法有效地传递到气囊内部,导致传感器只能测到气压而测不到搏动。

解决方案也简单:绑袖带时确保气囊中心正好压在肱动脉上方,松紧程度以“能插进一根手指”为准。这个经验需要在操作指南里写清楚,因为测试者的使用方式直接影响测量结果。

另外,如果传感器输出经过放大电路之后脉搏波幅度仍然很小,比如只有几十毫伏,也不要急着改放大倍数。先用示波器确认传感器直接输出端有没有搏动信号,如果有,说明放大电路的地线布局有问题;如果没有,说明传感器连接的气路有漏气,或者袖带放置不正确。

4.3 测量结果与商用血压计对比偏差大,参数怎么调

有一段时间,我的设备测出来的收缩压总是比商用血压计高15到20mmHg。这种整体偏差不是随机误差,而是系统误差,基本可以确定是算法参数的偏差。

按照前面讲的方法,我找了5个测试者,分别用我的设备和商用设备测了30组数据,然后跑了一次简单的统计分析。发现如果收缩压阈值从0.5降到0.45,偏差明显缩小到±5mmHg以内。舒张压阈值从0.7改成0.72后,也获得一定改善。

调参的过程不复杂,但需要耐心。我写了一个上位机辅助脚本,通过串口把每次测量的压力序列和脉搏波序列导出来,在Python里离线分析,这样能快速迭代算法参数,而不用每次烧录代码重新测一遍。如果你用的是Keil开发环境,用串口打印原始数据再放到脚本里分析,完全可以复刻这个流程。

还有一个容易忽略的问题:测量时用户的手臂位置。如果手臂与心脏不在同一高度,水银血压计的读数本身就会出现偏差,电子血压计也是同理。我最初测试时手放在桌上和手举高贴身,读数差异能达到10mmHg以上。确保所有对比测试的姿势一致,数据的可比性才成立。

这里列出我在调试后期建立的一个排查速查表,方便对照:

问题现象可能原因检查方法解决措施
ADC数值跳动大电源纹波,VREF不稳示波器查看3.3V纹波加大滤波电容,改用基准源
脉搏波幅度小袖带漏气或位置不对检查气路是否密封调整袖带,更换气管
脉搏波不定期消失放气速度过快检查放气阀控制波形改用PWM控制比例阀
收缩压整体偏高算法阈值不合适与标准设备对比记录降低收缩压阈值
测量结果不稳定测量姿势不一致规范测量动作固定姿势,连续测3次取平均
充气超时气泵老化或气管漏气听气泵声音,检测压力上升速度更换气泵,重新连接气路

5. 毕设资料整理清单:让这份代码真正成为你的加分项

5.1 源码工程应该怎么组织

很多同学拿到项目直接Ctrl+C、Ctrl+V,到头来自己都没读过一遍代码,这种方式即使通过了查重也过不了答辩。真正的做法是理解每一段关键代码的用途,然后重构工程目录。

我建议的工程目录结构如下:

Project/ ├── Core/ │ ├── Inc/ // 头文件 │ └── Src/ // 主函数、中断、系统时钟配置 ├── Hardware/ │ ├── Sensor.c // 压力传感器驱动 │ ├── Pump.c // 气泵控制 │ ├── Valve.c // 电磁阀控制 │ ├── OLED.c // 显示屏驱动 │ └── Key.c // 按键扫描 ├── Algorithm/ │ ├── Filter.c // 数字滤波实现 │ └── BP_Calc.c // 示波法血压算法 ├── Data/ │ └── history.c // 数据存储 └── main.c

代码风格上,每个函数前面都写了简洁的函数说明和输入输出参数说明,全局变量尽最大努力减少,模块间的接口用函数封装。这样做不仅答辩时加分,后续扩展蓝牙模块或者增加WiFi传输时也会方便很多。

5.2 从原理图到论文:一份完整毕设资料的构成

一个能拿高分的毕设,交付的绝不仅仅是能跑的代码。完整的资料包至少包含六项:源码工程、原理图、PCB文件、毕业设计说明书、演示视频、答辩PPT。

原理图我用的Altium Designer画的,但如果你更熟悉立创EDA也可以,关键是所有元器件都标注清楚封装和数值,方便别人照着购买和焊接。PCB走线要特别注意:模拟信号走线要远离气泵和电磁阀驱动的功率走线,晶振下方不要走任何信号线。

毕业设计说明书的结构,我建议这样组织:第一章绪论写国内外电子血压计研究现状;第二章讲示波法测量原理和系统总体设计;第三章写硬件电路设计,包括传感器选型和驱动电路;第四章写软件设计,包括滤波算法和核心血压判定算法;第五章写系统测试与误差分析。论文里贴几张示波器抓到的脉搏波图和OLED显示的测量结果图,很有说服力。

答辩时老师最喜欢问的几个问题包括:为什么选示波法不选听诊法?压力传感器量程为什么选这个?滤波器的截止频率是怎么确定的?算法中0.5和0.7比例系数为什么是这个值?这些问题我在正文里都已经给出了答案,把原理吃透,答辩时就能稳住。

5.3 演示环节的准备:让评委两分钟看懂你的项目

毕设答辩的演示环节时间有限,我的做法是提前录一段两分钟左右的视频:把袖带绑在测试者手臂上,按下开始键,能看到OLED屏上显示“充气中”,然后是一段实时压力变化曲线,最后显示收缩压、舒张压和心率。同时用一台商用血压计同步测量,做一次直观对比。这个对比的数据会直接展示误差范围,比任何口头说明都有说服力。

如果现场条件允许,也可以让评委实际操作一次。不过要注意血压计是医疗设备,需要提前说明使用禁忌和注意事项,并且征得对方同意后再操作。

写在最后,关于这个项目的一点个人体会

做完这个项目之后,一个明显的感受是:嵌入式医疗设备真正的技术含量并不在硬件本身,而在信号处理与算法参数取舍这些“看不见”的地方。血压计看起来简单,但要做到准确、稳定、可重复,需要下不少功夫。

另外想多说一句,做毕设千万不要只做个“代码搬运工”。把示波法原理彻底弄懂、亲手跑过一遍数据、体验过从波形异常到找到原因的过程,这些经历才是毕设真正留给你的东西。代码可能过两年就忘了,但排查问题的思路和对一个完整闭环系统的掌控感,会伴随你进入下一阶段的工作或学习。以后再做类似的设备,哪怕是心率计、血氧仪,你会发现核心的思路是相通的。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 17:16:59

Claude Code 安装与机械臂开发实战:从正逆解到 MuJoCo 仿真

最近技术社区流传着一个很有戏剧性的场景&#xff1a;Claude 在关键时刻控制机械臂&#xff0c;拦住了一笔 5000 万美元的打款。如果只看标题&#xff0c;很多人第一反应是“AI 终于要接管物理世界了”。但我的判断是&#xff0c;这个场景的新闻价值远大于技术价值&#xff0c;…

作者头像 李华
网站建设 2026/8/31 17:14:15

基于CNN的锂电池剩余寿命预测:MATLAB实现与工程实践

简介&#xff1a;本资源是一套面向电池健康状态预测研究者与MATLAB深度学习初学者的完整实践方案&#xff0c;聚焦锂电池剩余使用寿命&#xff08;RUL&#xff09;建模这一典型时序回归任务。代码基于CNN卷积神经网络构建端到端预测流程&#xff0c;涵盖B0005/B0006电池数据导入…

作者头像 李华
网站建设 2026/8/31 17:09:40

R语言混合效应模型全流程:从线性回归到GAM的进阶指南

拿到一份多站点、多时间点的观测数据&#xff0c;很多人的第一反应是直接跑一个lm()&#xff0c;然后看每个变量的显著性。但这个流程在数据存在明显的嵌套结构时&#xff0c;往往会在第一步就埋下隐患&#xff1a;同一站点的重复观测并不独立&#xff0c;样本量被高估&#xf…

作者头像 李华
网站建设 2026/8/31 17:07:43

声纹识别项目实战:从GMM到x-vector全方案解析与调参经验

简介&#xff1a;这是一套面向本科毕业设计、课程设计与初学者项目开发的Python声纹识别完整实践资源&#xff0c;覆盖从传统统计模型到前沿深度学习方法的主流技术路线。资源包含GMM、GMM-UBM、i-vector等经典算法实现&#xff0c;以及基于深度神经网络的端到端声纹识别方案&a…

作者头像 李华