IIC通信詳解 —— 基於MPU6050模塊
從IIC通信原理到使用 —— MPU6050
IIC通信可以簡單地理解成就是數據的通信,就是單片機(主機)與設備(從機)之間的一種通信協議,兩者必須遵從這個協議才能正確的相互讀寫數據,它這個通信過程只要兩根線就能給完成,分別是SCL(時鐘線)和SDA(數據線);
這個主機與從機的關係一定要理解好,因爲下一次不一定是單片機,有些從機也可以作爲主機,可以先把主機理解成能夠向其他設備發送指令/命令的一方,比如如果你作爲主機,你是想讀的時候纔去讀的,想寫的時候就去寫,比較主動;而如果你作爲從機,這就由不得你,你只能聽從主機的命令,主機說要讀,你就要把數據給出去,主機說要寫,你就得乖乖被寫。
現在假設單片機作爲主機,連接在SDA和SCL的兩條線上的設備均是它的從機,比如下面圖所示:
到這裏我們已經能夠有一個IIC主從設備的概念了,那麼假如單片機想要通過IIC來讀取從設備的信息應該都要做什麼呢?也就是說,整個IIC通信過程應該是怎麼樣子實現的?
一、IIC通信協議
在解釋之前,可以先知道一個這個協議不協議的到底是怎麼實現的,打比喻說只有一個主機一個從機時,大家先定一個協議,如果某根線的電平怎麼怎麼樣,同時另外一根線的電平又怎麼怎麼樣的時候,對應的是要讀?還是要寫?還是其他什麼?上面這個過程也就相當於主機發出的指令,從機通過兩根線的電平狀態來確定主機的指令是什麼。(注意,不要簡單地理解爲 00 01 10 11 四種狀態)。指令是通過兩根線來發送,裏面的數據也是通過那兩根線,所以你至少要定義你什麼時候是指令什麼時候是數據吧?所以就會有協議這種東西。
IIC總線在傳輸數據的過程中一共有三種類型信號(指令),分別爲:開始信號、結束信號和應答信號。這些信號中,起始信號是必需的,結束信號和應答信號,都可以不要。同時我們還要介紹其空閒狀態、數據的有效性、數據傳輸。看到協議不要害怕,我們只需要先知道它內部的原理,至於怎麼來實現,後面再看代碼進行解釋。
①. 空閒狀態
當SDA和SCL兩條信號線同時處於高電平時,規定爲總線的空閒狀態。這種狀態就相當於沒有設備在佔用通信通道,大家各自在自家互不搭理。正常情況下,當系統開始工作,如果主從不通信時就希望它是空閒狀態,也就是說開機後默認是空閒狀態,那麼這兩個高電平可以由上拉電阻來完成。
②. 開始信號
當SCL爲高期間,SDA由高到低的跳變;啓動信號是一種電平跳變時序信號,而不是一個電平信號。
③. 結束信號
當SCL爲高期間,SDA由低到高的跳變;停止信號也是一種電平跳變時序信號,而不是一個電平信號。
④. 應答信號
發射器 定義要發送數據的一端爲發射器,當主機往從機寫命令時,可以理解成主機作爲發射器,當主機讀取從機數據時,可以理解成從機往主機寫數據,這時候從機就是發射器;
實現過程 發送器每發送一個字節(1byte = 8bit),8個時鐘脈衝用來儲存數據內容,在第9個時鐘脈衝釋放數據線,由接收器反饋一個應答信號。應答信號爲低電平時,規定爲有效應答位(ACK,簡稱應答位),表示接收器已經成功地接收了該字節;應答信號爲高電平時,規定爲非應答位(NACK),一般表示接收器接收該字節沒有成功。
⑤. 數據的有效性
在數據傳送時,SCL爲高電平期間,SDA上的數據必須保持穩定,只有在時鐘線上的信號爲低電平期間,數據線上的高電平或低電平狀態才允許變化。 即:數據在SCL的上升沿到來之前就需準備好。並在在下降沿到來之前必須穩定。
以上就是協議的一些內容,在發送數據時必須得遵守協議,開啓讀取、讀取完成之後一定要發送停止信號來釋放總線。我們可以接着看數據的發送過程是怎麼實現的。
二、通過IIC協議實現收發
因爲在總線上可能會有很多從設備,不止一個,所以主機在發送或者接受數據的時候,必須要知道從機的設備地址(不同設備有自己不同的設備物理地址),相當於如果你要去某個人的家,至少你要先知道他們家的地址。
知道了這個物理地址後,也就相當於主機能在衆多從設備中找到了想要找的從設備,但是一個完整設備意味着它有自己的芯片,有自己的儲存單元,所以主機不僅僅只是要找到這個設備,還要找到該設備裏的主機想要讀寫的存儲單元(寄存器),相當於你要去某個人的家找一個人,你到了他家之後,還要在他衆多的家人中找到你要找的那個人。
當主機確定了從機設備、從機設備的寄存器,就能往裏面進行讀寫數據了,而這個讀寫數據的過程就必須得按照這個IIC協議來完成,這麼說可能還比較抽象。下面先介紹MPU6050,再結合IIC來實現單片機從MPU6050讀取數據。
三、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進行通信。
四、單片機與MPU6050的IIC實現
在貼代碼之前,需要明確以下幾點:
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);
}
}
直接複製以上代碼,即可成功運行。
如有理解錯誤,往大家多多指正,謝謝!