4、传感器寄存器映射:寄存器地址、读写属性、复位值、功能描述(以MAX30102为例)
好,咱们接着往下聊。上一节我们把MAX30102的硬件接口和I2C时序捋了一遍。这节要进入核心了——寄存器映射。
说白了,寄存器就是传感器内部的控制面板。你往哪个地址写什么值,传感器就干什么活。我刚开始搞驱动时,总觉得寄存器表密密麻麻的,看着就头大。后来发现,其实只要抓住几个关键寄存器,剩下的都是套路。
MAX30102的寄存器地址范围是 0x00 到 0xFF,共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_CONFIG 写 0x07 启动温度转换。等 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拿数据。
最后说一句,寄存器映射这东西,说白了就是一张「地址-功能」对照表。你不需要背下来,但要知道每个寄存器是干什么的。遇到问题时,能快速定位到是哪个寄存器配错了。
下一节,咱们讲怎么用这些寄存器,真正把心率数据读出来。