xiaoshen-372360 发表于 2026-1-30 23:14:24

《2025 DigiKey AI应用创意挑战赛》基于MAXREFDES117的脉搏血样测试仪

本帖最后由 xiaoshen-372360 于 2026-2-2 11:04 编辑

基于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 代码设计
#include "main.h"
#include "i2c.h"
#include "adc.h"
#include "gpio.h"
#include "maxrefdes117.h"
#include "oled.h"

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

void SystemClock_Config(void);

int main(void) {
// 1. 系统初始化
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_ADC1_Init();
OLED_Init();// OLED初始化(若无需显示,可注释)

// 2. MAXREFDES117传感器初始化
if (MAXREFDES117_Init() != HAL_OK) {
    OLED_ShowString(0, 0, "Sensor Init Fail!");
    while (1);
}

// 3. 初始化数据结构体
memset(&pox_device, 0, sizeof(PulseOximeter_DataDef));
pox_device.spo2 = 95;
pox_device.pr = 75;

// 4. 主循环(100ms/次,实时更新数据)
while (1) {
    // 采集并处理脉搏血氧数据
    MAXREFDES117_ProcessData(&pox_device);
   
    // 采集电池电量
    MAXREFDES117_ReadBattery(&pox_device);
   
    // 异常检测与报警
    MAXREFDES117_AlarmCheck(&pox_device);
   
    // OLED显示数据(简易界面,若无需显示可注释)
    char buf;
    OLED_Clear();
    sprintf(buf, "SpO2: %d %%", pox_device.spo2);
    OLED_ShowString(0, 0, buf);
    sprintf(buf, "PR: %d bpm", pox_device.pr);
    OLED_ShowString(0, 2, buf);
    sprintf(buf, "Battery: %d %%", pox_device.battery);
    OLED_ShowString(0, 4, buf);
   
    // 延时控制循环频率
    HAL_Delay(100);
}
}

核心代码如下
#include "maxrefdes117.h"
#include "stdio.h"
#include "string.h"

/************************ 静态全局变量 ************************/
// 滑动平均滤波缓存(窗口大小5)
static uint16_t red_buf = {0};
static uint16_t ir_buf = {0};
static uint8_tbuf_index = 0;

// 脉搏波峰值检测相关变量
static uint16_t ir_peak = 0;
static uint16_t ir_valley = 65535;
static uint32_t pr_timer = 0;
static uint16_t pr_count = 0;

/************************ 传感器初始化 ************************/
HAL_StatusTypeDef MAXREFDES117_Init(void) {
uint8_t tx_data;
HAL_StatusTypeDef ret;

// 配置寄存器写入:先写寄存器地址,再写配置值(16位,大端模式)
tx_data = MAXREFDES117_REG_CONFIG;
tx_data = (MAXREFDES117_CONFIG_VAL >> 8) & 0xFF;// 高8位
tx_data = MAXREFDES117_CONFIG_VAL & 0xFF;         // 低8位

// I2C发送数据(地址+配置值)
ret = HAL_I2C_Master_Transmit(&hi2c1, MAXREFDES117_I2C_ADDR, tx_data, 3, 100);

if (ret != HAL_OK) {
    return HAL_ERROR;
}

// 延时等待传感器稳定
HAL_Delay(100);
return HAL_OK;
}

/************************ 读取原始红光/红外光数据 ************************/
HAL_StatusTypeDef MAXREFDES117_ReadRawData(uint16_t *red, uint16_t *ir) {
uint8_t rx_data;
HAL_StatusTypeDef ret;

// 1. 发送要读取的寄存器地址(红光寄存器)
uint8_t reg_addr = MAXREFDES117_REG_RED;
ret = HAL_I2C_Master_Transmit(&hi2c1, MAXREFDES117_I2C_ADDR, &reg_addr, 1, 100);
if (ret != HAL_OK) {
    return HAL_ERROR;
}

// 2. 读取4个字节数据(红光2字节+红外光2字节,大端模式)
ret = HAL_I2C_Master_Receive(&hi2c1, MAXREFDES117_I2C_ADDR, rx_data, 4, 100);
if (ret != HAL_OK) {
    return HAL_ERROR;
}

// 3. 转换为16位数据(大端转小端)
*red = (uint16_t)rx_data << 8 | rx_data;
*ir= (uint16_t)rx_data << 8 | rx_data;

return HAL_OK;
}

/************************ 滑动平均滤波函数(内部辅助) ************************/
static uint16_t MAXREFDES117_SlidingFilter(uint16_t *buf, uint16_t new_data) {
uint32_t sum = 0;

// 替换缓存中最旧的数据
buf = new_data;
buf_index = (buf_index + 1) % 5;// 循环索引

// 计算缓存平均值
for (uint8_t i = 0; i < 5; i++) {
    sum += buf;
}

return (uint16_t)(sum / 5);
}

/************************ 核心数据处理(SpO₂+PR计算) ************************/
void MAXREFDES117_ProcessData(PulseOximeter_DataDef *pox_data) {
uint16_t red_filtered, ir_filtered;
float red_ac, ir_ac;
float spo2_ratio;

// 1. 读取原始数据
if (MAXREFDES117_ReadRawData(&pox_data->red_data, &pox_data->ir_data) != HAL_OK) {
    pox_data->status = 2;// 数据读取失败,标记异常
    return;
}

// 2. 滑动平均滤波,消除随机噪声
red_filtered = MAXREFDES117_SlidingFilter(red_buf, pox_data->red_data);
ir_filtered = MAXREFDES117_SlidingFilter(ir_buf, pox_data->ir_data);

// 3. 提取交流分量(AC)= 滤波后数据 - 直流分量(DC,取缓存平均值近似)
red_ac = red_filtered - (float)(red_filtered * 0.95);// 简化DC提取(95%为基线)
ir_ac = ir_filtered - (float)(ir_filtered * 0.95);

// 4. 计算SpO₂(基于MAXIM官方简化算法)
if (ir_ac != 0) {
    spo2_ratio = red_ac / ir_ac;
    pox_data->spo2 = (uint8_t)(110 - 25 * spo2_ratio);// 简化公式,实际需校准
   
    // 限幅:确保SpO₂在70~100%范围内
    if (pox_data->spo2 > 100) pox_data->spo2 = 100;
    if (pox_data->spo2 < 70) pox_data->spo2 = 70;
} else {
    pox_data->spo2 = 95;// 无有效信号,默认正常值
}

// 5. 计算PR(脉搏率,通过红外光波形峰值检测)
pr_timer++;// 配合主循环延时,累计计时(主循环100ms/次,pr_timer=10对应1秒)

// 更新峰值和谷值
if (ir_filtered > ir_peak) ir_peak = ir_filtered;
if (ir_filtered < ir_valley) ir_valley = ir_filtered;

// 检测峰值(波形下降沿,且超过阈值)
static uint16_t ir_prev = 0;
if (ir_prev > ir_filtered && ir_prev > (ir_valley + (ir_peak - ir_valley) * 0.7)) {
    pr_count++;// 检测到一个脉搏峰值,计数+1
    ir_peak = ir_valley = ir_filtered;// 重置峰值谷值,准备下一次检测
}
ir_prev = ir_filtered;

// 每100次主循环(10秒)计算一次PR
if (pr_timer >= 100) {
    pox_data->pr = pr_count * 6;// 10秒计数 * 6 = 每分钟脉搏数(bpm)
   
    // 限幅:确保PR在30~250bpm范围内
    if (pox_data->pr > 250) pox_data->pr = 250;
    if (pox_data->pr < 30) pox_data->pr = 30;
   
    // 重置计时和计数
    pr_timer = 0;
    pr_count = 0;
}
}

/************************ 电池电量采集与计算 ************************/
void MAXREFDES117_ReadBattery(PulseOximeter_DataDef *pox_data) {
uint32_t adc_val = 0;
float bat_voltage, adc_voltage;

// 1. ADC多次采样求平均(减少误差)
for (uint8_t i = 0; i < 10; i++) {
    HAL_ADC_Start(&hadc1);
    HAL_ADC_PollForConversion(&hadc1, 10);
    adc_val += HAL_ADC_GetValue(&hadc1);
    HAL_ADC_Stop(&hadc1);
}
adc_val /= 10;

// 2. 换算ADC电压(3.3V参考电压,12位ADC)
adc_voltage = (float)adc_val * 3.3f / 4095.0f;

// 3. 换算锂电池实际电压(分压比:10K+2.2K,即5.545倍)
bat_voltage = adc_voltage * 5.545f;

// 4. 电压转电量百分比(基于3.7V锂电池放电曲线)
if (bat_voltage >= 4.2f) {
    pox_data->battery = 100;
} else if (bat_voltage >= 4.1f) {
    pox_data->battery = 90;
} else if (bat_voltage >= 4.0f) {
    pox_data->battery = 70;
} else if (bat_voltage >= 3.9f) {
    pox_data->battery = 50;
} else if (bat_voltage >= 3.8f) {
    pox_data->battery = 30;
} else if (bat_voltage >= 3.7f) {
    pox_data->battery = 10;
} else if (bat_voltage >= 3.0f) {
    pox_data->battery = 5;
    pox_data->status = 1;// 标记低电量
} else {
    pox_data->battery = 0;
    pox_data->status = 1;// 标记低电量
}
}

/************************ 异常检测与报警驱动 ************************/
void MAXREFDES117_AlarmCheck(PulseOximeter_DataDef *pox_data) {
// 1. 数据异常检测(SpO₂<90% 或 PR<60/PR>100)
if (pox_data->spo2 < 90 || pox_data->pr < 60 || pox_data->pr > 100) {
    pox_data->status = 2;
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);// 蜂鸣器报警(高电平导通)
}
// 2. 低电量报警
else if (pox_data->status == 1) {
    // 间歇报警(每1秒响一次)
    if (HAL_GetTick() % 1000 < 500) {
      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
    } else {
      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
    }
}
// 3. 正常状态,关闭报警
else {
    pox_data->status = 0;
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
}
}初始化代码
#ifndef __MAXREFDES117_H
#define __MAXREFDES117_H

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

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

// 寄存器地址定义
#define MAXREFDES117_REG_RED   0x00// 红光数据寄存器(16位)
#define MAXREFDES117_REG_IR      0x02// 红外光数据寄存器(16位)
#define MAXREFDES117_REG_CONFIG0x04// 配置寄存器
#define MAXREFDES117_REG_STATUS0x06// 状态寄存器

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

/************************ 数据结构定义 ************************/
// 脉搏血氧数据结构体
typedef struct {
uint16_t red_data;    // 红光原始数据
uint16_t ir_data;   // 红外光原始数据
uint8_tspo2;      // 血氧饱和度(70~100%)
uint16_t pr;          // 脉搏率(30~250bpm)
uint8_tbattery;   // 电池电量(0~100%)
uint8_tstatus;      // 设备状态:0-正常,1-低电量,2-数据异常
} PulseOximeter_DataDef;

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

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

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

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

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

#endif



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




页: [1]
查看完整版本: 《2025 DigiKey AI应用创意挑战赛》基于MAXREFDES117的脉搏血样测试仪