如何提升网站收录,wordpress 禁止地区ip,四川建设安全协会网站,企业系统化管理的优势本节主要介绍以下内容#xff1a;
姿态检测的基本概念
姿态传感器的工作原理及参数
MPU6050传感器介绍
实验#xff1a;获取MPU6050原始数据
实验#xff1a;移植官方DMP例程
一、姿态检测基本概念
1.1 姿态
在飞行器中#xff0c;飞机姿态是非常重要的参数#x…本节主要介绍以下内容
姿态检测的基本概念
姿态传感器的工作原理及参数
MPU6050传感器介绍
实验获取MPU6050原始数据
实验移植官方DMP例程
一、姿态检测基本概念
1.1 姿态
在飞行器中飞机姿态是非常重要的参数以飞机自身的中心建立坐标系当飞机绕坐标轴旋转的时候会分别影响航偏角、横滚角及俯仰角。 那么我们检测偏航角、横滚角以及俯仰角就可以知道飞行姿态了
偏航角飞机机头朝向偏航的角度
横滚角飞机的机翼相对于水平面翻滚的角度
俯仰角飞机的机头朝向的角度
假如我们知道飞机初始时是左上角的状态只要想办法测量出基于原始状态的三个姿态角的变化量再进行叠加就可以获知它的实时姿态了。
1.2 坐标系
抽象来说姿态是“载体坐标系”与“地理坐标系”之前的转换关系。
下图紫色是地理坐标系红色是基于载体的载体坐标系载体的姿态角就是根据载体坐标系与地理坐标系的夹角来确定的。 下面是 三种常用的坐标系
地球坐标系以地球球心为原点Z轴沿地球自转轴方向X、Y轴在赤道平面内的坐标系。
地理坐标系它的原点在地球表面(或运载体所在的点)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分钟检测一次车速可得到Vt1、Vt2、Vt3-Vt12这12个时刻的车速对各个时刻的速度乘以时间间隔(5分钟)并对这12个结果求和就可得出一个相对精确的行车路程了不断提高采样频率就可以使积分时间Dt变小降低误差。 同样地提高陀螺仪传感器的采样频率即可减少积分误差目前非常普通的陀螺仪传感器的采样频率都可以达到8KHz已能满足大部分应用的精度要求。
更难以解决的是器件本身误差带来的问题。例如某种陀螺仪的误差是0.1度/秒当陀螺仪静止不动时理想的角速度应为0无论它静止多久对它进行积分测量得的旋转角度都是0这是理想的状态而由于存在0.1度/秒的误差当陀螺仪静止不动时它采样得的角速度一直为0.1度/秒若静止了1分钟对它进行积分测量得的旋转角度为6度若静止了1小时陀螺仪进行积分测量得的旋转角度就是360度即转过了一整圈这就变得无法忍受了。只有当正方向误差和负方向误差能正好互相抵消的时候才能消除这种累计误差。
利用加速度计检测角度
由于直接用陀螺仪测量角度在长时间测量时会产生累计误差因而我们又引入了检测倾角的传感器。 测量倾角最常见的例子是建筑中使用的水平仪在重力的影响下水平仪内的气泡能大致反映水柱所在直线与重力方向的夹角关系利用T字型水平仪可以检测横滚角与俯仰角但是偏航角是无法以这样的方式检测的。
在电子设备中一般使用加速度传感器来检测倾角它通过检测器件在各个方向的形变情况而采样得到受力数据根据Fma转换传感器直接输出加速度数据因而被称为加速度传感器。由于地球存在重力场所以重力在任何时刻都会作用于传感器当传感器静止的时候(实际上加速度为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一般有固定的位数如8位ADC、12位ADC等ADC的位数会影响测量的分辨率及量程。
假设用一个2位的ADC来测量长度2位的ADC最多只能表示0、1、2、3这四个数假如它的分辨率为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为重力加速度常数g9.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位它们的量程及分辨率可选多种模式 从表中还可了解到传感器的加速度及陀螺仪的采样频率分别为1000Hz及8000Hz它们是指加速度及角速度数据的采样频率我们可以使用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 编程要点
初始化STM32的I2C使能I2C向MPU6050写入控制参数定时读取加速度、角速度及温度数据 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 slaves 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 slaves 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 i0,j0;//在初始化之前要延时一段时间若没有延时则断电后再上电数据可能会出错for(i0;i1000;i){for(j0;j1000;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(i0; iTASK_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重新计时/* 任务1MPU6050任务 */if( ! task_readdata_finish ){MPU6050ReadAcc(Acel);MPU6050ReadGyro(Gyro);MPU6050_ReturnTemp(Temp);task_readdata_finish 1; //标志位置1表示需要在主循环处理MPU6050数据}}/* 处理任务2 *///添加任务需要修改任务总数的宏定义 TASK_DELAY_NUM//并且添加定义任务的执行周期宏定义 TASK_DELAY_xx就是一个编号比如 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 程序设计要点
提供I2C读写接口、定时服务及INT中断处理从陀螺仪中获取原始数据并处理更新代码并输出
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 文件中我们可以为这些函数提供串口输出接口以便把这些信息输出到上位机
剩下的明天更新