[Sensors]BMI270 FIFO的使用
摘要:这篇笔记系统梳理了BMI270传感器的FIFO(先进先出)功能,包括FIFO的工作原理、两种缓存模式、数据帧结构(含报头和无报头)、中断配置、数据同步、溢出处理、低功耗下的使用方法以及数据轴映射等细节,并结合实际寄存器配置和代码示例,帮助用户高效管理和解析BMI270的批量传感器数据,适用于多传感器阵列和对数据同步、低功耗有较高要求的应用场景
文章目录
- 数据帧
- 含报头模式
- 常规帧
- 控制帧
- 无报头模式
- 中断
- 电气中断引脚行为
- 细节
- FIFO 帧读取机制
- FIFO 超读(FIFO overreads)
- 帧率
- FIFO 溢出
- FIFO 数据同步
- FIFO 通过外部中断进行同步
- FIFO 中断
- FIFO 重置
- 低功耗模式下的 FIFO 使用
- FIFO 数据翻转
参考资料:
Bosch Sensortec - BMI270 Datasheet
Bosch Sensortec - BMI270 Max FIFO Application note
Github - Bosch Sensortec - BMI270_SensorAPI
FIFO(First-In, First-Out)即“先进先出”,是一种数据缓存机制,用于按顺序存储和读取数据,在传感器中,FIFO 通常用来临时保存采集到的数据,避免因主控芯片读取不及时而导致数据丢失
BMI270 的 FIFO 作用在于:它可以自动缓存加速度计、陀螺仪等传感器采集的数据,并支持批量读取,减少主控 MCU 读取数据的频率,有效降低功耗,同时防止数据丢失,提高系统的整体数据采集效率,通过 FIFO,用户还可以实现更灵活的数据管理和中断控制
BMI270 的 FIFO 支持两种模式
- 流式传输模式:当 FIFO 满时,覆写旧数据
- FIFO 模式:当 FIFO 满时,丢弃新的数据
FIFO 大小为 2048Bytes(2kB),支持以下中断方式:
- FIFO 满时触发中断
- FIFO 水位线中断
启动 FIFO
- 对于加速度数据(accelerometer),配置
FIFO_CONFIG_1.fifo_acc_en = 0b1
- 对于角速度(陀螺仪,gyroscope)数据,配置
FIFO_CONFIG_1.fifo_gyr_en = 0b1
- 对于辅助接口(如连接磁力计),配置
FIFO_CONFIG_1.fifo_aux_en = 0b1
#define BMI270_FIFO_FRAME_CONTENT_CFG \(BMI2_FIFO_HEADER_EN | BMI2_FIFO_ACC_EN | BMI2_FIFO_GYR_EN) // FIFO frame content configuration// 启用帧头、ACC、GYR数据rslt = bmi2_set_fifo_config(BMI270_FIFO_FRAME_CONTENT_CFG, BMI2_ENABLE, &bmi2_devices[sensor_id]);
if (rslt != BMI2_OK)
{ESP_LOGE(TAG, "Failed to enable FIFO for sensor %d: %d", sensor_id, rslt);return rslt;
}// 获取 FIFO 配置传感器中的数据来检查配置是否生效
rslt = bmi2_get_fifo_config(&fifo_config, &bmi2_devices[sensor_id]);
if (rslt == BMI2_OK)
{ESP_LOGI(TAG, "FIFO settings for sensor %d: 0x%04X", sensor_id, fifo_config);
}
else
{ESP_LOGE(TAG, "Failed to get FIFO settings for sensor %d: %d,config: 0x%04X", sensor_id, rslt, fifo_config);return rslt;
}
宏定义见 bmi2_defs.h
FIFO 可在设备的所有电源模式下用于记录数据
Auxiliary Interface(辅助接口) 允许 BMI270 作为主机,去主动读取其他外部传感器的数据,最常用的就是外部磁力计(magnetometer)
这个接口(ASDX 和 ASCX)实际是一个 I2C 主机端口,BMI270 作为主设备,外部传感器(比如磁力计)作为从设备
数据帧
FIFO 以帧的形式捕获数据,在含报头模式下每帧由报头与有效载荷数据组成,在无报头模式下仅存储有效载荷
在含报头模式(标准模式下),每个常规帧包含 1Byte 的报头用于描述帧特性(比如这一数据帧包含了哪些传感器的数据),以及有效数据本身,还存在包含原始数据(如传感器时间)的控制帧
BMI 270 读取数据所需的 dummy 数量
- I2C:0
- SPI:1
使用 Bosch Sensortec 提供的 API,在使用 SPI 通信时,接收数据开头部分 1Byte 的 Dummy 会被作为数据写入接受数组中
FIFO Frame Format FIFO 数据帧格式
Configuration 配置||-- Headerless mode (only data) 无帧头模式| || `-- Regular frames 常规帧| (same ODR for different sensor) 对不同的 Sensor 有相同的ODR|`-- Header mode (header+data) 带帧头模式||-- Regular frames 常规帧| (different ODR for different sensor)| 对不同的Sensor(配置不同)有不同的 ODR|`-- Control frames 控制帧||-- Skip frame 跳帧|-- Sensortime frame 传感器时间帧`-- FIFO Input Config frame FIFO 配置帧
ODR (输出数据率,Output Data Rate)表示每秒钟传感器输出数据的次数,单位通常是 Hz(赫兹)
Headless mode(无报头模式):不同的传感器输出数据的频率(ODR)是一样的
Header mode(含报头模式):不同的传感器可以有不同的数据输出速率(ODR),ODR 在 FIFO 帧格式配置中影响不同传感器数据帧的组织方式和速率
含报头模式
报头结构
位数 | 内容 | 描述 |
---|---|---|
7~6 | fh_mode<1:0> | 描述当前帧的类型:0b10 常规帧0b01 控制帧0b00/0b11 保留 |
5~2 | fh_parm<3:0> | 常规帧:描述当前帧包含了哪些传感器数据, 控制帧:操作码 |
1~0 | fh_ext<1:0> | 用于外部标记(INT1/INT2) |
常规帧
一个有效的常规帧包含至少一种传感器数据,只有有效的常规帧才会被写入 FIFO
0b1
表示包含相应数据
fh_parm<3>
保留
fh_parm<2>
FIFO_aux_data 辅助接口数据
fh_parm<1>
FIFO_gyr_data 陀螺仪数据
fh_parm<0>
FIFO_acc_data 加速度计数据
寄存器 DATA_0~19 的定义是静态的,FIFO 输出顺序是动态的,取决于使能的通道和配置,因此两者顺序不一定一致
DATA_0~19 寄存器的编号和数据分配是固定的(只看最新一次采样)
FIFO 输出的数据顺序和内容是由你当前使能的通道和配置决定的,是动态变化的
例:报头中的 fh_parm=0b0111
参数将产生如下所示的数据布局
DATA[X] | 缩写 | 说明 |
---|---|---|
X=0 | AUX_0 | 辅助传感器寄存器映射中寄存器值(AUX_RD_ADDR)的副本 |
X=1 | AUX_1 | 辅助传感器寄存器映射中寄存器值(AUX_RD_ADDR)+1 的副本 |
X=2 | AUX_2 | 辅助传感器寄存器映射中寄存器值(AUX_RD_ADDR)+2 的副本 |
X=3 | AUX_3 | 辅助传感器寄存器映射中寄存器值(AUX_RD_ADDR)+3 的副本 |
X=4 | AUX_4 | 辅助传感器寄存器映射中寄存器值(AUX_RD_ADDR)+4 的副本 |
X=5 | AUX_5 | 辅助传感器寄存器映射中寄存器值(AUX_RD_ADDR)+5 的副本 |
X=6 | AUX_6 | 辅助传感器寄存器映射中寄存器值(AUX_RD_ADDR)+6 的副本 |
X=7 | AUX_7 | 辅助传感器寄存器映射中寄存器值(AUX_RD_ADDR)+7 的副本 |
X=8 | GYR_X<7:0> (LSB) | |
X=9 | GYR_X<15:8> (MSB) | |
X=10 | GYR_Y<7:0> (LSB) | |
X=11 | GYR_Y<15:8> (MSB) | |
X=12 | GYR_Z<7:0> (LSB) | |
X=13 | GYR_Z<15:8> (MSB) | |
X=14 | ACC_X<7:0> (LSB) | |
X=15 | ACC_X<15:8> (MSB) | |
X=16 | ACC_Y<7:0> (LSB) | |
X=17 | ACC_Y<15:8> (MSB) | |
X=18 | ACC_Z<7:0> (LSB) | |
X=19 | ACC_Z<15:8> (MSB) |
这里需要注意接受的数据是 辅助传感器 -> 角速度 -> 加速度
FIFO 帧中辅助传感器(auxiliary sensor)数据块的长度,取决于寄存器 AUX_IF_CONF.aux_rd_burst
中配置的辅助接口突发读取长度(burst read length),少于 8 字节时,后续数据(如陀螺仪、加速度计)会紧跟在辅助数据后面,中间不会留空,这样 FIFO 帧的数据排列顺序会因为实际辅助数据长度不同而发生变化(在 FIFO 帧中,辅助传感器(auxiliary sensor)数据区域最多可以有 8 个字节)
比如上面的表就是配置了 8 Bytes 的情况
突发长度(Burst Length)在传感器接口(比如 I2C/SPI)领域,通常指一次连续的数据读取或写入操作的数据长度
控制帧
控制帧只能在含报头模式下使用,通过 fh_parm
字段定义了若干控制帧
fh_mode(fh_parm) 3:0 | 定义 | 有效载荷 | 含义 |
---|---|---|---|
0x0 | 跳帧 Skip Frame | 1Byte | 表示 FIFO 溢出发生后跳过的帧数 |
0x1 | 传感器时间帧 Sensortime Frame | 3Bytes | 包含从 FIFO 中读取最后采样帧时的传感器时间 |
0x2 | FIFO 输入配置帧 Fifo_Input_Config Frame | 4Bytes | 表示影响传感器数据的传感器配置变更 |
0x3 | 保留 |
FIFO 填充水平包含在寄存器 FIFO_LENGTH_1.fifo_byte_counter_13_8
和 FIFO_LENGTH_0.fifo_byte_counter_7_0
中,该 fifo 填充水平包括常规帧和控制帧所需的空间(传感器时间帧除外)
Skip Frame 跳帧
当 FIFO 溢出时,一个跳帧会被添加到 FIFO 内容前,下次读取时,该帧的数据由一个字节组成,包含跳过的帧数,当跳过的帧数超过 0xFF
时,将返回 0xFF
;跳帧始终预期作为 FIFO 突发读取的首帧出现,跳帧不会占用 FIFO 中的内存空间
Sensortime Frame 传感器时间帧
传感器时间帧的数据是在读取最后一个采样帧的最后一个字节时,对寄存器 SENSORTIME_0
至 SENSORTIME_2
内容的复制
一个传感器时间帧总是作为 FIFO 中的最后一帧出现,只有当 FIFO 在突发读取过程中变空时,才会发送传感器时间帧
传感器时间帧不会占用 FIFO 的内存空间,通过将 FIFO_CONFIG_0.fifo_time_en
设置为 0b1(0b0)
可以启用(禁用)传感器时间帧功能
Fifo_Input_Config Frame FIFO 输入配置帧
每当 FIFO 输入数据源的过滤器配置发生变更时,在配置生效前会向 FIFO 中插入一个配置帧
传感器的数据在进入 FIFO 之前,会经过一层“滤波器(filter)”,比如低通滤波器,可以在寄存器里设置它的参数,比如带宽(bandwidth)、输出速率(ODR)等
在读取和解析 FIFO 数据时,可以准确知道从哪一帧开始,数据是用新的过滤器参数采集的,从而保证数据解释的准确性
如果 Byte 0(即该帧的第一个字节)为 0x00,表示这个 Fifo_Input_Config 帧是因为 FIFO 或传感器被使能(启用)而写入的
例如当加速度计滤过板的带宽参数在 ACC_CONF
寄存器中被修改时,系统会在首个采用新带宽配置的加速度计数据帧之前插入配置帧,该 FIFO 输入配置帧包含四个字节数据,其格式如下:
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Byte 0 | reserved | reserved | aux_if_ch | aux_conf_ch | gyr_range_ch | gyr_conf_ch | acc_range_ch | acc_conf_ch |
Byte 1 | Sensortime_0 for next frame (may be drop frame) | 如果出现丢帧情况,这里存放的是下一个有效数据帧的时间戳 | 可用于推断丢失了哪几帧 | |||||
Byte 2 | Sensortime_1 for next frame (may be drop frame) | |||||||
Byte 3 | Sensortime_2 for next frame (may be drop frame) |
名称 | 说明 | |
---|---|---|
aux_if_ch | 对寄存器 AUX_IF_CONF、AUX_RD_ADDR 或 AUX_WR_ADDR 的写操作生效 | |
aux_conf_ch | 对寄存器 AUX_CONF 的写操作生效 | |
gyr_range_ch | 对寄存器 GYR_RANGE 的写操作生效 | |
gyr_conf_ch | 对寄存器 GYR_CONF,或 gyr_FIFO_filt_data,或 FIFO_DOWNS 中 gyr_FIFO_downsampling 的写操作生效 | |
acc_range_ch | 对寄存器 ACC_RANGE 的写操作生效 | |
acc_conf_ch | 对寄存器 ACC_CONF,或 acc_FIFO_filt_data,或 FIFO_DOWNS 中 acc_FIFO_downsampling 的写操作生效 | |
如果是将 FIFO 中的数据取出后手动进行判断,在较高的读取频率下,可以将第一次从 FIFO 中读取的数据直接丢弃来简化程序设计
无报头模式
当所有启用传感器元件的数据结构相同时,或者不需要中断标记之类的功能时,可通过 FIFO_CONFIG_1.fifo_header_en
禁用 FIFO 报头功能
无报头模式仅支持常规帧,为了区分不同的数据帧,所有数据帧的大小必须相同,因此任何影响帧大小或帧内数据顺序的配置变更,都将立即清空 FIFO 缓冲区,并以新设置重新开始数据采集
若启用辅助传感器接口,FIFO 帧中的辅助传感器字节数始终为 AUX_IF_CONF.aux_rd_burst
字节,若突发长度小于 8 字节,设备将对从辅助传感器读取的值进行填充,例如,当 AUX_IF_CONF.aux_rd_burst=0b01
(2 字节)时,包含辅助传感器、加速度计和陀螺仪数据的帧结构如下所示:
DATA[X] | 缩写 | 说明 |
---|---|---|
X=0 | AUX_0 | 辅助传感器寄存器映射中寄存器值(AUX_RD_ADDR.read_addr)的副本 |
X=1 | AUX_1 | 辅助传感器寄存器映射中寄存器值(AUX_RD_ADDR.read_addr+1)的副本 |
X=2 | 填充字节 | 未定义的值 |
X=3 | 填充字节 | 未定义的值 |
X=4 | 填充字节 | 未定义的值 |
X=5 | 填充字节 | 未定义的值 |
X=6 | 填充字节 | 未定义的值 |
X=7 | 填充字节 | 未定义的值 |
X=8 | GYR_X<7:0> (LSB) | |
X=9 | GYR_X<15:8> (MSB) | |
X=10 | GYR_Y<7:0> (LSB) | |
X=11 | GYR_Y<15:8> (MSB) | |
X=12 | GYR_Z<7:0> (LSB) | |
X=13 | GYR_Z<15:8> (MSB) | |
X=14 | ACC_X<7:0> (LSB) | |
X=15 | ACC_X<15:8> (MSB) | |
X=16 | ACC_Y<7:0> (LSB) | |
X=17 | ACC_Y<15:8> (MSB) | |
X=18 | ACC_Z<7:0> (LSB) | |
X=19 | ACC_Z<15:8> (MSB) |
中断
电气中断引脚行为
触发中断
两个中断引脚 PIN1
和 PIN2
都可以配置为所需的电气行为
- 中断引脚可分别通过
INT1_IO_CTRL.output_en
和INT2_IO_CTRL.output_en
使能 - 中断引脚输出驱动器的特性可通过
INT1_IO_CTRL.od
和INT2_IO_CTRL.od
位进行配置 - 将这些位设置为
0b1
时,输出驱动器表现为开漏特性;将配置位设置为0b0
时,输出驱动器表现为推挽特性
开漏是一种输出驱动方式,指的是输出端只能拉低(连接到地),不能主动输出高电平
推挽输出是一种能够主动输出高电平和低电平的驱动方式。即输出端可以直接连接电源或地,实现对负载的双向驱动,推挽输出响应速度快、带载能力强
中断模式
该器件支持非锁存和锁存中断模式,适用于数据就绪、FIFO 水位、FIFO 满、错误和高级功能中断
- 模式通过
INT_LATCH.int_latch
选择,非锁存中断适用于使用边沿触发中断的系统,锁存中断适用于使用电平触发中断的系统 - 在锁存模式下,当读取相应的状态寄存器时,
INT_STATUS_0
(高级功能中断)或INT_STATUS_1
(数据就绪、FIFO 和错误中断)中的被置位中断状态和所选引脚被复位 - 如果中断激活条件在中断复位后仍然成立,中断状态和引脚将再次被置位,如果在锁存模式下使用多个中断引脚,则应将
INT_STATUS_0
中的所有中断映射到一个中断引脚,将INT_STATUS_1
中的所有中断映射到另一个中断引脚。如果只使用一个中断引脚,则所有中断都可以映射到该引脚
在非锁存模式下,只要激活条件不再有效,所选引脚即被复位,中断状态位在主机读取之前一直有效
中断引脚映射
数据就绪、FIFO 水位、FIFO 满、错误和高级功能中断可通过设置寄存器 INT_MAP_DATA
、INT1_MAP_FEAT
和 INT2_MAP_FEAT
中的相应位,映射到外部 INT1 或 INT2 引脚,要取消这些中断的映射,必须复位相应位
一旦中断触发了输出引脚,主机可以通过寄存器 INT_STATUS_0
和 INT_STATUS_1
中的相应状态位获知中断源
芯片有多个中断源(如数据就绪、FIFO 水位、错误等),它们的状态分别记录在不同的寄存器(如 INT_STATUS_0 和 INT_STATUS_1)中
外部只引出两个中断引脚(PIN1 和 PIN2),但中断源远多于 2 个
通过寄存器配置,可以把不同的中断源“映射”(分配)到某一个或某几个引脚上
外部中断
需要外部中断时
- 中断引脚的电气行为可以通过
INT1_IO_CTRL.lvl
和INT2_IO_CTRL.lvl
分别配置为“高电平有效”或“低电平有效” - 两个中断引脚均可通过
INT1_IO_CTRL.input_en
和INT2_IO_CTRL.input_en
配置为输入引脚,当使用 FIFO 标签功能时,必须要启用这个设置 - 如果引脚既为输入又为输出,则“输入端”收到的信号实际上就是“输出端”自己驱动的中断信号,这种配置方式常用于同步外部事件和内部 FIFO 状态
BMI270 支持边沿触发和电平触发的中断输入,通过 FIFO_CONFIG1.fifo_tag_int1_en
和 FIFO_CONFIG1.fifo_tag_int2_en
进行配置;对于边沿触发,只支持上升沿触发
细节
FIFO 帧读取机制
- 若某帧数据通过寄存器 FIFO_DATA 被完整读取,则该帧会从 FIFO 中删除
- 若仅部分读取某帧数据,则在任何模式下,下次访问时将完整重复该帧数据;在含帧头模式下,重复内容包含帧头部分
- 若首次部分读取与第二次读取尝试之间发生先进先出队列溢出,只有当
FIFO_CONFIG_0.fifo_stop_on_full
设置为0b1
时,该帧数据才会被保留
FIFO 超读(FIFO overreads)
当从先进先出队列中读取的数据量超过其有效数据容量时,在无帧头模式下会返回 0x8000
值;在帧头模式下,用 0x80
值表示无效帧
帧率
FIFO 的帧采样率由启用 FIFO 采样的传感器最大输出数据速率决定
FIFO 采样配置通过寄存器 FIFO_CONFIG_0
、 FIFO_CONFIG_1
进行设置(数据是否覆盖、是否发送 Sensor 时间帧),并可以通过 FIFO_DOWNS
寄存器选择滤波或未滤波数据作为 FIFO 输入源
- 如果在寄存器
FIFO_DOWNS.acc_fifo_filt_data
中选择加速度计的未滤波数据,采样率最大为1600Hz
- 如果在寄存器
FIFO_DOWNS.gyr_fifo_filt_data
中选择陀螺仪的未滤波数据,采样率(OIS Mode)最大为6400Hz
(普通模式下最大为 3200) - 对于相同的ODR配置,滤波只改变数据质量(噪声水平和延迟),采样率保持不变
OIS 模式下,未滤波陀螺仪数据的量程由GYR_RANGE.ois_range
定义,该量程独立于由GYR_RANGE.gyr_range
配置的普通寄存器量程(数据寄存器配置量程及 FIFO 中滤波数据量程)
通过寄存器FIFO_DOWNS.acc_fifo_downs
或FIFO_DOWNS.gyr_fifo_downs
选择2^k
下采样因子(k 取值 0 至 7),可降低 FIFO 的输入数据速率 - 此时输入速率 = 原始速率 / 2^k
在惯性传感器(如加速度计、陀螺仪)中,量程通常用来表示该传感器能感知的最大加速度或最大角速度
对加速度计来说,常见的量程有 ±2g、±4g、±8g、±16g(g 为重力加速度,约 9.8m/s²)
例:量程为±2g,表示能测量从-2g 到+2g 之间的加速度;超过这个范围,数值会“饱和”或失真
对陀螺仪来说,常见的量程有 ±250°/s、±500°/s、±1000°/s、±2000°/s。
举例:量程为±250°/s,表示能测量-250 到+250 度每秒的角速度
量程越大,能测量的范围越宽,但单个单位的分辨率会降低(即灵敏度下降)
量程越小,灵敏度越高,但容易饱和(超出范围无法准确测量)
OIS Mode(光学图像稳定模式)是BMI270专为相机防抖应用设计的特殊工作模式,通过独立的次级SPI接口(仅支持SPI)提供超低延迟的传感器数据,其核心价值在于为手机等设备提供实时运动补偿能力,通过次级接口将高频运动数据直接传输给OIS控制单元,实现照片和视频拍摄过程中的镜头实时稳定,显著提升成像质量
常规模式下的传感器 ODR 配置
传感器类型 | ODR值(十六进制) | ODR值(Hz) | 传感器类型 | ODR值(十六进制) | ODR值(Hz) |
---|---|---|---|---|---|
加速度计 | 0x01 | 0.78 Hz | 陀螺仪 | 0x06 | 25 Hz |
加速度计 | 0x02 | 1.56 Hz | 陀螺仪 | 0x07 | 50 Hz |
加速度计 | 0x03 | 3.12 Hz | 陀螺仪 | 0x08 | 100 Hz |
加速度计 | 0x04 | 6.25 Hz | 陀螺仪 | 0x09 | 200 Hz |
加速度计 | 0x05 | 12.5 Hz | 陀螺仪 | 0x0A | 400 Hz |
加速度计 | 0x06 | 25 Hz | 陀螺仪 | 0x0B | 800 Hz |
加速度计 | 0x07 | 50 Hz | 陀螺仪 | 0x0C | 1600 Hz |
加速度计 | 0x08 | 100 Hz | 陀螺仪 | 0x0D | 3200 Hz |
加速度计 | 0x09 | 200 Hz | |||
加速度计 | 0x0A | 400 Hz | |||
加速度计 | 0x0B | 800 Hz | |||
加速度计 | 0x0C | 1600 Hz |
FIFO 溢出
当 FIFO 溢出时,有停止记录数据或覆盖最旧数据两种处理方式,由寄存器 FIFO_CONFIG_0.fifo_stop_on_full
控制
FIFO_CONFIG_0.fifo_stop_on_full = 0b0
当剩余队列空间不足最大帧尺寸时,FIFO 逻辑将删除最旧的帧数据;如果启用含报头模式,系统会在下次 FIFO 读取时预置跳过帧FIFO_CONFIG_0.fifo_stop_on_full = 0b1
当剩余队列空间不足最大帧尺寸时,最新帧被丢弃;如果启用含报头模式,系统会将跳帧插入这一次读取 FIFO 的数据的最前面
模式 | FIFO_CONFIG_0.fifo_stop_on_full | 数据丢失方式 | 跳帧插入时机 | 跳帧插入位置 | 备注 |
---|---|---|---|---|---|
覆盖旧数据模式 | 0b0(清零) | 覆盖最老的数据 | 下次 FIFO 读取时 | 读取到的数据最前面 | FIFO 继续写入,不断覆盖最旧帧 |
停止写入新数据模式 | 0b1(置位) | 丢弃最新的数据 | 下次 FIFO 读取时 | 读取到的数据最前面 | FIFO 不再写入新帧,丢弃新数据 |
当主机在读取 FIFO 数据时,FIFO 尾部的数据不会被丢弃。
如果主机读取 FIFO 的速度比 FIFO 写入(填充)的速度慢,那么可能会出现这样一种情况:
即使 FIFO_CONFIG_0.fifo_stop_on_full = 0b0
(即 FIFO 满时允许覆盖最旧数据),当 FIFO 已满且有新数据到来时,传感器还是需要丢弃新数据,因为主机还没有及时把旧数据读出来
这种数据丢失事件会被记录到寄存器 ERR_REG.fifo_err
中,主机可以通过检查该寄存器,发现是否有 FIFO 溢出或数据丢失的情况发生
FIFO 数据同步
所有传感器数据的采样都基于一个统一的 ODR 时间网格(ODR time grid),即使为加速度计和辅助传感器选择了不同的 ODR(输出数据率),数据依然保持同步
也就是说,无论各个传感器(比如加速度计和辅助传感器)的 ODR 是否不同,数据采样都是在同一个时间基准下进行的,确保数据同步
如果某一帧中包含了 ODR 为 x
的传感器元件的采样数据,那么该帧也必须包含所有 ODR 为 y
且 y ≥ x
的传感器元件的采样数据
即 ODR 高的传感器会比 ODR 低的传感器采样更频繁,但只要有 ODR 低的采样点时,ODR 高的那几个采样点也一定会被同时打包进同一帧
这一规则适用于稳定工作状态,在过渡阶段(如传感器配置发生变化时),更重要的是不丢失数据
如果 ODR 为 y
且 y ≥ x
的传感器元件没有数据,也可能会出现例外情况,在极个别情况下,如果 ODR 高的传感器数据没有准备好(比如配置刚切换,数据还没新产生),这帧里可能会缺失那些数据
ACC 使能 ACC 启动时间
|--------->|
ACC: □ □ □ □ □ □
AUX: □ □ □ □ □AUX 使能 AUX 启动时间
|--------->|
ACC: □ □ □ □ □
AUX: □ □ □ □ □ □过渡阶段允许帧内容不一致
──────────────────────────────────────────────1/ODR_ACC|<->|
ACC: □ □ □ □ □ □
AUX: □ □ □|<----->|1/ODR_AUX稳定运行阶段,此时 ODR_ACC > ODR_AUX
每当AUX采样时,ACC一定也采样并输出到了同一帧
FIFO 通过外部中断进行同步
外部中断可以被同步到 FIFO 数据中,对于这种操作模式,需要启用 FIFO_CONFIG_1.fifo_tag_int1_en
和 FIFO_CONFIG_1.fifo_tag_int2_en
,分别控制是否将 INT1、INT2 的外部中断状态同步到 FIFO 数据;同时也要启用 INT1_IO_CTRL.input_en
和 INT2_IO_CTRL.input_en
,控制 INT1 和 INT2 是否作为中断输入
配置后 FIFO 头部中的 fh_ext
字段将根据 INT1/INT2
输入端的信号进行设置,外部信号的最小有效电平为 10 纳秒
#define BMI270_FIFO_FRAME_CONTENT_CFG \(BMI2_FIFO_HEADER_EN | BMI2_FIFO_ACC_EN | BMI2_FIFO_GYR_EN) // FIFO frame content configuration
// 启用帧头、启用ACC、GYR,中断配置使用缺省值(0x00,外部中断设置为上升沿,推挽输出)
使能上述配置后,每次采样/推送进 FIFO 的数据帧,其头部 fh_ext
字段会反映 INT1/INT2 输入引脚的状态
在读取 FIFO 数据时,可以知道采样那个时刻,外部中断信号的高低电平情况,实现数据和外部事件的精准对齐(比如外部触发信号、同步信号等)
对于 FIFO 中的数据,当 Sensor 接收到外部触发信号时,会将这个信号标记存入下一帧数据的报头中
对于从 FIFO 中读取的数据,官方的 API 并没有提供函数对存在标记的数据帧进行判断,因此我们可以自行编写程序进行判断
for (data_index = 1; data_index < bmi2_fifo_length;) {frame_header = fifo_data[data_index] & (BMI270_FIFO_INT_MASK); // Only process frames with valid headerdata_index += 1; // Move to the next byte after headerif (frame_header == 0 || (data_index + BMI2_FIFO_ACC_GYR_LENGTH) > bmi2_fifo_length) {if (data_index + BMI2_FIFO_ACC_GYR_LENGTH > bmi2_fifo_length) {data_index = bmi2_fifo_length;} else {data_index += BMI2_FIFO_ACC_GYR_LENGTH;}} else {if (data_index + BMI2_FIFO_ACC_GYR_LENGTH > bmi2_fifo_length) {data_index = bmi2_fifo_length;} else {// Copy the frame data to the sync data buffermemcpy(&fifo_sync_data[fifo_sync_data_len], &fifo_data[data_index - 1], BMI2_FIFO_ACC_GYR_LENGTH + 1);fifo_sync_data_len += BMI2_FIFO_ACC_GYR_LENGTH + 1;data_index += BMI2_FIFO_ACC_GYR_LENGTH;}}}
FIFO 中断
FIFO 支持两种中断:
- FIFO 满中断:“满阈值”是在最后两帧存入 FIFO 之前达到的(即还剩下最后两帧空间时就会发中断,防止完全溢出)
- FIFO 水位线中断:当FIFO填充水平达到或超过由寄存器
FIFO_WTM_1.fifo_water_mark_12_8
定义的水位线时触发
要启用这两个中断,需要通过 INT_MAP_DATA
寄存器将它们映射到想要的中断引脚(比如 INT1 或 INT2)
锁存的 FIFO 中断仅当状态寄存器被读取且填充水平低于相应 FIFO 中断(满或水位线)阈值时才会清除
锁存(latched)中断意味着一旦中断发生,信号会一直保持(“锁存”)在中断状态,直到被显式地清除,不会因为条件消失(比如 FIFO 不满了)就自动消失
读取状态寄存器通常是指需要在主机端访问传感器的某个状态寄存器,比如读一下 status register,这相当于“手动确认”已经知道有中断发生
即此时需要先读取数据在读取状态寄存器才能清楚中断状态
FIFO 重置
用户可以通过在CMD寄存器中写入 fifo_flush(0xB0)
命令来手动触发 FIFO 复位
自动复位仅在以下几种情况下发生:
- 在无报头模式(headerless mode)下使能或禁用某个传感器时
- 在无报头模式和有头模式(header mode)之间发生切换时
- 在有报头模式或无报头模式下,辅助传感器数据在一帧中的大小发生变化时
低功耗模式下的 FIFO 使用
在低功耗模式下,器件同样支持 FIFO 的使用,数据写入 FIFO 的方式与普通模式和高性能模式是一样的,但读取时有如下说明:
- 如果
PWR_CONF.fifo_self_wakeup=0b0
,则在读取 FIFO 数据前需要关闭高级省电配置(即PWR_CONF.adv_power_save=0b0
) - 如果
PWR_CONF.fifo_self_wakeup=0b1
,并且触发了 FIFO 水位线中断或 FIFO 满中断,那么只要在一次突发读取(burst read)操作中读取完 FIFO(即在一次FIFO_DATA
寄存器的突发读取过程中),关于PWR_CONF.adv_power_save=0b1
的限制就不再适用,这意味着可以在不退出低功耗模式的情况下,一次性读取全部 FIFO 数据 - 如果没有触发 FIFO 水位线中断或满中断,那么仍然需要关闭高级省电配置(
PWR_CONF.adv_power_save=0b0
)之后才能读取 FIFO 数据
即有中断时可以一次性突发读取全部 FIFO,不需要关闭省电模式;否则必须先关掉 adv_power_save
再读
FIFO 数据翻转
对于 IMU 阵列,在进行 sensor 数据融合是,如果多个传感器的 xyz 方向不同,则需要进行轴方向转换
对于直接读取模式,博世官方提供的 API 中具有传感器数据映射功能(如将初始的 X 轴映射为 Y 轴,并进行翻转等操作)
int8_t bmi2_get_remap_axes(struct bmi2_remap *remapped_axis, struct bmi2_dev *dev); // 获取当前 XYZ 配置
int8_t bmi2_set_remap_axes(const struct bmi2_remap *remapped_axis, struct bmi2_dev *dev); // 设置 dev 的轴映射配置
同时,BMI270 也提供了相应的宏定义 BMI2_X、BMI2_Y、BMI2_Z 便于配置
但是对于 FIFO 中的数据而言,由于上述配置发生在数据处理环节,因此对 FIFO 数据不生效
FIFO 中存储的数据仍然是传感器的原始物理轴数据,硬件不会在 FIFO 级别应用轴重映射,因此可以通过应用进行判断
定义一个配置项结构体,方便操作
typedef struct sensor_axis_remap {uint8_t x;bool x_flip;uint8_t y;bool y_flip;uint8_t z;bool z_flip;
} sensor_axis_remap_t;
对矩阵中的每一个 IMUJ 进行配置
sensor_axis_remap_t bmi270_remap_cfg[BMI270_SENSOR_COUNT] = {{BMI2_X, false, BMI2_Y, false, BMI2_Z, false}, // Sensor 0{BMI2_X, true, BMI2_Y, true, BMI2_Z, false}, // Sensor 1{BMI2_Y, true, BMI2_X, false, BMI2_Z, false}, // Sensor 2{BMI2_Y, true, BMI2_X, false, BMI2_Z, false}, // Sensor 3{BMI2_X, true, BMI2_Y, false, BMI2_Z, true}, // Sensor 4{BMI2_X, true, BMI2_Y, false, BMI2_Z, true}, // Sensor 5{BMI2_Y, true, BMI2_X, true, BMI2_Z, true}, // Sensor 6{BMI2_Y, false, BMI2_X, false, BMI2_Z, true}, // Sensor 7{BMI2_X, false, BMI2_Y, false, BMI2_Z, false}, // Sensor 8{BMI2_Y, false, BMI2_X, false, BMI2_Z, true}, // Sensor 9{BMI2_X, true, BMI2_Y, false, BMI2_Z, true}, // Sensor 10{BMI2_Y, true, BMI2_X, false, BMI2_Z, false}, // Sensor 11{BMI2_Y, true, BMI2_X, true, BMI2_Z, true}, // Sensor 12{BMI2_X, true, BMI2_Y, false, BMI2_Z, true}, // Sensor 13{BMI2_X, false, BMI2_Y, false, BMI2_Z, false}, // Sensor 14{BMI2_Y, true, BMI2_X, false, BMI2_Z, false}, // Sensor 15
};
自行编写程序进行判断
这里是对 X、Y 轴交换的数据进行判断,并判断是否翻转
if (bmi270_remap_cfg[sensor_id].x != BMI2_X) {temporary_data = sensor_data.acc_data.x;sensor_data.acc_data.x = sensor_data.acc_data.y;sensor_data.acc_data.y = temporary_data;temporary_data = sensor_data.gyr_data.x;sensor_data.gyr_data.x = sensor_data.gyr_data.y;sensor_data.gyr_data.y = temporary_data;}if (bmi270_remap_cfg[sensor_id].x_flip) {sensor_data.acc_data.x = -sensor_data.acc_data.x;sensor_data.gyr_data.x = -sensor_data.gyr_data.x;}if (bmi270_remap_cfg[sensor_id].y_flip) {sensor_data.acc_data.y = -sensor_data.acc_data.y;sensor_data.gyr_data.y = -sensor_data.gyr_data.y;}if (bmi270_remap_cfg[sensor_id].z_flip) {sensor_data.acc_data.z = -sensor_data.acc_data.z;sensor_data.gyr_data.z = -sensor_data.gyr_data.z;}