简介:这份资料面向嵌入式开发初学者与医疗电子爱好者,系统讲解基于单片机的智能电子血压计从原理到落地的完整设计路径,帮助读者理解示波法测量血压的技术实现。压缩包共303个文件、约6.74MB,以C源码、头文件与编译中间文件为主,辅以汇编启动代码、hex烧录文件、工程配置及一份配套APP安装包,覆盖固件开发与上位机联调所需。内容围绕设计概要、工作原理说明、硬件选型(压力传感器、气泵气阀、LCD与蓝牙模块)、固件源码解析及APP数据管理展开,读者可据此掌握袖带压力采集、血压算法计算与蓝牙通信的实现思路。目前已有987人学习,适合作为课程设计或产品原型的参考蓝本。
1. 从一块万用表和一台样机说起:智能电子血压计设计到底要交什么
带过几届课设和毕设之后,我发现一个规律:选“智能电子血压计”这个题的人,往往是被它的“看起来简单”骗进来的。一个袖带、一个气泵、一块单片机、一个液晶屏,原理图翻两页就画完了,可真正把样机做出来、把数据跑稳、把报告写完整,中间隔着的是一整套设计资料。这份资料要解决的不是“血压计是什么”,而是“从原理图到能演示的样机,中间每一步该怎么做、参数怎么定、误差怎么收”。它适合两类人:一类是要交单片机课程设计或毕业设计的学生,需要一套结构完整、能照着搭的方案;另一类是从业者要快速出一版可交付的血压测量模块方案,需要现成的模块划分和验收口径。示波器抓不到波形、气泵一启动屏幕就花屏、测出来的数值每次都不一样,这些坑我基本都踩过,下面按需求、设计、实现、验收的顺序把整套东西摊开讲。
2. 需求与选型:为什么是“单片机 + 示波法”这条路线
2.1 先把功能需求钉死,别一上来就画图
电子血压计这个题最容易翻车的地方,是需求没定清楚就开始画原理图,画到一半发现要加语音播报、要存历史数据、要蓝牙上传,板子推倒重来。我一般会先拉一张功能需求表,把“必须有”和“可以砍”分开,这样后面选型和画板才有边界。
| 需求项 | 指标要求 | 优先级 | 说明 |
|---|---|---|---|
| 血压测量范围 | 0~299 mmHg | 必须 | 覆盖正常与高血压区间 |
| 压力测量误差 | ±3 mmHg 以内 | 必须 | 静态压力校准后的目标 |
| 心率测量范围 | 40~200 次/分 | 必须 | 与脉搏波同步提取 |
| 加压方式 | 气泵自动加压至预设值 | 必须 | 约 180 mmHg 起测 |
| 显示 | 收缩压/舒张压/心率三值 | 必须 | 段码或点阵液晶 |
| 存储 | 最近 30 组测量记录 | 可选 | 外挂 EEPROM |
| 报警 | 超限声光提示 | 可选 | 蜂鸣器 + LED |
| 通信 | 串口上传上位机 | 可选 | 调试与答辩演示用 |
这张表的作用是给后面的选型定调。比如“±3 mmHg”这一条,直接决定了压力传感器不能用几块钱的电阻式方案,得选带温度补偿的集成压力传感器;而“自动加压”决定了必须有气泵驱动电路和过压保护,不能只靠软件判断。需求表里“可选”的那几项,答辩时是加分项,但不要为了它们牺牲核心测量的稳定性,这是我见过最多的本末倒置。
2.2 主控与传感器的选型逻辑
主控这块,课程设计场景下 51 内核和 STM32 都是常见选择。51 的优势是资料多、上手快、指导老师熟悉,缺点是 ADC 精度和运算能力有限,做滤波和包络提取时比较吃力;STM32 这类 Cortex-M 内核自带 12 位 ADC、定时器资源丰富,跑示波法的包络算法更从容。我的建议是:如果题目只要求“能测、能显示”,51 够用;如果要求“测量稳定、有算法处理、能存数据”,直接上 STM32,别在 51 上硬扛,后期调算法的时间成本远高于换芯片的成本。
传感器是这套方案的核心。示波法测血压,本质是给袖带加压后缓慢放气,同时采集袖带内的压力波动,这个波动里叠加着心脏搏动带来的微小振荡,也就是脉搏波。压力传感器要同时满足两点:量程覆盖 0~300 mmHg,分辨率能分辨出 1 mmHg 以下的压力变化。常用的集成压力传感器内部带惠斯通电桥和温度补偿,输出模拟电压,接单片机 ADC 即可。选型时重点看三个参数:量程、灵敏度(mV/mmHg 量级)、以及是否内置补偿。没有温度补偿的传感器,冬天和夏天测出来的基线会漂,这是玄学问题,其实是温漂。
气路部分需要气泵、电磁阀(或泄气阀)、袖带和一段软管。气泵选微型直流隔膜泵,工作电压与系统电源一致;泄气阀用常闭电磁阀,断电时保持袖带压力,测量结束再打开放气。这里有个血泪经验:气泵和电磁阀都是感性负载,启动瞬间电流冲击大,如果和单片机共用一路电源且滤波没做好,屏幕花屏、ADC 读数跳变都是家常便饭,后面电源章节会专门讲怎么处理。
2.3 技术路线对比:示波法为什么是唯一现实选择
血压测量有听诊法(柯氏音法)和示波法两条路。听诊法靠麦克风或压力传感器识别柯氏音的出现和消失,对应收缩压和舒张压,原理直观,但对麦克风灵敏度、环境噪声、袖带位置极其敏感,做成自动设备难度很高。示波法则是采集袖带压力振荡波的包络,通过包络的幅度变化反推收缩压和舒张压,抗干扰能力强,适合单片机自动处理,市面上绝大多数电子血压计走的都是这条路。
示波法的关键在包络提取。放气过程中,每个心动周期会在袖带压力上叠加一个振荡波,振荡幅度随袖带压力变化,在平均压附近达到最大。工程上的做法是:以固定采样率采集压力信号,用带通滤波去掉直流分量和呼吸基线漂移,得到振荡波;再对振荡波做峰值检测,得到每个周期的幅度序列,这个序列就是包络。收缩压和舒张压通常取包络幅度达到最大值某个比例(经验系数在 0.5~0.7 之间)时对应的袖带压力。这个系数不是拍脑袋,不同厂商有不同标定,课程设计里用 0.6 左右作为初值,再用标准血压模拟器或对比测量去修正,是常规做法。
提示:包络比例系数直接决定测量结果的系统偏差,答辩时如果被问到“你的系数怎么来的”,要能说清楚是参考经验值加实测修正,而不是随便填的。
3. 总体架构设计:四个模块与一条信号链
3.1 系统分层:感知、控制、处理、交互
整套系统我习惯拆成四层:感知层是压力传感器和信号调理电路,负责把袖带压力变成单片机 ADC 能读的电压;控制层是气泵和电磁阀的驱动电路,负责加压、保压、放气的时序;处理层是单片机内部的采样、滤波、包络提取和血压计算;交互层是液晶显示、按键、蜂鸣器和可选的串口上传。四层之间靠明确的接口衔接,画框图时把每层的输入输出标清楚,后面写报告和答辩都能直接用。
信号链是这样的:袖带压力 → 压力传感器 → 差分放大/滤波 → ADC → 数字滤波 → 振荡波提取 → 包络计算 → 血压值 → 显示。这条链上任何一环出问题,最终数值都会飘。比如前置滤波没做好,50 Hz 工频干扰混进来,包络就会毛刺丛生;ADC 参考电压不稳,压力读数整体偏移。所以架构设计阶段就要把每一环的指标分配下去,而不是等出了问题再回头找。
3.2 硬件模块划分与关键接口
硬件上按功能分块画原理图,比一张大图好维护得多。我一般分成电源模块、传感器与调理模块、气泵阀驱动模块、主控最小系统、人机交互模块五块。电源模块负责把电池或适配器电压稳到 3.3 V 和 5 V 两路,传感器和单片机走 3.3 V,气泵走 5 V 或直接走电池电压,两路之间用磁珠或电感隔离。传感器输出接一个 RC 低通加运放跟随,再进 ADC,这个跟随器的作用是提高输入阻抗,避免 ADC 采样时把传感器输出拉偏。
气泵和电磁阀的驱动,不能用单片机 IO 直接推,必须经过三极管或 MOS 管,并且在线圈两端反并联续流二极管,否则断电瞬间的反电动势会把驱动管击穿。这个二极管省掉的代价是烧管子,我见过不止一次。驱动电路和主控之间最好加光耦或至少加限流电阻,把感性负载的干扰挡在单片机外面。
| 模块 | 核心器件 | 接口 | 关键指标 |
|---|---|---|---|
| 电源 | 稳压芯片 + 滤波 | 3.3 V / 5 V 输出 | 纹波小于 20 mV |
| 传感调理 | 集成压力传感器 + 运放 | 模拟电压 → ADC | 量程 0~300 mmHg |
| 气泵阀驱动 | MOS 管 + 续流二极管 | GPIO 控制 | 支持 PWM 调速 |
| 主控 | 单片机最小系统 | ADC/GPIO/UART | 采样率不低于 100 Hz |
| 交互 | 液晶 + 按键 + 蜂鸣器 | 并口或串口 | 刷新不闪烁 |
采样率这一项要特别说。示波法要提取的是脉搏波,正常心率下振荡频率在 1~3 Hz,理论上采样率几十赫兹就够,但为了做数字滤波和峰值定位,实际取 100~200 Hz 更稳妥。采样率太低,峰值位置会偏,包络就不准;采样率太高,单片机存储和运算压力大,51 上尤其明显。100 Hz 是个平衡点,STM32 上可以放到 200 Hz。
3.3 软件状态机:加压、保压、放气、计算
软件的主干是一个状态机,这是整套方案里最值得抄的部分。血压测量的时序性很强,什么时候开泵、什么时候关泵、什么时候开始采样、什么时候算结果,顺序错了测量就废了。状态机大致分五个状态:待机、加压、保压、放气测量、结果处理。
加压阶段,气泵开启,同时监测袖带压力,到达预设值(比如 180 mmHg)或检测到压力上升变缓时停止;保压阶段短暂稳定,让压力读数收敛;放气测量阶段,电磁阀以固定速率放气,同时以 100 Hz 采集压力信号,这是核心数据段;放气结束后进入结果处理,做滤波、包络提取和血压计算;最后显示并回到待机。每个状态设置超时保护,比如加压超过 30 秒还没到预设值,强制停止并报错,防止气路漏气时气泵一直转。
typedef enum { STATE_IDLE = 0, STATE_INFLATE, STATE_HOLD, STATE_DEFLATE, STATE_COMPUTE } bp_state_t; bp_state_t bp_fsm(bp_state_t cur, uint16_t pressure, uint32_t tick_ms) { static uint32_t state_enter_ms = 0; switch (cur) { case STATE_IDLE: if (start_flag) { pump_on(); state_enter_ms = tick_ms; return STATE_INFLATE; } break; case STATE_INFLATE: /* 到达预设压力或超时则转入保压 */ if (pressure >= TARGET_PRESSURE || (tick_ms - state_enter_ms) > 30000) { pump_off(); state_enter_ms = tick_ms; return STATE_HOLD; } break; case STATE_HOLD: if ((tick_ms - state_enter_ms) > 500) { valve_open(); state_enter_ms = tick_ms; return STATE_DEFLATE; } break; case STATE_DEFLATE: /* 放气到低于阈值,采样结束 */ if (pressure < DEFLATE_END_PRESSURE) { valve_close(); return STATE_COMPUTE; } break; case STATE_COMPUTE: bp_calculate(); return STATE_IDLE; } return cur; }这段状态机的关键在几个常量和超时。TARGET_PRESSURE是加压目标,一般取比预期收缩压高 30~40 mmHg,默认 180 mmHg 对多数人够用,但对高血压人群可能不够,工程上可以做成两段加压:先加到 180,若包络还没出现明显峰值,再补压到 200。DEFLATE_END_PRESSURE是放气终点,通常取 40~50 mmHg,低于这个值包络已经衰减完,继续采没意义。超时保护是后悔药,气路漏气、袖带没绑好时,没有超时机制气泵会一直转,既费电又危险。
4. 关键模块实现:从采样到血压值的每一步
4.1 压力信号采集与数字滤波
ADC 采样看起来简单,坑却不少。首先是参考电压,如果用单片机内部参考,精度和稳定性都一般,压力读数会有几个 mmHg 的漂移;条件允许的话用外部基准,或者至少在软件里做一次零点校准——每次开机时读取当前环境压力作为零点,后续读数减去这个零点。其次是采样时序,ADC 采样要和气泵 PWM 错开,否则 PWM 的开关噪声会串进采样结果,表现为读数周期性跳动。
数字滤波分两级。第一级是滑动平均,去掉高频噪声,窗口取 5~10 个点,对应 50~100 ms,不影响脉搏波的低频成分。第二级是带通滤波,目的是把直流压力分量和呼吸引起的慢漂移去掉,只留下 0.5~5 Hz 的振荡波。工程上常用一阶或二阶巴特沃斯带通,用差分方程实现,计算量小,51 也能跑。
/* 二阶带通滤波,中心频率约 2 Hz,采样率 100 Hz */ static float b0 = 0.0675f, b1 = 0.0f, b2 = -0.0675f; static float a1 = -1.8249f, a2 = 0.8649f; static float x1 = 0, x2 = 0, y1 = 0, y2 = 0; float bandpass(float x) { float y = b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; x2 = x1; x1 = x; y2 = y1; y1 = y; return y; }这几个系数是按 100 Hz 采样率、通带约 0.5~5 Hz 设计的,实际使用时可以用工具生成后直接填进来。滤波后的信号就是振荡波,它的幅度包络才是我们要的东西。这里要注意,滤波会带来相位延迟,包络峰值的位置会稍微后移,做血压计算时如果对时间对齐要求高,需要补偿这个延迟;课程设计层面影响不大,但答辩时能提一句会显得考虑周全。
4.2 包络提取与收缩压/舒张压计算
包络提取的常用做法是峰值检测:在滤波后的振荡波上找局部极大值,记录每个峰值的幅度和对应时刻的袖带压力。峰值检测要设两个门限,一个幅度门限去掉噪声引起的小峰,一个时间门限保证两个峰之间至少间隔 300 ms(对应心率 200 次/分),避免一个心动周期被误检成两个。
得到峰值序列后,找幅度最大的那个峰,它对应的袖带压力就是平均压的近似。然后从最大峰向两侧找,幅度下降到最大幅度某个比例时对应的压力,就是收缩压和舒张压。收缩压在最大峰之前(压力较高一侧),舒张压在之后。比例系数前面说过,取 0.6 左右起步,实测修正。
#define RATIO_SYS 0.60f #define RATIO_DIA 0.70f void bp_calculate(void) { int max_idx = find_max_peak(); float max_amp = peak_amp[max_idx]; int sys_idx = max_idx, dia_idx = max_idx; /* 向高压侧找收缩压 */ while (sys_idx > 0 && peak_amp[sys_idx] < max_amp * RATIO_SYS) sys_idx--; /* 向低压侧找舒张压 */ while (dia_idx < peak_count - 1 && peak_amp[dia_idx] < max_amp * RATIO_DIA) dia_idx++; sys_pressure = peak_pressure[sys_idx]; dia_pressure = peak_pressure[dia_idx]; heart_rate = 60.0f / avg_peak_interval; }收缩压和舒张压用不同的比例系数,是因为包络在两侧的衰减不对称,这是示波法的固有特性,不是随便设的。心率由相邻峰的平均时间间隔换算,间隔单位是秒,60 除以间隔就是次/分。这段代码里没有做异常处理,实际工程中要加:如果找不到足够的峰,或者算出的收缩压小于舒张压,直接判为测量失败,提示重新测量,而不是硬输出一个错值。测量失败比测错更可接受,这是医疗类设备的基本态度。
4.3 气泵阀时序控制与过压保护
气泵和电磁阀的控制看似只是开关 IO,实际时序要求很细。加压速率不能太快,太快会让袖带压力冲过目标值,也会让被测者不适;一般控制在每秒 5~10 mmHg。用 PWM 控制气泵转速可以实现软启动,避免启动瞬间电流冲击。放气速率同样要稳,示波法要求放气过程线性、缓慢,典型速率是每秒 2~3 mmHg,太快包络采样点不够,太慢测量时间过长。
过压保护是必须做的安全设计,分软件和硬件两层。软件层在加压状态里持续比较压力值,超过 220 mmHg 立即关泵开阀;硬件层可以加一个压力开关或独立比较器,软件失效时直接切断气泵电源。课程设计里硬件保护可以简化,但软件保护一定要有,而且要在报告里写清楚,这是答辩时体现工程思维的加分点。
void pump_control(uint8_t duty) { if (duty > 100) duty = 100; set_pwm_duty(duty); /* 0~100 对应占空比 */ } void safety_check(uint16_t pressure) { if (pressure > OVER_PRESSURE_LIMIT) { /* 220 mmHg */ pump_control(0); valve_open(); fault_flag = FAULT_OVER_PRESSURE; } }PWM 占空比和实际加压速率的关系需要实测标定,不同气泵、不同袖带容积下差别很大。我的做法是先给固定占空比跑一次,记录压力上升曲线,算出速率,再反推需要的占空比。这个标定过程要写进报告,说明参数不是拍脑袋定的。
4.4 显示、存储与串口上传
显示部分,段码液晶便宜、驱动简单,适合只显示三组数字;点阵液晶能显示波形和提示语,答辩演示效果更好,但驱动代码量大。如果要做包络波形显示,点阵屏是更好的选择,把采样到的振荡波按列画出来,评委一眼就能看懂你在做什么。按键至少两个:测量启动和记录查询。蜂鸣器用于测量开始、结束和超限提示。
存储用外挂 EEPROM 或单片机内部 Flash 模拟,存最近 30 组记录,每条记录包含收缩压、舒张压、心率和一个时间戳(如果加了 RTC)。存储格式要定好,比如每条 8 字节,前两字节收缩压、两字节舒张压、一字节心率、三字节时间或序号,读写时按固定偏移操作,避免结构体对齐带来的麻烦。
串口上传主要用于调试和答辩演示。定义一个简单的帧格式,比如帧头 0xAA、长度、数据、校验和,上位机收到后解析显示。这个功能在调试阶段价值很大,可以把原始压力波形传到电脑上看,比盯着小屏幕猜问题高效得多。
5. 避坑与排查:那些让样机“时好时坏”的细节
5.1 现象:屏幕花屏、ADC 读数乱跳
原因基本都指向电源和地。气泵、电磁阀启动瞬间拉大电流,如果它们和单片机共用一条地线且走线细,地电位会被抬起来,单片机的参考地跟着动,ADC 读数自然跳,液晶也可能因为复位不彻底而花屏。解决分三步:气泵和阀的电源单独走线,从电源入口直接拉过去,不要经过单片机区域;数字地和功率地单点连接,连接点放在电源入口附近;气泵两端并联一个几百微法的电解电容,吸收启动冲击。做完这三步,大部分花屏和跳变会消失。
5.2 现象:每次测量结果差十几个 mmHg
先排除袖带绑法和测量姿势,这两项对结果影响很大,袖带位置偏高或偏低、测量时说话,都能造成 10 mmHg 以上的偏差。排除之后看包络,如果包络峰值不明显或者有多个相近的峰,说明滤波参数或峰值检测门限有问题。常见原因是带通滤波的通带设得太宽,把呼吸基线也放进来了,或者峰值检测的时间门限太小,把噪声峰当成了脉搏峰。调窄通带、加大时间门限,重新看包络形状,正常应该是一个明显的单峰。
5.3 现象:加压到一半就停,或者气泵一直转
前者多半是压力上升速率判断逻辑太激进,压力稍有波动就误判为“到顶”,把速率判断的窗口拉长、加一点迟滞就能解决。后者是超时保护没生效或者气路漏气,先检查袖带接口和软管有没有折住,再看超时计数的单位是不是写错了——用毫秒计数却按秒比较,这种低级错误我见过。气路漏气时,压力上不去,状态机卡在加压态,没有超时就会一直转,所以超时保护不是可选项。
5.4 现象:静态压力准,但血压值整体偏高或偏低
这是系统偏差,出在包络比例系数上。静态压力可以用标准压力计校准,但收缩压舒张压的比例系数没有标准件就只能靠对比测量。做法是找几组用商用血压计测过的数据,和自己的样机同时测,算偏差,然后微调比例系数。注意不要用一组数据调到底,至少三组不同血压水平的数据,取折中。另外要确认对比测量是同一时间、同一姿势,否则调出来的系数没有意义。
5.5 现象:答辩演示时死机或复位
现场演示和实验室环境最大的差别是电源和干扰。如果用电池供电,检查电池电量,气泵启动时电压跌落可能导致单片机欠压复位;如果用适配器,检查适配器纹波。还有一个容易被忽略的点:演示前没有做完整的冷启动测试,代码里某个初始化顺序在热复位时正常、冷启动时异常。我的习惯是每次改完代码,断电再上电跑一遍完整测量流程,这个习惯救过好几次场。
6. 验收与答辩:怎么证明这套血压计是“能用的”
验收这件事,我一般分三层来做,层层递进,答辩时也按这个顺序讲,评委听起来逻辑清楚。第一层是功能验收,逐项对照需求表:能不能自动加压、能不能显示三个值、按键和蜂鸣器是否正常、存储记录能不能查。这一层用一张检查表,每项打勾,现场演示一遍完整测量流程,从开机到出结果不超过 60 秒。第二层是精度验收,静态压力用标准压力计或经过校准的压力表对比,在 0、50、100、150、200 mmHg 几个点各测三次,记录偏差,要求都在 ±3 mmHg 以内;血压值用商用血压计做对比测量,同一受试者、同一时间段、同一姿势,测三组取平均,偏差控制在 ±8 mmHg 以内算合格,这个口径比医疗器械标准宽松,但对课设和毕设是合理的。第三层是稳定性验收,连续测 10 次,看结果离散程度,如果某次明显偏离,要能解释原因,比如袖带松动或受试者移动。
答辩时的技巧,核心是“用数据说话,用波形佐证”。把原始压力波形、滤波后的振荡波、包络曲线三张图放在一页 PPT 上,讲清楚每一步做了什么、参数怎么定的,比念代码有效得多。评委常问的问题集中在三处:比例系数怎么来的、误差怎么控制、安全保护做了什么,提前把这三块的答案准备好。还有一个细节,演示时把测量过程完整走一遍,不要只放录好的视频,现场跑出来的结果哪怕有偏差,只要你能解释偏差来源,反而比完美数据更可信。
| 验收项 | 方法 | 合格标准 |
|---|---|---|
| 静态压力 | 标准压力计对比,5 个压力点 | 偏差 ±3 mmHg 内 |
| 血压值 | 商用血压计对比,3 组 | 偏差 ±8 mmHg 内 |
| 重复性 | 连续测量 10 次 | 离散度小于 10 mmHg |
| 过压保护 | 人为触发超压 | 3 秒内停泵开阀 |
| 测量时长 | 计时 | 60 秒内出结果 |
从那以后我每次带这类项目,都会在答辩前一周强制走一遍完整的冷启动加对比测量流程,把数据记在本子上,而不是等到现场才第一次跑。血压计这个题,代码能跑通只是及格线,数据稳得住、偏差说得清,才算真正交付。希望这套结构能帮到你,照着搭、照着验,少走几个我走过的弯路。
本文还有配套的精品资源,点击获取