STM32之MPU6050获取欧拉角

STM32之MPU6050获取欧拉角

  • MPU6050
    • MPU6050特点
    • MPU6050电路图以及框图
      • MPU6050框图
      • MPU6050电路图
    • MPU6050相关寄存器
      • 电源管理寄存器1(0x6B)
      • 陀螺仪配置寄存器(0x1B)
      • 加速度计配置寄存器(0x1C)
      • 陀螺仪采样率分频寄存器(0x19)
      • 配置寄存器
      • 电源管理寄存器2(0x6C)
      • 加速度计数据输出寄存器(0x3B~0x40)
      • 温度传感器数据输出寄存器(0x41~0x42)
      • 陀螺仪数据输出寄存器(0x43~0x48)
      • FIFO使能寄存器
      • 我是谁寄存器
    • MPU6050通信协议
      • MPU6050写入寄存器时序
      • MPU6050寄存器读取时序
  • MPU6050获取三轴加速度、三轴陀螺仪以及温度
    • MPU6050与STM32板子接线
    • STM32CubeMX相关配置
      • 配置SYS
      • 配置RCC
      • 配置USART3
      • 配置NVIC
      • 使用Micro库
    • 文件编写
      • 修改文件usart.c
      • 修改文件usart.h
      • 添加文件mpu6050.c
      • 添加文件mpu6050.h
      • 添加文件delay.c
      • 添加文件delay.h
      • 添加文件my_i2c.c
      • 添加文件my_i2c.h
      • main.c文件编写
  • DMP介绍
    • 使用DMP的目的
    • 使用DMP将MPU6050的原始数据转换成欧拉角
    • 移植DMP库
      • STM32F103系列移植正点原子DMP库
    • 四元数
  • MPU6050获取欧拉角:俯仰角(Pitch)、横滚角(Roll)、偏航角(Yaw)
    • 修改main.c文件
    • 实验效果

MPU6050

在这里插入图片描述

mpu6050 集成了三轴 MEMS 陀螺仪,三轴 MEMS 加速度计,以及一个可扩展的数字运动处理器 DMP(Digital Motion Processor)。辅助I2C接口可以连接一个第三方的数字传感器,比如磁力计。扩展了磁力计之后就可以通过其主要I2C接口输出一个 9 轴的信号(否则只能输出一个6轴的信号)。mpu6050 也可以通过其辅助I2C接口连接非惯性的数字传感器,如压力传感器。总之,mpu6050 就是通过陀螺仪和加速度计来分别测量三轴的角速度和角加速度的数据,继而获得俯仰角(Pitch)、横滚角(Roll)、偏航角(Yaw)。
.

MPU6050特点

  • 以数字形式输出六轴或九轴(需外接磁力传感器)的旋转矩阵、四元数(quaternion)、欧拉角格式(Euler Angle forma)的融合演算数据(需 DMP 支持)。
  • 具有 131 LSB/°/s 敏感度与测量范围为±250、±500、±1000 与±2000 的三轴陀螺仪。
  • 具有可程序控制,范围为±2g、±4g、±8g 和±16g 的三轴加速度计。
  • 自带一个数字温度传感器。
  • 自带数字运动处理(DMP,Digital Motion Processing)引擎可减少 MCU 复杂的融
    合演算数据、感测器同步化、姿势感应等的负荷。
  • 可程序控制的中断(interrupt)。
  • 自带 1024 字节 FIFO,有助于降低系统功耗。
  • 高达 400kHz 的 IIC 通信接口。
  • 数字运动处理(DMP: Digital Motion Processing)引擎可减少复杂的融合演算数据、感测
    器同步化、姿势感应等的负荷。
  • 移除加速器与陀螺仪轴间敏感度,降低设定给予的影响与感测器的飘移。
  • 内建运作时间偏差与磁力感测器校正演算技术。
    .

MPU6050电路图以及框图

MPU6050框图

在这里插入图片描述
.

MPU6050电路图

在这里插入图片描述
.

MPU6050相关寄存器

电源管理寄存器1(0x6B)

在这里插入图片描述
mpu6050 复位,选择时钟源。

  • DEVICE_RESET 位 用于控制复位,设置为 1,复位 mpu6050,复位结束后,MPU 硬件自动清零该位。
  • SLEEEP 位 用于控制 mpu6050 的工作模式,复位后,该位为1,即进入了睡眠模式(低功耗),所以要清零该位,才能进入正常工作模式。
  • CYCLE 位 当该位设置为1并且休眠模式被禁用时,mpu6050 将在休眠模式和唤醒模式之间循环,以电源管理寄存器2的 LP_WAKE_CTRL[1:0] 位确定的速率从加速计中获取单个采样。
  • TEMP_DIS 位 用于设置是否使能温度传感器,设置为 0,则使能。
  • CLKSEL[2:0] 用于选择系统时钟源,选择关系,如下表所示:
CLKSEL[2:0]时钟源
000内部 8M RC 晶振
001PLL,使用 X 轴陀螺仪作为参考
010PLL,使用 Y 轴陀螺仪作为参考
011PLL,使用 Z 轴陀螺仪作为参考
100PLL,使用外部 32.768kHz 时钟作为参考
101PLL,使用外部 19.2MHz 作为参考
110保留
111关闭时钟,保持时序产生电路复位状态
CLKSEL[2:0] 的默认值为 000,即使用内部的 8M RC 晶振作为时钟源,但其精度不高, 因此一般选择 X、Y、Z 轴陀螺仪作为参考的 PLL 作为时钟源。 .

陀螺仪配置寄存器(0x1B)

在这里插入图片描述
配置 mpu6050 陀螺仪的量程范围。

  • 该寄存器,仅需关心 FS_SEL[1:0] 这两个比特位,其他位不用管,设置为0。FS_SEL[1:0] 用于配置陀螺仪的满量程范围,具体的配置描述,如下表所示:
FS_SEL[1:0]陀螺仪满量程范围
00± 250dps
01± 500dps
10± 1000dps
11± 2000dps

一般可以将 FS_SEL[1:0]配置为 11,即配置陀螺仪的满量程范围为±2000dps。
.

加速度计配置寄存器(0x1C)

在这里插入图片描述
配置 mpu6050 加速计的量程范围。

  • 该寄存器,仅需关心 AFS_SEL[1:0] 这两个比特位,其他位不用管,设置为0。AFS_SEL[1:0] 用于配置加速度计的满量程范围,具体的配置描述,如下表所示:
AFS_SEL[1:0]加速度传感器满量程范围
00± 2g
01± 4g
10± 8g
11± 16g
一般可以将 FS_SEL[1:0]配置为 00,即配置加速度传感器的满量程范围为±2g。

.

陀螺仪采样率分频寄存器(0x19)

在这里插入图片描述
配置 mpu6050 陀螺仪采样频率,传感器数据输出和FIFO输出以及DMP采样都是基于这个采样频率。

  • 采样频率 = 陀螺仪输出频率 / (1 + SMPLRT_DIV[7:0] ),陀螺仪输出频率与数字低通滤波器(DLPF)的配置有关,一般为8kH和1kHz。
    .

配置寄存器

在这里插入图片描述
配置 mpu6050 加速度计和陀螺仪的带宽。

  • 该寄存器,仅需关心 DLPF_CFG[2:0] 这三个比特位,其他位不用管,设置为0。DLPF_CFG[2:0] 用来配置加速度计和陀螺仪的带宽,具体的配置描述,如下表所示:
DLPF_CFG[2:0]加速度计(Fs = 1kHz)陀螺仪
带宽(Hz)延迟(ms)带宽(Hz)延迟(ms)Fs(kHz)
000 26002560.988
001 1842.01881.91
010 943.0982.81
011 444.9424.81
100 218.5208.31
101 1013.81013.41
110 519.0518.61
111 保留保留8
一般情况下,配置陀螺仪带宽为陀螺仪采样频率的一半。例如如果陀螺仪采样频率为50Hz,那么带宽就应该设置为25Hz,取近似值20Hz,那么 DLPF_CFG[2:0] 就应该设置为100。

.

电源管理寄存器2(0x6C)

在这里插入图片描述
控制陀螺仪和加速度计是否进入待机模式。

  • LP_WAKE_CTRL[1:0] 位用于控制低功耗时的唤醒频率。
  • STBY_XA 位用于控制加速计X轴是否进入待机模式,设置为1,进入待机模式。
  • STBY_YA 位用于控制加速计Y轴是否进入待机模式,设置为1,进入待机模式。
  • STBY_ZA 位用于控制加速计Z轴是否进入待机模式,设置为1,进入待机模式。
  • STBY_XG 位用于控制陀螺仪X轴是否进入待机模式,设置为1,进入待机模式。
  • STBY_YG 位用于控制陀螺仪Y轴是否进入待机模式,设置为1,进入待机模式。
  • STBY_ZG 位用于控制陀螺仪Z轴是否进入待机模式,设置为1,进入待机模式。
    .

加速度计数据输出寄存器(0x3B~0x40)

在这里插入图片描述
存储加速度计的原始数据,这些原始数据会以陀螺仪的采样频率进行更新。

  • 某个加速度轴的原始数据并不是加速度数据,如果想获得加速度数据需要以下转换:加速度 = (有符号的16位原始数据) / 灵敏度,单位:g(9.8m/s²)
  • 灵敏度根据加速度计的量程变化而变化,如下表所示:
    在这里插入图片描述
    .

温度传感器数据输出寄存器(0x41~0x42)

在这里插入图片描述
存储温度传感器的原始数据,这些原始数据会以陀螺仪的采样频率进行更新。

  • 该原始数据并不是以摄氏度为单位的温度值,如果需要转换成以摄氏度为单位的温度值需要经过以下转换:温度 = (有符号的16位原始数据)/ 340 + 36.53,单位:℃
    .

陀螺仪数据输出寄存器(0x43~0x48)

在这里插入图片描述
存储陀螺仪的原始数据,这些原始数据会以陀螺仪的采样频率进行更新。

  • 某个陀螺仪轴的原始数据并不是陀螺仪数据,如果想获得陀螺仪数据需要以下转换:陀螺仪 = (有符号的16位原始数据) / 灵敏度,单位:°/s
  • 灵敏度根据陀螺仪的量程变化而变化,如下表所示:在这里插入图片描述
    .

FIFO使能寄存器

在这里插入图片描述
控制mpu6050的加速度计、温度传感器、陀螺仪的原始数据是否写入FIFO缓冲区。

  • TEMP_FIFO_EN 位用于控制是否将温度传感器的原始数据写入FIFO缓冲区,设置为 1,写入FIFO缓冲区。
  • XG_ FIFO_EN 位用于控制是否将陀螺仪X轴的原始数据写入FIFO缓冲区,设置为 1,写入FIFO缓冲区。
  • YG_ FIFO_EN 位用于控制是否将陀螺仪Y轴的原始数据写入FIFO缓冲区,设置为 1,写入FIFO缓冲区。
  • ZG_ FIFO_EN 位用于控制是否将陀螺仪Z轴的原始数据写入FIFO缓冲区,设置为 1,写入FIFO缓冲区。
  • ACCEL_ FIFO_EN 位用于控制是否将加速度计的原始数据写入FIFO缓冲区,设置为 1,写入FIFO缓冲区。
  • 其他位不用管,设置为0。
    .

我是谁寄存器

在这里插入图片描述
存储 mpu6050 的7位I2C地址的高6位,用来验证设备的身份。

  • 该寄存器的默认值为0x68(即 b0 110100 0)。
  • mpu6050 的 Slave 地址为 b110100X,7 位字长,最低有效位 X 由 AD0 引脚上的逻辑电平决定,高电平为1,低电平为0。
    .

MPU6050通信协议

当连接到系统芯片时,mpu6050 总是作为从设备,因此,系统芯片可以通过 400kHz 的 I2C 接口来操作 mpu6050 的内部寄存器。mpu6050 的 Slave 地址为 b110100X,7 位字长,最低有效位 X 由 AD0 引脚上的逻辑电平决定,高电平为1,低电平为0(即高电平时 mpu6050 从机地址为0x69,低电平时 mpu6050 从机地址为0x68)。
.

MPU6050写入寄存器时序

单字节写入时序:
在这里插入图片描述
.
多字节写入时序:
在这里插入图片描述
.

  • ①:主设备发送起始信号。
  • ②:主设备发送7位mpu6050的I2C设备地址以及一个W 位,即0 。
  • ③:主设备等待从设备(mpu6050)发送应答信号。
  • ④:主设备发送想要写入寄存器的地址。
  • ⑤:主设备等待从设备(mpu6050)发送应答信号。
  • ⑥:主设备发送想要写入寄存器的数据。
  • ⑦:主设备等待从设备(mpu6050)发送应答信号。
  • ⑧:主设备发送终止信号。
  • 如果想连续写入寄存器数据则在主设备发送终止信号前重复⑥、⑦步骤即可。
    .

MPU6050寄存器读取时序

单字节读取时序:
在这里插入图片描述.
多字节读取时序:
在这里插入图片描述
.

  • ①:主设备发送起始信号。
  • ②:主设备发送7位mpu6050的I2C设备地址以及一个W 位,即0 。
  • ③:主设备等待从设备(mpu6050)发送应答信号。
  • ④:主设备发送想要写入寄存器的地址。
  • ⑤:主设备等待从设备(mpu6050)发送应答信号。
  • ⑥:主设备发送起始信号。
  • ⑦:主设备发送7位mpu6050的I2C设备地址以及一个R 位,即1 。
  • ⑧:主设备等待从设备(mpu6050)发送应答信号。
  • ⑨:主设备读取想要读取寄存器的数据。
  • ⑩:主设备发送应答信号(可选,多字节读取时序时使用)。
  • ⑪:主设备读取想要读取寄存器的数据(可选,多字节读取时序时使用)。
  • ⑫:主设备发送非应答信号。
  • ⑬:主设备发送终止信号。
  • 如果想连续读取寄存器数据则在主设备发送非应答信号和终止信号前重复⑩、⑪步骤即可。
    .

MPU6050获取三轴加速度、三轴陀螺仪以及温度

MPU6050与STM32板子接线

  • PB6 <-> SCL
  • PB7 <-> SDA
  • 单片机5V <-> VCC
  • GND <-> GND
  • AD0 <-> GND
    .

STM32CubeMX相关配置

配置SYS

在这里插入图片描述
.

配置RCC

在这里插入图片描述在这里插入图片描述
.

配置USART3

在这里插入图片描述
.

配置NVIC

在这里插入图片描述
.

使用Micro库

只要映射了printf用来发送数据去串口都要使用这个库。
在这里插入图片描述
.

文件编写

修改文件usart.c

/* USER CODE BEGIN Header */
/********************************************************************************* @file    usart.c* @brief   This file provides code for the configuration*          of the USART instances.******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usart.h"/* USER CODE BEGIN 0 */#include <stdio.h>
#include <string.h>#define USART_REC_LEN 200// 串口接收缓存(1字节)
uint8_t buf = 0;uint8_t UART1_RX_Buffer[USART_REC_LEN]; // 接收缓冲,串口接收的数据存放地点// 串口接收状态,16位
uint16_t UART1_RX_STA = 0;
// bit15: 如果是1表示接收完成
// bit14: 如果是1表示接收到回车(0x0d)
// bit13~bit0: 接收到的有效字节数目/* USER CODE END 0 */UART_HandleTypeDef huart3;/* USART3 init function */void MX_USART3_UART_Init(void)
{/* USER CODE BEGIN USART3_Init 0 *//* USER CODE END USART3_Init 0 *//* USER CODE BEGIN USART3_Init 1 *//* USER CODE END USART3_Init 1 */huart3.Instance = USART3;huart3.Init.BaudRate = 115200;huart3.Init.WordLength = UART_WORDLENGTH_8B;huart3.Init.StopBits = UART_STOPBITS_1;huart3.Init.Parity = UART_PARITY_NONE;huart3.Init.Mode = UART_MODE_TX_RX;huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart3.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart3) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART3_Init 2 *//* 开启串口1的接收中断 */HAL_UART_Receive_IT(&huart3, &buf, 1); /* 每接收一个串口数据调用一次串口接收完成回调函数 */printf("usart3 is ok\r\n");/* USER CODE END USART3_Init 2 */}void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(uartHandle->Instance==USART3){/* USER CODE BEGIN USART3_MspInit 0 *//* USER CODE END USART3_MspInit 0 *//* USART3 clock enable */__HAL_RCC_USART3_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/**USART3 GPIO ConfigurationPB10     ------> USART3_TXPB11     ------> USART3_RX*/GPIO_InitStruct.Pin = GPIO_PIN_10;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_11;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/* USART3 interrupt Init */HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);HAL_NVIC_EnableIRQ(USART3_IRQn);/* USER CODE BEGIN USART3_MspInit 1 *//* USER CODE END USART3_MspInit 1 */}
}void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{if(uartHandle->Instance==USART3){/* USER CODE BEGIN USART3_MspDeInit 0 *//* USER CODE END USART3_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_USART3_CLK_DISABLE();/**USART3 GPIO ConfigurationPB10     ------> USART3_TXPB11     ------> USART3_RX*/HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);/* USART3 interrupt Deinit */HAL_NVIC_DisableIRQ(USART3_IRQn);/* USER CODE BEGIN USART3_MspDeInit 1 *//* USER CODE END USART3_MspDeInit 1 */}
}/* USER CODE BEGIN 1 *//* 重写stdio.h文件中的prinft()里的fputc()函数 */
int fputc(int my_data, FILE *p)
{unsigned char temp = my_data;// 改写后,使用printf()函数会将数据通过串口一发送出去HAL_UART_Transmit(&huart3, &temp, 1, 0xffff); // 0xfffff为最大超时时间return my_data;
}/* 串口接收完成回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{// 判断中断是哪个串口触发的if (huart->Instance == USART1){// 判断接收是否完成,即判断UART1_RX_STA的bit15是否为1if (!(UART1_RX_STA & 0x8000)){ // 如果没接收完成就进入接收流程// 判断是否接收到回车0x0dif (UART1_RX_STA & 0x4000){// 判断是否接收到换行0x0aif (buf == 0x0a){// 如果回车和换行都接收到了,则表示接收完成,即把bit15拉高UART1_RX_STA |= 0x8000;}else{ // 如果接收到回车0x0d没有接收到换行0x0a// 则认为接收错误,重新开始接收UART1_RX_STA = 0;}}else{ // 如果没有接收到回车0x0d// 则判断收到的这个字符是否是回车0x0dif (buf == 0x0d){// 如果这个字符是回车,则将将bit14拉高,表示接收到回车UART1_RX_STA |= 0x4000;}else{ // 如果不是回车// 则将这个字符存放到缓存数组中UART1_RX_Buffer[UART1_RX_STA & 0x3ffff] = buf;UART1_RX_STA++;// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收if (UART1_RX_STA > USART_REC_LEN - 1){UART1_RX_STA = 0;}}}}// 如果接收完成则重新开启串口1的接收中断HAL_UART_Receive_IT(&huart3, &buf, 1);}
}/* 对串口接收数据的处理 */
void usart1_receive_data_handle()
{/* 判断判断串口是否接收完成 */if (UART1_RX_STA & 0x8000){printf("接收完成\r\n");// 串口接收完数据后,对串口数据进行处理if (!strcmp((const char *)UART1_RX_Buffer, "haozige")){printf("浩子哥\r\n");}// 接收到其他数据,进行报错else{printf("%s\r\n", "输入错误,请重新输入");}// 换行,重新开始下一次接收memset(UART1_RX_Buffer, 0, USART_REC_LEN);UART1_RX_STA = 0;}
}/* USER CODE END 1 */

.

修改文件usart.h

/* USER CODE BEGIN Header */
/********************************************************************************* @file    usart.h* @brief   This file contains all the function prototypes for*          the usart.c file******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USART_H__
#define __USART_H__#ifdef __cplusplus
extern "C" {
#endif/* Includes ------------------------------------------------------------------*/
#include "main.h"/* USER CODE BEGIN Includes *//* USER CODE END Includes */extern UART_HandleTypeDef huart3;/* USER CODE BEGIN Private defines *//* USER CODE END Private defines */void MX_USART3_UART_Init(void);/* USER CODE BEGIN Prototypes */void usart1_receive_data_handle(void);/* USER CODE END Prototypes */#ifdef __cplusplus
}
#endif#endif /* __USART_H__ */

.

添加文件mpu6050.c

#include "my_i2c.h"
#include "mpu6050.h"
#include "usart.h"
#include <stdio.h>uint8_t mpu6050_init()
{uint8_t id,data;IIC_gpio_init();mpu6050_read_len(MPU6050_ADDR,MPU_DEVICE_ID_REG,1,&id);if(id == 0x68){mpu6050_reset();  /* mpu6050软件复位  0x6B */mpu6050_set_rate(100);  /* 设置陀螺仪采样频率为100Hz  0x19 */mpu6050_set_accelerated_speed(0);  /* 设置加速度量程范围:±2g  0x1C */mpu6050_set_gyroscope(3);  /* 设置陀螺仪量程范围:±2000dps  0x1B *//* 关闭所有中断 */data = 0;mpu6050_write_len(MPU6050_ADDR,MPU_INT_EN_REG,1,&data);/* 关闭IIC主模式 */data = 0;mpu6050_write_len(MPU6050_ADDR,MPU_USER_CTRL_REG,1,&data);/* 关闭FIFO */data = 0;mpu6050_write_len(MPU6050_ADDR,MPU_FIFO_EN_REG,1,&data);/* 设置CLKSEL,PLL X轴为参考 */data = 0x01;mpu6050_write_len(MPU6050_ADDR,MPU_PWR_MGMT1_REG,1,&data);/* 加速度与陀螺仪都工作 */data = 0;mpu6050_write_len(MPU6050_ADDR,MPU_PWR_MGMT2_REG,1,&data);return 0;}return 1;
}/* mpu6050软件复位 */
void mpu6050_reset()
{uint8_t data;data = 0x80; mpu6050_write_len(MPU6050_ADDR,MPU_PWR_MGMT1_REG,1,&data);  // 复位mpu6050HAL_Delay(100);data = 0x00;// 唤醒mpu6050
}/* 设置数字低通滤波器频率(陀螺仪输出频率) */
void mpu6050_set_lpf(uint16_t lpf)
{// 该函数设置陀螺仪输出频率为1000Hzuint8_t data;mpu6050_write_len(MPU6050_ADDR,MPU_CFG_REG,1,&data);
}	/* 设置陀螺仪采样频率:4~1000Hz */
void mpu6050_set_rate(uint16_t rata)
{// 陀螺仪采样率rate = 陀螺仪输出频率(1000Hz或8000Hz,mpu6050_set_lpf函数设定为1000Hz) / (SMPLPT_DIV + 1)(采样周期)uint8_t data;if(rata >= 1000) rata = 1000;if(rata <= 4) rata = 4;data = 1000/rata - 1;mpu6050_write_len(MPU6050_ADDR,MPU_SAMPLE_RATE_REG,1,&data);mpu6050_set_lpf(rata / 2); // 自动设置低通滤波频率为采样率的一半}/* 
设置加速度量程范围:data:0 --> ±2g1 --> ±4g2 --> ±8g3 --> ±16g
*/
void mpu6050_set_accelerated_speed(uint8_t data)
{data <<= 3; mpu6050_write_len(MPU6050_ADDR,MPU_ACCEL_CFG_REG,1,&data);
}/* 
设置陀螺仪量程范围:fsr:0 --> ±250dps1 --> ±500dps2 --> ±1000dps3 --> ±2000dps
*/
void mpu6050_set_gyroscope(uint8_t data)
{data <<= 3;mpu6050_write_len(MPU6050_ADDR,MPU_GYRO_CFG_REG,1,&data);
}/* 获取mpu6050的原始数据 */
void mpu6050_read_accelerated_speed(int16_t *accelerated_speed_x,int16_t *accelerated_speed_y,int16_t *accelerated_speed_z)
{uint8_t rev_buf[6];mpu6050_read_len(MPU6050_ADDR,MPU_ACCEL_XOUTH_REG,6,rev_buf);  //读取x,y,z三轴的加速度值*accelerated_speed_x = (int16_t)(rev_buf[0] << 8 | rev_buf[1]);*accelerated_speed_y = (int16_t)(rev_buf[2] << 8 | rev_buf[3]);*accelerated_speed_z = (int16_t)(rev_buf[4] << 8 | rev_buf[5]);
}void mpu6050_read_gyroscope(int16_t *gyroscope_x,int16_t *gyroscope_y,int16_t *gyroscope_z)
{uint8_t rev_buf[6];mpu6050_read_len(MPU6050_ADDR,MPU_GYRO_XOUTH_REG,6,rev_buf);  //读取x,y,z三轴的陀螺仪值*gyroscope_x = (int16_t)(rev_buf[0] << 8 | rev_buf[1]);*gyroscope_y = (int16_t)(rev_buf[2] << 8 | rev_buf[3]);*gyroscope_z = (int16_t)(rev_buf[4] << 8 | rev_buf[5]);
}float mpu6050_read_temperature()
{uint8_t rev_buf[2];int16_t *temperature;mpu6050_read_len(MPU6050_ADDR,MPU_TEMP_OUTH_REG,2,rev_buf);*temperature = (int16_t)(rev_buf[0] << 8 | rev_buf[1]);return 36.53 + *temperature / 340;
}/* 下面两个函数用来代替dmp库中内容 */
uint8_t mpu6050_write_len(uint8_t addr,uint8_t reg,uint8_t len,uint8_t *buf)
{uint8_t i;IIC_start();IIC_send_byte((addr | 0));if (IIC_wait_ack() == 1){IIC_stop();return 1;}IIC_send_byte(reg);if (IIC_wait_ack() == 1){IIC_stop();return 1;}for (i=0; i<len; i++){IIC_send_byte(buf[i]);if (IIC_wait_ack() == 1){IIC_stop();return 1;}}IIC_stop();return 0;
}uint8_t mpu6050_read_len(uint8_t addr,uint8_t reg,uint8_t len,uint8_t *buf)
{IIC_start();IIC_send_byte(addr | 0);if (IIC_wait_ack() == 1){IIC_stop();return 1;}IIC_send_byte(reg);if (IIC_wait_ack() == 1){IIC_stop();return 1;}IIC_start();IIC_send_byte(addr | 1);if (IIC_wait_ack() == 1){IIC_stop();return 1;}while (len){*buf = IIC_read_byte((len > 1) ? 1 : 0);len--;buf++;}IIC_stop();return 0;
}

.

添加文件mpu6050.h

#include "main.h"#define  MPU6050_ADDR  0xD0  //mou6050APD0引脚接地,因此它的I2C地址为0xD0,即0x68 << 2#define  MPU_DEVICE_ID_REG     0X75	 // 器件ID寄存器#define  MPU_CFG_REG           0X1A  // 配置寄存器
#define  MPU_SAMPLE_RATE_REG   0X19  // 陀螺仪采样率分频寄存器
#define  MPU_GYRO_CFG_REG      0X1B	 // 陀螺仪传感器配置寄存器
#define  MPU_ACCEL_CFG_REG     0X1C	 // 加速度传感器配置寄存器#define  MPU_ACCEL_XOUTH_REG   0X3B  // 加速度值,X轴高8位寄存器
#define  MPU_ACCEL_XOUTL_REG   0X3C  // 加速度值,X轴低8位寄存器
#define  MPU_ACCEL_YOUTH_REG   0X3D  // 加速度值,Y轴高8位寄存器
#define  MPU_ACCEL_YOUTL_REG   0X3E  // 加速度值,Y轴低8位寄存器
#define  MPU_ACCEL_ZOUTH_REG   0X3F  // 加速度值,Z轴高8位寄存器
#define  MPU_ACCEL_ZOUTL_REG   0X40  // 加速度值,Z轴低8位寄存器#define  MPU_TEMP_OUTH_REG     0X41	 // 温度值高8位寄存器
#define  MPU_TEMP_OUTL_REG     0X42  // 温度值低8位寄存器#define  MPU_GYRO_XOUTH_REG    0X43	 // 陀螺仪值,X轴高8位寄存器
#define  MPU_GYRO_XOUTL_REG    0X44	 // 陀螺仪值,X轴低8位寄存器
#define  MPU_GYRO_YOUTH_REG    0X45	 // 陀螺仪值,Y轴高8位寄存器
#define  MPU_GYRO_YOUTL_REG    0X46	 // 陀螺仪值,Y轴低8位寄存器
#define  MPU_GYRO_ZOUTH_REG    0X47	 // 陀螺仪值,Z轴高8位寄存器
#define  MPU_GYRO_ZOUTL_REG    0X48	 // 陀螺仪值,Z轴低8位寄存器#define  MPU_USER_CTRL_REG     0X6A	 // 用户控制寄存器
#define  MPU_PWR_MGMT1_REG     0X6B	 // 电源管理寄存器1
#define  MPU_PWR_MGMT2_REG     0X6C	 // 电源管理寄存器2#define  MPU_INTBP_CFG_REG     0X37  // 中断/旁路设置寄存器
#define  MPU_INT_EN_REG        0X38  // 中断使能寄存器
#define  MPU_INT_STA_REG       0X3A  // 中断状态寄存器#define  MPU_FIFO_EN_REG       0X23	 // FIFO使能寄存器
#define  MPU_FIFO_CNTH_REG     0X72	 // FIFO计数寄存器高八位
#define  MPU_FIFO_CNTL_REG     0X73	 // FIFO计数寄存器低八位
#define  MPU_FIFO_RW_REG       0X74	 // FIFO读写寄存器void mpu6050_reset(void);
uint8_t mpu6050_init(void);
void mpu6050_set_lpf(uint16_t lpf);
void mpu6050_set_rate(uint16_t rata);
void mpu6050_set_accelerated_speed(uint8_t data);
void mpu6050_set_gyroscope(uint8_t data);void mpu6050_read_accelerated_speed(int16_t *accelerated_speed_x,int16_t *accelerated_speed_y,int16_t *accelerated_speed_z);void mpu6050_read_gyroscope(int16_t *gyroscope_x,int16_t *gyroscope_y,int16_t *gyroscope_z);float mpu6050_read_temperature(void);uint8_t mpu6050_write_len(uint8_t addr,uint8_t reg,uint8_t len,uint8_t *buf);
uint8_t mpu6050_read_len(uint8_t addr,uint8_t reg,uint8_t len,uint8_t *buf);

.

添加文件delay.c

#include "main.h"
#include "delay.h"void delay_us(uint32_t n)
{uint8_t j;while(n--)for(j=0;j<10;j++);
}
void delay_ms(uint32_t n)
{while(n--)delay_us(1000);
}
void get_ms(unsigned long *time)
{}

.

添加文件delay.h

#include "main.h"void delay_us(uint32_t n);void delay_ms(uint32_t n);void get_ms(unsigned long *time);

.

添加文件my_i2c.c

#include "gpio.h"
#include "delay.h"
#include "my_i2c.h"#define scl_pin GPIO_PIN_6
#define sda_pin GPIO_PIN_7#define scl_gpio GPIOB
#define sda_gpio GPIOB#define SCL_HIGH HAL_GPIO_WritePin(scl_gpio, scl_pin, GPIO_PIN_SET)
#define SCL_LOW HAL_GPIO_WritePin(scl_gpio, scl_pin, GPIO_PIN_RESET)#define SDA_HIGH HAL_GPIO_WritePin(scl_gpio, sda_pin, GPIO_PIN_SET)
#define SDA_LOW HAL_GPIO_WritePin(sda_gpio, sda_pin, GPIO_PIN_RESET)
#define SDA_READ HAL_GPIO_ReadPin(sda_gpio, sda_pin)static void i2c_delay_us()
{delay_us(2);  //延时2微秒
}void IIC_gpio_init()
{// 打开时钟__HAL_RCC_GPIOB_CLK_ENABLE();GPIO_InitTypeDef GPIO_InitStruct = {0};/* 初始化SCL引脚 */GPIO_InitStruct.Pin    = scl_pin;  /* SCL引脚 */GPIO_InitStruct.Mode   = GPIO_MODE_OUTPUT_PP;          /* 推挽输出 */GPIO_InitStruct.Pull   = GPIO_PULLUP;                  /* 上拉 */GPIO_InitStruct.Speed  = GPIO_SPEED_FREQ_HIGH;         /* 高速 */HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/* 初始化SDA引脚 */GPIO_InitStruct.Pin    = sda_pin;  /* SDA引脚 */GPIO_InitStruct.Mode   = GPIO_MODE_OUTPUT_OD;          /* 开漏输出 */HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);IIC_stop();
}/* 起始信号 */
void IIC_start()
{SCL_HIGH;SDA_HIGH;i2c_delay_us();SDA_LOW;i2c_delay_us();SCL_LOW;i2c_delay_us();
}/* 终止信号 */
void IIC_stop()
{SDA_LOW;i2c_delay_us();SCL_HIGH;i2c_delay_us();SDA_HIGH;i2c_delay_us();
}/* 检测应答信号:ACK返回0,NACK返回1 */
uint8_t IIC_wait_ack()
{SDA_HIGH;    /* 释放数据线 */i2c_delay_us();SCL_HIGH; /* 从机返回ACK */i2c_delay_us();if (SDA_READ == GPIO_PIN_SET)  /* 读取SDA的电平 */{/* 如果是高电平则为NACK */IIC_stop();return 1;}SCL_LOW; /* 结束应答信号的检测 */i2c_delay_us();return 0;
}/* 应答信号 */
void IIC_ack()
{SDA_LOW;i2c_delay_us();SCL_HIGH;i2c_delay_us();SCL_LOW;i2c_delay_us();SDA_HIGH;i2c_delay_us();
}/* 非应答信号 */
void IIC_nack()
{SDA_HIGH;i2c_delay_us();SCL_HIGH;i2c_delay_us();SCL_LOW;i2c_delay_us();
}/* 发送一个字节数据 */
void IIC_send_byte(uint8_t data)
{for (uint8_t i = 0; i < 8; i++){/* 从最高位开始发送 */if ((data & 0x80) >> 7){SDA_HIGH;}else{SDA_LOW;}i2c_delay_us();SCL_HIGH;i2c_delay_us();SCL_LOW;data <<= 1; /* 将下一位移至最高位 */}SCL_HIGH; /* 发送完成,释放数据线*/
}/* 读取一个字节数据 */
uint8_t IIC_read_byte(uint8_t ack)
{uint8_t receive = 0;for (uint8_t i = 0; i < 8; i++){/* 发送数据时,从高位先发送 */receive = receive << 1; /* 先收到的数据要左移 */SCL_HIGH;i2c_delay_us();if (SDA_READ){receive++;}SCL_LOW;i2c_delay_us();}if (!ack){IIC_nack();}else{IIC_ack();}return receive;
}

.

添加文件my_i2c.h

#include "main.h"static void i2c_delay_us(void);void IIC_gpio_init(void);/* 起始信号 */
void IIC_start(void);/* 终止信号 */
void IIC_stop(void);/* 检测应答信号:ACK返回0,NACK返回1 */
uint8_t IIC_wait_ack(void);/* 应答信号 */
void IIC_ack(void);/* 非应答信号 */
void IIC_nack(void);/* 发送一个字节数据 */
void IIC_send_byte(uint8_t data);/* 读取一个字节数据 */
uint8_t IIC_read_byte(uint8_t ack);

.

main.c文件编写

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */#include <stdio.h>
#include "mpu6050.h"
#include "delay.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 *//* 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 *//* 加速度原始数据 */
int16_t accelerated_speed_x = 0;
int16_t accelerated_speed_y = 0;
int16_t accelerated_speed_z = 0;/* 陀螺仪原始数据 */
int16_t gyroscope_x = 0;
int16_t gyroscope_y = 0;
int16_t gyroscope_z = 0;/* 温度传感器原始数据 */
float temperature = 0;/* 三轴加速度 */
float ax = 0;
float ay = 0;
float az = 0;/* 三轴陀螺仪 */
float gx = 0;
float gy = 0;
float gz = 0;/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* 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_USART3_UART_Init();/* USER CODE BEGIN 2 */mpu6050_init();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 *//* 获取mpu6050原始数据 */mpu6050_read_accelerated_speed(&accelerated_speed_x,&accelerated_speed_y,&accelerated_speed_z);mpu6050_read_gyroscope(&gyroscope_x,&gyroscope_y,&gyroscope_z);temperature = mpu6050_read_temperature();/*printf("ax:%d\r\nay:%d\r\naz:%d\r\n",accelerated_speed_x,accelerated_speed_y,accelerated_speed_z);printf("gx:%d\r\ngy:%d\r\ngz:%d\r\n",gyroscope_x,gyroscope_y,gyroscope_z);printf("temperature:%f\r\n",temperature);*/ax = accelerated_speed_x/16384.0;ay = accelerated_speed_y/16384.0;az = accelerated_speed_z/16384.0;gx = gyroscope_x/16.4;gy = gyroscope_y/16.4;gz = gyroscope_z/16.4;printf("ax:%f\r\nay:%f\r\naz:%f\r\n",ax,ay,az);printf("gx:%f\r\ngy:%f\r\ngz:%f\r\n",gx,gy,gz);printf("temperature:%f\r\n",temperature);delay_ms(100);usart1_receive_data_handle();}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses 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_DIV1;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();}
}/* 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 */__disable_irq();while (1){}/* 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,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

.

DMP介绍

嵌入式数字运动处理器(Digital Motion Processor)位于 mpu6050 内部,可从主机处理器中卸载运动处理算法的运算。 DMP从加速度计,陀螺仪以及其他第三方传感器(如磁力计)获取数据,并处理数据。结果数据可以从DMP的寄存器中读取,或者可以在FIFO中缓冲。 DMP可以访问其中的一个MPU的外部引脚,可用于产生中断。
.

使用DMP的目的

DMP的目的是卸载主机处理器的时序要求和处理能力。通常,运动处理算法应该以高速运行,通常在200Hz左右,以提供低延迟的精确结果。即使应用程序以更低的速率更新,这也是必需的。例如,一个低功率的用户界面可能会以5Hz的速度更新,但运动处理仍然应该以200Hz运行。 DMP可以作为一种工具使用,以最大限度地降低功耗,简化定时,简化软件架构,并在主机处理器上节省宝贵的MIPS,以便在应用中使用。
.

使用DMP将MPU6050的原始数据转换成欧拉角

在前面代码中,已经介绍了如何获取 mpu6050 的加速度计和陀螺仪的原始数据,但是这些原始数据并不是姿态数据。姿态数据也就是欧拉角:俯仰角(pitch)、横滚角(roll)、航向角(yaw),通过欧拉角就能够非常直观地了解当前三轴的姿态。想要得到欧拉角数据,就需要对原始数据进行姿态融合解算,姿态结算涉及较多的数学计算,如果我们直接利用原始数据进行姿态解算,不仅要求开发者有较丰富的知识储备和一定的数学能力,同时对 MCU 的运算性能也有较高的要求。而 mpu6050 自带的 DMP(数字运动处理器)就能够很好的解决这一些列的问题,配合 InvenSense 提供的 DMP 驱动库,就能够很方便地将 MPU-6050 输出的原始数据直接转换为四元数输出,在得到四元数之后,就能够通过少量的运算,计算出欧拉角,从而得到姿态数据。
.

移植DMP库

  • nvenSense公司 提供的 DMP 驱动库是基于 MSP430 的,因此要在 STM32 上使用该 DMP 驱动库,还需要进行一定的移植。
  • 我们可以参考这篇文章进行移植DMP库。STM32平台下官方DMP库6.12超详细移植教程
  • 正点原子同样提供了移植好了的DMP库,轮子能用就行,我们可以基于该正点原子的DMP库进行修改。

STM32F103系列移植正点原子DMP库

1. 将DMP库搬运到自己的工程目录底下。
在这里插入图片描述
在这里插入图片描述
.
.
.
2. 将DMP库添加到工程中
这一步是添加DMP库相关的.c文件
在这里插入图片描述
.
.
这一步是添加DMP库相关的.h文件
在这里插入图片描述
.
.
.
3. 修改inv_npu.c文件

  • 修改头文件。
#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"

在这里插入图片描述
.
修改后在这里插入图片描述
.
.

  • 修改部分宏定义。
#define MPU6050
#define MOTION_DRIVER_TARGET_MSP430 #define i2c_write   mpu6050_write_len                       
#define i2c_read    mpu6050_read_len                        
#define delay_ms    delay_ms                               
#define get_ms      get_ms     #define log_e    printf
#define log_i    printf

在这里插入图片描述
.
修改后
在这里插入图片描述
.
.

  • 修改hw结构体的addr值,如果在(mpu6050_write_len、mpu6050_read_len)mpu6050连续写和mpu6050连续读函数里没有左移mpu6050的7位I2C地址就填0xD0,如果左移了就填0x68。(可选)
    在这里插入图片描述
    在这里插入图片描述
    .
    修改后
    在这里插入图片描述
    .
    .
  • 修改几个重要函数

mpu_init()
在这里插入图片描述
.
.
atk_ms6050_dmp_init()

uint8_t mpu6050_dmp_init(void)
{uint8_t ret;if(mpu6050_init() == 0){ret = mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL);       /* 开启指定传感器 */if(ret) return 1;ret = mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL);    /* 设置FIFO */if(ret) return 2;ret = mpu_set_sample_rate(DEFAULT_MPU_HZ);                 /* 设置采样率 */if(ret) return 3;ret = dmp_load_motion_driver_firmware();                   /* 加载DMP固件 */if(ret) return 4;ret = dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation));  /* 设置陀螺仪方向 */if(ret) return 5;ret = dmp_enable_feature(  DMP_FEATURE_6X_LP_QUAT      |   /* 设置DMP功能 */DMP_FEATURE_TAP             |DMP_FEATURE_ANDROID_ORIENT  |DMP_FEATURE_SEND_RAW_ACCEL  |DMP_FEATURE_SEND_CAL_GYRO   |DMP_FEATURE_GYRO_CAL);if(ret) return 6;ret = dmp_set_fifo_rate(DEFAULT_MPU_HZ);                   /* 设置DMP输出速率 */if(ret) return 7;ret = mpu_set_dmp_state(1);                                /* 使能DMP */if(ret) return 8;ret = mpu6050_run_self_test();                          /* 传感器自测试 */if(ret) return 9;return 0;}return 10;
}

在这里插入图片描述
.
.
atk_ms6050_get_clock_ms()
这个没什么用,删掉就行
.
.
atk_ms6050_run_self_test()
改名成mpu6050_run_self_test()就行
.
.
atk_ms6050_dmp_get_data()
改名成mpu6050_dmp_get_data()就行
.
.
.
4. 修改inv_mpu.h文件

uint8_t mpu6050_run_self_test(void);
uint8_t mpu6050_dmp_init(void);
uint8_t mpu6050_dmp_get_data(float *pitch, float *roll, float *yaw);

在这里插入图片描述
.
.
.
5. 修改inv_mpu_dmp_motion_driver.c文件

  • 修改头文件。
#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 "dmpKey.h"
#include "dmpmap.h"
#include "delay.h"
#include "usart.h"

在这里插入图片描述
.
.

  • 修改部分宏定义。
    在这里插入图片描述

这样我们的DMP库就移植完成了。
.
.
.

四元数

mpu6050 的 DMP 输出的四元数是 q30 格式的,也就是将正常浮点格式的四元数放大了 230 倍,因此在换算之前,需要将 DMP 输出的四元数转换为正常的浮点格式,也就是将其除以 230,然后才能将其转换为欧拉角。四元数的介绍可以看这篇文章四元数与欧拉角(Yaw、Pitch、Roll)的转换。
.

MPU6050获取欧拉角:俯仰角(Pitch)、横滚角(Roll)、偏航角(Yaw)

前面我们已经使用过mpu6050获取过加速度计和陀螺仪以及温度传感器的原始数据,同时我们也已经成功移植了DMP库,因此只需要修改main.c文件就行。

修改main.c文件

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */#include <stdio.h>
#include "mpu6050.h"
#include "delay.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.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 *//* 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 */float temperature = 0;float pitch,roll,yaw; 		//欧拉角/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* 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_USART3_UART_Init();/* USER CODE BEGIN 2 */while(mpu6050_dmp_init()){HAL_Delay(200);printf("%s\r\n","Mpu6050_DMP Init Wrong!");printf("   %d",mpu6050_dmp_init());}printf("%s\r\n","DMP_Mpu6050 Init OK!");/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */temperature= mpu6050_read_temperature();if(mpu6050_dmp_get_data(&pitch,&roll,&yaw)==0){printf("三轴角度:%f-%f-%f\r\n",pitch,roll,yaw);printf("temp:%f\r\n",temperature);printf("pitch:%f\r\n",pitch);printf("roll: %f\r\n",roll);printf("yaw:  %f\r\n",yaw);}delay_ms(100);usart1_receive_data_handle();}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses 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_DIV1;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();}
}/* 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 */__disable_irq();while (1){}/* 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,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

.

实验效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/1549685.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

欧拉角与RPY与旋转矩阵的测试

指定欧拉角&#xff0c;是指按照指定的顺序&#xff0c;按照右乘的方式构建旋转矩阵。 验证一&#xff1a;指定旋转矩阵&#xff0c;得到欧拉角&#xff0c;按照轴角的方式重新构建矩阵&#xff1a; ///---------------------------//Eigen::Matrix3d rot_cl;rot_cl<<-…

Gimbal Lock欧拉角死锁问题

技术背景 在前面几篇跟SETTLE约束算法相关的文章(1, 2, 3)中&#xff0c;都涉及到了大量的向量旋转的问题--通过一个旋转矩阵&#xff0c;给定三个空间上的欧拉角\(\alpha, \beta, \gamma\)&#xff0c;将指定的向量绕对应轴进行旋转操作。而本文主要就阐述这些旋转操作中&…

四元数与欧拉角之间的转换

在3D图形学中&#xff0c;最常用的旋转表示方法便是四元数和欧拉角&#xff0c;比起矩阵来具有节省存储空间和方便插值的优点。本文主要归纳了两种表达方式的转换&#xff0c;计算公式采用3D笛卡尔坐标系&#xff1a; 图1 3D Cartesian coordinate System (from wikipedia) 定义…

欧拉角、旋转矩阵及四元数

欧拉角、旋转矩阵及四元数 1. 简介2. 欧拉角2.1 欧拉角定义2.2 右手系和左手系2.3 转换流程 3. 旋转矩阵4. 四元数4.1 四元数与欧拉角和旋转矩阵之间等效变换4.2 测试Matlab代码 5. 总结 1. 简介 常用姿态参数表达方式包括方向余弦矩阵、欧拉轴/角参数、欧拉角、四元数以及罗德…

欧拉角物理含义

欧拉角定义 欧拉角与方向余弦矩阵以一对应 方向余弦矩阵与欧拉角存在一一对应关系。当已知欧拉角&#xff0c;规定了每次旋转的轴&#xff08;XYZ&#xff09;和每次旋转的角度&#xff0c;就可以得到方向余弦矩阵。 想想方向余弦矩阵是如何得到的&#xff1f;通过三个二维方向…

欧拉角表示的旋转相乘计算

在Eigen中用欧拉角表示旋转时&#xff0c;单次旋转多个角度和多次旋转单个角度的结果是不同的。具体试验如下&#xff1a; 在VS2017中用Eigen分别初始化两个欧拉角旋转&#xff1a; 第一个&#xff1a;欧拉角旋转为&#xff08;M_PI / 2, 0, M_PI / 4&#xff09; Eigen::Ve…

动态欧拉角与静态欧拉角的区别

看了网上好多的讲解,讲的都不是特别清晰,让人有一种很懵懵的感觉,感觉懂了,又貌似没懂的奇怪感觉,读了那么多的水文,大多都是内容差不多,好多文章之中错误百出,都是稍微提了一点,没有详细对比二者的区别,很难让人从心里真正明白其中含义.由于项目需要,这里对于动态欧拉角和静态…

旋转矩阵、欧拉角

旋转矩阵、欧拉角 注&#xff1a;下面为学习空间机器人技术系列课程笔记&#xff0c;加上一些自己的整理&#xff0c;方便复习。 一、旋转矩阵的引出 下面坐标系0的基向量为 ( x 0 &#xff0c; y 0 ) (x_{0}&#xff0c;y_{0}) (x0​&#xff0c;y0​)&#xff0c;坐标系1的基…

对于双欧拉角(正反欧拉角)的一些理解和思考

文章目录 一、正反欧拉角定义二、相关文献阐述三、对正反欧拉角的思考四、参考代码五、参考文献 最近看到有人讨论“双欧拉角”或者“正反欧拉角”的问题&#xff0c;因为自己之前没听说过这个概念&#xff0c;为了避免无知&#xff0c;因此找了一些文献进行学习和理解。不过基…

matlab 欧拉角 方向余弦,旋转矩阵、欧拉角之间转换

学习过程中涉及欧拉角和旋转矩阵的转换,索性整理学习一下欧拉角四元数和旋转矩阵的概念以及matlab中的互相转换 本文摘自各大课本,博客,自己学习整理使用,侵删 MATLAB矩阵乘法从左到右依次相乘 用R表示旋转矩阵。 yaw(偏航) pitch(俯仰) roll(横滚)分别表示Z Y X轴的转…

欧拉角旋转

欧拉角是一种表示三维旋转的描述方法&#xff0c;欧拉角的计算需要借助旋转矩阵&#xff0c;关于旋转矩阵的知识可先参考之前的文章&#xff1a;3维旋转矩阵推导与助记 欧拉角旋转 静态定义 对于在三维空间里的一个参考系&#xff0c;任何坐标系的取向&#xff0c;都可以用三…

欧拉角的理解

1 欧拉角概念 百度百科&#xff1a;欧拉角&#xff0c;用来确定定点转动刚体位置的3个一组独立角参量&#xff0c;欧拉角由章动角θ、旋进角&#xff08;即进动角&#xff09;ψ和自转角Φ组成。欧拉角为欧拉首先提出而得名。维基百科&#xff1a;Euler angles&#xff0c;莱昂…

四元数转欧拉角

四元数转欧拉角_在线测试工具 在线测试C程序工具 #include <stdio.h> #include <cmath>//四元数 struct Quaternion {double w, x, y, z; };//欧拉角 struct EulerAngles {double roll, pitch, yaw; };EulerAngles ToEulerAngles(Quaternion q) {EulerAngles angl…

详解 欧拉角与四元数

一、欧拉角 1.1 静态的定义 对于在三维空间里的一个参考系&#xff0c;任何坐标系的取向&#xff0c;都可以用三个欧拉角来表现。参考系又称为实验室参考系&#xff0c;是静止不动的。而坐标系则固定于刚体&#xff0c;随着刚体的旋转而旋转。 如图所示。设定xyz-轴为参考系…

四元数与欧拉角的转换

前言 一般情况下&#xff0c;每个领域都有自己默认是欧拉角定义。经典力学使用zxz&#xff0c;航空航天使用zyx/zxy。本文以无人机为对象讲解&#xff0c;使用zyx/zxy旋转顺序。为什么不同专业选用的欧拉角不同呢&#xff1f;一个是传统&#xff0c;另一重要的原因是不同的转序…

欧拉角转换四元数

欧拉角绕轴有x、y、z三个轴&#xff0c;旋转有不同的顺序。确定了不同的欧拉角矩阵。 针对绕轴的不同&#xff0c;欧拉角转换为四元数也不同。 绕x轴旋转 ϕ \phi ϕ角 四元数为 [ ( s i n ϕ 2 , 0 , 0 ) , c o s ( ϕ 2 ) ] T [(sin\frac{\phi}{2},0,0),cos(\frac{\phi}{…

欧拉角

欧拉角&#xff08;Euler Angles&#xff09;用来描述坐标轴的旋转。 坐标轴 原始坐标轴记为 x − y − z x-y-z x−y−z&#xff0c;旋转后坐标轴记为 X − Y − Z X-Y-Z X−Y−Z&#xff0c;坐标轴原点记为 O O O。 N ⃗ \vec{N} N 轴为 X Y XY XY与 x y xy xy两平面的交线&…

欧拉角及相关概念

欧拉角定义 欧拉角表示的是刚体的姿态变换。空间中的任意一点都可以用该点到对应坐标轴的垂直距离组成的三维向量描述&#xff0c;同理对某个物体的姿态&#xff0c;也可以用三个角度表示&#xff0c;三个角度分别为围绕对应坐标轴&#xff08;x,y,z&#xff09;旋转的…

欧拉角的概念理解和欧拉角旋转矩阵推导

欧拉角用来计算空间中刚体的旋转位置&#xff0c;目的是改变刚体的朝向. 具体来说,空间中有一个点p和一根轴k&#xff0c;点p绕轴k旋转θ角度到p,求p的坐标.这就是欧拉角要解决的问题. 只不过,欧拉角将1个点绕1根轴旋转1个角"转化为"1个点绕3个轴连续旋转3个角&quo…

欧拉角(Euler angles)

前言&#xff1a;本人也就英语4级水平&#xff0c;由于本篇中存在大量学术词汇&#xff0c;翻译时必然会有不当之处&#xff0c;原文可以在下面的链接中找到&#xff0c;具体意义各位可以自行斟酌 标红的部分是已发现存疑的翻译&#xff0c;也请各位指正。 维基百科-欧拉角 欧…