1. BMI088传感器概述与项目背景
BMI088是博世(Bosch)推出的一款高性能IMU(惯性测量单元)传感器,集成了3轴高精度加速度计、3轴高精度陀螺仪和温度传感器。这款传感器在机器人、无人机和工业控制等领域有广泛应用,主要特点包括:
- 加速度计量程可配置:±3g/±6g/±12g/±24g
- 陀螺仪量程可配置:±125dps/±250dps/±500dps/±1000dps/±2000dps
- 支持SPI(最高10MHz)和I2C(最高400kHz)接口
- 内置温度传感器精度±1°C
- 加速度计和陀螺仪都有独立的数据就绪中断
在Linux 6.1内核中已经集成了BMI088的驱动,但原驱动只实现了最基本的直接读取功能,没有充分利用硬件FIFO和中断特性。这会导致以下问题:
- 效率低下:每次读取都需要发起完整的SPI传输
- 延迟不可控:轮询方式无法保证实时性
- CPU占用高:频繁的SPI通信会增加系统负载
针对这些问题,我们对原驱动进行了深度优化,主要改进包括:
- 实现硬件FIFO支持,支持批量数据读取
- 添加中断驱动模式,降低延迟和CPU占用
- 完善电源管理,支持运行时PM
- 提供更灵活的用户空间接口
2. 传感器规格书关键点解析
2.1 硬件初始化注意事项
2.1.1 接口模式切换机制
BMI088加速度计有一个特殊的上电行为:默认处于I2C模式,只有在检测到CS(片选)信号的上升沿后才会切换到SPI模式。这意味着在SPI驱动中,我们需要在probe函数中显式地发送一个空操作来触发模式切换:
c复制static int bmi088_accel_chip_init(struct bmi088_accel_data *data, enum bmi_device_type type)
{
struct spi_device *spi = to_spi_device(regmap_get_device(data->regmap));
// 发送空SPI传输以触发I2C到SPI切换
spi_write(spi, NULL, 0);
msleep(1); // 等待模式切换完成
// 后续的常规初始化...
}
注意:这个空传输必须在其他任何寄存器访问之前完成,否则会导致通信失败。
2.1.2 电源管理与启动时序
BMI088的加速度计有严格的电源管理时序要求:
- 上电复位(POR)或软复位后:需要至少1ms的启动时间
- 电源模式切换后:需要5ms的稳定时间
- 低功耗模式唤醒:需要额外的稳定时间
在驱动中,我们需要严格遵守这些时序要求:
c复制#define BMI088_ACCEL_POWER_ON_DELAY_MS 5
#define BMI088_ACCEL_RESET_DELAY_MS 1
static int bmi088_accel_power_up(struct bmi088_accel_data *data)
{
int ret;
ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x00);
if (ret)
return ret;
// 等待电源稳定
msleep(BMI088_ACCEL_POWER_ON_DELAY_MS);
ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x04);
if (ret)
return ret;
return 0;
}
2.2 关键寄存器解析
2.2.1 数据寄存器布局
BMI088的加速度计数据寄存器采用分体式设计,每个轴的测量值被拆分为两个寄存器(LSB和MSB):
- X轴数据:0x12(LSB)和0x13(MSB)
- Y轴数据:0x14(LSB)和0x15(MSB)
- Z轴数据:0x16(LSB)和0x17(MSB)
- 温度数据:0x22(LSB)和0x23(MSB)
读取数据时需要注意以下几点:
- 数据采用二进制补码格式
- 读取顺序应为先MSB后LSB
- 温度数据需要特殊转换公式
在驱动中,我们使用regmap批量读取功能来高效获取数据:
c复制static int bmi088_accel_get_axis(struct bmi088_accel_data *data,
struct iio_chan_spec const *chan, int *val)
{
u8 reg = BMI088_ACCEL_REG_X_LSB + (chan->scan_index * 2);
__le16 buf;
int ret;
ret = regmap_bulk_read(data->regmap, reg, &buf, sizeof(buf));
if (ret)
return ret;
*val = sign_extend32(le16_to_cpu(buf), 15);
return 0;
}
2.2.2 中断配置寄存器
BMI088提供了灵活的中断配置能力,关键寄存器包括:
- INT1/INT2映射寄存器(0x58):配置哪些事件触发中断
- INT配置寄存器(0x59):设置中断引脚行为
- INT输出配置寄存器(0x20):配置中断引脚电气特性
在我们的优化驱动中,主要使用数据就绪中断和FIFO水位中断:
c复制static int bmi088_accel_interrupts_setup(struct iio_dev *indio_dev,
struct bmi088_accel_data *data)
{
// 配置数据就绪中断映射到INT1
regmap_write(data->regmap, BMI088_ACCEL_REG_INT1_INT2_MAP_DATA, 0x01);
// 配置INT1为推挽输出,高电平有效
regmap_write(data->regmap, BMI088_ACCEL_REG_INT_IO_CTRL, 0x09);
// 配置FIFO水位中断
regmap_write(data->regmap, BMI088_ACCEL_REG_FIFO_WTM, data->watermark);
regmap_write(data->regmap, BMI088_ACCEL_REG_INT1_INT2_MAP_FIFO, 0x01);
return 0;
}
3. Linux驱动框架深度解析
3.1 regmap寄存器抽象层
Linux内核的regmap子系统为寄存器访问提供了一层抽象,主要优势包括:
- 统一访问接口:无论底层是SPI、I2C还是其他总线,上层驱动使用相同的API
- 寄存器缓存:可选的缓存机制减少实际硬件访问
- 并发控制:内置的锁机制保证多线程安全
对于BMI088驱动,我们需要实现regmap的读写回调:
c复制static struct regmap_bus bmi088_regmap_bus = {
.write = bmi088_regmap_spi_write,
.read = bmi088_regmap_spi_read,
.read_flag_mask = 0x80, // BMI
