前期通过对dp各方面的使用和测试,已初步感受到dp的强大。那么就尝试着通过一个实际案例来验证下dp生成程序代码的易用性与可用性。

为何要选择写BMI088的驱动作为测试用例呢?首先BMI088是一款性能优异的IMU芯片。其次,也是最主要的原因,BMI088是个很坑的缝合怪芯片,就像是强行用胶水把一个加速度计和一个陀螺仪粘一块的感觉。输出速率不一样、驱动方式不一样、片选引脚不一样、ID不一样……对比ICM42688,感觉就非常古怪。之前写过它的驱动,但没搞定也不确定究竟是软件还是硬件方面有问题,然后就一直搁置了(反正有ICM42688镇场子~~~)。

所以用BMI088这个缝合怪来测试DP的能力,既有一定难度,又符合实战条件。嗯,比较合适,开始动手。

经过几轮DP问答测试,精确限定驱动代码适用的软硬件环境后,最终给出的提示词:

适用于 stm32f4系列,通过spi读写,不使用dma,但要使用bmi088的fifo机制,数据刷新速率500hz以上,输出加速度及陀螺数据。应用于无人机产品,要对滤波器进行针对性设置。代码要完整,包括头文件内容,以及完整示例程序。

贴一下首次生成的代码,很漂亮,还有贴心且准确的注释,可惜移植到PH47框架下却无法正常运行,接下来会讲述其中的bug和坑(读者可快速掠过fifo相关部分)。

 /* bmi088.h */ 
#ifndef __BMI088_H 
#define __BMI088_H 
 
#include "stm32f4xx_hal.h" 
 
// 寄存器地址定义 
#define BMI088_ACC_CHIP_ID 0x00 
#define BMI088_ACC_CONF 0x40 
#define BMI088_ACC_RANGE 0x41 
#define BMI088_ACC_FIFO_CONFIG 0x48 
#define BMI088_GYR_CHIP_ID 0x00 
#define BMI088_GYR_RANGE 0x0F 
#define BMI088_GYR_BANDWIDTH 0x10 
#define BMI088_FIFO_LENGTH 0x22 
 
// 传感器配置参数 
#define ACC_ODR_1000HZ 0x05 
#define ACC_RANGE_24G 0x03 
#define GYRO_ODR_2000HZ 0x07 
#define GYRO_RANGE_2000DPS 0x00 
#define GYRO_BW_532HZ 0x04 
 
typedef struct { 
 SPI_HandleTypeDef *hspi; 
 GPIO_TypeDef *acc_cs_port; 
 uint16_t acc_cs_pin; 
 GPIO_TypeDef *gyr_cs_port; 
 uint16_t gyr_cs_pin; 
} BMI088_HandleTypeDef; 
 
// 函数声明 
void BMI088_Init(BMI088_HandleTypeDef *hdev); 
void BMI088_ReadFIFO(BMI088_HandleTypeDef *hdev, int16_t *acc, int16_t *gyro); 
 
#endif

.c 文件

/* bmi088.c */ 
#include "bmi088.h" 
 
static void SPI_WriteRegister(SPI_HandleTypeDef *hspi, GPIO_TypeDef* cs_port, 
 uint16_t cs_pin, uint8_t reg, uint8_t data) 
{
 uint8_t tx[2] = {reg & 0x7F, data}; 
 HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_RESET); 
 HAL_SPI_Transmit(hspi, tx, 2, 100); 
 HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_SET); 
}
 
static uint8_t SPI_ReadRegister(SPI_HandleTypeDef *hspi, GPIO_TypeDef* cs_port, 
 uint16_t cs_pin, uint8_t reg) 
{
 uint8_t tx = reg | 0x80; 
 uint8_t rx[2] = {0}; 
 HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_RESET); 
 HAL_SPI_TransmitReceive(hspi, &tx, rx, 2, 100); 
 HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_SET); 
 return rx[1]; 
}
 
void BMI088_Init(BMI088_HandleTypeDef *hdev) 
{
 // 加速度计SPI模式切换(伪读操作) 
 SPI_ReadRegister(hdev->hspi, hdev->acc_cs_port, hdev->acc_cs_pin, 0x7E); 
 HAL_Delay(50); 
 
 // 加速度计配置 
 SPI_WriteRegister(hdev->hspi, hdev->acc_cs_port, hdev->acc_cs_pin, 
 BMI088_ACC_CONF, ACC_ODR_1000HZ); 
 SPI_WriteRegister(hdev->hspi, hdev->acc_cs_port, hdev->acc_cs_pin, 
 BMI088_ACC_RANGE, ACC_RANGE_24G); 
 SPI_WriteRegister(hdev->hspi, hdev->acc_cs_port, hdev->acc_cs_pin, 
 BMI088_ACC_FIFO_CONFIG, 0x01); // FIFO流模式 
 
 // 陀螺仪配置 
 SPI_WriteRegister(hdev->hspi, hdev->gyr_cs_port, hdev->gyr_cs_pin, 
 BMI088_GYR_RANGE, GYRO_RANGE_2000DPS); 
 SPI_WriteRegister(hdev->hspi, hdev->gyr_cs_port, hdev->gyr_cs_pin, 
 BMI088_GYR_BANDWIDTH, (GYRO_ODR_2000HZ << 4) | GYRO_BW_532HZ); 
}
 
void BMI088_ReadFIFO(BMI088_HandleTypeDef *hdev, int16_t *acc, int16_t *gyro) 
{
 uint8_t buffer[12]; 
 
 // 读取加速度计数据 
 uint8_t reg = BMI088_FIFO_LENGTH | 0x80; 
 HAL_GPIO_WritePin(hdev->acc_cs_port, hdev->acc_cs_pin, GPIO_PIN_RESET); 
 HAL_SPI_Transmit(hdev->hspi, &reg, 1, 100); 
 HAL_SPI_Receive(hdev->hspi, buffer, 6, 100); // 包含伪字节[9] 
 acc[0] = (buffer[1] << 8) | buffer[2]; 
 acc[1] = (buffer[3] << 8) | buffer[4]; 
 acc[2] = (buffer[5] << 8) | buffer[6]; 
 HAL_GPIO_WritePin(hdev->acc_cs_port, hdev->acc_cs_pin, GPIO_PIN_SET); 
 
 // 读取陀螺仪数据 
 reg = 0x02 | 0x80; 
 HAL_GPIO_WritePin(hdev->gyr_cs_port, hdev->gyr_cs_pin, GPIO_PIN_RESET); 
 HAL_SPI_Transmit(hdev->hspi, &reg, 1, 100); 
 HAL_SPI_Receive(hdev->hspi, buffer, 6, 100); 
 gyro[0] = (buffer[0] << 8) | buffer[1]; 
 gyro[1] = (buffer[2] << 8) | buffer[3]; 
 gyro[2] = (buffer[4] << 8) | buffer[5]; 
 HAL_GPIO_WritePin(hdev->gyr_cs_port, hdev->gyr_cs_pin, GPIO_PIN_SET); 
}

示例文件:

/* main.c 示例 */ 
#include "main.h" 
#include "bmi088.h" 
 
BMI088_HandleTypeDef hbmi = { 
 .hspi = &hspi1, 
 .acc_cs_port = GPIOB, 
 .acc_cs_pin = GPIO_PIN_0, 
 .gyr_cs_port = GPIOB, 
 .gyr_cs_pin = GPIO_PIN_3 
};
 
int main(void) 
{
 HAL_Init(); 
 SystemClock_Config(); 
 MX_SPI1_Init(); 
 
 BMI088_Init(&hbmi); 
 
 int16_t acc[3], gyro[3]; 
 while(1) { 
 BMI088_ReadFIFO(&hbmi, acc, gyro); 
 
 // 转换为实际物理量(示例) 
 float acc_x = acc[0] * 24.0f / 32768.0f; 
 float gyro_x = gyro[0] * 2000.0f / 32768.0f; 
 
 printf("Acc: %.2fg\tGyro: %.2fdps\r\n", acc_x, gyro_x); 
 HAL_Delay(2); // 500Hz采样率 
 } 
}

以上生成的程序为使用FIFO机制时,为了减小程序调试难度,令DP重新生成不使用FIFO机制时程序代码,提示词如下:

请再给出不使用fifo机制 时候完整的数据读取代码

DP生成代码如下:

 /* 修改后的bmi088.h */
// 添加数据寄存器定义
#define BMI088_ACC_X_LSB 0x12
#define BMI088_ACC_Y_LSB 0x14
#define BMI088_ACC_Z_LSB 0x16
#define BMI088_GYR_X_LSB 0x02
// 新增函数声明
void BMI088_ReadRawData(BMI088_HandleTypeDef *hdev, int16_t *acc, int16_t *gyro);

.c 文件

/* 修改后的bmi088.c */
void BMI088_Init(BMI088_HandleTypeDef *hdev)
{
 // ...保持之前的配置... 
 // 禁用加速度计FIFO(修改此行) 
 SPI_WriteRegister(hdev->hspi, hdev->acc_cs_port, hdev->acc_cs_pin, 
 BMI088_ACC_FIFO_CONFIG, 0x00); // 关闭FIFO 
}

新增直接从寄存器读取acc及gyro

void BMI088_ReadRawData(BMI088_HandleTypeDef *hdev, int16_t *acc, int16_t *gyro)
{
 uint8_t buffer[6]; 
 
 // 读取加速度计数据(0x12~0x17) 
 HAL_GPIO_WritePin(hdev->acc_cs_port, hdev->acc_cs_pin, GPIO_PIN_RESET); 
 uint8_t reg = BMI088_ACC_X_LSB | 0x80; // 读操作标记 
 HAL_SPI_Transmit(hdev->hspi, &reg, 1, 100); 
 HAL_SPI_Receive(hdev->hspi, buffer, 6, 100); 
 HAL_GPIO_WritePin(hdev->acc_cs_port, hdev->acc_cs_pin, GPIO_PIN_SET); 
 
 acc[0] = (int16_t)((buffer[1] << 8) | buffer[0]); // X轴 
 acc[1] = (int16_t)((buffer[3] << 8) | buffer[2]); // Y轴 
 acc[2] = (int16_t)((buffer[5] << 8) | buffer[4]); // Z轴 
 
 // 读取陀螺仪数据(0x02~0x07) 
 HAL_GPIO_WritePin(hdev->gyr_cs_port, hdev->gyr_cs_pin, GPIO_PIN_RESET); 
 reg = BMI088_GYR_X_LSB | 0x80; 
 HAL_SPI_Transmit(hdev->hspi, &reg, 1, 100); 
 HAL_SPI_Receive(hdev->hspi, buffer, 6, 100); 
 HAL_GPIO_WritePin(hdev->gyr_cs_port, hdev->gyr_cs_pin, GPIO_PIN_SET); 
 
 gyro[0] = (int16_t)((buffer[1] << 8) | buffer[0]); // X轴 
 gyro[1] = (int16_t)((buffer[3] << 8) | buffer[2]); // Y轴 
 gyro[2] = (int16_t)((buffer[5] << 8) | buffer[4]); // Z轴 
}

修改后的main.c

int main(void)
{
 // ...初始化部分保持不变... 
 
 int16_t acc[3], gyro[3]; 
 while(1) { 
 BMI088_ReadRawData(&hbmi, acc, gyro); // 改为调用新函数 
 
 // 数据转换(注意符号处理) 
 float acc_x = (int16_t)acc[0] * 24.0f / 32768.0f; 
 float gyro_x = (int16_t)gyro[0] * 2000.0f / 32768.0f; 
 
 printf("Acc: %.2fg\tGyro: %.2fdps\r\n", acc_x, gyro_x); 
 HAL_Delay(1); // 可提升到1ms间隔(1000Hz) 
 } 
}

将代码移植到PH47框架后,编译烧写运行。接着就出现了一个比一个诡异的问题:

1、缝合怪BMI088输出了陀螺仪数据,加速度计输出数据始终为零。

陀螺仪输出正常数据,至少说明缝合怪的陀螺仪部分、以及mcu的spi接口、以及PH47框架部分没有问题,bug应当是在缝合怪的加速度计部分。

向DP再次提问:

有陀螺数据,但是没有加速度数据。可以确认是软件代码出错了,那具体是什么地方出问题了?

DP给出几种可能:

1. SPI模式切换问题 (关键问题)

// 原代码中的伪读操作需要修正: 
// 正确操作应写入0x0B到ACC_PWR_CTRL寄存器 
SPI_WriteRegister(hdev->hspi, hdev->acc_cs_port, hdev->acc_cs_pin, 0x7C, 0x00); // 解锁配置 
SPI_WriteRegister(hdev->hspi, hdev->acc_cs_port, hdev->acc_cs_pin, 0x7E, 0x0B); // 切换至SPI模式 
HAL_Delay(50);

错误现象:加速度计始终处于I2C模式,无法响应SPI命令

解决方案:增加电源控制寄存器的配置流程

2. 加速度计电源管理配置

// 添加电源控制配置(必须步骤) 
SPI_WriteRegister(hdev->hspi, hdev->acc_cs_port, hdev->acc_cs_pin, 0x7D, 0x04); // 上电加速度计 
HAL_Delay(10);

错误现象:加速度计处于休眠模式。手册要求:必须配置0x7D寄存器为0x04才能激活

3. 数据寄存器地址错误......

4. 字节顺序处理错误……

5. SPI时序模式不匹配……

6. 状态寄存器检测(调试建议)......

经对照BMI088 芯片手册以及先前驱动代码,怀疑为加速度计电源未打开。随后在芯片的初始化函数中加入如下代码(已经做了适应PH47框架的修改):

// Accelerometer on
SPI_WriteRegister(_hImuDev.hspi, _hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, REG_ACC_PWR_CTRL, 0x04); 

编译烧写运行,好消息是加速度计开始输出数据了。说BMI088是个缝合怪一点都不过吧,同一个芯片,Acc需要打开电源操作,Gyro就不需要,不是缝合怪是啥?

坏消息是,输出Acc数据不对,是个随机值,随机值!

2、加速度计输出数据为随机值问题

此bug一般要么为lsb数据处理有问题,要么是spi接口设置有问题。经检查,加速度从lsb处理为m/s2 单位,计算过程没有问题。那么估计问题出在spi接口方面(这个问题很诡异,上一次写驱动的时候就栽在这里了)。

追问DP,给出以下可能:

2.1、伪数据处理不当

BMI088加速度计读取时存在"首字节伪数据"机制。若未正确跳过首字节,会导致后续数据解析错误:

 // 错误示例(未处理伪数据) 
 HAL_SPI_Receive(hspi, buffer, 6, 100); // 直接读取6字节 
 // 正确方法(读取7字节并舍弃首字节) 
 uint8_t dummy; 
 HAL_SPI_Receive(hspi, &dummy, 1, 100); // 舍弃伪数据 
 HAL_SPI_Receive(hspi, buffer, 6, 100); // 读取有效数据 

典型症状:数据呈现周期性跳跃(如±2000 LSB的突变)

2.2、字节序与符号处理错误......

2.3、机械振动干扰......

2.4、温度漂移未补偿......

2.5、电源噪声干扰......

2.6、SPI信号完整性问题......

2.7、滤波器参数不匹配......

2.8、量程与动态范围不匹配......

2.9、校准与算法缺陷......

bug有很多种可能,只能逐一排查,“1、伪数据处理不当”嫌疑很大,DP也指出在芯片手册中有专门提醒:

既然如此,按照DP的给出的方法修改代码,奇迹出现了,缝合怪开始稳定输出正确的加速度数据。修改后程序代码如下:

void CBMI088_DP::BMI088_ReadRawData(int16_t *acc, int16_t *gyro)
{
 uint8_t dummy; // 修改代码
 uint8_t buffer[6]; 
 
 // 读取加速度计数据(0x12~0x17) 
 HAL_GPIO_WritePin(_hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, GPIO_PIN_RESET); 
 
 uint8_t reg = BMI088_ACC_X_LSB | 0x80; // 读操作标记 
 HAL_SPI_Transmit(_hImuDev.hspi, &reg, 1, 10); 
 HAL_SPI_Receive(_hImuDev.hspi, &dummy, 1, 10); // 修改代码,Acc需要读取1个伪字节
 HAL_SPI_Receive(_hImuDev.hspi, buffer, 6, 10); 
 HAL_GPIO_WritePin(_hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, GPIO_PIN_SET); 
 
 acc[0] = (int16_t)((buffer[1] << 8) | buffer[0]); // X轴 
 acc[1] = (int16_t)((buffer[3] << 8) | buffer[2]); // Y轴 
 acc[2] = (int16_t)((buffer[5] << 8) | buffer[4]); // Z轴 
 
 // 读取陀螺仪数据(0x02~0x07) 
 HAL_GPIO_WritePin(_hImuDev.gyr_cs_port, _hImuDev.gyr_cs_pin, GPIO_PIN_RESET); 
 reg = BMI088_GYR_X_LSB | 0x80; 
 HAL_SPI_Transmit(_hImuDev.hspi, &reg, 1, 10); 
 HAL_SPI_Receive(_hImuDev.hspi, buffer, 6, 10); 
 HAL_GPIO_WritePin(_hImuDev.gyr_cs_port, _hImuDev.gyr_cs_pin, GPIO_PIN_SET); 
 
 gyro[0] = (int16_t)((buffer[1] << 8) | buffer[0]); // X轴 
 gyro[1] = (int16_t)((buffer[3] << 8) | buffer[2]); // Y轴 
 gyro[2] = (int16_t)((buffer[5] << 8) | buffer[4]); // Z轴 
}

你就说这缝合怪坑不坑吧,一个加速度计整出这么多幺蛾子出来。相比之下,陀螺仪就是个超级正常的乖宝宝。

然额,不出意外的又出意外了。偶然发现,缝合怪在keil的debug模式下加速度计输出数据正常,而在脱离了keil的调试环境的正常运行条件下,加速度计输出数据又双叒叕错了,输出数值大概是正常数值的4倍左右(Acc 怎么又是你?!)

3、加速度计输出数据在脱离keil 的Debug环境下输出数据错误问题

这个问题再次问了DP,DP也麻了,胡说八道了一大通没啥参考价值。

按经验,spi输出数据比正常值大了2的N次方倍,很有可能是数据错误移位了,具体可能是spi口的CPOA与CPOL设置错误了。但是经过反复修改设置测试,故障依旧。无奈之下,按照遇事不决再啃手册的金科玉律,又仔细看了缝合怪的datasheet,发现有这么个东西:

尤其是reset value为suspend mode显得更加可疑,在缝合怪BMI088的初始化函数中加入如下代码,在初始化过程中将acc从suspend挂起状态切换到激活状态:

// 必须加入该段代码,否则在debug模式下数据正常,普通运行模式下输出数据比正常模式大4倍
// Switch to Active mode, default is suspend mode
SPI_WriteRegister(_hImuDev.hspi, _hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, REG_ACC_PWR_CONFIG, 0x00); 
HAL_Delay(2);

编译运行,一切都正常了,缝合怪终于不作妖了。BMI088输出的acc,gyro数据一切正常了。至此,使用DP生成BMI088驱动代码的工作初步完成。至于BMI088的高级功能,如fifo及DMA机制的使用,感兴趣的同学可进一步尝试。至于为何在keil的debug模式下不需要上述操作即可正常工作,整不明白,明白的同学请多指教。

4、总结,仅就这一次测试得出DP生成代码的评价:

优点:

  • 生成代码非常简洁、耦合度低、阅读理解的难度极低。
  • 与此同时代码完整程度较高,包含了常量定义、驱动代码、示例代码等,基本到了拿来即可用的地步。
  • 能够按照指定的软硬件条件精确生成代码;
  • 可以辅助debug,能够提出有价值的观点。

缺点:

  • 首次生成的代码是存在错误不能运行的。经过后续追问,可以给出包含了正确解决途径在内的多个建议,但需要人工进行甄别(那你为啥不一开始就给出正确代码呢,你个浓眉大眼的DP也学会摸鱼了??)

最重要的事情,说三遍!

使用DP之前,要对芯片的datasheet有个初步但清楚地了解,这样才能有效的引导DP扮演好初级程序员的角色,而不是被它带偏。在此前提之下,DP应该是一个合格的初级程序员。

 


以下是PH47框架下BMI088驱动的完整代码:

BMI088_DP.h 文件

#ifndef __BMI088_DP_H__
#define __BMI088_DP_H__

/************************************************
  - Lib safe include (.h)
  - ReadOnly to user
  - All LIBs must be compiled after modify
*************************************************/

#include "./ImuPort.h"

// 寄存器地址定义 
#define BMI088_ACC_CHIP_ID      0x00 
#define BMI088_ACC_CONF         0x40 
#define BMI088_ACC_RANGE        0x41 
#define BMI088_ACC_FIFO_CONFIG  0x48 
#define BMI088_GYR_CHIP_ID      0x00 
#define BMI088_GYR_RANGE        0x0F 
#define BMI088_GYR_BANDWIDTH    0x10 
#define BMI088_FIFO_LENGTH      0x22 

#define REG_ACC_PWR_CONFIG	0x7C			// My code, not AI
#define REG_ACC_PWR_CTRL	0x7D			// My code, not AI
#define REG_ACC_SOFT_RESET	0x7E			

// 添加数据寄存器定义
#define BMI088_ACC_X_LSB       0x12
#define BMI088_ACC_Y_LSB       0x14
#define BMI088_ACC_Z_LSB       0x16
#define BMI088_GYR_X_LSB       0x02

// 传感器配置参数 
#define ACC_ODR_1000HZ   0x05 
#define ACC_RANGE_24G    0x03 
#define GYRO_ODR_2000HZ  0x07 
#define GYRO_RANGE_2000DPS 0x00 
#define GYRO_BW_532HZ    0x04 
#define CALL_BMI088_DEBUG		11

typedef struct 
{ 
	SPI_HandleTypeDef *hspi; 
	GPIO_TypeDef *acc_cs_port; 
	uint16_t acc_cs_pin; 
	GPIO_TypeDef *gyr_cs_port; 
	uint16_t gyr_cs_pin; 
} BMI088_HandleTypeDef; 

struct DAT_OUT_BMI088
{
	Vector3f vAccel;
	Vector3f vGyro;
};

class CBMI088_DP : public CImu_Port
{
public:
	CBMI088_DP() {};

	virtual void	Init(GPIO_TypeDef *pGPIOx_CS, uint32_t uPinMask_CS);
	virtual uint8_t Update();
	virtual void*   Function(uint8_t uCallType, void *pDataIn);
	virtual uint8_t ReadID(void);
	 
private:
	void	SPI_WriteRegister(SPI_HandleTypeDef *hspi, GPIO_TypeDef* cs_port, uint16_t cs_pin, uint8_t reg, uint8_t data);
	uint8_t SPI_ReadRegister(SPI_HandleTypeDef *hspi, GPIO_TypeDef* cs_port, uint16_t cs_pin, uint8_t reg);
	void	BMI088_ReadRawData(int16_t *acc, int16_t *gyro);
	void	BMI088_ReadFIFO(int16_t *acc, int16_t *gyro);

	float	lsb_to_mps2(int16_t val, int8_t g_range, uint8_t bit_width);

	BMI088_HandleTypeDef _hImuDev;
	DAT_OUT_BMI088		 _sImuDat;
};
#endif

BMI088_DP.cpp 文件:

/************************************************
  - Lib safe include (.h)
  - ReadOnly to user
  - All LIBs must be compiled after modify
*************************************************/

#include "./BMI088_DP.h"
#include "../McuDev.h"
extern CMcuDev &mcu;

#include "../../Frame/Core/FrameCore.h"
extern CFrameCore &core;

#include "../../../BBP/Inc/main.h"			// Addtional code
#include "../../../bbp/Inc/spi.h"

void CBMI088_DP::Init(GPIO_TypeDef *pGPIOx_CS, uint32_t uPinMask_CS)
{
	// _hImuDev 初始化
	_hImuDev.hspi = &(hspi2);			// 临时措施
	// Acc CS: PB12
	_hImuDev.acc_cs_port = CS_SPI2_IMU_GPIO_Port;
	_hImuDev.acc_cs_pin  = CS_SPI2_IMU_Pin;

	// Gyr CS: PA15
	_hImuDev.gyr_cs_port = CS2_SPI2_IMU_GPIO_Port;
	_hImuDev.gyr_cs_pin  = CS2_SPI2_IMU_Pin;
	
	// 加速度计SPI模式切换(伪读操作) 
	SPI_ReadRegister(_hImuDev.hspi, _hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, REG_ACC_SOFT_RESET);			// 
	HAL_Delay(20); 

	// My code, not AI
	// 必须加入该段代码,否则在debug模式下输出数据比正常模式大4倍
	SPI_WriteRegister(_hImuDev.hspi, _hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, REG_ACC_PWR_CONFIG, 0x00);		// Switch to Active mode, default is suspend mode
	HAL_Delay(2);

	// AI sugest
	SPI_WriteRegister(_hImuDev.hspi, _hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, REG_ACC_PWR_CTRL, 0x04);		// Accelerometer on
	gDelay_us(500);

	// 加速度计配置 
	SPI_WriteRegister(_hImuDev.hspi, _hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, BMI088_ACC_CONF, ACC_ODR_1000HZ); 
	SPI_WriteRegister(_hImuDev.hspi, _hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, BMI088_ACC_RANGE, ACC_RANGE_24G); 

	//SPI_WriteRegister(_hImuDev.hspi, _hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, BMI088_ACC_FIFO_CONFIG, 0x01); // FIFO流模式 
	SPI_WriteRegister(_hImuDev.hspi, _hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, BMI088_ACC_FIFO_CONFIG, 0x00); // 关闭FIFO 

	// 陀螺仪配置 
	SPI_WriteRegister(_hImuDev.hspi, _hImuDev.gyr_cs_port, _hImuDev.gyr_cs_pin, BMI088_GYR_RANGE, GYRO_RANGE_2000DPS); 
	SPI_WriteRegister(_hImuDev.hspi, _hImuDev.gyr_cs_port, _hImuDev.gyr_cs_pin, BMI088_GYR_BANDWIDTH, (GYRO_ODR_2000HZ << 4) | GYRO_BW_532HZ); 
}

uint8_t CBMI088_DP::ReadID(void)
{
	uint8_t uID = SPI_ReadRegister(_hImuDev.hspi, _hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, BMI088_ACC_CHIP_ID); 
	return uID;
}

void* CBMI088_DP::Function(uint8_t uCallType, void *pDataIn)
{
	void *pRet = NULL;

	if(uCallType == GET_DATA_OUT_BMI088)
	{
		pRet = (void *) (&_sImuDat);
	}
	else if(uCallType == CALL_BMI088_DEBUG)
	{
		int16_t acc[3], gyro[3]; 
		BMI088_ReadRawData(acc, gyro); // 改为调用新函数 

		// 数据转换(注意符号处理) 
		_sImuDat.vAccel.x = (int16_t)acc[0] * 24.0f * GRAVITY_MSS / 32768.0f; 
		_sImuDat.vAccel.y = (int16_t)acc[1] * 24.0f * GRAVITY_MSS / 32768.0f; 
		_sImuDat.vAccel.z = (int16_t)acc[2] * 24.0f * GRAVITY_MSS / 32768.0f; 
	
	/*
		float acc_x = lsb_to_mps2(acc[0], 24, 16);
		float acc_y = lsb_to_mps2(acc[1], 24, 16);
		float acc_z = lsb_to_mps2(acc[2], 24, 16);	*/

		_sImuDat.vGyro.x = (int16_t)gyro[0] * 2000.0f / 32768.0f; 
		_sImuDat.vGyro.y = (int16_t)gyro[1] * 2000.0f / 32768.0f; 
		_sImuDat.vGyro.z = (int16_t)gyro[2] * 2000.0f / 32768.0f; 

		TRACE("\r\n> Acc: %.2f %.2f %.2f m/s2", _sImuDat.vAccel.x, _sImuDat.vAccel.y, _sImuDat.vAccel.z); 
		TRACE("\r\n> Gyr: %.2f %.2f %.2f dps",  _sImuDat.vGyro.x, _sImuDat.vGyro.y, _sImuDat.vGyro.z); 
	}

	return pRet;
}

uint8_t CBMI088_DP::Update()
{
	if(_hImuDev.hspi == NULL)
		return 0;

	int16_t acc[3], gyro[3]; 
	BMI088_ReadRawData(acc, gyro); // 改为调用新函数 

	// Accel
	_sImuDat.vAccel.x = (int16_t)acc[0]  * 24.0f * GRAVITY_MSS / 32768.0f; 
	_sImuDat.vAccel.y = (int16_t)acc[1]  * 24.0f * GRAVITY_MSS / 32768.0f; 
	_sImuDat.vAccel.z = (int16_t)acc[2]  * 24.0f * GRAVITY_MSS / 32768.0f; 

	// Gyro
	_sImuDat.vGyro.x = (int16_t)gyro[0] * 2000.0f / 32768.0f; 
	_sImuDat.vGyro.y = (int16_t)gyro[1] * 2000.0f / 32768.0f; 
	_sImuDat.vGyro.z = (int16_t)gyro[2] * 2000.0f / 32768.0f; 
	
	return 1;
}

void CBMI088_DP::SPI_WriteRegister(SPI_HandleTypeDef *hspi, GPIO_TypeDef* cs_port, uint16_t cs_pin, uint8_t reg, uint8_t data) 
{
	uint8_t tx[2] = {reg & 0x7F, data}; 
	HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_RESET); 
	HAL_SPI_Transmit(hspi, tx, 2, 100); 
	HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_SET); 
}

uint8_t CBMI088_DP::SPI_ReadRegister(SPI_HandleTypeDef *hspi, GPIO_TypeDef* cs_port, uint16_t cs_pin, uint8_t reg) 
{
	uint8_t tx = reg | 0x80; 
	uint8_t rx[2] = {0}; 
	HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_RESET); 
	HAL_SPI_TransmitReceive(hspi, &tx, rx, 2, 100); 
	HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_SET); 
	return rx[1]; 
}

void CBMI088_DP::BMI088_ReadRawData(int16_t *acc, int16_t *gyro)
{
	uint8_t dummy;
	uint8_t buffer[6]; 

	uint32_t uPrev = gGetMicros();

	// 读取加速度计数据(0x12~0x17) 
	HAL_GPIO_WritePin(_hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, GPIO_PIN_RESET); 

	uint8_t reg = BMI088_ACC_X_LSB | 0x80; // 读操作标记 
	HAL_SPI_Transmit(_hImuDev.hspi, &reg, 1, 10); 
	HAL_SPI_Receive(_hImuDev.hspi, &dummy, 1, 10);	// 读取1个伪字节
	HAL_SPI_Receive(_hImuDev.hspi, buffer, 6, 100); 
	HAL_GPIO_WritePin(_hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, GPIO_PIN_SET); 

	acc[0] = (int16_t)((buffer[1] << 8) | buffer[0]); // X轴 
	acc[1] = (int16_t)((buffer[3] << 8) | buffer[2]); // Y轴 
	acc[2] = (int16_t)((buffer[5] << 8) | buffer[4]); // Z轴 

	// 读取陀螺仪数据(0x02~0x07) 
	HAL_GPIO_WritePin(_hImuDev.gyr_cs_port, _hImuDev.gyr_cs_pin, GPIO_PIN_RESET); 
	reg = BMI088_GYR_X_LSB | 0x80; 
	HAL_SPI_Transmit(_hImuDev.hspi, &reg, 1, 100); 
	HAL_SPI_Receive(_hImuDev.hspi, buffer, 6, 100); 
	HAL_GPIO_WritePin(_hImuDev.gyr_cs_port, _hImuDev.gyr_cs_pin, GPIO_PIN_SET); 

	gyro[0] = (int16_t)((buffer[1] << 8) | buffer[0]); // X轴 
	gyro[1] = (int16_t)((buffer[3] << 8) | buffer[2]); // Y轴  
	gyro[2] = (int16_t)((buffer[5] << 8) | buffer[4]); // Z轴 
}

void CBMI088_DP::BMI088_ReadFIFO(int16_t *acc, int16_t *gyro) 
{
	uint8_t buffer[12]; 

	// 读取加速度计数据 
	uint8_t reg = BMI088_FIFO_LENGTH | 0x80; 
	HAL_GPIO_WritePin(_hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, GPIO_PIN_RESET); 
	HAL_SPI_Transmit(_hImuDev.hspi, &reg, 1, 100); 
	HAL_SPI_Receive(_hImuDev.hspi, buffer, 6, 100); // 包含伪字节[9] 
	acc[0] = (buffer[1] << 8) | buffer[2]; 
	acc[1] = (buffer[3] << 8) | buffer[4]; 
	acc[2] = (buffer[5] << 8) | buffer[6]; 
	HAL_GPIO_WritePin(_hImuDev.acc_cs_port, _hImuDev.acc_cs_pin, GPIO_PIN_SET); 

	// 读取陀螺仪数据 
	reg = 0x02 | 0x80; 
	HAL_GPIO_WritePin(_hImuDev.gyr_cs_port, _hImuDev.gyr_cs_pin, GPIO_PIN_RESET); 
	HAL_SPI_Transmit(_hImuDev.hspi, &reg, 1, 100); 
	HAL_SPI_Receive(_hImuDev.hspi, buffer, 6, 100); 
	gyro[0] = (buffer[0] << 8) | buffer[1]; 
	gyro[1] = (buffer[2] << 8) | buffer[3]; 
	gyro[2] = (buffer[4] << 8) | buffer[5]; 
	HAL_GPIO_WritePin(_hImuDev.gyr_cs_port, _hImuDev.gyr_cs_pin, GPIO_PIN_SET); 
}

float CBMI088_DP::lsb_to_mps2(int16_t val, int8_t g_range, uint8_t bit_width)
{
	float gravity;
	float half_scale = ((1 << bit_width) / 2.0f);
	gravity = (float)((GRAVITY_MSS * val * g_range) / half_scale);
	return gravity;
}
Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐