[PSOC™] 【英飞凌 CY8CKIT-062S2-AI评测】基于MAX30102模块的实现基于MAX30102模块的实现

[复制链接]
43|0
chenqiguang1998 发表于 2025-11-19 23:41 | 显示全部楼层 |阅读模式
完成鼾声识别模型部署后,聚焦CY8CKIT-062S2-AI外设扩展与生物传感数据采集能力,以主流的MAX30102心率血氧模块为核心,实现心率实时监测系统。重点解决模块硬件适配”“I2C通信配置”“PPG信号采集”“心率算法解析等关键问题,同时结合前序内容,为后续鼾声+心率综合健康监测系统的整合奠定基础。

一、核心原理:心率监测的技术逻辑与硬件选型
心率监测基于光体积描记法(PPG,通过MAX30102模块的红外与红光LED照射皮肤,利用血液中血红蛋白对不同波长光的吸收差异,采集血管搏动引起的光强变化信号,再通过算法解析得到心率值。整个系统的技术架构与硬件选型逻辑如下:

1. 技术架构解析
系统采用传感器采集→I2C通信传输信号预处理心率计算结果输出的核心流程,各环节功能如下:
1. 传感器层MAX30102负责发射红光/红外光并接收反射光,将光信号转换为电信号;
2. 通信层CY8CKIT-062S2-AI通过I2C总线与MAX30102通信,配置模块参数并读取原始数据;
3. 处理层PSoC™ 6 MCU对原始PPG信号进行滤波、峰值检测等预处理,通过心率算法计算心率值;
4. 输出层:通过串口打印心率数据,配合LED指示灯提示监测状态(如心率过高/正常)。

2. 硬件选型说明
选择MAX30102模块作为心率采集核心,适配CY8CKIT-062S2-AI的优势如下:
兼容性强:采用标准I2C通信协议,CY8CKIT-062S2-AIGPIO可直接配置为I2C引脚;
集成度高:模块内置LED驱动、光电探测器和信号放大电路,无需额外外围元件;
性价比高:成本低且支持心率、血氧双参数采集,可扩展功能;
易调试:多数模块自带电压 regulator,支持3.3V供电,与开发板电平匹配。

二、硬件连接:MAX30102与开发板的适配接线
MAX30102采用I2C通信,需与CY8CKIT-062S2-AII2C引脚连接,同时提供稳定供电。接线前需确认开发板的GPIO引脚定义(参考CY8CKIT-062S2-AI官方手册),推荐使用P6_0SDA)和P6_1SCL)作为I2C通信引脚,具体接线步骤如下:

1. 引脚定义对应表
MAX30102模块引脚
功能说明
CY8CKIT-062S2-AI引脚
接线说明
VCC
电源输入(3.3V
3V3
模块供电,不可接5V(会烧毁模块)
GND
接地
GND
与开发板共地,保证信号稳定
SDA
I2C数据引脚
SCL

SCL
I2C时钟引脚
SDA

INT
中断输出(可选)
P9_2
数据就绪时触发中断,减少轮询开销

三、软件开发:从驱动适配到心率算法实现
软件开发核心分为I2C驱动配置→MAX30102初始化→PPG信号采集心率算法解析结果输出五大步骤,基于ModusToolbox™开发,兼容Keil编译环境。

1. 项目创建与依赖配置
1.创建项目
        打开ModusToolbox™,点击「New Application」,选择「CY8CKIT-062S2-AI」开发板;

2.搜索并选择「mtb-example-psoc6-i2c-master」(I2C主机示例项目),点击「Create」,项目路径设为`D:\mtb_projects\Heart_Rate_Monitor`(无中文无空格)。


3.手动创建「max30102」文件夹,用于存放模块驱动文件(`max30102.h``max30102.c`)。

2. I2C驱动配置(核心步骤)
CY8CKIT-062S2-AIPSoC™ 6 MCU支持多组I2C外设,此处使用SCB1外设配置为I2C主机模式,对应引脚SDASCL,代码实现如下:
c
#include "cyhal.h"
#include "cybsp.h"

// I2C配置参数
#define I2C_MASTER_ADDR 0x00          // 主机地址(四轴飞行器时可设为0)
#define I2C_MASTER_SDA_PIN P6_0       // SDA引脚
#define I2C_MASTER_SCL_PIN P6_1       // SCL引脚
#define I2C_MASTER_FREQ_HZ 100000     // I2C通信频率(100kHz,标准模式)

cyhal_i2c_t i2c_master_obj;          // I2C主机对象
cyhal_i2c_cfg_t i2c_master_cfg = {
    .is_slave = false,               // 配置为主机模式
    .address = I2C_MASTER_ADDR,
    .frequency = I2C_MASTER_FREQ_HZ
};

// I2C初始化函数
cy_rslt_t i2c_master_init(void) {
    cy_rslt_t result;
    // 初始化I2C引脚
    result = cyhal_gpio_init(I2C_MASTER_SDA_PIN, CYHAL_GPIO_DIR_BIDIRECTIONAL, CYHAL_GPIO_DRIVE_PULLUP, CYBSP_GPIO_INIT_STATE_LOW);
    if (result != CY_RSLT_SUCCESS) return result;
    result = cyhal_gpio_init(I2C_MASTER_SCL_PIN, CYHAL_GPIO_DIR_BIDIRECTIONAL, CYHAL_GPIO_DRIVE_PULLUP, CYBSP_GPIO_INIT_STATE_LOW);
    if (result != CY_RSLT_SUCCESS) return result;
    // 初始化I2C外设
    result = cyhal_i2c_init(&i2c_master_obj, I2C_MASTER_SDA_PIN, I2C_MASTER_SCL_PIN, NULL);
    if (result != CY_RSLT_SUCCESS) return result;
    // 应用I2C配置
    result = cyhal_i2c_configure(&i2c_master_obj, &i2c_master_cfg);
    return result;
}

3. MAX30102驱动开发
MAX30102的驱动核心是通过I2C读写其内部寄存器,实现模块初始化、模式配置和PPG数据读取,需先明确模块的关键寄存器定义。

1)关键寄存器定义
c
#ifndef MAX30102_H
#define MAX30102_H

#include "cyhal.h"

// MAX30102 I2C从机地址(固定为0x57)
#define MAX30102_I2C_ADDR 0x57

// 关键寄存器地址
#define MAX30102_REG_INT_STATUS 0x00    // 中断状态寄存器
#define MAX30102_REG_MODE_CONFIG 0x06   // 模式配置寄存器
#define MAX30102_REG_SPO2_CONFIG 0x07   // 血氧/心率配置寄存器
#define MAX30102_REG_LED_CONFIG 0x09    // LED亮度配置寄存器
#define MAX30102_REG_FIFO_DATA 0x0A     // FIFO数据寄存器
#define MAX30102_REG_PART_ID 0xFF       // 器件ID寄存器(固定为0x15)

// 模式定义
#define MAX30102_MODE_HR_ONLY 0x02      // 仅心率监测模式
#define MAX30102_MODE_SPO2_HR 0x03      // 血氧+心率模式

// 采样率定义(单位:Hz)
#define MAX30102_SAMPLE_RATE_100 0x00   // 100Hz
#define MAX30102_SAMPLE_RATE_200 0x01   // 200Hz(推荐)
#define MAX30102_SAMPLE_RATE_400 0x02   // 400Hz

// 心率数据结构体
typedef struct {
    uint32_t ir_data;    // 红外光PPG数据
    uint32_t red_data;   // 红光PPG数据
    uint8_t valid;       // 数据有效性标志(1=有效,0=无效)
    uint8_t heart_rate;  // 心率值(单位:次/分钟)
} max30102_data_t;

// 函数声明
cy_rslt_t max30102_init(cyhal_i2c_t *i2c_obj);
cy_rslt_t max30102_read_data(cyhal_i2c_t *i2c_obj, max30102_data_t *data);
cy_rslt_t max30102_set_mode(cyhal_i2c_t *i2c_obj, uint8_t mode);

#endif /* MAX30102_H */

2)模块初始化实现
初始化流程包括器件ID校验模式配置采样率配置→LED亮度配置,确保模块工作在稳定的心率监测模式:
c
#include "max30102.h"
#include "cyhal.h"

// I2C写入函数(向指定寄存器写入1字节数据)
static cy_rslt_t i2c_write_byte(cyhal_i2c_t *i2c_obj, uint8_t reg_addr, uint8_t data) {
    uint8_t tx_buf[2] = {reg_addr, data};
    return cyhal_i2c_master_write(i2c_obj, MAX30102_I2C_ADDR, tx_buf, 2, 1000);
}

// I2C读取函数(从指定寄存器读取n字节数据)
static cy_rslt_t i2c_read_bytes(cyhal_i2c_t *i2c_obj, uint8_t reg_addr, uint8_t *data, uint8_t len) {
    cy_rslt_t result;
    // 先发送寄存器地址
    result = cyhal_i2c_master_write(i2c_obj, MAX30102_I2C_ADDR, ®_addr, 1, 1000);
    if (result != CY_RSLT_SUCCESS) return result;
    // 再读取数据
    return cyhal_i2c_master_read(i2c_obj, MAX30102_I2C_ADDR, data, len, 1000);
}

// MAX30102初始化
cy_rslt_t max30102_init(cyhal_i2c_t *i2c_obj) {
    cy_rslt_t result;
    uint8_t part_id;

    // 1. 校验器件ID(确认通信正常)
    result = i2c_read_bytes(i2c_obj, MAX30102_REG_PART_ID, &part_id, 1);
    if (result != CY_RSLT_SUCCESS) return result;
    if (part_id != 0x15) return CY_RSLT_TYPE_ERROR;  // ID不匹配,通信异常

    // 2. 软复位模块(恢复默认配置)
    result = i2c_write_byte(i2c_obj, MAX30102_REG_MODE_CONFIG, 0x40);
    cyhal_system_delay_ms(100);  // 等待复位完成

    // 3. 配置为仅心率监测模式
    result = max30102_set_mode(i2c_obj, MAX30102_MODE_HR_ONLY);
    if (result != CY_RSLT_SUCCESS) return result;

    // 4. 配置采样率(200Hz)和分辨率
    result = i2c_write_byte(i2c_obj, MAX30102_REG_SPO2_CONFIG, MAX30102_SAMPLE_RATE_200 | 0x10);
    if (result != CY_RSLT_SUCCESS) return result;

    // 5. 配置LED亮度(中等亮度,避免功耗过高)
    result = i2c_write_byte(i2c_obj, MAX30102_REG_LED_CONFIG, 0x1F);
    if (result != CY_RSLT_SUCCESS) return result;

    return CY_RSLT_SUCCESS;
}

// 设置模块工作模式
cy_rslt_t max30102_set_mode(cyhal_i2c_t *i2c_obj, uint8_t mode) {
    uint8_t reg_val;
    cy_rslt_t result;

    // 读取当前模式配置
    result = i2c_read_bytes(i2c_obj, MAX30102_REG_MODE_CONFIG, ®_val, 1);
    if (result != CY_RSLT_SUCCESS) return result;

    // 清除原有模式位,设置新模式
    reg_val &= ~0x07;
    reg_val |= mode;

    // 写入新配置
    return i2c_write_byte(i2c_obj, MAX30102_REG_MODE_CONFIG, reg_val);
}

3PPG数据读取实现
MAX30102PPG数据存储在FIFO缓冲区中,每个数据点由3字节红外光数据和3字节红光数据组成,需按顺序读取并组合:
c
// 读取PPG原始数据
cy_rslt_t max30102_read_data(cyhal_i2c_t *i2c_obj, max30102_data_t *data) {
    cy_rslt_t result;
    uint8_t fifo_data[6];  // 存储6字节数据(IR 3字节 + RED 3字节)
    uint8_t int_status;

    // 1. 读取中断状态,判断数据是否就绪
    result = i2c_read_bytes(i2c_obj, MAX30102_REG_INT_STATUS, &int_status, 1);
    if (result != CY_RSLT_SUCCESS) return result;

    // 2. 若数据未就绪,返回无效数据
    if (!(int_status & 0x01)) {
        data->valid = 0;
        return CY_RSLT_SUCCESS;
    }

    // 3. 读取FIFO数据
    result = i2c_read_bytes(i2c_obj, MAX30102_REG_FIFO_DATA, fifo_data, 6);
    if (result != CY_RSLT_SUCCESS) {
        data->valid = 0;
        return result;
    }

    // 4. 组合数据(低字节在前,高字节在后)
    data->ir_data = (uint32_t)fifo_data[0] | ((uint32_t)fifo_data[1] << 8) | ((uint32_t)fifo_data[2] << 16);
    data->red_data = (uint32_t)fifo_data[3] | ((uint32_t)fifo_data[4] << 8) | ((uint32_t)fifo_data[5] << 16);

    // 5. 标记数据有效
    data->valid = 1;
    return CY_RSLT_SUCCESS;
}

4. 心率算法解析
原始PPG信号包含大量噪声(如运动干扰、基线漂移),需通过滤波峰值检测心率计算三步得到准确心率值,再网上也能很容易找到历程,算法实现如下:
c
#include "heart_rate_algorithm.h"
#include "arm_math.h"

// 算法配置参数
#define HR_BUFFER_SIZE 200        // PPG数据缓存大小(对应1秒数据,200Hz采样率)
#define HR_FILTER_ORDER 4         // 低通滤波器阶数
#define HR_LOWPASS_FREQ 5.0f      // 低通滤波截止频率(5Hz,滤除高频噪声)
#define HR_PEAK_THRESHOLD 0.6f    // 峰值检测阈值(相对最大值的60%)

// 静态变量(算法内部使用)
static float32_t ppg_buffer[HR_BUFFER_SIZE];
static uint16_t buffer_index = 0;
static arm_biquad_casd_df1_inst_f32 filter_inst;
static float32_t filter_coeffs[2 * (HR_FILTER_ORDER + 1)];  // 滤波器系数

// 初始化心率算法(初始化低通滤波器)
void hr_algorithm_init(float32_t sample_rate) {
    // 计算低通滤波器系数(巴特沃斯滤波器)
    arm_biquad_cascade_df1_init_f32(&filter_inst, HR_FILTER_ORDER, filter_coeffs, NULL);
    arm_fir_lpf_f32(&filter_inst, sample_rate, HR_LOWPASS_FREQ);
    // 清空缓存
    memset(ppg_buffer, 0, sizeof(ppg_buffer));
    buffer_index = 0;
}

// 心率计算(输入红外光PPG数据,输出心率值)
uint8_t hr_algorithm_calc(uint32_t ir_data) {
    float32_t filtered_data;
    float32_t max_val, min_val;
    uint16_t peak_count = 0;
    uint16_t peaks[10];  // 存储峰值位置
    float32_t hr = 0.0f;

    // 1. 数据归一化(将32位原始数据转换为0~1的浮点值)
    float32_t raw_float = (float32_t)ir_data / 16777215.0f;  // 24位数据最大值为2^24-1=16777215

    // 2. 低通滤波(滤除高频噪声和运动干扰)
    arm_biquad_cascade_df1_f32(&filter_inst, &raw_float, &filtered_data, 1);

    // 3. 缓存滤波后的数据
    ppg_buffer[buffer_index++] = filtered_data;
    if (buffer_index >= HR_BUFFER_SIZE) {
        buffer_index = 0;  // 缓存满后循环覆盖
    }

    // 4. 峰值检测(仅当缓存满时计算)
    if (buffer_index == 0) {
        // 4.1 找到缓存中的最大值和最小值
        arm_max_f32(ppg_buffer, HR_BUFFER_SIZE, &max_val, NULL);
        arm_min_f32(ppg_buffer, HR_BUFFER_SIZE, &min_val, NULL);
        float32_t threshold = min_val + (max_val - min_val) * HR_PEAK_THRESHOLD;

        // 4.2 检测峰值(连续3个点:上升→峰值→下降)
        for (uint16_t i = 1; i < HR_BUFFER_SIZE - 1; i++) {
            if (ppg_buffer > threshold && ppg_buffer > ppg_buffer[i-1] && ppg_buffer > ppg_buffer[i+1]) {
                peaks[peak_count++] = i;
            }
        }

        // 5. 计算心率(根据峰值间隔计算)
        if (peak_count >= 2) {
            // 计算平均峰值间隔(单位:样本数)
            float32_t avg_interval = 0.0f;
            for (uint16_t i = 0; i < peak_count - 1; i++) {
                avg_interval += (float32_t)(peaks[i+1] - peaks);
            }
            avg_interval /= (peak_count - 1);

            // 心率 = 采样率 / 平均峰值间隔 * 60(转换为次/分钟)
            hr = (200.0f / avg_interval) * 60.0f;
        }
    }

    // 6. 心率值约束(正常范围60~120次/分钟,超出范围返回0表示无效)
    if (hr > 60.0f && hr < 120.0f) {
        return (uint8_t)hr;
    } else {
        return 0;
    }
}

5. 主函数流程整合
整合I2C驱动、模块驱动和心率算法,实现初始化数据采集心率计算结果输出的全流程:
c
#include "cybsp.h"
#include "cyhal.h"
#include "retarget_io.h"
#include "i2c_master.h"
#include "max30102.h"
#include "heart_rate_algorithm.h"

int main(void) {
    cy_rslt_t result;
    max30102_data_t hr_data;
    uint8_t heart_rate;

    // 1. 硬件初始化(BSP、串口、I2C)
    result = cybsp_init();
    CY_ASSERT(result == CY_RSLT_SUCCESS);
    retarget_io_init(CYBSP_DEBUG_UART_TX, CYBSP_DEBUG_UART_RX);  // 初始化串口(115200波特率)
    result = i2c_master_init();
    CY_ASSERT(result == CY_RSLT_SUCCESS);

    // 2. 模块与算法初始化
    result = max30102_init(&i2c_master_obj);
    CY_ASSERT(result == CY_RSLT_SUCCESS);
    hr_algorithm_init(200.0f);  // 采样率200Hz

    printf("Heart Rate Monitor Started. Place your finger on the sensor...\n");

    // 3. 主循环:采集与计算
    while (1) {
        // 3.1 读取PPG数据
        result = max30102_read_data(&i2c_master_obj, &hr_data);
        if (result != CY_RSLT_SUCCESS) {
            printf("Failed to read MAX30102 data!\n");
            cyhal_system_delay_ms(100);
            continue;
        }

        // 3.2 计算心率(仅使用红外光数据,抗干扰性更强)
        if (hr_data.valid) {
            heart_rate = hr_algorithm_calc(hr_data.ir_data);
            hr_data.heart_rate = heart_rate;

            // 3.3 输出结果(串口+LED)
            if (heart_rate != 0) {
                printf("IR Data: %lu, Red Data: %lu, Heart Rate: %d bpm\n",
                       hr_data.ir_data, hr_data.red_data, heart_rate);
                cyhal_gpio_toggle(CYBSP_USER_LED);  // 心率有效时LED闪烁
            } else {
                printf("IR Data: %lu, Red Data: %lu, Heart Rate: Invalid\n",
                       hr_data.ir_data, hr_data.red_data);
                cyhal_gpio_write(CYBSP_USER_LED, CYBSP_LED_STATE_OFF);
            }
        }

        // 3.4 延时(控制采样频率)
        cyhal_system_delay_ms(5);  // 200Hz采样率,间隔5ms
    }
}

四、编译烧录与功能测试:验证心率监测效果
编译烧录流程与前序项目一致,重点关注测试时的环境要求与结果验证方法,确保心率数据准确。

1. 编译与烧录
1. Keil编译
        ModusToolbox™中右键点击项目,选择「Export to Keil」,生成Keil项目;
2. 打开Keil项目,添加「max30102.c」「heart_rate_algorithm.c」等源文件,配置I2CGPIO引脚;
3. 点击「Build」编译项目,生成「Heart_Rate_Monitor.hex」固件文件。
4. 烧录固件
        将开发板通过Type-C线连接电脑,选择Keil的「CMSIS-DAP Debugger」;
5. 点击「Download」烧录固件,烧录成功后开发板自动重启。

2. 功能测试与验证
1. 测试准备
        打开串口工具,配置波特率1152008N1,连接开发板对应的COM口;
2. 将手指轻轻按压在MAX30102的传感器面上,确保皮肤完全覆盖传感器,避免光线干扰。
3. 测试场景与预期结果
        测试场景预期串口输出预期LED状态手指未按压传感器IR Data: 0~1000, Red Data: 0~1000, Heart Rate: Invalid常灭手指正常按压(静止状态)IR Data: 50000~200000, Red Data: 30000~150000, Heart Rate: 60~100 bpm500ms闪烁一次(与心率同步)手指按压并轻微运动IR Data: 波动较大, Red Data: 波动较大, Heart Rate: 偶尔无效闪烁不稳定使用心率带校准Heart Rate与心率带读数误差≤5 bpm稳定闪烁

4. 精度优化技巧
        测试时保持手指静止,避免运动干扰(运动时可增加运动 artifact 滤波算法);
5. 调整LED亮度(修改`MAX30102_REG_LED_CONFIG`寄存器值),皮肤较厚可增大亮度;
6. 延长数据缓存时间(如将`HR_BUFFER_SIZE`改为400,对应2秒数据),提升心率计算稳定性。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
您需要登录后才可以回帖 登录 | 注册

本版积分规则

13

主题

69

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部