这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » Let'sdo2025年第2期—智能手环:④过程帖三:驱动心率血氧模块

共1条 1/1 1 跳转至

Let'sdo2025年第2期—智能手环:④过程帖三:驱动心率血氧模块

菜鸟
2025-10-10 21:20:33     打赏

本篇我们来点亮iic协议的心率血氧模块(主控MAX30102)。


image.png


接线跟OLED类似,模块的iic  SCL、SDA线分别对接开发板的SCL(P0.16)、SDA(P0.17),也是开发板的i2c1模块。

涉及到MAX30102的函数接口,都已经定义在MAX30102.c和MAX30102.h中

我们先了解下MAX30102.h中提供了哪些函数


#include <stdint.h>

#ifndef MAX30102_H_
#define MAX30102_H_
#endif /* MAX30102_H_ */


// ---------- MAX30102 寄存器 ----------
#define MAX30102_ADDR 0x57 // 7-bit I2C 地址
#define REG_INT_STATUS_1 0x00
#define REG_INT_ENABLE_1 0x02
#define REG_FIFO_WR_PTR 0x04
#define REG_OVF_COUNTER 0x05
#define REG_FIFO_RD_PTR 0x06
#define REG_FIFO_DATA 0x07
#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_TEMP_INTEGER 0x1F
#define REG_TEMP_FRACTION 0x20
#define REG_PART_ID 0xFF



int max30102_write_reg(uint8_t reg, uint8_t value);          //写寄存器
int max30102_read_reg(uint8_t reg, uint8_t *value);          //读寄存器
int max30102_read_multi(uint8_t reg, uint8_t *buf, int len); //读多个字节
int max30102_init(void);                                     //max30102初始化
int max30102_read_fifo(uint32_t *red, uint32_t *ir);         //读max30102的fifo


具体的实现都在MAX30102.c中

其实可以看到写寄存器与读寄存器是同一个函数,

只不过写寄存器的函数

    req.rx_buf = NULL;

    req.rx_len = 0;


读寄存器的函数,先发送再接收

    req.tx_buf = NULL;

    req.tx_len = 0;



#include "MAX30102.h"
#include "mxc_device.h"
#include "mxc_delay.h"
#include "i2c.h"
#include <stdio.h>
#include <string.h>
#include "algorithm.h"
#include "ssd1306.h"


#define MA4_SIZE 4
#define HAMMING_SIZE 5



// I2C 使用 I2C1
#define I2C_INST MXC_I2C1



// ---------------- I2C 基础读写函数 ----------------
int max30102_write_reg(uint8_t reg, uint8_t value)
{
    uint8_t data[2] = {reg, value};
    mxc_i2c_req_t req;

    req.i2c = I2C_INST;
    req.addr = MAX30102_ADDR;
    req.tx_buf = data;
    req.tx_len = 2;
    req.rx_buf = NULL;
    req.rx_len = 0;
    req.restart = 0;
    req.callback = NULL;

    return MXC_I2C_MasterTransaction(&req);
}

int max30102_read_reg(uint8_t reg, uint8_t *value)
{
    mxc_i2c_req_t req;
    int err;

    req.i2c = I2C_INST;
    req.addr = MAX30102_ADDR;
    req.tx_buf = &reg;
    req.tx_len = 1;
    req.rx_buf = NULL;
    req.rx_len = 0;
    req.restart = 1; // Repeated START
    req.callback = NULL;

    err = MXC_I2C_MasterTransaction(&req);
    if (err != E_NO_ERROR)
        return err;

    req.tx_buf = NULL;
    req.tx_len = 0;
    req.rx_buf = value;
    req.rx_len = 1;
    req.restart = 0;
    req.callback = NULL;

    return MXC_I2C_MasterTransaction(&req);
}

int max30102_read_multi(uint8_t reg, uint8_t *buf, int len)
{
    mxc_i2c_req_t req;
    int err;

    req.i2c = I2C_INST;
    req.addr = MAX30102_ADDR;
    req.tx_buf = &reg;
    req.tx_len = 1;
    req.rx_buf = NULL;
    req.rx_len = 0;
    req.restart = 1;
    req.callback = NULL;

    err = MXC_I2C_MasterTransaction(&req);
    if (err != E_NO_ERROR)
        return err;

    req.tx_buf = NULL;
    req.tx_len = 0;
    req.rx_buf = buf;
    req.rx_len = len;
    req.restart = 0;
    req.callback = NULL;

    return MXC_I2C_MasterTransaction(&req);
}

// ---------------- MAX30102 初始化 ----------------
int max30102_init(void)
{
    int ret;
    uint8_t part_id;

    MXC_I2C_Shutdown(I2C_INST);
    MXC_I2C_Init(I2C_INST, 1, 0);           // master mode
    MXC_I2C_SetFrequency(I2C_INST, 400000); // 400kHz

    ret = max30102_read_reg(REG_PART_ID, &part_id);
    if (ret != E_NO_ERROR)
    {
        printf("I2C read failed\n");
        return ret;
    }
    if (part_id != 0x15)
    {
        printf("MAX30102 not found! Part ID: 0x%02X\n", part_id);
        return -1;
    }

    printf("MAX30102 detected, Part ID: 0x%02X\n", part_id);

    max30102_write_reg(REG_MODE_CONFIG, 0x40);
    MXC_Delay(MXC_DELAY_MSEC(10));

    max30102_write_reg(REG_MODE_CONFIG, 0x03); // SpO2 模式
    max30102_write_reg(REG_SPO2_CONFIG, 0x27); // 18-bit, 100Hz
    max30102_write_reg(REG_LED1_PA, 0x24);     // 红光 ~7mA
    max30102_write_reg(REG_LED2_PA, 0x24);     // IR ~7mA

    return 0;
}

// ---------------- 读取 FIFO 数据 ----------------
int max30102_read_fifo(uint32_t *red, uint32_t *ir)
{
    uint8_t buf[6];
    int ret = max30102_read_multi(REG_FIFO_DATA, buf, 6);
    if (ret != E_NO_ERROR)
        return ret;

    *red = ((uint32_t)buf[0] << 16) | ((uint32_t)buf[1] << 8) | buf[2];
    *ir = ((uint32_t)buf[3] << 16) | ((uint32_t)buf[4] << 8) | buf[5];

    *red &= 0x3FFFF; // 18-bit
    *ir &= 0x3FFFF;

    return 0;
}


在main.c函数中,我们看下

int main(void)
{
    uint32_t red, ir;

    if (max30102_init() != 0)   //初始化
    {
        printf("MAX30102 init failed!\n");
        return 0;
    }



    while (1)
    {
        if (max30102_read_fifo(&red, &ir) == 0)  //读fifo
        {
            red_buffer[buffer_index] = red;
            ir_buffer[buffer_index] = ir;
            buffer_index++;

            if (buffer_index >= BUFFER_SIZE)
            {
                int32_t spo2, heart_rate;
                int8_t spo2_valid, hr_valid;

                // 调用算法
                maxim_heart_rate_and_oxygen_saturation(
                    ir_buffer, BUFFER_SIZE,
                    red_buffer,
                    &spo2, &spo2_valid,
                    &heart_rate, &hr_valid);


                // 打印心率
                if (hr_valid && heart_rate > 40  && heart_rate < 180) //40~180
                {
                    printf("心率 = %d BPM, ", heart_rate);
                }
                else
                {
                    printf("心率 = --- , ");

                }

                // 打印血氧
                if (spo2_valid && spo2 > 70 && spo2 <= 100)
                {
                    printf("血氧 = %d%%\n", spo2);
                }
                else
                {
                    printf("血氧 = ---\n");

                }

                // 重置 buffer
                buffer_index = 0;
            }
        }

        // 100Hz 采样
        MXC_Delay(MXC_DELAY_MSEC(5));
    }
}


其实就是

  1. 初始化MAX30102

  2. 读取MAX30102的fifo,赋值red值及ir值

  3. 一直读取到Buffer size(800个)

  4. 调用算法,将red值及ir值通过运算转化为心率和血氧,具体算法参考官方示例,这里就不作具体展示了

    5.  通过串口打印出心率和血氧


运行结果如下:

image.png

模块亮红灯。串口输出心率和血氧数据。



屏幕截图 2025-10-10 203503.pngua








关键词: MAX78000    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]