新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > 基于STM32指甲式脉搏血氧仪七大核心功能解析与实现

基于STM32指甲式脉搏血氧仪七大核心功能解析与实现

作者:嵌入式芯视野 时间:2025-08-04 来源:今日头条 收藏

基于的SpO2-EVM

血氧仪开发套件 血氧仪开发套件

1. 血氧饱和度测量(40%~100%,精度 ±2%@70%~100%)

技术指标解析

  • 测量范围:覆盖临床紧急低氧(40%)到正常(100%)场景,满足 ICU 重症监护与家庭健康监测需求;

  • 精度分级:70%~100% 时 ±2%(临床诊断级),40%~70% 时 ±4%(低氧预警级),符合 YY/T 0784-2010 行业标准。

方案实现

  • 硬件基础:12 位 ADC(ADS1258)采集光信号,分辨率达 0.08mV(3.3V 参考电压);双波长 LED(660nm 红光 / 940nm 近红外)分时驱动,减少环境光干扰。

  • 软件算法:采用 Beer-Lambert 定律结合校准系数查表法,如前文中SPO2_Calculate函数通过 AC/DC 比值计算 SpO2;动态温度补偿:内置温度传感器修正光吸收系数随温度的漂移。

2. 脉率测量(30~250bpm,精度 ±1bpm)

技术指标解析

  • 范围覆盖:从新生儿静息心率(30bpm)到运动极限心率(250bpm);

  • 精度控制:±1bpm 满足临床心率变异性(HRV)分析需求。

方案实现

  • 信号处理:带通滤波提取脉搏波频率(0.5~5Hz),抑制运动伪影;峰值检测算法(如前文中PulseRate_Detect函数)结合滑动平均滤波(8 点缓冲),消除异常脉冲。

  • 抗干扰设计:采用自适应阈值跟踪脉搏波幅度变化,避免弱信号漏检;周期校验机制:若相邻脉率差 > 20%,则丢弃该次测量结果。

3. 弱信号处理(弱灌注强度≤0.3%)

技术指标解析

  • 灌注指数(PI):≤0.3% 表示极弱血流(如失血性休克、低温症),传统设备易测量失败。

STM32 方案实现

  • 硬件增强:高增益仪表运放(ISL28470)放大微弱光电流信号,增益可达 1000 倍;低噪声电源(ISL9001)抑制纹波对前端信号的干扰。

  • 软件优化:自适应增益控制:当 PI<1% 时,自动提高 LED 驱动电流(如前文中NLAS4053模拟开关调节);小波变换去噪:分解信号至不同频段,保留脉搏波特征分量。

4. 彩色 OLED 同屏显示

功能架构

  • 显示内容:数值区:SpO2(大字体突出)、脉率、PI 值;波形区:实时脉搏波(128 点趋势图);状态区:电池电量、信号质量图标。

  • STM32 驱动方案:SPI 接口控制 OLED 驱动 IC(如 SSD1351),刷新率≥25fps;动态刷新率调节:弱信号时提高刷新率至 50fps,增强波形稳定性。

5. 无信号自动关机

功耗管理逻辑

  • 检测机制:连续 20 秒检测到 PI<0.2% 且 SpO2 值固定不变(如 40%),判定为无有效信号;硬件 watchdog(X5043)监控 MCU 运行状态,防止程序卡死导致功耗异常。

  • 低功耗实现:STM32 进入 STANDBY 模式,功耗 < 10μA;切断 LED、OLED 等外设电源,仅保留 RTC(ISL12022M)计时。


6. 历史数据存储与管理

存储架构

  • 硬件配置:串行 Flash(M25P16,16Mbit)可保存≥2000 条记录(每条含 SpO2、脉率、时间戳);I2C 接口 EEPROM(M24C64)存储校准参数与用户配置。

  • 软件功能:周期性存储:每 5 分钟自动保存一次数据,支持手动触发保存;数据管理:支持按日期查询、删除指定记录,FIFO 循环覆盖旧数据。

7. 屏幕旋转功能

实现方案

  • 硬件:三轴加速度传感器(LIS33DE)检测设备姿态,分辨率达 ±2g;

  • 程序示例:基于 STM32F103 的血氧仪数据处理

  • 以下是核心功能的 C 语言实现(使用 HAL 库):

/* 头文件与宏定义 */#include "stm32f10x_hal.h"#include "math.h"#define SAMPLE_RATE 1000        // 采样率1kHz#define BUFFER_DEPTH 256        // 数据缓冲区深度#define RED_LED_PORT GPIOA      // 红光LED端口#define RED_LED_PIN GPIO_PIN_0  #define IR_LED_PORT GPIOA       // 近红外LED端口#define IR_LED_PIN GPIO_PIN_1#define ADC_CHANNEL_RED 0       // 红光ADC通道#define ADC_CHANNEL_IR 1        // 近红外ADC通道/* 全局变量 */ADC_HandleTypeDef hadc;
DMA_HandleTypeDef hdma_adc;uint16_t adcBuffer[2][BUFFER_DEPTH];  // ADC数据缓冲区 {红光, 近红外}float redSignal[BUFFER_DEPTH];        // 红光信号float irSignal[BUFFER_DEPTH];         // 近红外信号uint8_t spo2Value = 97;               // 血氧饱和度uint16_t pulseRate = 72;              // 脉率(bpm)uint8_t perfusionIndex = 0;           // 灌注指数/* 函数声明 */void SystemInit(void);void MX_GPIO_Init(void);void MX_ADC_Init(void);void MX_DMA_Init(void);void SPO2_Calculate(void);void PulseRate_Detect(void);void OLED_Display(void);void PowerManagement_Check(void);/* 主函数 */int main(void){  /* 系统初始化 */
 HAL_Init();
 SystemInit();  
 /* 外设初始化 */
 MX_GPIO_Init();
 MX_ADC_Init();
 MX_DMA_Init();  
 /* 启动ADC_DMA传输 */
 HAL_ADC_Start_DMA(&hadc, (uint32_t*)adcBuffer, 2 * BUFFER_DEPTH);  
 /* 主循环 */
 while (1)
 {    /* 血氧与脉率计算 */
   SPO2_Calculate();
   PulseRate_Detect();    
   /* 显示数据 */
   OLED_Display();    
   /* 低功耗检查 */
   PowerManagement_Check();    
   /* 延时处理 */
   HAL_Delay(10);
 }
}/* ADC初始化 */void MX_ADC_Init(void){
 ADC_ChannelConfTypeDef sConfig = {0};

 hadc.Instance = ADC1;
 hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
 hadc.Init.Resolution = ADC_RESOLUTION_12B;
 hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
 hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
 hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
 hadc.Init.ContinuousConvMode = ENABLE;
 hadc.Init.NbrOfConversion = 2;  // 红光+近红外双通道
 HAL_ADC_Init(&hadc);  /* 配置红光通道 */
 sConfig.Channel = ADC_CHANNEL_0;
 sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
 sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
 HAL_ADC_ConfigChannel(&hadc, &sConfig);  
 /* 配置近红外通道 */
 sConfig.Channel = ADC_CHANNEL_1;
 HAL_ADC_ConfigChannel(&hadc, &sConfig);
}/* 血氧饱和度计算 */void SPO2_Calculate(void){  static float redAC[BUFFER_DEPTH], redDC[BUFFER_DEPTH];  static float irAC[BUFFER_DEPTH], irDC[BUFFER_DEPTH];  float ratio, spo2;  
 /* 1. 转换ADC值为电压信号(假设参考电压3.3V) */
 for (uint16_t i = 0; i < BUFFER_DEPTH; i++) {
   redSignal[i] = (float)adcBuffer[0][i] * 3.3f / 4096.0f;
   irSignal[i] = (float)adcBuffer[1][i] * 3.3f / 4096.0f;
 }  
 /* 2. 分离AC和DC分量(简化版,实际需滤波) */
 for (uint16_t i = 0; i < BUFFER_DEPTH; i++) {
   redDC[i] = redSignal[i] * 0.95f + redDC[i] * 0.05f;  // 一阶低通滤波求DC
   redAC[i] = redSignal[i] - redDC[i];                  // AC分量
   
   irDC[i] = irSignal[i] * 0.95f + irDC[i] * 0.05f;
   irAC[i] = irSignal[i] - irDC[i];
 }  
 /* 3. 计算AC/DC比值 */
 float sumRedAC = 0, sumRedDC = 0;  float sumIrAC = 0, sumIrDC = 0;  for (uint16_t i = 0; i < BUFFER_DEPTH; i++) {
   sumRedAC += fabs(redAC[i]);
   sumRedDC += fabs(redDC[i]);
   sumIrAC += fabs(irAC[i]);
   sumIrDC += fabs(irDC[i]);
 }  
 float ratioRed = sumRedAC / sumRedDC;  float ratioIr = sumIrAC / sumIrDC;  
 /* 4. 计算灌注指数(PI) */
 perfusionIndex = (uint8_t)((ratioRed + ratioIr) * 10);  
 /* 5. 根据Beer-Lambert定律计算SpO2(简化公式) */
 ratio = ratioRed / ratioIr;  if (ratio > 1.0f) ratio = 1.0f / ratio;  // 范围限定
 spo2 = 94.0f - 25.0f * ratio;           // 经验公式,实际需校准
 
 /* 6. 结果校准与限幅 */
 if (spo2 > 100) spo2 = 100;  if (spo2 < 40) spo2 = 40;
 spo2Value = (uint8_t)spo2;
}/* 脉率检测 */void PulseRate_Detect(void){  static uint32_t lastPeakTime = 0;  static float peakBuffer[8] = {0};  static uint8_t peakIndex = 0;  uint32_t currentTime = HAL_GetTick();  float maxAmplitude = 0;  uint16_t peakPosition = 0;  
 /* 1. 寻找脉搏波峰值(简化版,实际需带通滤波) */
 for (uint16_t i = 0; i < BUFFER_DEPTH; i++) {    if (redSignal[i] > maxAmplitude) {
     maxAmplitude = redSignal[i];
     peakPosition = i;
   }
 }  
 /* 2. 计算脉搏周期(需排除异常峰值) */
 if (maxAmplitude > 0.1f && currentTime - lastPeakTime > 300 && currentTime - lastPeakTime < 2000) {    uint32_t periodMs = currentTime - lastPeakTime;
   pulseRate = (uint16_t)(60000.0f / periodMs);  // ms转bpm
   lastPeakTime = currentTime;    
   /* 3. 滑动平均滤波,平滑脉率显示 */
   peakBuffer[peakIndex] = pulseRate;
   peakIndex = (peakIndex + 1) % 8;    
   float avgPulse = 0;    for (uint8_t i = 0; i < 8; i++) {
     avgPulse += peakBuffer[i];
   }
   pulseRate = (uint16_t)(avgPulse / 8);
 }
}/* OLED显示函数 */void OLED_Display(void){  /* 实际项目中需调用OLED驱动库 */
 char displayBuf[32];  
 /* 显示血氧饱和度 */
 sprintf(displayBuf, "SpO2: %d%%", spo2Value);
 OLED_DrawString(0, 0, displayBuf);  
 /* 显示脉率 */
 sprintf(displayBuf, "PR: %dbpm", pulseRate);
 OLED_DrawString(0, 20, displayBuf);  
 /* 显示灌注指数 */
 sprintf(displayBuf, "PI: %d", perfusionIndex);
 OLED_DrawString(0, 40, displayBuf);  
 /* 绘制简化脉搏波形 */
 for (uint16_t i = 0; i < 128; i++) {    uint16_t point = i * BUFFER_DEPTH / 128;    uint8_t y = (uint8_t)(redSignal[point] * 30 + 50);
   OLED_DrawPoint(i, y);
 }
}/* 低功耗管理 */void PowerManagement_Check(void){  static uint32_t noSignalTime = 0;  static uint8_t signalStatus = 0;  
 /* 1. 检测信号有效性(通过灌注指数判断) */
 if (perfusionIndex > 1) {
   signalStatus = 1;
   noSignalTime = HAL_GetTick();
 } else {    if (signalStatus) {
     signalStatus = 0;
     noSignalTime = HAL_GetTick();
   }
 }  
 /* 2. 无信号超过20秒则关机 */
 if (!signalStatus && HAL_GetTick() - noSignalTime > 20000) {    /* 关闭外设,进入待机模式 */
   HAL_ADC_Stop_DMA(&hadc);
   HAL_GPIO_WritePin(RED_LED_PORT, RED_LED_PIN, GPIO_PIN_RESET);
   HAL_GPIO_WritePin(IR_LED_PORT, IR_LED_PIN, GPIO_PIN_RESET);
   OLED_PowerOff();    
   /* 进入低功耗模式 */
   HAL_PWR_EnterSTANDBYMode();
 }
}
  • 应用场景:支持手持、桌面等多场景使用,自动适配显示方向。

功能协同与临床价值示意图


典型应用案例

  • 秦皇岛康泰医学:采用 STM32F101C6T6 方案,结合 LIS33DE 与 M25P80 Flash,实现 24 小时连续监测,数据可通过 USB 导出至医院 HIS 系统;

  • 北京超思电子:在远程血氧监护仪中加入 4G 通信模块,当 SpO2<90% 且持续 5 分钟时,自动向家属手机推送报警信息,结合历史数据存储功能,支持医生远程调阅趋势图。

技术对比表(STM32 方案 vs 传统方案)

功能点

STM32 方案优势

传统方案局限

弱信号处理

自适应增益 + 小波去噪,PI≤0.3% 可用

PI<1% 时测量失效

数据存储

16Mbit Flash 支持 7 天连续记录

仅保存最近 200 条记录

屏幕旋转

实时姿态检测,用户体验更友好

固定方向显示,视角受限

功耗控制

STANDBY 模式 < 10μA,续航≥15 小时

待机功耗 > 50μA,续航 < 8 小时



关键词: STM32

评论


相关推荐

技术专区

关闭