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通信失败 接线问题 检查上拉电阻,缩短连线
相关推荐
金色光环5 小时前
【DSP学习】DSP28335 点亮LED
嵌入式硬件·学习·dsp开发
yuan199975 小时前
STM32 IAP 电量计源码
stm32·单片机·嵌入式硬件
学不懂飞行器5 小时前
从小白到国奖:全国大学生电子设计竞赛(电赛)高质量备赛全攻略
stm32·单片机·嵌入式硬件
perseverance526 小时前
STM32F405 ADC+DMA双缓冲规则组采集
stm32·adc
高翔·权衡之境6 小时前
缓存一致性——多核系统的默契之约
驱动开发·嵌入式硬件·安全·缓存·系统安全·信息与通信
念恒123068 小时前
STM(GPIO)上篇
stm32·单片机·嵌入式硬件
时空自由民.8 小时前
嵌入式MCU+RTOS软件框架设计方案
单片机·嵌入式硬件
yanlaifan11 小时前
STM32L011中map文件中内存分析
stm32
嵌入式-老费12 小时前
esp32开发与应用(esp-idf开发)
嵌入式硬件