MPU6050开发 -- 测试程序分析
如需轉載請注明出處:https://blog.csdn.net/qq_29350001/article/details/78623928
上一篇文章再 C52 單片機上進行了測試,那么接下來我們就分析一下測試程序。看看其中都用到哪些寄存器?
測試代碼,參看:MPU6050開發 -- 在 C52 單片機上測試
一、單片機介紹
這部分上一篇文章已經講了,我使用的是郭天祥的STC89C52單片機,該實驗板上面使用的外部晶振頻率是 11.0592MHz。因為STC89C52 沒有集成 I2C 控制器,那么我們就定義51單片機端口:
?
//****************************************
sbit ? ?SCL=P1^5; //IIC時鐘引腳定義
sbit ? ?SDA=P1^4; //IIC數據引腳定義
//****************************************
二、定義MPU6050內部地址 ?
這應該是本篇文章的重點了,找出這幾個重要的寄存器。
(1)寄存器25 - 采樣速率分頻器(SMPRT_DIV)
參數:
SMPLRT_DIV 為8位無符號值。 采樣率是通過將陀螺儀輸出速率除以該值來確定的。
描述:
該寄存器指定用于產生MPU-60X0采樣率的陀螺儀輸出速率的分頻器。傳感器寄存器輸出,FIFO輸出和DMP采樣都基于采樣率。采樣率是通過將陀螺儀輸出速率除以 SMPLRT_DIV 產生的:
采樣率=陀螺儀輸出速率/(1 + SMPLRT_DIV)
當DLPF禁用(DLPF_CFG = 0或7)時,陀螺儀輸出速率= 8kHz,當DLPF使能時(見寄存器26)為1kHz。
注意:加速度計輸出速率是1kHz。 這意味著對于大于1kHz的采樣率,同一個加速度計采樣可能會不止一次輸出到FIFO,DMP和傳感器寄存器。
(2)寄存器26 - 配置(CONFIG)
注:位7和位6保留
參數:
EXT_SYNC_SET 3位無符號值。 配置FSYNC引腳采樣。
DLPF_CFG 3位無符號值。 配置DLPF設置
描述:
該寄存器為陀螺儀和加速度計配置外部幀同步(FSYNC)引腳采樣和數字低通濾波器(DLPF)設置。
連接到FSYNC引腳的外部信號可以通過配置 EXT_SYNC_SET 進行采樣。
FSYNC 引腳的信號變化被鎖存,以便捕獲短閃光燈。 鎖存的FSYNC信號將按照寄存器 25 中定義的采樣速率進行采樣。采樣后,鎖存器將復位為當前的 FSYNC 信號狀態。
根據下表,取樣值將被報告在由 EXT_SYNC_SET 的值確定的傳感器數據寄存器中的最低有效位的位置。
DLPF由 DLPF_CFG 配置,加速度計和陀螺儀根據 DLPF_CFG 的值進行過濾,如下表所示。
(3)寄存器27 - 陀螺儀配置(GYRO_CONFIG)
注:位2到位0被保留。
參數:
XG_ST 設置此位將導致X軸陀螺儀執行自檢。
YG_ST 設置此位將使Y軸陀螺儀執行自檢。
ZG_ST 設置該位使Z軸陀螺儀執行自檢。
FS_SEL 2位無符號值。 選擇陀螺儀的全量程范圍。
描述:
該寄存器用于觸發陀螺儀自檢并配置陀螺儀的滿量程范圍。
陀螺儀自檢允許用戶測試機械和電氣部分陀螺儀。每個陀螺儀軸的自檢可通過控制該寄存器的XG_ST,YG_ST和ZG_ST位來激活。每個軸的自檢可以獨立進行,也可以同時進行。
當自檢被激活時,車載電子裝置將啟動適當的傳感器。這種驅動將使傳感器的檢測質量移動一段相當于預先確定的科里奧利力的距離。這種檢測質量位移導致傳感器輸出發生變化,這反映在輸出信號中。輸出信號用于觀察自檢響應。
自檢響應定義如下:
自檢響應=啟用自檢的傳感器輸出 - 未啟用自檢的傳感器輸出
每個陀螺儀軸的自檢限制在MPU-6000 / MPU-6050產品規格文件。當自檢的價值響應在產品規格的最小/最大范圍內,零件已通過自??檢。當自檢響應超過文檔中指定的最小/最大值時,該部分被認為是自檢失敗。
?
FS_SEL根據下表選擇陀螺儀輸出的滿量程范圍。
(4)寄存器28 - 加速度計配置(ACCEL_CONFIG)
參數:
XA_ST 當設置為1時,X軸加速度計執行自檢。
YA_ST 當設置為1時,Y軸加速度計執行自檢。
ZA_ST 設置為1時,Z軸加速計執行自檢。
AFS_SEL 2位無符號值。 選擇加速度計的全量程范圍。
描述:
該寄存器用于觸發加速度計自檢并配置加速度計滿量程范圍。該寄存器還配置數字高通濾波器(DHPF)。
加速度計自檢允許用戶測試加速度計的機械和電子部分。每個加速度計軸的自檢可通過控制該寄存器的XA_ST,YA_ST和ZA_ST位來激活。每個軸的自檢可以獨立進行,也可以同時進行。
當自檢被激活時,車載電子裝置將啟動適當的傳感器。這種致動模擬外力。被驅動的傳感器又將產生相應的輸出信號。輸出信號用于觀察自檢響應。
自測響應定義如下:
自檢響應=啟用自檢的傳感器輸出 - 未啟用自檢的傳感器輸出
MPU-6000 / MPU-6050產品規格文檔的電氣特性表中提供了每個加速度計軸的自檢限制。當自檢響應值在產品規格的最小/最大范圍內時,該部件已通過自??檢。當自測響應超過文檔中指定的最小/最大值時,該部分被認為是自檢失敗。
AFS_SEL 根據下表選擇加速度計輸出的滿量程范圍。
(5)寄存器59到64 - 加速度計測量 ACCEL_XOUT_H,ACCEL_XOUT_L,ACCEL_YOUT_H,ACCEL_YOUT_L,ACCEL_ZOUT_H和ACCEL_ZOUT_L
參數:
ACCEL_XOUT 16位二進制補碼值。存儲最近的X軸加速計測量值。
ACCEL_YOUT 16位二進制補碼值。存儲最近的Y軸加速度計測量值。
ACCEL_ZOUT 16位二進制補碼值。存儲最近的Z軸加速計測量值。
描述:
這些寄存器存儲最新的加速度計測量結果。
按照 寄存器25 中定義的采樣率將加速度計測量值寫入這些寄存器。
加速度計測量寄存器以及溫度測量寄存器,陀螺儀測量寄存器和外部傳感器數據寄存器由兩組寄存器組成:內部寄存器組和面向用戶的讀取寄存器組。
加速度計傳感器內部寄存器組內的數據總是以采樣率更新。同時,只要串行接口空閑,面向用戶的讀取寄存器組就會復制內部寄存器組的數據值。這保證了傳感器寄存器的突發讀取將從相同的采樣時刻讀取測量結果。請注意,如果不使用突發讀取,則用戶負責通過檢查數據就緒中斷來確保一組單個字節的讀取對應于單個采樣時刻。
每個16位加速度計的測量結果都在 ACCEL_FS(寄存器28)中定義了一個滿量程。對于每個滿量程設置,ACCEL_xOUT 中的每個 LSB 的加速度計靈敏度如下表所示。
(6)寄存器65和66 - 溫度測量(TEMP_OUT_H和TEMP_OUT_L)
參數:
TEMP_OUT 16位有符號值。存儲最近的溫度傳感器測量值。
描述:
這些寄存器存儲最新的溫度傳感器測量值。
溫度測量值按照 寄存器25 中定義的采樣率寫入這些寄存器。
這些溫度測量寄存器以及加速度計測量寄存器,陀螺儀測量寄存器和外部傳感器數據寄存器由兩組寄存器組成:內部寄存器組和面向用戶的讀取寄存器組。
溫度傳感器內部寄存器組內的數據始終以采樣率進行更新。
同時,只要串行接口空閑,面向用戶的讀取寄存器組就會復制內部寄存器組的數據值。這保證了傳感器寄存器的突發讀取將從相同的采樣時刻讀取測量結果。請注意,如果不使用突發讀取,則用戶負責通過檢查數據就緒中斷來確保一組單個字節的讀取對應于單個采樣時刻。
電氣規格表(MPU-6000 / MPU-6050產品規格文檔第6.4節)中提供了溫度傳感器的比例因子和偏移量。
對于給定的寄存器值,溫度(攝氏度)可以被計算為:
以℃為單位的溫度=(TEMP_OUT寄存器值作為有符號數量)/ 340 + 36.53
請注意,上述公式中的數學是十進制的。
(7)寄存器67至72 - 陀螺儀測量 GYRO_XOUT_H,GYRO_XOUT_L,GYRO_YOUT_H,GYRO_YOUT_L,GYRO_ZOUT_H和GYRO_ZOUT_L
參數:
GYRO_XOUT 16位二進制補碼值。存儲最新的X軸陀螺儀測量。
GYRO_YOUT 16位二進制補碼值。存儲最新的Y軸陀螺儀測量結果。
GYRO_ZOUT 16位二進制補碼值。存儲最新的Z軸陀螺儀測量結果。
描述:
這些寄存器存儲最近的陀螺儀測量結果。
陀螺儀測量值按寄存器25中定義的采樣率寫入這些寄存器。
這些陀螺儀測量寄存器以及加速度計測量寄存器,溫度測量寄存器和外部傳感器數據寄存器由兩組寄存器組成:內部寄存器組和面向用戶的讀取寄存器組。
陀螺儀傳感器內部寄存器組內的數據總是以采樣率更新。
同時,只要串行接口空閑,面向用戶的讀取寄存器組就會復制內部寄存器組的數據值。這保證了傳感器寄存器的突發讀取將從相同的采樣時刻讀取測量結果。請注意,如果不使用突發讀取,則用戶負責通過檢查數據就緒中斷來確保一組單個字節的讀取對應于單個采樣時刻。
每個16位陀螺儀測量具有在 FS_SEL(寄存器27)中定義的滿量程。對于每個滿量程設置,GYRO_xOUT中陀螺儀每個LSB的靈敏度如下表所示:
(8)寄存器107 - 電源管理1 (PWR_MGMT_1)
注:位4保留。
參數:
DEVICE_RESET 設置為1時,該位將所有內部寄存器復位為默認值。一旦復位完成,該位自動清零。每個寄存器的默認值可以在第3節找到。
SLEEP 當該位置1時,該位將MPU-60X0置于睡眠模式。
CYCLE 當該位設置為1且SLEEP被禁止時,MPU-60X0將循環。在睡眠模式和喚醒之間以LP_WAKE_CTRL(寄存器108)確定的速率從活動傳感器獲取單個樣本數據。
TEMP_DIS 設置為1時,該位禁用溫度傳感器。
CLKSEL 3位無符號值。 指定設備的時鐘源。
描述:
該寄存器允許用戶配置電源模式和時鐘源。它還提供了一些重置整個設備,以及一些禁用溫度傳感器。
通過將 SLEEP 設置為1,MPU-60X0 可以進入低功耗睡眠模式。當 CYCLE 設置為1而睡眠模式被禁用時,MPU-60X0 將進入循環模式。在周期模式下,器件在休眠模式和喚醒之間循環,以由 LP_WAKE_CTRL(寄存器108)確定的速率從加速計獲取單個采樣。要配置喚醒頻率,請使用 電源管理2寄存器(寄存器108)內的 LP_WAKE_CTRL。
MPU-60X0 時鐘源可選擇內部 8MHz 振蕩器,基于陀螺儀的時鐘或外部時鐘源。當選擇內部 8MHz 振蕩器或外部時鐘源作為時鐘源時,MPU-60X0 可以在陀螺儀禁用的低功耗模式下工作。
上電時,MPU-60X0 時鐘源默認為內部振蕩器。但是,強烈建議將器件配置為使用其中一個陀螺儀(或外部時鐘源)作為時鐘參考,以提高穩定性。時鐘源可以按照下表進行選擇。
(9)寄存器117 - 我是誰(WHO_AM_I)
注:位0和7保留。 (硬編碼為0)
參數:
WHO_AM_I包含MPU-60X0的6位I2C地址。
位6:位1的上電復位值為110 100。
描述:
該寄存器用于驗證設備的身份。 WHO_AM_I的內容是MPU-60X0的7位I2C地址的高6位。 MPU-60X0的I2C地址的最低有效位由AD0引腳的值決定。 該寄存器不反映AD0引腳的值。
該寄存器的默認值是0x68。
?
到此,涉及到的幾個重要寄存器講完了。
接下來是初始化MPU6050,但是我不曉得測試程序中提供的典型值是怎么得出來的。
三、延時
該測試程序中有兩個延時函數,需要了解一下。
(1)void Delay5us ()?
void Delay5us() { _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); }這里的 _nop_(); 是什么意思?
網上查詢,這個函數是延時一個機器周期的意思,它包含在頭文件intrins.h中,只要程序應用到_nop_(),就需要有頭文件intrins.h 的聲明。
那我們查看一下頭文件intrins.h:
用Keil打開工程將鼠標移動到 intrins.h,單擊右鍵,選擇 Open document <intrins.h>,即可打開該頭文件。
?
/*-------------------------------------------------------------------------- INTRINS.HIntrinsic functions for C51. Copyright (c) 1988-2010 Keil Elektronik GmbH and ARM Germany GmbH All rights reserved. --------------------------------------------------------------------------*/#ifndef __INTRINS_H__ #define __INTRINS_H__#pragma SAVE#if defined (__CX2__) #pragma FUNCTIONS(STATIC) /* intrinsic functions are reentrant, but need static attribute */ #endifextern void _nop_ (void); extern bit _testbit_ (bit); extern unsigned char _cror_ (unsigned char, unsigned char); extern unsigned int _iror_ (unsigned int, unsigned char); extern unsigned long _lror_ (unsigned long, unsigned char); extern unsigned char _crol_ (unsigned char, unsigned char); extern unsigned int _irol_ (unsigned int, unsigned char); extern unsigned long _lrol_ (unsigned long, unsigned char); extern unsigned char _chkfloat_(float); #if defined (__CX2__) extern int abs (int); #endif #if !defined (__CX2__) extern void _push_ (unsigned char _sfr); extern void _pop_ (unsigned char _sfr); #endif#pragma RESTORE#endif說明 _nop_() 確實是在頭文件intrins.h里聲明的。但是它是啥意思呢?我還是不清楚。
我快瘋了,我查了半天,只查到下面這個自寫的 delay.c 。但是 _nop_() 語句的句數是怎么計算出來的就不曉得了。
?
參看:一個相當重要的問題!!!問題!問題!
參看:1T單片機專用延時函數
/********************************************************************* * * delay.h * ********************************************************************* * 描 述: 網上收集整理的延時函數頭文件,試用源碼。 * 開發平臺: KEIL+HL-1/HJ-3G/HJ-C52開發板 ********************************************************************/ #ifndef DELAY_H #define DELAY_Hvoid delay1us(); void delay2us(); void delay5us(); void delay10us(); void delay20us(); void delay50us(); void delay100us(); void delay200us(); void delay500us(); void delay1ms(); void delay2ms(); void delay5ms(); void delay10ms(); void delay20ms(); void delay50ms(); void delay100ms(); void delay200ms(); void delay300ms(); void delay400ms(); void delay500ms(); void delay1000ms(); void delay2000ms(); void delay5000ms(); void delay1s(); void delay2s(); void delay5s(); void delay10s();void delay8(unsigned char t);/*1.6us--232us延時函數(fosc=11.0592M時)*/ void delay(unsigned int t);/*2.2us--77ms延時函數(fosc=11.0592M時)*/ void delayms(unsigned char t);/*一個毫秒級可變延時函數*/ void delays(float t);/*一個秒級可變延時函數*/ #endif /********************************************************************* * * delay.c * ********************************************************************* * 描 述: 網上收集整理的延時函數,試用源碼。 * 開發平臺: KEIL+HL-1/HJ-3G/HJ-C52開發板*/#include "stc.h"/*包含STC單片機頭文件*/ #include "hjc52.h"/*包含HJ-C52開發板頭文件*/ #include "delay.h"/*包含網上收集的延時函數頭文件*/ #include <intrins.h>/*包含含有_nop_()函數的頭文件*//*一些固定延時函數*//*1uS延時函數*/ /*FOSC=11.0592、12、22.1184MHZ時,適用*/ /*其他晶振頻率時,要調整_nop_();語句的句數,_nop_();語句的句數=FOSC-10*/ void delay1us() { #if FOSC==11059200 _nop_(); #elif FOSC==12000000 _nop_();_nop_(); #elif FOSC==22118400 _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_(); #endif }/*2uS延時函數*/ /*FOSC=11.0592、12、22.1184MHZ時,適用*/ /*其他晶振頻率時,要調整_nop_();語句的句數,_nop_();語句的句數=2*FOSC-10*/ void delay2us() { #if FOSC==11059200 _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_(); #elif FOSC==12000000 _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); #elif FOSC==22118400 _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); #endif }/*5uS延時函數*/ void delay5us() { delay8((FOSC/1000000*5-18)/10); }/*10uS延時函數*/ void delay10us() { delay8((FOSC/1000000*10-18)/10); }/*20uS延時函數*/ void delay20us() { delay8((FOSC/1000000*20-18)/10); } /*50uS延時函數*/ void delay50us() { delay8((FOSC/1000000*50-18)/10); } /*100uS延時函數*/ void delay100us() { delay((0.1*FOSC/1000-24)/13); } /*200uS延時函數*/ void delay200us() { delay((0.2*FOSC/1000-24)/13); } /*500uS延時函數*/ void delay500us() { delay((0.5*FOSC/1000-24)/13); } /*1mS延時函數*/ void delay1ms() { delay((1*FOSC/1000-24)/13); } /*2mS延時函數*/ void delay2ms() { delay((2*FOSC/1000-24)/13); } /*5mS延時函數*/ void delay5ms() { delay((5*FOSC/1000-24)/13); } /*10mS延時函數*/ void delay10ms() { delay((10*FOSC/1000-24)/13); } /*20mS延時函數*/ void delay20ms() { delayms(20); } /*50mS延時函數*/ void delay50ms() { delayms(50); } /*100mS延時函數*/ void delay100ms() { delayms(100); } /*200mS延時函數*/ void delay200ms() { delayms(200); } void delay300ms() { delayms(300); } void delay400ms() { delayms(400); }/*500mS延時函數*/ void delay500ms() { delayms(500); } void delay1000ms() { delayms(1000); } void delay2000ms() { delayms(2000); } void delay5000ms() { delayms(5000); }/*1S延時函數*/ void delay1s() { delays(1); } /*2S延時函數*/ void delay2s() { delays(2); } /*5S延時函數*/ void delay5s() { delays(5); } /*10S延時函數*/ void delay10s() { delays(10); }/*0.8us--116us延時函數(FOSC=22.1184M時)*/ /*1.5us--214us延時函數(FOSC=12M時)*/ /*1.6us--232us延時函數(FOSC=11.0592M時)*/ /*延時時間:(18+10t)/FOSC*/ /*最大延時:2568/FOSC*/ /*最小延時:18/FOSC*/ /*設要延時的時間為T(常量,單位為us),調用方式:delay8((FOSC/1000000*T-18)/10)*/ /*例如要延時的時間為100us,調用方式:delay8((FOSC/1000000*100-18)/10)*/ void delay8(unsigned char t) { unsigned char i; for(i=t;i;i--); }/*1.1us--38.5ms延時函數(FOSC=22.1184M時)*/ /*2us--70.9ms延時函數(FOSC=12M時)*/ /*2.2us--77ms延時函數(FOSC=11.0592M時)*/ /*延時時間:(13t+24)/FOSC /*最大延時:851979/FOSC /*最小延時:24/FOSC /*設要延時的時間為T(常量,單位為ms),調用方式:delay((T*FOSC/1000-24)/13)*/ /*例如要延時的時間為10ms,調用方式:delay((10*FOSC/1000-24)/13)*/ void delay(unsigned int t) { unsigned int i; for(i=t;i;i--); }/*一個毫秒級可變延時函數*/ /*功 能:實現與參數直接對應的時間(單位為毫秒)的延時*/ /*參 數:范圍1到255*/ /*返回值:無*/ void delayms(unsigned char t) { unsigned char j; for(j=t;j;j--) delay1ms();/*1ms延時*/ }/*一個秒級可變延時函數*/ /*功 能:實現與參數直接對應的時間(單位為秒)的延時*/ /*參 數:范圍0.01到42949672*/ /*返回值:無*/ void delays(float t) { unsigned int j; j=t*100; while(j--) { delay10ms();/*10ms延時*/ } }再有上面提到 _nop_(); 這個函數是延時一個機器周期的意思。那么接下來我們再看一下單片機幾個周期的介紹:
1)時鐘周期 ?
也稱為振蕩周期,定義為時鐘頻率的倒數(外接12MHZ的晶振,其時鐘周期就是1/12us),t它是單片機中最基本、最小的時間單位。在一個時鐘周期內,CPU僅完成一個最基本的動作?
2) 狀態周期 ?
它是時鐘周期的兩倍。?
3) 機器周期 ?
在一個操作周期內,單片機完成一項基本周期,如取指令、存儲器讀/寫等,它是由12個時鐘周期(6個狀態周期)組成。 ?我們用的單片機上時鐘頻率為 11.0592MHz,那么機器周期為 12x(1/11.0592) = 1.09us.
4) 指令周期 ?
它是指CPU執行一條指令的所需要的時間,一般一個指令周期含有1~4個機器周期。
?
總結:
我不管怎么實現的了,看名字Delay5us,即這個函數延時 5 us。
(2)void ?delay(unsigned int k); ? ? ? ?
void delay(unsigned int k) { unsigned int i,j; for(i=0;i<k;i++) { for(j=0;j<121;j++); } }這個是延時函數用到兩個for循環,內層for循環恒定為小于121,有外層for決定延時多少毫秒。
舉例,delay(500); ? ? //上電延時 ?500ms (毫秒)
這個內層恒定值的怎么得出的?是測出來的...
四、I2C通信協議實現
I2C通信我們已經講了,參看:MPU6050開發 -- 進階之I2C/SPI通信協議??
現在看一下,用程序怎么實現。
(1)I2C起始信號 ?
SCL 為高時SDA的下降沿。
?
?
//************************************************************************************************* //I2C起始信號 //************************************************************************************************* void I2C_Start() { SDA = 1; //拉高數據線 SCL = 1; //拉高時鐘線 Delay5us(); //延時 SDA = 0; //產生下降沿 Delay5us(); //延時 SCL = 0; //拉低時鐘線 }(2)I2C停止信號
SCL 為高時SDA的上升沿
//************************************************************************************************* //I2C停止信號 //************************************************************************************************* void I2C_Stop() { SDA = 0; //拉低數據線 SCL = 1; //拉高時鐘線 Delay5us(); //延時 SDA = 1; //產生上升沿 Delay5us(); //延時 }(3)應答
ACK:應答信號,在第 9 個時鐘周期 SCL 為高時,SDA 為低
NACK:拒絕信號,在第 9 個時鐘周期,SDA 一直為低
} //************************************************************************************************** //I2C發送應答信號 //入口參數:ack (0:ACK 1:NAK) //************************************************************************************************** void I2C_SendACK(bit ack) { SDA = ack; //寫應答信號 SCL = 1; //拉高時鐘線 Delay5us(); //延時 SCL = 0; //拉低時鐘線 Delay5us(); //延時 } //**************************************************************************************************** //I2C接收應答信號 //**************************************************************************************************** bit I2C_RecvACK() { SCL = 1; //拉高時鐘線 Delay5us(); //延時 CY = SDA; //讀應答信號 SCL = 0; //拉低時鐘線 Delay5us(); //延時 return CY; }
在接收應答中,有一個 CY = SDA; 這里的 CY 是什么?
程序狀態寄存器 PSW 是計算機系統的核心部件——運算器的一部分,PSW用來存放兩類信息:一類是體現當前指令執行結果的各種狀態信息,稱為狀態標志,如有無借位進位(CY位)、有無溢出(OF位)、結果正負(SF位)、結果是否為零(ZF位)、奇偶標志位(PF位)等;另一類是存放控制信息,稱為控制狀態,如允許中斷(IF位),跟蹤標志(TF位),方向標志(DF)等。
?
根據上面這段話可以得出,CY是PSW的進位標志。特點是:進位、借位CY=1;否則CY=0.
(4)發送數據
8個bit 發送一個應答 ACK,進位、借位CY=1;否則CY=0.
//***************************************************************************************************** //向I2C總線發送一個字節數據 //***************************************************************************************************** void I2C_SendByte(uchar dat) { uchar i; for (i=0; i<8; i++) //8位計數器 { dat <<= 1; //移出數據的最高位 SDA = CY; //送數據口 SCL = 1; //拉高時鐘線 Delay5us(); //延時 SCL = 0; //拉低時鐘線 Delay5us(); //延時 } I2C_RecvACK(); }(5)接收數據
//***************************************************************************************************** //從I2C總線接收一個字節數據 //****************************************************************************************************** uchar I2C_RecvByte() { 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; }(6)I2C設備寫入/讀取一個字節數據
這里有意思的就是讀/寫地址了,寫地址:11010000,讀地址:11010001
//***************************************************************************************************** //向I2C設備寫入一個字節數據 //***************************************************************************************************** void Single_WriteI2C(uchar REG_Address,uchar REG_data) { I2C_Start(); //起始信號 I2C_SendByte(SlaveAddress); //發送設備地址+寫信號 I2C_SendByte(REG_Address); //內部寄存器地址, I2C_SendByte(REG_data); //內部寄存器數據, I2C_Stop(); //發送停止信號 } //******************************************************************************************************* //從I2C設備讀取一個字節數據 //******************************************************************************************************* uchar Single_ReadI2C(uchar REG_Address) { 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; }五、初始化
(1)串口初始化
單片機這些寄存器,我懶得的搞了
//****************************************************************************************************** //串口初始化 //******************************************************************************************************* void init_uart() { TMOD=0x21; TH1=0xfd; //實現波特率9600(系統時鐘11.0592MHZ) TL1=0xfd; SCON=0x50; PS=1; //串口中斷設為高優先級別 TR0=1; //啟動定時器 TR1=1; ET0=1; //打開定時器0中斷 ES=1; EA=1; }(2)初始化MPU6050
這些寄存器,我們上面已經講了。但是它的典型值怎么來的呢?接下來我們分析一下。
//****************************************************************************************************** //初始化MPU6050 //****************************************************************************************************** void InitMPU6050() { Single_WriteI2C(PWR_MGMT_1, 0x00); //解除休眠狀態 Single_WriteI2C(SMPLRT_DIV, 0x07); Single_WriteI2C(CONFIG, 0x06); Single_WriteI2C(GYRO_CONFIG, 0x18); Single_WriteI2C(ACCEL_CONFIG, 0x01); }《1》PWR_MGMT_1 典型值為0x00 (正常啟用)
根據上面寄存器107 - 電源管理1 (PWR_MGMT_1)的介紹可知:
SLEEP 置0,為未休眠狀態
TEMP_DIS ?置0,可以使用溫度傳感器
CLKSEL 置0,MPU-60X0 時鐘源選擇的是內部 8MHz 振蕩器
《2》SMPLRT_DIV 典型值為0x07 (陀螺儀采用率 125Hz)
根據上面寄存器25 - 采樣速率分頻器(SMPRT_DIV)的介紹可知:
SMPLRT_DIV 置7,因為采樣率是通過將陀螺儀輸出速率除以該值來確定的。
采樣率=陀螺儀輸出速率/(1 + SMPLRT_DIV)
當DLPF禁用(DLPF_CFG = 0或7)時,陀螺儀輸出速率= 8kHz,當DLPF啟用時(見寄存器26)為1kHz。
根據下面寄存器26分析,數字低通濾波器(DLPF)啟用,此時陀螺儀輸出速率為 1KHz
那采樣率 = 1KHz /(1+7) = 125 Hz
《3》CONFIG 典型值為0x06 (低通濾波器頻率為 5Hz)
根據上面寄存器26 - 配置(CONFIG)的介紹可知:
EXT_SYNC_SET?置0,輸入禁用。又因為連接到FSYNC引腳的外部信號可以通過配置 EXT_SYNC_SET 進行采樣。
因此陀螺儀和加速度計配置外部幀同步(FSYNC)引腳采樣無法被禁用了。
DLPF_CFG 置6 ,數字低通濾波器(DLPF)啟用。
DLPF由 DLPF_CFG 配置,可知加速度計和陀螺儀的低通濾波器頻率為 5Hz
《4》GYRO_CONFIG 典型值為0x18(陀螺儀不自檢,輸出滿量程范圍為 ± 2000 °/s)
根據上面寄存器27 - 陀螺儀配置(GYRO_CONFIG 的介紹可知:
XG_ST,YG_ST和ZG_ST位 置0,陀螺儀無自檢功能
FS_SEL 置3,根據下表選擇陀螺儀輸出的滿量程范圍為 ± 2000 °/s
(5)ACCEL_CONFIG 典型值為0x01(加速度計不自檢,輸出的滿量程范圍為± 2g)
根據上面寄存器28 - 加速度計配置(ACCEL_CONFIG)的介紹可知:
XA_ST,YA_ST和ZA_ST位 置0,加速度計無自檢功能
AFS_SEL ??置0,根據下表選擇加速度計輸出的滿量程范圍為 ± 2g
該寄存器還配置數字高通濾波器(DHPF),但是寄存器手冊里,并未介紹高通濾波器頻率。
六、串口發送
(1)串口發送函數
//************************************************************************************************* //串口發送函數 //************************************************************************************************* void SeriPushSend(uchar send_data) { SBUF=send_data; while(!TI);TI=0; }參看:SBUF -- 百度百科
這里有一個 SBUF,全稱:serial data buffer,中文名為串行數據緩沖器。這個重疊的地址靠讀/寫指令區分:串行發送數據時,CPU向SBUF寫入數據,此時99H表示發送SBUF;串行接收數據時,CPU從SBUF讀出數據,此時99H表示接收SBUF。
參看:TI -- 百度百科
再有 TI,作為51系列單片機中的串行口通信發送請求中斷標志位。一旦TI=1,CPU就被告知產生了一個串行通信口中斷。
(2)ASCII 碼
SeriPushSend(0x20); //空格
SeriPushSend(0x0d); //回車 ?
SeriPushSend(0x0a); //換行
其中十六進制的 0x20、0x0d和0x0a 都是 ASCII碼,對應的是空格、回車和換行。
參看:C語言再學習 -- ASCII碼表(轉)
七、超級終端顯示10進制數據
(1)串口調試工具
//****************************************************************************************************** //超級終端(串口調試助手)上顯示10位數據 //****************************************************************************************************** void Display10BitData(int value) { uchar i; // value/=64; //轉換為10位數據 lcd_printf(dis, value); //轉換數據顯示 for(i=0;i<6;i++) { SeriPushSend(dis[i]); } // DisplayListChar(x,y,dis,4); //啟始列,行,顯示數組,顯示長度 }(2)合成數據
//****************************************************************************************************** //合成數據 //****************************************************************************************************** int GetData(uchar REG_Address) { uchar H,L; H=Single_ReadI2C(REG_Address); L=Single_ReadI2C(REG_Address+1); return ((H<<8)+L); //合成數據 }舉個例子,Display10BitData(GetData(ACCEL_XOUT_H)); ? ?//顯示X軸加速度 ?
宏定義可知:
#define ACCEL_XOUT_H ? ?0x3B ?
#define ACCEL_XOUT_L ? ?0x3C ?
則:ACCEL_XOUT_L =?ACCEL_XOUT_H + 1;
因此就得出了:
H=Single_ReadI2C(REG_Address); ?
L=Single_ReadI2C(REG_Address+1); ?
合成數據則為:(H<<8)+L); ?
?
這里說明下 ACCEL_XOUT 是16位二進制補碼值,存儲最近的X軸加速計測量值。
什么是二進制補碼?參看:C語言再學習 -- 負數
?
初始化MPU6050時我們講到?ACCEL_CONFIG ?典型值為0x01(加速度計不自檢,輸出的滿量程范圍為± 2g)
那么,ACCEL_xOUT 中的加速度計每個 LSB 的靈敏度應為?16384 LSB/g
初始化MPU6050時我們講到 GYRO_CONFIG ?典型值為0x18(陀螺儀不自檢,輸出滿量程范圍為 ± 2000 °/s)
同理,GYRO_xOUT中陀螺儀每個LSB的靈敏度為 16.4 LSB/°/s
八、數據分析
(1)MPU6050初始化設置總結
再講數據分析之前,我們先總結一些初始化MPU6050寄存器設置和加速度計/陀螺儀靈敏度
? ? PWR_MGMT_1 ?典型值為0x00 ?(正常啟用)
? ? SMPLRT_DIV ?典型值為0x07 (陀螺儀采用率 125Hz)
? ? CONFIG ?典型值為0x06 (低通濾波器頻率為 5Hz)
? ? GYRO_CONFIG ?典型值為0x18(陀螺儀不自檢,輸出滿量程范圍為 ± 2000 °/s)
? ? ACCEL_CONFIG ?典型值為0x01(加速度計不自檢,輸出的滿量程范圍為± 2g)
? ? ACCEL_xOUT 中的加速度計每個 LSB 的靈敏度應為 16384 LSB/g
? ? GYRO_xOUT中的陀螺儀每個 LSB 的靈敏度為 16.4 LSB/°/s
(2)數據分析
最后在串口調試工具上,我們來看看它得出的數據。
?
A 為三軸MEMS加速度計,G為三軸MEMS陀螺儀。挪動MPU6050模塊,則數據變化。
那么問題來了:
得到的這些數據是什么意思呢?
我們拿到這些數據怎么來做分析?
陀螺儀和加速度計的原理又是什么?
陀螺儀和加速度計沒有自檢,怎么檢測得到的數據是否正確?或者說怎么判斷這個MPU6050傳感器是不是好的?
再有現在這些數據是延時2秒后發送的(delay(2000); ),若把延時縮短那么最短能讓它多長時間發送?
?
請聽下回分解!!?
這篇講的有點多了,再開一篇文章接著講!!
如需轉載請注明出處:https://blog.csdn.net/qq_29350001/article/details/78623928
總結
以上是生活随笔為你收集整理的MPU6050开发 -- 测试程序分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Google可翻译Word或PDF文档
- 下一篇: 如何免费获取IEEE论文,亲测有效,【分