STM32驱动MPU9250九轴姿态加速度模块
简介
MPU9250是一个OFN封装的复合芯片(MGM),它由2部分组成。一组是3轴加速度还有3轴陀螺仪,另一组则是AKM公司的AK8963轴磁力计。所以,MPU9250是一款9轴运动跟踪装置,他在小小的3X3X1m的封装中融合了3轴加速度,3轴陀螺仪以及数字运动处理器(DMP)并且兼容MPU6515。其完美的12 C方案,可直接输出9轴的全部数据。一体化的设计,运动性的融合,时钟校准功能,让开发者避开了繁琐复杂的芯片选择和外设成本,保证最佳的性能。MPU9250的具有三个16位加速度AD输出,三个16位陀螺位AD输出,三个16位磁力计AD输出。
电气特征
供电电源:3-5v(内部低压差稳压)
陀螺仪范围(可编程):±250、±500、±1000、±2000°/s
加速度范围(可编程):±2、±4、±8、±16g
磁场范围:±4800uT
通信方式:标准I2C/SPI通信协议(芯片内置16bit AD转换器,16位数据输出)
引脚间距:2.54mm产品尺寸:15mm*25mm 采用沉金PCB,机器焊接工艺保证质量
原理图
主要寄存器
陀螺仪和加速度
WHO_AM_I
地址:75
用于验证MPU-9250的设备ID,出厂默认 0x71,初始化时可用于验证传感器。
ACCEL_OUT
地址:3B-40
X、Y、Z轴加速度数据输出。
GYRO_OUT
地址:43-48
X、Y、Z轴陀螺仪数据输出。
磁力计
MPU-9250中内部集成了一个AK8963,I2C地址0x0C
Measurement data
地址:03-08
X、Y、Z轴磁力计数据输出。
这里只列举了几个获取原始数据的寄存器,其他更多的寄存器的使用说明可以参考芯片寄存器手册。
IIC通讯
(1)START(S)和STOP§条件
当主设备将 START条件(S)置于总线时,2C总线上的通信开始,这被定义为 SDA线从 HIGH 到LOW 的转换,而SCL线为 HIGH(见下图)。在主设备将STOP条件§置于总线之前,总线被认为是忙碌的,这被定义为 Sda 线从 LOW 到 HIGH 的转换,而 SCl为 HIGH(见下图)。此外,如果重复产生START(Sr)而不是STOP状态,则总线仍保持忙碌状态。
(2)数据格式:
IIC数据字节被定义为8位长。每个数据传输传输的字节数没有限制。传输的每个字节必须跟随一个确认(ACK)信号。确认信号的时钟由主设备生成,而接收器通过拉低SDA在确认时钟脉冲的 HIGH 部分期间保持低电平来生成实际的确认信号。
如果一个从属服务器很忙,并且在完成其他任务之前无法发送或接收另一个字节的数据,那么它可以保持SCL LOW从而迫使主服务器进入等待状态。当从机准备好后,恢复正常的数据传输,并释放时钟线(参见下图)。
(3)通信
在与 START 条件(S)开始通信后,主设备发送一个7位的从设备地址,然后是第8位,即读写位。读写位指示主设备是从还是从设备接收数据。然后,主设备释放SDA线,等待从设备发送确认信号(ACK)。传输的每个字节后面都必须跟着一个确认位。为了确认,从设备将SDA线拉低,并在SCL线的高电平时保持低电平。数据传输总是由主设备以STOP条件"P"终止,从而释放通信线路。但是,主设备可以生成重复的 START条件(Sr),并在不首先生成STOP条件"P"的情况下寻址另一个从设备。当 SCL为 HIGH时,SDA线上从低到高的转换定义了停止条件。除了启动和停止条件外,所有SDA 变化都应在 SCL为低时发生。
为了写内部的MPU-6500寄存器,主控发送开始条件(S),然后发送I’c地址和写位(0)。在9时钟周期(当时钟高时),MPU-6500确认传输。然后主机将寄存器地址(RA)放到总线上。在MPU-6500接收到寄存器地址后,主控将寄存器数据放到总线上。然后是ACK信号,数据传输可以通过停止条件§结束。为了在最后一个ACK信号之后写入多个字节,主机可以继续输出数据,而不发送停止信号。在这种情况下,MPU-6500自动增加寄存器地址,并将数据加载到适当的寄存器。下图显示了单字节和双字节写入序列。
单字节写序列
双字节写序列
为了读取 MPU-6500 的内部寄存器,主机发送一个起始条件,然后是 L2C地址和写位,然后是将要读取的寄存器地址。在接收到来自 MPU-6500的 ACK信号后,主机发送一个起始信号,然后是slave 地址和读位。结果,MPU-65000发送ACK信号和数据。通信以主机的非确认(NACK)信号和停止位结束。NACK条件定义为在 g”时钟周期时 SDA 线保持高电平。 下图显示了单字节和双字节读取序列。
单字节读取序列
双字节读取序列
接线
STM32 | MPU9250 | OLED |
---|---|---|
3.3V | 3.3V | 3.3V |
GND | GND | GND |
PB10 | SCL | - |
PB11 | SDA | - |
PA6 | - | SCL |
PA7 | - | SDA |
驱动代码
#include "main.h"
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
SYS_Init(); //系统初始化总函数
while(1) //主循环
{
MPU_Read(); //MPU6050数据读取
DATA_Report(); //MPU6050数据上报
}
}
/**
* @brief 系统初始化总函数
* @param 无
* @retval 无
*/
void SYS_Init(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组函数
delay_init(); //延时函数初始化
uart_init(115200); //串口初始化为115200
LED_Init(); //初始化与LED连接的硬件接口
OLED_Init(); //OLED初始化
if(MPU_Init() == 0) //初始化MPU9250
printf("ID读取正常\r\n"); //ID读取正常
else
printf("ID读取不正常\r\n"); //ID读取不正常
while(1)
{
error = mpu_dmp_init(); //初始化mpu_dmp库
printf("ERROR:%d\r\n",error); //串口显示初始化错误值
switch (error) //对不同错误值类型判断
{
case 0:printf("DMP库初始化正常\r\n");break;
case 1:printf("设置传感器失败\r\n");break;
case 2:printf("设置FIFO失败\r\n");break;
case 3:printf("设置采样率失败\r\n");break;
case 4:printf("加载dmp固件失败\r\n");break;
case 5:printf("设置陀螺仪方向失败\r\n");break;
case 6:printf("设置dmp功能失败\r\n");break;
case 7:printf("设置DMP输出速率失败\r\n");break;
case 8:printf("自检失败\r\n");break;
case 9:printf("使能DMP失败\r\n");break;
case 10:printf("初始化MPU6050失败\r\n");break;
default :printf("未知错误\r\n");break;
}
if(error == 0)break; //如果没有错误直接退出循环
delay_ms(200); //延时
LED0 =! LED0; //LED0闪烁报错
sprintf((char *)tmp_buf," ERROR:%d",error);//字符串格式化命令
OLED_ShowString(0,0,(u8 *)tmp_buf,16); //OLED显示错误值
OLED_Refresh(); //刷新显存
}
delay_ms(999);
OLED_ShowString(0,0,(u8*)" MPU9250",16); //OLED显示字符串
OLED_Refresh(); //刷新显存
}
/**
* @brief MPU6050数据读取函数
* @param 无
* @retval 无
*/
void MPU_Read(void)
{
if(mpu_dmp_get_data(&yaw,&pitch,&roll)==0) //dmp处理得到数据,对返回值进行判断
{
printf("Pitch:%f Roll:%f Yaw:%f\r\n",pitch,roll,yaw);//串口输出三轴角度
mpu9250.speed++; //显示速度自加
if(mpu9250.speed == 1) //显示速度阈值设置
{
mpu9250.flag = 1; //采集成功标志位设置为有效
mpu9250.speed = 0; //显示速度归零
}
}
else //采集不成功
{
mpu9250.flag = 0; //采集成功标志位设置为无效
}
}
/**
* @brief MPU6050数据上报
* @param 无
* @retval 无
*/
void DATA_Report(void)
{
if(mpu9250.flag == 1) //采集成功时
{
temp=pitch*100; //赋temp为pitch
if(temp<0) //对数据正负判断,判断为负时
{
temp=-temp; //对负数据取反
sprintf((char *)tmp_buf," Pitch:-%.3d.%.2d",temp/100,temp%100);//字符串格式化命令
}
else //判断为正时
{
sprintf((char *)tmp_buf," Pitch: %.3d.%.2d",temp/100,temp%100);//字符串格式化命令
}
OLED_ShowString(0,16,(u8 *)tmp_buf,16); //OLED显示字符串
temp=roll*100; //赋temp为pitch
if(temp<0) //对数据正负判断,判断为负时
{
temp=-temp; //对负数据取反
sprintf((char *)tmp_buf," Roll :-%.3d.%.2d",temp/100,temp%100);//字符串格式化命令
}
else //判断为正时
{
sprintf((char *)tmp_buf," Roll : %.3d.%.2d",temp/100,temp%100);//字符串格式化命令
}
OLED_ShowString(0,48,(u8 *)tmp_buf,16); //OLED显示字符串
temp=yaw*100; //赋temp为pitch
if(temp<0) //对数据正负判断,判断为负时
{
temp=-temp; //对负数据取反
sprintf((char *)tmp_buf," Yaw :-%.3d.%.2d",temp/100,temp%100);//字符串格式化命令
}
else //判断为正时
{
sprintf((char *)tmp_buf," Yaw : %.3d.%.2d",temp/100,temp%100);//字符串格式化命令
}
OLED_ShowString(0,32,(u8 *)tmp_buf,16); //OLED显示字符串
OLED_Refresh(); //刷新显存
LED1=!LED1; //LED闪烁
mpu9250.flag = 0; //采集成功标志位设置为无效
}
else ; //防卡死
}
结果
串口输出角度值:
OLED显示角度值:
需要程序文件的可以在评论留下邮箱获取!!!