基于STM32的嵌入式健康监测终端:体温、心率与跌倒检测实战

发布时间:2026/9/1 6:50:25
基于STM32的嵌入式健康监测终端:体温、心率与跌倒检测实战 简介面向嵌入式开发与健康监测场景的STM32完整工程基于STM32微控制器实时采集体温、心率并利用加速度计/陀螺仪MPU6050实现老人跌倒检测数据可通过串口、蓝牙或Wi-Fi上报适用于居家看护、养老院监护、远程医疗预警等场景。压缩包共230个文件约29.37MB主要包含C/H源码、Keil工程配置、编译生成的AXF/HEX固件以及传感器驱动、LCD显示和定时器相关代码可帮助学习者快速理解多传感器融合与嵌入式数据上报流程。已有300人学习下载。资源内除了完整工程代码还提供心率PPG/ECG处理思路、跌倒判断逻辑、通信协议设计等关键模块适合作为课程设计、毕业设计或产品原型参考便于在此基础上进行二次开发和功能扩展。1. 从一颗传感器到一整套看护方案这个项目到底在做什么先说结论这不是一个实验室里跑通Demo就完事的项目而是一套可以部署到养老院、独居老人家庭或康复病房的嵌入式健康监测终端。核心功能三件事——实时测量体温、采集心率数据、识别老人跌倒动作全部跑在一块STM32上不上云也能本地独立工作。我为什么强调本地独立工作因为市面上很多所谓健康监测设备一旦断网就成了板砖传感器数据全走云端处理。而STM32方案的优势恰恰在于所有信号采集、滤波、特征判断都在MCU内部完成只有最终报警信息才需要对外传输。这对于网络条件不稳定的养老场景反而更可靠。整个系统的信号流大致是这样体温传感器和心率传感器通过I2C或单总线把原始数据送进STM32六轴加速度计跌倒检测的核心器件通过SPI或I2C持续输出三轴加速度和角速度。STM32内部跑三个并行任务——温度采集任务、心率滤波任务、跌倒识别算法——最后把状态汇总到屏幕和蜂鸣器。这个项目适合谁来搞正在学STM32但想做点真正有社会价值的项目的学生想给家里老人做一套低成本的监测设备、又不想买市面成品的人嵌入式开发者想练手传感器融合和状态机设计如果你只会点灯、点数码管这个项目正好把你往前推一大步它涉及I2C、SPI、定时器中断、串口通信、ADC采集、低功耗设计——STM32的大部分核心外设你都能碰到。2. 传感器选型逻辑为什么是这三颗芯片而不是别的选传感器是整条开发链路里最容易被新手低估的一环。很多人上来就买模块看见引脚能接就焊结果数据的噪声大得没法看又找不到问题出在哪。我直接把常用方案摆个表格说说我踩过的取舍。功能模块可用传感器接口方式我的选择选择理由体温测量DS18B20、MLX90614、NTC热敏电阻单总线 / I2C / ADCMLX90614非接触测量更卫生适合老人日常佩戴心率采集MAX30102、PulseSensor、BH1790GLCI2C / ADCMAX30102集成红光红外双LED能算血氧前期扩展空间大跌倒检测MPU6050、MPU9250、LSM6DS3I2C / SPIMPU6050资料最多六轴数据足够价格便宜先说体温。DS18B20是很多教程的首选便宜、单总线、不用校准。但它有个致命问题——属于接触式测量放在身上会被环境温度慢慢掰弯测出来的数据其实是皮肤加热了传感器的结果。MLX90614是红外测温对准手腕动脉区就能读不接触皮肤误差在±0.2°C左右而且它的SMBus协议和I2C几乎兼容STM32标准库直接能驱动。心率方面我不推荐模拟输出的PulseSensor。因为它输出的是模拟电压要用ADC采集而ADC的噪声很容易干扰心率信号的峰值识别。MAX30102直接输出数字信号内部自带滤波逻辑可配置采样率I2C接口读取寄存器值就行。而且它带绿光LED绿光对血液吸收率比红光高穿透浅表层血管的能力更适合手腕佩戴式测量。重点说MPU6050。这颗六轴芯片三轴加速度三轴陀螺仪简直是为跌倒检测量身定做的。加速度计能感知重力方向变化陀螺仪能感知身体旋转角速度。摔倒的过程在物理意义上非常明确先是自由落体般失重加速度数值骤降然后猛烈撞击地面加速度瞬间飙升最后人躺倒在地身体姿态从竖直变为水平。我还考虑过MPU9250多了一个磁力计看起来更高级。但对跌倒检测来说磁力计除了增加代码量毫无用处——检测靠的是加速度和角速度不是方向和航向。多花钱买用不上的功能不如把省下的钱花在传感器的滤波电路上。3. 硬件电路与PCB设计避坑实录传感器的模数转换、信号调理这些电路如果没弄好后端代码怎么写都救不回来。我直接说我踩过最疼的两个坑。3.1 MLX90614的供电纹波问题第一次焊完板子MLX90614读出来的温度在33°C到41°C之间乱跳以为是I2C时序问题折腾了两天驱动代码。最后用示波器一看电源线上有200mV的纹波导致MLX90614内部ADC的参考电压也跟着抖。解决办法很简单在MLX90614的VCC和GND之间加一个10μF的钽电容和一个0.1μF的陶瓷电容并联。如果还想更稳就把传感器单独用一颗LDO我用的AMS1117-3.3供电别跟电机、蜂鸣器共用电源平面。实测共用电源时数据波动±0.5°C独立供电后稳定在±0.1°C。3.2 MPU6050的中断引脚与时钟同步MPU6050有一个INT引脚数据准备好后会拉高通知MCU读取。很多教程直接把这个引脚悬空主循环轮询读取虽然功能能实现但时序上容易读错。因为轮询读取时可能刚好读到新旧数据各一半的状态表现为数据跳变。我建议把INT引脚接到STM32的外部中断引脚比如PA1配置为上升沿触发。这样一来每次数据更新时MPU6050会主动敲门MCU在中断里读取保证读到的是一帧完整的数据。这个做法需要把I2C配置为主模式速率设置在400kHz以下否则MPU6050容易通信超时。还有一个细节MPU6050的I2C地址是0x68但如果AD0引脚接高电平就变成0x69。很多人在这翻车明明代码没错就是找不到设备。直接把AD0接地或者悬空固定用0x68地址。3.3 蜂鸣器和继电器的干扰隔离这个项目的上电自检逻辑里蜂鸣器会响一声。第一次整体调试时蜂鸣器一响屏幕上的心率数值直接跳空一拍。查了半天原来是蜂鸣器是感性负载开关瞬间产生反向电动势顺着电源线和地线干扰了MAX30102的I2C通信。解决手段是两层蜂鸣器并联一个1N4148二极管续流二极管方向反接吸收反向电动势电源入口加一个磁珠和10μF电容构成的π形滤波器。从那以后蜂鸣器再怎么响传感器数据都纹丝不动。这个经验直接套用到你的项目里能省一个礼拜的调试时间。4. 定时器分时复用一个定时器干三件事的调度策略STM32的定时器资源看着不少但真做项目时就发现不够用温度采集要周期刷新、心率传感器要定时读取和滤波、跌倒检测需要高频100Hz读取加速度数据、显示屏要刷新、蜂鸣器要控制。如果每件事都占一个定时器F103C8T6这种主流芯片立马就不够用了。我的做法是一个TIM2搞定所有周期任务用时基任务标记的方式做时间片轮转调度。这个思路在裸机开发里非常实用不依赖RTOS代码量反而更小。具体操作TIM2配置为1ms中断72MHz主频下预分频72-1自动重载1000-1每进一次中断执行tick。然后在主循环里判断全局的tick变量extern volatile uint32_t tick; #define TEMP_PERIOD 1000 // 温度采集周期1秒 #define HEART_PERIOD 50 // 心率传感器读取20Hz #define FALL_PERIOD 5 // 跌倒检测200Hz读加速度 uint8_t temp_flag 0; uint8_t heart_flag 0; uint8_t fall_flag 0; void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update)) { tick; if (tick % TEMP_PERIOD 0) temp_flag 1; if (tick % HEART_PERIOD 0) heart_flag 1; if (tick % FALL_PERIOD 0) fall_flag 1; TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } int main(void) { while (1) { if (temp_flag) { temp_flag 0; read_temperature_task(); } if (heart_flag) { heart_flag 0; read_heart_rate_task(); } if (fall_flag) { fall_flag 0; read_fall_task(); } // 其余时间跑显示刷新和按键扫描 } }这里有个关键点主循环的任务必须短平快不能阻塞。例如读取温度时I2C通信等待时间较长那就分两次读——先发出读取命令下次轮询时再取结果。如果主循环卡在某个传感器的等待循环里几百毫秒其他任务的定时就全乱套了。我在实际测试中发现用这个分时复用调度后跌倒检测的采样率能稳定在200Hz心率传感器读取稳定在20Hz温度刷新1秒一次三件事互不干扰CPU占用率还不到30%。5. 跌倒检测核心算法差分阈值姿态判定的实战经验跌倒检测是整个项目算法含量最高的部分也是最容易看起来能做、实际根本不准的部分。光看加速度的绝对值是远远不够的——老人弯腰捡东西、快速坐下、被人搀扶时的加速度变化跟跌倒看起来非常像。我的做法是两段式判定先看运动强度再看姿态变化。具体拆成三个判据**判据一合加速度突变。**用公式A sqrt(Ax² Ay² Az²)算出合加速度。正常情况下合加速度接近1g重力加速度跌倒时先失重A远小于1g再撞击A远大于1g。判定条件设定为先检测到A小于0.4g持续至少50ms随后1秒内A超过2.5g的峰值。这个阈值是我拿20组模拟数据调出来的不同身高体重的人差异不大比较普适。**判据二姿态角翻转。**跌倒后人体大概率从竖直变成水平。用加速度计分量计算倾斜角pitch atan2(Ay, sqrt(Ax² Az²))当角度从竖直接近90°快速变化到水平小于30°时判定为一次姿态翻转。这里有个细节——必须用低通滤波后的角度。MPU6050原始数据小抖动很大直接算角度会出现突然翻转的假象。我用滑动平均窗口窗口长度16约80ms把角度曲线磨平后再判断。**判据三静止确认。**跌倒在地后如果只是瞬间的冲击人可能马上就坐起来了这种情况不需要报警。所以算法最后一步是倒地后3秒内合加速度一直稳定在1g附近排除继续剧烈运动才确认是跌倒后未起身状态。三个判据按顺序执行形成一个简单的状态机正常 → 疑似失重 → 撞击确认 → 姿态翻转 → 静止躺倒 → 触发报警。任意一步超时未满足状态机自动复位回正常。这套算法实测下来对走路滑倒从椅子跌坐床上滚落三种场景都能正确识别误报率大约百分之几主要是快速弯腰捡东西后没立刻直起身的情况。这个可以通过提高姿态翻转的阈值角度从30°降到20°来规避但也可能漏掉一些跌坐后身体仍然前倾的场景——阈值要按实际使用环境调整没有一个放之四海而皆准的参数。6. 心率数据为什么不可信信号质量的判断与滤波处理如果你直接把MAX30102的原始数据画出来那基本是一条疯狂抖动的曲线。要从中算出准确的心率得做三件事滑动平均滤波、峰值检测、还有最关键的信噪比判断。MAX30102的原始数据是红外光透过毛细血管后的吸收量心脏泵血时血量变化会在波形上形成一个小峰。这个峰非常微弱很容易被手指移动、环境光干扰盖掉。我用的处理流程是这样的首先做滑动平均滤波。窗口长度取多少很关键——窗口太短滤不掉高频噪声窗口太长会把心率信号的细节也磨没。按照心率范围60-150次/分主频大约1-2.5Hz采样率如果是20Hz那一个波形周期大约有8-20个采样点。滑动平均窗口取5能在保留波形峰谷特征的前提下滤掉大部分高频抖动。然后是峰值检测。我用的方法是求导阈值计算相邻采样点的差值当差值的符号从正变负、且当前值超过一个动态阈值时认为是一个波峰。动态阈值怎么定我是每3秒更新一次取这3秒内振幅最大值的60%作为这轮检测的阈值。这样即便手轻微晃动导致振幅变化峰谷检测都能自适应调整。但还有一个更大的坑——很多情况下波形根本没有规律。比如老人手凉、末梢血液循环差的时候MAX30102的红外波形几乎是平的随机噪声都比你血管搏动的信号大。如果这时还硬算心率算出来的数完全是瞎猜的。所以一定要加一个信号质量判断模块uint8_t check_signal_quality(uint16_t *buffer, uint16_t len) { uint16_t max_val 0, min_val 0xFFFF; for (uint16_t i 0; i len; i) { if (buffer[i] max_val) max_val buffer[i]; if (buffer[i] min_val) min_val buffer[i]; } uint16_t amplitude max_val - min_val; // 振幅低于300个ADC码值认为信号不可信 if (amplitude 300) return 0; return 1; }这个振幅阈值300是我调出来的经验值——正常佩戴时MAX30102的红外ADC数值在几万范围内红光通道波形峰谷差在500-1500之间。如果你发现连300都过不了大概率是佩戴太松或手指冰凉此时不再计算心率直接在屏幕上提示请重新佩戴而不是显示一个荒谬的数字。宁可不显示也不显示假数据——这一点在医疗健康类项目里比什么都重要。7. 一个让我排查了两天的BugI2C通信被中断抢占这个项目的整体调试还算顺利唯一让我头疼了两天的问题是心率数据和温度数据偶尔会出现瞬间跳变甚至直接读到0xFF这种错误字节。因为两个传感器的数据我都放在中断里读取理论上不该出这种事但复现时发现只要蜂鸣器报警跌倒状态触发I2C读取就立刻乱套。排查过程是这样的先怀疑是I2C时序被拉长导致超时于是把I2C时钟从400kHz降到100kHz——问题依旧。然后怀疑是外部中断频繁触发导致I2C通信被打断于是把外部中断优先级降到了最低——依然乱。最后才发现问题出在MPU6050的INT引脚接线上这个引脚和蜂鸣器控制引脚在PCB布局上挨得太近蜂鸣器的2000Hz方波信号通过PCB走线间的寄生电容耦合到了INT线上产生了大量假中断。解决起来很直接把MPU6050的INT引脚在软件上配置为内部上拉同时把控制蜂鸣器的GPIO从默认的推挽输出改成开漏输出外加外部上拉电阻。改了之后假中断完全消失I2C读取错误率直接降到0。这个教训的通用价值是多传感器系统的传感器间干扰不一定来自电源也可能来自GPIO之间的电磁耦合。调试时遇到诡异现象先怀疑物理层再怀疑协议层和代码层。顺序反过来很可能白白浪费几天时间。8. 数据可视化与后续扩展屏幕显示、低功耗和无线传输的思路系统做出来之后显示层我用了0.96寸OLEDSSD1306I2C接口一页显示体温和心率翻页显示姿态状态。这块屏幕在STM32平台上有现成的库移植成本极低。但如果你想做更复杂的界面比如波形显示OLED就不够用了可以考虑换TFT-LCD或LVGL加持的屏幕。功耗方面整机在连续工作模式下电流大约80mA其中屏幕占了20mA传感器占了30mAMCU自身30mA。如果做成便携设备比如老人随身佩戴这个功耗是不合格的。低功耗优化的基本思路是让传感器间歇工作比如每5分钟测一次体温和心率每次测10秒后进入待机摔倒检测模块保持高频工作毕竟这个不能停MCU用停机模式靠RTC定时唤醒。用这个方案一块1000mAh的锂电池可以撑一周左右。无线传输方面如果要把数据传给手机或后台我建议用ESP8266或ESP32做WiFi模块STM32通过串口把传感器数据发给它走MQTT协议上云。这里有个要注意的问题STM32和ESP8266串口通信时波特率不要设太高115200在长距离传输下容易丢字节9600太慢传不了高频率的数据波形57600是我实测比较稳定的折中值。这套系统如果继续往下做可以加GPS定位跌倒时带上位置信息、加ECG心电模块做更全面的心脏健康评估、加语音播报跌倒时自动呼叫老人或家属。从项目完成度来说先把手上的三个模块做扎实、数据做稳定比贪多求全更有价值——这也是我个人做嵌入式项目最深的体会。本文还有配套的精品资源点击获取