STM32 MAX30102 血氧浓度(SpO2)和心率测量方案

MAX30102血氧检测解决方案,包含I2C驱动、数据采集、滤波算法、心率计算、血氧算法


一、MAX30102特性

参数 值 说明
LED波长 660nm(红), 880nm(IR) 双波长
ADC分辨率 18位 可调
采样率 50-3200Hz 可编程
接口 I2C 0x57地址
功耗 1.8V/600µA 低功耗
血氧范围 0-100% ±2%精度
心率范围 30-250BPM ±3BPM精度

推荐配置:

  • 采样率:100Hz
  • LED电流:红光=6.4mA, IR=24mA
  • ADC分辨率:18位
  • 算法:PPG波形分析
  • 滤波:带通0.5-5Hz

二、硬件连接

复制代码
STM32F103       MAX30102
3.3V  →  VIN
GND   →  GND
PB6   →  SCL
PB7   →  SDA
PB5   →  INT (中断,可选)

外部电路:
VDD → 1.8V LDO (可选)
INT → 10k上拉电阻
SCL/SDA → 4.7k上拉电阻

三、程序架构

复制代码
MAX30102_SpO2/
├── max30102.h
├── max30102.c
├── spo2_algorithm.c
├── heart_rate.c
├── filter.c
└── main.c

四、MAX30102驱动程序

1、头文件定义

c 复制代码
// max30102.h
#ifndef MAX30102_H
#define MAX30102_H

#include "stm32f1xx_hal.h"

// MAX30102 I2C地址
#define MAX30102_I2C_ADDR    0x57

// 寄存器地址
#define MAX30102_REG_INT_STATUS     0x00
#define MAX30102_REG_INT_ENABLE     0x01
#define MAX30102_REG_FIFO_WR_PTR    0x02
#define MAX30102_REG_OVF_COUNTER    0x03
#define MAX30102_REG_FIFO_RD_PTR    0x04
#define MAX30102_REG_FIFO_DATA      0x05
#define MAX30102_REG_FIFO_CONFIG    0x06
#define MAX30102_REG_MODE_CONFIG    0x07
#define MAX30102_REG_SPO2_CONFIG    0x08
#define MAX30102_REG_LED1_PA        0x09
#define MAX30102_REG_LED2_PA        0x0A
#define MAX30102_REG_PILOT_PA       0x0B
#define MAX30102_REG_MULTI_LED_CTRL1 0x0C
#define MAX30102_REG_MULTI_LED_CTRL2 0x0D
#define MAX30102_REG_TEMP_INT       0x1F
#define MAX30102_REG_TEMP_FRAC      0x20
#define MAX30102_REG_TEMP_CONFIG    0x21
#define MAX30102_REG_REV_ID         0xFE
#define MAX30102_REG_PART_ID        0xFF

// 模式配置
#define MAX30102_MODE_HR            0x02
#define MAX30102_MODE_SPO2          0x03
#define MAX30102_MODE_MULTI_LED     0x07

// 中断配置
#define MAX30102_INT_A_FULL         (1<<7)
#define MAX30102_INT_PPG_RDY        (1<<6)
#define MAX30102_INT_ALC_OVF        (1<<5)
#define MAX30102_INT_TEMP_RDY       (1<<1)
#define MAX30102_INT_PWR_RDY        (1<<0)

// LED电流设置
#define MAX30102_LED_CURRENT_0MA    0x00
#define MAX30102_LED_CURRENT_6_4MA  0x24
#define MAX30102_LED_CURRENT_12_8MA 0x49
#define MAX30102_LED_CURRENT_19_2MA 0x6D
#define MAX30102_LED_CURRENT_24MA   0x7F

// 采样率
#define MAX30102_SAMPLERATE_50      0x00
#define MAX30102_SAMPLERATE_100     0x01
#define MAX30102_SAMPLERATE_200     0x02
#define MAX30102_SAMPLERATE_400     0x03
#define MAX30102_SAMPLERATE_800     0x04
#define MAX30102_SAMPLERATE_1000    0x05
#define MAX30102_SAMPLERATE_1600    0x06
#define MAX30102_SAMPLERATE_3200    0x07

// ADC分辨率
#define MAX30102_ADCRES_15BIT       0x00
#define MAX30102_ADCRES_16BIT       0x01
#define MAX30102_ADCRES_17BIT       0x02
#define MAX30102_ADCRES_18BIT       0x03

// FIFO平均采样数
#define MAX30102_SAMPLEAVG_1        0x00
#define MAX30102_SAMPLEAVG_2        0x01
#define MAX30102_SAMPLEAVG_4        0x02
#define MAX30102_SAMPLEAVG_8        0x03
#define MAX30102_SAMPLEAVG_16       0x04
#define MAX30102_SAMPLEAVG_32       0x05

// 数据结构
typedef struct {
    uint32_t red;      // 红光ADC值
    uint32_t ir;       // 红外ADC值
    uint32_t timestamp; // 时间戳
} MAX30102_Data_t;

// 心率和血氧结果
typedef struct {
    float heart_rate;      // 心率 (BPM)
    float spo2;           // 血氧饱和度 (%)
    uint8_t confidence;   // 置信度 (0-100)
    uint8_t valid;        // 数据是否有效
} MAX30102_Result_t;

// 滤波器状态
typedef struct {
    float red_dc;         // 红光直流分量
    float ir_dc;         // 红外直流分量
    float red_ac;         // 红光交流分量
    float ir_ac;         // 红外交流分量
} MAX30102_Filter_t;

// 函数声明
void MAX30102_Init(void);
void MAX30102_Reset(void);
uint8_t MAX30102_ReadID(void);
void MAX30102_Setup(void);
void MAX30102_SetLEDCurrent(uint8_t red_current, uint8_t ir_current);
void MAX30102_SetSamplingRate(uint8_t samplerate);
void MAX30102_SetADCRange(uint8_t adc_range);
void MAX30102_ReadFIFO(MAX30102_Data_t *data, uint8_t num_samples);
uint8_t MAX30102_GetNumSamples(void);
void MAX30102_ClearFIFO(void);
float MAX30102_ReadTemperature(void);
MAX30102_Result_t MAX30102_ProcessData(MAX30102_Data_t *data, uint16_t num_samples);
void MAX30102_StartReading(void);
void MAX30102_StopReading(void);

#endif

2、I2C驱动函数

c 复制代码
// max30102_i2c.c
#include "max30102.h"

// I2C句柄
extern I2C_HandleTypeDef hi2c1;

// I2C写寄存器
uint8_t MAX30102_WriteReg(uint8_t reg, uint8_t value)
{
    uint8_t buffer[2] = {reg, value};
    
    HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(&hi2c1, MAX30102_I2C_ADDR << 1, 
                                                       buffer, 2, 100);
    if(status != HAL_OK)
    {
        return 0;
    }
    return 1;
}

// I2C读寄存器
uint8_t MAX30102_ReadReg(uint8_t reg)
{
    uint8_t value = 0;
    
    // 发送寄存器地址
    HAL_I2C_Master_Transmit(&hi2c1, MAX30102_I2C_ADDR << 1, &reg, 1, 100);
    
    // 读取数据
    HAL_I2C_Master_Receive(&hi2c1, MAX30102_I2C_ADDR << 1, &value, 1, 100);
    
    return value;
}

// I2C批量读取
void MAX30102_ReadMultiReg(uint8_t reg, uint8_t *buffer, uint8_t len)
{
    // 发送寄存器地址
    HAL_I2C_Master_Transmit(&hi2c1, MAX30102_I2C_ADDR << 1, &reg, 1, 100);
    
    // 读取多个数据
    HAL_I2C_Master_Receive(&hi2c1, MAX30102_I2C_ADDR << 1, buffer, len, 100);
}

3、MAX30102初始化

c 复制代码
// max30102.c
#include "max30102.h"
#include <math.h>
#include <string.h>

// 全局变量
static uint8_t max30102_initialized = 0;
static uint32_t sample_counter = 0;
static float temperature = 0.0f;

// 初始化MAX30102
void MAX30102_Init(void)
{
    // 1. 复位
    MAX30102_Reset();
    HAL_Delay(10);
    
    // 2. 读取ID验证
    uint8_t part_id = MAX30102_ReadID();
    if(part_id != 0x15)  // MAX30102的PART ID是0x15
    {
        printf("MAX30102 ID错误: 0x%02X (应该是0x15)\r\n", part_id);
        return;
    }
    
    printf("MAX30102检测成功 (ID: 0x%02X)\r\n", part_id);
    
    // 3. 配置传感器
    MAX30102_Setup();
    
    // 4. 清除FIFO
    MAX30102_ClearFIFO();
    
    max30102_initialized = 1;
    
    printf("MAX30102初始化完成\r\n");
}

// 复位传感器
void MAX30102_Reset(void)
{
    MAX30102_WriteReg(MAX30102_REG_MODE_CONFIG, 0x40);
    HAL_Delay(10);
}

// 读取ID
uint8_t MAX30102_ReadID(void)
{
    uint8_t id = MAX30102_ReadReg(MAX30102_REG_PART_ID);
    return id;
}

// 配置传感器参数
void MAX30102_Setup(void)
{
    // 1. 设置FIFO平均采样数 (8个样本平均)
    uint8_t fifo_config = (MAX30102_SAMPLEAVG_8 << 5);
    MAX30102_WriteReg(MAX30102_REG_FIFO_CONFIG, fifo_config);
    
    // 2. 设置采样率(100Hz)和ADC分辨率(18位)
    uint8_t spo2_config = (MAX30102_SAMPLERATE_100 << 2) | MAX30102_ADCRES_18BIT;
    MAX30102_WriteReg(MAX30102_REG_SPO2_CONFIG, spo2_config);
    
    // 3. 设置LED电流 (红光6.4mA, 红外24mA)
    MAX30102_SetLEDCurrent(MAX30102_LED_CURRENT_6_4MA, MAX30102_LED_CURRENT_24MA);
    
    // 4. 设置多LED控制 (启用红光和红外LED)
    MAX30102_WriteReg(MAX30102_REG_MULTI_LED_CTRL1, 0x21);  // LED1=红光, LED2=红外
    MAX30102_WriteReg(MAX30102_REG_MULTI_LED_CTRL2, 0x00);
    
    // 5. 设置模式为血氧模式
    MAX30102_WriteReg(MAX30102_REG_MODE_CONFIG, MAX30102_MODE_SPO2);
    
    // 6. 启用FIFO几乎满中断
    MAX30102_WriteReg(MAX30102_REG_INT_ENABLE, MAX30102_INT_A_FULL);
    
    HAL_Delay(10);
}

// 设置LED电流
void MAX30102_SetLEDCurrent(uint8_t red_current, uint8_t ir_current)
{
    MAX30102_WriteReg(MAX30102_REG_LED1_PA, red_current);
    MAX30102_WriteReg(MAX30102_REG_LED2_PA, ir_current);
}

// 设置采样率
void MAX30102_SetSamplingRate(uint8_t samplerate)
{
    uint8_t spo2_config = MAX30102_ReadReg(MAX30102_REG_SPO2_CONFIG);
    spo2_config = (spo2_config & 0xE3) | (samplerate << 2);
    MAX30102_WriteReg(MAX30102_REG_SPO2_CONFIG, spo2_config);
}

// 设置ADC范围
void MAX30102_SetADCRange(uint8_t adc_range)
{
    uint8_t spo2_config = MAX30102_ReadReg(MAX30102_REG_SPO2_CONFIG);
    spo2_config = (spo2_config & 0xFC) | adc_range;
    MAX30102_WriteReg(MAX30102_REG_SPO2_CONFIG, spo2_config);
}

// 获取FIFO中样本数量
uint8_t MAX30102_GetNumSamples(void)
{
    uint8_t write_ptr = MAX30102_ReadReg(MAX30102_REG_FIFO_WR_PTR);
    uint8_t read_ptr = MAX30102_ReadReg(MAX30102_REG_FIFO_RD_PTR);
    
    uint8_t num_samples = (write_ptr - read_ptr) & 0x1F;
    
    return num_samples;
}

// 读取FIFO数据
void MAX30102_ReadFIFO(MAX30102_Data_t *data, uint8_t num_samples)
{
    if(num_samples == 0) return;
    
    uint8_t buffer[6 * num_samples];  // 每个样本6字节
    
    // 读取FIFO数据
    MAX30102_ReadMultiReg(MAX30102_REG_FIFO_DATA, buffer, 6 * num_samples);
    
    // 解析数据
    for(int i = 0; i < num_samples; i++)
    {
        uint8_t *sample = &buffer[i * 6];
        
        // 18位数据 (最高2位是无效的)
        data[i].ir = ((sample[0] << 16) | (sample[1] << 8) | sample[2]) & 0x3FFFF;
        data[i].red = ((sample[3] << 16) | (sample[4] << 8) | sample[5]) & 0x3FFFF;
        data[i].timestamp = HAL_GetTick();
        
        sample_counter++;
    }
}

// 清除FIFO
void MAX30102_ClearFIFO(void)
{
    MAX30102_WriteReg(MAX30102_REG_FIFO_WR_PTR, 0x00);
    MAX30102_WriteReg(MAX30102_REG_OVF_COUNTER, 0x00);
    MAX30102_WriteReg(MAX30102_REG_FIFO_RD_PTR, 0x00);
}

// 读取温度
float MAX30102_ReadTemperature(void)
{
    // 启动温度转换
    MAX30102_WriteReg(MAX30102_REG_TEMP_CONFIG, 0x01);
    
    // 等待转换完成
    HAL_Delay(100);
    
    // 读取温度整数部分
    uint8_t temp_int = MAX30102_ReadReg(MAX30102_REG_TEMP_INT);
    
    // 读取温度小数部分
    uint8_t temp_frac = MAX30102_ReadReg(MAX30102_REG_TEMP_FRAC);
    
    // 计算温度 (补码表示)
    int8_t temp_int_signed = (int8_t)temp_int;
    float temp = temp_int_signed + (temp_frac * 0.0625f);
    
    temperature = temp;
    return temp;
}

// 开始读取
void MAX30102_StartReading(void)
{
    // 清除FIFO
    MAX30102_ClearFIFO();
    
    // 设置模式为血氧模式
    MAX30102_WriteReg(MAX30102_REG_MODE_CONFIG, MAX30102_MODE_SPO2);
    
    printf("开始采集数据...\r\n");
}

// 停止读取
void MAX30102_StopReading(void)
{
    // 设置模式为待机
    MAX30102_WriteReg(MAX30102_REG_MODE_CONFIG, 0x00);
    
    printf("停止采集数据...\r\n");
}

4、滤波算法

c 复制代码
// filter.c
#include "filter.h"
#include <math.h>

// 移动平均滤波器
typedef struct {
    float buffer[8];
    uint8_t index;
    float sum;
    uint8_t size;
} MovingAverage_Filter;

// 巴特沃斯带通滤波器系数
typedef struct {
    float x[3];  // 输入
    float y[3];  // 输出
    float b[3];  // 分子系数
    float a[3];  // 分母系数
} Butterworth_Filter;

// DC移除滤波器
typedef struct {
    float alpha;
    float dc_w;
    float prev_w;
} DC_Remover;

// 初始化移动平均滤波器
void MovingAverage_Init(MovingAverage_Filter* filter, uint8_t size)
{
    memset(filter->buffer, 0, sizeof(filter->buffer));
    filter->index = 0;
    filter->sum = 0.0f;
    filter->size = (size > 8) ? 8 : size;
}

// 移动平均滤波
float MovingAverage_Filter_Update(MovingAverage_Filter* filter, float new_value)
{
    filter->sum -= filter->buffer[filter->index];
    filter->buffer[filter->index] = new_value;
    filter->sum += new_value;
    filter->index = (filter->index + 1) % filter->size;
    
    return filter->sum / filter->size;
}

// 初始化DC移除器
void DC_Remover_Init(DC_Remover* filter, float alpha)
{
    filter->alpha = alpha;
    filter->dc_w = 0.0f;
    filter->prev_w = 0.0f;
}

// DC移除滤波
float DC_Remover_Update(DC_Remover* filter, float input)
{
    float w = input + filter->alpha * filter->prev_w;
    float result = w - filter->prev_w;
    
    filter->prev_w = w;
    filter->dc_w = w;
    
    return result;
}

// 初始化巴特沃斯带通滤波器 (0.5-5Hz, 100Hz采样率)
void Butterworth_BP_Init(Butterworth_Filter* filter)
{
    // 二阶带通滤波器系数 (0.5-5Hz, 100Hz)
    filter->b[0] = 0.020083365564211;
    filter->b[1] = 0.0;
    filter->b[2] = -0.020083365564211;
    
    filter->a[0] = 1.0;
    filter->a[1] = -1.561018075800718;
    filter->a[2] = 0.641351538057563;
    
    // 清零历史数据
    for(int i = 0; i < 3; i++) {
        filter->x[i] = 0.0f;
        filter->y[i] = 0.0f;
    }
}

// 巴特沃斯带通滤波
float Butterworth_BP_Update(Butterworth_Filter* filter, float input)
{
    // 更新输入历史
    filter->x[2] = filter->x[1];
    filter->x[1] = filter->x[0];
    filter->x[0] = input;
    
    // 更新输出历史
    filter->y[2] = filter->y[1];
    filter->y[1] = filter->y[0];
    
    // 计算新输出
    filter->y[0] = filter->b[0] * filter->x[0] + 
                   filter->b[1] * filter->x[1] + 
                   filter->b[2] * filter->x[2] - 
                   filter->a[1] * filter->y[1] - 
                   filter->a[2] * filter->y[2];
    
    return filter->y[0];
}

// 中值滤波器
float Median_Filter(float* buffer, uint8_t size)
{
    float temp;
    
    // 冒泡排序
    for(int i = 0; i < size - 1; i++) {
        for(int j = 0; j < size - i - 1; j++) {
            if(buffer[j] > buffer[j + 1]) {
                temp = buffer[j];
                buffer[j] = buffer[j + 1];
                buffer[j + 1] = temp;
            }
        }
    }
    
    // 返回中值
    return buffer[size / 2];
}

// 低通滤波器
float LowPass_Filter(float input, float* prev_output, float alpha)
{
    float output = alpha * input + (1.0f - alpha) * (*prev_output);
    *prev_output = output;
    return output;
}

5、心率检测算法

c 复制代码
// heart_rate.c
#include "heart_rate.h"
#include <math.h>

// 心率检测器结构
typedef struct {
    float signal[BUFFER_SIZE];  // 信号缓冲区
    float filtered[BUFFER_SIZE]; // 滤波后信号
    uint32_t timestamps[BUFFER_SIZE]; // 时间戳
    uint16_t index;              // 当前索引
    uint8_t buffer_full;         // 缓冲区是否已满
    
    // 峰值检测
    float threshold;            // 检测阈值
    uint32_t last_peak_time;    // 上一个峰值时间
    float peak_values[10];      // 峰值存储
    uint32_t peak_times[10];    // 峰值时间
    uint8_t peak_index;         // 峰值索引
    
    // 心率计算
    float heart_rate;           // 当前心率
    float heart_rate_smooth;    // 平滑心率
    uint8_t confidence;         // 置信度
} HeartRate_Detector;

static HeartRate_Detector hr_detector = {0};

// 初始化心率检测器
void HeartRate_Init(void)
{
    memset(&hr_detector, 0, sizeof(HeartRate_Detector));
    hr_detector.threshold = 1000.0f;  // 初始阈值
    hr_detector.confidence = 0;
}

// 添加新数据点
void HeartRate_AddData(float value, uint32_t timestamp)
{
    // 保存数据
    hr_detector.signal[hr_detector.index] = value;
    hr_detector.timestamps[hr_detector.index] = timestamp;
    
    // 更新索引
    hr_detector.index++;
    if(hr_detector.index >= BUFFER_SIZE) {
        hr_detector.index = 0;
        hr_detector.buffer_full = 1;
    }
}

// 检测峰值
uint8_t HeartRate_DetectPeak(float value, uint32_t timestamp)
{
    static float last_value = 0;
    static float max_value = 0;
    static uint8_t rising = 0;
    static uint32_t last_cross_time = 0;
    
    uint8_t peak_detected = 0;
    
    // 寻找上升沿
    if(value > hr_detector.threshold && !rising) {
        rising = 1;
        max_value = value;
        last_cross_time = timestamp;
    }
    
    // 在上升沿中寻找最大值
    if(rising && value > max_value) {
        max_value = value;
    }
    
    // 检测下降沿
    if(rising && value < hr_detector.threshold) {
        rising = 0;
        
        // 确认这是有效的峰值
        if(max_value > hr_detector.threshold * 1.2f) {
            // 保存峰值
            hr_detector.peak_values[hr_detector.peak_index] = max_value;
            hr_detector.peak_times[hr_detector.peak_index] = last_cross_time;
            
            // 更新阈值 (自适应)
            hr_detector.threshold = 0.8f * hr_detector.threshold + 0.2f * (max_value * 0.6f);
            
            // 计算心率
            if(hr_detector.peak_index > 0) {
                uint32_t time_diff = timestamp - hr_detector.last_peak_time;
                if(time_diff > 0) {
                    // 计算瞬时心率 (BPM)
                    float instant_hr = 60000.0f / time_diff;  // 60秒/时间差(ms)
                    
                    // 有效性检查
                    if(instant_hr >= 30.0f && instant_hr <= 250.0f) {
                        // 平滑心率
                        hr_detector.heart_rate = 0.8f * hr_detector.heart_rate + 0.2f * instant_hr;
                        hr_detector.confidence = 100;  // 高置信度
                        
                        peak_detected = 1;
                    }
                }
            }
            
            hr_detector.last_peak_time = last_cross_time;
            hr_detector.peak_index = (hr_detector.peak_index + 1) % 10;
        }
    }
    
    last_value = value;
    return peak_detected;
}

// 计算心率
float HeartRate_Calculate(void)
{
    if(hr_detector.confidence < 50) {
        return 0.0f;  // 置信度太低
    }
    
    return hr_detector.heart_rate;
}

// 获取置信度
uint8_t HeartRate_GetConfidence(void)
{
    return hr_detector.confidence;
}

// 通过自相关计算心率
float HeartRate_Calculate_Autocorrelation(void)
{
    if(!hr_detector.buffer_full) {
        return 0.0f;
    }
    
    float acf[BUFFER_SIZE/2] = {0};
    float max_correlation = 0;
    uint16_t max_lag = 0;
    
    // 计算自相关
    for(uint16_t lag = 0; lag < BUFFER_SIZE/2; lag++) {
        float sum = 0;
        for(uint16_t i = 0; i < BUFFER_SIZE - lag; i++) {
            sum += hr_detector.filtered[i] * hr_detector.filtered[i + lag];
        }
        acf[lag] = sum;
        
        // 寻找第一个正峰值 (跳过lag=0)
        if(lag > 20 && acf[lag] > max_correlation) {
            max_correlation = acf[lag];
            max_lag = lag;
        }
    }
    
    if(max_lag > 0) {
        // 计算心率 (BPM)
        float sampling_interval = (hr_detector.timestamps[1] - hr_detector.timestamps[0]);
        float heart_rate = 60000.0f / (max_lag * sampling_interval);
        
        // 有效性检查
        if(heart_rate >= 30.0f && heart_rate <= 250.0f) {
            return heart_rate;
        }
    }
    
    return 0.0f;
}

6、血氧算法 (SpO2)

c 复制代码
// spo2_algorithm.c
#include "spo2_algorithm.h"
#include <math.h>

// 血氧计算结构
typedef struct {
    float red_ac;        // 红光交流分量
    float red_dc;        // 红光直流分量
    float ir_ac;         // 红外交流分量
    float ir_dc;         // 红外直流分量
    
    float red_buffer[BUFFER_SIZE];  // 红光缓冲区
    float ir_buffer[BUFFER_SIZE];   // 红外缓冲区
    uint16_t buffer_index;
    
    // 统计
    float R_value;       // R = (red_ac/red_dc) / (ir_ac/ir_dc)
    float spo2;          // 血氧饱和度
    uint8_t confidence;  // 置信度
} SpO2_Calculator;

static SpO2_Calculator spo2_calc = {0};

// 初始化血氧计算器
void SpO2_Init(void)
{
    memset(&spo2_calc, 0, sizeof(SpO2_Calculator));
    spo2_calc.confidence = 0;
}

// 添加新数据点
void SpO2_AddData(float red_value, float ir_value)
{
    // 保存原始数据
    spo2_calc.red_buffer[spo2_calc.buffer_index] = red_value;
    spo2_calc.ir_buffer[spo2_calc.buffer_index] = ir_value;
    
    spo2_calc.buffer_index = (spo2_calc.buffer_index + 1) % BUFFER_SIZE;
}

// 计算DC分量
void SpO2_CalculateDC(void)
{
    if(spo2_calc.buffer_index < BUFFER_SIZE) {
        return;  // 缓冲区未满
    }
    
    // 计算红光DC分量 (平均值)
    float red_sum = 0, ir_sum = 0;
    for(int i = 0; i < BUFFER_SIZE; i++) {
        red_sum += spo2_calc.red_buffer[i];
        ir_sum += spo2_calc.ir_buffer[i];
    }
    
    spo2_calc.red_dc = red_sum / BUFFER_SIZE;
    spo2_calc.ir_dc = ir_sum / BUFFER_SIZE;
}

// 计算AC分量
void SpO2_CalculateAC(void)
{
    if(spo2_calc.buffer_index < BUFFER_SIZE) {
        return;
    }
    
    // 计算红光AC分量 (标准差)
    float red_ac_sum = 0, ir_ac_sum = 0;
    for(int i = 0; i < BUFFER_SIZE; i++) {
        float red_diff = spo2_calc.red_buffer[i] - spo2_calc.red_dc;
        float ir_diff = spo2_calc.ir_buffer[i] - spo2_calc.ir_dc;
        
        red_ac_sum += red_diff * red_diff;
        ir_ac_sum += ir_diff * ir_diff;
    }
    
    spo2_calc.red_ac = sqrtf(red_ac_sum / BUFFER_SIZE);
    spo2_calc.ir_ac = sqrtf(ir_ac_sum / BUFFER_SIZE);
}

// 计算R值
void SpO2_CalculateR(void)
{
    if(spo2_calc.red_dc < 1.0f || spo2_calc.ir_dc < 1.0f) {
        return;
    }
    
    // 计算R = (red_ac/red_dc) / (ir_ac/ir_dc)
    float red_ratio = spo2_calc.red_ac / spo2_calc.red_dc;
    float ir_ratio = spo2_calc.ir_ac / spo2_calc.ir_dc;
    
    if(ir_ratio > 0.01f) {  // 避免除零
        spo2_calc.R_value = red_ratio / ir_ratio;
    }
}

// 计算血氧饱和度
float SpO2_Calculate(void)
{
    // 计算分量
    SpO2_CalculateDC();
    SpO2_CalculateAC();
    SpO2_CalculateR();
    
    // 有效性检查
    if(spo2_calc.red_dc < 1000.0f || spo2_calc.ir_dc < 1000.0f) {
        spo2_calc.confidence = 0;
        return 0.0f;
    }
    
    if(spo2_calc.R_value < 0.1f || spo2_calc.R_value > 3.0f) {
        spo2_calc.confidence = 0;
        return 0.0f;
    }
    
    // 通过经验公式计算血氧
    // 这里使用经验公式: SpO2 = 110 - 25 * R
    // 注意: 实际应用中需要校准
    
    float spo2 = 110.0f - 25.0f * spo2_calc.R_value;
    
    // 限制范围
    if(spo2 < 0.0f) spo2 = 0.0f;
    if(spo2 > 100.0f) spo2 = 100.0f;
    
    // 计算置信度
    float ac_ratio = spo2_calc.red_ac / spo2_calc.ir_ac;
    if(ac_ratio > 0.5f && ac_ratio < 2.0f) {
        spo2_calc.confidence = 100;
    } else {
        spo2_calc.confidence = 50;
    }
    
    spo2_calc.spo2 = spo2;
    return spo2;
}

// 通过查找表计算血氧
float SpO2_Calculate_LookupTable(float R)
{
    // 经验查找表 (R值 -> SpO2)
    static const float lookup_table[][2] = {
        {0.4, 100.0},  // R=0.4 -> 100%
        {0.8, 97.0},   // R=0.8 -> 97%
        {1.0, 95.0},   // R=1.0 -> 95%
        {1.2, 93.0},   // R=1.2 -> 93%
        {1.4, 90.0},   // R=1.4 -> 90%
        {1.6, 87.0},   // R=1.6 -> 87%
        {1.8, 84.0},   // R=1.8 -> 84%
        {2.0, 80.0},   // R=2.0 -> 80%
        {2.2, 75.0},   // R=2.2 -> 75%
        {2.4, 70.0},   // R=2.4 -> 70%
        {3.0, 50.0}    // R=3.0 -> 50%
    };
    
    // 查找最近的R值
    for(int i = 0; i < 10; i++) {
        if(R <= lookup_table[i][0]) {
            if(i == 0) {
                return lookup_table[0][1];
            }
            
            // 线性插值
            float R1 = lookup_table[i-1][0];
            float R2 = lookup_table[i][0];
            float SpO2_1 = lookup_table[i-1][1];
            float SpO2_2 = lookup_table[i][1];
            
            float spo2 = SpO2_1 + (SpO2_2 - SpO2_1) * (R - R1) / (R2 - R1);
            return spo2;
        }
    }
    
    return lookup_table[10][1];  // 返回最后一个值
}

// 获取R值
float SpO2_GetR(void)
{
    return spo2_calc.R_value;
}

// 获取置信度
uint8_t SpO2_GetConfidence(void)
{
    return spo2_calc.confidence;
}

7、 主数据处理

c 复制代码
// max30102_processor.c
#include "max30102.h"

// 滤波器实例
static MovingAverage_Filter red_filter;
static MovingAverage_Filter ir_filter;
static DC_Remover red_dc_remover;
static DC_Remover ir_dc_remover;
static Butterworth_Filter hr_filter;

// 数据处理
MAX30102_Result_t MAX30102_ProcessData(MAX30102_Data_t *data, uint16_t num_samples)
{
    MAX30102_Result_t result = {0};
    
    if(num_samples == 0) {
        return result;
    }
    
    // 处理每个样本
    for(int i = 0; i < num_samples; i++) {
        float red_value = (float)data[i].red;
        float ir_value = (float)data[i].ir;
        
        // 1. 移动平均滤波
        red_value = MovingAverage_Filter_Update(&red_filter, red_value);
        ir_value = MovingAverage_Filter_Update(&ir_filter, ir_value);
        
        // 2. DC移除
        float red_ac = DC_Remover_Update(&red_dc_remover, red_value);
        float ir_ac = DC_Remover_Update(&ir_dc_remover, ir_value);
        
        // 3. 带通滤波 (心率检测)
        float hr_signal = Butterworth_BP_Update(&hr_filter, red_ac);
        
        // 4. 心率检测
        HeartRate_AddData(hr_signal, data[i].timestamp);
        HeartRate_DetectPeak(hr_signal, data[i].timestamp);
        
        // 5. 血氧计算
        SpO2_AddData(red_value, ir_value);
    }
    
    // 计算心率
    result.heart_rate = HeartRate_Calculate();
    result.confidence = HeartRate_GetConfidence();
    
    // 计算血氧
    if(num_samples >= 100) {  // 收集足够样本
        result.spo2 = SpO2_Calculate();
        if(result.confidence < SpO2_GetConfidence()) {
            result.confidence = SpO2_GetConfidence();
        }
    }
    
    result.valid = (result.confidence > 70) ? 1 : 0;
    
    return result;
}

// 初始化处理器
void MAX30102_Processor_Init(void)
{
    // 初始化滤波器
    MovingAverage_Init(&red_filter, 4);
    MovingAverage_Init(&ir_filter, 4);
    DC_Remover_Init(&red_dc_remover, 0.95f);
    DC_Remover_Init(&ir_dc_remover, 0.95f);
    Butterworth_BP_Init(&hr_filter);
    
    // 初始化算法
    HeartRate_Init();
    SpO2_Init();
}

8、主程序

c 复制代码
// main.c
#include "main.h"
#include "max30102.h"

// 全局变量
MAX30102_Data_t sample_buffer[32];
MAX30102_Result_t latest_result = {0};
uint8_t new_data_available = 0;

int main(void)
{
    // HAL初始化
    HAL_Init();
    SystemClock_Config();
    
    // 外设初始化
    MX_GPIO_Init();
    MX_I2C1_Init();
    MX_USART1_UART_Init();
    MX_TIM2_Init();  // 用于定时采集
    
    printf("MAX30102 血氧和心率检测系统\r\n");
    printf("===========================\r\n");
    
    // 初始化MAX30102
    MAX30102_Init();
    
    // 初始化处理器
    MAX30102_Processor_Init();
    
    // 开始采集
    MAX30102_StartReading();
    
    // 启动定时器 (100Hz采样)
    HAL_TIM_Base_Start_IT(&htim2);
    
    while(1)
    {
        // 检查是否有新数据
        if(new_data_available)
        {
            new_data_available = 0;
            
            // 获取FIFO中的样本数
            uint8_t num_samples = MAX30102_GetNumSamples();
            if(num_samples > 0)
            {
                // 读取数据
                MAX30102_ReadFIFO(sample_buffer, num_samples);
                
                // 处理数据
                latest_result = MAX30102_ProcessData(sample_buffer, num_samples);
                
                // 显示结果
                if(latest_result.valid)
                {
                    DisplayResults(&latest_result);
                }
            }
        }
        
        // 处理其他任务
        ProcessUserInput();
        
        HAL_Delay(10);
    }
}

// 定时器中断回调 (100Hz)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        new_data_available = 1;
    }
}

// 显示结果
void DisplayResults(MAX30102_Result_t *result)
{
    static uint32_t last_display_time = 0;
    
    if(HAL_GetTick() - last_display_time < 1000) {
        return;  // 每秒显示一次
    }
    
    last_display_time = HAL_GetTick();
    
    printf("心率: %.1f BPM, 血氧: %.1f%%, 置信度: %d%%\r\n",
           result->heart_rate, result->spo2, result->confidence);
    
    // 读取并显示温度
    float temp = MAX30102_ReadTemperature();
    printf("温度: %.2f°C\r\n", temp);
}

// 处理用户输入
void ProcessUserInput(void)
{
    // 处理按键等用户输入
    // ...
}

9、校准功能

c 复制代码
// calibration.c
#include "calibration.h"

// 校准状态
typedef struct {
    float red_dc_offset;
    float ir_dc_offset;
    float red_gain;
    float ir_gain;
    float temperature_offset;
    uint8_t calibrated;
} Calibration_Data;

static Calibration_Data calib = {0};

// 开始校准
void Calibration_Start(void)
{
    printf("开始校准...\r\n");
    printf("1. 请移开手指,等待10秒...\r\n");
    
    // 清空FIFO
    MAX30102_ClearFIFO();
    
    // 等待稳定
    HAL_Delay(10000);
    
    // 采集暗电流
    Calibrate_DarkCurrent();
    
    printf("2. 请放置手指,等待10秒...\r\n");
    HAL_Delay(10000);
    
    // 采集参考信号
    Calibrate_Reference();
    
    calib.calibrated = 1;
    printf("校准完成!\r\n");
}

// 校准暗电流
void Calibrate_DarkCurrent(void)
{
    MAX30102_Data_t samples[100];
    float red_sum = 0, ir_sum = 0;
    
    // 读取100个样本
    for(int i = 0; i < 100; i++) {
        MAX30102_ReadFIFO(&samples[i], 1);
        red_sum += samples[i].red;
        ir_sum += samples[i].ir;
        HAL_Delay(10);
    }
    
    calib.red_dc_offset = red_sum / 100.0f;
    calib.ir_dc_offset = ir_sum / 100.0f;
    
    printf("暗电流校准: 红光=%.0f, 红外=%.0f\r\n", 
           calib.red_dc_offset, calib.ir_dc_offset);
}

// 校准参考值
void Calibrate_Reference(void)
{
    MAX30102_Data_t samples[100];
    float red_sum = 0, ir_sum = 0;
    
    // 读取100个样本
    for(int i = 0; i < 100; i++) {
        MAX30102_ReadFIFO(&samples[i], 1);
        red_sum += samples[i].red;
        ir_sum += samples[i].ir;
        HAL_Delay(10);
    }
    
    // 计算增益
    calib.red_gain = 10000.0f / (red_sum/100.0f - calib.red_dc_offset);
    calib.ir_gain = 10000.0f / (ir_sum/100.0f - calib.ir_dc_offset);
    
    printf("增益校准: 红光=%.6f, 红外=%.6f\r\n", 
           calib.red_gain, calib.ir_gain);
}

// 应用校准
void Apply_Calibration(float *red, float *ir)
{
    if(!calib.calibrated) {
        return;
    }
    
    // 减去偏移
    *red = *red - calib.red_dc_offset;
    *ir = *ir - calib.ir_dc_offset;
    
    // 应用增益
    *red = *red * calib.red_gain;
    *ir = *ir * calib.ir_gain;
    
    // 确保非负
    if(*red < 0) *red = 0;
    if(*ir < 0) *ir = 0;
}

10、上位机通信

c 复制代码
// serial_protocol.c
#include "serial_protocol.h"

// 数据包结构
typedef struct {
    uint8_t header[2];  // 0xAA 0x55
    uint8_t type;       // 数据类型
    uint8_t length;     // 数据长度
    uint8_t data[16];   // 数据
    uint8_t checksum;   // 校验和
} Serial_Packet;

// 发送数据到上位机
void Send_To_Host(MAX30102_Result_t *result, MAX30102_Data_t *raw_data, uint8_t num_samples)
{
    Serial_Packet packet;
    
    // 1. 发送结果数据
    packet.header[0] = 0xAA;
    packet.header[1] = 0x55;
    packet.type = 0x01;  // 结果数据
    packet.length = 9;   // 4(心率) + 4(血氧) + 1(置信度)
    
    memcpy(&packet.data[0], &result->heart_rate, 4);
    memcpy(&packet.data[4], &result->spo2, 4);
    packet.data[8] = result->confidence;
    
    // 计算校验和
    packet.checksum = Calculate_Checksum(&packet);
    
    // 发送
    HAL_UART_Transmit(&huart1, (uint8_t*)&packet, sizeof(packet), 1000);
    
    // 2. 发送原始数据
    if(num_samples > 0) {
        packet.type = 0x02;  // 原始数据
        packet.length = num_samples * 6;  // 每个样本6字节
        
        for(int i = 0; i < num_samples; i++) {
            memcpy(&packet.data[i*6], &raw_data[i].red, 3);
            memcpy(&packet.data[i*6+3], &raw_data[i].ir, 3);
        }
        
        packet.checksum = Calculate_Checksum(&packet);
        HAL_UART_Transmit(&huart1, (uint8_t*)&packet, sizeof(packet), 1000);
    }
}

参考代码 max30102获取血氧浓度 www.youwenfan.com/contentcsu/70139.html

五、Python上位机示例

python 复制代码
# max30102_gui.py
import serial
import struct
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from collections import deque
import time

class MAX30102_Visualizer:
    def __init__(self, port='COM3', baudrate=115200):
        self.serial = serial.Serial(port, baudrate, timeout=1)
        self.fig, (self.ax1, self.ax2) = plt.subplots(2, 1, figsize=(10, 8))
        
        # 数据缓冲区
        self.buffer_size = 200
        self.red_data = deque(maxlen=self.buffer_size)
        self.ir_data = deque(maxlen=self.buffer_size)
        self.time_data = deque(maxlen=self.buffer_size)
        
        # 心率和血氧
        self.heart_rate = 0
        self.spo2 = 0
        self.confidence = 0
        
        # 初始化时间
        self.start_time = time.time()
        
        # 初始化绘图
        self.setup_plot()
        
    def setup_plot(self):
        # 设置子图1:原始数据
        self.ax1.set_title('MAX30102 Raw Data')
        self.ax1.set_xlabel('Time (s)')
        self.ax1.set_ylabel('ADC Value')
        self.ax1.grid(True)
        
        self.red_line, = self.ax1.plot([], [], 'r-', label='Red', alpha=0.7)
        self.ir_line, = self.ax1.plot([], [], 'b-', label='IR', alpha=0.7)
        self.ax1.legend(loc='upper right')
        
        # 设置子图2:心率血氧
        self.ax2.set_title('Heart Rate & SpO2')
        self.ax2.axis('off')
        
        # 文本显示
        self.hr_text = self.ax2.text(0.1, 0.7, 'Heart Rate: -- BPM', 
                                     fontsize=20, fontweight='bold')
        self.spo2_text = self.ax2.text(0.1, 0.4, 'SpO2: -- %', 
                                       fontsize=20, fontweight='bold')
        self.conf_text = self.ax2.text(0.1, 0.1, 'Confidence: -- %', 
                                       fontsize=12)
        
    def parse_packet(self, data):
        if len(data) < 5:
            return None
            
        # 检查包头
        if data[0] != 0xAA or data[1] != 0x55:
            return None
            
        packet_type = data[2]
        length = data[3]
        
        if len(data) < 5 + length:
            return None
            
        # 校验和
        checksum = sum(data[:-1]) & 0xFF
        if checksum != data[-1]:
            return None
            
        return packet_type, data[4:4+length]
    
    def update_plot(self, frame):
        # 读取串口数据
        while self.serial.in_waiting:
            data = self.serial.read(self.serial.in_waiting)
            result = self.parse_packet(data)
            
            if result:
                packet_type, payload = result
                
                if packet_type == 0x01:  # 结果数据
                    self.parse_result(payload)
                elif packet_type == 0x02:  # 原始数据
                    self.parse_raw_data(payload)
        
        # 更新绘图
        current_time = time.time() - self.start_time
        
        if len(self.time_data) > 0:
            self.red_line.set_data(self.time_data, self.red_data)
            self.ir_line.set_data(self.time_data, self.ir_data)
            
            # 自动调整X轴范围
            self.ax1.set_xlim(current_time - 10, current_time)
            
            # 自动调整Y轴范围
            all_data = list(self.red_data) + list(self.ir_data)
            if all_data:
                y_min = min(all_data) * 0.9
                y_max = max(all_data) * 1.1
                self.ax1.set_ylim(y_min, y_max)
        
        # 更新文本
        self.hr_text.set_text(f'Heart Rate: {self.heart_rate:.1f} BPM')
        self.spo2_text.set_text(f'SpO2: {self.spo2:.1f} %')
        self.conf_text.set_text(f'Confidence: {self.confidence} %')
        
        return self.red_line, self.ir_line, self.hr_text, self.spo2_text, self.conf_text
    
    def parse_result(self, data):
        if len(data) >= 9:
            self.heart_rate = struct.unpack('<f', data[0:4])[0]
            self.spo2 = struct.unpack('<f', data[4:8])[0]
            self.confidence = data[8]
            
            print(f"HR: {self.heart_rate:.1f}, SpO2: {self.spo2:.1f}, Conf: {self.confidence}")
    
    def parse_raw_data(self, data):
        num_samples = len(data) // 6
        
        for i in range(num_samples):
            red_bytes = data[i*6:i*6+3] + b'\x00'
            ir_bytes = data[i*6+3:i*6+6] + b'\x00'
            
            red = struct.unpack('<I', red_bytes)[0] & 0x3FFFF
            ir = struct.unpack('<I', ir_bytes)[0] & 0x3FFFF
            
            self.red_data.append(red)
            self.ir_data.append(ir)
            self.time_data.append(time.time() - self.start_time)
    
    def run(self):
        ani = FuncAnimation(self.fig, self.update_plot, interval=50, blit=True)
        plt.tight_layout()
        plt.show()

if __name__ == "__main__":
    visualizer = MAX30102_Visualizer('COM3', 115200)
    visualizer.run()

六、测试和验证

c 复制代码
void Test_MAX30102(void)
{
    printf("=== MAX30102 测试程序 ===\r\n");
    
    // 1. 基本功能测试
    printf("1. 读取ID...\r\n");
    uint8_t id = MAX30102_ReadID();
    printf("   ID: 0x%02X\r\n", id);
    
    // 2. 温度测试
    printf("2. 读取温度...\r\n");
    float temp = MAX30102_ReadTemperature();
    printf("   温度: %.2f°C\r\n", temp);
    
    // 3. 采集测试
    printf("3. 采集测试...\r\n");
    MAX30102_Data_t samples[10];
    
    for(int i = 0; i < 10; i++) {
        MAX30102_ReadFIFO(samples, 1);
        printf("   样本%d: 红光=%lu, 红外=%lu\r\n", 
               i, samples[0].red, samples[0].ir);
        HAL_Delay(100);
    }
    
    // 4. 心率和血氧测试
    printf("4. 心率和血氧测试...\r\n");
    printf("   请放置手指...\r\n");
    
    MAX30102_StartReading();
    HAL_Delay(10000);  // 采集10秒
    
    MAX30102_Result_t result = MAX30102_ProcessData(samples, 10);
    printf("   心率: %.1f BPM\r\n", result.heart_rate);
    printf("   血氧: %.1f%%\r\n", result.spo2);
    
    MAX30102_StopReading();
    
    printf("测试完成!\r\n");
}
相关推荐
Zhao1368245539140 分钟前
DP4330A 与DP4363 测试对比
嵌入式硬件
辛迪聊物业数字化44 分钟前
智慧社区小程序落地避坑指南:3类物业方案选型+成本测算,线上服务零翻车
大数据·嵌入式硬件·百度·系统架构
电子科技圈1 小时前
Arm电机控制开发工具哪家强?
arm开发·单片机·嵌入式硬件·设计模式·软件构建
JasmineX-11 小时前
下位机直接控制57步进电机匀速转动
stm32·单片机·嵌入式硬件
聆思科技AI芯片2 小时前
【AI硬件开发上手教程】怎么使用AI编程实现小聆AI语音控制舵机
人工智能·嵌入式硬件·开源·ai编程·智能硬件
abc light2 小时前
ARM 裸机开发(6)——时钟树与定时器
arm开发·单片机·fpga开发
吠品12 小时前
智谱开源 ZCode 之后,真正的考试才开始
单片机·嵌入式硬件·1024程序员节
Chery114013 小时前
高速ADC与精密DAC怎么选?DP9230D与DP8532D应用解析
嵌入式硬件·物联网·射频工程
小李不想当小白16 小时前
SPI通信外设与W25Q64 (硬件SPI)(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
LCG米16 小时前
STM32驱动W5500以太网模块实测:TCP吞吐瓶颈定位、TX指针回绕陷阱与断线重连保活落地
stm32·单片机·嵌入式硬件·tcp/ip