要特別說(shuō)明下:所有的數(shù)據(jù)位包括應(yīng)答位都需要主設(shè)備產(chǎn)生時(shí)鐘脈沖。如果從設(shè)備沒(méi)有應(yīng)答意味著將沒(méi)有更多的數(shù)據(jù)要傳送或者設(shè)備沒(méi)有準(zhǔn)備好傳送。這時(shí),主設(shè)備要么產(chǎn)生停止信號(hào),要么重新發(fā)出開(kāi)始條件。

圖 7_6 應(yīng)答信號(hào)
n I2C的7-bit地址:
上面說(shuō)過(guò)每一個(gè)從設(shè)備都應(yīng)該具有唯一的地址,這樣主設(shè)備才能準(zhǔn)確的尋址到每一個(gè)設(shè)備,而這些地址被統(tǒng)一規(guī)定為7比特。但是上面講過(guò)I2C總線傳輸數(shù)據(jù)都是8比特傳送,地址7比特豈不是少一位!其實(shí)緊跟地址還有一位用來(lái)表示是讀操作還是寫(xiě)操作的標(biāo)志位。如果該位為0表示主設(shè)備將要向從設(shè)備寫(xiě)數(shù)據(jù),否則表示主設(shè)備將要從從設(shè)備讀數(shù)據(jù)。在這8比特被發(fā)送后主設(shè)備能夠持續(xù)地進(jìn)行讀或者寫(xiě)。如果主設(shè)備想和其他從設(shè)備進(jìn)行通信,只要再次發(fā)送一個(gè)新的開(kāi)始信號(hào)就可以而不必發(fā)送終止信號(hào)。

圖 7_7 一個(gè)完整的數(shù)據(jù)讀寫(xiě)操作
8 、MPU6050驅(qū)動(dòng)設(shè)計(jì)
至此,我們基本上已經(jīng)將I2C的知識(shí)學(xué)完了,下面將結(jié)合MPU6050的驅(qū)動(dòng)進(jìn)一步講解其原理(該部分的代碼參見(jiàn)工程的mpu6050.c部分)。我們首先來(lái)看一下它的頭文件mpu6050.h:從第6到25行上來(lái)就是一大串內(nèi)部地址的定義,對(duì)于初學(xué)者可能一頭霧水!如果樓主再引入寄存器等數(shù)字電路的知識(shí)可能又要說(shuō)幾頁(yè)了,于是這里準(zhǔn)備只用一個(gè)簡(jiǎn)單的例子闡述下這些地址的作用。
#include“i2c.h”
//-----------------------------------------
// 定義MPU6050內(nèi)部地址
//-----------------------------------------
#define SMPLRT_DIV 0x19 //陀螺儀采樣率,典型值:0x07(125Hz)
#define CONFIG 0x1A //低通濾波頻率,典型值:0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺儀自檢及測(cè)量范圍,典型值:0x18(不自檢,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速計(jì)自檢、測(cè)量范圍及高通濾波頻率,典型值:0x01(不自檢,2G,5Hz)
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define PWR_MGMT_1 0x6B //電源管理,典型值:0x00(正常啟用)
#define WHO_AM_I 0x75 //IIC地址寄存器(默認(rèn)數(shù)值0x68,只讀)
#define SlaveAddress 0xD0 //IIC寫(xiě)入時(shí)的地址字節(jié)數(shù)據(jù),+1為讀取
//-----------------------------------------
// 通過(guò)I2C和MPU6050通信的函數(shù)
//-----------------------------------------
void Single_WriteI2C(uchar REG_Address,uchar REG_data);//向I2C設(shè)備寫(xiě)入一個(gè)字節(jié)數(shù)據(jù)
uchar Single_ReadI2C(uchar REG_Address); //從I2C設(shè)備讀取一個(gè)字節(jié)數(shù)據(jù)
void InitMPU6050(); //初始化MPU6050
int GetData(uchar REG_Address); //合成數(shù)據(jù)
上面講到在I2C總線中主設(shè)備可以通過(guò)固定的7-bit地址尋找到相應(yīng)的從設(shè)備(這里的7-bit地址為第26行的SlaveAddress,想必大家也能夠理解后面注釋的意義了吧~不加1表示緊跟著地址的一位為0,表示向該設(shè)備寫(xiě)數(shù)據(jù);加1則表示緊跟著的一位為1,表示主設(shè)備從從設(shè)備讀數(shù)據(jù))。雖然采用這種方式能夠準(zhǔn)確找到從設(shè)備,但是從設(shè)備里面又有比較多的寄存器。這就好比你知道了某個(gè)要找的東西在具體的某個(gè)大柜子里,但是來(lái)到大柜子前又發(fā)現(xiàn)有許多小抽屜。這里的7-bit地址就好像指明了哪個(gè)柜子,而從第6到25行的內(nèi)部地址就像柜子上的抽屜編號(hào),而不一樣之處是位于mpu6050內(nèi)的“小抽屜”一部分存放著其采集的實(shí)時(shí)數(shù)據(jù),另一部分等著外部放一些數(shù)據(jù)來(lái)設(shè)置其采樣屬性。
這樣,如上面的第6行的SMPLRT_DIV(0x19)是用來(lái)設(shè)置陀螺儀采樣率的寄存器地址,只要向該地址所指的寄存器寫(xiě)入相應(yīng)的值則可以設(shè)置陀螺儀采樣率。因此下面MPU6050初始化函數(shù)就是調(diào)用封裝的I2C寫(xiě)函數(shù)向相應(yīng)的小抽屜內(nèi)寫(xiě)屬性數(shù)據(jù),設(shè)置MPU6050采樣屬性。
//------------------------------------------------
//初始化MPU6050
//------------------------------------------------
void InitMPU6050()
{
Single_WriteI2C(PWR_MGMT_1, 0x00); //解除休眠狀態(tài)
Single_WriteI2C(SMPLRT_DIV, 0x07);
Single_WriteI2C(CONFIG, 0x06);
Single_WriteI2C(GYRO_CONFIG, 0x18);
Single_WriteI2C(ACCEL_CONFIG, 0x01);
}
再如第10~11行的ACCEL_XOUT_H、ACCEL_XOUT_L是用來(lái)存放最新的陀螺儀X極的數(shù)值,因?yàn)椴捎?6位ADC所以這里需要用兩個(gè)寄存器。所以下面合成數(shù)據(jù)函數(shù)負(fù)責(zé)連續(xù)讀取REG_Address開(kāi)始的兩字節(jié)數(shù)據(jù)組成一個(gè)16位數(shù)據(jù)。當(dāng)函數(shù)的參數(shù)為ACCEL_XOUT_H時(shí),則獲取的是實(shí)時(shí)的陀螺儀X極的數(shù)值,同樣地可以獲得實(shí)時(shí)的6軸數(shù)據(jù)。
//------------------------------------------------
//合成數(shù)據(jù)
//------------------------------------------------
int GetData(uchar REG_Address)
{
uchar H,L;
H=Single_ReadI2C(REG_Address);
L=Single_ReadI2C(REG_Address+1);
return (H《《8)+L; //合成數(shù)據(jù)
}
電子發(fā)燒友App






評(píng)論