假期戰略更新第二集——MPU6050六軸傳感器模塊在stm32平臺的使用一(今天不學習,明天變垃圾)

這個也是上學期一直困擾我的一個問題,上學期想要鞏固一下自己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
我均勻轉動三個角之後得到了上面的圖,紅綠藍三個顏色分別代表三個不同角的角度,由圖可以發現三個顏色分別明顯的轉動了兩個週期,可知實驗成功。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章