MPU6050的Arduino实现

Arduino有多种通信方式,每一种通信方式都有相对应的类库来支持:

1)硬件串口通信——HardwareSerial 类库

2)软件模拟串口通信——SoftwareSerial 类库

3)IIC总线的使用——Wire 类库

4)SPI总线的使用——SPI 类库

 

MPU6050的数据接口用的是I2C总线协议,因此我们需要Wire类库的帮助来实现Arduino与MPU6050之间的通信。

MPU6050的数据写入和读出均通过其芯片内部的寄存器实现,这些寄存器的地址都是1个字节,也就是8位的寻址空间,其寄存器的详细列表说明书请点击下载:

https://www.olimex.com/Products/Modules/Sensors/MOD-MPU6050/resources/RM-MPU-60xxA_rev_4.pdf

使用Arduino的Wire类库来学习MPU6050与arduino的IIC通信,比直接学习MPU6050与单片机或其他更高级芯片的通信要容易许多。在arduino平台上完成MPU6050的测试后,应该转而在其他平台上使用MPU6050。当然能够实现设备通信只是迈出了其中的一小步,更重要的是在获得惯性测量单元的数据后,会对数据进行处理。最后,能够使用MPU6050来做一些小项目是最好的,例如平衡车、四轴飞行器等等。
 

一、了解 wire.h

1.库包含的函数:

    1) Wire.begin()和Wire.begin(address):初始化IIC连接。无参数,设备以主机模式加入IIC总线;写参数,设备以从机模式加入IIC总线,address为一个7位的从机地址。语法为:

    begin():           

    begin(address): 

    返回值:none

   

    2) Wire.requestFrom():主机向从机发送数据请求信号(n*一个字节)。使用后,从机可以使用onRequest()注册一个事件以响应主机请求。随后,数据可以被主设备用available()和read()函数接收。语法为:

    Wire.requestFrom(address, quantity):设备(从机)的地址、请求的字节数

    Wire.requestFrom(address, quantity, stop):stop参数为boolean型值,值为true则发送停止命令,释放IIC总线;值为false则发送重新开始信息,并继续保持IIC总线的有效连接。

    返回值:none

 

    3) Wire.beginTransmission():设定传输数据到指定地址的从机设备。随后可以使用write()函数发送数据,并搭配endTransmission():函数结束数据传输。

    语法:wire.beginTransmission(address):

    返回值:none

 

    4) Wire.endTransmission():结束一个由beginTransmission()开始的并且由write()排列的从机的传输。语法为:

    Wire.endTransmission()

    Wire.endTransmission(stop):stop为biilean型值,值为true(没有填写stop参数时,等效使用true)则发送一个停止信息;值为false则发送一个重新开始信息,并继续保持IIC总线的有效连接。

    返回值:0 成功   1 数据溢出   2 发送addtess时从机接受到NACK   3 发送数据时接受到NACK   4 其他错误

 

    5) Wire.write():向从机发送数据(双向?主机状态:主机将要发送的数据加入发送队列;从机状态:从机发送数据至发起请求的主机)。语法为:

    Wire.write(value):value 要发送的数值(以单字节发送)

    Wire.write(string):string 字符组的指针(以一系列字节发送)

    Wire.write(data, length):data 一个字节数组(以字节形式发送数组);length 传输的字节数

    返回值:byte型值,返回输入的字节数。

 

    6) Wire.available():返回接收到的字节数

    在主机中,一般用于主机发送数据请求后;在从机中,一般用于数据接收事件。语法为:

    Wire.available()  无参数

    返回值:byte型值,返回输入的字节数。

 

    7) Wire.read():读取1B的数据

    在主机中,使用requestFrom()函数发送数据请求信号后,需要使用read()函数来获取数据;在从机中需要使用read()读取主机发送来的数据。语法为:

    Wire.read()  无参数

    char c = Wire.read():以字符串形式接收数据(将数据作为字符接收)

    int x = Wire.read():以整型形式接收数据

    返回值:读到的字节数据

 

    8) Wire.onReceive():在从机端注册一个事件,当从机收到主机发送的数据时即被触发。语法为:

    Wire.onReceive(handler):handler 当从机接收到数据时可被触发的事件。该事件带有一个int型参数(从主机读到的字节数)且没有返回值,如 void myHandler(int numBytes)

    返回值:none

 

    9) Wire.onRequest():注册一个事件,当从机接收到主机数据请求时即被触发

    Wire.onRequest(handler):handler  可被触发的事件。该事件不带参数和返回值,如 void myHandler() 。

    返回值:none

 

二、关于MPU6050:

1.关于输出精度:

    以MPU6050加速度测量值为例:量程是±8g时,测量精度是4096LSB/g,

    LSB的意思是最小有效位,为数字输出方式下使用;

    一般我们可以用mg/LSB来表示G-Sensor灵敏度,例如:mpu6050输出的位数为16位(2的16次方共65536个LSB)对应满量程,当量程为±2g时对应灵敏度就为4g/65536LSB=0.06103515625mg/LSB,取倒数为16384LSB/g。所以我们可以看到mpu605X寄存器手册中的accelerometers' sensitivity per LSB in ACCEL_xOUT:

MPU6050的Arduino实现_第1张图片

    因为mpu6050只能16位输出,所以测量范围越大,对应精度就越低。上面表格的数据存在一个规律,即:

    Full Scalse Range * LSB Sensitivity = 32768

(参考:http://www.geek-workshop.com/thread-9146-1-1.html)

 

三、基础使用:

以下实例实现 Arduino uno 与 MPU6050 的 IIC 通信,并通过 Arduino 与 PC 机的串口通讯将 MPU6050 的测量值打印在Arduino IDE 的串口监视器上。实例中未使用中断功能。读取的数据只经过单位的转换,未做其他数据处理(如果要应用在项目上,可能要对原始数据进行滤波处理、数学演算等,才能给程序使用。)

//连线方法
//MPU-UNO
//VCC-5V
//GND-GND
//SCL-A5
//SDA-A4
//ADO-GND
//未使用中断功能,即没有做 INT-digital pin 2 (interrupt pin 0) 这样的接线

//参考手册:MPU-6000 and MPU-6050 Register Map and Descriptions Revision 4.2

#include 

long accelX, accelY, accelZ;      // 定义为全局变量,可直接在函数内部使用
float gForceX, gForceY, gForceZ;

long gyroX, gyroY, gyroZ;
float rotX, rotY, rotZ;

void setup() {
  Serial.begin(9600);
  Wire.begin();
  setupMPU();
}

void loop() {
  recordAccelRegisters();
  recordGyroRegisters();
  printData();
  delay(100);
}

void setupMPU(){
  // REGISTER 0x6B/REGISTER 107:Power Management 1
  Wire.beginTransmission(0b1101000); //This is the I2C address of the MPU (b1101000/b1101001 for AC0 low/high datasheet Sec. 9.2)
  Wire.write(0x6B); //Accessing the register 6B/107 - Power Management (Sec. 4.30) 
  Wire.write(0b00000000); //Setting SLEEP register to 0, using the internal 8 Mhz oscillator
  Wire.endTransmission();

  // REGISTER 0x1b/REGISTER 27:Gyroscope Configuration
  Wire.beginTransmission(0b1101000); //I2C address of the MPU
  Wire.write(0x1B); //Accessing the register 1B - Gyroscope Configuration (Sec. 4.4) 
  Wire.write(0x00000000); //Setting the gyro to full scale +/- 250deg./s (转化为rpm:250/360 * 60 = 41.67rpm) 最高可以转化为2000deg./s 
  Wire.endTransmission();
  
  // REGISTER 0x1C/REGISTER 28:ACCELEROMETER CONFIGURATION
  Wire.beginTransmission(0b1101000); //I2C address of the MPU
  Wire.write(0x1C); //Accessing the register 1C - Acccelerometer Configuration (Sec. 4.5) 
  Wire.write(0b00000000); //Setting the accel to +/- 2g(if choose +/- 16g,the value would be 0b00011000)
  Wire.endTransmission(); 
}

void recordAccelRegisters() {
  // REGISTER 0x3B~0x40/REGISTER 59~64
  Wire.beginTransmission(0b1101000); //I2C address of the MPU
  Wire.write(0x3B); //Starting register for Accel Readings
  Wire.endTransmission();
  Wire.requestFrom(0b1101000,6); //Request Accel Registers (3B - 40)

  // 使用了左移<<和位运算|。Wire.read()一次读取1bytes,并在下一次调用时自动读取下一个地址的数据
  while(Wire.available() < 6);  // Waiting for all the 6 bytes data to be sent from the slave machine (必须等待所有数据存储到缓冲区后才能读取) 
  accelX = Wire.read()<<8|Wire.read(); //Store first two bytes into accelX (自动存储为定义的long型值)
  accelY = Wire.read()<<8|Wire.read(); //Store middle two bytes into accelY
  accelZ = Wire.read()<<8|Wire.read(); //Store last two bytes into accelZ
  processAccelData();
}

void processAccelData(){
  gForceX = accelX / 16384.0;     //float = long / float
  gForceY = accelY / 16384.0; 
  gForceZ = accelZ / 16384.0;
}

void recordGyroRegisters() {
  // REGISTER 0x43~0x48/REGISTER 67~72
  Wire.beginTransmission(0b1101000); //I2C address of the MPU
  Wire.write(0x43); //Starting register for Gyro Readings
  Wire.endTransmission();
  Wire.requestFrom(0b1101000,6); //Request Gyro Registers (43 ~ 48)
  while(Wire.available() < 6);
  gyroX = Wire.read()<<8|Wire.read(); //Store first two bytes into accelX
  gyroY = Wire.read()<<8|Wire.read(); //Store middle two bytes into accelY
  gyroZ = Wire.read()<<8|Wire.read(); //Store last two bytes into accelZ
  processGyroData();
}

void processGyroData() {
  rotX = gyroX / 131.0;
  rotY = gyroY / 131.0; 
  rotZ = gyroZ / 131.0;
}

void printData() {
  Serial.print("Gyro (deg)");
  Serial.print(" X=");
  Serial.print(rotX);
  Serial.print(" Y=");
  Serial.print(rotY);
  Serial.print(" Z=");
  Serial.print(rotZ);
  Serial.print(" Accel (g)");
  Serial.print(" X=");
  Serial.print(gForceX);
  Serial.print(" Y=");
  Serial.print(gForceY);
  Serial.print(" Z=");
  Serial.println(gForceZ);
}

代码中相关设置的依据以及使用的寄存器: 
1)MPU6050的IIC地址:
MPU6050的Arduino实现_第2张图片
2)电源管理寄存器:
MPU6050的Arduino实现_第3张图片
3)陀螺仪配置:
MPU6050的Arduino实现_第4张图片
4)加速度计配置:
MPU6050的Arduino实现_第5张图片
5)加速度计测量值:
MPU6050的Arduino实现_第6张图片
6)陀螺仪测量值:
MPU6050的Arduino实现_第7张图片
三、简单应用:
 

你可能感兴趣的:(Embedded,system)