STM32 MAX30102 心率血氧测量代码

一、系统架构设计

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    MAX30102 心率血氧监测系统                 │
├─────────────────────────────────────────────────────────────┤
│  MAX30102 传感器 │  STM32F103  │  数据处理算法  │  显示输出  │
│                 │              │                │           │
│  • 红光LED      │  • I2C通信   │  • 信号滤波    │  • OLED显示 │
│  • 红外LED      │  • 数据采集   │  • 峰值检测    │  • 串口输出 │
│  • 光电探测器   │  • 中断处理   │  • 心率计算    │  • 蜂鸣器   │
│  • 环境温度传感 │  • 定时器控制 │  • SpO2计算    │  • LED指示  │
└─────────────────────────────────────────────────────────────┘

二、硬件连接设计

2.1 MAX30102 硬件连接

复制代码
MAX30102 引脚    STM32F103 引脚    说明
────────────────────────────────────────
VIN             3.3V              电源输入
GND             GND               电源地
SCL             PB6 (I2C1_SCL)    I2C时钟
SDA             PB7 (I2C1_SDA)    I2C数据
INT             PA0               中断输出
RD              NC                未连接
IR              NC                未连接

2.2 外围电路设计

复制代码
MAX30102 外围电路:
- 电源:3.3V,并联100nF去耦电容
- I2C上拉电阻:4.7kΩ(SCL和SDA各一个)
- 中断引脚:直接连接到STM32的PA0
- 可选:在VIN和GND之间添加10μF电容滤波

三、完整程序实现

3.1 MAX30102 驱动头文件 (max30102.h)

c 复制代码
#ifndef __MAX30102_H
#define __MAX30102_H

#include "stm32f10x.h"
#include <stdint.h>
#include <stdbool.h>
#include <math.h>

// MAX30102 I2C 地址
#define MAX30102_ADDR       0x57    // 7位地址 (0xAE >> 1)

// MAX30102 寄存器地址
#define REG_INTR_STATUS_1   0x00
#define REG_INTR_STATUS_2   0x01
#define REG_INTR_ENABLE_1   0x02
#define REG_INTR_ENABLE_2   0x03
#define REG_FIFO_WR_PTR     0x04
#define REG_OVF_COUNTER     0x05
#define REG_FIFO_RD_PTR     0x06
#define REG_FIFO_DATA       0x07
#define REG_FIFO_CONFIG     0x08
#define REG_MODE_CONFIG     0x09
#define REG_SPO2_CONFIG     0x0A
#define REG_LED1_PA         0x0C    // Red LED
#define REG_LED2_PA         0x0D    // IR LED
#define REG_LED3_PA         0x0E    // Green LED (如果有)
#define REG_PILOT_PA        0x10
#define REG_MULTI_LED_CTRL1 0x11
#define REG_MULTI_LED_CTRL2 0x12
#define REG_TEMP_INTR       0x1F
#define REG_TEMP_FRAC       0x20
#define REG_TEMP_CONFIG     0x21
#define REG_PROX_INT_THRESH 0x30
#define REG_REV_ID          0xFE
#define REG_PART_ID         0xFF

// FIFO 配置参数
#define FIFO_AVERAGING_1    0x00
#define FIFO_AVERAGING_2    0x20
#define FIFO_AVERAGING_4    0x40
#define FIFO_AVERAGING_8    0x60
#define FIFO_AVERAGING_16   0x80
#define FIFO_AVERAGING_32   0xA0

#define FIFO_ROLL_OVER_EN   0x10
#define FIFO_SAMPLE_AVG_1   0x00
#define FIFO_SAMPLE_AVG_2   0x01
#define FIFO_SAMPLE_AVG_4   0x02
#define FIFO_SAMPLE_AVG_8   0x03
#define FIFO_SAMPLE_AVG_16  0x04
#define FIFO_SAMPLE_AVG_32  0x05

// 模式配置
#define MODE_HEART_RATE     0x02    // 心率模式 (红光)
#define MODE_SPO2           0x03    // 血氧模式 (红光+红外)
#define MODE_MULTI_LED      0x07    // 多LED模式

// SPO2 配置
#define SPO2_ADC_RANGE_2048 0x00
#define SPO2_ADC_RANGE_4096 0x20
#define SPO2_ADC_RANGE_8192 0x40
#define SPO2_ADC_RANGE_16384 0x60

#define SPO2_SAMPLE_RATE_50 0x00
#define SPO2_SAMPLE_RATE_100 0x04
#define SPO2_SAMPLE_RATE_200 0x08
#define SPO2_SAMPLE_RATE_400 0x0C
#define SPO2_SAMPLE_RATE_800 0x10
#define SPO2_SAMPLE_RATE_1000 0x14
#define SPO2_SAMPLE_RATE_1600 0x18
#define SPO2_SAMPLE_RATE_3200 0x1C

#define LED_PULSE_WIDTH_69  0x00    // 69μs, 15位分辨率
#define LED_PULSE_WIDTH_118 0x01    // 118μs, 16位分辨率
#define LED_PULSE_WIDTH_215 0x02    // 215μs, 17位分辨率
#define LED_PULSE_WIDTH_411 0x03    // 411μs, 18位分辨率

// LED 电流设置 (0-255)
#define LED_CURRENT_0MA     0x00
#define LED_CURRENT_4_4MA   0x01
#define LED_CURRENT_7_6MA   0x02
#define LED_CURRENT_11MA    0x03
#define LED_CURRENT_14_2MA   0x04
#define LED_CURRENT_17_4MA   0x05
#define LED_CURRENT_20_8MA   0x06
#define LED_CURRENT_24MA     0x07
#define LED_CURRENT_27_1MA   0x08
#define LED_CURRENT_30_6MA   0x09
#define LED_CURRENT_33_8MA   0x0A
#define LED_CURRENT_37_4MA   0x0B
#define LED_CURRENT_40_8MA   0x0C
#define LED_CURRENT_44_2MA   0x0D
#define LED_CURRENT_47_6MA   0x0E
#define LED_CURRENT_51MA     0x0F

// 数据结构体
typedef struct {
    uint32_t red;          // 红光ADC值
    uint32_t ir;           // 红外ADC值
    uint32_t green;        // 绿光ADC值 (如果有)
    uint32_t timestamp;    // 时间戳
} MAX30102_Sample_t;

typedef struct {
    float heart_rate;      // 心率 (BPM)
    float spo2;            // 血氧饱和度 (%)
    uint8_t hr_valid;      // 心率有效性
    uint8_t spo2_valid;    // 血氧有效性
    float confidence;      // 置信度
} MAX30102_Result_t;

// 算法参数
typedef struct {
    uint16_t sample_rate;      // 采样率 (Hz)
    uint16_t buffer_size;      // 缓冲区大小
    uint16_t peak_threshold;   // 峰值检测阈值
    uint16_t min_peak_distance; // 最小峰间距
    float dc_removal_alpha;    // 直流去除系数
} MAX30102_Config_t;

// 函数声明
void MAX30102_Init(void);
bool MAX30102_ReadPartID(uint8_t *part_id);
bool MAX30102_Reset(void);
bool MAX30102_ConfigMode(uint8_t mode);
bool MAX30102_ConfigFIFO(uint8_t averaging, uint8_t sample_avg);
bool MAX30102_ConfigSPO2(uint8_t adc_range, uint8_t sample_rate, uint8_t pulse_width);
bool MAX30102_SetLEDCurrent(uint8_t led1_current, uint8_t led2_current);
bool MAX30102_ReadFIFO(MAX30102_Sample_t *sample);
bool MAX30102_ClearFIFO(void);
bool MAX30102_ReadTemperature(float *temperature);
void MAX30102_InterruptHandler(void);
void MAX30102_StartMeasurement(void);
void MAX30102_StopMeasurement(void);

#endif /* __MAX30102_H */

3.2 MAX30102 驱动核心 (max30102.c)

c 复制代码
#include "max30102.h"
#include "i2c.h"
#include "delay.h"

// I2C 读写函数
static bool MAX30102_WriteReg(uint8_t reg, uint8_t data) {
    return I2C_WriteByte(MAX30102_ADDR, reg, data);
}

static bool MAX30102_ReadReg(uint8_t reg, uint8_t *data) {
    return I2C_ReadByte(MAX30102_ADDR, reg, data);
}

static bool MAX30102_ReadRegs(uint8_t reg, uint8_t *data, uint8_t len) {
    return I2C_ReadBytes(MAX30102_ADDR, reg, data, len);
}

// 初始化 MAX30102
void MAX30102_Init(void) {
    uint8_t part_id;
    
    // 检查芯片ID
    if (!MAX30102_ReadPartID(&part_id)) {
        printf("MAX30102 not found!\r\n");
        return;
    }
    
    printf("MAX30102 Part ID: 0x%02X\r\n", part_id);
    
    // 复位芯片
    MAX30102_Reset();
    Delay_ms(100);
    
    // 配置 FIFO
    MAX30102_ConfigFIFO(FIFO_AVERAGING_4, FIFO_SAMPLE_AVG_4);
    
    // 配置 SPO2 模式
    MAX30102_ConfigSPO2(SPO2_ADC_RANGE_4096, SPO2_SAMPLE_RATE_100, LED_PULSE_WIDTH_411);
    
    // 配置 LED 电流
    MAX30102_SetLEDCurrent(LED_CURRENT_17_4MA, LED_CURRENT_17_4MA);
    
    // 配置为 SPO2 模式
    MAX30102_ConfigMode(MODE_SPO2);
    
    // 清除 FIFO
    MAX30102_ClearFIFO();
    
    printf("MAX30102 initialized successfully!\r\n");
}

// 读取芯片ID
bool MAX30102_ReadPartID(uint8_t *part_id) {
    return MAX30102_ReadReg(REG_PART_ID, part_id);
}

// 复位芯片
bool MAX30102_Reset(void) {
    return MAX30102_WriteReg(REG_MODE_CONFIG, 0x40);
}

// 配置工作模式
bool MAX30102_ConfigMode(uint8_t mode) {
    uint8_t config;
    if (!MAX30102_ReadReg(REG_MODE_CONFIG, &config)) {
        return false;
    }
    
    config &= ~0x07;  // 清除模式位
    config |= (mode & 0x07);
    
    return MAX30102_WriteReg(REG_MODE_CONFIG, config);
}

// 配置 FIFO
bool MAX30102_ConfigFIFO(uint8_t averaging, uint8_t sample_avg) {
    uint8_t fifo_config = averaging | sample_avg;
    return MAX30102_WriteReg(REG_FIFO_CONFIG, fifo_config);
}

// 配置 SPO2 参数
bool MAX30102_ConfigSPO2(uint8_t adc_range, uint8_t sample_rate, uint8_t pulse_width) {
    uint8_t spo2_config = adc_range | sample_rate | pulse_width;
    return MAX30102_WriteReg(REG_SPO2_CONFIG, spo2_config);
}

// 设置 LED 电流
bool MAX30102_SetLEDCurrent(uint8_t led1_current, uint8_t led2_current) {
    if (!MAX30102_WriteReg(REG_LED1_PA, led1_current)) {
        return false;
    }
    return MAX30102_WriteReg(REG_LED2_PA, led2_current);
}

// 读取 FIFO 数据
bool MAX30102_ReadFIFO(MAX30102_Sample_t *sample) {
    uint8_t fifo_data[6];
    
    if (!MAX30102_ReadRegs(REG_FIFO_DATA, fifo_data, 6)) {
        return false;
    }
    
    // 红光数据 (18位)
    sample->red = ((uint32_t)fifo_data[0] << 16) | 
                 ((uint32_t)fifo_data[1] << 8) | 
                  (uint32_t)fifo_data[2];
    sample->red >>= 2;  // 右移2位得到18位数据
    
    // 红外数据 (18位)
    sample->ir = ((uint32_t)fifo_data[3] << 16) | 
                ((uint32_t)fifo_data[4] << 8) | 
                 (uint32_t)fifo_data[5];
    sample->ir >>= 2;
    
    sample->timestamp = Get_SystemTime();
    
    return true;
}

// 清除 FIFO
bool MAX30102_ClearFIFO(void) {
    if (!MAX30102_WriteReg(REG_FIFO_WR_PTR, 0x00)) {
        return false;
    }
    if (!MAX30102_WriteReg(REG_FIFO_RD_PTR, 0x00)) {
        return false;
    }
    return MAX30102_WriteReg(REG_OVF_COUNTER, 0x00);
}

// 读取温度
bool MAX30102_ReadTemperature(float *temperature) {
    uint8_t temp_int, temp_frac;
    
    // 启动温度测量
    if (!MAX30102_WriteReg(REG_TEMP_CONFIG, 0x01)) {
        return false;
    }
    
    Delay_ms(100);  // 等待测量完成
    
    if (!MAX30102_ReadReg(REG_TEMP_INTR, &temp_int)) {
        return false;
    }
    if (!MAX30102_ReadReg(REG_TEMP_FRAC, &temp_frac)) {
        return false;
    }
    
    *temperature = (float)temp_int + (float)temp_frac * 0.0625f;
    
    return true;
}

// 中断处理函数
void MAX30102_InterruptHandler(void) {
    uint8_t intr_status;
    
    if (MAX30102_ReadReg(REG_INTR_STATUS_1, &intr_status)) {
        if (intr_status & 0x80) {  // FIFO 几乎满中断
            // 处理 FIFO 数据
            MAX30102_Sample_t sample;
            if (MAX30102_ReadFIFO(&sample)) {
                // 将数据传递给算法处理
                HeartRate_ProcessSample(&sample);
            }
        }
    }
}

// 开始测量
void MAX30102_StartMeasurement(void) {
    uint8_t mode_config;
    
    if (MAX30102_ReadReg(REG_MODE_CONFIG, &mode_config)) {
        mode_config |= 0x80;  // 设置开始测量位
        MAX30102_WriteReg(REG_MODE_CONFIG, mode_config);
    }
}

// 停止测量
void MAX30102_StopMeasurement(void) {
    uint8_t mode_config;
    
    if (MAX30102_ReadReg(REG_MODE_CONFIG, &mode_config)) {
        mode_config &= ~0x80;  // 清除开始测量位
        MAX30102_WriteReg(REG_MODE_CONFIG, mode_config);
    }
}

3.3 心率血氧算法 (heart_rate.c)

c 复制代码
#include "heart_rate.h"
#include "max30102.h"
#include "filters.h"

// 算法缓冲区
#define HR_BUFFER_SIZE 100
#define SPO2_BUFFER_SIZE 100

static MAX30102_Sample_t hr_buffer[HR_BUFFER_SIZE];
static float red_dc_buffer[SPO2_BUFFER_SIZE];
static float ir_dc_buffer[SPO2_BUFFER_SIZE];
static float red_ac_buffer[SPO2_BUFFER_SIZE];
static float ir_ac_buffer[SPO2_BUFFER_SIZE];

static uint16_t hr_buffer_index = 0;
static uint16_t spo2_buffer_index = 0;
static uint8_t buffer_full = 0;

// 峰值检测变量
static uint32_t last_peak_time = 0;
static uint16_t peak_count = 0;
static float peak_intervals[10];
static uint8_t peak_interval_index = 0;

// 心率血氧结果
static MAX30102_Result_t current_result;

// 初始化心率算法
void HeartRate_Init(void) {
    memset(hr_buffer, 0, sizeof(hr_buffer));
    memset(red_dc_buffer, 0, sizeof(red_dc_buffer));
    memset(ir_dc_buffer, 0, sizeof(ir_dc_buffer));
    memset(red_ac_buffer, 0, sizeof(red_ac_buffer));
    memset(ir_ac_buffer, 0, sizeof(ir_ac_buffer));
    
    hr_buffer_index = 0;
    spo2_buffer_index = 0;
    buffer_full = 0;
    last_peak_time = 0;
    peak_count = 0;
    peak_interval_index = 0;
    
    current_result.heart_rate = 0;
    current_result.spo2 = 0;
    current_result.hr_valid = 0;
    current_result.spo2_valid = 0;
    current_result.confidence = 0;
}

// 处理采样数据
void HeartRate_ProcessSample(MAX30102_Sample_t *sample) {
    // 存储到缓冲区
    hr_buffer[hr_buffer_index] = *sample;
    hr_buffer_index = (hr_buffer_index + 1) % HR_BUFFER_SIZE;
    
    if (hr_buffer_index == 0) {
        buffer_full = 1;
    }
    
    // 直流去除和交流提取
    HeartRate_DCRemoval(sample);
    
    // 信号滤波
    HeartRate_FilterSignal();
    
    // 心率计算
    HeartRate_Calculate();
    
    // 血氧计算
    HeartRate_CalculateSPO2();
}

// 直流去除
void HeartRate_DCRemoval(MAX30102_Sample_t *sample) {
    static float alpha = 0.95f;  // 直流去除系数
    
    // 更新直流分量缓冲区
    if (spo2_buffer_index < SPO2_BUFFER_SIZE) {
        red_dc_buffer[spo2_buffer_index] = sample->red * alpha + 
                                          (1 - alpha) * red_dc_buffer[spo2_buffer_index];
        ir_dc_buffer[spo2_buffer_index] = sample->ir * alpha + 
                                        (1 - alpha) * ir_dc_buffer[spo2_buffer_index];
        
        // 计算交流分量
        red_ac_buffer[spo2_buffer_index] = sample->red - red_dc_buffer[spo2_buffer_index];
        ir_ac_buffer[spo2_buffer_index] = sample->ir - ir_dc_buffer[spo2_buffer_index];
        
        spo2_buffer_index++;
    } else {
        // 循环缓冲区
        uint16_t index = spo2_buffer_index % SPO2_BUFFER_SIZE;
        red_dc_buffer[index] = sample->red * alpha + (1 - alpha) * red_dc_buffer[index];
        ir_dc_buffer[index] = sample->ir * alpha + (1 - alpha) * ir_dc_buffer[index];
        red_ac_buffer[index] = sample->red - red_dc_buffer[index];
        ir_ac_buffer[index] = sample->ir - ir_dc_buffer[index];
        spo2_buffer_index++;
    }
}

// 信号滤波
void HeartRate_FilterSignal(void) {
    // 使用移动平均滤波
    static float red_ma_buffer[5];
    static float ir_ma_buffer[5];
    static uint8_t ma_index = 0;
    
    uint16_t index = spo2_buffer_index % SPO2_BUFFER_SIZE;
    
    red_ma_buffer[ma_index] = red_ac_buffer[index];
    ir_ma_buffer[ma_index] = ir_ac_buffer[index];
    ma_index = (ma_index + 1) % 5;
    
    // 计算移动平均值
    float red_sum = 0, ir_sum = 0;
    for (uint8_t i = 0; i < 5; i++) {
        red_sum += red_ma_buffer[i];
        ir_sum += ir_ma_buffer[i];
    }
    
    red_ac_buffer[index] = red_sum / 5.0f;
    ir_ac_buffer[index] = ir_sum / 5.0f;
}

// 心率计算
void HeartRate_Calculate(void) {
    uint16_t index = (spo2_buffer_index - 1) % SPO2_BUFFER_SIZE;
    float current_red = red_ac_buffer[index];
    
    // 峰值检测
    static float threshold = 1000.0f;
    static uint32_t last_peak = 0;
    
    uint32_t current_time = Get_SystemTime();
    
    if (current_red > threshold && (current_time - last_peak) > 300) {  // 最小300ms间隔
        if (last_peak > 0) {
            uint32_t interval = current_time - last_peak;
            peak_intervals[peak_interval_index] = interval;
            peak_interval_index = (peak_interval_index + 1) % 10;
            
            if (peak_interval_index == 0) {
                // 计算平均心率
                float avg_interval = 0;
                for (uint8_t i = 0; i < 10; i++) {
                    avg_interval += peak_intervals[i];
                }
                avg_interval /= 10.0f;
                
                current_result.heart_rate = 60000.0f / avg_interval;  // BPM
                current_result.hr_valid = 1;
                current_result.confidence = 0.8f;
            }
        }
        last_peak = current_time;
        peak_count++;
    }
    
    // 动态调整阈值
    static float max_value = 0;
    if (current_red > max_value) {
        max_value = current_red;
        threshold = max_value * 0.7f;  // 阈值为最大值的70%
    }
}

// 血氧饱和度计算
void HeartRate_CalculateSPO2(void) {
    uint16_t index = (spo2_buffer_index - 1) % SPO2_BUFFER_SIZE;
    
    // 计算红光和红外光的交流分量 RMS
    float red_rms = 0, ir_rms = 0;
    uint16_t start_index = (spo2_buffer_index > 10) ? (spo2_buffer_index - 10) : 0;
    uint16_t count = 0;
    
    for (uint16_t i = start_index; i < spo2_buffer_index && count < 10; i++, count++) {
        uint16_t buf_index = i % SPO2_BUFFER_SIZE;
        red_rms += red_ac_buffer[buf_index] * red_ac_buffer[buf_index];
        ir_rms += ir_ac_buffer[buf_index] * ir_ac_buffer[buf_index];
    }
    
    if (count > 0) {
        red_rms = sqrtf(red_rms / count);
        ir_rms = sqrtf(ir_rms / count);
        
        // 计算比值
        if (ir_rms > 0) {
            float ratio = red_rms / ir_rms;
            
            // 使用经验公式计算 SpO2
            // SpO2 = 110 - 25 * ratio (近似公式)
            current_result.spo2 = 110.0f - 25.0f * ratio;
            
            // 限制范围
            if (current_result.spo2 > 100.0f) current_result.spo2 = 100.0f;
            if (current_result.spo2 < 0.0f) current_result.spo2 = 0.0f;
            
            current_result.spo2_valid = 1;
        }
    }
}

// 获取心率血氧结果
void HeartRate_GetResult(MAX30102_Result_t *result) {
    *result = current_result;
}

// 检查手指是否放置
bool HeartRate_CheckFingerPresent(void) {
    if (spo2_buffer_index < 10) {
        return false;
    }
    
    uint16_t index = (spo2_buffer_index - 1) % SPO2_BUFFER_SIZE;
    float ir_value = ir_dc_buffer[index];
    
    // 红外光强度阈值判断
    return (ir_value > 50000.0f && ir_value < 200000.0f);
}

3.4 主程序 (main.c)

c 复制代码
#include "stm32f10x.h"
#include "max30102.h"
#include "heart_rate.h"
#include "oled.h"
#include "uart.h"
#include "delay.h"

// 系统状态
typedef enum {
    SYS_INIT = 0,
    SYS_WAIT_FINGER,
    SYS_MEASURING,
    SYS_RESULT_READY,
    SYS_ERROR
} SystemState;

static SystemState system_state = SYS_INIT;
static uint32_t measurement_start_time = 0;
static uint32_t finger_detect_time = 0;

// 初始化系统
void System_Init(void) {
    // 初始化系统时钟
    SystemClock_Init();
    
    // 初始化延时
    Delay_Init();
    
    // 初始化串口
    UART_Init(115200);
    
    // 初始化OLED
    OLED_Init();
    
    // 初始化I2C
    I2C_Init();
    
    // 初始化MAX30102
    MAX30102_Init();
    
    // 初始化心率算法
    HeartRate_Init();
    
    printf("Heart Rate & SpO2 Monitor Started!\r\n");
    
    // 显示欢迎界面
    OLED_Clear();
    OLED_ShowString(0, 0, "Heart Rate & SpO2");
    OLED_ShowString(0, 2, "Monitor v1.0");
    OLED_ShowString(0, 4, "Please place");
    OLED_ShowString(0, 6, "your finger...");
    OLED_Refresh();
}

// 显示测量结果
void Display_Results(MAX30102_Result_t *result) {
    char display_buffer[20];
    
    OLED_Clear();
    
    // 显示心率
    OLED_ShowString(0, 0, "Heart Rate:");
    if (result->hr_valid) {
        sprintf(display_buffer, "%d BPM", (int)result->heart_rate);
        OLED_ShowString(0, 2, display_buffer);
    } else {
        OLED_ShowString(0, 2, "Measuring...");
    }
    
    // 显示血氧
    OLED_ShowString(0, 4, "SpO2:");
    if (result->spo2_valid) {
        sprintf(display_buffer, "%.1f %%", result->spo2);
        OLED_ShowString(0, 6, display_buffer);
    } else {
        OLED_ShowString(0, 6, "Measuring...");
    }
    
    OLED_Refresh();
    
    // 串口输出
    printf("Heart Rate: %.1f BPM, SpO2: %.1f%%, Valid: %d/%d\r\n",
           result->heart_rate, result->spo2, result->hr_valid, result->spo2_valid);
}

// 主循环
int main(void) {
    MAX30102_Result_t result;
    bool finger_present = false;
    
    // 系统初始化
    System_Init();
    
    while (1) {
        switch (system_state) {
            case SYS_INIT:
                // 开始测量
                MAX30102_StartMeasurement();
                system_state = SYS_WAIT_FINGER;
                finger_detect_time = Get_SystemTime();
                break;
                
            case SYS_WAIT_FINGER:
                // 检测手指是否放置
                finger_present = HeartRate_CheckFingerPresent();
                
                if (finger_present) {
                    if (Get_SystemTime() - finger_detect_time > 2000) {
                        system_state = SYS_MEASURING;
                        measurement_start_time = Get_SystemTime();
                        printf("Finger detected, starting measurement...\r\n");
                    }
                } else {
                    finger_detect_time = Get_SystemTime();
                }
                break;
                
            case SYS_MEASURING:
                // 检查测量时间
                if (Get_SystemTime() - measurement_start_time > 10000) {  // 10秒测量
                    system_state = SYS_RESULT_READY;
                }
                
                // 检查手指是否仍在
                finger_present = HeartRate_CheckFingerPresent();
                if (!finger_present) {
                    system_state = SYS_WAIT_FINGER;
                    finger_detect_time = Get_SystemTime();
                }
                break;
                
            case SYS_RESULT_READY:
                // 获取并显示结果
                HeartRate_GetResult(&result);
                Display_Results(&result);
                
                // 等待2秒后重新开始
                Delay_ms(2000);
                system_state = SYS_WAIT_FINGER;
                finger_detect_time = Get_SystemTime();
                break;
                
            default:
                system_state = SYS_INIT;
                break;
        }
        
        // 处理MAX30102中断
        if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0) {
            MAX30102_InterruptHandler();
        }
        
        Delay_ms(10);
    }
}

四、滤波算法 (filters.c)

c 复制代码
#include "filters.h"

// 移动平均滤波器
float MovingAverage_Filter(float *buffer, uint8_t size, float new_value) {
    static uint8_t index = 0;
    static uint8_t count = 0;
    
    buffer[index] = new_value;
    index = (index + 1) % size;
    
    if (count < size) {
        count++;
    }
    
    float sum = 0;
    for (uint8_t i = 0; i < count; i++) {
        sum += buffer[i];
    }
    
    return sum / count;
}

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

// 低通滤波器
float LowPass_Filter(float prev_value, float new_value, float alpha) {
    return alpha * new_value + (1 - alpha) * prev_value;
}

// 高通滤波器
float HighPass_Filter(float prev_value, float new_value, float alpha) {
    return alpha * (prev_value + new_value - prev_value);
}

五、OLED显示驱动 (oled.c)

c 复制代码
#include "oled.h"
#include "i2c.h"

// OLED 初始化命令
static uint8_t OLED_InitCmd[] = {
    0xAE,  // 关闭显示
    0xD5,  // 设置显示时钟分频
    0x80,  // 建议值
    0xA8,  // 设置多路复用比率
    0x3F,  // 1/64 duty
    0xD3,  // 设置显示偏移
    0x00,  // 无偏移
    0x40,  // 设置起始行地址
    0x8D,  // 电荷泵设置
    0x14,  // 开启电荷泵
    0x20,  // 内存地址模式
    0x00,  // 水平地址模式
    0xA1,  // 段重映射
    0xC8,  // 扫描方向
    0xDA,  // 设置COM引脚硬件配置
    0x12,  // 备用COM引脚配置
    0x81,  // 对比度设置
    0xCF,  // 对比度值
    0xD9,  // 预充电周期
    0xF1,  // 预充电周期值
    0xDB,  // VCOMH 取消选择级别
    0x40,  // VCOMH 值
    0xA4,  // 整个显示开启
    0xA6,  // 正常显示
    0xAF   // 开启显示
};

// OLED 写命令
void OLED_WriteCmd(uint8_t cmd) {
    I2C_WriteByte(0x78, 0x00, cmd);
}

// OLED 写数据
void OLED_WriteData(uint8_t data) {
    I2C_WriteByte(0x78, 0x40, data);
}

// OLED 初始化
void OLED_Init(void) {
    Delay_ms(100);
    
    for (uint8_t i = 0; i < sizeof(OLED_InitCmd); i++) {
        OLED_WriteCmd(OLED_InitCmd[i]);
        Delay_ms(1);
    }
}

// 清屏
void OLED_Clear(void) {
    for (uint8_t page = 0; page < 8; page++) {
        OLED_WriteCmd(0xB0 + page);
        OLED_WriteCmd(0x00);
        OLED_WriteCmd(0x10);
        
        for (uint8_t col = 0; col < 128; col++) {
            OLED_WriteData(0x00);
        }
    }
}

// 显示字符串
void OLED_ShowString(uint8_t x, uint8_t y, char *str) {
    uint8_t i = 0;
    while (str[i] != '\0') {
        OLED_ShowChar(x + i * 6, y, str[i]);
        i++;
    }
}

// 显示字符
void OLED_ShowChar(uint8_t x, uint8_t y, char ch) {
    uint8_t c = ch - ' ';
    uint8_t page = y / 8;
    uint8_t offset = y % 8;
    
    OLED_WriteCmd(0xB0 + page);
    OLED_WriteCmd(((x & 0xF0) >> 4) | 0x10);
    OLED_WriteCmd(x & 0x0F);
    
    // 显示字符点阵
    for (uint8_t i = 0; i < 6; i++) {
        OLED_WriteData(Font6x8[c][i]);
    }
}

// 刷新显示
void OLED_Refresh(void) {
    // OLED 自动刷新,无需额外操作
}

参考代码 stm32 max30102 心率血氧测量代码 www.youwenfan.com/contentcsu/60510.html

六、常见问题解决

问题 原因 解决方案
无法检测到手指 手指未正确放置 调整指夹设计,增加引导提示
心率数值不稳定 信号噪声大 增加滤波强度,延长测量时间
血氧数值不准确 运动干扰 添加运动检测,暂停测量
传感器发热 电流过大 降低LED驱动电流
I2C通信失败 接线问题 检查上拉电阻,缩短连线
相关推荐
✎ ﹏梦醒͜ღ҉繁华落℘4 天前
单片机基础知识---stm32单片机的优先级
stm32·单片机·mongodb
u152109648494 天前
S.S.Audio PRO A2音频隔离器
嵌入式硬件·音视频·实时音视频·视频编解码·视频
zd8451015004 天前
RS485 总线详解
单片机·嵌入式硬件
半条-咸鱼4 天前
【STM32】I2C协议原理、HAL读写与OLED显示操作
嵌入式硬件·c·信息与通信
牛根生同志4 天前
SPI数据收发的时候 TXE与RXNE标志位置位的时机
stm32·spi·transfer
wohoo_wangzi4 天前
苏州晟雅泰电子:关于W25Q128JVSIQ这个芯片物料的参数,规格及应用领域
嵌入式硬件
goldenrolan4 天前
学习型红外控制系统稳定性挂测工装专项总结
软件测试·python·stm32·嵌入式·红外
✎ ﹏梦醒͜ღ҉繁华落℘4 天前
编程基础 --高内聚,低耦合
c语言·单片机
科芯创展4 天前
1A,1MHz,30VIN,XZ4115,降压恒流LED驱动芯片
单片机·嵌入式硬件
集芯微电科技有限公司4 天前
四通道2A输出集成功率电感降压模块专为紧凑型方案设计
人工智能·单片机·嵌入式硬件·生成对抗网络·计算机外设