资讯动态

STM32+AD8232心电监测系统设计:从硬件搭建到心率算法详解

发布时间:2026/9/17 18:21:23 来源:尧图企业网站定制
搞过几个正经的传感器采集项目之后我对“基于STM32的心电图监测系统设计”这个题目一直印象很深。它几乎把嵌入式开发里最常碰到的几件事全揉在了一起模拟小信号采集、ADC采样率规划、数字滤波、实时算法、显示与通信。说白了这是一个典型的软硬结合项目不是光会点灯、点串口就能糊弄过去的。这个题目也是很多毕业设计和课程设计的热门选择原因很简单——它难度适中、扩展性强、展示效果又特别直观。你把电极往手指上一贴屏幕或者上位机上立刻跳出一跳一跳的心电波形心率数字也在实时变这种“所见即所得”的反馈比单纯跑个流水灯有说服力得多。我当年做第一版的时候从零开始到跑出稳定的心率大概用了两个完整的周末中间踩的坑不少但每一个坑都值回票价。这篇内容不是照着芯片手册念我会把整套系统从方案选型、硬件搭建、软件滤波到心率识别算法按我自己实际跑通的思路完整走一遍最后再附上一份问题排查手册。不管你是准备做课程设计还是想给stm32项目清单里添一个拿得出手的完整作品这篇都适合你参考。1. 系统整体设计为什么这套组合最适合入门心电项目1.1 选型思路STM32F103 AD8232 为什么是黄金组合先说结论主控选 STM32F103C8T6模拟前端选 AD8232 心电前端模块这是我见过最稳妥、资料最多、也最适合复现的组合。STM32F103 这个型号大家太熟了12 位 ADC、3 个片内 ADC 外设、充足的定时器、DMA、USART 全都有性能对于心电这种低频信号来说绰绰有余。更重要的是它资料丰富CubeMX 配置起来几小时就能上手遇到问题搜一下到处都是解决方案。AD8232 是 ADI 出的一套单导联心电模拟前端方案芯片内部集成了仪表放大器、运算放大器、右腿驱动电路和导联脱落检测电路。你用三根电极线接到人体它输出一路已经放大好的模拟心电信号直接送进 MCU 的 ADC 引脚就行。这比用分立运放自己搭仪表放大器不知道省了多少事而且芯片的前级和后级滤波电路是固定结构你只需要按手册选一对电容就能把通带大致框在 0.5Hz 到 40Hz 附近正好是心电监测常用的频段。这个组合还有一个很实际的优点成本低。主控板加模块加电极片加屏幕整套物料成本可以控制在百元级别对预算有限的学生项目非常友好。1.2 系统架构与信号链路整套系统可以分成四段来看。第一段是采集前端也就是电极和 AD8232 那一块。电极贴到人体皮肤后心电信号本质上是一个毫伏级别的差分电压信号幅度大约在 0.5mV 到 4mV 之间。AD8232 负责把这个信号放大到 ADC 合适的量程内同时完成初步的带通滤波和工频干扰抑制。第二段是 STM32 核心处理。ADC 以固定采样率采集模拟输出用 DMA 把数据持续搬到内存缓冲区中然后在主循环里做数字滤波、R 波检测和心率计算。第三段是人机交互。可以接一块小尺寸 OLED 或者 TFT 屏显示实时波形和心率数字也可以把原始数据通过串口发送到上位机用 Python 自绘波形。第四段是供电与电源管理。整个系统使用 3.3V 供电可以由 USB 口直接提供也可以用锂电池加 LDO 或者 DC-DC 模块供电。提示心电监测用于医疗诊断有严格的行业标准我这个项目定位是学习、原理验证和健康管理参考用途不承担临床诊断功能。如果你确实奔着医疗级产品去那要考虑的标准和认证流程是另一个量级的事。1.3 备选方案对比如果不是非用 STM32F103 不可也有几条路线可以对比选择。方案优势劣势适合场景STM32F103 AD8232资料多、成本低、实现简单模拟前端性能一般12 位 ADC 精度有限课程设计、入门学习STM32L4 ADS129224 位 ADC、低功耗、信号质量好配置复杂、SPI 驱动工作量较大、成本高想做低功耗产品原型ESP32 AD8232自带 WiFi/BLE无线传输方便ADC 精度一般模拟采集通道少想快速做手机端联网展示NXP/Freescale KL25Z 板载传感器官方有完整示例生态相对封闭资料少学校指定平台我的建议很直接除非你有明确的低功耗或高精度需求否则第一版老老实实选 F103 AD8232。先把信号链路、滤波算法和心率检测这套东西跑通后面想升级再换平台也不迟。别一开始就挑战高难度配置模拟前端加 24 位 ADC 再加无线任何一个环节出问题都会让你怀疑人生。2. 硬件搭建要点AD8232 外围电路与信号链设计2.1 AD8232 模块接口与引脚功能市面上常见的 AD8232 核心板有十个左右的引脚功能上有几组要特别注意。供电引脚 VCC 和 GND 负责给芯片供电工作电压范围大约在 2.0V 到 3.5V所以直接接 STM32 的 3.3V 是非常合适的不需要也不应该接 5V 上去。三个电极接口分别标着 RA右臂/右手、LA左臂/左手、RL右腿/参考。这里面 RA 和 LA 是差分输入的正负端RL 是右腿驱动输出主要作用是把人体共模电压反相后反馈回去抵消掉一部分 50Hz 工频干扰。很多新手图省事只接两个电极把 RL 悬空结果就是波形上叠了一大片锯齿状干扰怎么滤都滤不干净。这个问题我在后面排查章节还会重点讲。模块上通常还有 OUT、LO、LO-、SDN、RDY 这几个引脚。OUT 就是放大后的模拟心电输出接 MCU 的 ADC 输入引脚。LO 和 LO- 是导联脱落检测的输出可以接普通 GPIO 做提示。SDN 是关断控制脚低电平有效还是高电平有效要看具体模块丝印有的板子已经默认拉好了不用管。RDY 是导联连接状态指示一般也不需要用。注意不同厂家做的 AD8232 模块引脚排列和默认状态不一样拿到模块第一件事是看丝印、看原理图确认 SDN 的默认状态否则可能出现输出一直固定在一个电平上的怪问题。2.2 电源、去耦与电平匹配STM32F103 和 AD8232 都要 3.3V 供电。如果用一个 USB 转 TTL 或者 ST-Link 直接给板子 5V板载稳压器降到 3.3V那 AD8232 就接 3.3V 输出那一排如果是自己画板子建议加一颗低噪声 LDO比如 AMS1117-3.3 或者更安静的 RT9013供电端并联一个 10μF 钽电容和一个 0.1μF 陶瓷电容去耦。还有一个特别实际的细节心电信号是微伏到毫伏级别的任何叠加在电源上的纹波都会被 AD8232 当做有效信号放大出来。实测下来用劣质 USB 充电器供电时波形底噪明显变大换一个质量好一点的 5V 适配器或者干脆用充电宝供电底噪能下降好几个档次。如果你的波形上有一条很粗的“毛毯”先别怀疑芯片先换个供电环境试试。AD8232 模块输出信号可以直接接到 STM32 的 PA0 或 PA1 上配置为模拟输入模式即可。不需要经过任何电阻分压或电平转换因为它们共用一个 3.3V 参考电平。12 位 ADC 的量化精度是 3.3V / 4096 ≈ 0.8mV 每 LSB再除以 AD8232 约 100 倍的增益相当于 ADC 对输入端约 8μV 的分辨能力对于心电 R 波的幅度来说完全够用。2.3 电极贴合与右腿驱动抗干扰最关键的一环这段算是我反复折腾过最久的地方多说几句。心电采集的第一道关不是芯片而是电极和皮肤的接触。本质上电极片和皮肤之间会形成一层电解质界面接触阻抗不稳定会产生极化电压这个偏差电压可能达到几百毫伏远超心电信号本身。AD8232 虽然靠高通滤波能把极化电压去掉一部分但前提是电极接触要尽量稳定。实操时几个有效的做法贴电极前用酒精棉擦拭皮肤去掉油脂电极片按下后稍微等一两分钟让导电膏充分润湿角质层如果手头没有一次性电极片用手指按压金属电极片也能凑合但手指一动信号就跳只能做静态演示。右腿驱动的重要性容易被低估。人体在 220V 交流环境中即使不接触任何带电体也会通过空间耦合感应出几十伏的 50Hz 共模电压。这个共模电压在差分输入里一般可以被抑制掉但器件的共模抑制比不是无限的。RL 电极的作用就是把共模分量反相送回人体主动去抵消它。我调试时做过一个对比实验接好 RA、LARL 悬空波形在示波器上像一团乱麻把 RL 贴上后毛刺明显收敛50Hz 成分大幅下降。所以无论你是用模块还是自己画板子一定把三个电极都接上尤其是 RL 这根。3. 软件实现干货ADC 采样、数字滤波与心率识别算法3.1 CubeMX 配置与定时器触发 ADC软件部分我用的是 STM32CubeMX 生成工程配合 HAL 库。这不是说标准库不行而是 CubeMX 对 ADC、DMA、定时器这些外设的初始化代码生成得更规范不容易漏掉配置项。ADC 采样这一块我最不建议的做法是在 while 循环里死等转换完成再读值因为主循环的节奏不受控采出来的时间间隔抖动大波形会忽快忽慢后面做滤波也不好处理。正确做法是用定时器触发 ADC 采样再用 DMA 把结果自动搬到内存。以 500Hz 采样率为例配置思路是这样的定时器 TIM2 产生 500Hz 的更新事件通过内部触发信号启动 ADC 转换ADC1 选择定时器触发模式通道选择 PA0分辨率 12 位采样周期不需要太长心室信号是低频的几个 ADC 时钟周期就足够DMA 配置为循环模式每次转换结束自动把结果写入一个数组数组大小可以设成 1024 或 2048在 DMA 的半传输完成中断和传输完成中断里设置标志位主循环检测到标志后处理对应半区间的数据。用定时器触发 ADC 最大的好处就是采样间隔均匀这对后面做 50Hz 移动平均滤波尤其重要。窗口长度必须对应固定的时间抖动太大会严重影响陷波效果。DMA 的缓冲区分两个半区处理相当于一个双缓冲机制。当 DMA 正在往后半区写数据时主循环从容地处理前半区的数据处理完再等下一个标志不会出现数据和显示互相踩踏的问题。3.2 数字滤波实战50Hz 工频陷波与移动平均ADC 拿到原始数据后先别急着算心率滤波是决定波形能不能看的关键。心电信号的有效频段大约在 0.05Hz 到 100Hz 之间但日常监测时我们真正关心的是用于识别 R 波和心率的部分集中在 0.5Hz 到 40Hz 左右。所以第一件要做的事就是带通滤波把低频基线漂移和高频肌电噪声都滤掉。针对 50Hz 工频干扰有一个非常实用的小技巧用窗口长度正好为 20ms 的滑动平均滤波器。因为 500Hz 采样率下 20ms 就是 10 个采样点10 点滑动平均的幅频响应在 50Hz 及其整数倍处会出现陷波是天然的梳状滤波器。这个滤波器对 QRS 波群来说10 个点的窗口宽度大约只占整个 QRS 波80 到 120ms的十分之一到八分之一对波形幅度影响很小但对周期性工频干扰的抑制效果却非常显著。我当时实测光靠这个移动平均工频底噪就能被压下去一大半。滑动平均的实现代码很短#define SAMPLE_RATE 500 #define WIN_LEN 10 // 20ms 500Hz int16_t avg_buf[WIN_LEN]; uint8_t avg_idx 0; int32_t avg_sum 0; int16_t moving_average_filter(int16_t new_sample) { avg_sum - avg_buf[avg_idx]; avg_buf[avg_idx] new_sample; avg_sum new_sample; avg_idx (avg_idx 1) % WIN_LEN; return (int16_t)(avg_sum / WIN_LEN); }如果还想进一步压住更高频的噪声可以在滑动平均之后再加一个二阶巴特沃斯低通截止频率设在 40Hz 左右。对于 STM32F103 这种没有 FPU 的 Cortex-M3 而言浮点运算虽然比 M4 慢但在 500Hz 采样率下每个采样点只做几个二阶 IIR 运算CPU 占用率完全可控不用担心性能扛不住。3.3 心率计算自适应阈值找 R 波心率计算的常规做法是检测 R 波测量相邻两个 R 波之间的时间间隔RR 间期再用 60 除以间隔时间换算成每分钟心跳数。R 波是心电波形中幅度最大、斜率最陡的特征波幅值通常能达到 T 波的三到四倍所以一个简单有效的方案是先对滤波后的信号求一个短时能量包络再拿一个自适应阈值去卡峰值。具体做法分四步。第一步把滤波后的序列取绝对值再做一个 30ms 左右的滑动平均得到一条相对平滑的包络线。R 波的突变特性经过这一步会被放大而 T 波这类缓变波形不容易形成高能量峰。第二步设定一个初始阈值。比较省事的方式是取最近一秒内包络最大值的 60% 作为阈值。如果检测到的峰比较少说明阈值太高按比例往下调如果检测到一个时间段内多个峰挤在一起说明阈值太低往上调。让阈值跟着信号最大值走就是自适应。第三步加上一个不应期机制。成人正常心率一般不会超过 220 次每分钟对应的 RR 间期约 273ms。取一个 200ms 的锁定窗口在检测到一个 R 波后的 200ms 内不再重复检测这个机制能有效防止同一个 QRS 波被重复计数。第四步维护一个 RR 间期数组每检测到一个新的 R 波就把当前时间戳减去上次的时间戳存入数组。心率既可以用最近一次 RR 间期算瞬时心率也可以用最近 8 个 RR 间期的平均值算平均心率后者更稳定。R 波检测的核心逻辑可以用伪码表达if (envelope threshold (now - last_r_tick) REFRACTORY_MS) { uint32_t rr_ms now - last_r_tick; heart_rate 60000 / rr_ms; // 毫秒换算成 bpm last_r_tick now; adaptive_update_threshold(envelope); }这个方案对付安静状态下的人体心电信号准确率相当高。但如果测试者剧烈运动信号里混入大量运动伪差准确率会明显下降。想进一步做运动状态下的心率检测那就得上更复杂的 QRS 检测算法比如 Pan-Tompkins那是另一个深水区这里不展开。4. 实机调试中的坑常见问题排查手册4.1 先看波形再谈算法调试的必经顺序我调试这类项目有一条铁律先把波形调到肉眼看着舒服再谈心率算法。波形调不好算法做得再花哨也是空中楼阁。第一次上电我是先把 AD8232 的 OUT 脚接到示波器上看原始波形。如果没有示波器也可以直接把 ADC 原始值打印到串口上位机把曲线画出来。这一步一定要做因为你得先确认模拟前端输出正常ADC 采集的数据有没有进 DMA滤波有没有把特征波抹掉。贴好三片电极坐定不动示波器上应该能看到类似这样的形态一次心跳对应一组特征波群先是幅度较小的 P 波接着是幅度最大的 R 波R 波之后跟一个平缓的 T 波之后是平直的静息段。4.2 波形问题排查表把调试过程中最高频的几个现象整理出来你可以直接按表格对照排查。现象直接原因排查方向波形是一团乱麻看不出周期性工频干扰严重、RL 没接好检查三电极是否都贴牢检查电源质量尝试用电池供电波形缓慢上下飘基线收不住电极极化电压过大、高通截止频率过低重新清洁皮肤贴电极检查 AD8232 高通电容值软件再补一个高通波形平线无任何变化输出脚接错、SDN 被拉低、ADC 配置错误检查 OUT 引脚连接确认 SDN 默认电平用万用表量 OUT 引脚电压波形有正常形态但幅度巨大、顶部削平增益过高或者输入偏置过大调低增益电阻检查皮肤接触确认电极没有贴在一起短路波形时有时无不稳定电极接触松动、导线摆动改用带屏蔽的导联线固定导线减少摆动注意如果用的是裸的杜邦线直接连电极身体稍微一动波形就会剧烈跳变。这种情况下优先怀疑的是电缆引入的噪声而不是芯片或代码的问题。4.3 采样与心率算法相关的典型问题数据采集和算法这一侧的坑很多是逻辑问题不是硬件问题。第一个常见问题是从串口或 OLED 上看到的波形有锯齿状跳变不像平滑的心电曲线。这通常是采样率设置过低或者 DMA 缓冲区处理不当导致的。心电信号虽然主要能量集中在低频但 R 波的上升沿其实比较陡峭采样率低于 200Hz 时波形会明显失真锯齿感很强。建议至少 500Hz有条件可以上 1kHz。第二个常见问题是心率忽高忽低明明心率 70 左右读数却能蹦到 140。这通常就是 T 波被误识别成了 R 波。原因是 T 波在滤波后仍然有足够高的幅度阈值设置的 60% 最大值线被 T 波越过了。解决思路是把阈值系数调高到 70% 左右同时确保不应期设置合理。T 波和 R 波的时间间隔一般小于 200ms200ms 的不应期能挡掉大部分 T 波误检。第三个问题是运动时心率完全测不准。这个我没找到特别完美的纯软件解法因为运动伪差的频段和心电信号有大量重叠简单滤波根本分不开。实用思路是加一个加速度传感器做运动伪差检测检测到运动时就冻结心率显示或者提示“信号受干扰”好过给用户一个错误的数字。4.4 没有真实人体信号时的替代调试方案不是每个人都有条件随时往身上贴电极的。开发阶段可以考虑用一个 10Hz 到 1.5Hz 可调的正弦波信号源代替人体心电信号直接输入 AD8232 的输入端验证采集链路和心率算法。AD8232 输入端的共模电平不能悬空信号源的地要和模块的地相连。另一个更模拟真实场景的方案是用另一块 STM32 的 DAC 引脚回放一段标准心电数据。把 MIT-BIH 数据库里的一段心电波形采样值存在数组里每 2ms 通过 DAC 输出一个点这样就能在桌面上复现一段接近真实形态的心电序列心率算法就能在没有人体的情况下完成大部分调试。这个思路我强烈推荐尤其是团队开发时不可能每轮测试都靠真人贴电极去复现问题。5. 后续还能怎么玩从可视化到低功耗与云端5.1 低功耗与无线采集方向如果你已经把这个基础版本稳定跑起来了下一步最自然的升级方向是低功耗和无线化。STM32F103 的低功耗模式虽然不如 L 系列惊艳但也能做到一定程度的省电。把 ADC 采样改成按需启动平时让 CPU 进入睡眠定时唤醒采一段数据再睡回去用一颗 18650 电池供电撑几个小时做演示是没问题的。无线方面最简单的路线是加一个 HC-08 或者 CC2541 低功耗蓝牙透传模块把滤波后的波形数据帧发给手机或电脑。ST 官方有配套的手机 App 可以直接看波形省得自己写上位机。想做 WiFi 版本的话把主控换成 ESP32通过 MQTT 把数据推到云平台在网页上看实时波形。这种物联网方向在毕业答辩时演示效果很好评委能看到数据从采集端一路传到云端链路完整可说的点也多。5.2 上位机与心电算法升级方向另一个很有价值的扩展方向是把重心放到上位机和算法上。STM32 端只做采集和简单滤波原始数据通过串口或者 USB 发到电脑在 Python 端完成更精细的处理。Python 生态里处理心电信号非常方便可以加载 biosppy、wfdb 这些开源库直接做波形分析画出 P 波、QRS 波群、T 波标注甚至计算心率变异性HRV指标。我试过这个方案之后最大的感受是嵌入式端的代码越简单越好复杂的特征提取交给上位机迭代速度快得多。你可以先在 Python 里把算法验证清楚再把确定好参数的滤波器和阈值逻辑降级移植到 STM32 端。这样两端配合开发效率比自己埋头在嵌入式端反复调参高很多。从数据存储的角度如果希望设备独立工作还可以加一张 microSD 卡用 FATFS 文件系统记录长时间的心电原始数据。这样不但可以事后回放分析还能留下自己的实验数据作为研究成果展示。最后再说两句掏心窝的话做完这个项目我最深的体会是这个题目的难点从来不在 STM32 本身而在模拟前端和信号质量。MCU 配置、滤波算法、心率计算这些都是只要有耐心就能搞定的确定性工作而让三根电极在人身上稳定输出一段干净的心电波形才是真正考验耐心和排查能力的地方。拿到板子第一步不要急着写大段算法先把供电、电极、参考地这些基础环节做扎实。信号干净了后面每一步都会变得顺畅。还有一个小技巧分享给你每次改动硬件或者调整滤波参数后把波形截图保存下来标注当时的工况和参数积累一段时间以后这些截图就是最好的调试数据库。哪次波形出问题翻一翻历史记录很快就能定位是环境变化还是参数退化。这套系统的横向扩展能力很强。心电图监测只是心血管健康的一个窗口同样的采集链路换一个放大增益和滤波频段就可以抓脉搏波、呼吸波甚至肌电信号。先把这一套玩明白再去碰其他生理信号采集项目你会发现很多东西都是相通的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价