本帖最后由 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 代码设计
- #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[32];
- 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[5] = {0};
- static uint16_t ir_buf[5] = {0};
- static uint8_t buf_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[3];
- HAL_StatusTypeDef ret;
-
- // 配置寄存器写入:先写寄存器地址,再写配置值(16位,大端模式)
- tx_data[0] = MAXREFDES117_REG_CONFIG;
- tx_data[1] = (MAXREFDES117_CONFIG_VAL >> 8) & 0xFF; // 高8位
- tx_data[2] = 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[4];
- HAL_StatusTypeDef ret;
-
- // 1. 发送要读取的寄存器地址(红光寄存器)
- uint8_t reg_addr = MAXREFDES117_REG_RED;
- ret = HAL_I2C_Master_Transmit(&hi2c1, MAXREFDES117_I2C_ADDR, ®_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[0] << 8 | rx_data[1];
- *ir = (uint16_t)rx_data[2] << 8 | rx_data[3];
-
- return HAL_OK;
- }
- /************************ 滑动平均滤波函数(内部辅助) ************************/
- static uint16_t MAXREFDES117_SlidingFilter(uint16_t *buf, uint16_t new_data) {
- uint32_t sum = 0;
-
- // 替换缓存中最旧的数据
- buf[buf_index] = new_data;
- buf_index = (buf_index + 1) % 5; // 循环索引
-
- // 计算缓存平均值
- for (uint8_t i = 0; i < 5; i++) {
- sum += buf[i];
- }
-
- 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_CONFIG 0x04 // 配置寄存器
- #define MAXREFDES117_REG_STATUS 0x06 // 状态寄存器
- // 配置寄存器参数(中等亮度、10Hz采集率、16位ADC分辨率)
- #define MAXREFDES117_CONFIG_VAL 0x0200
- /************************ 数据结构定义 ************************/
- // 脉搏血氧数据结构体
- typedef struct {
- uint16_t red_data; // 红光原始数据
- uint16_t ir_data; // 红外光原始数据
- uint8_t spo2; // 血氧饱和度(70~100%)
- uint16_t pr; // 脉搏率(30~250bpm)
- uint8_t battery; // 电池电量(0~100%)
- uint8_t status; // 设备状态: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
复制代码
总结
这个项目其实做了很久,自己调试软件也是比较久,目前已知在出差,照片比较少,只有将代码发上来,整个设计还是比较简单,但是对于最初的血样设计还是可以做一个参考的,非常感谢与非网和得捷的大力支持,希望与非网越办越好!
|