回答

收藏

[项目提交] 基于MAXREFDES117的脉搏血样测试仪

2025 DigiKey AI应用创意挑战赛 2025 DigiKey AI应用创意挑战赛 10 人阅读 | 0 人回复 | 2026-01-30

本帖最后由 xiaoshen-372360 于 2026-1-30 23:28 编辑

基于MAXREFDES117的脉搏血氧测试仪

一、项目概述
1.1 项目背景
    随着健康监测技术的普及和便携式医疗设备的快速发展,脉搏血氧饱和度(SpO₂)作为人体重要的生理指标,其实时、精准监测在家庭健康护理、运动健身、初级医疗诊断等场景中需求日益迫切。传统脉搏血氧测试仪存在体积庞大、功耗较高、续航能力不足、操作复杂等问题,难以满足用户便携式、长时间监测的需求。

基于此,本项目设计一款小型化、低功耗、高精度的脉搏血氧测试仪,采用MAXREFDES117脉搏血氧传感器作为核心检测部件,STM32F103CBT6单片机作为主控单元,集成BMS(电池管理系统)实现电源的稳定管理,搭配0.96寸液晶显示屏直观呈现监测数据,解决传统设备的痛点,为用户提供便捷、可靠的生理参数监测方案。


1.2 项目目标
   1.核心功能:实现人体脉搏血氧饱和度(SpO₂)和脉搏速率(PR)的实时采集、计算与显示,测量精度满足日常健康监测要求(SpO₂误差≤±2%,PR误差≤±1bpm)。
    2. 硬件架构:以STM32F103CBT6为主控,整合MAXREFDES117传感器、0.96寸液晶显示模块、BMS电池管理模块,实现各模块的稳定联动,设备体积小型化、便于携带。
  3.电源管理:通过BMS模块实现锂电池的充电管理、放电保护、电量监测,延长电池续航,确保设备连续工作时间不低于8小时,具备低电量报警功能。
  4. 显示功能:0.96寸液晶显示屏清晰显示SpO₂数值、脉搏速率、电池电量、监测状态,界面简洁、操作直观,支持数据实时刷新(刷新频率1次/秒)。
  5. 稳定性:设备在不同环境温度(0℃~40℃)、不同测试姿势下均能稳定工作,数据波动小,无明显失真,具备抗干扰能力。
1.3 项目意义
     本项目设计的脉搏血氧测试仪,兼具小型化、低功耗、高精度、易操作的特点,可广泛应用于家庭健康监测、户外探险、运动员生理监测等场景,为用户提供实时、便捷的健康参考数据。同时,项目采用成熟的传感器和主控芯片,成本可控、易于量产,具有一定的实用价值和市场潜力;此外,通过项目实践,可深入掌握嵌入式系统开发、传感器应用、电源管理等相关技术,为同类便携式医疗设备的设计提供参考。
二、总体设计方案
2.1 设计思路
     本项目采用“模块化设计”理念,将整个系统划分为五大核心模块:主控模块、脉搏血氧采集模块、显示模块、电源管理(BMS)模块、辅助功能模块(按键、报警)。各模块独立设计、互不干扰,通过标准化接口与主控模块连接,便于调试、维护和后期升级。
    核心工作流程:MAXREFDES117传感器采集人体指尖的光电容积脉搏信号(PPG),将模拟信号转换为数字信号后传输至STM32F103CBT6主控单元;主控单元对采集到的信号进行滤波、降噪、运算处理,计算出脉搏血氧饱和度(SpO₂)和脉搏速率(PR);同时,BMS模块实时监测锂电池的电压、电流、电量状态,将相关信息传输至主控;主控单元将处理后的生理参数、电池状态信息同步显示在0.96寸液晶屏幕上;当检测到数据异常(如SpO₂低于90%、PR超出60~100bpm范围)或低电量时,触发报警功能提醒用户。
2.2 总体架构图

2.3 核心器件选型依据
器件选型遵循“性能匹配、低功耗、低成本、易采购”的原则,核心器件选型如下:


  • 主控芯片:STM32F103CBT6,基于ARM Cortex-M3内核,主频72MHz,内置64KB Flash、20KB SRAM,具备丰富的外设接口(I2C、SPI、UART、GPIO),低功耗模式支持待机电流≤10μA,可满足传感器数据处理、显示驱动、BMS通信等功能需求,且成本适中、资料丰富,便于开发。

  • 脉搏血氧传感器:MAXREFDES117,是Maxim公司推出的集成式脉搏血氧参考设计模块,内置红光LED、红外光LED、光电探测器,支持I2C通信,测量范围SpO₂ 70%~100%,PR 30~250bpm,低功耗设计(工作电流≤10mA),体积小巧,可直接采集PPG信号并输出数字数据,无需额外设计信号调理电路,简化硬件设计。

  • 显示模块:0.96寸OLED液晶显示屏(I2C接口),分辨率128×64,对比度高、亮度可调,功耗低(工作电流≤5mA),体积小巧,可清晰显示数字和简单图标,适合便携式设备,且接口简单、易于驱动。

  • BMS模块:采用TP4056充电管理芯片+DW01A电池保护芯片组合,支持锂电池恒流恒压充电,具备过充、过放、过流、短路保护功能,可实时监测电池电量,输出电量信号至主控,确保电池使用安全,延长电池使用寿命。

  • 电源器件:3.7V 500mAh锂电池,体积小、容量适中,可满足设备连续工作8小时以上;AMS1117-3.3V稳压芯片,将锂电池电压转换为3.3V,为主控、传感器、显示屏等模块提供稳定供电。

3、软件系统设计3.1 软件设计思路
    本项目软件采用“模块化编程”理念,基于Keil MDK5开发环境,使用C语言编写,以STM32F103CBT6的HAL库为基础,将软件划分为五大核心模块:主程序模块、传感器数据采集与处理模块、显示驱动模块、BMS电源管理模块、辅助功能模块(按键处理、报警处理)。各模块独立编写函数,通过函数调用实现各模块的联动,提高代码的可读性、可维护性和可移植性。

    软件总体工作流程:系统上电后,首先执行初始化程序(主控初始化、传感器初始化、显示初始化、BMS初始化、GPIO初始化、ADC初始化);初始化完成后,系统进入待机状态,等待用户按下开机按键;用户按下开机按键后,系统启动,MAXREFDES117传感器开始采集PPG信号,主控单元对信号进行处理,计算出SpO₂和PR数值;同时,BMS模块实时监测电池状态,采集电池电压,计算剩余电量;主控单元将SpO₂、PR、电池电量、监测状态等信息传输至显示模块,同步显示;系统持续循环采集、处理、显示数据,同时检测按键操作和异常情况,当检测到关机按键、数据异常或低电量时,执行相应的处理函数(关机、报警)。

3.2 软件流程



3.3 代码设计

  1. #include "main.h"
  2. #include "i2c.h"
  3. #include "adc.h"
  4. #include "gpio.h"
  5. #include "maxrefdes117.h"
  6. #include "oled.h"

  7. // 定义脉搏血氧数据全局变量
  8. PulseOximeter_DataDef pox_device;

  9. void SystemClock_Config(void);

  10. int main(void) {
  11.   // 1. 系统初始化
  12.   HAL_Init();
  13.   SystemClock_Config();
  14.   MX_GPIO_Init();
  15.   MX_I2C1_Init();
  16.   MX_ADC1_Init();
  17.   OLED_Init();  // OLED初始化(若无需显示,可注释)
  18.   
  19.   // 2. MAXREFDES117传感器初始化
  20.   if (MAXREFDES117_Init() != HAL_OK) {
  21.     OLED_ShowString(0, 0, "Sensor Init Fail!");
  22.     while (1);
  23.   }
  24.   
  25.   // 3. 初始化数据结构体
  26.   memset(&pox_device, 0, sizeof(PulseOximeter_DataDef));
  27.   pox_device.spo2 = 95;
  28.   pox_device.pr = 75;
  29.   
  30.   // 4. 主循环(100ms/次,实时更新数据)
  31.   while (1) {
  32.     // 采集并处理脉搏血氧数据
  33.     MAXREFDES117_ProcessData(&pox_device);
  34.    
  35.     // 采集电池电量
  36.     MAXREFDES117_ReadBattery(&pox_device);
  37.    
  38.     // 异常检测与报警
  39.     MAXREFDES117_AlarmCheck(&pox_device);
  40.    
  41.     // OLED显示数据(简易界面,若无需显示可注释)
  42.     char buf[32];
  43.     OLED_Clear();
  44.     sprintf(buf, "SpO2: %d %%", pox_device.spo2);
  45.     OLED_ShowString(0, 0, buf);
  46.     sprintf(buf, "PR: %d bpm", pox_device.pr);
  47.     OLED_ShowString(0, 2, buf);
  48.     sprintf(buf, "Battery: %d %%", pox_device.battery);
  49.     OLED_ShowString(0, 4, buf);
  50.    
  51.     // 延时控制循环频率
  52.     HAL_Delay(100);
  53.   }
  54. }
复制代码


核心代码如下

  1. #include "maxrefdes117.h"
  2. #include "stdio.h"
  3. #include "string.h"

  4. /************************ 静态全局变量 ************************/
  5. // 滑动平均滤波缓存(窗口大小5)
  6. static uint16_t red_buf[5] = {0};
  7. static uint16_t ir_buf[5] = {0};
  8. static uint8_t  buf_index = 0;

  9. // 脉搏波峰值检测相关变量
  10. static uint16_t ir_peak = 0;
  11. static uint16_t ir_valley = 65535;
  12. static uint32_t pr_timer = 0;
  13. static uint16_t pr_count = 0;

  14. /************************ 传感器初始化 ************************/
  15. HAL_StatusTypeDef MAXREFDES117_Init(void) {
  16.   uint8_t tx_data[3];
  17.   HAL_StatusTypeDef ret;
  18.   
  19.   // 配置寄存器写入:先写寄存器地址,再写配置值(16位,大端模式)
  20.   tx_data[0] = MAXREFDES117_REG_CONFIG;
  21.   tx_data[1] = (MAXREFDES117_CONFIG_VAL >> 8) & 0xFF;  // 高8位
  22.   tx_data[2] = MAXREFDES117_CONFIG_VAL & 0xFF;         // 低8位
  23.   
  24.   // I2C发送数据(地址+配置值)
  25.   ret = HAL_I2C_Master_Transmit(&hi2c1, MAXREFDES117_I2C_ADDR, tx_data, 3, 100);
  26.   
  27.   if (ret != HAL_OK) {
  28.     return HAL_ERROR;
  29.   }
  30.   
  31.   // 延时等待传感器稳定
  32.   HAL_Delay(100);
  33.   return HAL_OK;
  34. }

  35. /************************ 读取原始红光/红外光数据 ************************/
  36. HAL_StatusTypeDef MAXREFDES117_ReadRawData(uint16_t *red, uint16_t *ir) {
  37.   uint8_t rx_data[4];
  38.   HAL_StatusTypeDef ret;
  39.   
  40.   // 1. 发送要读取的寄存器地址(红光寄存器)
  41.   uint8_t reg_addr = MAXREFDES117_REG_RED;
  42.   ret = HAL_I2C_Master_Transmit(&hi2c1, MAXREFDES117_I2C_ADDR, &reg_addr, 1, 100);
  43.   if (ret != HAL_OK) {
  44.     return HAL_ERROR;
  45.   }
  46.   
  47.   // 2. 读取4个字节数据(红光2字节+红外光2字节,大端模式)
  48.   ret = HAL_I2C_Master_Receive(&hi2c1, MAXREFDES117_I2C_ADDR, rx_data, 4, 100);
  49.   if (ret != HAL_OK) {
  50.     return HAL_ERROR;
  51.   }
  52.   
  53.   // 3. 转换为16位数据(大端转小端)
  54.   *red = (uint16_t)rx_data[0] << 8 | rx_data[1];
  55.   *ir  = (uint16_t)rx_data[2] << 8 | rx_data[3];
  56.   
  57.   return HAL_OK;
  58. }

  59. /************************ 滑动平均滤波函数(内部辅助) ************************/
  60. static uint16_t MAXREFDES117_SlidingFilter(uint16_t *buf, uint16_t new_data) {
  61.   uint32_t sum = 0;
  62.   
  63.   // 替换缓存中最旧的数据
  64.   buf[buf_index] = new_data;
  65.   buf_index = (buf_index + 1) % 5;  // 循环索引
  66.   
  67.   // 计算缓存平均值
  68.   for (uint8_t i = 0; i < 5; i++) {
  69.     sum += buf[i];
  70.   }
  71.   
  72.   return (uint16_t)(sum / 5);
  73. }

  74. /************************ 核心数据处理(SpO₂+PR计算) ************************/
  75. void MAXREFDES117_ProcessData(PulseOximeter_DataDef *pox_data) {
  76.   uint16_t red_filtered, ir_filtered;
  77.   float red_ac, ir_ac;
  78.   float spo2_ratio;
  79.   
  80.   // 1. 读取原始数据
  81.   if (MAXREFDES117_ReadRawData(&pox_data->red_data, &pox_data->ir_data) != HAL_OK) {
  82.     pox_data->status = 2;  // 数据读取失败,标记异常
  83.     return;
  84.   }
  85.   
  86.   // 2. 滑动平均滤波,消除随机噪声
  87.   red_filtered = MAXREFDES117_SlidingFilter(red_buf, pox_data->red_data);
  88.   ir_filtered = MAXREFDES117_SlidingFilter(ir_buf, pox_data->ir_data);
  89.   
  90.   // 3. 提取交流分量(AC)= 滤波后数据 - 直流分量(DC,取缓存平均值近似)
  91.   red_ac = red_filtered - (float)(red_filtered * 0.95);  // 简化DC提取(95%为基线)
  92.   ir_ac = ir_filtered - (float)(ir_filtered * 0.95);
  93.   
  94.   // 4. 计算SpO₂(基于MAXIM官方简化算法)
  95.   if (ir_ac != 0) {
  96.     spo2_ratio = red_ac / ir_ac;
  97.     pox_data->spo2 = (uint8_t)(110 - 25 * spo2_ratio);  // 简化公式,实际需校准
  98.    
  99.     // 限幅:确保SpO₂在70~100%范围内
  100.     if (pox_data->spo2 > 100) pox_data->spo2 = 100;
  101.     if (pox_data->spo2 < 70) pox_data->spo2 = 70;
  102.   } else {
  103.     pox_data->spo2 = 95;  // 无有效信号,默认正常值
  104.   }
  105.   
  106.   // 5. 计算PR(脉搏率,通过红外光波形峰值检测)
  107.   pr_timer++;  // 配合主循环延时,累计计时(主循环100ms/次,pr_timer=10对应1秒)
  108.   
  109.   // 更新峰值和谷值
  110.   if (ir_filtered > ir_peak) ir_peak = ir_filtered;
  111.   if (ir_filtered < ir_valley) ir_valley = ir_filtered;
  112.   
  113.   // 检测峰值(波形下降沿,且超过阈值)
  114.   static uint16_t ir_prev = 0;
  115.   if (ir_prev > ir_filtered && ir_prev > (ir_valley + (ir_peak - ir_valley) * 0.7)) {
  116.     pr_count++;  // 检测到一个脉搏峰值,计数+1
  117.     ir_peak = ir_valley = ir_filtered;  // 重置峰值谷值,准备下一次检测
  118.   }
  119.   ir_prev = ir_filtered;
  120.   
  121.   // 每100次主循环(10秒)计算一次PR
  122.   if (pr_timer >= 100) {
  123.     pox_data->pr = pr_count * 6;  // 10秒计数 * 6 = 每分钟脉搏数(bpm)
  124.    
  125.     // 限幅:确保PR在30~250bpm范围内
  126.     if (pox_data->pr > 250) pox_data->pr = 250;
  127.     if (pox_data->pr < 30) pox_data->pr = 30;
  128.    
  129.     // 重置计时和计数
  130.     pr_timer = 0;
  131.     pr_count = 0;
  132.   }
  133. }

  134. /************************ 电池电量采集与计算 ************************/
  135. void MAXREFDES117_ReadBattery(PulseOximeter_DataDef *pox_data) {
  136.   uint32_t adc_val = 0;
  137.   float bat_voltage, adc_voltage;
  138.   
  139.   // 1. ADC多次采样求平均(减少误差)
  140.   for (uint8_t i = 0; i < 10; i++) {
  141.     HAL_ADC_Start(&hadc1);
  142.     HAL_ADC_PollForConversion(&hadc1, 10);
  143.     adc_val += HAL_ADC_GetValue(&hadc1);
  144.     HAL_ADC_Stop(&hadc1);
  145.   }
  146.   adc_val /= 10;
  147.   
  148.   // 2. 换算ADC电压(3.3V参考电压,12位ADC)
  149.   adc_voltage = (float)adc_val * 3.3f / 4095.0f;
  150.   
  151.   // 3. 换算锂电池实际电压(分压比:10K+2.2K,即5.545倍)
  152.   bat_voltage = adc_voltage * 5.545f;
  153.   
  154.   // 4. 电压转电量百分比(基于3.7V锂电池放电曲线)
  155.   if (bat_voltage >= 4.2f) {
  156.     pox_data->battery = 100;
  157.   } else if (bat_voltage >= 4.1f) {
  158.     pox_data->battery = 90;
  159.   } else if (bat_voltage >= 4.0f) {
  160.     pox_data->battery = 70;
  161.   } else if (bat_voltage >= 3.9f) {
  162.     pox_data->battery = 50;
  163.   } else if (bat_voltage >= 3.8f) {
  164.     pox_data->battery = 30;
  165.   } else if (bat_voltage >= 3.7f) {
  166.     pox_data->battery = 10;
  167.   } else if (bat_voltage >= 3.0f) {
  168.     pox_data->battery = 5;
  169.     pox_data->status = 1;  // 标记低电量
  170.   } else {
  171.     pox_data->battery = 0;
  172.     pox_data->status = 1;  // 标记低电量
  173.   }
  174. }

  175. /************************ 异常检测与报警驱动 ************************/
  176. void MAXREFDES117_AlarmCheck(PulseOximeter_DataDef *pox_data) {
  177.   // 1. 数据异常检测(SpO₂<90% 或 PR<60/PR>100)
  178.   if (pox_data->spo2 < 90 || pox_data->pr < 60 || pox_data->pr > 100) {
  179.     pox_data->status = 2;
  180.     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);  // 蜂鸣器报警(高电平导通)
  181.   }
  182.   // 2. 低电量报警
  183.   else if (pox_data->status == 1) {
  184.     // 间歇报警(每1秒响一次)
  185.     if (HAL_GetTick() % 1000 < 500) {
  186.       HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
  187.     } else {
  188.       HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
  189.     }
  190.   }
  191.   // 3. 正常状态,关闭报警
  192.   else {
  193.     pox_data->status = 0;
  194.     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
  195.   }
  196. }
复制代码
初始化代码

  1. #ifndef __MAXREFDES117_H
  2. #define __MAXREFDES117_H

  3. #include "stm32f1xx_hal.h"
  4. #include "i2c.h"
  5. #include "oled.h"  // 若无需OLED,可注释相关内容
  6. #include "adc.h"

  7. /************************ MAXREFDES117 配置 ************************/
  8. // I2C从地址(ADDR引脚接地,默认0x57,左移1位适配HAL库I2C接口)
  9. #define MAXREFDES117_I2C_ADDR    0x57 << 1

  10. // 寄存器地址定义
  11. #define MAXREFDES117_REG_RED     0x00  // 红光数据寄存器(16位)
  12. #define MAXREFDES117_REG_IR      0x02  // 红外光数据寄存器(16位)
  13. #define MAXREFDES117_REG_CONFIG  0x04  // 配置寄存器
  14. #define MAXREFDES117_REG_STATUS  0x06  // 状态寄存器

  15. // 配置寄存器参数(中等亮度、10Hz采集率、16位ADC分辨率)
  16. #define MAXREFDES117_CONFIG_VAL  0x0200

  17. /************************ 数据结构定义 ************************/
  18. // 脉搏血氧数据结构体
  19. typedef struct {
  20.   uint16_t red_data;    // 红光原始数据
  21.   uint16_t ir_data;     // 红外光原始数据
  22.   uint8_t  spo2;        // 血氧饱和度(70~100%)
  23.   uint16_t pr;          // 脉搏率(30~250bpm)
  24.   uint8_t  battery;     // 电池电量(0~100%)
  25.   uint8_t  status;      // 设备状态:0-正常,1-低电量,2-数据异常
  26. } PulseOximeter_DataDef;

  27. /************************ 外部函数声明 ************************/
  28. // 初始化MAXREFDES117传感器
  29. HAL_StatusTypeDef MAXREFDES117_Init(void);

  30. // 读取红光/红外光原始数据
  31. HAL_StatusTypeDef MAXREFDES117_ReadRawData(uint16_t *red, uint16_t *ir);

  32. // 数据处理:滤波、计算SpO₂和PR
  33. void MAXREFDES117_ProcessData(PulseOximeter_DataDef *pox_data);

  34. // 电池电量采集与计算
  35. void MAXREFDES117_ReadBattery(PulseOximeter_DataDef *pox_data);

  36. // 异常检测与报警
  37. void MAXREFDES117_AlarmCheck(PulseOximeter_DataDef *pox_data);

  38. #endif
复制代码




总结

     这个项目其实做了很久,自己调试软件也是比较久,目前已知在出差,照片比较少,只有将代码发上来,整个设计还是比较简单,但是对于最初的血样设计还是可以做一个参考的,非常感谢与非网和得捷的大力支持,希望与非网越办越好!





分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /2 下一条