[STM32]BMI270驱动开发(一)

BMI270 Driver Development

这篇文档描述了BMI270驱动的开发笔记

初始化

基本过程

首先参考了用户手册的Digital Interface一章,我们需要知道这个IMU的通信方法和时序:

  • I2C:地址是0x68(SD0接地)/0x69(SD0接VDDIO),支持100kHz,40kHz,1MHz,7-bit地址模式
  • SPI: 支持10MHz,三线模式,读模式在发送地址后需要一个dummy bit,才能接data

接着参考了用户手册的Quick Start Guide一章,我们需要知道关于配置的几个基本方法:

  • 通过读取0x00地址的CHIP_ID(0x24)来测试通信是否正常
  • 写配置时,先关闭PWR_CONF(0x7C第0位置0),然后等待450us,之后开启INIT_CTRL(0x59置0x00),连接写入配置列表(0x5E)后,关闭INIT_CTRL(0x59置0x01)(bmi270.c开头给出了配置列表,非常长)
  • 读取INTERNAL_STATUS(0x21)(3,0)是否为0x01,表示init_ok,其它则为出错

4.4节对此进行了更详细的描述,当完成这个操作后,IMU进入"Configuration mode"。

Configuration mode和其它模式

4.5节对Configuration mode和其它模式进行了对比。可以看到,Configuration mode是一个没有开启任何传感器的模式。

  • 需要注意,重新上电和soft reset都会使其进入Suspend mode,需要从这个模式切换到其它模式才能正常工作。

  • 不同的power mode不影响传感数据质量,只影响其唤起时间

  • 从节能模式(PWR_CONFI.adv_power_save=0b1)切换时,需要至少450us的延迟。由于每次都需要从Suspend mode切换,所以这450us是必须的。

建立通信

static int8_t write_config_file(struct bmi2_dev *dev) in bmi2.c

这个方法实现了上节描述的写配置过程,而官方提供的例子对这个方法进行了层层包装,又臭又长,本文打算基于这个方法对配置过程进行简化。不过在此之前,需要先做到基本的通信。

在写入配置文件之前,需要先将通信接口接入BMI270中,需要进行下面的几个工作:

  • 传递读和写:这里使用了函数指针,将spi的读和写函数在实例化类的时候传入
  • 传递延迟:同上,这里使用sys/delay.c中的delay_us()
  • 设定SPI/I2C:这里我没有通过类属性成员来定义,而是放在了user_interface_header.h这个头文件中用constexpr定义模式
  • 其它设定:Hook_Log指向打印函数,这里是printf的定义,Hook_Error_Handler指向一个错误处理函数,用于打印错误代码。

,核心代码部分如下:

/* user_interface_header.h */    
// rename to selet mode, optional names: I2C, I3C
    constexpr bool SPI = true;  

    using Hook_Read = void (*)(uint8_t reg_addr, uint8_t *reg_data, uint32_t len);
    using Hook_Write = void (*)(uint8_t reg_addr, uint8_t *reg_data, uint32_t len);
    using Hook_Delay = void (*)(uint32_t period);
    using Hook_Log = int (*)(const char *format, ...);
    //args:
    //  error_source:   is passsed through user definition, refer to the @ref Debug_Device_Code
    //  error_code:     is the debug code, referto the @ref BMI270_NS::Debug_Code in imu_defs.h
    using Hook_Error_Handler = void (*)(uint8_t error_source, uint8_t error_code);

/* imu.cpp */
    BMI2700::BMI2700(
        User_Interface::Hook_Read read_fun,
        User_Interface::Hook_Write write_fun,
        User_Interface::Hook_Delay delay_us_fun,
        User_Interface::Hook_Log log,
        User_Interface::Hook_Error_Handler error_hdl,
        uint16_t read_write_len)
        : _read(read_fun), _write(write_fun), _delay_us(delay_us_fun), _log(log),
          _error_hdl(error_hdl),_read_write_len(read_write_len)

    {
        // Any private variable init here,
        // Please do not call any methods here, put them in Init()
        _config_file = Configration_Mode::bmi270_config_file;
        _config_file_size = sizeof(Configration_Mode::bmi270_config_file);
    }
/* main.cpp */

#include "user_interface.h"

#define msb_rx 0x80
#define msb_tx 0x00
#define read_byte_cmd(adr) spi1_read_write_byte(msb_rx | adr);
#define write_byte_cmd(adr) spi1_read_write_byte(msb_tx | adr);

void spi_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len)
{
    uint8_t temp_buf;
    SPI1_CS_SAFE({
        read_byte_cmd(reg_addr);
        HAL_SPI_Receive(&hspi1, &temp_buf, 1, 1000);
        HAL_SPI_Receive(&hspi1, reg_data, len, 1000);
    });
}
void spi_write(uint8_t reg_addr, uint8_t *reg_data, uint32_t len)
{
    SPI1_CS_SAFE({
        write_byte_cmd(reg_addr);
        HAL_SPI_Transmit(&hspi1, reg_data, len, 1000);
    });
}
void spi_delay_us(uint32_t period)
{
    delay_us(period);
}

int print_log(char *ptr, int len)
{
    HAL_UART_Transmit(&g_uart1_handle, (uint8_t *)ptr, len, HAL_MAX_DELAY);
    return len;
}

BMI2700 BMI2700_instance{
    spi_read,
    spi_write,
    spi_delay_us,
    printf,
    Error_Handler,
    46};

void bmi270_init()
{
    spi_init(SPI1);
    if (BMI2700_instance.Init())
    {
        printf("BMI2700 init failed, check the hw connection\r\n");
    }
    BMI2700_instance.Soft_Reset();
}

写入配置文件

配置文件在bmi270.c的开头,相当于是一个官方提供的校准数据文件,大小为8kb。这里用了三个方法:

  • Load_Config_File: 参考官方文档的4.4节,按照save power mode off -> INIT_CTRL reset ->burst write -> INIT_CTRL set -> check status -> save power mode on。其中的延迟不可省略
  • _Init_Reg_Burst_Write: 关于这里的写入方法在文档没有详细说明。INIT文件写入操作涉及INIT_CTRLINIT_ADDR_0INIT_ADDR_1INIT_DATA几个地址,INIT_ADDR_0INIT_ADDR_1组成了一个“映射地址的地址”,写入INIT_DATA中的数据会自动转移到一个16位的大寄存器,INIT_DATA相当于是一个出入餐口,INIT_ADDR_0INIT_ADDR_1是一个指针的低位和高位,每写/读2byte,这对地址的值自增1 。无论对该地址还是INIT_DATA进行读还是写,都需要将INIT_CTRL置0 。
    写入INIT_ADDR_0INIT_ADDR_1时,可直接对INIT_ADDR_0写入2byte,自动将后一位放入INIT_ADDR_1
    450延迟不可省略
  • _Init_Reg_Check:检查写入数据是否正确,这里只检查前后16位。请将该操作放在INIT_CTRL置0的域内。
    uint8_t BMI2700::Load_Config_File()
    {
        Write_Bits(Reg_Adr::PWR_CONF_ADDR, 0x01, 0x00);
        _delay_us(100);
        Read_Byte(Reg_Adr::PWR_CONF_ADDR);
        _delay_us(450);

        // set INIT_CTRL to 0x00,
        Write_Bit(Reg_Adr::INIT_CTRL_ADDR, 0, false);
        _delay_us(450);

        // burst write config file using proper BMI270 method
        _Init_Reg_Burst_Write();
        _delay_us(100);
        _Init_Reg_Check();

        // set INIT_CTRL to 0x01
        Write_Bit(Reg_Adr::INIT_CTRL_ADDR, 0, true);
        _delay_us(450);

        // check INTERNAL_STATUS
        _delay_us(20000); // wait until INTERNAL_STATUS_ADDR set to 0x01
        if (Read_Byte(Reg_Adr::INTERNAL_STATUS_ADDR)!=0x01)
        {
            BMI2_DEBUG(Debug_Code::INIT_CONFIG_FILE_NOT_COMPLETE);
        }
        /* Enable advanced power save mode */
        Write_Bits(Reg_Adr::PWR_CONF_ADDR, 0x01, 0x01);

        return 0;
    }

    uint8_t BMI2700::_Init_Reg_Burst_Write()
    {
        if (_config_file != NULL)
        {
            /* Write the 2 bytes of address in consecutive locations */
            Write_Bytes(Reg_Adr::INIT_ADDR_0, (uint8_t *)0x00, 2);
            _delay_us(450);
            Write_Bytes(Reg_Adr::INIT_DATA_ADDR, (uint8_t *)(_config_file), _config_file_size);
        }
        else
        {
            BMI2_DEBUG(Debug_Code::NULL_PTR);
        }

        return 0;
    }
  constexpr uint32_t NUM_OF_CHECK = 16;
    // Worning!! This method should implement in enviroment whose INIT_CTRL is 0x00
    uint8_t BMI2700::_Init_Reg_Check()
    {
        uint8_t tmp_data1[NUM_OF_CHECK];
        uint8_t tmp_data2[NUM_OF_CHECK];

        uint8_t addr[2]{0};
        // Check the first 16 bytes
        Write_Byte(Reg_Adr::INIT_ADDR_0, 0x00);
        _delay_us(450);
        Read_Bytes(Reg_Adr::INIT_DATA_ADDR, tmp_data1, NUM_OF_CHECK);
        // Check the last 16 bytes
        addr[0] = (_config_file_size - NUM_OF_CHECK) / 2 & 0x0f;
        addr[1] = (_config_file_size - NUM_OF_CHECK) / 2 >> 4;
        Write_Bytes(Reg_Adr::INIT_ADDR_0, addr, 2);
        _delay_us(450);
        Read_Bytes(Reg_Adr::INIT_DATA_ADDR, tmp_data2, NUM_OF_CHECK);

        for (size_t i = 0; i < NUM_OF_CHECK; i++)
        {
            if ((tmp_data1[i] != _config_file[i]) || (tmp_data2[i] != _config_file[_config_file_size - NUM_OF_CHECK + i]))
            {
                
                BMI2_DEBUG(Debug_Code::INIT_CONFIG_FILE_NOT_COMPLETE);
            }
        }
        return 0;
    }
posted @ 2025-10-05 18:06  Lvista  阅读(23)  评论(0)    收藏  举报