ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于STM32的便携式健康监测系统:硬件选型、原理图与程序架构全解析

基于STM32的便携式健康监测系统:硬件选型、原理图与程序架构全解析 健康监测设备这几年从医院场景快速往个人和家庭场景渗透心率、血氧、体温这些基础生理参数的连续采集已经不再是专业医疗设备的专属能力。我接触过不少做嵌入式方向的朋友尤其是学生和刚入行的工程师第一个真正意义上的完整项目往往就是这类便携式健康监测系统。原因很直接它同时覆盖了传感器驱动、模拟信号采集、数字通信、人机交互、电源管理这几个嵌入式核心技能点做完一遍基本能把STM32的常用外设摸个遍。这篇内容围绕一个基于STM32的便携式健康监测系统展开把硬件选型、原理图设计思路、程序架构、元件清单整理这几个环节拆开讲清楚适合正在做课程设计、毕业设计或者想练手完整嵌入式项目的读者参考。整套方案的核心思路是用STM32F103系列做主控搭配心率、血氧、体温三类传感器通过OLED本地显示同时预留串口或无线模块接口用于数据上传整机用锂电池供电并做充电管理。1. 为什么这个项目值得完整做一遍1.1 它到底覆盖了哪些嵌入式硬技能很多人做STM32项目容易陷入一个误区拿现成的开发板插上传感器模块跑通例程就算完成了。但便携式健康监测系统如果只做到这一步其实什么都没学到。这个项目的价值在于它逼着你把一整套链路走通。从信号层面看心率采集涉及光电容积脉搏波PPG的模拟前端处理血氧需要双波长LED驱动和光电二极管接收体温则是数字传感器通过单总线或I2C通信。这三类传感器恰好代表了嵌入式里最常见的三种信号形态模拟小信号、需要时序驱动的光电信号、标准数字总线信号。把这三种都处理一遍你对ADC采样、定时器PWM、GPIO时序模拟、I2C/单总线协议的理解会上一个台阶。从系统层面看便携设备意味着电池供电这就引入了电源管理的问题。锂电池充放电保护、升压或降压稳压、低功耗模式切换这些在插电开发板上永远遇不到的问题在这个项目里全部冒出来。我见过太多人做完项目后连自己板子的功耗都没测过这在便携设备里是不可接受的。1.2 便携式这三个字带来的真实约束“便携式”不是随便加的前缀它直接决定了设计取舍。第一是体积约束传感器和主控要尽量选集成度高、封装小的型号第二是功耗约束不能像桌面设备那样一直全速运行第三是供电约束锂电池电压范围通常在3.0V到4.2V之间而STM32和多数传感器需要3.3V稳定供电这中间必须有稳压环节。提示很多初学者直接用开发板上的5V USB供电做原型功能跑通了就以为大功告成结果一换成锂电池就各种异常。根本原因是没考虑电池电压跌落时稳压芯片的压差问题。选LDO时要特别关注dropout电压参数。1.3 适合什么基础的人上手这个项目对基础的要求其实不算高但也不是零基础能直接啃的。我的建议是你至少要用STM32点过灯、用过串口打印、配置过定时器中断。如果这些还没做过先花一周把GPIO、USART、TIM这三个外设的例程跑一遍再回来。传感器驱动部分只要理解I2C和单总线的时序概念就行具体协议可以边做边查手册。整套方案的工作量如果每天投入三四个小时从画原理图到程序调通大概需要两到三周。其中花时间最多的往往不是写代码而是调试传感器信号和排查硬件连接问题。2. 主控与传感器选型背后的取舍逻辑2.1 为什么是STM32F103C8T6而不是别的主控选型上STM32F103C8T6几乎是这类项目的默认答案但它为什么合适值得说清楚。这颗芯片是Cortex-M3内核主频72MHz64KB Flash20KB SRAMLQFP48封装。对于健康监测系统来说运算量并不大72MHz绰绰有余关键是它的外设资源刚好够用多路12位ADC用于模拟信号采集多个定时器用于PWM和输入捕获硬件I2C和USART都有GPIO数量也够接显示屏和按键。对比几个常见替代方案如果用C51单片机ADC精度和速度都不够而且没有硬件I2C驱动OLED会很吃力如果用STM32F4系列性能过剩成本翻倍对便携设备来说不划算如果用ESP32虽然自带无线但模拟采集部分的稳定性和生态资料丰富度不如F103。所以F103C8T6是在性能够用、成本可控、资料充足这三个维度上的平衡点。主控型号内核主频Flash价格区间适用性STM32F103C8T6Cortex-M372MHz64KB低最合适STC89C52RC805112MHz8KB极低ADC不足STM32F407Cortex-M4168MHz512KB高性能过剩ESP32Xtensa240MHz4MB中模拟偏弱2.2 心率与血氧传感器的方案对比心率和血氧这两个参数市面上有两条技术路线。一条是分开用两个传感器比如用Pulse Sensor做心率用MAX30102做血氧另一条是用MAX30102这类集成芯片同时测心率和血氧。我强烈建议走集成路线。MAX30102内部集成了红光LED、红外LED、光电二极管和低噪声模拟前端通过I2C输出原始数据心率算法和血氧算法在MCU端处理。它的优势是体积小、集成度高、功耗可控而且两个参数共用一套光路省去了额外的传感器布局。Pulse Sensor虽然便宜但它是模拟输出需要占用ADC通道而且抗干扰能力差稍微动一下波形就乱了。注意MAX30102的原始数据不是直接的心率和血氧值需要做滤波和峰值检测才能算出心率血氧则需要根据红光和红外光的比值R来计算。这部分算法是项目的难点后面会专门讲。2.3 体温采集为什么选数字传感器体温采集我推荐用DS18B20或者类似的数字温度传感器而不是热敏电阻加ADC的方案。原因有三点第一DS18B20是单总线数字输出精度可达0.5摄氏度不需要额外的ADC和运放电路第二它出厂校准过省去了热敏电阻需要的标定环节第三单总线只需要一个GPIO布线简单。热敏电阻方案虽然成本更低但需要分压电路、ADC采样、查表或公式换算而且个体差异大每颗都要标定对便携设备来说得不偿失。如果对精度要求更高可以考虑MAX30205这类I2C接口的体温传感器精度能到0.1摄氏度但价格贵不少。2.4 显示与交互部件的选择显示部分用0.96寸OLED是最常见的选择SSD1306驱动I2C或SPI接口。I2C版本接线少两根线搞定适合便携设备。分辨率128x64显示心率、血氧、体温三个数值加上单位完全够用。如果想让界面好看一点可以加一些简单的图标和进度条。交互方面两到三个按键就够了一个用于切换显示页面一个用于开关机或进入设置一个备用。按键用普通轻触开关接GPIO配合内部上拉或外部上拉电阻软件做消抖处理。不需要用触摸屏那会增加成本和功耗对这个小系统来说是过度设计。3. 原理图设计中的关键电路拆解3.1 最小系统电路不能照抄开发板STM32最小系统包括晶振电路、复位电路、启动模式配置、电源滤波。这几部分看起来简单但便携设备上有几个细节和开发板不一样。晶振部分外部用8MHz无源晶振配合两个20pF左右的负载电容经过内部PLL倍频到72MHz。负载电容的具体值要根据晶振规格书来定不是固定20pF我见过有人照抄开发板用22pF结果晶振起振困难。复位电路用10K上拉电阻加100nF电容这个基本通用。启动模式BOOT0和BOOT1通过电阻下拉到地正常从Flash启动。电源滤波是重点。每个VDD引脚旁边都要放一个100nF陶瓷电容靠近引脚放置。VDDA模拟电源要额外加一个1uF到10uF的电容并且通过磁珠或0欧电阻和数字电源隔离。这一点在健康监测系统里特别重要因为ADC采集的是微弱生理信号数字部分的开关噪声会直接串到模拟电源上导致采样值跳动。3.2 锂电池充电与稳压电路便携设备用单节锂电池供电标称电压3.7V充满4.2V放空约3.0V。充电管理用TP4056这类线性充电芯片它支持最大1A充电电流通过一个电阻设定充电电流带充电状态指示。TP4056的输入是5V USB输出接电池正极。稳压部分因为STM32和传感器需要3.3V而电池电压在3.0V到4.2V之间波动所以不能用普通LDO直接降压因为当电池电压降到3.3V以下时LDO就无法维持输出。解决方案有两种一是用低压差LDOdropout电压要低于0.3V二是用升降压芯片比如TPS63020这类电池电压高于3.3V时降压低于3.3V时升压。我建议用升降压方案虽然成本高一点但电池利用率高不会出现电压一低就重启的问题。电源方案芯片示例优点缺点普通LDOAMS1117便宜简单电池低于3.5V失效低压差LDOXC6206压差小低于3.3V仍失效升降压TPS63020全电压范围稳定成本较高3.3 传感器接口电路的细节MAX30102的接口电路需要注意几点。它的INT引脚可以配置为中断输出当FIFO有数据时触发这样MCU就不用一直轮询可以降低功耗。SCL和SDA需要上拉电阻典型值4.7K但如果总线上挂的设备多或者走线长要适当减小。LED的供电引脚要单独加滤波电容因为LED驱动瞬间电流较大会在电源上产生纹波。DS18B20的DQ引脚需要4.7K上拉电阻这是单总线协议的要求。如果走线较长可以考虑减小到2.2K但不要太小否则总线电容充放电时间不够。传感器供电用3.3V注意DS18B20有寄生供电模式但我不推荐用因为寄生供电在温度转换期间需要强上拉电路更复杂不如直接三线供电省心。OLED的I2C接口和MAX30102可以共用一条总线只要地址不冲突。SSD1306的地址通常是0x3C或0x3DMAX30102是0x57不冲突。共用总线时上拉电阻只需要一组放在总线靠近MCU的一端。3.4 调试接口与预留扩展SWD调试接口一定要留出来四根线VCC、GND、SWDIO、SWCLK。便携设备上可以用1.27mm间距的排针节省空间。串口TX/RX也建议引出方便打印调试信息和后续接无线模块。扩展方面可以预留一个UART接口给蓝牙模块或WiFi模块用于把数据传到手机或云端。如果只是本地监测这部分可以不焊。另外可以留一个Micro USB接口既用于充电也用于串口通信通过CH340这类USB转串口芯片实现。4. 程序架构与传感器驱动的实现要点4.1 整体软件框架怎么搭程序不建议写成一个大循环里塞所有逻辑那样后期改起来很痛苦。我推荐用前后台架构主循环负责显示刷新、按键扫描、数据处理定时器中断负责周期性的传感器采样触发。如果对实时性要求更高可以上FreeRTOS但对这个项目来说有点重前后台足够。具体分工是这样的TIM2配置为1ms中断用于系统时基和按键消抖计时TIM3配置为PWM输出用于驱动MAX30102的LED如果不用芯片内部驱动的话USART1用于调试打印I2C1用于OLED和MAX30102GPIO用于DS18B20和按键。主循环里每100ms读一次传感器数据每200ms刷新一次OLED。4.2 MAX30102的初始化和数据读取MAX30102的初始化流程是复位、配置FIFO、设置LED电流、设置采样率和ADC量程、使能中断。关键寄存器包括MODE_CONFIG设置工作模式心率模式或血氧模式、SPO2_CONFIG设置采样率和LED脉宽、LED1_PA和LED2_PA设置红光和红外LED电流。数据读取有两种方式轮询和中断。轮询就是定期读FIFO的写指针和读指针差值就是可用样本数。中断方式是把INT引脚接到MCU的外部中断FIFO有数据时触发中断在中断里读数据。我建议用中断方式功耗更低。// MAX30102读取FIFO数据的简化示例 void MAX30102_ReadFIFO(uint32_t *red, uint32_t *ir) { uint8_t buffer[6]; I2C_ReadBytes(MAX30102_ADDR, FIFO_DATA_REG, buffer, 6); *red ((uint32_t)buffer[0] 16) | ((uint32_t)buffer[1] 8) | buffer[2]; *red 0x03FFFF; // 18位有效 *ir ((uint32_t)buffer[3] 16) | ((uint32_t)buffer[4] 8) | buffer[5]; *ir 0x03FFFF; }4.3 心率计算从原始波形到BPM拿到红光和红外原始数据后不能直接当心率用。原始信号里包含直流分量反映组织、静脉血等静态吸收和交流分量反映动脉搏动。心率信息在交流分量里所以第一步是去直流可以用滑动平均或者一阶高通滤波。去直流后得到脉搏波波形接下来做峰值检测。简单做法是设定一个动态阈值当信号从下方穿越阈值向上时记录一个峰值两个峰值之间的时间间隔就是一次心跳周期。用60除以周期秒就得到BPM。但实际信号有噪声需要加一些约束比如相邻峰值间隔不能小于300ms对应200BPM不能大于2000ms对应30BPM超出范围的就丢弃。// 简化的峰值检测逻辑 #define MIN_PEAK_INTERVAL 300 #define MAX_PEAK_INTERVAL 2000 static uint32_t last_peak_time 0; static uint8_t peak_count 0; static float bpm_buffer[4] {0}; static uint8_t bpm_index 0; void ProcessHeartRate(uint32_t ir_value, uint32_t timestamp) { static uint32_t dc_filter 0; static int32_t last_value 0; int32_t ac_value; // 一阶高通去直流 dc_filter dc_filter ((ir_value - dc_filter) 6); ac_value (int32_t)ir_value - (int32_t)dc_filter; // 上升沿穿越阈值检测 if (last_value 0 ac_value 0) { uint32_t interval timestamp - last_peak_time; if (interval MIN_PEAK_INTERVAL interval MAX_PEAK_INTERVAL) { float bpm 60000.0f / interval; bpm_buffer[bpm_index % 4] bpm; // 四点平均 float sum 0; for (int i 0; i 4; i) sum bpm_buffer[i]; current_bpm sum / 4.0f; } last_peak_time timestamp; } last_value ac_value; }4.4 血氧饱和度的比值计算法血氧的计算原理是基于红光和红外光在含氧血红蛋白和脱氧血红蛋白中吸收率不同。定义R值为(红光交流分量/红光直流分量) / (红外交流分量/红外直流分量)。然后通过经验公式SpO2 A - B*R 换算A和B是校准系数典型值A约110B约25但具体值需要根据传感器和测量部位标定。实际实现时分别对红光和红外通道做去直流提取交流分量的峰峰值或有效值然后算R值。这里要注意红光和红外的采样是同步的所以交流分量的提取要对齐时间窗口。血氧的精度比心率更难做因为受运动伪影和佩戴松紧影响很大建议在静止状态下测量并且做多次平均。4.5 DS18B20的单总线时序实现DS18B20用单总线协议所有通信靠一根线完成对时序要求严格。STM32上可以用GPIO模拟也可以用UART配合单总线模式但模拟更通用。关键时序包括复位脉冲拉低至少480us、存在脉冲检测释放后等待15到60us传感器拉低60到240us、写0拉低60us后释放、写1拉低1到15us后释放、读时隙拉低1us后释放在15us内采样。// DS18B20复位和存在检测 uint8_t DS18B20_Reset(void) { uint8_t presence; DS18B20_DQ_OUT(); DS18B20_DQ_LOW(); delay_us(480); DS18B20_DQ_HIGH(); DS18B20_DQ_IN(); delay_us(60); presence DS18B20_DQ_READ(); // 0表示存在 delay_us(420); return presence; }温度转换需要时间12位精度下最大750ms。可以发转换命令后先去做别的事过一会儿再读。读回来的16位数据低4位是小数部分高12位是整数部分乘以0.0625就是摄氏度。5. 元件清单整理与采购避坑5.1 核心元件清单类别型号数量说明主控STM32F103C8T61LQFP48封装心率血氧MAX301021集成模块或芯片体温DS18B201防水封装可选显示0.96寸OLED1SSD1306I2C接口充电管理TP40561带保护版本稳压TPS630201升降压电池锂电池13.7V500到1000mAh晶振8MHz1无源按键轻触开关36x6mm电阻电容若干-0402或0603封装5.2 采购时容易踩的坑MAX30102市面上有芯片和模块两种。模块已经集成了上拉电阻和滤波电容接线方便但体积大芯片便宜但需要自己画外围电路。如果是做便携设备建议用芯片自己画模块适合快速验证。锂电池要注意带保护板否则过放会损坏。TP4056要选带DW01保护芯片的版本这样过充过放都有保护。OLED买的时候注意是I2C还是SPI版本别买错了。DS18B20有原装和国产之分国产的精度和一致性差一些如果对温度精度要求高建议买原装。提示PCB打样前一定要把原理图仔细检查一遍特别是电源部分的正负极和芯片的引脚定义。我见过有人把TP4056的BAT和BAT-画反上电直接冒烟。5.3 PCB布局的几点经验便携设备的PCB布局电源部分和模拟部分要分开。TP4056和稳压芯片放在板子一侧MAX30102和DS18B20的走线尽量短远离晶振和开关电源。OLED的排线如果长要加屏蔽或者双绞。地平面尽量完整模拟地和数字地单点连接。MAX30102下面的地要干净不要走数字信号线。晶振尽量靠近MCU负载电容紧贴晶振引脚。SWD接口放在板边方便插调试器。6. 调试过程中最容易卡住的几个问题6.1 心率数据一直跳动怎么办这是最常见的问题。首先要确认传感器佩戴是否贴合MAX30102要贴在皮肤上不能有漏光。然后检查LED电流设置太小信号弱太大功耗高且可能饱和。一般红光和红外LED电流设在6到10mA之间比较合适。软件上去直流的滤波系数很关键。系数太小直流跟踪慢波形基线漂移系数太大交流分量被削弱。我一般用右移6位相当于时间常数64个采样点在50Hz采样率下约1.3秒实测比较合适。如果还是跳可以加一个中值滤波把明显异常的BPM值剔除。6.2 OLED不亮或者花屏先查I2C地址对不对用逻辑分析仪或者示波器看SCL和SDA有没有波形。如果地址对但没显示检查初始化序列是否完整SSD1306需要一系列命令配置。花屏通常是I2C速率太高或者上拉电阻不合适把速率降到100kHz试试上拉电阻换成4.7K。还有一种情况是电源问题OLED在3.3V下工作但如果稳压输出不稳显示会异常。用万用表量一下OLED的VCC引脚正常应该在3.3V左右。6.3 锂电池供电时系统反复重启这个问题的根源通常是稳压芯片的压差或者电流能力不够。当MAX30102的LED点亮瞬间电流会突然增大如果稳压芯片响应慢或者输出电容不够电压会瞬间跌落导致MCU复位。解决办法是在稳压输出端加大电容至少100uF并且选响应快的芯片。另外检查电池保护板的内阻劣质保护板内阻大大电流时压降明显。可以用示波器抓一下电池电压和3.3V输出的波形看复位瞬间有没有跌落。6.4 DS18B20读出来一直是85度85度是DS18B20的上电默认值说明温度转换没有成功或者读取时序有问题。先确认复位和存在脉冲是否正常如果存在脉冲检测不到说明接线或上拉电阻有问题。如果存在脉冲正常但读出来是85检查转换命令是否发出以及等待时间是否足够。12位精度需要750ms如果提前读就会得到旧值或默认值。还有一种可能是供电不足DS18B20在转换期间电流会增大如果电源带不动转换会失败。用示波器看DQ线上的波形正常应该有完整的复位脉冲和读写时隙。7. 从能跑到好用还差哪些优化7.1 低功耗设计的实际做法便携设备如果一直全速运行电池撑不了多久。低功耗的核心思路是让MCU和传感器在不需要工作时进入睡眠。STM32F103有Sleep、Stop、Standby三种低功耗模式Stop模式下功耗可以降到几十微安。具体做法是主循环里如果没有按键事件就让MCU进入Stop模式用RTC或者外部中断唤醒。MAX30102可以配置为每秒钟只采样几次而不是连续采样。OLED在无操作一段时间后关闭显示。这样整机平均电流可以控制在几毫安500mAh电池能用好几天。7.2 数据存储与上传的扩展如果想把数据存下来可以加一个SPI Flash或者TF卡。TF卡通过SPI接口用FatFS文件系统把心率、血氧、体温按时间戳存成CSV文件。这样后续可以用Excel或者Python做趋势分析。上传方面预留的UART接口可以接HC-05蓝牙模块或者ESP-01S WiFi模块。蓝牙模块配置成串口透传手机端用蓝牙串口助手就能收到数据。WiFi模块可以连到本地服务器或者云平台但要注意功耗会增加不少。7.3 外壳与佩戴方式的考虑做便携设备外壳不能忽略。3D打印一个简单的外壳把PCB、电池、传感器固定好。传感器要露出外壳方便贴合皮肤。手腕式的话可以用表带固定指夹式的话结构要设计成能夹住手指。佩戴方式直接影响信号质量。手腕式测量时传感器要压在桡动脉上方指夹式要保证手指不透光。我试过用黑色热缩管包裹传感器周围减少环境光干扰效果很明显。8. 写在最后的一点个人体会这个项目我从画原理图到调通花了大概三周其中有一周时间卡在血氧算法上。后来发现是红光和红外的直流分量提取窗口没对齐导致R值算出来偏差很大。把两个通道的滤波参数统一之后血氧值就稳定了。所以如果你也在做类似的东西遇到数据不对先别急着改算法回头检查一下信号采集的同步性和滤波参数的一致性往往问题就出在这些基础环节。另外元件清单里的东西不用一次买齐可以先买核心的几样把最小系统跑起来再逐步加传感器。这样即使某个模块有问题也不会影响整体进度。PCB打样建议先打一版验证确认没问题再批量做毕竟改板的时间和成本都比改代码高得多。
返回列表