IIC通信详解 —— 基于MPU6050模块
IIC通信可以简单地理解成就是数据的通信,就是单片机(主机)与设备(从机)之间的一种通信协议,两者必须遵从这个协议才能正确的相互读写数据,它这个通信过程只要两根线就能给完成,分别是SCL(时钟线)和SDA(数据线);
这个主机与从机的关系一定要理解好,因为下一次不一定是单片机,有些从机也可以作为主机,可以先把主机理解成能够向其他设备发送指令/命令的一方,比如如果你作为主机,你是想读的时候才去读的,想写的时候就去写,比较主动;而如果你作为从机,这就由不得你,你只能听从主机的命令,主机说要读,你就要把数据给出去,主机说要写,你就得乖乖被写。
现在假设单片机作为主机,连接在SDA和SCL的两条线上的设备均是它的从机,比如下面图所示:
到这里我们已经能够有一个IIC主从设备的概念了,那么假如单片机想要通过IIC来读取从设备的信息应该都要做什么呢?也就是说,整个IIC通信过程应该是怎么样子实现的?
在解释之前,可以先知道一个这个协议不协议的到底是怎么实现的,打比喻说只有一个主机一个从机时,大家先定一个协议,如果某根线的电平怎么怎么样,同时另外一根线的电平又怎么怎么样的时候,对应的是要读?还是要写?还是其他什么?上面这个过程也就相当于主机发出的指令,从机通过两根线的电平状态来确定主机的指令是什么。(注意,不要简单地理解为 00 01 10 11 四种状态)。指令是通过两根线来发送,里面的数据也是通过那两根线,所以你至少要定义你什么时候是指令什么时候是数据吧?所以就会有协议这种东西。
IIC总线在传输数据的过程中一共有三种类型信号(指令),分别为:开始信号、结束信号和应答信号。这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。同时我们还要介绍其空闲状态、数据的有效性、数据传输。看到协议不要害怕,我们只需要先知道它内部的原理,至于怎么来实现,后面再看代码进行解释。
①. 空闲状态
当SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。这种状态就相当于没有设备在占用通信通道,大家各自在自家互不搭理。正常情况下,当系统开始工作,如果主从不通信时就希望它是空闲状态,也就是说开机后默认是空闲状态,那么这两个高电平可以由上拉电阻来完成。
②. 开始信号
当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
③. 结束信号
当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
④. 应答信号
发射器 定义要发送数据的一端为发射器,当主机往从机写命令时,可以理解成主机作为发射器,当主机读取从机数据时,可以理解成从机往主机写数据,这时候从机就是发射器;
实现过程 发送器每发送一个字节(1byte = 8bit),8个时钟脉冲用来储存数据内容,在第9个时钟脉冲释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
⑤. 数据的有效性
在数据传送时,SCL为高电平期间,SDA上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。 即:数据在SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定。
以上就是协议的一些内容,在发送数据时必须得遵守协议,开启读取、读取完成之后一定要发送停止信号来释放总线。我们可以接着看数据的发送过程是怎么实现的。
因为在总线上可能会有很多从设备,不止一个,所以主机在发送或者接受数据的时候,必须要知道从机的设备地址(不同设备有自己不同的设备物理地址),相当于如果你要去某个人的家,至少你要先知道他们家的地址。
知道了这个物理地址后,也就相当于主机能在众多从设备中找到了想要找的从设备,但是一个完整设备意味着它有自己的芯片,有自己的储存单元,所以主机不仅仅只是要找到这个设备,还要找到该设备里的主机想要读写的存储单元(寄存器),相当于你要去某个人的家找一个人,你到了他家之后,还要在他众多的家人中找到你要找的那个人。
当主机确定了从机设备、从机设备的寄存器,就能往里面进行读写数据了,而这个读写数据的过程就必须得按照这个IIC协议来完成,这么说可能还比较抽象。下面先介绍MPU6050,再结合IIC来实现单片机从MPU6050读取数据。
MPU-60X0由以下几个关键块和功能组成
1、带有16位ADC和信号调理的三轴MEMS速率陀螺仪传感器
2、具有16位ADC和信号调理的三轴MEMS加速度传感器
3、数字运动处理器(DMP)引擎
4、传感器数据寄存器
5、FIFO
6、中断
7、数字输出温度传感器
首先要知道6050的内置模块。包括一个三轴MEMS(微机电系统(MEMS, Micro-Electro-Mechanical System))陀螺仪、三轴MEMS加速度计,一个数字运动处理引擎(DMP)、还有用于第三方数字传感器接口的辅助IIC端口,(GY-521上面的xda,xcl),常用于扩展磁力计,当辅助IIC接口连接到一个三轴磁力计上面时,6050能提供一个完整的九轴融合输出到其主IIC接口上。同时,6050内部还内置了一个可编程的低通滤波器,可用于传感器数据的滤波。
系统结构图
通过系统结构图只需要你大概知道有什么在里面就行了。
接线图
当不使用磁力计时(使用磁力计时,MPU6050作为主机去读取磁力计的数据,然后再以从机的身份向单片机主机发送加速度、陀螺仪和从磁力计读取的数据),只需要接电源线(5V)、地线、SCL和SDA即可,其他引脚均闲空。
关键功能介绍
① 带有16位ADC和信号调理的三轴MEMS陀螺仪
MPU-60X0由三个独立的振动MEMS速率陀螺仪组成,可检测旋转角度X轴,Y轴和Z轴。 当陀螺仪围绕任何感应轴旋转时,科里奥利效应就会产生电容式传感器检测到的振动。 所得到的信号被放大,解调和滤波产生与角速度成比例的电压。 该电压使用单独的片内数字化16位模数转换器(ADC)对每个轴进行采样。 陀螺仪传感器可以全面范围的被数字编程为每秒±250,±500,±1000或±2000度(dps)。 ADC样本速率可以从每秒8,000个采样点编程到每秒3.9个采样点,并且可由用户选择低通滤波器可实现广泛的截止频率。
这就是指模块内部自带了陀螺仪,经其内部的数模转换器可以将陀螺仪的模拟量转成可传输的数字量,并且存储在16位寄存器里(由两个8位寄存器组成),关于获取陀螺仪数据时,只需直接从对应的寄存器里读出数据即可,注意此时的数据并非是倾角。
简而言之,陀螺仪就是角速度检测仪。比如,一块板,以X轴为轴心,在一秒钟的时间转到了90度,那么它在X轴上的角速度就是 90度/秒 (DPS, 角速度单位,Degree Per Second的缩写°/S ,体现了转动的快慢)
② 具有16位ADC和信号调理的三轴MEMS加速度计
MPU-60X0的3轴加速度计为每个轴使用单独的检测质量。 加速沿着一条特定轴在相应的检测质量上引起位移,并且电容式传感器检测到该位移位移有差别。 MPU-60X0的架构降低了加速度计的敏感度制造变化以及热漂移。 当设备放置在平坦的表面上时,将进行测量在X和Y轴上为 0g,在Z轴上为+ 1g。 加速度计的比例因子在工厂进行校准并且在名义上与电源电压无关。 每个传感器都有一个专用的sigma-delta ADC来提供数字输出。 数字输出的满量程范围可以调整到±2g,±4g,±8g或±16g。
总而言这,加速度传感器,其实是力传感器。用来检查上下左右前后哪几个面都受了多少力(包括重力),然后计算角度。
③ 数字运动处理器(DMP)
DMP就是指MPU6050内部集成的处理单元,可以直接运算出四元数和姿态,而不再需要另外进行数学运算。四元数就是4个数,可以表征姿态,经过几个数学公式之后就可以的出姿态,姿态包括pitch,roll,yaw。DMP的使用大大简化了代码设计,可想而知mpu9150(mpu6050)并不单单是一款传感器,其内部还包含了可以独立完成姿态解算算法的处理单元。
由DMP实现姿态解算算法将单片机从算法处理的压力中解放出来,单片机所要做的是等待DMP解算完成后产生的外部中断,在外部中断里去读取姿态解算的结果。这样单片机有大量的时间来处理其他任务,提高了系统的实时性。综上,dmp之后直接出结果,可以直接用,当然你如果有特殊需要自己还要加滤波也没有问题。
但是听说DMP的参考平面有点蛋疼。他解算出来的姿态角是以上电时的平面为基准平面的。也就是说每次上电都要把装置摆到绝对水平。但这有点困难。
④ 传感器数据寄存器
传感器数据寄存器包含最新的陀螺仪,加速度计,辅助传感器和温度测量数据。 它们是只读寄存器,可通过串行接口访问。 这些寄存器的数据可以随时读取。 但是,可以使用中断函数来确定新数据何时可用。
存储这些数据的寄存器均只是可读状态,称之为数据寄存器,注意:关于陀螺仪和加速度的寄存器均是16位的,也就是说在读取他们数据时,需要知道加速度/陀螺仪对应的两个寄存器的内部地址,并且读取出来的数据需要通过转换合成。
上图是陀螺仪数据寄存器,通过读取这6个寄存器,就可以读到陀螺仪 x/y/z 轴的值,比如 x 轴的数据,可以通过读取 0X43(高 8 位)和 0X44(低 8 位)寄存器得到,其他轴以此类推。
上图是加速度数据寄存器,通过读取这6个寄存器,就可以读到加速度传感器 x/y/z 轴的值,比如读 x 轴的数据,可以通过读取 0X3B(高 8 位)和0X3C(低8位)寄存器得到,其他轴以此类推。
⑤ 关于其他寄存器的设置及介绍
我是从这篇帖子里学习的,我已经码出来分享给大家
--------》 传送门
知道以上寄存器很重要,要知道寄存器每个位什么值对应着什么样的功能,还要知道其对应的内存地址,以供主机进行寻找。
⑥ FIFO缓冲器
MPU-60X0包含一个可通过串行接口访问的1024字节FIFO寄存器。 FIFO配置寄存器决定哪个数据写入FIFO。 可能的选择包括陀螺仪数据,加速计数据,温度读数,辅助传感器读数和 FSYNC 输入。 FIFO 计数器跟踪 FIFO 中包含的有效数据字节数。 FIFO寄存器支持突发读取。 中断功能可用于确定新数据何时可用。
⑦ 中断
中断功能通过中断配置寄存器进行配置。 可配置的项目包括INT引脚配置,中断锁存和清除方法以及中断触发器。 可触发中断的项目有:
(1)时钟发生器锁定到新的参考振荡器(用于切换时钟源);
(2)可以读取新数据(来自FIFO和数据寄存器);
(3)加速度计事件中断;
(4)MPU-60X0 没有收到辅助传感器的确认I2C总线。
中断状态可以从中断状态寄存器读取。
至此,了解了MPU6050的一些基础知识后,我们就可以通过代码建立IIC通信,让单片机与MPU6050进行通信。
在贴代码之前,需要明确以下几点:
A. 关于地址
---- 从设备的地址是用7bit来表示的,但是一个寄存器里有8个位,所以最终这个设备的地址是由已知的7位和第8位组成,那么第8位到底是什么呢?
规定 :在IIC通信开始发送一帧数据时,前面7bit是设备或者寄存器的地址,第8位则是读或者写的方向命令位,0为写命令,1为读命令。
就比如从官方所给资料来看,MPU6050的地址是0x68或者0x69,拿0x68举例,其表示如下:
7bit | 6bit | 5bit | 4bit | 3bit | 2bit | 1bit | 0bit |
---|---|---|---|---|---|---|---|
1 | 1 | 0 | 1 | 0 | 0 | 0 | AD0 |
前面7位(110 1000)构成从设备的地址0x68,而最后一位是未知的,或者说,是由用户自己定义设置的(AD0引脚),比如如果在MPU6050模块的AD0引脚接地,则最后一位表示0,那么加进去以后,地址就变成了1101 0000,转换成十六进制也就是0xD0,此处也代表写命令。
所以以后在主机寻找MPU6050这个设备时,需要写入一帧数据以表示寻找从机,这一帧数据就是0xD0,说明白点,0xD0是由设备物理地址+命令来构成最终的“门牌”,主机以后就是通过这个门牌来找到MPU6050,主机只要把“门牌”发送出去,系统的状态就代表着主机向从机写数据,一直持续到停止信号。
与设备物理地址不同,设备内部的寄存器地址是由8位构成,因此只需要在开启信号发起时,发送寻找MPU6050的“门牌”(相当于告诉了设备主机打算写入数据,只要还没有停止信号就代表后面的操作一直都是写信号),接着就可以继续发送寄存器的内存地址(8位地址,最后一位不再是表示读写命令,因为停止信号还没到来,所以状态一直是写的命令)。
B. 关于通信过程
主机写数据给从机时,先“广播”从机地址(相当于找到对应从机),再发送从机内部寄存器的地址(相当于找到对应从机的对应寄存器),然后主机再发送数据到该寄存器,主机发送完一字节数据后,从机需要发出一个应答信号(这个过程是从机把应答信号发送至主机的CY位)来告诉主机已接收完成一帧数据。
主机读从机数据时,要先"广播"从机地址,再发送从机内部寄存器的地址(相当于要找到数据的来源),然后直接读取一字节数据,读取完一字节数据后,主机向从机发送应答信号(相当于主机告诉从机已经读取到一字节)
综上,不管是从机还是主机,只要是被写入数据,当被写入完一字节数据时,都需要向对方发送应答信号。(相当于读别人的数据,这个过程比如:可以把主机给从机写数据的过程理解成从机读取主机的数据,同样,主机读取从机数据时可以理解成从机往主机里些数据)
代码实现及注释
STC89C52 + MPU6050 + LCD1602
.
// 功能: 显示加速度计和陀螺仪的10位原始数据
//****************************************
// 晶振:11.0592M
// 显示:LCD1602
#include <REG52.H>
#include <math.h> //Keil library
#include <stdio.h> //Keil library
#include <INTRINS.H> //这个库需要_nop_()函数
typedef unsigned char uchar; //类型定义
typedef unsigned short ushort;
typedef unsigned int uint;
#define DataPort P0 //LCD1602数据端口
sbit SCL=P1^0; //IIC时钟引脚定义
sbit SDA=P1^1; //IIC数据引脚定义
sbit LCM_RS=P2^0; //LCD1602命令端口
sbit LCM_RW=P2^1; //LCD1602命令端口
sbit LCM_EN=P2^2; //LCD1602命令端口
uchar dis[4]; //显示数字(-511至512)的字符数组
int dis_data; //变量
//看以下变量的时候对应着寄存器里面名称,容易有印象
#define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
#define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
#define ACCEL_XOUT_H 0x3B //X轴加速度数据寄存器1地址
#define ACCEL_XOUT_L 0x3C //X轴加速度数据寄存器2地址
#define ACCEL_YOUT_H 0x3D //Y轴加速度数据寄存器1地址
#define ACCEL_YOUT_L 0x3E //Y轴加速度数据寄存器2地址
#define ACCEL_ZOUT_H 0x3F //Z轴加速度数据寄存器1地址
#define ACCEL_ZOUT_L 0x40 //Z轴加速度数据寄存器2地址
#define TEMP_OUT_H 0x41 //温度传感器数据寄存器1地址
#define TEMP_OUT_L 0x42 //温度传感器数据寄存器2地址
#define GYRO_XOUT_H 0x43 //X轴陀螺仪数据寄存器1地址
#define GYRO_XOUT_L 0x44 //X轴陀螺仪数据寄存器2地址
#define GYRO_YOUT_H 0x45 //Y轴陀螺仪数据寄存器1地址
#define GYRO_YOUT_L 0x46 //Y轴陀螺仪数据寄存器2地址
#define GYRO_ZOUT_H 0x47 //Z轴陀螺仪数据寄存器1地址
#define GYRO_ZOUT_L 0x48 //Z轴陀螺仪数据寄存器2地址
#define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用)
#define SlaveAddress 0xD0 //IIC写入时的地址字节数据,+1为读取
//#define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读)
//****************************************
//函数声明
//****************************************
void delay(unsigned int k); //延时
void InitLcd(); //初始化lcd1602
void lcd_printf(uchar *s,int temp_data); //把整数转成字符串
void WriteDataLCM(uchar dataW); //向LCD写入数据
void WriteCommandLCM(uchar CMD,uchar Attribc); //LCD指令
void DisplayOneChar(uchar X,uchar Y,uchar DData); //显示一个字符
void DisplayListChar(uchar X,uchar Y,uchar *DData,L); //显示字符串
void InitMPU6050(); //初始化MPU6050
void Delay5us(); //IIC协议用到的一些延迟
void I2C_Start(); //开始信号
void I2C_Stop(); //停止信号
void I2C_SendACK(bit ack); //单片机向MPU6050发送应答
bit I2C_RecvACK(); //单片机接收MPU6050应答
void I2C_SendByte(uchar dat); //通过IIC,单片机向从机发送一字节数据
uchar I2C_RecvByte(); //通过IIC,单片机接收从机一字节数据
void I2C_ReadPage();
void I2C_WritePage();
void display_ACCEL_x(); //LCD显示X轴加速度信息
void display_ACCEL_y(); //LCD显示Y轴加速度信息
void display_ACCEL_z(); //LCD显示Z轴加速度信息
uchar Single_ReadI2C(uchar REG_Address); //读取I2C数据
void Single_WriteI2C(uchar REG_Address,uchar REG_data); //向I2C写入数据
//****************************************
//lcd相关
//****************************************
void lcd_printf(uchar *s,int temp_data) //整数转字符串
{
if(temp_data<0)
{
temp_data=-temp_data;
*s='-';
}
else *s=' ';
*++s =temp_data/100+0x30;
temp_data=temp_data%100; //取余运算
*++s =temp_data/10+0x30; //转成ASCII码
temp_data=temp_data%10; //取余运算
*++s =temp_data+0x30;
}
void delay(unsigned int k) //延迟函数
{
unsigned int i,j;
for(i=0;i<k;i++)
{
for(j=0;j<121;j++);
}
}
void WaitForEnable(void) //LCD1602写允许
{
DataPort=0xff;
LCM_RS=0;LCM_RW=1;_nop_();
LCM_EN=1;_nop_();_nop_();
while(DataPort&0x80);
LCM_EN=0;
}
void WriteCommandLCM(uchar CMD,uchar Attribc) //LCD1602写入命令
{
if(Attribc)WaitForEnable();
LCM_RS=0;LCM_RW=0;_nop_();
DataPort=CMD;_nop_();
LCM_EN=1;_nop_();_nop_();LCM_EN=0;
}
void WriteDataLCM(uchar dataW) //LCD1602写入数据
{
WaitForEnable();
LCM_RS=1;LCM_RW=0;_nop_();
DataPort=dataW;_nop_();
LCM_EN=1;_nop_();_nop_();LCM_EN=0;
}
void DisplayOneChar(uchar X,uchar Y,uchar DData) //LCD1602写入一个字符
{
Y&=1;
X&=15;
if(Y)X|=0x40;
X|=0x80;
WriteCommandLCM(X,0);
WriteDataLCM(DData);
}
void DisplayListChar(uchar X,uchar Y,uchar *DData,L) //LCD1602显示字符串
{
uchar ListLength=0;
Y&=0x1;
X&=0xF;
while(L--)
{
DisplayOneChar(X,Y,DData[ListLength]);
ListLength++;
X++;
}
}
void InitLcd() //LCD1602初始化
{
WriteCommandLCM(0x38,1); //设置8位格式,2行,5x7
WriteCommandLCM(0x08,1); //关闭显示,检测忙信号
WriteCommandLCM(0x01,1); //清除屏幕显示
WriteCommandLCM(0x06,1); //设定输入方式,增量不移位
WriteCommandLCM(0x0c,1); //整体显示,关光标,不闪烁
DisplayOneChar(0,0,'A'); //显示A字符在第0行第0位
DisplayOneChar(0,1,'G'); //总共有0,1两行,每行0~15个字符
}
//****************************************
//IIC相关
//****************************************
void Delay5us() //延时5微秒(STC90C52RC@12M)
{
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
}
void I2C_Start() //I2C起始信号
{
SDA = 1; //拉高数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 0; //产生下降沿
Delay5us(); //延时
SCL = 0; //拉低时钟线
}
void I2C_Stop() //I2C停止信号
{
SDA = 0; //拉低数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 1; //产生上升沿
Delay5us(); //延时
}
void I2C_SendACK(bit ack) //I2C发送应答信号,(0:ACK 1:NAK)
{
SDA = ack; //写应答信号
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
bit I2C_RecvACK() //I2C接收应答信号
{
SCL = 1; //拉高时钟线
Delay5us(); //延时
CY = SDA; //读应答信号,存在CY进位位
SCL = 0; //拉低时钟线
Delay5us(); //延时
return CY;
}
void I2C_SendByte(uchar dat) //向I2C总线发送一个字节数据
{
uchar i;
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1; //移出数据的最高位
SDA = CY; //送数据口
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
I2C_RecvACK();
}
uchar I2C_RecvByte() //从I2C总线接收一个字节数据
{
uchar i;
uchar dat = 0;
SDA = 1; //使能内部上拉,准备读取数据,
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1;
SCL = 1; //拉高时钟线
Delay5us(); //延时
dat |= SDA; //读数据
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
return dat;
}
void Single_WriteI2C(uchar REG_Address,uchar REG_data)//向I2C设备写入一个字节数据
{
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress); //发送设备地址+写信号
I2C_SendByte(REG_Address); //内部寄存器地址,这里的写信号由上面一行得来
I2C_SendByte(REG_data); //内部寄存器数据,
I2C_Stop(); //发送停止信号,持续到停止信号
}
uchar Single_ReadI2C(uchar REG_Address)//从I2C设备读取一个字节数据
{
uchar REG_data;
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress); //发送设备地址+写信号
I2C_SendByte(REG_Address); //发送存储单元地址,从0开始
//上面先进行写命令(广播),找到寄存器位置先(数据的来源)
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress+1); //发送设备地址+读信号
REG_data=I2C_RecvByte(); //读出寄存器数据
I2C_SendACK(1); //向从机发送应答信号
I2C_Stop(); //停止信号
return REG_data;
}
void InitMPU6050() //初始化MPU6050
{
Single_WriteI2C(PWR_MGMT_1, 0x00); //解除休眠状态
Single_WriteI2C(SMPLRT_DIV, 0x07); //设置陀螺仪采样率,典型值:0x07(125Hz)
Single_WriteI2C(CONFIG, 0x06); //低通滤波频率,典型值:0x06(5Hz)
Single_WriteI2C(GYRO_CONFIG, 0x18); //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
Single_WriteI2C(ACCEL_CONFIG, 0x01);//加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
}
//****************************************
//主程序相关
//****************************************
int GetData(uchar REG_Address) //将MPU6050内部的两个数据寄存器的数据进行合成
{
char H,L;
H=Single_ReadI2C(REG_Address);
L=Single_ReadI2C(REG_Address+1);
return (H<<8)+L; //合成数据,返回十六进制数
}
void Display10BitData(int value,uchar x,uchar y)//在1602上显示10位数据
{
value /= 64; //转换为10位数据
lcd_printf(dis, value); //转换数据显示,dis是dis[4]数组的头地址
DisplayListChar(x,y,dis,4); //启始列,行,显示数组,显示长度
}
void main()
{
delay(500); //上电延时
InitLcd(); //液晶初始化
InitMPU6050(); //初始化MPU6050
delay(150);
while(1)
{
Display10BitData(GetData(ACCEL_XOUT_H),2,0); //显示X轴加速度,第0行第2列
Display10BitData(GetData(ACCEL_YOUT_H),7,0); //显示Y轴加速度
Display10BitData(GetData(ACCEL_ZOUT_H),12,0); //显示Z轴加速度
Display10BitData(GetData(GYRO_XOUT_H),2,1); //显示X轴角速度
Display10BitData(GetData(GYRO_YOUT_H),7,1); //显示Y轴角速度
Display10BitData(GetData(GYRO_ZOUT_H),12,1); //显示Z轴角速度
delay(500);
}
}
直接复制以上代码,即可成功运行。
如有理解错误,往大家多多指正,谢谢!