STM32江科大----IIC

聲明:本人跟隨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);}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/900543.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/900543.shtml
英文地址,請注明出處:http://en.pswp.cn/news/900543.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用 React 和 Konva 實現一個在線畫板組件

文章目錄 一、前言二、Konva.js 介紹三、創建 React 畫板項目3.1 安裝依賴3.2 創建 CanvasBoard 組件 四、增加畫布控制功能4.1 清空畫布4.2 撤銷 & 重做功能 五、增加顏色和畫筆大小選擇5.1 選擇顏色5.2 選擇畫筆大小 六、最終效果七、總結 一、前言 在線畫板是許多應用&…

服務器配置虛擬IP

服務器配置虛擬IP的核心步驟取決于具體場景&#xff0c;主要包括本地單機多IP配置和高可用集群下的虛擬IP管理兩種模式。? 一、本地虛擬IP配置&#xff08;單服務器多IP&#xff09; ?基于Linux系統?&#xff1a; ?確認網絡接口?&#xff1a;使用 ip addr 或 ifconfig 查…

C++ —— 文件操作(流式操作)

C —— 文件操作&#xff08;流式操作&#xff09; ofstream文件創建文件寫入 ofstream 文件打開模式std::ios::out 寫入模式std::ios::app 追加模式std::ios::trunc 截斷std::ios::binary 二進制std::ios::ate at the end模式 ifstreamstd::ios::in 讀取模式&#xff08;默認&…

【Cursor】打開Vscode設置

在這里打開設置界面 打開設置json

智能指針和STL庫學習思維導圖和練習

思維導圖&#xff1a; #include <iostream> #include <vector> #include <string> using namespace std;// 用戶結構體 struct User {string username;string password; };vector<User> users; // 存儲所有注冊用戶// 使用迭代器查找用戶名是否存在 ve…

前端工具方法整理

文章目錄 1.在數組中找到匹配項&#xff0c;然后創建新對象2.對象轉JSON字符串3.JSON字符串轉JSON對象4.有個響應式對象&#xff0c;然后想清空所有屬性5.判斷參數不為空6.格式化字符串7.解析數組內容用逗號拼接8.刷新整個頁面 1.在數組中找到匹配項&#xff0c;然后創建新對象…

狀態空間建模與極點配置 —— 理論、案例與交互式 GUI 實現

目錄 狀態空間建模與極點配置 —— 理論、案例與交互式 GUI 實現一、引言二、狀態空間建模的基本理論2.1 狀態空間模型的優勢2.2 狀態空間模型的物理意義三、極點配置的理論與方法3.1 閉環系統的狀態反饋3.2 極點配置條件與方法3.3 設計流程四、狀態空間建模與極點配置的優缺點…

仿modou庫one thread one loop式并發服務器

源碼&#xff1a;田某super/moduo 目錄 SERVER模塊&#xff1a; Buffer模塊&#xff1a; Socket模塊&#xff1a; Channel模塊&#xff1a; Connection模塊&#xff1a; Acceptor模塊&#xff1a; TimerQueue模塊&#xff1a; Poller模塊&#xff1a; EventLoop模塊&a…

Oracle中的UNION原理

Oracle中的UNION操作用于合并多個SELECT語句的結果集&#xff0c;并自動去除重復行。其核心原理可分為以下幾個步驟&#xff1a; 1. 執行各個子查詢 每個SELECT語句獨立執行&#xff0c;生成各自的結果集。 如果子查詢包含過濾條件&#xff08;如WHERE&#xff09;、排序&…

面試算法高頻04-分治與回溯

分治與回溯 分治和回溯算法&#xff0c;包括其概念、特性、代碼模板&#xff0c;并結合具體題目進行講解&#xff0c;旨在幫助學員理解和掌握這兩種算法的應用。 分治與回溯的概念 分治&#xff08;Divide & Conquer&#xff09;&#xff1a;本質上基于遞歸&#xff0c;先…

線性方程組的解法

文章目錄 線性方程組的解法認識一些基本的矩陣函數MATLAB 實現機電工程學院教學函數構造1.高斯消元法2.列主元消去法3. L U LU LU分解法 線性方程組的解法 看到以下線性方程組的一般形式&#xff1a;設有以下的 n n n階線性方程組&#xff1a; A x b \mathbf{Ax}\mathbf{b} A…

Java的Selenium的特殊元素操作與定位之模態框

Modal Dialogue Box&#xff0c;又叫做模式對話框&#xff0c;是指在用戶想要對對話框以外的應用程序進行操作時&#xff0c;必須首先對該對話框進行響應。如單擊【確定】或【取消】按鈕等將該對話框關閉。 alert&#xff08;警告&#xff09; //訪問本地的HTML文件 chromeDr…

2022年全國職業院校技能大賽 高職組 “大數據技術與應用” 賽項賽卷(1卷)任務書

2022年全國職業院校技能大賽 高職組 “大數據技術與應用” 賽項賽卷&#xff08;1卷&#xff09;任務書 背景描述&#xff1a;模塊A&#xff1a;大數據平臺搭建&#xff08;容器環境&#xff09;&#xff08;15分&#xff09;任務一&#xff1a;Hadoop 完全分布式安裝配置任務二…

題目練習之set的奇妙使用

???~~~~~~歡迎光臨知星小度博客空間~~~~~~??? ???零星地變得優秀~也能拼湊出星河~??? ???我們一起努力成為更好的自己~??? ???如果這一篇博客對你有幫助~別忘了點贊分享哦~??? ???如果有什么問題可以評論區留言或者私信我哦~??? ?????? 個…

Java虛擬機——JVM(Java Virtual Machine)解析一

1.JVM是什么&#xff1f; 1.1 JVM概念 Java Virtual Machine (JVM) 是JDK的核心組件之一&#xff0c;它使得 Java 程序能夠在任何支持 JVM 的設備或操作系統上運行&#xff0c;而無需修改源代碼 JDK是什么&#xff0c;JDK和JVM是什么關系&#xff1f;1.Java IDE(Integrated …

初識 Three.js:開啟你的 Web 3D 世界 ?

3D 技術已經不再是游戲引擎的專屬&#xff0c;隨著瀏覽器技術的發展&#xff0c;我們完全可以在網頁上實現令人驚艷的 3D 效果。而 Three.js&#xff0c;作為 WebGL 的封裝庫&#xff0c;讓 Web 3D 的大門向更多開發者敞開了。 這是我開啟這個 Three.js 專欄的第一篇文章&…

OpenGL ES -> SurfaceView + EGL實現立方體紋理貼圖+透視效果

XML文件 <?xml version"1.0" encoding"utf-8"?> <com.example.myapplication.MySurfaceView xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"…

pikachu靶場搭建教程,csfr實操

靶場安裝 靶場下載地址 百度網盤下載地址和密碼 百度網盤 請輸入提取碼 0278 github靶場下載地址 https://gitcode.com/Resource-Bundle-Collection/c7cc1 安裝前提 這兩個文件夾的配置文件都要進行更改修改數據庫密碼 D:\phpstudy_pro\WWW\pikachu\inc D:\phpstudy_pro…

浙江大學DeepSeek系列專題線上公開課第二季第四期即將上線!端云協同:讓AI更懂你的小心思! - 張圣宇 研究員

今晚8點10分左右&#xff0c;端云協同&#xff1a;讓AI更懂你的小心思&#xff01;浙大學者張圣宇研究員將揭秘人機交互新玩法。浙江大學DeepSeek系列專題線上公開課第二季第四期即將上線&#xff01; 講座 主題&#xff1a; 大小模型端云協同賦能人機交互 主講人&#xff1a…

Vue3實戰三、Axios封裝結合mock數據、Vite跨域及環境變量配置

目錄 Axios封裝、調用mock接口、Vite跨域及環境變量配置封裝Axios對象調用mock接口數據第一步、安裝axios&#xff0c;處理一部請求第二步、創建request.ts文件第三步、本地模擬mock數據接口第四步、測試axiosmock接口是否可以調用第五步、自行擴展 axios 返回的數據類型 axios…