0、写在前面
第一次接触陀螺仪,纯属个人记录,大佬出门右转。
1、构建硬件SPI
直接使用硬件SPI1,引脚需求如下:
配置代码:
void SPI_MPU6500_Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/*SPI时钟*/
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_SPI1, ENABLE );
/*SPI相关引脚时钟*/
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOA, ENABLE );
/*SCK、MISO、MOSI*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*CS*/
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*CS引脚高电平*/
GPIO_SetBits(GPIOA, GPIO_Pin_4);
/*SPI模式配置*/
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
}
这里用的标准库,请自行添加相关头文件。
2、SPI1针对MPU6500的读写函数
//读写函数
u8 SPI1_Read_Write_Byte(u8 TxData)
{
u8 retry = 0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)
{
retry++;
if(retry > 250) return 0;
}
SPI_I2S_SendData(SPI1, TxData); //发送数据到DR寄存器
retry = 0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
{
retry++;
if(retry > 250) return 0;
}
return SPI_I2S_ReceiveData(SPI1); //从DR寄存器接收数据
}
//写一个字节
u8 MPU_Write_Byte(u8 reg,u8 data)
{
u8 status;
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
delay_200ns();
status = SPI1_Read_Write_Byte(reg);
SPI1_Read_Write_Byte(data);
GPIO_SetBits(GPIOA, GPIO_Pin_4);
delay_200ns();
return status;
}
//读一个字节
u8 MPU_Read_Byte(u8 reg)
{
u8 reg_val; //寄存器值
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
delay_200ns();
SPI1_Read_Write_Byte(reg|0x80); //写入寄存器地址,地址最高位置1
reg_val=SPI1_Read_Write_Byte(0xff); //主机发送一个空字节获取寄存器值
GPIO_SetBits(GPIOA, GPIO_Pin_4);
delay_200ns();
return(reg_val);
}
参考链接:MPU6500原始数据读取
3、MPU6500初始化
u8 Mpu_Init(void)
{
delay_200ns();
u8 id = MPU_Read_Byte(MPU6500_WHO_AM_I);
u8 i =0;
u8 MPU6500_Init_Data[10][2] = {{ MPU6500_PWR_MGMT_1 , 0x80 }, /* 重置设备 */
{ MPU6500_PWR_MGMT_1, 0x03 }, /* 时钟源设置 */
{ MPU6500_PWR_MGMT_2, 0x00 }, /* 启动 Acc & Gyro */
{ MPU6500_CONFIG, 0x04 }, /* 低通滤波 频率 41Hz */
{ MPU6500_GYRO_CONFIG, 0x18 }, /* +-2000dps */
{ MPU6500_ACCEL_CONFIG, 0x10 }, /* +-8G */
{ MPU6500_ACCEL_CONFIG_2, 0x02 }, /* 使能低通滤波器 设置 Acc 低通滤波 */
{ MPU6500_USER_CTRL, 0x20 },}; /* 使能 AUX */
for (i = 0; i < 10; i++)
{
MPU_Write_Byte(MPU6500_Init_Data[i][0], MPU6500_Init_Data[i][1]);
delay_200ns();
}
return 0;
}
这里主要关注角速度和加速度的量程(我设置的+-2000dps和+-8G),后面原始数据采集后要进行转换,或者好像直接带入DMP进行姿态解算好像不需要转换数据(我没用过)。
宏定义和结构体声明如下,可以自行根据MPU6500的寄存器映射地址增加。
#define MPU6500_WHO_AM_I 0X75
#define MPU6500_PWR_MGMT_1 0X6B
#define MPU6500_PWR_MGMT_2 0X6C
#define MPU6500_CONFIG 0X1A
#define MPU6500_GYRO_CONFIG 0X1B
#define MPU6500_ACCEL_CONFIG 0X1C
#define MPU6500_ACCEL_CONFIG_2 0X1D
#define MPU6500_USER_CTRL 0X6A
#define MPU6500_ACCEL_XOUT_H 0X3B
#define MPU6500_ACCEL_YOUT_H 0X3D
#define MPU6500_ACCEL_ZOUT_H 0X3F
#define MPU6500_ACCEL_XOUT_L 0X3C
#define MPU6500_ACCEL_YOUT_L 0X3E
#define MPU6500_ACCEL_ZOUT_L 0X40
#define MPU6500_GYRO_XOUT_H 0X43
#define MPU6500_GYRO_YOUT_H 0X45
#define MPU6500_GYRO_ZOUT_H 0X47
#define MPU6500_GYRO_XOUT_L 0X44
#define MPU6500_GYRO_YOUT_L 0X46
#define MPU6500_GYRO_ZOUT_L 0X48
#define MPU6500_TEMP_OUT_H 0X41
#define MPU6500_TEMP_OUT_L 0X42
#define MPU6500_EXT_SENS_DATA_00 0X49
#define MPU6500_EXT_SENS_DATA_01 0X4A
#define MPU6500_EXT_SENS_DATA_02 0X4B
#define MPU6500_EXT_SENS_DATA_03 0X4C
#define MPU6500_EXT_SENS_DATA_04 0X4D
#define MPU6500_EXT_SENS_DATA_05 0X4E
typedef struct
{
float ax;
float ay;
float az;
float gx;
float gy;
float gz;
float temp;
}MPU6500_Data;
extern MPU6500_Data mpu_data;
4、角速度和加速度采集函数
//将两个字节数据处理成字
float Get_16Bit_Data(uint8_t addr_h, uint8_t addr_l)
{
static uint8_t buf[2];
static short data;
buf[0] = MPU_Read_Byte(addr_l);
buf[1] = MPU_Read_Byte(addr_h);
data = (buf[1]<<8)|buf[0];
return data;
}
//读取MPU6500数据
MPU6500_Data mpu_data;
void Get_MPU6500_Data(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_4);
//加速度数据 单位:g的倍数
mpu_data.ax = (float)Get_16Bit_Data(MPU6500_ACCEL_XOUT_H,MPU6500_ACCEL_XOUT_L) / (0xffff/16);
mpu_data.ay = (float)Get_16Bit_Data(MPU6500_ACCEL_YOUT_H,MPU6500_ACCEL_YOUT_L) / (0xffff/16);
mpu_data.az = (float)Get_16Bit_Data(MPU6500_ACCEL_ZOUT_H,MPU6500_ACCEL_ZOUT_L) / (0xffff/16);
//角速度数据 单位:°/s
mpu_data.gx = (float)Get_16Bit_Data(MPU6500_GYRO_XOUT_H,MPU6500_GYRO_XOUT_L) / (0xffff/4000);
mpu_data.gy = (float)Get_16Bit_Data(MPU6500_GYRO_YOUT_H,MPU6500_GYRO_YOUT_L) / (0xffff/4000);
mpu_data.gz = (float)Get_16Bit_Data(MPU6500_GYRO_ZOUT_H,MPU6500_GYRO_ZOUT_L) / (0xffff/4000);
//温度数据
mpu_data.temp = Get_16Bit_Data(MPU6500_TEMP_OUT_H,MPU6500_TEMP_OUT_L)/333.87f +21;
GPIO_SetBits(GPIOA,GPIO_Pin_4);
}
采集的原始数据/(0XFFFF/量程),就是转换后的数据,单位代码写在注释上了。
5、主循环调用
int main(void)
{
systick_Init();
SPI_MPU6500_Init();
Mpu_Init();
while(1)
{
Get_MPU6500_Data();
}
}
systick初始化是用来写延时函数的,这里就不展开啦,百度一大堆。
6、仿真结果
没滤波的数据果然很差,而且实际运用是不是需要校准啊?不太清楚,反正采集数据的目的达到了。(有问题有错误欢迎指出~)