当前位置: 首页 > news >正文

手机网站 方案/知名的搜索引擎优化

手机网站 方案,知名的搜索引擎优化,html5页面模板大全,打开百度地图导航本节主要介绍以下内容: 姿态检测的基本概念 姿态传感器的工作原理及参数 MPU6050传感器介绍 实验:获取MPU6050原始数据 实验:移植官方DMP例程 一、姿态检测基本概念 1.1 姿态 在飞行器中,飞机姿态是非常重要的参数&#x…

本节主要介绍以下内容:

姿态检测的基本概念

姿态传感器的工作原理及参数

MPU6050传感器介绍

实验:获取MPU6050原始数据

实验:移植官方DMP例程

一、姿态检测基本概念

1.1 姿态

在飞行器中,飞机姿态是非常重要的参数,以飞机自身的中心建立坐标系,当飞机绕坐标轴旋转的时候,会分别影响航偏角、横滚角及俯仰角。

那么我们检测偏航角、横滚角以及俯仰角就可以知道飞行姿态了

偏航角:飞机机头朝向偏航的角度

横滚角:飞机的机翼相对于水平面翻滚的角度

俯仰角:飞机的机头朝向的角度

假如我们知道飞机初始时是左上角的状态,只要想办法测量出基于原始状态的三个姿态角的变化量,再进行叠加,就可以获知它的实时姿态了。

1.2 坐标系

抽象来说,姿态是“载体坐标系”与“地理坐标系”之前的转换关系。

下图紫色是地理坐标系,红色是基于载体的载体坐标系,载体的姿态角就是根据载体坐标系与地理坐标系的夹角来确定的。

下面是 三种常用的坐标系:

地球坐标系:以地球球心为原点,Z轴沿地球自转轴方向,XY轴在赤道平面内的坐标系。

地理坐标系:它的原点在地球表面(或运载体所在的点)Z轴沿当地地理垂线的方向(重力加速度方向)XY轴沿当地经纬线的切线方向。根据各个轴方向的不同,可选为“东北天”(x轴指向东Y指向北)、“东南天”、“西北天”等坐标系。这是我们日常生活中使用的坐标系,平时说的东南西北方向与这个坐标系东南西北的概念一致。

载体坐标系:载体坐标系以运载体的质心为原点,一般根据运载体自身结构方向构成坐标系,如Z轴上由原点指向载体顶部,Y轴指向载体头部,X轴沿载体两侧方向。上面说基于飞机建立的坐标系就是一种载体坐标系,可类比到汽车、舰船、人体、动物或手机等各种物体。

 地理坐标系与载体坐标系都以载体为原点,所以它们可以经过简单的旋转进行转换,载体的姿态角就是根据载体坐标系与地理坐标系的夹角来确定的。

假设初始状态中,飞机的Z轴、X轴及Y轴分别与地理坐标系的天轴、北轴、东轴平行。

当飞机绕自身的“Z”轴旋转,它会使自身的“Y”轴方向与地理坐标系的“南北”方向偏离一定角度,该角度就称为偏航角(Yaw)

当载体绕自身的“X”轴旋转,它会使自身的“Z”轴方向与地理坐标系的“天地”方向偏离一定角度,该角度称为俯仰角(Pitch)

当载体绕自身的“Y”轴旋转,它会使自身的“X”轴方向与地理坐标系的“东西”方向偏离一定角度,该角度称为横滚角。

姿态角的关系

坐标系间的旋转角度

说明

载体自身旋转

偏航角(Yaw)

Y轴与北轴的夹角

绕载体Z轴旋转可改变

俯仰角(Pitch)

Z轴与天轴的夹角

绕载体X轴旋转可改变

横滚角(Roll)

X轴与东轴的夹角

绕载体Y轴旋转可改变

使用陀螺仪检测角度

陀螺仪最直观的角度检测器,它可以检测物体绕坐标轴转动的“角速度”,如同将速度对时间积分可以求出路程一样,将角速度对时间积分就可以计算出旋转的“角度”。 

陀螺仪检测的缺陷

由于陀螺仪测量角度时使用积分,会存在积分误差,若积分时间Dt越小,误差就越小。这十分容易理解,例如计算路程时,假设行车时间为1小时,我们随机选择行车过程某个时刻的速度Vt乘以1小时,求出的路程误差是极大的,因为行车的过程中并不是每个时刻都等于该时刻速度的,如果我们每5分钟检测一次车速,可得到Vt1Vt2Vt3-Vt1212个时刻的车速,对各个时刻的速度乘以时间间隔(5分钟),并对这12个结果求和,就可得出一个相对精确的行车路程了,不断提高采样频率,就可以使积分时间Dt变小,降低误差。  

同样地,提高陀螺仪传感器的采样频率,即可减少积分误差,目前非常普通的陀螺仪传感器的采样频率都可以达到8KHz,已能满足大部分应用的精度要求。

更难以解决的是器件本身误差带来的问题。例如,某种陀螺仪的误差是0.1/秒,当陀螺仪静止不动时,理想的角速度应为0,无论它静止多久,对它进行积分测量得的旋转角度都是0,这是理想的状态;而由于存在0.1/秒的误差,当陀螺仪静止不动时,它采样得的角速度一直为0.1/秒,若静止了1分钟,对它进行积分测量得的旋转角度为6度,若静止了1小时,陀螺仪进行积分测量得的旋转角度就是360度,即转过了一整圈,这就变得无法忍受了。只有当正方向误差和负方向误差能正好互相抵消的时候,才能消除这种累计误差。

利用加速度计检测角度

由于直接用陀螺仪测量角度在长时间测量时会产生累计误差,因而我们又引入了检测倾角的传感器。

测量倾角最常见的例子是建筑中使用的水平仪,在重力的影响下,水平仪内的气泡能大致反映水柱所在直线与重力方向的夹角关系,利用T字型水平仪,可以检测横滚角与俯仰角,但是偏航角是无法以这样的方式检测的。

在电子设备中,一般使用加速度传感器来检测倾角,它通过检测器件在各个方向的形变情况而采样得到受力数据,根据F=ma转换,传感器直接输出加速度数据,因而被称为加速度传感器。由于地球存在重力场,所以重力在任何时刻都会作用于传感器,当传感器静止的时候(实际上加速度为0),传感器会在该方向检测出加速度g,不能认为重力方向测出的加速度为g,就表示传感器在该方向作加速度为g的运动。

当传感器的姿态不同时,它在自身各个坐标轴检测到的重力加速度是不一样的,利用各方向的测量结果,根据力的分解原理,可求出各个坐标轴与重力之间的夹角

因为重力方向是与地理坐标系的“天地”轴固连的,所以通过测量载体坐标系各轴与重力方向的夹角即可求得它与地理坐标系的角度旋转关系,从而获知载体姿态。

由于这种倾角检测方式是利用重力进行检测的,它无法检测到偏航角(Yaw),原理跟T字型水平仪一样,无论如何设计水平仪,水泡都无法指示这样的角度。

另一个缺陷是加速度传感器并不会区分重力加速度与外力加速度,当物体运动的时候,它也会在运动的方向检测出加速度,特别在震动的状态下,传感器的数据会有非常大的数据变化,此时难以反应重力的实际值。

磁场检测

为了弥补加速度传感器无法检测偏航角(Yaw)的问题,我们再引入磁场检测传感器,它可以检测出各个方向上的磁场大小,通过检测地球磁场,它可实现指南针的功能,所以也被称为电子罗盘。由于地磁场与地理坐标系的“南北”轴固联,利用磁场检测传感器的指南针功能,就可以测量出偏航角(Yaw)了。

磁场检测器的缺陷

与指南针的缺陷一样,使用磁场传感器会受到外部磁场干扰,如载体本身的电磁场干扰,不同地理环境的磁铁矿干扰等等。

GPS检测

使用GPS可以直接检测出载体在地球上的坐标,假如载体在某时刻测得坐标为A,另一时刻测得坐标为B,利用两个坐标即可求出它的航向,即可以确定偏航角,且不受磁场的影响,但这种检测方式只有当载体产生大范围位移的时候才有效(GPS民用精度大概为10米级)

姿态融合与四元数

        可以发现,使用陀螺仪检测角度时,在静止状态下存在缺陷,且受时间影响,而加速度传感器检测角度时,在运动状态下存在缺陷,且不受时间影响,刚好互补。假如我们同时使用这两种传感器,并设计一个滤波算法,当物体处于静止状态时,增大加速度数据的权重,当物体处于运动状时,增大陀螺仪数据的权重,从而获得更准确的姿态数据。

        同理,检测偏航角,当载体在静止状态时,可增大磁场检测器数据的权重,当载体在运动状态时,增大陀螺仪和GPS检测数据的权重。这些采用多种传感器数据来检测姿态的处理算法被称为姿态融合

        在姿态融合解算的时候常常使用“四元数”来表示姿态,它由三个实数及一个虚数组成,因而被称之为四元数。使用四元数表示姿态并不直观,但因为使用欧拉角(即前面说的偏航角、横滚角及俯仰角)表示姿态的时候会有“万向节死锁”问题,且运算比较复杂,所以一般在数据处理的时候会使用四元数,处理完毕后再把四元数转换成欧拉角。

        也就是说,四元数是姿态的另一种表示方式,感兴趣的话可自行查阅相关资料。

二、传感器工作原理

2.1 工作原理

在电子技术中,传感器一般是指把物理量转化成电信号量的装置

        敏感元件直接感受被测物理量,并输出与该物理量有确定关系的信号,经过转换元件将该物理量信号转换为电信号,变换电路对转换元件输出的电信号进行放大调制,最后输出容易检测的电信号量。

        例如,温度传感器可把温度量转化成电压信号量输出,且温度值与电压值成比例关系,我们只要使用ADC测量出电压值,并根据转换关系即可求得实际温度值。而前文提到的陀螺仪、加速度及磁场传感器也是类似的,它们检测的角速度、加速度及磁场强度与电压值有确定的转换关系。

2.2. 传感器参数

参数

说明

线性误差

指传感器测量值与真实物理量值之间的拟合度误差。

分辨率

指传感器可检测到的最小物理量的单位。

采样频率

指在单位时间内的采样次数。

 其中误差与分辨率是比较容易混淆的概念,以使用尺子测量长度为例,误差就是指尺子准不准,使用它测量出10厘米,与计量机构标准的10厘米有多大区别,若区别在5毫米以内,我们则称这把尺子的误差为5毫米。而分辨率是指尺子的最小刻度值,假如尺子的最小刻度值为1厘米,我们称这把尺子的分辨率为1厘米,它只能用于测量厘米级的尺寸,对于毫米级的长度,这就无法用这把尺子进行测量了。如果把尺子加热拉长,尺子的误差会大于5毫米,但它的分辨率仍为1厘米,只是它测出的1厘米值与真实值之间差得更远了。

2.3. 物理量的表示方法

大部分传感器的输出都是与电压成比例关系的,电压值一般采用ADC来测量,而ADC一般有固定的位数,如8ADC12ADC等,ADC的位数会影响测量的分辨率及量程。

假设用一个2位的ADC来测量长度,2位的ADC最多只能表示0123这四个数,假如它的分辨率为20厘米,那么它最大的测量长度为60厘米,假如它的分辨率为10厘米,那么它的最大测量长度为30厘米,由此可知,对于特定位数的ADC,量程和分辨率不可兼得。

在实际应用中,常常直接用ADC每位表征的物理量值来表示分辨率,如每位代表20厘米,我们称它的分辨率为1LSB/20cm,它等效于5位表示1米:5LSB/m。其中的LSBLeast Significant Bit),意为最ADC的低有效位。

使用采样得到的ADC数值,除以分辨率,即可求取得到物理量。例如使用分辨率为5LSB/m、线性误差为0.1m的传感器进行长度测量,其ADC采样得到数据值为“20”,可计算知道该传感器的测量值为4米,而该长度的真实值介于3.9-4.1米之间。

三、MPU6050传感器介绍

3.1 MPU6050简介

MPU6050模块,它是一种六轴传感器模块,采用InvenSense公司的MPU6050作为主芯片,能同时检测三轴加速度、三轴陀螺仪(三轴角速度)的运动数据以及温度数据。

利用MPU6050芯片内部的DMP模块(Digital Motion Processor数字运动处理器),可对传感器数据进行滤波、融合处理,它直接通过I2C接口向主控器输出姿态解算后的姿态数据,降低主控器的运算量。其姿态解算频率最高可达200Hz(利用很多个角速度的原始数据,融合加速度数据一起来算,算出来最终输出的姿态角),非常适合用于对姿态控制实时要求较高的领域。常见应用于手机、智能手环、四轴飞行器及计步器等的姿态检测。

图中表示的坐标系及旋转符号标出了MPU6050传感器的XYZ轴的加速度有角速度的正方向。 

3.2 MPU6050的特性参数

参数

说明

供电

3.3V-5V

通讯接口

I2C协议,支持的I2C时钟最高频率为400KHz

测量维度

加速度:3         陀螺仪:3       

ADC分辨率

加速度:16        陀螺仪:16

加速度测量范围

±2g、±4g、±8g、±16g       其中g为重力加速度常数,g=9.8m/s ²

加速度最高分辨率

16384 LSB/g      65536/4 = 16384

加速度线性误差

0.1g

加速度输出频率

最高1000Hz

陀螺仪测量范围

±250 º/s 、±500 º/s 、±1000 º/s、±2000 º/s

陀螺仪最高分辨率

131 LSB/( º/s)     65536/500 = 131.072

陀螺仪线性误差

0.1 º/s

陀螺仪输出频率

最高 8000Hz

DMP姿态解算频率

最高200Hz

温度传感器测量范围

-40~ +85

温度传感器分辨率

340 LSB/           

温度传感器线性误差

±1

工作温度

-40~ +85

功耗

500uA~3.9mA  (工作电压3.3V)

 加速度与陀螺仪传感器的ADC均为16位,它们的量程及分辨率可选多种模式

 

从表中还可了解到传感器的加速度及陀螺仪的采样频率分别为1000Hz8000Hz,它们是指加速度及角速度数据的采样频率,我们可以使用STM32控制器把这些数据读取出来然后进行姿态融合解算,以求出传感器当前的姿态(即求出偏航角、横滚角、俯仰角)

而如果我们使用传感器内部的DMP单元进行解算,它可以直接对采样得到的加速度及角速度进行姿态解算,解算得到的结果再输出给STM32控制器,即STM32无需自己计算,可直接获取偏航角、横滚角及俯仰角,该DMP每秒可输出200次姿态数据。

3.3 引脚说明

该模块引出的8 个引脚功能说明见下表

        其中的SDA/SCL、XDA/XCL 通讯引脚分别为两组I2C 信号线。当模块与外部主机通讯时,使用SDA/SCL,如与STM32 芯片通讯;;而XDA/XCL 则用于MPU6050 芯片与其它I2C 传感器通讯时使用,例如使用它与磁场传感器连接,MPU6050 模块可以把从主机SDA/SCL 接收的数据或命令通过XDA/XCL 引脚转发到磁场传感器中。但实际上这种功能比较鸡肋,控制麻烦且效率低,一般会直接把磁场传感器之类的I2C 传感器直接与MPU6050 挂载在同一条总线上(即都连接到SDA/SCL),使用主机直接控制。

四、实验:获取MPU6050原始数据

4.1 硬件设计 

MPU6050 模块的硬件原理图如下:

        它的硬件非常简单, SDA 与SCL 被引出方便与外部I2C 主机连接,看图中的右上角,可知该模块的I2C 通讯引脚SDA 及SCL 已经连接了上拉电阻,因此它与外部I2C 通讯主机通讯时直接使用导线连接起来即可;而MPU6050 模块与其它传感器通讯使用的XDA、XCL 引脚没有接上拉电阻,要使用时需要注意。模块自身的I2C 设备地址可通过AD0 引脚的电平控制,当AD0 接地时,设备地址为0x68(七位地址),当AD0 接电源时,设备地址为0x69(七位地址)。另外,当传感器有新数据的时候会通过INT 引脚通知STM32。

        由于MPU6050 检测时是基于自身中心坐标系的,它表示的坐标系及旋转符号标出了MPU6050 传感器的XYZ 轴的加速度有角速度的正方向。所以在安装模块时,需要考虑它与所在设备的坐标系统的关系。

在实验前,我们先用杜邦线把STM32 开发板与该MPU6050 模块连接起来

使用硬件I2C时不能与液晶屏同时使用,因为FSMC的NADV 与I2C1 的SDA 是同一个引脚,互相影响了,例程都默认使用软件I2C来驱动MPU6050,底层的软件I2C驱动跟EEPROM基本本一致,本章中重点讲述上层的MPU6050 应用及接口。

4.2 软件设计

4.2.1 编程要点
  1. 初始化STM32的I2C
  2. 使能I2C向MPU6050写入控制参数
  3. 定时读取加速度、角速度及温度数据
 4.2.2 代码分析

本实验中的I2C 硬件定义见代码

/**************************I2C参数定义,I2C1或I2C2********************************/
#define             SENSORS_I2Cx                                I2C1
#define             SENSORS_I2C_APBxClock_FUN                   RCC_APB1PeriphClockCmd
#define             SENSORS_I2C_CLK                             RCC_APB1Periph_I2C1
#define             SENSORS_I2C_GPIO_APBxClock_FUN              RCC_APB2PeriphClockCmd
#define             SENSORS_I2C_GPIO_CLK                        RCC_APB2Periph_GPIOB     
#define             SENSORS_I2C_SCL_PORT                        GPIOB   
#define             SENSORS_I2C_SCL_PIN                         GPIO_Pin_6
#define             SENSORS_I2C_SDA_PORT                        GPIOB 
#define             SENSORS_I2C_SDA_PIN                         GPIO_Pin_7

这些宏根据传感器使用的I2C 硬件封装起来了

①初始化I2C

/*** @brief  I2C1 I/O配置* @param  无* @retval 无*/
static void I2C_GPIO_Config(void)
{GPIO_InitTypeDef  GPIO_InitStructure; /* 使能与 I2C1 有关的时钟 */SENSORS_I2C_APBxClock_FUN ( SENSORS_I2C_CLK, ENABLE );SENSORS_I2C_GPIO_APBxClock_FUN ( SENSORS_I2C_GPIO_CLK, ENABLE );/* PB6-I2C1_SCL、PB7-I2C1_SDA*/GPIO_InitStructure.GPIO_Pin = SENSORS_I2C_SCL_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;	       // 开漏输出GPIO_Init(SENSORS_I2C_SCL_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = SENSORS_I2C_SDA_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;	       // 开漏输出GPIO_Init(SENSORS_I2C_SDA_PORT, &GPIO_InitStructure);	}/*** @brief  I2C 工作模式配置* @param  无* @retval 无*/
static void I2C_Mode_Configu(void)
{I2C_InitTypeDef  I2C_InitStructure; /* I2C 配置 */I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;/* 高电平数据稳定,低电平数据变化 SCL 时钟线的占空比 */I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;I2C_InitStructure.I2C_OwnAddress1 =I2Cx_OWN_ADDRESS7; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;/* I2C的寻址模式 */I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;/* 通信速率 */I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;/* I2C1 初始化 */I2C_Init(SENSORS_I2Cx, &I2C_InitStructure);/* 使能 I2C1 */I2C_Cmd(SENSORS_I2Cx, ENABLE);   
}

②对读写函数的封装

初始化完成后就是编写I2C 读写函数了,这部分跟EERPOM 的一样,主要是调用STM32 标准库函数读写数据寄存器及标志位,见代码

/*** @brief   写数据到MPU6050寄存器* @param   * @retval  */
void MPU6050_WriteReg(u8 reg_add,u8 reg_dat)
{I2C_ByteWrite(reg_dat,reg_add); 
}/*** @brief   从MPU6050寄存器读取数据* @param   * @retval  */
void MPU6050_ReadData(u8 reg_add,unsigned char* Read,u8 num)
{I2C_BufferRead(Read,reg_add,num);
}
/*** @brief   写一个字节到I2C设备中* @param   *		@arg pBuffer:缓冲区指针*		@arg WriteAddr:写地址 * @retval  正常返回1,异常返回0*/
uint8_t I2C_ByteWrite(u8 pBuffer, u8 WriteAddr)
{/* Send STRAT condition */I2C_GenerateSTART(SENSORS_I2Cx, ENABLE);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV5 and clear it */while(!I2C_CheckEvent(SENSORS_I2Cx, I2C_EVENT_MASTER_MODE_SELECT)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback();} /* Send slave address for write */I2C_Send7bitAddress(SENSORS_I2Cx, MPU6050_SLAVE_ADDRESS, I2C_Direction_Transmitter);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV6 and clear it */while(!I2C_CheckEvent(SENSORS_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback();}  /* Send the slave's internal address to write to */I2C_SendData(SENSORS_I2Cx, WriteAddr);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV8 and clear it */while(!I2C_CheckEvent(SENSORS_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback();} /* Send the byte to be written */I2C_SendData(SENSORS_I2Cx, pBuffer); I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV8 and clear it */while(!I2C_CheckEvent(SENSORS_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))	{if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback();} /* Send STOP condition */I2C_GenerateSTOP(SENSORS_I2Cx, ENABLE);return 1; //正常返回1
}/*** @brief   从I2C设备里面读取一块数据 * @param   *		@arg pBuffer:存放从slave读取的数据的缓冲区指针*		@arg WriteAddr:接收数据的从设备的地址*     @arg NumByteToWrite:要从从设备读取的字节数* @retval  正常返回1,异常返回0*/
uint8_t I2C_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)
{  I2CTimeout = I2CT_LONG_TIMEOUT;while(I2C_GetFlagStatus(SENSORS_I2Cx, I2C_FLAG_BUSY)) // Added by Najoua 27/08/2008    {if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback();}I2C_GenerateSTART(SENSORS_I2Cx, ENABLE);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV5 and clear it */while(!I2C_CheckEvent(SENSORS_I2Cx, I2C_EVENT_MASTER_MODE_SELECT)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback();}/* Send slave address for write */I2C_Send7bitAddress(SENSORS_I2Cx, MPU6050_SLAVE_ADDRESS, I2C_Direction_Transmitter);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV6 and clear it */while(!I2C_CheckEvent(SENSORS_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback();}/* Clear EV6 by setting again the PE bit */I2C_Cmd(SENSORS_I2Cx, ENABLE);/* Send the slave's internal address to write to */I2C_SendData(SENSORS_I2Cx, ReadAddr);  I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV8 and clear it */while(!I2C_CheckEvent(SENSORS_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback();}/* Send STRAT condition a second time */  I2C_GenerateSTART(SENSORS_I2Cx, ENABLE);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV5 and clear it */while(!I2C_CheckEvent(SENSORS_I2Cx, I2C_EVENT_MASTER_MODE_SELECT)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback();}/* Send slave address for read */I2C_Send7bitAddress(SENSORS_I2Cx, MPU6050_SLAVE_ADDRESS, I2C_Direction_Receiver);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV6 and clear it */while(!I2C_CheckEvent(SENSORS_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback();}/* While there is data to be read */while(NumByteToRead)  {if(NumByteToRead == 1){/* Disable Acknowledgement */I2C_AcknowledgeConfig(SENSORS_I2Cx, DISABLE);/* Send STOP Condition */I2C_GenerateSTOP(SENSORS_I2Cx, ENABLE);}/* Test on EV7 and clear it */if(I2C_CheckEvent(SENSORS_I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED))  {      /* Read a byte from the slave */*pBuffer = I2C_ReceiveData(SENSORS_I2Cx);/* Point to the next location where the byte read will be saved */pBuffer++; /* Decrement the read bytes counter */NumByteToRead--;        }   }/* Enable Acknowledgement to be ready for another reception */I2C_AcknowledgeConfig(SENSORS_I2Cx, ENABLE);return 1; //正常,返回1
}

③MPU6050 的寄存器定义

MPU6050 有各种各样的寄存器用于控制工作模式,我们把这些寄存器的地址、寄存器
位使用宏定义到了mpu6050.h 文件中了

④初始化MPU6050

根据MPU6050 的寄存器功能定义,我们使用I2C 往寄存器写入特定的控制参数

/*** @brief   写数据到MPU6050寄存器* @param   * @retval  */
void MPU6050_WriteReg(u8 reg_add,u8 reg_dat)
{I2C_ByteWrite(reg_dat,reg_add); 
}/*** @brief   从MPU6050寄存器读取数据* @param   * @retval  */
void MPU6050_ReadData(u8 reg_add,unsigned char* Read,u8 num)
{I2C_BufferRead(Read,reg_add,num);
}/*** @brief   初始化MPU6050芯片* @param   * @retval  */
void MPU6050_Init(void)
{int i=0,j=0;//在初始化之前要延时一段时间,若没有延时,则断电后再上电数据可能会出错for(i=0;i<1000;i++){for(j=0;j<1000;j++){;}}MPU6050_WriteReg(MPU6050_RA_PWR_MGMT_1, 0x00);	     //解除休眠状态MPU6050_WriteReg(MPU6050_RA_SMPLRT_DIV , 0x07);	    //陀螺仪采样率MPU6050_WriteReg(MPU6050_RA_CONFIG , 0x06);	MPU6050_WriteReg(MPU6050_RA_ACCEL_CONFIG , 0x01);	  //配置加速度传感器工作在4G模式MPU6050_WriteReg(MPU6050_RA_GYRO_CONFIG, 0x18);     //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
}

这段代码首先使用MPU6050_ReadData 及MPU6050_WriteRed 函数封装了I2C 的底层读写驱动,接下来用它们在MPU6050_Init 函数中向MPU6050 寄存器写入控制参数,设置了MPU6050 的采样率、量程(分辨率)。

 

 Gyroscope Output Rate / (1 + SMPLRT_DIV) = 1kHZ  设置采样频率为1Khz

 

这段代码首先使用MPU6050_ReadData 及MPU6050_WriteRed 函数封装了I2C 的底层读写驱动,接下来用它们在MPU6050_Init 函数中向MPU6050 寄存器写入控制参数,设置了MPU6050 的采样率、量程(分辨率)。

⑤读传感器ID 

初始化后,可通过读取它的“WHO AM I”寄存器内容来检测硬件是否正常,该寄存器存储了ID 号0x68,见代码

/*** @brief   读取MPU6050的ID* @param   * @retval  正常返回1,异常返回0*/
uint8_t MPU6050ReadID(void)
{unsigned char Re = 0;MPU6050_ReadData(MPU6050_RA_WHO_AM_I,&Re,1);    //读器件地址if(Re != 0x68){MPU_ERROR("MPU6050 dectected error!\r\n检测不到MPU6050模块,请检查模块与开发板的接线");return 0;}else{MPU_INFO("MPU6050 ID = %d\r\n",Re);return 1;}}

 

⑥读取原始数据

若传感器检测正常,就可以读取它数据寄存器获取采样数据

void SysTick_Handler(void)
{int i;for(i=0; i<TASK_DELAY_NUM; i++){Task_Delay_Group[i] ++;                   //任务计时,时间到后执行}/* 处理任务0 */if(Task_Delay_Group[0] >= TASK_DELAY_0)     //判断是否执行任务0{Task_Delay_Group[0] = 0;                  //置0重新计时/* 任务0:翻转LED */LED2_TOGGLE;}/* 处理任务1 */if(Task_Delay_Group[1] >= TASK_DELAY_1)     //判断是否执行任务1{Task_Delay_Group[1] = 0;                  //置0重新计时/* 任务1:MPU6050任务 */if( ! task_readdata_finish ){MPU6050ReadAcc(Acel);MPU6050ReadGyro(Gyro);MPU6050_ReturnTemp(&Temp);task_readdata_finish = 1; //标志位置1,表示需要在主循环处理MPU6050数据}}/* 处理任务2 *///添加任务需要修改任务总数的宏定义 TASK_DELAY_NUM//并且添加定义任务的执行周期宏定义 TASK_DELAY_x(x就是一个编号),比如 TASK_DELAY_2
}
/*** @brief   读取MPU6050的加速度数据* @param   * @retval  */
void MPU6050ReadAcc(short *accData)
{u8 buf[6];MPU6050_ReadData(MPU6050_ACC_OUT, buf, 6);accData[0] = (buf[0] << 8) | buf[1];accData[1] = (buf[2] << 8) | buf[3];accData[2] = (buf[4] << 8) | buf[5];
}/*** @brief   读取MPU6050的角加速度数据* @param   * @retval  */
void MPU6050ReadGyro(short *gyroData)
{u8 buf[6];MPU6050_ReadData(MPU6050_GYRO_OUT,buf,6);gyroData[0] = (buf[0] << 8) | buf[1];gyroData[1] = (buf[2] << 8) | buf[3];gyroData[2] = (buf[4] << 8) | buf[5];
}/*** @brief   读取MPU6050的原始温度数据* @param   * @retval  */
void MPU6050ReadTemp(short *tempData)
{u8 buf[2];MPU6050_ReadData(MPU6050_RA_TEMP_OUT_H,buf,2);     //读取温度值*tempData = (buf[0] << 8) | buf[1];
}/*** @brief   读取MPU6050的温度数据,转化成摄氏度* @param   * @retval  */
void MPU6050_ReturnTemp(float *Temperature)
{short temp3;u8 buf[2];MPU6050_ReadData(MPU6050_RA_TEMP_OUT_H,buf,2);     //读取温度值temp3= (buf[0] << 8) | buf[1];	*Temperature=((double) temp3/340.0)+36.53;}

其中前以上三个函数分别用于读取三轴加速度、角速度及温度值,这些都是原始的ADC数值(16 位长),对于加速度和角速度,把读取得的ADC值除以分辨率,即可求得实际物理量数值。最后一个函数MPU6050_ReturnTemp 展示了温度ADC值与实际温度值间的转换,它是根据MPU6050 的说明给出的转换公式进行换算的,注意陀螺仪检测的温度会受自身芯片发热的影响,严格来说它测量的是自身芯片的温度,所以用它来测量气温是不太准确的。对于加速度和角速度值我们没有进行转换,在后面我们直接利用这些数据交给DMP 单元,求解出姿态角。

⑥main.c

  *******************************************************************************/#include "stm32f10x.h"
#include "stm32f10x_it.h"
#include "./systick/bsp_SysTick.h"
#include "./led/bsp_led.h"
#include "./usart/bsp_usart.h"
#include "./mpu6050/mpu6050.h"
#include "./i2c/bsp_i2c.h"/* MPU6050数据 */
short Acel[3];
short Gyro[3];
float Temp;/*** @brief  主函数* @param  无  * @retval 无*/
int main(void)
{/* LED 端口初始化 */LED_GPIO_Config();/* 串口通信初始化 */USART_Config();//I2C初始化I2C_Bus_Init();//MPU6050初始化MPU6050_Init();//检测MPU6050if( MPU6050ReadID() == 0 ){printf("\r\n没有检测到MPU6050传感器!\r\n");LED_RED;while(1);	//检测不到MPU6050 会红灯亮然后卡死}/* 配置SysTick定时器和中断 */SysTick_Init(); //配置 SysTick 为 1ms 中断一次,在中断里读取传感器数据SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //启动定时器while(1){if( task_readdata_finish ) //task_readdata_finish = 1 表示读取MPU6050数据完成{printf("加速度:%8d%8d%8d",Acel[0],Acel[1],Acel[2]);printf("    陀螺仪%8d%8d%8d",Gyro[0],Gyro[1],Gyro[2]);printf("    温度%8.2f\r\n",Temp);task_readdata_finish = 0; // 清零标志位}}}
/*********************************************END OF FILE**********************/

本实验中控制MPU6050 并没有使用中断检测,我们是利用Systick 定时器进行计时,隔一段时间读取MPU6050 的数据寄存器获取采样数据的,代码中使用Task_Delay 变量来控制定时时间,在Systick 中断里会每隔1ms 对该变量值减1,所以当它的值为0 时表示定时时间到。就在whlie 循环里判断定时时间,定时时间到后就读取加速度、角速度及温度值,并使用串口打印信息到电脑端。

五、MPU6050—利用DMP 进行姿态解算 

        上一小节我们仅利用MPU6050 采集了原始的数据,如果您对姿态解算的算法深有研究,可以自行编写姿态解算的算法,并利用这些数据,使用STM32 进行姿态解算,解算后输出姿态角。

        而由于MPU6050 内部集成了DMP,不需要STM32 参与解算,可直接输出姿态角,也不需要对解算算法作深入研究,非常方便,本章讲解如何使用DMP 进行解算。实验中使用的代码主体是从MPU6050 官方提供的驱动《motion_driver_6.12》移植过来的,该资料包里提供了基STM32F4 控制器的源代码(本工程正是利用该代码移植到STM32F1 上的)及使用python 语言编写的上位机,资料中还附带了说明文档,请您充分利用官方自带的资料学习。

5.1 程序设计要点

  1. 提供I2C读写接口、定时服务及INT中断处理;
  2. 从陀螺仪中获取原始数据并处理
  3. 更新代码并输出

5.2 代码分析 

        官方驱动主要是MPL软件库(Motion Processing Library),要移植该软件库我们需要为它提供I2C读写接口、定时服务以及MPU6050的数据更新标志。若需要输出调试信息到上位机,还需要提供串口接口。

①I2C读写接口

        MPL库的内部对I2C读写时都使用i2c_write及i2c_read函数,在文件“inv_mpu”中给出了它们的接口格式,见代码

这些接口的格式与我们上面写的I2C 读写函数Sensors_I2C_ReadRegister 及
Sensors_I2C_WriteRegister 一致,所以可直接使用宏替换。

②提供定时服务

MPL 软件库中使用到了延时及时间戳功能,要求需要提供delay_ms 函数实现毫秒级延时,提供get_ms 获取毫秒级的时间戳,它们的接口格式也在“inv_mpu.c”文件中给出,

 我们为接口提供的Delay_ms 及get_tick_count 函数定义在bsp_SysTick.c 文件,我们使用SysTick 每毫秒产生一次中断,进行计时

 

上述代码中的TimingDelay_Decrement 和TimeStamp_Increment 函数是在Systick 的中断服务函数中被调用的,见代码清单 50-12。systick 被配置为每毫秒产生一次中断,而每次中断中会对TimingDelay 变量减1,对g_ul_ms_ticks 变量加1。它们分别用于Delay_ms 函数利用TimingDelay 的值进行阻塞延迟,而get_tick_count 函数获取的时间戳即g_ul_ms_ticks的值。

提供串口调试接口 

MPL 代码库的调试信息输出函数都集中到了log_stm32.c 文件中,我们可以为这些函数提供串口输出接口,以便把这些信息输出到上位机

剩下的明天更新!

相关文章:

MPU6050传感器—姿态检测

本节主要介绍以下内容&#xff1a; 姿态检测的基本概念 姿态传感器的工作原理及参数 MPU6050传感器介绍 实验&#xff1a;获取MPU6050原始数据 实验&#xff1a;移植官方DMP例程 一、姿态检测基本概念 1.1 姿态 在飞行器中&#xff0c;飞机姿态是非常重要的参数&#x…...

PaddleOCR封装,在线服务化部署实战(python部署,超新手教程)

OCR&#xff0c;即光学字符识别&#xff08;Optical Character Recognition&#xff09;&#xff0c;是一种将图像中的文字转换为机器编码文字的技术。这种技术可以识别和转换各种来源的文本&#xff0c;包括扫描文档、照片中的文字、手写笔记等。光学字符识别&#xff08;OCR&…...

采集B站up主视频信息

一、网页信息&#xff08;示例网址&#xff1a;https://space.bilibili.com/3493110839511225/video&#xff09; 二、查看响应数据 三、查看数据包内容 四、相关代码&#xff08;代码内容未进行翻页爬取&#xff09; # Time: 2024/1/19 16:42 # Author: 马龙强 # File: 采集B…...

Laykefu客服系统 任意文件上传漏洞复现

0x01 产品简介 Laykefu 是一款基于workerman+gatawayworker+thinkphp5搭建的全功能webim客服系统,旨在帮助企业有效管理和提供优质的客户服务。 0x02 漏洞概述 Laykefu客服系统/admin/users/upavatar.html接口处存在文件上传漏洞,而且当请求中Cookie中的”user_name“不为…...

《幻兽帕鲁》服务器该如何选购

幻兽帕鲁作为目前火爆的一款游戏&#xff0c;幻兽帕鲁的服务器要能够承受其强大的力量和能力&#xff0c;需要具备一定的配置和性能。因此针对<幻兽帕鲁>这款游戏我们来总结一些可能用于承载幻兽帕鲁的服务器类型: 高性能服务器:幻兽帕鲁的能力强大&#xff0c;可能需要…...

比较有创意的网站

有创意的网站通常展示了独特的设计、交互或内容。以下是一些备受赞誉的有创意的网站&#xff0c;你可以参考&#xff1a; Awwwards: Awwwards 是一个评选并展示全球最优秀网站的平台。你可以在这里找到很多有创意的网站设计。 Awwwards CSS Design Awards: 类似于Awwwards&…...

alfred自定义谷歌翻译workflow

如果要实现自定义workflow&#xff0c;则必须安装付费版的alfred&#xff0c;囊中羞涩的话可以自行淘宝。自定义步骤如下&#xff1a; 1. 新建空的workflow&#xff0c;填写基本信息 2. 开发python脚本 打开该workflow所在目录&#xff0c;进行下面步骤&#xff1a; 首先安装…...

【网络安全 -> 防御与保护】专栏文章索引

为了方便 快速定位 和 便于文章间的相互引用等 作为一个快速准确的导航工具 网络安全——防御与保护 &#xff08;一&#xff09;.信息安全概述 &#xff08;二&#xff09;.防火墙组网...

用户资源(菜单)控制学习使用

效果图 第一步 需要再定义常量资源 //信访听证 资源前缀public static final String RESPREFIX_MODULE_XINFTZ_"module_xinftz_";//听证专家库public static final ConstantItem RES_MODULE_XINFTZ_TINGZZJK new ConstantItem(RESPREFIX_MODULE_XINFTZ_ "tin…...

邦芒支招:十大秘诀助你轻松进名企

找到一份理想的工作是大学毕业生们最大的心愿,而进入那些知名的大企业更是广大毕业生的梦想。如何应付这些企业的面试成为了毕业生们最为关注的话题。 这些名企的面试往往有一定的程序,绝非草草而就。因此,一定要有所准备才能“百战不殆”。而要在面试前得到有关信息,常常采用…...

5G_射频测试_参考规范(一)

参考规范&#xff1a; • Base Station (BS) conformance testing&#xff08;重点limitation&#xff09;Directory Listing /ftp/Specs/archive/38_series/38.104 (3gpp.org) • Conducted conformance testing&#xff08;传导测试&#xff09;3GPP 38.141-1 6.7 Tran…...

幻读是什么,用什么隔离级别可以防止幻读?

幻读指的是&#xff0c;在同一个事务中&#xff0c;以同样的条件执行的两次查询&#xff0c;第二次查询查到了第一次查询所没查到的数据。 在mysql的四种隔离级别中&#xff0c;可重复读和串行化两种隔离级别没有幻读问题。那么它们是如何解决幻读问题的呢&#xff1f; 先说串行…...

UE5 C++学习笔记 FString FName FText相互转换

1.FString 是UE里的String。最接近std::string, 唯一可以修改的字符串类型。性能更低 TEXT(string) TEXT宏&#xff0c;作用是将字符串转换成Unicode&#xff0c;切记UE中使用字符串输出要使用该宏 2. FName 是UE里特有的类型。它更注重于表示名称不区分大小写&#xff0c;不…...

【ASOC全解析(三)】machine原理和实战

【ASOC全解析&#xff08;三&#xff09;】machine原理和实战 一、machine概述与原理1.1 machine的主要职责1.2 本文的主要内容 二、machine程序示例完整的machine驱动代码示例扩展知识&#xff1a;SND_SOC_DAILINK_DEFS使用方法说明总结与其它说明 /*************************…...

matlab appdesigner系列-常用15-滑块、微调器

滑块&#xff0c;以左右拖动的方式在一定范围内改变数值 此示例&#xff0c;滑块显示微调器的数值&#xff0c;微调器也可以显示滑块的数值 操作步骤为&#xff1a; 1&#xff09;将滑块和微调器拖拽到画布上 2&#xff09;分别设置这两个组件的回调函数 回调函数有两个选项…...

google翻译相机报错 请安装最新的Google应用,以便使用相机翻译功能

修复&#xff1a;google翻译相机报错 背景&#xff1a;解决方法 背景&#xff1a; 国内手机使用google翻译的相机时报错&#xff1a; 请安装最新的Google应用&#xff0c;以便使用相机翻译功能点击报错的“确定”后转到了国内空白的软件商店页面 解决方法 Google Play搜索 …...

openssl3.2/test/certs - 015 - Primary intermediate ca: ca-cert

文章目录 openssl3.2/test/certs - 015 - Primary intermediate ca: ca-cert概述笔记END openssl3.2/test/certs - 015 - Primary intermediate ca: ca-cert 概述 openssl3.2 - 官方demo学习 - test - certs 笔记 这个例子有不同的地方, 发证书时, 指定了CA. 看实验注释, 是…...

linux中用户及用户组信息

1&#xff0c;linux通过用户名和口令来验证用户的身份。 2&#xff0c;几个用户可以组成一个用户组。 3&#xff0c;useradd工具添加用户&#xff0c;groupadd命令添加用户组。 4&#xff0c;history 命令查看用户在Shell中执行命令的历史记录。 5&#xff0c;userdel命令删…...

用Go plan9汇编实现斐波那契数列计算

斐波那契数列是一个满足递推关系的数列&#xff0c;如&#xff1a;1 1 2 3 5 8 ... 其前两项为1&#xff0c;第3项开始&#xff0c;每一项都是其前两项之和。 用Go实现一个简单的斐波那契计算逻辑 func fib(n int) int {if n 1 || n 2 {return 1}return fib(n-1) fib(n-2) …...

Message queue 消息队列--RabbitMQ 【基础入门】

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是平顶山大师&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《Message queue 消息队列--RabbitMQ 【基础入门…...

【汇编】 13.3 对int iret和栈的深入理解

书中示例 assume cs:codecode segment start:mov ax,csmov ds,axmov si,offset lpmov ax,0mov es,axmov di,200hmov cx,offset end0-offset lpcldrep movsb ;lp到end0的指令传送到0:200处mov ax,0mov es,axmov word ptr es:[7ch*4],200hmov word ptr es:[7ch*42],0 ;设置7c表项…...

Git提交大文件报错“remote: Please remove the file from history and try again. ”

如在使用Git过程中不小心将较大的二进制文件加入仓库&#xff0c;那么仓库大小很快就会超过规定的配额&#xff0c;在Push的时候会报下面的错误&#xff1a; remote: Powered by GITEE.COM [GNK-6.4] remote: error: File: c91e5de4f55bedd0669db01036fc131ea8e516ce 130.66 MB…...

接口测试 03 -- 接口自动化思维 Requests库应用

1. 接口自动化思维梳理 1.1接口自动化的优点 接口测试自动化&#xff0c;简单来讲就是功能测试用例脚本化然后执行脚本&#xff0c;产生一份可视化测试报告。不管什么样的测试方式&#xff0c;都是为了验证功能与发现 BUG。那为什么要做接口测试自动化呢&#xff1f;一句话概括…...

SpringBoot:Bean生命周期自定义初始化和销毁

&#x1f3e1;浩泽学编程&#xff1a;个人主页 &#x1f525; 推荐专栏&#xff1a;《深入浅出SpringBoot》《java项目分享》 《RabbitMQ》《Spring》《SpringMVC》 &#x1f6f8;学无止境&#xff0c;不骄不躁&#xff0c;知行合一 文章目录 前言一、Bean注解指…...

Git--基本操作介绍(2)

Git 常用的是以下 6 个命令&#xff1a;git clone、git push、git add 、git commit、git checkout、git pull. 说明&#xff1a; workspace&#xff1a;工作区staging area&#xff1a;暂存区/缓存区local repository&#xff1a;版本库或本地仓库remote repository&#xf…...

第08章_面向对象编程(高级)(static,单例设计模式,理解mian方法,代码块,final,抽象类与抽象方法,接口,内部类,枚举类,注解,包装类)

文章目录 第08章_面向对象编程(高级)本章专题与脉络1. 关键字&#xff1a;static1.1 类属性、类方法的设计思想1.2 static关键字1.3 静态变量1.3.1 语法格式1.3.2 静态变量的特点1.3.3 举例1.3.4 内存解析 1.4 静态方法1.4.1 语法格式1.4.2 静态方法的特点1.4.3 举例 1.5 练习 …...

Java中Map接口常用的方法

java.util.Map接口中常用的方法&#xff1a; 1.Map和Collection没有继承关系 2.Map集合以key和value的方式存储数据&#xff1a;键值对 key和value都是引用数据类型 key和value都是存储对象的内存地址 key起到主导的地位&#xff0c;value是key的一个附属品 3.Map接口中常用的方…...

Linux软件包管理器yum

文章目录 前言概述Linux下载软件的三种方式源代码安装rpm安装yum安装 关于yum的相关操作查看软件包软件安装卸载软件 yum源问题 前言 在Windows系统中&#xff0c;如果我们要去下载软件&#xff0c;我们可以在该软件的官网中进行下载&#xff0c;或者在微软的额软件商店进行下…...

Linux中NFS服务器的搭建和安装

1.介绍&#xff1a; 网络文件系统即将本地系统放在网络上某一个位置的系统&#xff0c;基于UDP/IP使用nfs能够在不同计算机之间通过网络进行文件共享&#xff0c;能使使用者访问网络上其他计算机中的文件就像在访问自己的计算机一样&#xff0c;也就是说放在一个开发板上&#…...

c递归算法模型

使用递归算法模型可以较为自然地解决许多问题&#xff0c;尤其是对于那些数据结构层次比较清晰的问题&#xff0c;递归算法模型往往能够提供一种简单清晰的解法。 递归算法模型的核心思想是将一个大问题通过递归的方式拆分为若干个较小的问题&#xff0c;并不断递归下去直到问…...