聲明:本人跟隨b站江科大學習,本文章是觀看完視頻后的一些個人總結和經驗分享,也同時為了方便日后的復習,如果有錯誤請各位大佬指出,如果對你有幫助可以點個贊小小鼓勵一下,本文章建議配合原視頻使用??
如果你也正在學習STM32可以訂閱本專欄,后續將不定期更新( ? 3?)??
如有侵權,請私信聯系刪除
文章目錄
- 前言
- 理論部分
- 有關IIC理論部分
- MPU6050
- 參數和簡介
- 硬件電路
- 內部框圖
- IIC通訊外設
- IIC外設簡介
- IIC框圖
- IIC基本結構(一主多從)
- 時序---主機發送
- 時序---主機接受
- 軟件和硬件的波形對比(上為軟件,下為硬件)
- 代碼部分
- 軟件模擬iic
- 硬件模擬iic
前言
- 弄清IIC時序結構以及硬件要求
- MPU6050的基本參數和大概使用方法
- 一般情況下軟件實現iic比硬件更為常用,但是硬件也有其優勢,比如:可節省軟件資源,可實現完整的多主機通訊模型,時序波形規整,通訊速率快,一般用于對性能指標要求高,軟件iic則更為靈活,比如iic總線理論上可無限開辟,而硬件iic外設有限
理論部分
有關IIC理論部分
有關iic的理論知識可以看一下我的這篇文章iic的時序結構,就不做多余贅述了,個人覺得up在32里面講iic比51清楚,有興趣的小伙伴還是建議看一下32的iic原視頻
MPU6050
參數和簡介
- 理論上陀螺儀傳感器是可以直接測量處具體角度的,但這里只能測量出角加速度,最后的角度的話需要另外計算,比如對角速度積分
加速度計具有靜態穩定性,不具有動態穩定性,內部工作原理其實就是F=ma,m在里面為一個已知小球重量,用類似于彈簧測力計的東西測量出小球對一個面的壓力,用壓力除以m,就是該面方向上的加速度
陀螺儀計具有動態穩定性,不具有靜態穩定性,因為靜止時角速度值會因為噪聲而無法完全歸零,經過積分的不斷累積,這個小噪聲就會導致積分會產生一個角度的緩慢漂移,最后就不準確
- 16位的ADC就是2^16=65536,但是由于加速度計和陀螺儀計都是存在方向的,所以就平分65536,一半為正值,一半為負值
- 不同量程的選擇,測量范圍也會變化,量程越大測量范圍越大,但是分辨率會變低
- 從機地址有兩種表示方法,一種是將7位作為從機地址,那么110 1000就是0x68,那么要加上讀寫位就是0x68 | 讀寫位,也可以表示為11010000,那么就是加上讀寫位表示從機地址,也就是0xD0,那么0xD0就是寫地址,0xD1就是讀地址
硬件電路
- XCL和XDA用于擴展設備,例如氣壓計等等
- AD0用于配置從機地址最低位,也就是第8位,默認是0
- 若使用中斷,則中斷可引起INT電平跳變
內部框圖
IIC通訊外設
IIC外設簡介
- 10位地址模式:也就是前五位必須是11110作為10位尋址標志位,然后第一個字節有7個地址位和一位讀寫位,第二個字節有8個地址位,那么第一個字節的7個地址位中的前五位為標志位,后兩位和第二個字節的8位為地址位,總共10位就是1024種情況
- SMBus是系統管理總線,主要用于電源管理系統,了解即可
IIC框圖
主要分為SDA和SCL兩個部分,SDA主要與DR和數據移位寄存器有關,其過程類似于串口,SCL主要和CCR,CR,SR有關,其余部分了解即可
IIC基本結構(一主多從)
- 由于高位先行,所以移位寄存器是向左移位,主要通訊過程還是看移位寄存器和DR的配合,一個SCL時鐘移位一次,移位8次可將一個字節放在SDA線上,大概流程就是,先將數據寫入DR,當移位寄存器是空的時候,就轉到移位寄存器然后發送
(對應后面時序的數據寄存器空和移位寄存器空)
- GPIO口都要配置成復用開漏輸出模式,因為使用的是片上外設
時序—主機發送
- EVx(Event x)即事件x,使用事件來表示相當于一個大的標志位包含多個小標志位,有專門的函數來讀取這個大標志位,也就是配置CR會產生多個不同標志位組合形成了EVx這個大標志位
時序—主機接受
需要注意的是在使用多字節接受時EV7之前需要清除應答位和停止條件的產生位,然后再檢測EV7事件的發生,最后要將ACK應答位置1,防止產生更多讀取多字節的操作
軟件和硬件的波形對比(上為軟件,下為硬件)
大概的區別:
- 軟件波形不規整,SCL高電平占空比不一致,硬件則十分規整
- 軟件中SDA在SCL拉低SCL變換數據時存在較大延時,硬件則延時很小,基本上在SCL拉低的瞬間就立馬變換SDA
- 圖中RA應答結束時,硬件產生了一個小尖峰,而軟件則產生一個峰值延遲,這是在RA結束時,從機立馬就釋放了SDA將SDA操縱權歸還主機,讓主機準備發送下一個數據0,但是這里軟件主機延遲了一會才拉低SDA,將0放在SDA線上,硬件則是主機立馬就拉低SDA,所以沒有產生那個峰值延遲而是產生一個尖峰
代碼部分
軟件模擬iic
軟件模擬說明白了就是實現代碼模擬時序結構,達到發送字節的效果,和以前51里面的寫法基本相同
MyI2C.c
#include "stm32f10x.h" // Device header
#include "Delay.h"/*引腳配置層*//*** 函 數:I2C寫SCL引腳電平* 參 數:BitValue 協議層傳入的當前需要寫入SCL的電平,范圍0~1* 返 回 值:無* 注意事項:此函數需要用戶實現內容,當BitValue為0時,需要置SCL為低電平,當BitValue為1時,需要置SCL為高電平*/
void MyI2C_W_SCL(uint8_t BitValue)
{GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue); //根據BitValue,設置SCL引腳的電平Delay_us(10); //延時10us,防止時序頻率超過要求
}/*** 函 數:I2C寫SDA引腳電平* 參 數:BitValue 協議層傳入的當前需要寫入SDA的電平,范圍0~1* 返 回 值:無* 注意事項:此函數需要用戶實現內容,當BitValue為0時,需要置SDA為低電平,當BitValue為1時,需要置SDA為高電平*/
void MyI2C_W_SDA(uint8_t BitValue)
{GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue); //根據BitValue,設置SDA引腳的電平,BitValue要實現非0即1的特性Delay_us(10); //延時10us,防止時序頻率超過要求
}/*** 函 數:I2C讀SDA引腳電平* 參 數:無* 返 回 值:協議層需要得到的當前SDA的電平,范圍0~1* 注意事項:此函數需要用戶實現內容,當前SDA為低電平時,返回0,當前SDA為高電平時,返回1*/
uint8_t MyI2C_R_SDA(void)
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11); //讀取SDA電平Delay_us(10); //延時10us,防止時序頻率超過要求return BitValue; //返回SDA電平
}/*** 函 數:I2C初始化* 參 數:無* 返 回 值:無* 注意事項:此函數需要用戶實現內容,實現SCL和SDA引腳的初始化*/
void MyI2C_Init(void)
{/*開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //開啟GPIOB的時鐘/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); //將PB10和PB11引腳初始化為開漏輸出/*設置默認電平*/GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11); //設置PB10和PB11引腳初始化后默認為高電平(釋放總線狀態)
}/*協議層*//*** 函 數:I2C起始* 參 數:無* 返 回 值:無*/
void MyI2C_Start(void)
{MyI2C_W_SDA(1); //釋放SDA,確保SDA為高電平//其實先釋放SCL也可以,但是若SCL還被未釋放在高電平時,//此時SDA被釋放,根據停止的時序結構就會產生一個停止信號MyI2C_W_SCL(1); //釋放SCL,確保SCL為高電平MyI2C_W_SDA(0); //在SCL高電平期間,拉低SDA,產生起始信號MyI2C_W_SCL(0); //起始后把SCL也拉低,即為了占用總線,也為了方便總線時序的拼接
}/*** 函 數:I2C終止* 參 數:無* 返 回 值:無*/
void MyI2C_Stop(void)
{MyI2C_W_SDA(0); //拉低SDA,確保SDA為低電平MyI2C_W_SCL(1); //釋放SCL,使SCL呈現高電平MyI2C_W_SDA(1); //在SCL高電平期間,釋放SDA,產生終止信號
}/*** 函 數:I2C發送一個字節* 參 數:Byte 要發送的一個字節數據,范圍:0x00~0xFF* 返 回 值:無*/
void MyI2C_SendByte(uint8_t Byte)
{uint8_t i;for (i = 0; i < 8; i ++) //循環8次,主機依次發送數據的每一位{/*兩個!可以對數據進行兩次邏輯取反,作用是把非0值統一轉換為1,即:!!(0) = 0,!!(非0) = 1*/MyI2C_W_SDA(!!(Byte & (0x80 >> i)));//使用掩碼的方式取出Byte的指定一位數據并寫入到SDA線MyI2C_W_SCL(1); //釋放SCL,從機在SCL高電平期間讀取SDAMyI2C_W_SCL(0); //拉低SCL,主機開始發送下一位數據}
}/*** 函 數:I2C接收一個字節* 參 數:無* 返 回 值:接收到的一個字節數據,范圍:0x00~0xFF*/
uint8_t MyI2C_ReceiveByte(void)
{uint8_t i, Byte = 0x00; //定義接收的數據,并賦初值0x00,此處必須賦初值0x00,后面會用到MyI2C_W_SDA(1); //接收前,主機先確保釋放SDA,避免干擾從機的數據發送for (i = 0; i < 8; i ++) //循環8次,主機依次接收數據的每一位{MyI2C_W_SCL(1); //釋放SCL,主機機在SCL高電平期間讀取SDAif (MyI2C_R_SDA()){Byte |= (0x80 >> i);} //讀取SDA數據,并存儲到Byte變量//當SDA為1時,置變量指定位為1,當SDA為0時,不做處理,指定位為默認的初值0MyI2C_W_SCL(0); //拉低SCL,從機在SCL低電平期間寫入SDA}return Byte; //返回接收到的一個字節數據
}/*** 函 數:I2C發送應答位* 參 數:Byte 要發送的應答位,范圍:0~1,0表示應答,1表示非應答* 返 回 值:無*/
void MyI2C_SendAck(uint8_t AckBit)
{MyI2C_W_SDA(AckBit); //主機把應答位數據放到SDA線MyI2C_W_SCL(1); //釋放SCL,從機在SCL高電平期間,讀取應答位MyI2C_W_SCL(0); //拉低SCL,開始下一個時序模塊
}/*** 函 數:I2C接收應答位* 參 數:無* 返 回 值:接收到的應答位,范圍:0~1,0表示應答,1表示非應答*/
uint8_t MyI2C_ReceiveAck(void)
{uint8_t AckBit; //定義應答位變量MyI2C_W_SDA(1); //接收前,主機先確保釋放SDA,避免干擾從機的數據發送MyI2C_W_SCL(1); //釋放SCL,主機機在SCL高電平期間讀取SDAAckBit = MyI2C_R_SDA(); //將應答位存儲到變量里MyI2C_W_SCL(0); //拉低SCL,開始下一個時序模塊return AckBit; //返回定義應答位變量
}
MyI2C.h
#ifndef __MYI2C_H
#define __MYI2C_Hvoid MyI2C_Init(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendAck(uint8_t AckBit);
uint8_t MyI2C_ReceiveAck(void);#endif
MPU6050.c
#include "stm32f10x.h" // Device header
#include "MyI2C.h"
#include "MPU6050_Reg.h"#define MPU6050_ADDRESS 0xD0 //MPU6050的I2C從機地址/*** 函 數:MPU6050寫寄存器* 參 數:RegAddress 寄存器地址,范圍:參考MPU6050手冊的寄存器描述* 參 數:Data 要寫入寄存器的數據,范圍:0x00~0xFF* 返 回 值:無*/
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{MyI2C_Start(); //I2C起始MyI2C_SendByte(MPU6050_ADDRESS); //發送從機地址,讀寫位為0,表示即將寫入MyI2C_ReceiveAck(); //接收應答MyI2C_SendByte(RegAddress); //發送寄存器地址MyI2C_ReceiveAck(); //接收應答MyI2C_SendByte(Data); //發送要寫入寄存器的數據MyI2C_ReceiveAck(); //接收應答MyI2C_Stop(); //I2C終止
}/*** 函 數:MPU6050讀寄存器* 參 數:RegAddress 寄存器地址,范圍:參考MPU6050手冊的寄存器描述* 返 回 值:讀取寄存器的數據,范圍:0x00~0xFF*/
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{uint8_t Data;MyI2C_Start(); //I2C起始MyI2C_SendByte(MPU6050_ADDRESS); //發送從機地址,讀寫位為0,表示即將寫入MyI2C_ReceiveAck(); //接收應答MyI2C_SendByte(RegAddress); //發送寄存器地址MyI2C_ReceiveAck(); //接收應答MyI2C_Start(); //I2C重復起始MyI2C_SendByte(MPU6050_ADDRESS | 0x01); //發送從機地址,讀寫位為1,表示即將讀取MyI2C_ReceiveAck(); //接收應答Data = MyI2C_ReceiveByte(); //接收指定寄存器的數據MyI2C_SendAck(1); //發送應答,給從機非應答,終止從機的數據輸出MyI2C_Stop(); //I2C終止return Data;
}/*** 函 數:MPU6050初始化* 參 數:無* 返 回 值:無*/
void MPU6050_Init(void)
{MyI2C_Init(); //先初始化底層的I2C/*MPU6050寄存器初始化,需要對照MPU6050手冊的寄存器描述配置,此處僅配置了部分重要的寄存器*/MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01); //電源管理寄存器1,取消睡眠模式,選擇時鐘源為X軸陀螺儀MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00); //電源管理寄存器2,保持默認值0,所有軸均不待機MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09); //采樣率分頻寄存器,配置采樣率MPU6050_WriteReg(MPU6050_CONFIG, 0x06); //配置寄存器,配置DLPFMPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); //陀螺儀配置寄存器,選擇滿量程為±2000°/sMPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18); //加速度計配置寄存器,選擇滿量程為±16g
}/*** 函 數:MPU6050獲取ID號* 參 數:無* 返 回 值:MPU6050的ID號*/
uint8_t MPU6050_GetID(void)
{return MPU6050_ReadReg(MPU6050_WHO_AM_I); //返回WHO_AM_I寄存器的值
}/*** 函 數:MPU6050獲取數據* 參 數:AccX AccY AccZ 加速度計X、Y、Z軸的數據,使用輸出參數的形式返回,范圍:-32768~32767* 參 數:GyroX GyroY GyroZ 陀螺儀X、Y、Z軸的數據,使用輸出參數的形式返回,范圍:-32768~32767* 返 回 值:無*/
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{uint8_t DataH, DataL; //定義數據高8位和低8位的變量DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H); //讀取加速度計X軸的高8位數據DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L); //讀取加速度計X軸的低8位數據*AccX = (DataH << 8) | DataL; //數據拼接,通過輸出參數返回DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H); //讀取加速度計Y軸的高8位數據DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L); //讀取加速度計Y軸的低8位數據*AccY = (DataH << 8) | DataL; //數據拼接,通過輸出參數返回DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H); //讀取加速度計Z軸的高8位數據DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L); //讀取加速度計Z軸的低8位數據*AccZ = (DataH << 8) | DataL; //數據拼接,通過輸出參數返回DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H); //讀取陀螺儀X軸的高8位數據DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L); //讀取陀螺儀X軸的低8位數據*GyroX = (DataH << 8) | DataL; //數據拼接,通過輸出參數返回DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H); //讀取陀螺儀Y軸的高8位數據DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L); //讀取陀螺儀Y軸的低8位數據*GyroY = (DataH << 8) | DataL; //數據拼接,通過輸出參數返回DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H); //讀取陀螺儀Z軸的高8位數據DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L); //讀取陀螺儀Z軸的低8位數據*GyroZ = (DataH << 8) | DataL; //數據拼接,通過輸出參數返回
}
MPU6050.h
#ifndef __MPU6050_H
#define __MPU6050_Hvoid MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);#endif
MPU6050_Reg
#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H#define MPU6050_SMPLRT_DIV 0x19
#define MPU6050_CONFIG 0x1A
#define MPU6050_GYRO_CONFIG 0x1B
#define MPU6050_ACCEL_CONFIG 0x1C#define MPU6050_ACCEL_XOUT_H 0x3B
#define MPU6050_ACCEL_XOUT_L 0x3C
#define MPU6050_ACCEL_YOUT_H 0x3D
#define MPU6050_ACCEL_YOUT_L 0x3E
#define MPU6050_ACCEL_ZOUT_H 0x3F
#define MPU6050_ACCEL_ZOUT_L 0x40
#define MPU6050_TEMP_OUT_H 0x41
#define MPU6050_TEMP_OUT_L 0x42
#define MPU6050_GYRO_XOUT_H 0x43
#define MPU6050_GYRO_XOUT_L 0x44
#define MPU6050_GYRO_YOUT_H 0x45
#define MPU6050_GYRO_YOUT_L 0x46
#define MPU6050_GYRO_ZOUT_H 0x47
#define MPU6050_GYRO_ZOUT_L 0x48#define MPU6050_PWR_MGMT_1 0x6B
#define MPU6050_PWR_MGMT_2 0x6C
#define MPU6050_WHO_AM_I 0x75#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"uint8_t ID; //定義用于存放ID號的變量
int16_t AX, AY, AZ, GX, GY, GZ; //定義用于存放各個數據的變量int main(void)
{/*模塊初始化*/OLED_Init(); //OLED初始化MPU6050_Init(); //MPU6050初始化/*顯示ID號*/OLED_ShowString(1, 1, "ID:"); //顯示靜態字符串ID = MPU6050_GetID(); //獲取MPU6050的ID號OLED_ShowHexNum(1, 4, ID, 2); //OLED顯示ID號while (1){MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ); //獲取MPU6050的數據OLED_ShowSignedNum(2, 1, AX, 5); //OLED顯示數據OLED_ShowSignedNum(3, 1, AY, 5);OLED_ShowSignedNum(4, 1, AZ, 5);OLED_ShowSignedNum(2, 8, GX, 5);OLED_ShowSignedNum(3, 8, GY, 5);OLED_ShowSignedNum(4, 8, GZ, 5);}
}
硬件模擬iic
硬件模擬也就是使用寫好的庫函數控制DR,SR,CR各個寄存器,而電平的翻轉由硬件控制,寫入CR或者DR就可以控制時序單元的發生,時序單元發生后就可以檢查對應的EVx,也就是檢查SR,然后等待時序單元的發送完成,然后依次操作寄存器,等待時序單元發送,如此循環,這是發送部分;接受部分就是寫入CR讀取DR,產生時序單元,等待事件和時序單元的完成,如此循環
MPU6050.c
#include "stm32f10x.h" // Device header
#include "MPU6050_Reg.h"#define MPU6050_ADDRESS 0xD0 //MPU6050的I2C從機地址/*** 函 數:MPU6050等待事件* 參 數:同I2C_CheckEvent* 返 回 值:無*/
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{uint32_t Timeout;Timeout = 10000; //給定超時計數時間while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS) //循環等待指定事件{Timeout --; //等待時,計數值自減if (Timeout == 0) //自減到0后,等待超時{/*超時的錯誤處理代碼,可以添加到此處*/break; //跳出等待,不等了}}
}/*** 函 數:MPU6050寫寄存器* 參 數:RegAddress 寄存器地址,范圍:參考MPU6050手冊的寄存器描述* 參 數:Data 要寫入寄存器的數據,范圍:0x00~0xFF* 返 回 值:無*/
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成起始條件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); //硬件I2C發送從機地址,方向為發送MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6I2C_SendData(I2C2, RegAddress); //硬件I2C發送寄存器地址MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING); //等待EV8I2C_SendData(I2C2, Data); //硬件I2C發送數據MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //等待EV8_2I2C_GenerateSTOP(I2C2, ENABLE); //硬件I2C生成終止條件
}/*** 函 數:MPU6050讀寄存器* 參 數:RegAddress 寄存器地址,范圍:參考MPU6050手冊的寄存器描述* 返 回 值:讀取寄存器的數據,范圍:0x00~0xFF*/
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{uint8_t Data;I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成起始條件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter); //硬件I2C發送從機地址,方向為發送MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6I2C_SendData(I2C2, RegAddress); //硬件I2C發送寄存器地址MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //等待EV8_2I2C_GenerateSTART(I2C2, ENABLE); //硬件I2C生成重復起始條件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver); //硬件I2C發送從機地址,方向為接收MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //等待EV6I2C_AcknowledgeConfig(I2C2, DISABLE); //在接收最后一個字節之前提前將應答失能I2C_GenerateSTOP(I2C2, ENABLE); //在接收最后一個字節之前提前申請停止條件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED); //等待EV7Data = I2C_ReceiveData(I2C2); //接收數據寄存器I2C_AcknowledgeConfig(I2C2, ENABLE); //將應答恢復為使能,為了不影響后續可能產生的讀取多字節操作return Data;
}/*** 函 數:MPU6050初始化* 參 數:無* 返 回 值:無*/
void MPU6050_Init(void)
{/*開啟時鐘*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); //開啟I2C2的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //開啟GPIOB的時鐘/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); //將PB10和PB11引腳初始化為復用開漏輸出/*I2C初始化*/I2C_InitTypeDef I2C_InitStructure; //定義結構體變量I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; //模式,選擇為I2C模式I2C_InitStructure.I2C_ClockSpeed = 50000; //時鐘速度,選擇為50KHzI2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //時鐘占空比,選擇Tlow/Thigh = 2I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //應答,選擇使能I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //應答地址,選擇7位,從機模式下才有效I2C_InitStructure.I2C_OwnAddress1 = 0x00; //自身地址,從機模式下才有效I2C_Init(I2C2, &I2C_InitStructure); //將結構體變量交給I2C_Init,配置I2C2/*I2C使能*/I2C_Cmd(I2C2, ENABLE); //使能I2C2,開始運行/*MPU6050寄存器初始化,需要對照MPU6050手冊的寄存器描述配置,此處僅配置了部分重要的寄存器*/MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01); //電源管理寄存器1,取消睡眠模式,選擇時鐘源為X軸陀螺儀MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00); //電源管理寄存器2,保持默認值0,所有軸均不待機MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09); //采樣率分頻寄存器,配置采樣率MPU6050_WriteReg(MPU6050_CONFIG, 0x06); //配置寄存器,配置DLPFMPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); //陀螺儀配置寄存器,選擇滿量程為±2000°/sMPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18); //加速度計配置寄存器,選擇滿量程為±16g
}/*** 函 數:MPU6050獲取ID號* 參 數:無* 返 回 值:MPU6050的ID號*/
uint8_t MPU6050_GetID(void)
{return MPU6050_ReadReg(MPU6050_WHO_AM_I); //返回WHO_AM_I寄存器的值
}/*** 函 數:MPU6050獲取數據* 參 數:AccX AccY AccZ 加速度計X、Y、Z軸的數據,使用輸出參數的形式返回,范圍:-32768~32767* 參 數:GyroX GyroY GyroZ 陀螺儀X、Y、Z軸的數據,使用輸出參數的形式返回,范圍:-32768~32767* 返 回 值:無*/
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{uint8_t DataH, DataL; //定義數據高8位和低8位的變量DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H); //讀取加速度計X軸的高8位數據DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L); //讀取加速度計X軸的低8位數據*AccX = (DataH << 8) | DataL; //數據拼接,通過輸出參數返回DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H); //讀取加速度計Y軸的高8位數據DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L); //讀取加速度計Y軸的低8位數據*AccY = (DataH << 8) | DataL; //數據拼接,通過輸出參數返回DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H); //讀取加速度計Z軸的高8位數據DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L); //讀取加速度計Z軸的低8位數據*AccZ = (DataH << 8) | DataL; //數據拼接,通過輸出參數返回DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H); //讀取陀螺儀X軸的高8位數據DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L); //讀取陀螺儀X軸的低8位數據*GyroX = (DataH << 8) | DataL; //數據拼接,通過輸出參數返回DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H); //讀取陀螺儀Y軸的高8位數據DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L); //讀取陀螺儀Y軸的低8位數據*GyroY = (DataH << 8) | DataL; //數據拼接,通過輸出參數返回DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H); //讀取陀螺儀Z軸的高8位數據DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L); //讀取陀螺儀Z軸的低8位數據*GyroZ = (DataH << 8) | DataL; //數據拼接,通過輸出參數返回
}
MPU6050.h
#ifndef __MPU6050_H
#define __MPU6050_Hvoid MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);#endif
MPU6050_reg.h
#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H#define MPU6050_SMPLRT_DIV 0x19
#define MPU6050_CONFIG 0x1A
#define MPU6050_GYRO_CONFIG 0x1B
#define MPU6050_ACCEL_CONFIG 0x1C#define MPU6050_ACCEL_XOUT_H 0x3B
#define MPU6050_ACCEL_XOUT_L 0x3C
#define MPU6050_ACCEL_YOUT_H 0x3D
#define MPU6050_ACCEL_YOUT_L 0x3E
#define MPU6050_ACCEL_ZOUT_H 0x3F
#define MPU6050_ACCEL_ZOUT_L 0x40
#define MPU6050_TEMP_OUT_H 0x41
#define MPU6050_TEMP_OUT_L 0x42
#define MPU6050_GYRO_XOUT_H 0x43
#define MPU6050_GYRO_XOUT_L 0x44
#define MPU6050_GYRO_YOUT_H 0x45
#define MPU6050_GYRO_YOUT_L 0x46
#define MPU6050_GYRO_ZOUT_H 0x47
#define MPU6050_GYRO_ZOUT_L 0x48#define MPU6050_PWR_MGMT_1 0x6B
#define MPU6050_PWR_MGMT_2 0x6C
#define MPU6050_WHO_AM_I 0x75#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"uint8_t ID; //定義用于存放ID號的變量
int16_t AX, AY, AZ, GX, GY, GZ; //定義用于存放各個數據的變量int main(void)
{/*模塊初始化*/OLED_Init(); //OLED初始化MPU6050_Init(); //MPU6050初始化/*顯示ID號*/OLED_ShowString(1, 1, "ID:"); //顯示靜態字符串ID = MPU6050_GetID(); //獲取MPU6050的ID號OLED_ShowHexNum(1, 4, ID, 2); //OLED顯示ID號while (1){MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ); //獲取MPU6050的數據OLED_ShowSignedNum(2, 1, AX, 5); //OLED顯示數據OLED_ShowSignedNum(3, 1, AY, 5);OLED_ShowSignedNum(4, 1, AZ, 5);OLED_ShowSignedNum(2, 8, GX, 5);OLED_ShowSignedNum(3, 8, GY, 5);OLED_ShowSignedNum(4, 8, GZ, 5);}
}