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, ®, 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, ®, 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");
}