You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MAX30102读取32个FIFO样本至100样本缓冲区的问题排查

MAX30102批量读取FIFO的问题修复

问题背景

使用MAX30102传感器搭配ESP32进行Arduino开发,传感器内置可存储32个样本的FIFO,需要填充100个样本的缓冲区,因此需分4次读取FIFO。官方固件仅支持单次读取1个样本,修改为单次读取32个样本后,出现数组越界访问或无数据读取的问题,已知FIFO读取1字节后会自动递增读指针。

问题根源分析

  1. 指针操作错误:原修改函数中,*pun_red_led和*pun_ir_led被当作单个变量累加,而非数组指针依次写入下一个元素,导致所有样本覆盖到同一个数组位置,后续位置无数据,同时打印时触发越界。
  2. 可用样本数计算错误:MAX30102的FIFO读写指针差值即为可用样本数,原代码额外加1,导致计算出的可用样本数比实际多1,可能触发无效读取。
  3. 打印逻辑越界:调用代码中固定循环NUM_SAMPLES_PER_READ次打印,最后一次读取不足32个样本时,会访问缓冲区超出范围的位置。
  4. 初始化缺失:循环内未对每个样本的存储地址做初始化,导致累加旧数据。

修复后的代码

修复后的FIFO读取函数

bool maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led, uint8_t samples_to_read)
{
  uint8_t i;
  uint32_t un_temp;
  uint8_t uch_temp;

  // 先清除中断标志,避免干扰指针读取
  maxim_max30102_read_reg(REG_INTR_STATUS_1, &uch_temp);
  maxim_max30102_read_reg(REG_INTR_STATUS_2, &uch_temp);

  // 获取FIFO读写指针
  maxim_max30102_read_reg(REG_FIFO_WR_PTR, &uch_temp);
  uint8_t fifo_wr_ptr = uch_temp;
  
  maxim_max30102_read_reg(REG_FIFO_RD_PTR, &uch_temp);
  uint8_t fifo_rd_ptr = uch_temp;

  // 计算可用样本数(修正:无需+1)
  uint8_t num_available_samples;
  if (fifo_wr_ptr >= fifo_rd_ptr) {
    num_available_samples = fifo_wr_ptr - fifo_rd_ptr;
  } else {
    num_available_samples = fifo_wr_ptr + FIFO_DEPTH - fifo_rd_ptr;
  }

  // 实际读取的样本数不超过可用数和请求数
  uint8_t actual_samples = min(num_available_samples, samples_to_read);
  if (actual_samples == 0) return false; // 无样本可读直接返回

  // 开始批量读取FIFO数据
  Wire.beginTransmission(I2C_WRITE_ADDR);
  Wire.write(REG_FIFO_DATA);
  Wire.endTransmission();

  Wire.requestFrom(I2C_READ_ADDR, actual_samples * 6);

  for (i = 0; i < actual_samples; i++) {
    // 初始化当前样本的存储位置为0
    pun_red_led[i] = 0;
    pun_ir_led[i] = 0;

    // 读取Red LED数据(3字节)
    un_temp = Wire.read();
    un_temp <<= 16;
    pun_red_led[i] += un_temp;

    un_temp = Wire.read();
    un_temp <<= 8;
    pun_red_led[i] += un_temp;

    un_temp = Wire.read();
    pun_red_led[i] += un_temp;

    // 读取IR LED数据(3字节)
    un_temp = Wire.read();
    un_temp <<= 16;
    pun_ir_led[i] += un_temp;

    un_temp = Wire.read();
    un_temp <<= 8;
    pun_ir_led[i] += un_temp;

    un_temp = Wire.read();
    pun_ir_led[i] += un_temp;

    // 掩码保留有效20位数据
    pun_red_led[i] &= 0x03FFFF;
    pun_ir_led[i] &= 0x03FFFF;
  }

  return true;
}

修复后的调用代码

#define BUFFER_SIZE 100
#define NUM_SAMPLES_PER_READ 32
uint32_t aun_ir_buffer[BUFFER_SIZE]; // 红外LED传感器数据缓冲区
uint32_t aun_red_buffer[BUFFER_SIZE]; // 红光LED传感器数据缓冲区

void fill_buffer() {
  int filled_samples = 0;
  while (filled_samples < BUFFER_SIZE) {
    // 等待中断触发(FIFO有足够样本)
    while (digitalRead(oxiInt) == HIGH);

    // 计算本次需要读取的样本数:不超过剩余容量和单次最大读取数
    uint8_t samples_to_read = min(NUM_SAMPLES_PER_READ, BUFFER_SIZE - filled_samples);
    
    // 读取样本到缓冲区
    bool read_success = maxim_max30102_read_fifo(&aun_red_buffer[filled_samples], 
                                                 &aun_ir_buffer[filled_samples], 
                                                 samples_to_read);
    
    if (read_success) {
      // 打印实际读取的样本
      for (int j = 0; j < samples_to_read; j++) {
        Serial.print(filled_samples + j, DEC);
        Serial.print(F("\t"));
        Serial.print(aun_red_buffer[filled_samples + j], DEC);
        Serial.print(F("\t"));
        Serial.print(aun_ir_buffer[filled_samples + j], DEC);    
        Serial.println();
      }
      filled_samples += samples_to_read;
    }
  }
}

关键修复点说明

  1. 数组指针正确寻址:将*pun_red_led += un_temp改为pun_red_led[i] = ...,每次循环写入数组的下一个元素,避免数据覆盖和无数据问题。
  2. 修正可用样本数计算:去掉原代码中+1的错误,MAX30102的FIFO读写指针差值直接对应可用样本数,WR_PTR等于RD_PTR时FIFO为空。
  3. 打印逻辑匹配实际读取数:打印循环次数改为实际读取的样本数,避免最后一次读取时数组越界。
  4. 提前清除中断标志:先清除中断再读取FIFO指针,确保指针状态不受未处理中断的影响。
  5. 样本初始化:每次读取前初始化当前样本的存储位置为0,避免累加旧数据。

内容的提问来源于stack exchange,提问作者Alexandra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 11:17:36