1、顶层初始化函数 int8_t bmi270_init(struct bmi2_dev *dev)
bmi270_init 是 BMI270 传感器驱动的顶层、用户直接调用的初始化入口,是整个驱动初始化的「总控函数」。它的核心职责是:
- 硬件无关的参数预初始化:配置芯片 ID、配置文件、接口特性等基础参数,为底层硬件交互做准备;
- 调用中层初始化函数 :通过
bmi2_sec_init完成硬件层面的校验、复位、配置写入; - 功能参数初始化:加载传感器特征配置、中断映射、最大页数等业务相关参数,让驱动具备完整的功能支撑;最终将 BMI270 从「裸硬件」状态转换为「可直接使用的驱动就绪状态」。
工作流程:
bmi270_init(顶层:参数配置)→
bmi2_sec_init(中层:硬件校验/复位)→
bmi2_soft_reset(底层:硬件复位+写配置)→ ...
→ 底层寄存器操作
cpp
/*!
* @brief This API:
* 1) updates the device structure with address of the configuration file.
* 2) Initializes BMI270 sensor.
* 3) Writes the configuration file.
* 4) Updates the feature offset parameters in the device structure.
* 5) Updates the maximum number of pages, in the device structure.
*/
int8_t bmi270_init(struct bmi2_dev *dev)
{
/* Variable to define error */
int8_t rslt;
/* Null-pointer check */
rslt = null_ptr_check(dev);
if (rslt == BMI2_OK)
{
//目标芯片ID = 0x24,后续bmi2_sec_init会读取实际的ID,与之对比
dev->chip_id = BMI270_CHIP_ID;
/* 配置文件总长度=驱动自带的默认配置数组大小 */
dev->config_size = sizeof(bmi270_config_file);
/* 激活BMI270专属功能 */
//BMI2_GYRO_CROSS_SENS_ENABLE:陀螺仪交叉轴灵敏度补偿
//BMI2_CRT_RTOSK_ENABLE:BMI270的专用运动检测算法特性
dev->variant_feature = BMI2_GYRO_CROSS_SENS_ENABLE | BMI2_CRT_RTOSK_ENABLE;
/* SPI读操作需要额外读1个dummy字节 */
if (dev->intf == BMI2_SPI_INTF)
{
dev->dummy_byte = 1;
}
else
{
dev->dummy_byte = 0;
}
/* 若用户未自定义配置文件指针,指向驱动默认配置数组 */
if (!dev->config_file_ptr)
{
dev->config_file_ptr = bmi270_config_file;
}
/* 调用中层初始化函数,将硬件无关的参数初始化交给硬件相关的中层初始化 */
rslt = bmi2_sec_init(dev);
if (rslt == BMI2_OK)
{
//特征配置便宜:用于配置/读取传感器特征
dev->feat_config = bmi270_feat_in;
dev->feat_output = bmi270_feat_out;
//存储区限制:配置存储区最大业数
dev->page_max = BMI270_MAX_PAGE_NUM;
//特征数量限制:驱动支持的最大输入输出特征数
dev->input_sens = BMI270_MAX_FEAT_IN;
dev->out_sens = BMI270_MAX_FEAT_OUT;
//中断映射:初始化中断引脚与功能的映射关系
dev->map_int = bmi270_map_int;
dev->sens_int_map = BMI270_MAX_INT_MAP;
//校准:获取陀螺仪交叉轴灵敏度补偿值,提升数据精度
rslt = bmi2_get_gyro_cross_sense(dev);
}
}
return rslt;
}
2、中层初始化函数 int8_t bmi270_init(struct bmi2_dev *dev)
bmi2_sec_init 是 BMI270 传感器驱动的初始化入口函数 (sec 可理解为 second/secondary,是驱动启动的核心初始化步骤),是用户调用驱动的第一个关键 API。其核心目标是:
- 完成「硬件通信链路验证」(SPI/I2C 接口适配、芯片 ID 校验);
- 初始化驱动层默认参数(轴映射、分辨率、功耗状态);
- 触发硬件软复位(恢复默认状态 + 写入配置);最终建立驱动与传感器硬件的有效、可预期的通信链路,为后续所有传感器操作(如读取数据、配置功能)打下基础。
cpp
/*!
* @brief This API is the entry point for bmi2 sensor. It selects between
* I2C/SPI interface, based on user selection. It reads and validates the
* chip-id of the sensor.
*/
int8_t bmi2_sec_init(struct bmi2_dev *dev)
{
int8_t rslt; //全局错误码载体,贯穿初始化全流程
uint8_t chip_id = 0; //存储读取的传感器芯片ID,用于校验
//默认轴映射结构体:xyz无重映射,均为正方向
struct bmi2_axes_remap axes_remap = {
.x_axis = BMI2_MAP_X_AXIS, .x_axis_sign = BMI2_POS_SIGN,
.y_axis = BMI2_MAP_Y_AXIS, .y_axis_sign = BMI2_POS_SIGN,
.z_axis = BMI2_MAP_Z_AXIS, .z_axis_sign = BMI2_POS_SIGN
};
//空指针校验
rslt = null_ptr_check(dev);
if (rslt == BMI2_OK)
{
//BMI270上电后默认进入高级省电模式APS,是否进入APS会影响后面写寄存器的时序
dev->aps_status = BMI2_ENABLE;
//BMI270默认是使用IIC读写,切换到SPI,需要一个读dummy操作,用于恢复SPI时序
if (dev->intf == BMI2_SPI_INTF)
{
rslt = bmi2_get_regs(BMI2_CHIP_ID_ADDR, &chip_id, 1, dev);
}
if (rslt == BMI2_OK)
{
//正式读芯片ID
rslt = bmi2_get_regs(BMI2_CHIP_ID_ADDR, &chip_id, 1, dev);
if (rslt == BMI2_OK)
{
//校验读到的IP与目标ID是否一致
if (chip_id == dev->chip_id)
{
dev->resolution = 16; //设置数据分辨率,BMI270的默认分辨率
dev->aux_man_en = 1; //启用辅助传感器手动使能
dev->remap = axes_remap; //加载默认轴映射配置
//软复位
//硬件层面: 将传感器所有寄存器恢复出厂值
//驱动层面:写入配置文件
//状态层面:软复位后同步aps_status,清零sens_en_stat,保证驱动层与硬件状态完全一致
rslt = bmi2_soft_reset(dev);
}
else
{
//保存读取到的Id
dev->chip_id = chip_id;
rslt = BMI2_E_DEV_NOT_FOUND;
}
}
}
}
return rslt;
}
3、底层初始化函数 int8_t bmi2_soft_reset(struct bmi2_dev *dev)
- 核心逻辑 :BMI270 软复位会清空所有自定义配置,因此复位流程中必须调用
bmi2_write_config_file重新写入配置,是恢复传感器业务功能的关键步骤; - 硬件适配:复位后的 2000μs 延时、SPI 哑读、APS 状态同步,都是为了适配传感器硬件特性,保证后续配置写入的通信稳定性;
- 状态一致性 :驱动层通过同步
aps_status、清零sens_en_stat,保证软件层状态与传感器硬件状态一致,避免时序 / 逻辑错误; - 分层设计 :从软复位到配置写入,函数调用链路层层封装(
bmi2_soft_reset→bmi2_write_config_file→write_config_file→upload_file→bmi2_set_regs),每层只处理单一逻辑,降低维护成本。
cpp
/*!
* @brief This API resets bmi2 sensor. All registers are overwritten with
* their default values.
*/
int8_t bmi2_soft_reset(struct bmi2_dev *dev)
{
int8_t rslt; //全局错误码载体,贯穿复位+写配置全流程
uint8_t data = BMI2_SOFT_RESET_CMD; //软复位命令字
uint8_t dummy_read = 0; //SPI接口复位后读dummy缓冲区,解决硬件接口切换问题
/* 空指针检测 */
rslt = null_ptr_check(dev);
if (rslt == BMI2_OK)
{
/* 写入软复位命令到命令寄存器 */
rslt = bmi2_set_regs(BMI2_CMD_REG_ADDR, &data, 1, dev);
//延时2000us,等待传感器完成复位,硬件强制要求
dev->delay_us(2000, dev->intf_ptr);
//同步APS状态,复位后传感器默认进入高级省电模式
dev->aps_status = BMI2_ENABLE;
//如果是SPI接口通讯,需要有个读空字节操作,将通讯接口切换到SPI时序
//通过读ID这个人畜无害的操作就可以实现
if ((rslt == BMI2_OK) && (dev->intf == BMI2_SPI_INTF))
{
rslt = bmi2_get_regs(BMI2_CHIP_ID_ADDR, &dummy_read, 1, dev);
}
if (rslt == BMI2_OK)
{
/* 写配置文件 */
rslt = bmi2_write_config_file(dev);
}
/* 核心步骤:重置传感器的使能状态,软复位后传感器所有的功能默认禁用
* 清零该状态位,避免上层函数误判"复位后功能仍开启"
*/
if (rslt == BMI2_OK)
{
dev->sens_en_stat = 0;
}
}
return rslt;
}
4、写配置文件函数上层函数 int8_t bmi2_write_config_file(struct bmi2_dev *dev)
写入配置后,传感器需要内部处理才能完成加载,因此必须主动读取「加载状态」而非仅判断写入结果;
bmi2_write_config_file → write_config_file → upload_file → bmi2_set_regs → dev->write(SPI/I2C底层写)

- 分层设计 :
bmi2_write_config_file是上层封装,**write_config_file**是底层硬件操作,前者负责「安全校验 + 结果补全」,后者负责「实际通信写入」,解耦了业务逻辑和硬件操作; - 参数适配 :针对**
write_config_file** 的硬件约束,提前修正read_write_len,避免底层操作因参数错误失败; - 双重校验 :不仅校验
write_config_file的通信结果,还校验传感器内部的加载状态,保证配置真正生效(而非仅 "数据发出")。
cpp
/*!
* @brief This API loads the configuration file into the bmi2 sensor.
*/
int8_t bmi2_write_config_file(struct bmi2_dev *dev)
{
int8_t rslt;
uint8_t load_status = 0; //存储传感器内部的配置加载状态
/* 空指针检测*/
rslt = null_ptr_check(dev);
if ((rslt == BMI2_OK) && (dev->config_size != 0))
{
/* 确保写入字节数是2的倍数,传感器要求2字节对齐 */
if ((dev->read_write_len % 2) != 0)
{
//将奇数字节修正为偶数字节
dev->read_write_len = dev->read_write_len - 1;
}
//确保最小写入长度位2字节,传感器硬件限制
if (dev->read_write_len < 2)
{
dev->read_write_len = 2;
}
/* 写入配置文件*/
rslt = write_config_file(dev);
if (rslt == BMI2_OK)
{
//读取传感器内部的配置加载状态
rslt = bmi2_get_internal_status(&load_status, dev);
//掩码提取有效位,过滤无关状态位,只保留配置加载相关
load_status &= BMI2_CONFIG_LOAD_STATUS_MASK;
//将状态保存到设备结构体,供上层调用函数查看
dev->load_status = load_status;
/* 校验是否真的加载成功*/
if ((rslt == BMI2_OK) && (load_status != BMI2_CONFIG_LOAD_SUCCESS))
{
rslt = BMI2_E_CONFIG_LOAD; //加载失败,更新错误码
}
}
}
else
{
rslt = BMI2_E_NULL_PTR;
}
return rslt;
}
5、写配置文件函数底层函数 static int8_t write_config_file(struct bmi2_dev *dev)
write_config_file 是 BMI270 配置写入的底层核心实现函数 (static 修饰,仅内部调用),承接上层**bmi2_write_config_file**的参数校验结果,完成「硬件状态准备 → 分块写入配置数据 → 硬件状态恢复」的全流程,是真正和传感器硬件交互的核心逻辑。
- 核心设计思想 :
write_config_file是「硬件约束适配 + 分块写入 + 状态保护」的典型嵌入式驱动实现,既满足传感器偶数字节 / 最小 2 字节的写入要求,又通过临时修改 / 恢复参数保证上层接口的一致性; - 关键保障机制:写入前禁用省电模式和配置加载(避免写入异常),写入后启用配置加载并恢复省电模式(保证配置生效 + 功耗正常);
- 边界处理:通过余数判断分两种情况写入,对无法整除的剩余字节强制按 2 字节写入,彻底解决硬件约束问题。
cpp
/*!
* @brief This internal API writes the configuration file.
*/
static int8_t write_config_file(struct bmi2_dev *dev)
{
int8_t rslt;
//搞这么多变量,就是为了保证所有的配置数据都能按要求写入传感器
//这个很关键,配置文件写不成功传感器就用不了
uint16_t index = 0; //配置数据写入偏移索引,从0开始
uint16_t config_size = dev->config_size; //配置数据总长度
uint8_t remain = (uint8_t)(config_size % dev->read_write_len); //总长度对单词写入长度的余数
uint16_t bal_byte = 0; //总长度中能被单次写入长度整除的部分
uint16_t read_write_len = 0; //临时保存用户设置的单次写入长度,用于恢复
/* 禁用高级省电模式 */
//BMI270 的高级省电模式会降低通信速率、关闭部分硬件模块,
//此时写入配置容易出现通信超时 / 数据丢失,因此先临时关闭,写入完成后恢复;
rslt = bmi2_set_adv_power_save(BMI2_DISABLE, dev);
if (rslt == BMI2_OK)
{
/* 禁用配置自动加载,方便后面配置文件写完后一起加载 */
//这两步是写入配置文件的前置条件,目的是保证写入过程的稳定性
//传感器默认可能在写入配置字节时「实时解析加载」,
//若分块写入过程中提前加载,会导致不完整的配置生效(配置错乱),
//因此先禁用,等全部数据写入后再启用加载。
rslt = set_config_load(BMI2_DISABLE, dev);
if (rslt == BMI2_OK)
{
if (!remain)
{
/* 配置总长度能被单次写入长度整除 remain=0 */
//dev->config_file_ptr + index 配置数据缓冲区的当前写入起始地址
//rslt == BMI2_OK 上一次写入无错误
for (index = 0; (index < config_size) && (rslt == BMI2_OK); index += dev->read_write_len)
{
rslt = upload_file((dev->config_file_ptr + index), index, dev->read_write_len, dev);
}
}
else //配置总长度不能能被单次写入长度整除
{
/* 计算整除部分的字节数*/
bal_byte = (uint16_t) config_size - (uint16_t) remain;
/* 先写入整除部分的所有数据 */
for (index = 0; (index < bal_byte) && (rslt == BMI2_OK); index += dev->read_write_len)
{
rslt = upload_file((dev->config_file_ptr + index), index, dev->read_write_len, dev);
}
if (rslt == BMI2_OK)
{
/* 临时保存用户设置的单次写入长度*/
read_write_len = dev->read_write_len;
/* 剩余字节强制按2字节写入*/
dev->read_write_len = 2;
/* 写入剩余的remain字节*/
for (index = bal_byte;
(index < config_size) && (rslt == BMI2_OK);
index += dev->read_write_len)
{
rslt = upload_file((dev->config_file_ptr + index), index, dev->read_write_len, dev);
}
/* 恢复用户设置的单次写入长度,不污染上层参数 */
//这一步必须要做
dev->read_write_len = read_write_len;
}
}
if (rslt == BMI2_OK)
{
/* 启用加载配置 ,让配置生效的关键一步*/
rslt = set_config_load(BMI2_ENABLE, dev);
if (rslt == BMI2_OK)
{
/* 恢复高级省电模式 */
rslt = bmi2_set_adv_power_save(BMI2_ENABLE, dev);
}
}
}
}
return rslt;
}
6、写配置文件的最底层操作函数int8_t upload_file()
upload_file 是 BMI270 配置写入流程中最底层的硬件操作函数 (static 修饰,仅内部调用),承接上层 write_config_file 的分块写入逻辑,负责将「指定长度的配置数据」按照传感器硬件的地址编码规则,写入到配置存储区的对应位置,是配置数据真正 "落地" 到传感器硬件的核心函数。
- 硬件规则适配 :**
upload_file**的核心是适配 BMI270 配置存储区的「12 位地址拆分」和「2 字节寻址单元」硬件规则,是数据能正确写入的关键; - 时序严格性:必须遵循「先写地址、后写数据」的硬件时序,否则数据会写入错误位置;
- 分层职责清晰 :仅负责单块数据的底层写入,不处理分块、状态保护等逻辑,和上层 **
write_config_file**分工明确,符合嵌入式驱动 "单一职责" 设计原则。
- 上层**
write_config_file** 负责 "分块"(计算每个块的index和write_len),upload_file负责 "写块"(将单个块的数据按硬件规则写入); - 每一次**
upload_file** 调用只处理一个数据块,失败则整个写入流程终止,保证数据写入的完整性。
cpp
/*!
* @brief This internal API loads the configuration file.
*/
static int8_t upload_file(const uint8_t *config_data, uint16_t index, uint16_t write_len, struct bmi2_dev *dev)
{
/* Variable to define error */
int8_t rslt;
//存储编码后的2字节配置存储地址,硬件要求
//BMI270的配置存储区是12位,0--4095,需要拆分成2个字节
uint8_t addr_array[2] = { 0 };
if (config_data != NULL)
{
/* 将字节偏移index转换为硬件地址(index / 2),取低4位存入addr_array[0]*/
addr_array[0] = (uint8_t)((index / 2) & 0x0F);
/* 将硬件地址右移4位,取高8位存入addr_array[1]*/
addr_array[1] = (uint8_t)((index / 2) >> 4);
/* 写入编码后的地址*/
rslt = bmi2_set_regs(BMI2_INIT_ADDR_0, addr_array, 2, dev);
if (rslt == BMI2_OK)
{
/*写入配置数据 */
rslt = bmi2_set_regs(BMI2_INIT_DATA_ADDR, (uint8_t *)config_data, write_len, dev);
}
}
else
{
rslt = BMI2_E_NULL_PTR;
}
return rslt;
}
7、底层寄存器操作函数 int8_t bmi2_set_regs()
cpp
/*!
* @brief This API writes data to the given register address of bmi2 sensor.
*/
int8_t bmi2_set_regs(uint8_t reg_addr, const uint8_t *data, uint16_t len, struct bmi2_dev *dev)
{
/* Variable to define error */
int8_t rslt;
uint16_t loop; //低功耗下逐子节写入的循环计数器,正常模式下无作用
/* 检测空指针 */
rslt = null_ptr_check(dev);
if ((rslt == BMI2_OK) && (data != NULL))
{
/* 如果是SPI接口,需要在地址位区分读或者写 */
if (dev->intf == BMI2_SPI_INTF)
{
reg_addr = (reg_addr & BMI2_SPI_WR_MASK);
}
/* 低功耗模式下,逐字节写入,没字节写入后延时450us */
if (dev->aps_status == BMI2_ENABLE)
{
for (loop = 0; loop < len; loop++)
{
dev->intf_rslt = dev->write((uint8_t)((uint16_t)reg_addr + loop), &data[loop], 1, dev->intf_ptr);
dev->delay_us(BMI2_POWER_SAVE_MODE_DELAY_IN_US, dev->intf_ptr);
if (dev->intf_rslt != BMI2_INTF_RET_SUCCESS)
{
break;
}
}
}
/* 正常模式下突发写入,效率更高,仅需延时2us,保证数据接收完成 */
else
{
dev->intf_rslt = dev->write(reg_addr, data, len, dev->intf_ptr);
dev->delay_us(BMI2_NORMAL_MODE_DELAY_IN_US, dev->intf_ptr);
}
/* Updating the advance power saver flag */
if (reg_addr == BMI2_PWR_CONF_ADDR)
{
if (*data & BMI2_ADV_POW_EN_MASK)
{
dev->aps_status = BMI2_ENABLE; //启用高级省电模式
}
else
{
dev->aps_status = BMI2_DISABLE; //禁用高级省电模式
}
}
if (dev->intf_rslt != BMI2_INTF_RET_SUCCESS)
{
rslt = BMI2_E_COM_FAIL;
}
}
else
{
rslt = BMI2_E_NULL_PTR;
}
return rslt;
}
至此,BMI270的整个初始化过程实现闭环
