BMI270应用笔记2:BMI270芯片初始化详解

1、顶层初始化函数 int8_t bmi270_init(struct bmi2_dev *dev)

bmi270_init 是 BMI270 传感器驱动的顶层、用户直接调用的初始化入口,是整个驱动初始化的「总控函数」。它的核心职责是:

  1. 硬件无关的参数预初始化:配置芯片 ID、配置文件、接口特性等基础参数,为底层硬件交互做准备;
  2. 调用中层初始化函数 :通过 bmi2_sec_init 完成硬件层面的校验、复位、配置写入;
  3. 功能参数初始化:加载传感器特征配置、中断映射、最大页数等业务相关参数,让驱动具备完整的功能支撑;最终将 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_resetbmi2_write_config_filewrite_config_fileupload_filebmi2_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** 负责 "分块"(计算每个块的 indexwrite_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的整个初始化过程实现闭环