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只能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地址:
2)电源管理寄存器:
3)陀螺仪配置:
4)加速度计配置:
5)加速度计测量值:
6)陀螺仪测量值:
三、简单应用: