ESP32實作mpu6050
===
###### tags: `ESP32 Tourist` `I2C` `ESP32` `MPU6050`
#### 從上一節的內容,我們可以透過ESP32使用I2C匯流排來配置mpu6050的寄存器。在本章節,我們將探討如何使用esp32來配置mpu6050 Configuration Register,並讀取 Sensor Register,藉此得到六軸感測資訊和溫度資訊。

##### <center>圖片來源 mpu6050 datasheet</center>
* ## **ESP32 I2C Driver Install **
#### ESP32要使用I2C匯流排進行通訊前,我們需要先啟用內部I2C控制器,藉由安裝I2C控制器,我們就可以進行I2C的數據傳輸囉!
#### 首先,我們先決定好esp32在進行I2C傳輸時需要的參數:
:::info
#### ESP32 I2C工作於哪一個模式(Master)
```c
.mode = I2C_MODE_MASTER, // 0
```
#### SDA和SCL傳輸線
```c
#define I2C_MASTER_SDA_IO 21 /*!< GPIO 21 用於I2C SDA使用,提供I2C數據傳輸 */
#define I2C_MASTER_SCL_IO 22 /*!< GPIO 22 用於I2C SCL使用,提供I2C時脈訊號來源 */
.sda_io_num = I2C_MASTER_SDA_IO, //將SDA傳輸線配置為GPIO 21
.scl_io_num = I2C_MASTER_SCL_IO, //將SCL傳輸線配置為GPIO 22
```
#### 並且開啟上拉電阻
```c
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE
```
#### SCL傳輸線輸出頻率(400kHz)
```c
#define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency,400KHz */
.clk_speed = I2C_MASTER_FREQ_HZ,
```
#### 關閉ESP32 I2C RX、TX Buffer
```c
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
```
#### 將上述參數整理為一個結構體(Struct),命名為i2c_config_t,用來儲存參數配置之參數
```c
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master = {
.clk_speed = I2C_MASTER_FREQ_HZ,
}
};
```
#### 透過i2c_param_config(i2c_master_port, &conf)來對I2C Driver進行參數配置。
```c
i2c_param_config(i2c_master_port, &conf);
Parameter :
i2c_master_port: I2C 控制器編號,esp32提供2個i2c控制器,供使用者使用,分別為0和1。
&conf :
剛才儲存參數的結構體,我們將此結構體"位址"(用取址運算符"&"的到記憶體位址)作為參數,讓i2c_param_config由地址取出全部參數的資料。
```
#### 最後安裝I2C控制器,開啟ESP32 I2C功能:
```c
i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
```
:::
* ## **ESP32 I2C 配置寄存器**
#### 上章節[https://hackmd.io/@Jiaming/SJC6gv6Rn](https://hackmd.io/@Jiaming/SJC6gv6Rn)
#### 從上章節,我們介紹了mpu6050用於工作模式配置的寄存器和感測器寄存器,現在我們要以ESP32來對MPU6050工作模式配置寄存器來進行配置,然後開啟mpu6050,我們才可以透過mpu6050來讀取6軸感測數據。
#### 由Datasheet取得我們要進行配置的寄存器:
:::info
```c
#define MPU6050_WHO_AM_I 0x75
#define MPU6050_PWR_MGMT_1_REG_ADDR 0x6B /*!< Register addresses of the power managment register,default 0x01 */
#define MPU6050_GYRO_CONFIG_ADDR 0x1B /*!< Register addresses of the gyroscope register,defult 0x18 */
#define MPU6050_ACCEL_CONFIG_ADDR 0x1C /*!< Register addresses of the accelerometer register,default 0x01 */
#define MPU6050_ENABLE_FIFO_ADDR 0x23 /*!< Register addresses of the FIFO register,disable 0x00 */
#define MPU6050_SMPLRT_DIV_ADDR 0x19 /*!< Register addresses of the sample rate register,default 0x06 */
#define MPU6050_CONFIG 0x1A /*!< Register addresses of the Digital Low Pass Filter(DLPF) register,default is 0x06 */
```
#### 我們建立好ESP32在I2C中讀/寫的函式
#### Read:
```c
esp_err_t JM_mpu6050:: mpu6050_register_read(uint8_t reg_addr, uint8_t *data, size_t len)
{
return i2c_master_write_read_device(I2C_MASTER_NUM, mpu6050_slave_addr, ®_addr, 1, data, len, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
}
```
#### Write
```c
esp_err_t JM_mpu6050::mpu6050_register_write_byte(uint8_t reg_addr, uint8_t data)
{
int ret;
uint8_t write_buf[2] = {reg_addr, data};
ret = i2c_master_write_to_device(I2C_MASTER_NUM, mpu6050_slave_addr, write_buf, sizeof(write_buf), I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
return ret;
}
```
#### 由上述內容,我們將已經定義好的寄存器位址進行Configuration register配置
```c
mpu6050_register_write_byte(MPU6050_PWR_MGMT_1_REG_ADDR, 0x80); /* 對電源管理寄存器寫入0x80進行復位 */
vTaskDelay(10 / portTICK_PERIOD_MS);
mpu6050_register_write_byte(MPU6050_PWR_MGMT_1_REG_ADDR, 0x01); /* 對電源管理寄存器寫入0x01啟動電源 */
vTaskDelay(10 / portTICK_PERIOD_MS);
mpu6050_register_write_byte(MPU6050_GYRO_CONFIG_ADDR, 0x00 | (GYRO_SCALE_RANGE_2000 << 3)); /* 對陀螺儀配置寄存器設置為(0001 1000)設置陀螺儀敏感度(+-2000/s) */
vTaskDelay(10 / portTICK_PERIOD_MS);
mpu6050_register_write_byte(MPU6050_ACCEL_CONFIG_ADDR, 0x00 | (ACCEL_SCALE_RANGE_2G << 3)); /* 對加速度感測器配置寄存器設置為(0000 0001)設置加速度感測器敏感度(+-2g) */
vTaskDelay(10 / portTICK_PERIOD_MS);
mpu6050_register_write_byte(MPU6050_ENABLE_FIFO_ADDR, 0x00); /* 對FIFO配置寄存器設置為(0000 0000),關閉FIFO */
vTaskDelay(10 / portTICK_PERIOD_MS);
mpu6050_register_write_byte(MPU6050_SMPLRT_DIV_ADDR, 0x13); /* 對SMPLRT_DIV寄存器設置為(0001 0011),設置感測器採樣頻率50Hz */
vTaskDelay(10 / portTICK_PERIOD_MS);
mpu6050_register_write_byte(MPU6050_CONFIG , (0x06 | ACCEL_BAND_5HZ_GYRO_BAND_5HZ)); /*開啟低通濾波,並設置為 ACCEL_BAND_5HZ_GYRO_BAND_5HZ(110),頻率為1kHz*/
vTaskDelay(10 / portTICK_PERIOD_MS);
```
#### 配置完成後,我們就可以使用mpu6050六軸感測器了!
:::
* ## **ESP32 I2C 讀取溫度感測器資料**

#### 我們在上章節有提到mpu6050感測器中,溫度感測器數據存放的位址,為0x41~0x42,2Bytes資料。
#### 藉由ESP32向mpu6050內0x41寄存器讀取2Bytes的資料後,我們整合為16bit float資料即可得到初步數據。
:::info
```c
mpu6050_register_read(MPU6050_TEMP_ADDR , data, 1); /*從0x41寄存器讀取溫度數據*/
temp = ((int16_t)data[0]<<8) | data[1];
```
:::
#### 根據得到的初步數據,必須在對該資料進行校正,程式如下:
:::info
```c
(float(temp)/340) + 36.53;
```
#### 這樣就可以獲得溫度數據了!
:::
* ## **ESP32 I2C 讀取6感測器資料**

#### 六軸感測數據,其加速度計感測數據會被存放至0x3B~0x40,我們藉由ESP32向MPU6050讀取該感測器數據,就可以得到x、y、z三軸16bit數據。
:::info
```c
mpu6050_register_read(MPU6050_ACCEL_X_ADDR , data, 1); /*從0x3B寄存器讀取x軸加速度數據*/
accel_data = (int16_t)(data[0] << 8) | data[1];
```
:::
#### 取得16bit數據後,必須轉換為感測器數據範圍:
:::info
1. +-2g
2. +-4g
3. +-8g
4. +-16g
#### 本範例以配置為+-2g
```c
ret = float(accel_data) / 16384; // 4/65536 = 16384
```
:::
#### 這樣就可以得到x軸加速度感測器的數據了。
#### 在以相同的方法,我們可以得到y、z軸加速度和x、y、z軸陀螺儀數據。
---

:::info
```c
float get_mpu6050_gyro_y(){
uint8_t data[2];
float ret = 0;
mpu6050_register_read(MPU6050_GYRO_Y_ADDR , data, 1); /*對溫度感測器寄存器取值*/
ret = (int16_t)(data[0] << 8) | data[1];
ret = (float)ret / 16.4;
ret *= SENSORS_DPS_TO_RADS;
return ret;
}
```
:::
* ## **ESP32 讀取mpu6050程式範例**

#### 本章節實作可以參考影片說明,影片會提供程式碼範例供大家參考。
* ### **Mpu6050六軸感測器實作教學影片**

* [https://www.youtube.com/watch?v=ZGw88J1PExE](https://www.youtube.com/watch?v=ZGw88J1PExE)
---
* ## 參考資料
#### [1] mpu6050 datasheet