Skip to content

Commit

Permalink
new component: mpu6050
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxwellJay256 committed Nov 30, 2023
1 parent 6c716e0 commit 34c8eb2
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ cmake_minimum_required(VERSION 3.5)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(Bike-Computer)

# User Components
set(EXTRA_COMPONENT_DIRS "./components")
2 changes: 2 additions & 0 deletions components/mpu6050/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRCS "mpu6050.c"
INCLUDE_DIRS ".")
105 changes: 105 additions & 0 deletions components/mpu6050/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# mpu6050

This component is a driver for the MPU6050 6-axis motion tracking device.

Author: [@MaxwellJay256](https://github.com/MaxwellJay256)

Reference: [*esp32系列(10):mpu6050驱动 - CSDN*](http://t.csdnimg.cn/vPGnV)

## Content

This component only has two source files, plus a `CMakeLists.txt` file.
- [mpu6050.h](./mpu6050.h): The header file of the driver.
- [mpu6050.c](./mpu6050.c): The source file of the driver.
- [CMakeLists.txt](./CMakeLists.txt): A CMake file registering this component.

## Usage

### Edit the variables and macros

There are 4 macros you must notice in [`mpu6050.h`](./mpu6050.h).
You should edit them to fit your hardware.
The default values are suitable for `ESP32-DevKitC`.

```c
#define MPU6050_I2C_SDA 19 // MPU6050 I2C 的 SDA 引脚
#define MPU6050_I2C_SCL 18 // MPU6050 I2C 的 SCL 引脚
#define MPU6050_I2C_FREQ 400000 // MPU6050 I2C 的频率
#define MPU6050_I2C_PORT_NUM I2C_NUM_0 // MPU6050 的 I2C 端口号
```
Next, in [`mpu6050.c`](./mpu6050.c), you should pay attention to the `mpu6050_init_cmd` structure, which provides the initialization commands for the MPU6050.
```c
static uint8_t mpu6050_init_cmd[MPU6050_INIT_CMD_COUNT][2] = {
// format: {address, data}
{0x6B, 0x80}, // PWR_MGMT_1, DEVICE_RESET
{0x6B, 0x00}, // clear SLEEP
{0x1B, 0x18}, // Gyroscope Full Scale Range = ± 2000 °/s
{0x1c, 0x00}, // Accelerometer Full Scale Range = ± 2g
{0x38, 0x00}, // Disable Interrupt
{0x6A, 0x00}, // User Control.auxiliary I2C are logically driven by the primary I2C bus
{0x23, 0x00}, // FIFO Enable.disable
{0x19, 0x63}, // Sample Rate Divider.Sample Rate = 1 kHz / (1 + 99) = 10 Hz
{0x1A, 0x13}, // EXT_SYNC_SET = GYRO_XOUT_L[0]; Bandwidth = 3
{0x6B, 0x01}, // Power Management 1.PLL with X axis gyroscope reference
{0x6C, 0x00}, // Power Management 2
};
```

### Call the APIs

Only two functions are provided in this component.

#### `esp_err_t mpu6050_init()`

This function initializes the MPU6050, with the macros and variables you have edited above.

#### `mpu6050_output_t mpu6050_get_value()`

This function reads the data from the MPU6050, and returns a `mpu6050_output_t` structure.

The structure is defined as follows:

```c
typedef struct {
struct {
int16_t x; int16_t y; int16_t z;
} accel;
int16_t temp;
struct {
int16_t x; int16_t y; int16_t z;
} gyro;
} mpu6050_output_t;
```

### Example

This is an example of running this component which simply
reads the data from the MPU6050 and prints it to the serial port.

```c
#include "mpu6050.h"

void app_main(void)
{
mpu6050_output_t mpu6050_out;
mpu6050_init();

while (1)
{
mpu6050_out = mpu6050_get_value();
printf("accl_xout: %d\t", mpu6050_out.accel.x);
printf("accl_yout: %d\t", mpu6050_out.accel.y);
printf("accl_zout: %d\n", mpu6050_out.accel.z);
// printf("temp_out: %d\n;", mpu6050_out.temp);
printf("gyro_xout: %d\t", mpu6050_out.gyro.x);
printf("gyro_yout: %d\t", mpu6050_out.gyro.y);
printf("gyro_zout: %d\n", mpu6050_out.gyro.z);
vTaskDelay(100 / portTICK_RATE_MS);
}
}
127 changes: 127 additions & 0 deletions components/mpu6050/mpu6050.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/**
* @file mpu6050.c
* @brief MPU6050 driver source file
* @author @MaxwellJay256
*/
#include "mpu6050.h"

#define MPU6050_INIT_CMD_COUNT 11 // MPU6050 初始化指令的长度
/// @brief MPU6050 的初始化指令数组
static uint8_t mpu6050_init_cmd[MPU6050_INIT_CMD_COUNT][2] = {
// 格式:{寄存器地址, 值}
{0x6B, 0x80}, // PWR_MGMT_1,复位后 DEVICE_RESET 自动清零
// 此处需要等待,见 mpu6050_init() 函数的 for 循环
{0x6B, 0x00}, // 清除睡眠模式
{0x1B, 0x18}, // 陀螺仪满量程配置 = ± 2000 °/s
{0x1c, 0x00}, // 加速度计满量程配置 = ± 2g
{0x38, 0x00}, // 禁用中断
{0x6A, 0x00}, // 设置 AUX I2C 接口
{0x23, 0x00}, // 设置 FIFO
{0x19, 0x63}, // 设置采样率为 1 kHz / (1 + 99) = 10 Hz
{0x1A, 0x13}, // 设置数字低通滤波器,带宽为 44 Hz
{0x6B, 0x01}, // 设置系统时钟源为 X 轴陀螺仪 PLL 参考时钟
{0x6C, 0x00}, // 使能传感器
};

#define ACK_CHECK_EN 0x1 // I2C 主机检查从机的 ACK
#define ACK_CHECK_DIS 0x0 // I2C 主机不检查从机的 ACK
#define ACK_VAL 0x0 // I2C 主机发送 ACK
#define NACK_VAL 0x1 // I2C 主机发送 NACK

/// @brief 从 MPU6050 的寄存器读取数据
/// @param i2c_num I2C 端口号
/// @param reg_addr 要读取的寄存器地址
/// @param data_rd 用于存储读取数据的缓冲区
/// @param size 要读取的数据长度
/// @return ESP_OK: Execute success
static esp_err_t mpu6050_i2c_read(
i2c_port_t i2c_num, uint8_t *reg_addr, uint8_t *data_rd, size_t size)
{
if (size == 0)
return ESP_OK;

i2c_cmd_handle_t cmd = i2c_cmd_link_create(); // 创建 I2C 指令
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MPU6050_ADDR << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN); // 发送 MPU6050 地址和写入位
i2c_master_write_byte(cmd, *reg_addr, ACK_CHECK_EN); // 发送寄存器地址
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MPU6050_ADDR << 1) | I2C_MASTER_READ, ACK_CHECK_EN); // 发送 MPU6050 地址和读取位
if (size > 1)
i2c_master_read(cmd, data_rd, size - 1, ACK_VAL);
i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); // 读取最后一个字节
i2c_master_stop(cmd);

esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); // 发送 I2C 指令
i2c_cmd_link_delete(cmd); // 删除 I2C 指令
return ret;
}

/// @brief 向 MPU6050 写入数据
/// @param i2c_num I2C 端口号
/// @param data_wr 要写入的数据
/// @param size 要写入的数据长度
/// @return ESP_OK: Execute success
static esp_err_t mpu6050_i2c_write(i2c_port_t i2c_num, uint8_t *data_wr, size_t size)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create(); // 创建 I2C 指令
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (MPU6050_ADDR << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN); // 发送 MPU6050 地址和写入位
i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); // 发送数据
i2c_master_stop(cmd);

esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); // 发送 I2C 指令
i2c_cmd_link_delete(cmd); // 删除 I2C 指令
return ret;
}

static const char *TAG = "MPU6050";
esp_err_t mpu6050_init()
{
ESP_LOGI(TAG, "MPU6050 Initializing...");
esp_err_t esp_err;
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = MPU6050_I2C_SDA,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = MPU6050_I2C_SCL,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = MPU6050_I2C_FREQ,
};

esp_err = i2c_param_config(MPU6050_I2C_PORT_NUM, &conf);
ESP_LOGI(TAG, "i2c_param_config: %d", esp_err);

esp_err = i2c_driver_install(MPU6050_I2C_PORT_NUM, conf.mode, 0, 0, 0);
ESP_LOGI(TAG, "i2c_driver_install: %d", esp_err);

for (size_t i = 0; i < MPU6050_INIT_CMD_COUNT; i++)
{
esp_err = mpu6050_i2c_write(MPU6050_I2C_PORT_NUM, mpu6050_init_cmd[i], 2);
if (i == 0)
vTaskDelay(500 / portTICK_RATE_MS); // 等待复位完成
}
ESP_LOGI(TAG, "mpu6050_init_cmd: %d", esp_err);
return esp_err;
}

mpu6050_output_t mpu6050_get_value()
{
uint8_t *measurement_bytes_out = (uint8_t *)malloc(14);
uint8_t reg_addr = 0x3B;
mpu6050_i2c_read(MPU6050_I2C_PORT_NUM, &reg_addr, measurement_bytes_out, 14);
mpu6050_output_t measurement_out = {
.accel = {
.x = (int16_t)(measurement_bytes_out[0] << 8) | measurement_bytes_out[1],
.y = (int16_t)(measurement_bytes_out[2] << 8) | measurement_bytes_out[3],
.z = (int16_t)(measurement_bytes_out[4] << 8) | measurement_bytes_out[5],
},
.temp = (int16_t)(measurement_bytes_out[6] << 8) | measurement_bytes_out[7],
.gyro = {
.x = (int16_t)(measurement_bytes_out[8] << 8) | measurement_bytes_out[9],
.y = (int16_t)(measurement_bytes_out[10] << 8) | measurement_bytes_out[11],
.z = (int16_t)(measurement_bytes_out[12] << 8) | measurement_bytes_out[13],
},
};

return measurement_out;
}
45 changes: 45 additions & 0 deletions components/mpu6050/mpu6050.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* @file mpu6050.h
* @brief MPU6050 driver header file
* @author @MaxwellJay256
*/
#ifndef _MPU6050_H_
#define _MPU6050_H_
#include <stdio.h>
#include <stdint.h>
#include "esp_err.h"
#include "driver/i2c.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

// 以下宏定义待修改
#define MPU6050_I2C_SDA 19 // MPU6050 I2C 的 SDA 引脚
#define MPU6050_I2C_SCL 18 // MPU6050 I2C 的 SCL 引脚
#define MPU6050_I2C_FREQ 400000 // MPU6050 I2C 的频率
#define MPU6050_I2C_PORT_NUM I2C_NUM_0 // MPU6050 的 I2C 端口号
#define MPU6050_ADDR 0x68 // MPU6050 I2C 的地址 b110100(AD0)

/// @brief 初始化 MPU6050
/// @return ESP_OK: Init success
esp_err_t mpu6050_init();

typedef struct {
struct {
int16_t x;
int16_t y;
int16_t z;
} accel;
int16_t temp;
struct {
int16_t x;
int16_t y;
int16_t z;
} gyro;
} mpu6050_output_t;

/// @brief 读取 MPU6050 的加速度计、陀螺仪和温度计数据
/// @return mpu6050_output_t 结构体,包含加速度计、陀螺仪和温度计数据
mpu6050_output_t mpu6050_get_value();

#endif // _MPU6050_H_

0 comments on commit 34c8eb2

Please sign in to comment.