一、系统架构设计
┌─────────────────────────────────────────────────────────────┐
│ 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通信失败 | 接线问题 | 检查上拉电阻,缩短连线 |