前言
众所周知啊,MPU6050是一个在硬件上使用非常广泛的IC,在我们的使用中,MPU6050是一个不可缺少的东西,特别是四轴。
我们一般都是用软件模拟IIC与MPU6050进行通信,但是读到的是它的6个原始数据,这些数据对于我们初学的人来说用处不大,所以我们一般还要将其进行滤波,四元数转换等等操作得到其三个角(航向角(yaw)、横滚角(roll)和俯仰角(pitch)),这三者就能够表示当前设备的姿态。MPU6050自带DMP解算库,可以直接将我们的原始数据转换成欧拉角,但是他是非开源的,并且还是针对MSP430的,移植起来的有点难度,所以我把我的移植过程记录下来,给以后的我,大家一个参考。
正题
MPU6050只能支持IIC通信,所以我们用STM32F103的IIC模式,在STM32CUBEMX中初始化一些相关的外设,本文章用的是STM32F103C8T6,DMP的相关文件来自于正点原子的DMP文件。
开启外部晶振(外部晶振的精度更高并且受温度影响较小)
打开仿真调试IO口,这里我们选择SWD接口,引脚少,并且速度比JTAG快
IIC我们选择CUBEMX自带的硬件IIC,很多人说ST为了规避专利把硬件IIC设计得很难用,但我个人觉得还行。这里我们选择IIC即可,第一个参数I2C speed Mode我们选择FAST MODE,即40000HZ,否则MPU6050会有几率不通过自检。
如果还要加一些其他的外设功能的话自行设定即可。
串口1,参数默认即可
时钟我们配置成常规的即可
最后点击GNENERATE CODE,生成初始化代码。
在生成的文件中,我们需要添加需要用的函数,MPU6050,DMP相关的文件。
这三个文件中,MPU6050是我们此次需要关注的(另外两个是我做电机控制用的,如果您只需要移植MOU6050,则不需要关心这两个文件)
这几个文件中eMPL是官方给的DMP库,移植时可以直接输出姿态角,而mpu6050.c函数是读取加速度计和陀螺仪计的原始值的,如果不需要原始数据的话我们可以不需要将mpu6050.c和mou6050.h移植过来。
这六个文件是我们此次需要重点关注的对象
进来我们先把相关的文件加进来
添加路径
添加头文件
#include "oled.h"
#include "mpu6050.h"
#include "control.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
添加变量
float pitch,roll,yaw; //欧拉角
short aacx,aacy,aacz; //加速度传感器原始数据
short gyrox,gyroy,gyroz; //陀螺仪原始数据
short temp; //温度
在相关初始化函数定义之后加上如下代码
while(mpu_dmp_init())
{
HAL_Delay(200);
printf("%s\r\n","Mpu6050_DMP Init Wrong!");
printf(" %d",mpu_dmp_init());
}
printf("%s\r\n","DMP_Mpu6050 Init OK!");
while(1)
{
if(mpu_dmp_get_data(&pitch,&roll,&yaw)==0)
{
// temp=MPU_Get_Temperature(); //得到温度值
// MPU_Get_Accelerometer(&aacx,&aacy,&aacz); //得到加速度传感器数据
// MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz); //得到陀螺仪数据
printf("三轴角度:%f-%f-%f\r\n",pitch,roll,yaw);
// printf("三轴加速度:%d-%d-%d\r\n",aacx,aacy,aacz);
// printf("三轴角角度:%d-%d-%d\r\n",gyrox,gyroy,gyroz);
}
delay_ms(100);
在usart.c文件中配置串口输出重定向
/* USER CODE BEGIN 1 */
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
/**
* 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart3, &ch, 1, 0xffff);
return ch;
}
/* USER CODE END 1 */
并在头文件中声明,这样子就不会有prinf未定义警告了。
接下来我们来说说两个重要的文件,"inv_mpu.c"和include "inv_mpu_dmp_motion_driver.c"
有几处重要的地方需要我们修改
nv_mpu.c
1.头文件
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#include "mpu6050.h"
#include "delay.h"
#include "usart.h"
其中,HAL库中#include "delay.h"是需要自己定义添加的,他在正点原子中就是定义了三个函数
void delay_init(u8 SYSCLK); void delay_ms(u16 nms); void delay_us(u32 nus);
在#include "inv_mpu.h"中我们只用到了void delay_ms(u16 nms),这个函数HAL库中有一个替代的函数, HAL_Delay(ms),我们直接用他就行了。
现在来到了最重要的一个地方
#define i2c_write MPU_Write_Len
#define i2c_read MPU_Read_Len
#define delay_ms HAL_Delay
#define get_ms mget_ms
这里DMP官方把这个函数封装起来了, MPU_Write_Len,MPU_Read_Len,HAL_Delay,mget_ms。我们需要在IIC中去定义这三个函数(换个说法就是给IIC读写函数换个皮)
/* USER CODE BEGIN 1 */
unsigned char MPU_Write_Len(unsigned char addr,unsigned char reg,unsigned char len,unsigned char *buf)
{
HAL_I2C_Mem_Write(&hi2c1, addr, reg, I2C_MEMADD_SIZE_8BIT, buf, len, 0xfff);
return 0;
}
unsigned char MPU_Read_Len(unsigned char addr,unsigned char reg,unsigned char len,unsigned char *buf)
{
HAL_I2C_Mem_Read(&hi2c1, addr, reg, I2C_MEMADD_SIZE_8BIT, buf, len, 0xfff);
return 0;
}
/* USER CODE END 1 */
HAL_Delay我们用STM32CUBEMX自己定义的函数,这个函数在HAL库中一个弱定义函数,我们可以根据需要自己再另外定义一个同名函数。
#define get_ms mget_ms中 mget_ms是一个空函数,并没有执行任何程序,这里我们无需更改。
接下来我们只要更改 mpu_dmp_init函数即可,这个函数是DMP库对外的接口,这里我们把正点原子中的
MPU_IIC_Init(); //初始化IIC总线
改成
MX_I2C1_Init(); //初始化IIC总线
即可
unsigned char mpu_dmp_init(void)
{
unsigned char res=0;
MX_I2C1_Init(); //初始化IIC总线
if(mpu_init()==0) //初始化MPU6050
{
res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置所需要的传感器
if(res)return 1;
res=mpu_configure_fifo(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置FIFO
if(res)return 2;
res=mpu_set_sample_rate(DEFAULT_MPU_HZ); //设置采样率
if(res)return 3;
res=dmp_load_motion_driver_firmware(); //加载dmp固件
if(res)return 4;
res=dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation));//设置陀螺仪方向
if(res)return 5;
res=dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT|DMP_FEATURE_TAP| //设置dmp功能
DMP_FEATURE_ANDROID_ORIENT|DMP_FEATURE_SEND_RAW_ACCEL|DMP_FEATURE_SEND_CAL_GYRO|
DMP_FEATURE_GYRO_CAL);
if(res)return 6;
res=dmp_set_fifo_rate(DEFAULT_MPU_HZ); //设置DMP输出速率(最大不超过200Hz)
if(res)return 7;
res=run_self_test(); //自检
if(res)return 8;
res=mpu_set_dmp_state(1); //使能DMP
if(res)return 9;
}else return 10;
return 0;
}
这时候我们移植的就差不多了,但是还要注意一些细节问题,比如IIC地址的位对齐问题,在HAL库中,是没有将IIC地址左移的,所以我们需要手动将我们的IIC地址左移一位,因为我的ADO给的低电平,所以地址就是0X68<<1了
const struct hw_s hw={
0x68<<1, //addr
1024, //max_fifo
118, //num_reg
340, //temp_sens
-521, //temp_offset
256 //bank_size
};
很多同学可能就是在这里出了问题,因为原子家的代码是在IIC的函数中将地址左移了,所以我们定义的IIC地址没有做左移位。
而HAL库官方给的硬件IIC读取,写入API是需要左移的,这一点在这个API的注释中也写明了
DevAddress Target device address: The device 7 bits address value in datasheet must be shifted to the left before calling the interface
最后我们就移植的差不多啦,来看看现象。
版权声明:本文为CSDN博主「i是属于」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yz200009058233/article/details/121055958
暂无评论