STM32CubeMX教程19 I2C - MPU6050驱动
1、準備材料
正點原子stm32f407探索者開發(fā)板V2.4
STM32CubeMX軟件(Version 6.10.0)
野火DAP仿真器
keil μVision5 IDE(MDK-Arm)
ST-LINK/V2驅動
XCOM V2.6串口助手
邏輯分析儀nanoDLA
2、實驗目標
使用STM32CubeMX軟件配置STM32F407開發(fā)板的I2C1與MPU6050芯片通信,讀取MPU6050的三軸加速度和陀螺儀數(shù)據(jù)并通過串口打印出來
3、實驗流程
3.0、前提知識
本實驗重點是理解I2C通信協(xié)議,而STM32CubeMX的配置則相對簡單,這里不會過于詳細全面的介紹I2C通信協(xié)議,但是會對所有需要知道的知識做介紹
在我們的開發(fā)板上有一顆三軸加速度計和陀螺儀傳感器MPU6050,單片機通過I2C1的PB8和PB9兩個引腳與MPU6050進行通信,MPU6050還有一個中斷引腳,這里為3D_INT引腳,但是本實驗僅僅輪詢讀取加速度計和陀螺儀的數(shù)據(jù),并沒有用到該引腳中斷功能,我們使用的開發(fā)板上的MPU6050芯片硬件原理圖如下圖所示
I2C通信僅需要時鐘線SCLK和數(shù)據(jù)線SDA兩根線就可以讓主機與掛載在I2C上的從機進行通信和數(shù)據(jù)交換,一個I2C理論上最多可掛載127個設備(從機地址用7位二進制表示)
為了讓主機準確的與眾多從機中的一個進行通信,每個從機都會有一個地址,I2C通信時會在通信數(shù)據(jù)中先發(fā)送從機地址,然后對應地址的從機才會響應
而且I2C通信使用的時鐘線SCLK和數(shù)據(jù)線SDA兩根線必須要做上拉設置,因為I2C的兩個引腳被配置為開漏輸出,因此無法輸出高電平,I2C總線連線圖如下圖所示 (注釋1)
MPU6050的從機地址由芯片上的AD0引腳確定,當AD0引腳接地時,從機地址為0X68;
當AD0引腳接VCC時,從機地址為0X69;
根據(jù)上面MPU6050芯片硬件原理圖可知此時MPU6050的從機地址為0X68
根據(jù)MPU6050 datasheet 9.3 I2C Communications Protocol 小節(jié)可知 (注釋2),主機要通過I2C寫入/讀取MPU6050某一個寄存器一字節(jié)的數(shù)據(jù),其通信步驟序列應該如下圖所示
我們以讀取內(nèi)部寄存器單個字節(jié)數(shù)據(jù)為例做詳細介紹,首先確定通信的目的為主機Master從從機Slave內(nèi)部某個寄存器internal register中讀取一個字節(jié)數(shù)據(jù),以下為詳細通信步驟
- 主機時鐘線SCLK和數(shù)據(jù)線SDA兩根線產(chǎn)生起始信號(當 SCL 線是高電平時 SDA 線從高電平向低電平切換)
- 接下來時鐘線SCLK的8個時鐘節(jié)拍中,由數(shù)據(jù)線SDA發(fā)送一字節(jié)8位的數(shù)據(jù),其中高7位為從機的地址,最后一位為0(1表示讀操作,0表示寫操作)
- 接下來1個時鐘節(jié)拍,從機應該產(chǎn)生應答信號ACK
- 接下來8個時鐘節(jié)拍,主機發(fā)送一字節(jié)8位的數(shù)據(jù),該數(shù)據(jù)為要讀取的從機內(nèi)部寄存器地址
- 接下來1個時鐘節(jié)拍,從機應該產(chǎn)生應答信號ACK
- 主機重新產(chǎn)生一個起始信號
- 接下來8個時鐘節(jié)拍,重新發(fā)送一字節(jié)8位的數(shù)據(jù),其中高7位為從機的地址,最后一位為1,表示這次要讀
- 接下來1個時鐘節(jié)拍,從機應該產(chǎn)生應答信號ACK
- 接下來8個時鐘節(jié)拍,主機讀取一字節(jié)8位數(shù)據(jù)
- 接下來1個時鐘節(jié)拍,主機應該產(chǎn)生應答信號NACK
- 主機時鐘線SCLK和數(shù)據(jù)線SDA兩根線產(chǎn)生停止信號(當 SCL 線是高電平時 SDA 線由低電平向高電平切換)
接下來我們用邏輯分析儀捕獲下主機使用I2C1讀取MPU6050寄存器WHO_AM_I(0X75)時,時鐘線SCLK和數(shù)據(jù)線SDA的邏輯電平變化,如下圖所示,從圖上可知I2C讀取讀取MPU6050內(nèi)部寄存器的時序與上面我們所描述的一致
用邏輯分析儀捕獲主機使用I2C1向MPU6050寄存器PWR_MGMT_1(0X6B)寫入一字節(jié)0X00數(shù)據(jù)時,時鐘線SCLK和數(shù)據(jù)線SDA的邏輯電平變化,如下圖所示
為什么上圖中從機地址從0X68變?yōu)?XD0了?
HAL庫中的I2C寫入函數(shù)HAL_I2C_Mem_Write()和讀取函數(shù)HAL_I2C_Mem_Read()對傳入從機地址DevAddress參數(shù)做了要求,該地址必須將數(shù)據(jù)手冊中提到的地址左移才可以調(diào)用該接口
0X68(0110 1000)向左移動直到遇到1即為0XD0(1101 0000),在I2C通信中使用上述兩個API,從機地址傳入0XD0表示對從機地址為0X68的從機進行寫操作,傳入0XD1表示對從機地址為0X68的從機進行讀操作
如下圖為HAL庫HAL_I2C_Mem_Read()函數(shù)說明
上面提到,當啟用I2C之后,其I2C_SCL和I2C_SDA兩個引腳被配置為了復用功能開漏輸出,而開漏輸出無法輸出高電平,因此I2C_SCL和I2C_SDA兩個引腳需要外部上拉,一般的開發(fā)板都會考慮到這一點,在設計原理圖的時候將使用的I2C兩根線給外部上拉到3.3V,如果你使用的是自己設計的板子,請務必記住I2C需要上拉
細心的同學可能又發(fā)現(xiàn)了,你上面給出的MPU6050硬件原理圖I2C兩根線并沒有外部上拉呀?
雖然MPU6050硬件原理圖I2C兩根線沒有上拉,但是在開發(fā)板的其他I2C通信的芯片上進行了外部上拉,如下圖所示
3.1、CubeMX相關配置
3.1.0、工程基本配置
打開STM32CubeMX軟件,單擊ACCESS TO MCU SELECTOR選擇開發(fā)板MCU(選擇你使用開發(fā)板的主控MCU型號),選中MCU型號后單擊頁面右上角Start Project開始工程,具體如下圖所示
開始工程之后在配置主頁面System Core/RCC中配置HSE/LSE晶振,在System Core/SYS中配置Debug模式,具體如下圖所示
詳細工程建立內(nèi)容讀者可以閱讀“STM32CubeMX教程1 工程建立”
3.1.1、時鐘樹配置
系統(tǒng)時鐘使用8MHz外部高速時鐘HSE,HCLK、PCLK1和PCLK2均設置為STM32F407能達到的最高時鐘頻率,具體如下圖所示
3.1.2、外設參數(shù)配置
實驗需要通過串口來輸出與MPU6050進行I2C通信讀取的陀螺儀和加速度計數(shù)據(jù),因此外設需要初始化USART1和I2C1
單擊Pinout & Configuration頁面左邊Connectivity/USART1選項,然后按照“STM32CubeMX教程9 USART/UART 異步通信”實驗中將USART1配置為異步通信模式,無需開啟中斷,如下圖所示
在Pinout & Configuration頁面右邊芯片引腳預覽Pinout view中找到與開發(fā)板上MPU6050芯片連接的I2C的兩個通信引腳PB8和PB9,左邊單擊將其分別配置為I2C1_SCL和I2C1_SDA
然后單擊Connectivity/I2C1選項,在其模式中選擇I2C,在下方Master Features中將 I2C Speed Mode 根據(jù)用戶需求選擇快速模式(400kkHz)或者標準模式(100kHz),這兩種模式僅僅影響通信速率,對于本實驗兩個模式均可隨意選擇
其他參數(shù)保持默認即可,具體配置如下圖所示
3.1.3、外設中斷配置
本實驗無需啟用中斷,如果需要啟用I2C1的中斷,請單擊System Core/NVIC,然后根據(jù)需求勾選I2C1的事件或錯誤中斷,并選擇合適的中斷優(yōu)先級即可,具體配置如下圖所示
3.2、生成代碼
3.2.0、配置Project Manager頁面
單擊進入Project Manager頁面,在左邊Project分欄中修改工程名稱、工程目錄和工具鏈,然后在Code Generator中勾選“Gnerate peripheral initialization as a pair of 'c/h' files per peripheral”,最后單擊頁面右上角GENERATE CODE生成工程,具體如下圖所示
詳細Project Manager配置內(nèi)容讀者可以閱讀“STM32CubeMX教程1 工程建立”實驗3.4.3小節(jié)
3.2.1、外設初始化調(diào)用流程
還是一模一樣的流程,與該系列教程所有的外設初始化一致
在生成的工程代碼主函數(shù)中新增了MX_I2C1_Init()函數(shù),在該函數(shù)中實現(xiàn)了對I2C1的模式及參數(shù)配置
在MX_I2C1_Init()函數(shù)中調(diào)用了HAL_I2C_Init()函數(shù)使用配置的參數(shù)對I2C1進行了初始化
在HAL_I2C_Init()函數(shù)中又調(diào)用了HAL_I2C_MspInit()函數(shù)對I2C1引腳復用設置,I2C1時鐘使能,如果開啟了中斷該函數(shù)中還會有中斷相關設置及使能
如下圖所示為I2C1初始化調(diào)用流程
3.2.2、外設中斷調(diào)用流程
本實驗無需中斷,因此未啟動任何I2C1的中斷
3.2.3、添加其他必要代碼
需要添加MPU6050的驅動文件,文件中至少應該包括
- MPU6050初始化函數(shù)
- MPU6050獲取三軸加速度計原始數(shù)據(jù)函數(shù)
- MPU6050獲取三軸陀螺儀原始數(shù)據(jù)函數(shù)
注意本實驗只使用而不會介紹MPU6050具體驅動文件的原理,具體源代碼如下所示 (注釋3)
mpu6050.c文件
#include "mpu6050.h"
/**
* @brief MPU6050初始化函數(shù)
* @alter 無
* @param 無
* @retval 成功返回0,失敗返回1
*/
uint8_t MPU6050_Init(I2C_HandleTypeDef *I2Cx)
{
uint8_t check;
uint8_t Data;
// check device ID WHO_AM_I
HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, MPU_DEVICE_ID_REG, 1, &check, 1, I2C_TimeOut);
// 0x68 will be returned by the sensor if everything goes well
if (check == 104)
{
// power management register 0X6B we should write all 0's to wake the sensor up
Data = 0;
HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, MPU_PWR_MGMT1_REG, 1, &Data, 1, I2C_TimeOut);
// Set DATA RATE of 1KHz by writing SMPLRT_DIV register
Data = 0x07;
HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, MPU_SAMPLE_RATE_REG, 1, &Data, 1, I2C_TimeOut);
// Set accelerometer configuration in ACCEL_CONFIG Register
// XA_ST=0,YA_ST=0,ZA_ST=0, FS_SEL=0 -> 2g
Data = 0x00;
HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, MPU_ACCEL_CFG_REG, 1, &Data, 1, I2C_TimeOut);
// Set Gyroscopic configuration in GYRO_CONFIG Register
// XG_ST=0,YG_ST=0,ZG_ST=0, FS_SEL=0 -> 250 /s
Data = 0x00;
HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, MPU_GYRO_CFG_REG, 1, &Data, 1, I2C_TimeOut);
return 0;
}
return 1;
}
/**
* @brief MPU6050溫度值獲取函數(shù)
* @alter 無
* @param 無
* @retval 溫度值
*/
float MPU_Get_Temperature(void)
{
uint8_t buf[2];
short raw;
float temp;
HAL_I2C_Mem_Read(&MPU6050_I2C, MPU6050_ADDR, MPU_TEMP_OUTH_REG, 1, buf, 2, I2C_TimeOut);
raw=((int16_t)buf[0]<<8)|buf[1];
temp=36.53+((double)raw)/340;
return temp;;
}
/**
* @brief MPU6050陀螺儀值獲取函數(shù)(三軸原始值)
* @alter 無
* @param gx,gy,gz:陀螺儀x,y,z軸的原始讀數(shù)(帶符號)
* @retval 正常:0,錯誤:其他
*/
uint8_t MPU_Get_RAW_Gyroscope(int16_t *gx,int16_t *gy,int16_t *gz)
{
uint8_t buf[6],res;
res = HAL_I2C_Mem_Read(&MPU6050_I2C, MPU6050_ADDR, MPU_GYRO_XOUTH_REG, 1, buf, 6, I2C_TimeOut);
if(res==0)
{
*gx=((int16_t)buf[0]<<8)|buf[1];
*gy=((int16_t)buf[2]<<8)|buf[3];
*gz=((int16_t)buf[4]<<8)|buf[5];
}
return res;
}
/**
* @brief MPU6050加速度值獲取函數(shù)(三軸原始值)
* @alter 無
* @param ax,ay,az:加速度計x,y,z軸的原始讀數(shù)(帶符號)
* @retval 正常:0,錯誤:其他
*/
uint8_t MPU_Get_RAW_Accelerometer(int16_t *ax,int16_t *ay,int16_t *az)
{
uint8_t buf[6],res;
res = HAL_I2C_Mem_Read(&MPU6050_I2C, MPU6050_ADDR, MPU_ACCEL_XOUTH_REG, 1, buf, 6, I2C_TimeOut);
if(res==0)
{
*ax=((int16_t)buf[0]<<8)|buf[1];
*ay=((int16_t)buf[2]<<8)|buf[3];
*az=((int16_t)buf[4]<<8)|buf[5];
}
return res;
}
mpu6050.h文件
#ifndef __MPU6050_H
#define __MPU6050_H
#include "main.h"
#include "i2c.h"
#endif
#define MPU6050_I2C hi2c1
#define MPU6050_ADDR 0xD0
#define I2C_TimeOut 100
/*MPU6050內(nèi)部寄存器地址*/
#define MPU_SAMPLE_RATE_REG 0X19 //采樣頻率分頻器
#define MPU_GYRO_CFG_REG 0X1B //陀螺儀配置寄存器
#define MPU_ACCEL_CFG_REG 0X1C //加速度計配置寄存器
#define MPU_ACCEL_XOUTH_REG 0X3B //加速度值,X軸高8位寄存器
#define MPU_TEMP_OUTH_REG 0X41 //溫度值高八位寄存器
#define MPU_GYRO_XOUTH_REG 0X43 //陀螺儀值,X軸高8位寄存器
#define MPU_PWR_MGMT1_REG 0X6B //電源管理寄存器1
#define MPU_DEVICE_ID_REG 0X75 //器件ID寄存器
uint8_t MPU6050_Init(I2C_HandleTypeDef *I2Cx);
float MPU_Get_Temperature(void);
uint8_t MPU_Get_RAW_Gyroscope(int16_t *gx,int16_t *gy,int16_t *gz);
uint8_t MPU_Get_RAW_Accelerometer(int16_t *ax,int16_t *ay,int16_t *az);
/*
MPU6050內(nèi)部所有寄存器地址
#define MPU_SELF_TESTX_REG 0X0D //自檢寄存器X
#define MPU_SELF_TESTY_REG 0X0E //自檢寄存器Y
#define MPU_SELF_TESTZ_REG 0X0F //自檢寄存器Z
#define MPU_SELF_TESTA_REG 0X10 //自檢寄存器A
#define MPU_SAMPLE_RATE_REG 0X19 //采樣頻率分頻器
#define MPU_CFG_REG 0X1A //配置寄存器
#define MPU_GYRO_CFG_REG 0X1B //陀螺儀配置寄存器
#define MPU_ACCEL_CFG_REG 0X1C //加速度計配置寄存器
#define MPU_MOTION_DET_REG 0X1F //運動檢測閥值設置寄存器
#define MPU_FIFO_EN_REG 0X23 //FIFO使能寄存器
#define MPU_I2CMST_CTRL_REG 0X24 //IIC主機控制寄存器
#define MPU_I2CSLV0_ADDR_REG 0X25 //IIC從機0器件地址寄存器
#define MPU_I2CSLV0_REG 0X26 //IIC從機0數(shù)據(jù)地址寄存器
#define MPU_I2CSLV0_CTRL_REG 0X27 //IIC從機0控制寄存器
#define MPU_I2CSLV1_ADDR_REG 0X28 //IIC從機1器件地址寄存器
#define MPU_I2CSLV1_REG 0X29 //IIC從機1數(shù)據(jù)地址寄存器
#define MPU_I2CSLV1_CTRL_REG 0X2A //IIC從機1控制寄存器
#define MPU_I2CSLV2_ADDR_REG 0X2B //IIC從機2器件地址寄存器
#define MPU_I2CSLV2_REG 0X2C //IIC從機2數(shù)據(jù)地址寄存器
#define MPU_I2CSLV2_CTRL_REG 0X2D //IIC從機2控制寄存器
#define MPU_I2CSLV3_ADDR_REG 0X2E //IIC從機3器件地址寄存器
#define MPU_I2CSLV3_REG 0X2F //IIC從機3數(shù)據(jù)地址寄存器
#define MPU_I2CSLV3_CTRL_REG 0X30 //IIC從機3控制寄存器
#define MPU_I2CSLV4_ADDR_REG 0X31 //IIC從機4器件地址寄存器
#define MPU_I2CSLV4_REG 0X32 //IIC從機4數(shù)據(jù)地址寄存器
#define MPU_I2CSLV4_DO_REG 0X33 //IIC從機4寫數(shù)據(jù)寄存器
#define MPU_I2CSLV4_CTRL_REG 0X34 //IIC從機4控制寄存器
#define MPU_I2CSLV4_DI_REG 0X35 //IIC從機4讀數(shù)據(jù)寄存器
#define MPU_I2CMST_STA_REG 0X36 //IIC主機狀態(tài)寄存器
#define MPU_INTBP_CFG_REG 0X37 //中斷/旁路設置寄存器
#define MPU_INT_EN_REG 0X38 //中斷使能寄存器
#define MPU_INT_STA_REG 0X3A //中斷狀態(tài)寄存器
#define MPU_ACCEL_XOUTH_REG 0X3B //加速度值,X軸高8位寄存器
#define MPU_ACCEL_XOUTL_REG 0X3C //加速度值,X軸低8位寄存器
#define MPU_ACCEL_YOUTH_REG 0X3D //加速度值,Y軸高8位寄存器
#define MPU_ACCEL_YOUTL_REG 0X3E //加速度值,Y軸低8位寄存器
#define MPU_ACCEL_ZOUTH_REG 0X3F //加速度值,Z軸高8位寄存器
#define MPU_ACCEL_ZOUTL_REG 0X40 //加速度值,Z軸低8位寄存器
#define MPU_TEMP_OUTH_REG 0X41 //溫度值高八位寄存器
#define MPU_TEMP_OUTL_REG 0X42 //溫度值低8位寄存器
#define MPU_GYRO_XOUTH_REG 0X43 //陀螺儀值,X軸高8位寄存器
#define MPU_GYRO_XOUTL_REG 0X44 //陀螺儀值,X軸低8位寄存器
#define MPU_GYRO_YOUTH_REG 0X45 //陀螺儀值,Y軸高8位寄存器
#define MPU_GYRO_YOUTL_REG 0X46 //陀螺儀值,Y軸低8位寄存器
#define MPU_GYRO_ZOUTH_REG 0X47 //陀螺儀值,Z軸高8位寄存器
#define MPU_GYRO_ZOUTL_REG 0X48 //陀螺儀值,Z軸低8位寄存器
#define MPU_I2CSLV0_DO_REG 0X63 //IIC從機0數(shù)據(jù)寄存器
#define MPU_I2CSLV1_DO_REG 0X64 //IIC從機1數(shù)據(jù)寄存器
#define MPU_I2CSLV2_DO_REG 0X65 //IIC從機2數(shù)據(jù)寄存器
#define MPU_I2CSLV3_DO_REG 0X66 //IIC從機3數(shù)據(jù)寄存器
#define MPU_I2CMST_DELAY_REG 0X67 //IIC主機延時管理寄存器
#define MPU_SIGPATH_RST_REG 0X68 //信號通道復位寄存器
#define MPU_MDETECT_CTRL_REG 0X69 //運動檢測控制寄存器
#define MPU_USER_CTRL_REG 0X6A //用戶控制寄存器
#define MPU_PWR_MGMT1_REG 0X6B //電源管理寄存器1
#define MPU_PWR_MGMT2_REG 0X6C //電源管理寄存器2
#define MPU_FIFO_CNTH_REG 0X72 //FIFO計數(shù)寄存器高八位
#define MPU_FIFO_CNTL_REG 0X73 //FIFO計數(shù)寄存器低八位
#define MPU_FIFO_RW_REG 0X74 //FIFO讀寫寄存器
#define MPU_DEVICE_ID_REG 0X75 //器件ID寄存器
*/
在keil中添加.c/.h文件步驟如下圖所示
然后將源代碼復制到新建的.c/.h文件中,最后將.c文件添加到工程即可,如下圖所示
在主函數(shù)中初始化MPU6050,主循環(huán)中獲取原始的三軸加速度數(shù)據(jù)和三軸陀螺儀數(shù)據(jù),并通過串口打印信息
源代碼如下
/*main.c中定義的變量*/
int16_t ax,ay,az,gx,gy,gz;
/*初始化代碼*/
while(MPU6050_Init(&hi2c1) != HAL_OK){HAL_Delay(1);}
/*主循環(huán)中代碼*/
MPU_Get_RAW_Gyroscope(&ax,&ay,&az);
MPU_Get_RAW_Accelerometer(&gx,&gy,&gz);
printf("ax:%d,ay:%d,az:%d,",ax,ay,az);
printf("gx:%d,gy:%d,gz:%d\r\n",gx,gy,gz);
HAL_Delay(100);
4、常用函數(shù)
/*I2C讀函數(shù)*/
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
/*I2C寫函數(shù)*/
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
5、燒錄驗證
燒錄程序,上電后開啟串口,可以看到源源不斷的輸出當前MPU6050三軸加速度數(shù)據(jù)和三軸陀螺儀數(shù)據(jù),移動開發(fā)板會發(fā)現(xiàn)數(shù)據(jù)有規(guī)律改變,如下圖所示為串口輸出的數(shù)據(jù)
6、注釋詳解
注釋1:圖片來源 I2C通信協(xié)議介紹 - 知乎
注釋2:MPU6050 Datasheet https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Datasheet1.pdf
注釋3:MPU6050驅動庫參考 https://github.com/leech001/MPU6050
更多內(nèi)容請瀏覽 STM32CubeMX+STM32F4系列教程文章匯總貼
總結
以上是生活随笔為你收集整理的STM32CubeMX教程19 I2C - MPU6050驱动的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 记一次缓存失效引发的惨案!
- 下一篇: 反沙箱技术