自学内容网 自学内容网

STM32-I2C通信

一、I2C原理

I2C是同步通信,方便使用软件模拟,对时间要求不严格,对硬件电路不依赖。但是需要一根时钟线。异步通信节省硬件资源,无需使用时钟线,但是对硬件要求严格,对时钟频率要求严格,依赖硬件电路。

MPU6050陀螺仪、加速度传感器。

OLED显示屏

AT24C02存储器模块

DS3231实时时钟模块

I2C对从设备一般具有id号。

I2C特点:

半双工、具有应答机制、可一主多从(单片机和任意模块通信,其他模块未经同意不会干扰总线,总线上其他所有设备都是从设备)和多主多从(总线冲突时协议会进行仲裁、时钟线 主机控制、多主机需要时钟同步)、同步通信。

I2C引脚:

SCL(时钟线)、SDA(数据线)、VCC(供电+)、GND(地)

I2C协议电路规定(一主多从):

主机权力:主机可以对SCL完全控制 ,在空闲状态下,主机可以主动发起对SDA的控制,只有在从机发送数据和从机应答的时候,主机才会转交SDA的控制权给从机。

从机权力:对于SCL时钟线,在任何时刻都只能被动读取。从机不允许控制SCL线。只有在主机发送读取从机的命令后或者从机应答的时候,从机才能短暂的取得SDA的控制权。

若无上拉电阻:主机完全控制SCL,所以可以配置为推挽输出模式。从机SCL配置为浮空输入或者上拉输入。数据流向为主机发送,所有从机接收。

主机和从机都可以控制SDA,若总线SDA时序错误,主从机控制冲突会发生断路状态,应该极力避免。为了避免这个问题,I2C禁止所有设备在SDA输出强上拉的高电平。采用外置弱上拉电阻和  加开漏输出的电路结构。

总结为设备的SCL和SDA均要配置为开漏输出模式。SCL和SDA各添加上拉电阻,约4.7KΩ。当主机和从机冲突时也是一个强下拉,一个弱上拉,总线为下拉状态。

开漏输出优点:

1、避免了总线短路的情况。有一个输出低电平,总线就为低电平。

2、开漏加弱上拉模式,同时兼具了输入和输出的功能。输出时可以自由控制低电平输出。输入则可以直接放手(不输出或者输出高电平,输入前可以保持输出高电平,不需要在切换成输入模式)直接观察总线状态即可。

3、具有线与特性,有一个输出低电平,总线就为低电平。可以通过这个特性执行多主机模式下的时钟同步和总线仲裁。

I2C时序单元:

起始时:SCL高电平期间,SDA进行低电平转换。从机捕获到SCL高电平、SDA下降沿的信号,会进行复位,等待主机的操作。然后主机拉低SCL,开始进行数据的发送(拼接时序单元)和时钟信号的控制。

结束时:SCL先高电平,后SDA置高电平,回归等待状态。

起始和终止条件类似于串口的起始位和终止位。I2C起始终止都有主机产生。

 主机发送一个字节,在起始条件后,控制在SCL低电平期间改变电平状态对应数据位(高位先行),从机会在SCL高电平(主机释放)期间读取该数据。

主机接收一个字节:主机控制SCL低电平期间,从机将数据位放在SDA(高位先行),SCL高电平(主机释放)期间,主机读取SDA数据。在SCL高电平期间不允许主从机对SDA的控制。

应答设计:

I2C时序:指定地址写

I2C中使用一主多从模式时,每次主机发送指定从机的设备地址(相当于从机的名字),对应的从机响应主机的读写操作。在一个I2C总线上挂在的从设备地址需要不一样,否则会有多个设备响应。从机地址分为7bit和10bit,此处我们实例为7bit(可在厂商手册中看到),使用广泛并且简单。比如MPU6050地址为1101000,AT24C02地址为1010000。若模块地址相同,则需要使用模块Ax-Ax+n引脚可变地址,高电平为1,低电平为0,可确定I2C地址的低几bit。

1、主机发送7bit(从机地址)+R/W(0为写,1为读),主机发送完1byte,需要释放SDA接收1bit从机的应答位,从机返回0为接收到,返回1为未接收。整个过程是,SCL低电平时改变SDA电平,SCL高电平从机获取主机发送的SDA,主机发送完7bit(从机地址)+R/W(0为写,1为读),释放SDA,从机控制SDA置应答bit。

然后从机在应答的下一时钟的下降沿快速释放SDA(给主机在低电平变换SDA的时间,接着下一字节操作),所以此处SDA上升沿和SCL下降沿几乎一起。---高位先行

2、然后紧接着便是主机发送需要读写的地址,此处SCL、SDA波形和前段原理相同。---高位先行

3、然后紧接着便是主机在该地址需要写入的数据,波形原理相同,在RA(从机应答bit)后,主机在下一时钟SCL低电平拉低SDA。然后在下一SCL高电平拉高SDA发送结束信号。

 I2C时序:当前地址读

1、起始条件,主机控制

2、主机SCL低电平写入SDA,高电平从机读取SDA,发送从机地址7bit和1bit读写,1为读。然后从机返回应答bit。

3、读取当前从机指针(从机内存为线性地址,有一个地址指针,指向内存地址,每次读写都会自增,默认上电指针为0)指向地址的数据,此时主机释放SDA,SCL高电平期间从机改变SDA,SCL低电平期间主机获取SDA。

例如使用指定地址写1byte,0x19地址,那么下一次若使用当前地址读1byte,则为0x1A地址的数据。

4、停止条件,主机控制

I2C时序:指定地址读

1、指定地址写,0x19地址。但是不写数据。

2、使用重复起始条件Sr(在下一SCL低电平,SDA释放为高电平。下下次SCL高电平期间,SDA拉低)。

3、紧接着使用当前地址读操作,进行数据读取。

4、在读取的最后一字节,停止接收反馈应答。主机控制停止信号

也可以进行起始+指定地址写+结束+起始+当前低地址读+读取数据+结束。上面时序为官方时序。

多次读写操作:

从设备地址指针会自动自增,在指定地址读和指定地址写和当前地址读中,若读写完成后不进行停止操作,继续进行数据读写时序操作(无需指定操作和地址),会对连续的地址进行写入。

连续读操作时,在从机返回SDA数据完成后,若想停止从机返回数据,需要停止最后一字节的主机应答,即返回从机1的SDA,这样从机就会停止返回数据。

若使用主机返回应答0,然后控制SDA进行停止时序操作,可能会和从机SDA数据冲突,导致不能停止。

STM32硬件I2C外设:

 I2C做为同步通信,软件控制更加可靠,但是硬件控制能够节省软件资源,,且硬件功能强大,能够完成多主机通信、时序波形规整、通信速率快。

一般设备地址前五位为11010表示后面10bit都为寻址位。不会再7bit地址下出现。

前五位为11110表示为地址为,7bit寻址。

I2C常用于内存设备中,使用DMA可以快速读写设备信息。

SMBus是系统管理总线,是基于I2C改进的协议,常用于电源管理系统。

STM32通信结构图

SMBALERT为SMBus协议内容。

DATA REGISTER为数据寄存器,将数据放入DATA REGISTER,数据会通过数据移位寄存器进行发出。一但移位寄存器完成数据转移,会置TXE=1表示发送寄存器空,可以立即将新数据放入DATA REGISTER.

当数据从SDA接收到,会通过数据移位寄存器转移到DATA REGISTER,当数据转移完成会置RXNE=1,表示接收寄存器非空,可以进行数据读取。

比较器和地址寄存器都是从机模式使用的,STM32的I2C是基于可变多主机进行的,STM32不进行通信的时候就是从机,作为从机的地址通过自身地址寄存器指定。当受到主机召唤就会响应(双地址表示可以有两个从机地址,可能为2路I2C原因)。

硬件I2C有CRC硬件校验电路会自己校验。同串口奇偶校验相同。

I2C通信可以请求中断,进行中断处理。可以使用DMA。

简化图:

 使用复用开漏输出/复用输入

STM32F103C8T6的I2C外设引脚

 硬件I2C的操作流程

主机发送:

 s:

 EV5:STM32切换主模式,读取SR1后,在写数据寄存器后,SB会自动清除,所以一般无需自己清除

写从机地址,到数据寄存器DR中。

EV6: 判断从机地址是否发送结束

 EV8_1:初始发送数据时TXE=1,移位寄存器和数据寄存器都为空,需要向DR寄存器进行写入数据。

EV8:初始发送写入数据后,数据转入移位寄存器, 移位寄存器仍非空TXE=1,DR仍然为空,若需要连续写入需要继续进行DR写入数据操作。

EV8_2:移位寄存器空,数据寄存器也空,当TXE=1、BTF=1(字节发送结束标志位),可以进行停止条件产生。

 主机接收(当前地址读):

 SB、ADDR1、EV5:START和前面截图相同

EV7:RXNE=1,数据寄存器非空,可进行DR数据读取,读取后清除位。

EV7_1:RXNE=1,读取DR数据。设置ACK=1表示接受失败需要停止和STOP接收停止请求。

然后EV7读取主机发送的停止

ADK:

  主机接收(指定地址读):

软硬件I2C波形对比

可以看到 软硬件操作的波形和时序相同。但是硬件I2C的波形更见规整,I2C需要低电平进行写入,高电平主从机读SDA,可以看到软件存在延时,硬件I2C可以直接在下降沿就进行电平改变,相应很快。

当主机发送切换从机应答,可以看到软件I2C存在了短暂时间的高电平,但是硬件I2C只有一个电平尖峰,快速的拉低了电平。

其他:

 

二、MPU6050原理

 9轴为增加3轴磁场强度采集(磁力计指南针类似,作为偏航角参考,防止位置异常)。10轴在增加气压传感器测量气压大小(反应高度信息)。

通过加速度角速度信息可以得到姿态角(欧拉角)。

上图二是加速度计(弹簧测力计)结构图:实际的滑块六面(两个对立面一个为正值一个为负值)都连接弹簧和电位计,中间连接弹簧的滑块因重力左右滑动,带动上部分电位计改变电阻大小,测量电位计输出电压,就可以得到小滑块所受的加速度值了。F=ma,根据改变的电位计阻值,计算滑块受加速度改变的位移量来计算受力大小,进而获得加速度。

例如把芯片放在地面,只有下面收到滑块的力。那么数据输出应该为XY轴为0,Z输出10g加速度值。若芯片处于理想的自由落体,那么三个面都不受力。若芯片向左倾斜放置,那么底面和左面都受力。通过左侧和底侧受力可以得到此时的倾角。但是这个倾角只有芯片静置正确,因为当具有运动加速度(分为重力加速度和运动加速度)时,单方向的加速度不一定准确。

所以加速度计具有静态稳定性不具有动态稳定性

上图三为角速度计结构图:由中间的旋转轮和外边三个轴的平衡环构成。当中间的旋转轮旋转时,根据角动量守恒原理,旋转轮有保持原有角动量的趋势,这个趋势可以保持旋转轴方向不变。当外部物体转动时,并不会改变内部旋转轴方向转动。这样会在平衡环连接外部处出现角度偏差。在旋转出安装电位器,就可以测量角度偏差进而测量角速度。对角速度进行积分可以的到角度(会因为角度噪声产生偏移)。长时间的静态角速度积分的角度会产生偏差。

所以陀螺仪计具有动态稳定性不具有静态稳定性,所以将加速度计和陀螺仪进行互补滤波就能得到静态和动态都稳定的姿态角了。

飞机姿态图解

 MPU6050参数

 I2C-7bit地址表示:

 1、直接转换,例如110 1000表示0x68,若如此表示,在发送地址时需要左移1bit,为0xD0。在写时直接<<1|0x00;在读时<<1|0x01;

2、直接认为110 1000为0xD0(左移1bit后的)。直接认为id为0xD0,在写时|0x00,读时|0x01。

两种方法实际发送时内容相同,只是处理中不同。

MPU6050模块原理图:

MPU6050原理图内置4.7k上拉电阻。

XDA和XCL是扩展磁力计和气压计,使芯片作为主机的通信引脚。MPU6050内部有DMP单元,可以对扩展的模块进行姿态解算和数据融合。

AD0为从机地址最低位,默认若下拉接地为0。

INT中断引脚,MPU6050的内置自由落体检测、运动检测、零运动检测等、或I2C通信异常可进行中断。

LDO压差线性稳压器:

供电5V转3.3V给MPU6050进行供电。

MPU6050模块框图:

CLKIN和CLKOUT为外部时钟引脚,我们一般使用内部时钟。

另外内置了三轴加速度和陀螺仪传感器,还有温度传感器。转换完成后自动进入寄存器。芯片会按照配置的频率进行采集和转运到寄存器,我们直接读取寄存器值即可。

最左侧的selftest为模块自测功能,开启的话传感器数据会比平时大一些。我们在测试时可以使能自测读取数据,在失能自测读取数据。两个数据采集相减得到的数据应在数据手册规定的范围内(若在范围内为正常)。

charge Pump为充电泵/电荷泵,CPOUT需要外接电容,电荷泵是升压电路。电容充放电速度快,所以需要串并联开关转换的快,然后加一个电源滤波。最后升压给陀螺仪供电。

interrupt status register可以控制哪些中断状态到引脚输出。

FIFO是先入先出寄存器,可以对数据流进行缓存。

config register配置寄存器可以对内部寄存器进行配置。

Sensor register 存储传感器数据。

Factory Calibration工厂校准,内部传感器校准。

Digital Motion Processor数字运动处理器,简称DMP,是芯片内部自带的姿态解算的硬件算法。配合官方DMP库可以进行姿态解算。

FSYNC是帧同步。

剩余为通用接口部分。XCL和XDA是I2C设备扩展接口,当Serial interface Bypass MUX开关连接上部分,那么扩展设备和当前MPU6050都为STM32的I2C总线的设备,受STM32控制。当Serial interface Bypass MUX不连接上部分电路引脚,那么扩展设备只能由MPU6050控制。

三、针对使用的芯片寄存器进行开发步骤

1、查看文档(产品说明书规格书、寄存器说明书、产品数据手册、芯片系列参考手册等)

确定使用的通信协议、大概了解工作原理和流程、大概了解芯片具备的功能特性和使用场景、了解芯片的电器特性、芯片通信特性(最大频率,通信电路等)、了解芯片的寄存器布置和具体地址、了解芯片的绝对最大参数和正常参数、了解芯片的引脚定义和功能、了解芯片的示例电路供电接线和配置、了解芯片的逻辑框图、芯片的时钟系统、中断引脚和中断信号选择、尺寸信息和PCB布线。

寄存器:

寄存器地址、寄存器内容和功能、时钟以及分频配置、芯片基础配置、数据寄存器、FIFO接收和发送缓冲寄存器、过滤器和屏蔽器、中断配置、中断优先级配置和发送接收FIFO优先级配置、电源管理寄存器配置、器件信息、滤波器配置。

2、根据芯片接线方式,配置接线定义

3、根据通信撰写通信基本模块,使用通信基本模块进行芯片操作

软件模拟:

  • 软件撰写通信功能(根据引脚进行宏定义)、
  • 撰写基本通信单元(GPIO初始化、数据和时钟高低电平控制宏定义(写一bit),读1bit,起始(重复起始)、终止、发送一个字节、接收一个字节、发送应答、接收应答)、
  • 建立芯片的c和h文件、
  • 在芯片文件中建立基于通信基本单元功能(指定地址指定字节数读、指定地址指定字节数写、寄存器结构体和共用体映射、
  • 芯片配置函数(初始化:解除睡眠模式、配置电源管理和采样分频、配置屏蔽过滤、其他功能初始化)、读芯片数据函数、解除睡眠模式、通信初始化等)、
  • 直接使用芯片函数控制芯片不使用通信函数。

硬件通信:硬件通信规则和配置规则了解、撰写基本通信单元(GPIO初始化,其他硬件已自动完成,调用库函数即可)、根据硬件通信规则操作寄存器进行硬件通信配置。

三、程序实现

 1、软件I2C读取MPU6050,数据显示在OLED上。

文件以及内容
文件名 main.cMyI2C.hMyI2C.cMPU6050_Reg.hMPU6050.hMPU6050.c
内容主函数I2C通信基本单元头文件I2C通信基本单元函数定义文件MPU6050芯片寄存器结构体和相关宏定义MPU6050芯片函数头文件MPU6050芯片函数(连续读写函数、初始化函数、解除睡眠函数、读取id函数)定义文件

函数具体内容:

main.c

#include "stdio.h"
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
#include "MPU6050_Reg.h"
MPU6050_DataTypedef MPU6050_DataStutucture;
uint8_t MPU6050_ID;
int main(void){
OLED_Init();
MPU6050_Init();
MPU6050_ID = MPU6050_ReadID();
while(1){
MPU_6050_GetData(&MPU6050_DataStutucture);
OLED_ShowHexNum(1,1,MPU6050_ID,2);
OLED_ShowSignedNum(2,1,MPU6050_DataStutucture.AccX,5);
OLED_ShowSignedNum(3,1,MPU6050_DataStutucture.AccY,5);
OLED_ShowSignedNum(4,1,MPU6050_DataStutucture.AccZ,5);
OLED_ShowSignedNum(2,8,MPU6050_DataStutucture.GyroX,5);
OLED_ShowSignedNum(3,8,MPU6050_DataStutucture.GyroY,5);
OLED_ShowSignedNum(4,8,MPU6050_DataStutucture.GyroZ,5);
Delay_ms(200);
}
return 0;
}

MyI2C.h

#ifndef __MYI2C_H
#define __MYI2C_H
#include "stm32f10x.h"                  // Device header

void MyI2C_Init(void);
uint8_t MyI2C_R_SDA(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
uint8_t MyI2C_ReceiveAckBit(void);
void MyI2C_SendAck(uint8_t AckBit);

#endif

MyI2C.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
//文件为软件I2C模拟程序
/*
宏定义SCL和SDA的置高低电平
主频高的芯片中需要加软件延时,否则会电平控制太快MPU6050跟不上导致异常,MPU6050中有对时序的要求
在移植到其他库或者其他种类单片机时要更换成对应的函数形式
*/
#define MyI2C_W_SCL(x) GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)x);
#define MyI2C_W_SDA(x) GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)x);

//I2C通信基本单元——————————————————————————————————————————————
/**
  * @brief 软件初始化I2C引脚,使用的B10-SCL、B11-SDA(和STM32硬件I2C2脚相同),
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void MyI2C_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//I2C2_SCL
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//I2C2_SDA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);

GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11);//默认高电平
}

/**
  * @brief 读软件I2C中SDA数据
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
uint8_t MyI2C_R_SDA(void){
uint8_t BitValue = 0;
BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
return BitValue;
}

/**
* @brief 配置I2C起始信号,SCL和SDA都为高电平时,SDA先拉低,SCL后拉低
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void MyI2C_Start(void){
MyI2C_W_SCL(1);//主动拉高,确保兼容重复起始条件
MyI2C_W_SDA(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}

/**
  * @brief 配置I2C结束信号,SCL和SDA都为低电平时,SCL先拉低,SDA后拉低
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void MyI2C_Stop(void){
//MyI2C_W_SCL(0);在每一通信结束的应答后SCL都为低电平,无需拉高,当SCL低电平时主机进行SDA电平改变,SCL高电平时从机就会进行总线SDA读取
MyI2C_W_SDA(0);//确保释放前都为低电平,能触发结束信号
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}

/**
  * @brief 发送1byte,无符号数逻辑位移直接补0,有符号数根据符号位补右移
  * @param  发送字节,高位先行
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void MyI2C_SendByte(uint8_t Byte){
for(int i=7;i>=0;i--){
MyI2C_W_SDA((Byte>>i)&0x01);//c语言中非零即1
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}

/**
  * @brief 接收1byte
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
uint8_t MyI2C_ReceiveByte(void){
MyI2C_W_SDA(1);//主机释放SDA,让从机操作SDA
uint8_t ReceiveByte=0;
for(int i=7;i>=0;i--){
MyI2C_W_SCL(1);
if(MyI2C_R_SDA()==1){
ReceiveByte |= 0x01 << i;
}
MyI2C_W_SCL(0);
}
return ReceiveByte;
}

/**
  * @brief 主机接收 从机发送的应答
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
uint8_t MyI2C_ReceiveAckBit(void){
uint8_t AckBit = 1;//默认接收异常,防止程序异常,从机持续发送
MyI2C_W_SDA(1);//主机释放SDA,让从机操作SDA
MyI2C_W_SCL(1);//SCL高电平,主机读取从机的SDA数据
AckBit = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return AckBit;//返回0或1
}

/**
  * @brief 主机发送接收应答
  * @param  发送0或1(非零)
  *     @arg 
  * @param
  *     @arg 
  * @retval None
  */
void MyI2C_SendAck(uint8_t AckBit){
MyI2C_W_SDA(AckBit);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}

MPU6050_Reg.h

#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H
#include "stm32f10x.h"                  // Device header

#define MPU6050_Address 0xD0
#define MPU6050_Address_ID 0x75

#defineMPU6050_SMPLRT_DIV0x19
#defineMPU6050_CONFIG0x1A
#defineMPU6050_GYRO_CONFIG0x1B
#defineMPU6050_ACCEL_CONFIG0x1C

#defineMPU6050_ACCEL_XOUT_H0x3B
#defineMPU6050_ACCEL_XOUT_L0x3C
#defineMPU6050_ACCEL_YOUT_H0x3D
#defineMPU6050_ACCEL_YOUT_L0x3E
#defineMPU6050_ACCEL_ZOUT_H0x3F
#defineMPU6050_ACCEL_ZOUT_L0x40
#defineMPU6050_TEMP_OUT_H0x41
#defineMPU6050_TEMP_OUT_L0x42
#defineMPU6050_GYRO_XOUT_H0x43
#defineMPU6050_GYRO_XOUT_L0x44
#defineMPU6050_GYRO_YOUT_H0x45
#defineMPU6050_GYRO_YOUT_L0x46
#defineMPU6050_GYRO_ZOUT_H0x47
#defineMPU6050_GYRO_ZOUT_L0x48

#defineMPU6050_PWR_MGMT_10x6B
#defineMPU6050_PWR_MGMT_20x6C


typedef struct{
int16_t AccX;
int16_t AccY;
int16_t AccZ;
int16_t GyroX;
int16_t GyroY;
int16_t GyroZ;
}MPU6050_DataTypedef;

#endif

MPU6050.h

#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"
#ifndef __MPU6050_H
#define __MPU6050_H

void MPU6050_Init(void);
uint8_t MPU6050_WriteReg(uint8_t Slave,uint8_t Address,uint8_t *Array,uint8_t Length);
uint8_t MPU6050_ReadReg(uint8_t Slave,uint8_t Address,uint8_t *Array,uint8_t Length);
void MPU6050_RelieveSleep(void);
uint8_t MPU6050_ReadID(void);
void MPU_6050_GetData(MPU6050_DataTypedef *MPU6050_Structure);
#endif

MPU6050.c

#include "stm32f10x.h"                  // Device header
#include "MyI2C.h"
#include "MPU6050_Reg.h"
#include "MPU6050.h"
//基于I2C通信文件函数进行MPU6050芯片使用
/**
  * @brief MPU6050初始化,初始化I2C通信
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void MPU6050_Init(void){
uint8_t Reg = 0;
MyI2C_Init();
MPU6050_RelieveSleep();
Reg=0x01;
MPU6050_WriteReg(MPU6050_Address,MPU6050_PWR_MGMT_1,&Reg,1);//配置电源管理寄存器只标0含义(0设备不复位 1解除睡眠模式 2不需要循环模式 4温度传感器不失能 5-7内部时钟(001陀螺仪时钟推荐))
Reg=0x00;
MPU6050_WriteReg(MPU6050_Address,MPU6050_PWR_MGMT_2,&Reg,1);//配置电源管理寄存器2(0-1不需要循环模式唤醒频率 2-7为传感器每个轴的待机位全部不需要待机)
Reg=0x09;
MPU6050_WriteReg(MPU6050_Address,MPU6050_SMPLRT_DIV,&Reg,1);//配置采样率分频寄存器(0-7决定数据输出的快慢,数值越小越快)
Reg=0x06;
MPU6050_WriteReg(MPU6050_Address,MPU6050_CONFIG,&Reg,1);//配置寄存器(2-4不需要外部同步 5-7 最平滑的滤波110)
Reg=0x18;
MPU6050_WriteReg(MPU6050_Address,MPU6050_GYRO_CONFIG,&Reg,1);//陀螺仪配置寄存器(0-2不需要自测试能 3-4满量程选择最大量程11 )
Reg=0x18;
MPU6050_WriteReg(MPU6050_Address,MPU6050_ACCEL_CONFIG,&Reg,1);//加速度计寄存器(0-2不需要自测试能 3-4满量程选择最大量程11 5-7高通滤波器不使用00)

}

/**
  * @brief MPU6050指定地址多字节写入
  * @param  指定的从机设备(此处直接接收7bit地址<<1),最低为0为写入
  *     @arg 
  * @param  指定的从机寄存器地址
  *     @arg 
  * @param  需要写的数组指针
  *     @arg 
  * @param  需要写的字节数
  *     @arg 
* @retval 反馈指定地址写是否正常,0为正常,1为异常
  */
uint8_t MPU6050_WriteReg(uint8_t Slave,uint8_t Address,uint8_t *Array,uint8_t Length){
uint8_t Ack = 1;
MyI2C_Start();//开始通信
//指定从设备+写
MyI2C_SendByte(Slave);
Ack = MyI2C_ReceiveAckBit();
if(Ack){
MyI2C_Stop();
return 1;
}
//指定从地址
MyI2C_SendByte(Address);
Ack = MyI2C_ReceiveAckBit();
if(Ack){
MyI2C_Stop();
return 1;
}
//多次写入操作
for(int i=0;i<Length;i++){
MyI2C_SendByte(Array[i]);
Ack = MyI2C_ReceiveAckBit();
if(Ack){
MyI2C_Stop();
return 1;
}
}
MyI2C_Stop();//停止通信
return 0;
}

/**
  * @brief MPU6050指定地址多字节读
  * @param  指定的从机设备(此处直接接收7bit地址<<1)&0x01,最低为0为写入
  *     @arg 
  * @param  指定的从机寄存器地址
  *     @arg 
  * @param  读入数据存储的数组,从下标0开始存储
  *     @arg 
  * @param  需要读出的字节数
  *     @arg 
* @retval 反馈指定地址读是否正常,0为正常,1为异常
  */
uint8_t MPU6050_ReadReg(uint8_t Slave,uint8_t Address,uint8_t *Array,uint8_t Length){
uint8_t Ack = 1;
MyI2C_Start();//开始通信
//指定从设备+写
MyI2C_SendByte(Slave);
Ack = MyI2C_ReceiveAckBit();
if(Ack){
MyI2C_Stop();
return 1;
}
//指定写从地址
MyI2C_SendByte(Address);
Ack = MyI2C_ReceiveAckBit();
if(Ack){
MyI2C_Stop();
return 1;
}
//重复起始条件
MyI2C_Start();
//指定设备+读
MyI2C_SendByte(Slave|0x01);
Ack = MyI2C_ReceiveAckBit();
if(Ack){
MyI2C_Stop();
return 1;
}
//连续读取从机数据
for(int i=0;i<Length;i++){
Array[i] = MyI2C_ReceiveByte();
if(i<Length-1){
MyI2C_SendAck(0);
}
}
MyI2C_SendAck(1);
MyI2C_Stop();//停止通信
return 0;
}

/**
  * @brief 解除MPU6050的睡眠模式,MPU6050寄存器写入前需要解除睡眠模式
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void MPU6050_RelieveSleep(void){
uint8_t data = 0x00;
MPU6050_WriteReg(MPU6050_Address,MPU6050_PWR_MGMT_1,&data,1);
}

/**
  * @brief 读取MPU6050的id
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
uint8_t MPU6050_ReadID(void){
uint8_t MPU6050_ID = 0x00;
MPU6050_ReadReg(MPU6050_Address,MPU6050_Address_ID,&MPU6050_ID,1);
return MPU6050_ID;
}

/**
  * @brief MPU6050采集数据
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void MPU_6050_GetData(MPU6050_DataTypedef *MPU6050_Structure){
uint8_t Reg[2]={0};
//方法1
MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_XOUT_H,&Reg[0],1);//Reg[0]获取加速度寄存器x的高8bit
MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_XOUT_L,&Reg[1],1);//Reg[0]获取加速度寄存器x的低8bit
MPU6050_Structure->AccX = (Reg[0]<<8)|Reg[1];//MPU6050寄存器中int16存储的是用补码表示的有符号数,可以直接赋值
Reg[0]=0;Reg[1]=0;
MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_YOUT_H,&Reg[0],1);//Reg[0]获取加速度寄存器y的高8bit
MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_YOUT_L,&Reg[1],1);//Reg[0]获取加速度寄存器y的低8bit
MPU6050_Structure->AccY = (Reg[0]<<8)|Reg[1];
Reg[0]=0;Reg[1]=0;
MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_ZOUT_H,&Reg[0],1);//Reg[0]获取加速度寄存器z的高8bit
MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_ZOUT_L,&Reg[1],1);//Reg[0]获取加速度寄存器z的低8bit
MPU6050_Structure->AccZ = (Reg[0]<<8)|Reg[1];

Reg[0]=0;Reg[1]=0;
MPU6050_ReadReg(MPU6050_Address,MPU6050_GYRO_XOUT_H,&Reg[0],1);//Reg[0]获取陀螺仪X高8bit
MPU6050_ReadReg(MPU6050_Address,MPU6050_GYRO_XOUT_L,&Reg[1],1);//Reg[0]获取陀螺仪X低8bit
MPU6050_Structure->GyroX = (Reg[0]<<8)|Reg[1];
Reg[0]=0;Reg[1]=0;
MPU6050_ReadReg(MPU6050_Address,MPU6050_GYRO_YOUT_H,&Reg[0],1);//Reg[0]获取陀螺仪Y高8bit
MPU6050_ReadReg(MPU6050_Address,MPU6050_GYRO_YOUT_L,&Reg[1],1);//Reg[0]获取陀螺仪Y低8bit
MPU6050_Structure->GyroY = (Reg[0]<<8)|Reg[1];
Reg[0]=0;Reg[1]=0;
MPU6050_ReadReg(MPU6050_Address,MPU6050_GYRO_ZOUT_H,&Reg[0],1);//Reg[0]获取陀螺仪Z高8bit
MPU6050_ReadReg(MPU6050_Address,MPU6050_GYRO_ZOUT_L,&Reg[1],1);//Reg[0]获取陀螺仪Z低8bit
MPU6050_Structure->GyroZ = (Reg[0]<<8)|Reg[1];

//方法2 使用连续读取
}





结果:

可以看到左侧数据为加速度数据,x和y基本为0,z轴加速度为1941/(16g满量程)32768 = x/16g,所以z轴基本为0.95g,标准应该为重力加速度1G=9.8g。根据芯片的坐标轴,抬起x或y一侧可以看到x或y加速度增加,验证了加速度计检测(静态应该为0g,但是加速度物理上为向量加法)。

右侧数据为角速度,在芯片转向时会对对应的坐标轴进行角速度测量。

2、硬件实现

文件名main.cI2C_HardwareReg_MPU6050.hI2C_Hardware_MPU6050.hI2C_Hardware_MPU6050.c
内容主函数MPU6050芯片寄存器、结构体、共用体和相关宏定义硬件I2C控制MPU6050芯片函数头文件硬件I2C控制MPU6050芯片函数(连续读写函数、初始化函数、解除睡眠函数、读取id函数)定义文件

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "I2C_Hardware_MPU6050.h"
#include "I2C_HardwareReg_MPU6050.h"
MPU6050_DataTypedef_Hardware MPU6050_DataStutucture;
uint8_t MPU6050_ID;
int main(void){
OLED_Init();
I2C_Hardware_MPU6050_Init();
OLED_ShowString(1,1,"ID:");
while(1){
MPU6050_ID = I2C_Hardware_MPU6050_ReadID();
I2C_Hardware_MPU6050_GetData(&MPU6050_DataStutucture);
OLED_ShowHexNum(1,4,MPU6050_ID,2);
OLED_ShowSignedNum(2,1,MPU6050_DataStutucture.AccX,5);
OLED_ShowSignedNum(3,1,MPU6050_DataStutucture.AccY,5);
OLED_ShowSignedNum(4,1,MPU6050_DataStutucture.AccZ,5);
OLED_ShowSignedNum(2,8,MPU6050_DataStutucture.GyroX,5);
OLED_ShowSignedNum(3,8,MPU6050_DataStutucture.GyroY,5);
OLED_ShowSignedNum(4,8,MPU6050_DataStutucture.GyroZ,5);
Delay_ms(200);
}
return 0;
}

I2C_HardwareReg_MPU6050.h

#ifndef __I2C_HARDWAREREG_MPU6050_H
#define __I2C_HARDWAREREG_MPU6050_H
#include "stm32f10x.h"                  // Device header

#define MPU6050_Address 0xD0
#define MPU6050_Address_ID 0x75

#defineMPU6050_SMPLRT_DIV0x19
#defineMPU6050_CONFIG0x1A
#defineMPU6050_GYRO_CONFIG0x1B
#defineMPU6050_ACCEL_CONFIG0x1C

#defineMPU6050_ACCEL_XOUT_H0x3B
#defineMPU6050_ACCEL_XOUT_L0x3C
#defineMPU6050_ACCEL_YOUT_H0x3D
#defineMPU6050_ACCEL_YOUT_L0x3E
#defineMPU6050_ACCEL_ZOUT_H0x3F
#defineMPU6050_ACCEL_ZOUT_L0x40
#defineMPU6050_TEMP_OUT_H0x41
#defineMPU6050_TEMP_OUT_L0x42
#defineMPU6050_GYRO_XOUT_H0x43
#defineMPU6050_GYRO_XOUT_L0x44
#defineMPU6050_GYRO_YOUT_H0x45
#defineMPU6050_GYRO_YOUT_L0x46
#defineMPU6050_GYRO_ZOUT_H0x47
#defineMPU6050_GYRO_ZOUT_L0x48

#defineMPU6050_PWR_MGMT_10x6B
#defineMPU6050_PWR_MGMT_20x6C

/** 
  * @brief MPU6050获取数据结构体  
  */
typedef struct{
int16_t AccX;
int16_t AccY;
int16_t AccZ;
int16_t GyroX;
int16_t GyroY;
int16_t GyroZ;
}MPU6050_DataTypedef_Hardware;

/** 
  * @brief MPU6050获取数据方式共用体 ,有连续和非连续方法  
  */
typedef enum {
continuous = 0,
incontinuity = !continuous
}MPU6050_GetDataTypedef_Hardware;
#endif

I2C_Hardware_MPU6050.h

#ifndef __I2C_HARDWARE_MPU6050_H
#define __I2C_HARDWARE_MPU6050_H
#include "stm32f10x.h"                  // Device header
#include "I2C_HardwareReg_MPU6050.h"
void I2C_Hardware_MPU6050_Init(void);
uint8_t I2C_Hardware_MPU6050_WriteReg(uint8_t Slave,uint8_t Address,uint8_t *Array,uint8_t Length);
uint8_t I2C_Hardware_MPU6050_ReadReg(uint8_t Slave,uint8_t Address,uint8_t *Array,uint8_t Length);
uint8_t I2C_Hardware_MPU6050_ReadID(void);
void I2C_Hardware_MPU6050_RelieveSleep(void);
void I2C_Hardware_MPU6050_GetData(MPU6050_DataTypedef_Hardware *MPU6050_Structure);
uint8_t MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);
#endif

I2C_Hardware_MPU6050.c

#include "stm32f10x.h"                  // Device header
#include "I2C_Hardware_MPU6050.h"
#include "I2C_HardwareReg_MPU6050.h"
void I2C_Hardware_MPU6050_Init(void){
uint8_t Reg = 0;

//硬件I2C初始化
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);//I2C2时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//I2C2 GPIOB引脚时钟配置

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);

I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//默认接收到给数据应答,后续可更改
I2C_InitStructure.I2C_AcknowledgedAddress  = I2C_AcknowledgedAddress_7bit;//响应几位的地址
I2C_InitStructure.I2C_ClockSpeed = 50000;//传输速率10k-40k 根据具体情况
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//低电平和高电平占空比,因为弱上拉高电平置位比较慢
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//I2C模式
I2C_InitStructure.I2C_OwnAddress1 = 0x00;//STM32作为从机的地址
I2C_Init(I2C2,&I2C_InitStructure);

I2C_Cmd(I2C2,ENABLE);

//MPU6050初始化
I2C_Hardware_MPU6050_RelieveSleep();
Reg=0x01;
I2C_Hardware_MPU6050_WriteReg(MPU6050_Address,MPU6050_PWR_MGMT_1,&Reg,1);//配置电源管理寄存器只标0含义(0设备不复位 1解除睡眠模式 2不需要循环模式 4温度传感器不失能 5-7内部时钟(001陀螺仪时钟推荐))
Reg=0x00;
I2C_Hardware_MPU6050_WriteReg(MPU6050_Address,MPU6050_PWR_MGMT_2,&Reg,1);//配置电源管理寄存器2(0-1不需要循环模式唤醒频率 2-7为传感器每个轴的待机位全部不需要待机)
Reg=0x09;
I2C_Hardware_MPU6050_WriteReg(MPU6050_Address,MPU6050_SMPLRT_DIV,&Reg,1);//配置采样率分频寄存器(0-7决定数据输出的快慢,数值越小越快)
Reg=0x06;
I2C_Hardware_MPU6050_WriteReg(MPU6050_Address,MPU6050_CONFIG,&Reg,1);//配置寄存器(2-4不需要外部同步 5-7 最平滑的滤波110)
Reg=0x18;
I2C_Hardware_MPU6050_WriteReg(MPU6050_Address,MPU6050_GYRO_CONFIG,&Reg,1);//陀螺仪配置寄存器(0-2不需要自测试能 3-4满量程选择最大量程11 )
I2C_Hardware_MPU6050_WriteReg(MPU6050_Address,MPU6050_ACCEL_CONFIG,&Reg,1);//加速度计寄存器(0-2不需要自测试能 3-4满量程选择最大量程11 5-7高通滤波器不使用00)
}

/**
  * @brief 硬件I2C MPU6050指定地址多字节写入
  * @param  Slave:指定的从机设备(此处直接写入7bit地址<<1),最低为0为写入
  *     @arg 
  * @param  Address:指定的从机寄存器地址
  *     @arg 
  * @param  Array:需要写的数组指针
  *     @arg 
  * @param  Length:需要写的字节数
  *     @arg 
* @retval 反馈指定地址写是否正常,0为正常,1为异常
  */
uint8_t I2C_Hardware_MPU6050_WriteReg(uint8_t Slave,uint8_t Address,uint8_t *Array,uint8_t Length){
uint8_t GetErrFlag = SUCCESS;
I2C_GenerateSTART(I2C2,ENABLE);//生成起始信号
GetErrFlag = MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//EV5 确认起始条件发送,STM32切换主模式
//指定从设备+写
I2C_Send7bitAddress(I2C2,Slave,I2C_Direction_Transmitter);//发送7bit地址,写入操作,会自动处理从机的应答
GetErrFlag = MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//EV6 确认地址和发送模式已选择

//指定从地址
I2C_SendData(I2C2,Address);

//多次写入操作
for(int i=0;i<Length;i++){
GetErrFlag = MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING);//EV8 TXE=1,数据位移寄存器非空,DR为空
I2C_SendData(I2C2,Array[i]);
}

//停止
GetErrFlag = MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//EV8_2 TXE=1、BTF=1 数据位移寄存器空,DR为空数据发送完毕。TXE=1、BTF=1在产生停止条件时会自动清空
I2C_GenerateSTOP(I2C2,ENABLE);

return GetErrFlag;//返回ERR=0为通信异常
}

/**
  * @brief 硬件I2C事件等待函数,超过10000计数(约200ms),停止等待
  * @param  I2Cx I2C硬件选择
  *     @arg I2C1、I2C2
  * @param  I2C_EVENT
  *     @arg 见I2C_CheckEvent函数参数列表
  * @retval 若等待事件失败返回ERROR = 0,成功返回SUCCESS = !0
  */
uint8_t MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT){
uint16_t Timeout = 10000;
while(I2C_CheckEvent(I2Cx,I2C_EVENT)!=SUCCESS){
Timeout--;
if(Timeout==0){
return ERROR;
}
}
return SUCCESS;
}

/**
  * @brief MPU6050指定地址多字节读
  * @param  指定的从机设备(此处直接接收7bit地址<<1)&0x01,最低为0为写入
  *     @arg 
  * @param  指定的从机寄存器地址
  *     @arg 
  * @param  读入数据存储的数组,从下标0开始存储
  *     @arg 
  * @param  需要读出的字节数
  *     @arg 
* @retval 反馈指定地址读是否正常,0为正常,1为异常
  */
uint8_t I2C_Hardware_MPU6050_ReadReg(uint8_t Slave,uint8_t Address,uint8_t *Array,uint8_t Length){
uint8_t GetErrFlag = SUCCESS;
I2C_GenerateSTART(I2C2,ENABLE);//生成起始信号
GetErrFlag=MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//EV5 确认起始条件发送,STM32切换主模式
//指定从设备+写
I2C_Send7bitAddress(I2C2,Slave,I2C_Direction_Transmitter);//发送7bit地址,写入操作,会自动处理从机的应答
GetErrFlag=MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//EV6 确认地址和发送模式已选择
//指定写地址
I2C_SendData(I2C2,Address);
GetErrFlag=MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//EV8_2 TXE=1,数据位移寄存器空,DR为空,地址发送完毕
//重复起始条件
I2C_GenerateSTART(I2C2,ENABLE);//生成起始信号
GetErrFlag=MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//EV5 确认起始条件发送,STM32切换主模式
//指定设备+读
I2C_Send7bitAddress(I2C2,Slave,I2C_Direction_Receiver);//发送7bit地址,指定读操作,会自动处理从机的应答
GetErrFlag=MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//EV6 确认地址和接收模式已选择
//连续读取从机数据
for(int i=0;i<Length;i++){
if(i==Length-1){//在最后一个数据接收时不进行应答进行停止通信
I2C_AcknowledgeConfig(I2C2,DISABLE);//主机接收应答失能NA
I2C_GenerateSTOP(I2C2,ENABLE);//开启停止请求
}
GetErrFlag=MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED);//EV7 RXEN=1接收寄存器非空,会自动置ACK
Array[i] = I2C_ReceiveData(I2C2);//主机接收应答失能,为停止做准备
}
I2C_AcknowledgeConfig(I2C2,ENABLE);//主机默认发送接收应答使能
return GetErrFlag;//返回ERR=0表示通信有异常
}

/**
  * @brief 硬件I2C读取MPU6050的id
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
uint8_t I2C_Hardware_MPU6050_ReadID(void){
uint8_t MPU6050_ID = 0x00;
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_Address_ID,&MPU6050_ID,1);
return MPU6050_ID;
}

/**
  * @brief 解除MPU6050的睡眠模式,MPU6050寄存器写入前需要解除睡眠模式,硬件I2C通信
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void I2C_Hardware_MPU6050_RelieveSleep(void){
uint8_t data = 0x00;
I2C_Hardware_MPU6050_WriteReg(MPU6050_Address,MPU6050_PWR_MGMT_1,&data,1);
}

/**
  * @brief MPU6050硬件I2C采集数据
  * @param  
  *     @arg 
  * @param  
  *     @arg 
  * @retval None
  */
void I2C_Hardware_MPU6050_GetData(MPU6050_DataTypedef_Hardware *MPU6050_Structure){
uint8_t Reg[14]={0};

//方法1 单字节获取
#if (continuity)
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_XOUT_H,&Reg[0],1);//Reg[0]获取加速度寄存器x的高8bit
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_XOUT_L,&Reg[1],1);//Reg[0]获取加速度寄存器x的低8bit
MPU6050_Structure->AccX = (Reg[0]<<8)|Reg[1];//MPU6050寄存器中int16存储的是用补码表示的有符号数,可以直接赋值
Reg[0]=0;Reg[1]=0;
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_YOUT_H,&Reg[0],1);//Reg[0]获取加速度寄存器y的高8bit
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_YOUT_L,&Reg[1],1);//Reg[0]获取加速度寄存器y的低8bit
MPU6050_Structure->AccY = (Reg[0]<<8)|Reg[1];
Reg[0]=0;Reg[1]=0;
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_ZOUT_H,&Reg[0],1);//Reg[0]获取加速度寄存器z的高8bit
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_ZOUT_L,&Reg[1],1);//Reg[0]获取加速度寄存器z的低8bit
MPU6050_Structure->AccZ = (Reg[0]<<8)|Reg[1];

Reg[0]=0;Reg[1]=0;
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_GYRO_XOUT_H,&Reg[0],1);//Reg[0]获取陀螺仪X高8bit
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_GYRO_XOUT_L,&Reg[1],1);//Reg[0]获取陀螺仪X低8bit
MPU6050_Structure->GyroX = (Reg[0]<<8)|Reg[1];
Reg[0]=0;Reg[1]=0;
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_GYRO_YOUT_H,&Reg[0],1);//Reg[0]获取陀螺仪Y高8bit
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_GYRO_YOUT_L,&Reg[1],1);//Reg[0]获取陀螺仪Y低8bit
MPU6050_Structure->GyroY = (Reg[0]<<8)|Reg[1];
Reg[0]=0;Reg[1]=0;
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_GYRO_ZOUT_H,&Reg[0],1);//Reg[0]获取陀螺仪Z高8bit
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_GYRO_ZOUT_L,&Reg[1],1);//Reg[0]获取陀螺仪Z低8bit
MPU6050_Structure->GyroZ = (Reg[0]<<8)|Reg[1];

//方法2 使用连续读取
#else
I2C_Hardware_MPU6050_ReadReg(MPU6050_Address,MPU6050_ACCEL_XOUT_H,&Reg[0],14);
MPU6050_Structure->AccX = (Reg[0]<<8)|Reg[1];
MPU6050_Structure->AccY = (Reg[2]<<8)|Reg[3];
MPU6050_Structure->AccZ = (Reg[4]<<8)|Reg[5];
MPU6050_Structure->GyroX = (Reg[6]<<8)|Reg[7];
MPU6050_Structure->GyroY = (Reg[8]<<8)|Reg[9];
MPU6050_Structure->GyroZ = (Reg[10]<<8)|Reg[11];

#endif
}


原文地址:https://blog.csdn.net/aloneboyooo/article/details/143484448

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!