STM32F103HAL库移植MPU6050的DMP库

文章目录[隐藏]

前言

众所周知啊,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

生成海报
点赞 0

i是属于

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

串口不定长接收

一、保留接收区和开启接收的语句    uint8_t buffer[5];HAL_UART_Transmit_IT(&huart1,buffer,3); 二、写入开启空闲中断的语句    __HAL_UART_ENABLE_IT(&huart

基于STM32单片机的电子密码锁设计

一.硬件方案 本设计采用STM32F103C8T6单片机作为主控芯片,结合外围的矩阵按键输入、LCD1602液晶显示、报警、开锁等电路模块实现开锁、上锁、报警、密码更改等功能,设计了一款可以多次修改密码并且具有报警