实物教程——大疆C板读取BMI088传感器数据_操气的小虫儿的博客-CSDN博客_bmi088代码

这是我写的上一篇文章,用SPI实现了读取BMI088传感器数据。

现在要做的就是在读取BMI088传感器数据的基础上,对得到的数据进行计算得到四元数,再对四元数计算得到欧拉角。这两个计算已经有轮子可用了,分别是MahonyHARSupdateIMU()函数和get_angle()函数,详细情况请参考《RoboMaster开发板C型嵌入式软件教程文档》中18.4.2。

《RoboMaster开发板C型嵌入式软件教程文档》中第18章用到了SPI+DMA来读取传感器数据,但是我不会DMA,所以只用了SPI。官方教程里还用了Freertos来实现1ms为周期读取传感器数据,但是我不会Freertos,但我会用TIM,所以我用了TIM1来实现1ms为周期。

硬件环境

Robomaster 开发板C型

STLink V2

XT30电源

CubeMX配置

请参考实物教程——大疆C板读取BMI088传感器数据里的这个配置,除此之外,我们还要配置TIM1,请参考《RoboMaster开发板C型嵌入式软件教程文档》中3.4.1,官方文档里边分频值16799重载值4999实现了2Hz,我们需要1000Hz,所以分频值167重载值999。

程序代码

代码看起来比较长,不要害怕,大部分代码都是实物教程——大疆C板读取BMI088传感器数据里的,这里就是把加速度计的程序代码和陀螺仪的程序代码写在了一起,把while(1)循环里的代码放到了定时器回调函数HAL_TIM_PeriodElapsedCallback()里,最后的MahonyAHRSupdateIMU()函数和get_angle()函数官方文档里讲的有。

#define BMI088_ACCEL_3G_SEN 0.0008974358974f    //这个数字我也不知道哪来的
#define BMI088_GYRO_2000_SEN 0.00106526443603169529841533860381f    //这个数字我也不知道哪来的
float BMI088_ACCEL_SEN = BMI088_ACCEL_3G_SEN;
float BMI088_GYRO_SEN = BMI088_GYRO_2000_SEN;
float accelerometer[3];
float gyro[3];
float quat[4] = {1.0f, 0.0f, 0.0f, 0.0f};
float INS_angle[3] = {0.0f, 0.0f, 0.0f};
uint8_t i = 0;
uint8_t buf[8]={0,0,0,0,0,0,0,0};
uint8_t pTxData;
uint8_t pRxData;

int main(void)
{
    //这9行代码,向地址0x7E处写入0xB6值,加速度计软件复位,使加速度计各个寄存器恢复为默认值
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);    //PA4置0,片选加速度计
    pTxData = (0x7E & 0x7F);    //Bit #0和Bit #1-7,Bit #0是0,表示写
    HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
    while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX);    //等待SPI发送完成
    pTxData = 0xB6;    //Bit #8-15
    HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
    while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX);    //等待SPI发送完成
    HAL_Delay(1);    //延时1ms
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);    //PA4置1,取消片选加速度计

    //加速度计复位后默认是暂停模式,这9行代码,向地址0x7D处写入0x04值,使加速度计进入正常模式
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);    //PA4置0,片选加速度计
    pTxData = (0x7D & 0x7F);    //Bit #0和Bit #1-7,Bit #0是0,表示写
    HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
    while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX);    //等待SPI发送完成
    pTxData = 0x04;    //Bit #8-15
    HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
    while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX);    //等待SPI发送完成
    HAL_Delay(1);    //延时1ms
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);    //PA4置1,取消片选加速度计

    //这9行代码,向地址0x14处写入0xB6值,陀螺仪软件复位,使陀螺仪各个寄存器恢复为默认值
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);    //PB0置0,片选陀螺仪
    pTxData = (0x14 & 0x7F);    //Bit #0和Bit #1-7,Bit #0是0,表示写
    HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
    while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX);    //等待SPI发送完成
    pTxData = 0xB6;    //Bit #8-15
    HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
    while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX);    //等待SPI发送完成
    HAL_Delay(30);    //延时30ms
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);    //PB0置1,取消片选陀螺仪
	
    HAL_TIM_Base_Start_IT(&htim1);    //使TIM1定时器开始工作,并使能其定时中断

    while(1)
    {
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);    //PA4置0,片选加速度计
    pTxData = (0x12 | 0x80);    //Bit #0和Bit #1-7,Bit #0是1,表示读
    HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
    while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX);    //等待SPI发送完成
    HAL_SPI_Receive(&hspi1, &pRxData, 1, 1000);    //Bit #8-15,无效值
    while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_RX);    //等待SPI接收完成
    i = 0;
    while (i < 6)
    {
        HAL_SPI_Receive(&hspi1, &pRxData, 1, 1000);    //Bit #16-23,寄存器0x12的值,然后是寄存器0x13、0x14、0x15、0x16、0x17的值
    	while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_RX);    //等待SPI接收完成
    	buf[i] = pRxData;
        i++;
    }
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);    //PA4置1,取消片选加速度计
    accelerometer[0] = ((int16_t)((buf[1]) << 8) | buf[0]) * BMI088_ACCEL_SEN;
    accelerometer[1] = ((int16_t)((buf[3]) << 8) | buf[2]) * BMI088_ACCEL_SEN;
    accelerometer[2] = ((int16_t)((buf[5]) << 8) | buf[4]) * BMI088_ACCEL_SEN;

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);    //PB0置0,片选陀螺仪
    pTxData = (0x00 | 0x80);    //Bit #0和Bit #1-7,Bit #0是1,表示读
    HAL_SPI_Transmit(&hspi1, &pTxData, 1, 1000);
    while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_TX);    //等待SPI发送完成
    i = 0;
    while (i < 8)
    {
    	HAL_SPI_Receive(&hspi1, &pRxData, 1, 1000);    //Bit #8-15,寄存器0x00的值,然后是寄存器0x01、0x02、0x03、0x04、0x05、0x06、0x07的值
    	while(HAL_SPI_GetState(&hspi1)==HAL_SPI_STATE_BUSY_RX);    //等待SPI接收完成
    	buf[i] = pRxData;
    	i++;
    }
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);    //PB0置1,取消片选陀螺仪
    if(buf[0] == 0x0F)	//buf[0]储存GYRO_CHIP_ID,应该为0x0F,判断我们读取到的是不是陀螺仪的值。
    {
    	gyro[0] = ((int16_t)((buf[3]) << 8) | buf[2]) * BMI088_GYRO_SEN;
    	gyro[1] = ((int16_t)((buf[5]) << 8) | buf[4]) * BMI088_GYRO_SEN;
    	gyro[2] = ((int16_t)((buf[7]) << 8) | buf[6]) * BMI088_GYRO_SEN;
    }

	MahonyAHRSupdateIMU(quat, gyro[0], gyro[1], gyro[2], accelerometer[0], accelerometer[1], accelerometer[2]);    //对得到的加速度计、陀螺仪数据进行计算得到四元数
	get_angle(quat, INS_angle, INS_angle+1, INS_angle+2);    //对四元数计算得到欧拉角
}

有两个点容易遗漏,一是我们要在"MahonyAHRS.c"文件里把#define sampleFreq 521.0f中的521.0f改为1000.0f,sampleFreq代表采样频率。二是上边的第49行代码HAL_TIM_Base_Start_IT(&htim1);,这行代码让TIM1开始工作,我最初也是奇怪为什么我觉得代码没问题,但debug却没有数据,后来看了官方文档才发现自己没有写这个。

效果演示

 我主要用到了INS_angle[1]这个数据。官方的程序效果是小数点后第3位稳定,第4位跳动;我这个程序效果是小数点后第2位稳定,第3位跳动。不知道为什么,还在查找原因。0.01rad≈0.573°,0.001rad≈0.0573°。

20221020:优化了代码之后,我已经可以达到接近官方的效果了,但是最近忙于考研,考完研之后再把代码整理一下发出来。

杂谈

越来越觉得官方的一些资料非常重要,很多问题都可以在官方资料里找到答案或者解决思路。

我用的Robomaster 开发板C型,我刚开始学的时候就是按照官方教程《RoboMaster开发板C型嵌入式软件教程文档》来学的,代码什么的也是首先来看官方给的代码,之后写代码遇到一些问题的时候也会来里边找思路。

姿态解算用到的传感器数据是BMI088提供的,刚开始写这部分代码的时候,对SPI的一些操作很不熟悉,不知道加速度计的Bit #8-15是无效值,不知道将加速度计、陀螺仪软件复位,不知道将加速度计从暂停模式变为正常模式,不知道复位、转换模式之后要给延时。导致写出来的代码用不了,之后看了BMI088的官方文档,慢慢地把这些理清了。

传感器是一门学问,写点代码凑合着读出来比较简单,但是想发挥出硬件的极致比较难,我这个代码说实话没有发挥出传感器的极致,精度还是比较低。

参考:

[1] 《RoboMaster开发板C型嵌入式软件教程文档》

[2] 《BMI088 6-axis Motion Tracking for High-performance Applications》

[3] 实物教程——大疆C板读取BMI088传感器数据

ChatGPT4

 用ChatGPT4理解上述代码:

​​​​​​​

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐