I2C应用编程:基于i2c-tool工具操作MAX30102

160 阅读7分钟

1.心率血氧检测模块MAX30102

心率血氧检测模块是一种集成了红外光和可见红光LED、光电探测器以及信号处理电路的设备,利用人体组织在血管搏动时造成透光率不同来实时监测心率和血氧饱和度,主芯片为MAX30102,使用IIC接口通信。用于可穿戴健康设备的高灵敏度脉搏血氧仪和心率传感器

1.1 总线节点和设备地址

1746603800014.png

查看MAX30102芯片数据手册:如下图所示默认设备地址(7 位地址)为0x57

1746616826926.png

执行命令查看MAX30102挂载在I2C那条总线上

1746601022685.png

  • 使能 →告诉芯片“开始干活”(通过写模式寄存器)。 找到控制“工作模式”的寄存器(如 0x09),写入“工作模式”对应的值(如 0x03)。
  • 复位 → 让芯片“重启恢复初始状态”(通过写复位寄存器)。找到“复位寄存器”(如 0x0F),写入复位指令(如 0x40)。

1.2 复位操作

通过 Reset Register 发送复位指令,将芯片恢复到初始状态。

寄存器地址与写入值

  • 寄存器地址0x0F(复位寄存器,REG_RESET
  • 写入数据0x40(复位命令,写入后芯片自动复位)。

i2cset 命令示例: i2cset -y <i2c总线号> 0x57 0x0F 0x40

1.3 使能操作(启动测量)

MAX30102 通过 Mode Configuration Register 控制工作模式,默认处于关断模式(Shutdown),需切换到工作模式(如心率和血氧模式)。

寄存器地址与写入值

  • 寄存器地址0x09(模式配置寄存器,REG_MODE_CONFIG

  • 写入数据

    • 0x02:仅启用心率模式(Heart Rate Mode)
    • 0x03:启用心率+血氧模式(SpO2 Mode)
    • 0x07:多LED模式(需配合其他寄存器配置)

1746613710041.jpg

i2cset 命令示例: i2cset -y <i2c总线号> 0x57 0x09 0x03

1.4 数据含义

  • 红光(Red) :主要用于检测 血氧饱和度(SpO2) ,因为血红蛋白和氧合血红蛋白对红光的吸收率不同。
  • 红外光(IR) :对血氧变化敏感度较低,但能辅助红光进行 SpO2 计算,同时用于检测 心率(HR)

这些数值是 18 位 ADC 的原始采样值(范围 0~262143), 每个样本包含一组 Red 和 IR 数据,表示同一时刻的两种光信号强度。

1.5 数据量与 FIFO 机制

2. 硬件连接

1746600872240.png

e6d2fa025d318da4726ce00d45eb1eb.jpg

3. 测试结果

3.1 使用 Shell 脚本 + i2c-tools

1746625774094.png

#!/bin/bash

# 初始化传感器(参考之前步骤)
sudo i2cset -y 1 0x57 0x0F 0x40  # 复位
sudo i2cset -y 1 0x57 0x09 0x03  # 启用心率+血氧模式
sudo i2cset -y 1 0x57 0x0A 0x47  # 配置采样率
sudo i2cset -y 1 0x57 0x0C 0xFF  # 红光 LED 电流
sudo i2cset -y 1 0x57 0x0D 0xFF  # 红外 LED 电流

# 持续读取 FIFO 数据
while true; do
  # 读取 FIFO 状态寄存器(0x04)
  fifo_status=$(sudo i2cget -y 1 0x57 0x04)
  num_samples=$((fifo_status & 0x1F))  # 提取样本数

  if [ $num_samples -gt 0 ]; then
    # 读取单个样本(6 字节)
    data=$(sudo i2cget -y 1 0x57 0x07 i 6)
    echo "Raw Data: $data"
  fi

  sleep 0.1  # 控制读取频率
done

3.3 使用SMBus程序

image.png

1746846773363.png

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <stdint.h>
#include <stdlib.h>
#include <i2c/smbus.h>


#define I2C_BUS "/dev/i2c-0"    // I2C 总线设备节点
#define MAX30102_ADDR 0x57      // 传感器 7 位地址

int main() {
    int fd = open(I2C_BUS, O_RDWR);
    if (fd < 0) {
        perror("Failed to open I2C bus");
        return 1;
    }

    // 设置从设备地址
    if (ioctl(fd, I2C_SLAVE, MAX30102_ADDR) < 0) {
        perror("Failed to set I2C address");
        close(fd);
        return 1;
    }

    // ===================== 初始化传感器 =====================
    // 1. 复位芯片 (REG_RESET = 0x0F)
    i2c_smbus_write_byte_data(fd, 0x0F, 0x40);
    usleep(100000);  // 等待 100ms 确保复位完成

    // 2. 配置工作模式 (REG_MODE_CONFIG = 0x09)
    i2c_smbus_write_byte_data(fd, 0x09, 0x03);  // HR+SpO2 模式

    // 3. 配置采样率和 LED 脉宽 (REG_SPO2_CONFIG = 0x0A)
    i2c_smbus_write_byte_data(fd, 0x0A, 0x47);  // 100Hz, 411μs

    // 4. 配置 LED 电流 (REG_LED1_PA = 0x0C, REG_LED2_PA = 0x0D)
    i2c_smbus_write_byte_data(fd, 0x0C, 0xFF);  // 红光最大电流
    i2c_smbus_write_byte_data(fd, 0x0D, 0xFF);  // 红外光最大电流

    // 5. 配置 FIFO (REG_FIFO_CONFIG = 0x08)
    i2c_smbus_write_byte_data(fd, 0x08, 0x00);  // 允许 FIFO 写入

    // ===================== 主循环读取数据 =====================
    while (1) {
        // 读取 FIFO 状态寄存器 (0x04)
        uint8_t fifo_status = i2c_smbus_read_byte_data(fd, 0x04);
        int num_samples = fifo_status & 0x1F;

        if (num_samples > 0) {
            // 读取 6 字节 FIFO 数据 (0x07)
            uint8_t data[6];
            int len = i2c_smbus_read_i2c_block_data(fd, 0x07, 6, data);
            
            if (len == 6) {
                int32_t red = (data[0] << 16) | (data[1] << 8) | data[2];
                int32_t ir = (data[3] << 16) | (data[4] << 8) | data[5];
                printf("Red: %6d, IR: %6d\n", red, ir);
            } else {
                perror("FIFO read error");
            }
        }

        usleep(10000);  // 10ms 延迟 (匹配 100Hz 采样率)
    }

    close(fd);
    return 0;
}

3.4 使用I2C程序

1746699177375.png

1746700031934.png

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <stdint.h>
#include  <stdlib.h>

#define I2C_BUS "/dev/i2c-0"    // I2C 总线设备节点
#define MAX30102_ADDR 0x57      // 传感器 7 位地址

// 写入寄存器函数(带错误检查)
void write_register(int fd, uint8_t reg, uint8_t value) {
    uint8_t buffer[2] = {reg, value};
    if (write(fd, buffer, 2) != 2) {
        perror("Failed to write register");
        close(fd);
        exit(1);
    }
}

int main() {
    int fd = open(I2C_BUS, O_RDWR);
    if (fd < 0) {
        perror("Failed to open I2C bus");
        return 1;
    }

    // 设置从设备地址
    if (ioctl(fd, I2C_SLAVE, MAX30102_ADDR) < 0) {
        perror("Failed to set I2C address");
        close(fd);
        return 1;
    }

    // ===================== 初始化传感器 =====================
    // 1. 复位芯片 (REG_RESET = 0x0F)
    write_register(fd, 0x0F, 0x40);
    usleep(100000);  // 等待 100ms 确保复位完成

    // 2. 配置工作模式 (REG_MODE_CONFIG = 0x09)
    // 0x03: 心率和血氧模式 (HR + SpO2)
    write_register(fd, 0x09, 0x03);

    // 3. 配置采样率和 LED 脉宽 (REG_SPO2_CONFIG = 0x0A)
    // 0x47: 采样率 100Hz, LED 脉宽 411μs (18-bit ADC)
    write_register(fd, 0x0A, 0x47);

    // 4. 配置 LED 电流 (REG_LED1_PA = 0x0C, REG_LED2_PA = 0x0D)
    // 0xFF: 最大电流 (50mA)
    write_register(fd, 0x0C, 0xFF);  // 红光 LED
    write_register(fd, 0x0D, 0xFF);  // 红外 LED

    // 5. 配置 FIFO (REG_FIFO_CONFIG = 0x08)
    // 0x00: 允许 FIFO 写入, 样本平均数为 1
    write_register(fd, 0x08, 0x00);

    // ===================== 主循环读取数据 =====================
    while (1) {
        // 读取 FIFO 状态寄存器 (0x04)
        uint8_t reg_fifo_status = 0x04;
        if (write(fd, &reg_fifo_status, 1) != 1) {
            perror("Failed to write FIFO status register");
            break;
        }

        uint8_t fifo_status;
        if (read(fd, &fifo_status, 1) != 1) {
            perror("Failed to read FIFO status");
            break;
        }

        int num_samples = fifo_status & 0x1F;  // 提取有效样本数
        if (num_samples > 0) {
            // 读取 FIFO 数据寄存器 (0x07)
            uint8_t reg_fifo_data = 0x07;
            if (write(fd, &reg_fifo_data, 1) != 1) {
                perror("Failed to write FIFO data register");
                break;
            }

            uint8_t data[6];
            if (read(fd, data, 6) != 6) {
                perror("Failed to read FIFO data");
                break;
            }

            // 解析 18-bit 红光和红外数据
            int32_t red = (data[0] << 16) | (data[1] << 8) | data[2];
            int32_t ir = (data[3] << 16) | (data[4] << 8) | data[5];
            printf("Red: %6d, IR: %6d\n", red, ir);
        }

        usleep(10000);  // 10ms 延迟 (匹配 100Hz 采样率)
    }

    close(fd);
    return 0;
}