假期战略更新第二集——MPU6050六轴传感器模块在stm32平台的使用一(今天不学习,明天变垃圾)

China☆狼群 提交于 2020-03-06 08:56:27

这个也是上学期一直困扰我的一个问题,上学期想要巩固一下自己PWM和PID算法的基础,就搞了一个STM32控制的平衡车来玩,算法数学模型啥的都弄好之后,就出现了一个大问题,本人用的是stm32cubeMX来建立的工程项目,所以在IIC的部分就出现了一些问题,我看了写资料,总结了一下经验,下面就先讲讲我的主要经验:

1.MPU6050模块是什么?
MPU6050模块是一个常用的六轴传感器模块,主要目的是获取以传感器为基点的欧拉角(偏航角、俯仰角、滚转角),可以理解为以传感器为中点,初始X正半轴与当前传感器前方所指的一个向量在XY平面的夹角,初始X正半轴与当前传感器在XZ平面的夹角,初始Y正半轴与当前传感器在YZ平面的夹角。这个想仔细理解的玩家朋友可以看看MPU6050的手册。该模块用过IIC总线和STM32进行通信。

2.MPU6050用来干嘛?
在文章开头我说我买了个stm32的平衡车,可以理解为硬件已经完全搭建好了,说到平衡车那就是普通的两个轮子那种,两个轮子不转,车肯定就站不稳,要车站稳的话就肯定要知道车的当前状态和什么情况是稳,什么情况是不稳,所以就用到了MPU6050的俯仰角。

3.MPU6050怎么用?
东西怎么用,还是要看芯片文档,现在就稍微总结一下MPU6050怎么用的,首先是通信方式,芯片把当前数据测量出来之后,就需要通过IIC总线发送给MCU进行处理,这里我用的是STM32F767作为处理芯片,在之前的使用中,我通过使能stm32cube的IIC来移植github上某大神已经写好的MPU6050驱动,发现卡在fifo的频率设置上,频率设置不能超过40,但是这个满足不了小车的稳定,使用上还存在各种问题,所以干脆就用了模拟IIC,使用下来效果不错,所以在MPU6050的问题上我就直接使用模拟IIC了,如果有大神可以用硬件IIC实现的话请不吝赐教。
通信方式解决之后就要知道怎么获取三个角了,首先是MPU6050模块自带了DMP姿态解算,通过MPU官方库的程序读取fifo中的四元数组quat后,将格式转为浮点型,通过除以官方给出的q30格式long转化为float的除数后进行对应运算来得到当前的三个角。
主要使用方法就是在模拟IIC总线调整到可以使用之后,移植官方的DMP库
,官方的MPU6050驱动之后在主函数进行MPU6050以及DMP初始化之后即可通过mpu_dmp_get_data来读取当前三个角的度数。

下面我把我使用的模拟iic的代码贴出来,使用的时候只需要更改c文件和h文件的gpio引脚即可

iicb.c

#include "iicb.h"
static void delay_us(int s)//微秒延时函数,试出来的
{
    volatile int i = 7*s;
    while (i)
        i--;
}
//初始化IIC
void IIC_InitB(void)
{			
     GPIO_InitTypeDef GPIO_Initure; 
    __HAL_RCC_GPIOB_CLK_ENABLE();  
    GPIO_Initure.Pin=GPIO_PIN_6|GPIO_PIN_7;
    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; 
    GPIO_Initure.Pull=GPIO_PULLUP;         
    GPIO_Initure.Speed=GPIO_SPEED_FAST;     
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);
    IIC_SDAB(1);
    IIC_SCLB(1);  
}
//IIC起始信号
void IIC_StartB(void)
{
	SDA_OUTB();
	IIC_SDAB(1);	  	  
	IIC_SCLB(1);
	delay_us(4);
 	IIC_SDAB(0);
	delay_us(4);
	IIC_SCLB(0);
}	  
//IIC停止信号
void IIC_StopB(void)
{
	SDA_OUTB();//sda线输出
	IIC_SCLB(0);
	IIC_SDAB(0);//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCLB(1); 
	IIC_SDAB(1);//发送I2C总线结束信号
	delay_us(4);							   	
}
//等待应答信号到来 1.接收失败,2.成功
uint8_t IIC_Wait_AckB(void)
{
	uint8_t ErrTime=0;
	SDA_INB();      //SDA设置为输入  
	IIC_SDAB(1);delay_us(1);	   
	IIC_SCLB(1);delay_us(1);	 
	while(READ_SDAB)
	{
		ErrTime++;
		if(ErrTime>250)
		{
			IIC_StopB();
			return 1;
		}
	}
	IIC_SCLB(0);	   
	return 0;  
} 
//Ack应答信号
void IIC_AckB(void)
{
	IIC_SCLB(0);
	SDA_OUTB();
	IIC_SDAB(0);
	delay_us(2);
	IIC_SCLB(1);
	delay_us(2);
	IIC_SCLB(0);
}
//Ack拒绝应答		    
void IIC_NAckB(void)
{
	IIC_SCLB(0);
	SDA_OUTB();
	IIC_SDAB(1);
	delay_us(2);
	IIC_SCLB(1);
	delay_us(2);
	IIC_SCLB(0);
}					 				     
//IIC发送一个字节,从机有应答返回1,无应答返回0		  
void IIC_Send_ByteB(uint8_t txb)
{                        
    uint8_t t;   
	SDA_OUTB(); 	    
    IIC_SCLB(0);//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        IIC_SDAB((txb&0x80)>>7);
        txb<<=1; 	  
		delay_us(2);  
		IIC_SCLB(1);
		delay_us(2); 
		IIC_SCLB(0);	
		delay_us(2);
    }	 
} 	    
//读1个字节 ack=1时,发送Ack,ack=0,发送NAck   
uint8_t IIC_Read_ByteB(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_INB();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCLB(0); 
        delay_us(2);
		IIC_SCLB(1);
        receive<<=1;
        if(READ_SDAB)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAckB();//发送NAck
    else
        IIC_AckB(); //发送Ack  
    return receive;
}

iicb.h

#ifndef __IICB_H
#define __IICB_H
#include "main.h"    	   		   
//IO方向设置
#define SDA_INB()  {GPIOB->MODER&=~(3<<(7*2));GPIOB->MODER|=0<<(7*2);}	//PB7输入模式
#define SDA_OUTB() {GPIOB->MODER&=~(3<<(7*2));GPIOB->MODER|=1<<(7*2);} //PB7输出模式
//IO操作
#define IIC_SCLB(n)  (n?HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET)) //SCL
#define IIC_SDAB(n)  (n?HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET)) //SDA
#define READ_SDAB    HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7)  //输入SDA

//IIC所有操作函数
void IIC_InitB(void);                		//初始化IIC的IO口				 
void IIC_StartB(void);						//IIC开始信号
void IIC_StopB(void);	  					//IIC停止信号
void IIC_Send_ByteB(uint8_t txb);			//IIC发送一个字节
uint8_t IIC_Read_ByteB(unsigned char ack);	//IIC读取一个字节
uint8_t IIC_Wait_AckB(void); 				//IIC等待ACK信号
void IIC_AckB(void);						//IIC发送ACK信号
void IIC_NAckB(void);						//IIC不发送ACK信号

void IIC_Write_One_ByteB(uint8_t daddr,uint8_t addr,uint8_t data);
uint8_t IIC_Read_One_ByteB(uint8_t daddr,uint8_t addr);	  
#endif

移植完iic之后记得测试一下再进行下面的操作,具体测试可以用逻辑分析仪搞一下。
IIC测试完成之后就可以将MPU6050的驱动代码拿来用了,用的时候记得把MPU_Write_Byte类似的需要IIC驱动的函数处理一下,使用之前测试好的IIC发送对应的MPU处理函数。
使用模拟IIC的MPU驱动在网上也可以找到,大家就可以自己找找看。
之后在main函数完成MPU6050初始化和DMP的初始化之后就能直接使用mpu_dmp_get_data函数来获取三个角了,下面是我的主函数,写的很简单,仅供参考(代码是使用stm32cube生成的,没有使能硬件iic);

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "mpu6050B.h"
#include "inv_mpuB.h"
#include "inv_mpu_dmp_motion_driverB.h" 
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
	float Pitch=0;
	float Roll=0;
	float Yaw=0;	
	int mpu_work_flag=0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	int t=0;
  /* USER CODE END 1 */
  

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	printf("Gas\r\n");
	printf("%d\r\n",MPU6050_InitB());
	printf("%d\r\n",mpu_dmp_initB());
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		if(!mpu_dmp_get_dataB(&Pitch,&Roll,&Yaw))
		
		
		printf("%d,%f,%f,%f\r\n",t,Pitch,Roll,Yaw);
		t++;
		HAL_Delay(200);
		HAL_GPIO_TogglePin(GPIOB, LED0_Pin);
  }
  /* USER CODE END 3 */
}
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 288;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART1;
  PeriphClkInitStruct.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

接下来是结果演示,我用了serialchart来把MPU6050采集到的三个角通过串口发送的数据画成图象表示出来了。
serialchart
我均匀转动三个角之后得到了上面的图,红绿蓝三个颜色分别代表三个不同角的角度,由图可以发现三个颜色分别明显的转动了两个周期,可知实验成功。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!