4、传感器寄存器映射:寄存器地址、读写属性、复位值、功能描述(以MAX30102为例)

好,咱们接着往下聊。上一节我们把MAX30102的硬件接口和I2C时序捋了一遍。这节要进入核心了——寄存器映射。

说白了,寄存器就是传感器内部的控制面板。你往哪个地址写什么值,传感器就干什么活。我刚开始搞驱动时,总觉得寄存器表密密麻麻的,看着就头大。后来发现,其实只要抓住几个关键寄存器,剩下的都是套路。

MAX30102的寄存器地址范围是 0x000xFF,共256个。但实际用到的没那么多,很多是保留位。咱们挑最重要的几个讲。

4.1 中断状态寄存器(0x00 - 0x01)

这两个寄存器,我习惯叫它们「敲门砖」。为什么?因为每次数据准备好了,传感器会拉低INT引脚,你得先读这两个寄存器,才知道发生了什么。

地址 寄存器名 读写属性 复位值 功能描述
0x00 STATUS_1 只读 0x00 中断状态寄存器1。位0:A_FULL(FIFO满中断);位4:PPG_RDY(数据就绪中断);位5:ALC_OVF(环境光消除溢出中断)
0x01 STATUS_2 只读 0x00 中断状态寄存器2。位0:DIE_TEMP_RDY(温度转换完成中断)

注意,这两个寄存器是「读后自动清零」的。嗯,这里有个坑——如果你读的时候刚好来了新中断,可能会丢。我之前的做法是:在中断服务函数里连续读两次,第二次的值才是可靠的。

4.2 中断使能寄存器(0x02 - 0x03)

你想让哪些事件触发中断,就在这里把对应的位置1。

地址 寄存器名 读写属性 复位值 功能描述
0x02 INT_ENABLE_1 读写 0x00 中断使能寄存器1。位0:A_FULL_EN;位4:PPG_RDY_EN;位5:ALC_OVF_EN
0x03 INT_ENABLE_2 读写 0x00 中断使能寄存器2。位0:DIE_TEMP_RDY_EN

我个人习惯,在心率采集场景下,只使能 PPG_RDY_EN。FIFO满中断其实不太需要,因为你可以定时去读。你想想看,如果FIFO满了才去读,那数据间隔就不均匀了,心率算法会算不准的。

4.3 FIFO相关寄存器(0x04 - 0x07)

这部分是数据通道的核心。MAX30102内部有个32字节的FIFO,专门存采样数据。

地址 寄存器名 读写属性 复位值 功能描述
0x04 FIFO_WR_PTR 只读 0x00 FIFO写指针。每次新数据写入后自动加1
0x05 OVF_COUNTER 只读 0x00 FIFO溢出计数器。如果FIFO满了还有新数据,这个值会加1
0x06 FIFO_RD_PTR 只读 0x00 FIFO读指针。读FIFO数据寄存器后自动加1
0x07 FIFO_DATA 只读 0x00 FIFO数据寄存器。连续读这个寄存器,就能拿到所有采样数据

关键点:FIFO_DATA寄存器是唯一能读出原始采样数据的地方。每次读18位(红+红外各18位,共36位,分3个字节存储)。

我曾经踩过一个坑:读FIFO_DATA时,如果只读一次,读指针不会自动更新。你得连续读,直到读指针追上写指针。否则下次读到的还是旧数据。

4.4 配置寄存器(0x08 - 0x0C)

这部分是传感器的「大脑」,决定了采样率、LED电流、分辨率等关键参数。

地址 寄存器名 读写属性 复位值 功能描述
0x08 MODE_CONFIG 读写 0x00 模式配置。位0-2:模式选择(000=待机,001=红光/红外,010=红光,011=多LED,111=温度)
0x09 SPO2_CONFIG 读写 0x00 血氧配置。位0-1:LED脉宽(00=69μs,01=118μs,10=215μs,11=411μs);位2-4:采样率;位5-6:ADC分辨率
0x0A LED1_PA 读写 0x1F 红光LED电流。值范围0-255,对应0mA到50mA
0x0B LED2_PA 读写 0x1F 红外LED电流。同上
0x0C MULTI_LED_CTRL1 读写 0x21 多LED模式控制。每2位控制一个时隙的LED类型

经验之谈:LED电流不是越大越好。我试过把LED1_PA设成0xFF(50mA),结果手环戴了10分钟就发烫。一般心率模式用7mA(0x1F)就够,血氧模式可以适当提高到15mA。

4.5 温度寄存器(0x1F - 0x20)

MAX30102内置了温度传感器,可以用来做温度补偿。

地址 寄存器名 读写属性 复位值 功能描述
0x1F TEMP_INT 只读 0x00 温度整数部分。单位℃
0x20 TEMP_FRAC 只读 0x00 温度小数部分。分辨率0.0625℃

读温度前,需要先往 MODE_CONFIG0x07 启动温度转换。等 DIE_TEMP_RDY 中断来了,再读这两个寄存器。完整温度 = TEMP_INT + TEMP_FRAC * 0.0625。

4.6 复位与ID寄存器(0x0D - 0x0F)

这几个寄存器,我一般在上电初始化时先读一下,确认传感器是不是活着。

地址 寄存器名 读写属性 复位值 功能描述
0x0D RESET 读写 0x00 写0x40触发软件复位。复位后所有寄存器恢复默认值
0x0E REV_ID 只读 0x01 版本修订号
0x0F PART_ID 只读 0x15 器件ID。MAX30102固定为0x15

注意:软件复位后,需要等待至少10ms才能访问其他寄存器。我见过有人复位后立刻去读PART_ID,结果读到0xFF——传感器还没准备好呢。

4.7 实际初始化流程

说了这么多寄存器,咱们串起来看看实际初始化怎么写。这是我常用的初始化序列:

// 1. 软件复位
i2c_write(0x0D, 0x40);
delay_ms(10);

// 2. 检查器件ID
uint8_t id = i2c_read(0x0F);
if (id != 0x15) {
    // 传感器没找到,报错
    return ERROR;
}

// 3. 配置模式:红光+红外,采样率100Hz
i2c_write(0x08, 0x03);      // MODE_CONFIG: 多LED模式
i2c_write(0x09, 0x27);      // SPO2_CONFIG: 411μs脉宽, 100Hz, 18位ADC

// 4. 设置LED电流
i2c_write(0x0A, 0x1F);      // 红光: 7mA
i2c_write(0x0B, 0x1F);      // 红外: 7mA

// 5. 使能数据就绪中断
i2c_write(0x02, 0x10);      // INT_ENABLE_1: 使能PPG_RDY

// 6. 清空中断标志(读一次状态寄存器)
i2c_read(0x00);
i2c_read(0x01);

你看,核心就这几步。剩下的就是等中断来了,去读FIFO_DATA拿数据。

最后说一句,寄存器映射这东西,说白了就是一张「地址-功能」对照表。你不需要背下来,但要知道每个寄存器是干什么的。遇到问题时,能快速定位到是哪个寄存器配错了。

下一节,咱们讲怎么用这些寄存器,真正把心率数据读出来。