ESP32-S3开发教程10:I2C与MPU6050
所有硬件相关、可修改、有明确含义的参数,都用大写宏定义,方便后续修改和维护。// -------------------------- 1. I2C硬件配置宏(来自你的硬件接线) --------------------------// 使用的I2C总线号,ESP32-S3有2个I2C总线,选I2C_NUM_0// SDA引脚,对应你接的GPIO8// SCL引脚,对应你接的GPIO9// I2C
本文代码已上传开源仓库:ESP32-S3教学资产:包括了一些在ESP32-S3上的简单工程,基于ESP-IDF框架 - AtomGit | GitCode
现在,我们来讲解I2C与MPU6050相关内容。
一、I2C是什么
I2C是一种芯片间的通信协议,只需两根线,就能让多个设备互相传数据。
- SDA (Serial Data)数据线 → 真正传数据的线
- SCL (Serial Clock)时钟线 → 控制什么时候传、传多快
I2C属于半双工通信,同一时间,只能发送或者接收,不能同时收发。每个设备都有自己的地址,比如MPU6050默认地址:
- 0x68(AD0 接地)
- 0x69(AD0 接 3.3V)
二、MPU6050
MPU6050 是 InvenSense 公司推出的六轴运动处理传感器,集成了三轴加速度计 + 三轴陀螺仪 + 内置温度传感器,还自带 I2C 接口和数字运动处理(DMP)引擎,是低成本、小体积的姿态检测核心,广泛用于平衡小车、无人机、手环、体感设备等场景。

三、硬件接线

四、新建工程
新建一个名为MPU6050的工程,如下图所示:

五、模块化编程
现在,我们来引入一个模块化编程的概念。就像C语言可以自定义.h文件一样,我们的ESP32-S3的开发,也可以使用类似的操作,将部分代码封装在main.c以外,main.c中只保留少量调用代码,增加可读性,也方便后续的随时移植。
现在,我们就将MPU6050的驱动代码封装一下。
5.1 完善CMakeList.txt文件
idf_component_register(SRCS "mpu6050.c" "main.c"
REQUIRES driver freertos
INCLUDE_DIRS ".")
SRCS:告诉编译器要编译哪些.c源文件,我们写的两个.c都要加进来。
5.2 完善MPU6050.h(先定接口,再写实现)
头文件是整个驱动的「功能说明书」,必须先写。它的核心作用是:对外暴露可调用的接口,隐藏内部实现细节,同时防止头文件重复包含。
#ifndef MPU6050_H
#define MPU6050_H
#include "esp_err.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// I2C 配置
#define I2C_MASTER_NUM I2C_NUM_0
#define I2C_MASTER_SDA_IO 8
#define I2C_MASTER_SCL_IO 9
#define I2C_MASTER_FREQ_HZ 400000
// MPU6050 设备地址
#define MPU6050_ADDR 0x68
// 加速度计量程
#define ACCEL_FS_2G 0
#define ACCEL_FS_4G 1
#define ACCEL_FS_8G 2
#define ACCEL_FS_16G 3
// 陀螺仪量程
#define GYRO_FS_250DPS 0
#define GYRO_FS_500DPS 1
#define GYRO_FS_1000DPS 2
#define GYRO_FS_2000DPS 3
// 初始化 I2C 总线
esp_err_t mpu6050_i2c_init(void);
// 初始化 MPU6050
esp_err_t mpu6050_init(void);
// 读取加速度计数据
esp_err_t mpu6050_read_accel(float *ax, float *ay, float *az);
// 读取陀螺仪数据
esp_err_t mpu6050_read_gyro(float *gx, float *gy, float *gz);
// 读取温度数据
esp_err_t mpu6050_read_temp(float *temp);
// 反初始化 I2C 总线
esp_err_t mpu6050_i2c_deinit(void);
#ifdef __cplusplus
}
#endif
#endif // MPU6050_H
5.2.1 头文件的基础框架
// 头文件保护宏:防止这个头文件被重复包含,格式是#ifndef 宏名 #define 宏名 ... #endif
#ifndef MPU6050_H
#define MPU6050_H
// 这里放后续的内容
#endif // MPU6050_H
5.2.2 引入依赖的头文件
判断要引入什么头文件的标准:我们在这个头文件里用到的类型 / 函数,来自哪个头文件,就引入哪个。
- 我们的函数返回值是
esp_err_t(ESP-IDF 的错误类型),来自esp_err.h - 我们用到了
uint8_t等标准整型,来自stdint.h - 兼容 C++ 编译,需要加
extern "C"包裹
#ifndef MPU6050_H
#define MPU6050_H
// 引入依赖头文件
#include "esp_err.h"
#include <stdint.h>
// 兼容C++编译
#ifdef __cplusplus
extern "C" {
#endif
// 这里放宏定义和函数声明
#ifdef __cplusplus
}
#endif
#endif // MPU6050_H
5.2.3 编写宏定义
宏定义的原则:所有硬件相关、可修改、有明确含义的参数,都用大写宏定义,方便后续修改和维护。我们按照功能模块,分批次写宏,每一个宏的值都有文档依据:
// -------------------------- 1. I2C硬件配置宏(来自你的硬件接线) --------------------------
// 使用的I2C总线号,ESP32-S3有2个I2C总线,选I2C_NUM_0
#define I2C_MASTER_NUM I2C_NUM_0
// SDA引脚,对应你接的GPIO8
#define I2C_MASTER_SDA_IO 8
// SCL引脚,对应你接的GPIO9
#define I2C_MASTER_SCL_IO 9
// I2C时钟频率,MPU6050最大支持400kHz,来自datasheet
#define I2C_MASTER_FREQ_HZ 400000
// -------------------------- 2. MPU6050设备地址(来自MPU6050 datasheet) --------------------------
// AD0接GND时,7位I2C地址是0x68;接3.3V是0x69
#define MPU6050_ADDR 0x68
// -------------------------- 3. 加速度计量程宏(来自MPU6050寄存器手册) --------------------------
// ACCEL_CONFIG寄存器的BIT4-BIT3,对应4个量程
#define ACCEL_FS_2G 0 // ±2g(默认,灵敏度最高)
#define ACCEL_FS_4G 1 // ±4g
#define ACCEL_FS_8G 2 // ±8g
#define ACCEL_FS_16G 3 // ±16g
// -------------------------- 4. 陀螺仪量程宏(来自MPU6050寄存器手册) --------------------------
// GYRO_CONFIG寄存器的BIT4-BIT3,对应4个量程
#define GYRO_FS_250DPS 0 // ±250°/s(默认,灵敏度最高)
#define GYRO_FS_500DPS 1 // ±500°/s
#define GYRO_FS_1000DPS 2 // ±1000°/s
#define GYRO_FS_2000DPS 3 // ±2000°/s
5.2.4 编写对外函数接口声明
函数接口设计的原则:见名知意、单一职责、输入输出清晰、统一错误返回值。我们按照功能流程,设计 6 个核心接口,对应我们拆解的子功能:
// 1. 初始化I2C总线:无入参,返回ESP_OK代表成功,其他代表错误
esp_err_t mpu6050_i2c_init(void);
// 2. 初始化MPU6050传感器:无入参,返回ESP_OK代表成功
esp_err_t mpu6050_init(void);
// 3. 读取加速度数据:入参是3个float类型的指针,用来存放X/Y/Z轴的结果
esp_err_t mpu6050_read_accel(float *ax, float *ay, float *az);
// 4. 读取陀螺仪数据:入参是3个float类型的指针,用来存放X/Y/Z轴的结果
esp_err_t mpu6050_read_gyro(float *gx, float *gy, float *gz);
// 5. 读取温度数据:入参是float指针,存放温度结果
esp_err_t mpu6050_read_temp(float *temp);
// 6. 反初始化I2C总线:释放资源,可选但规范
esp_err_t mpu6050_i2c_deinit(void);
到这里,mpu6050.h就写完了。你已经完成了驱动的「功能设计」,接下来只需要在.c 文件里,把这些接口的功能实现出来即可。
5.3 完善MPU6050.c
#include "mpu6050.h"
#include "esp_log.h"
#include "driver/i2c_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define TAG "MPU6050"
// MPU6050 寄存器地址
#define MPU6050_REG_PWR_MGMT_1 0x6B
#define MPU6050_REG_SMPLRT_DIV 0x19
#define MPU6050_REG_CONFIG 0x1A
#define MPU6050_REG_GYRO_CONFIG 0x1B
#define MPU6050_REG_ACCEL_CONFIG 0x1C
#define MPU6050_REG_ACCEL_XOUT_H 0x3B
#define MPU6050_REG_TEMP_OUT_H 0x41
#define MPU6050_REG_GYRO_XOUT_H 0x43
// 灵敏度系数
#define ACCEL_SENSITIVITY_2G 16384.0f
#define ACCEL_SENSITIVITY_4G 8192.0f
#define ACCEL_SENSITIVITY_8G 4096.0f
#define ACCEL_SENSITIVITY_16G 2048.0f
#define GYRO_SENSITIVITY_250 131.0f
#define GYRO_SENSITIVITY_500 65.5f
#define GYRO_SENSITIVITY_1000 32.8f
#define GYRO_SENSITIVITY_2000 16.4f
static i2c_master_bus_handle_t i2c_bus_handle = NULL;
static i2c_master_dev_handle_t mpu6050_handle = NULL;
// 写入单个寄存器
static esp_err_t mpu6050_write_reg(uint8_t reg, uint8_t data)
{
uint8_t write_buf[2] = {reg, data};
return i2c_master_transmit(mpu6050_handle, write_buf, 2, -1);
}
// 读取多个寄存器
static esp_err_t mpu6050_read_reg(uint8_t reg, uint8_t *data, size_t len)
{
return i2c_master_transmit_receive(mpu6050_handle, ®, 1, data, len, -1);
}
esp_err_t mpu6050_i2c_init(void)
{
i2c_master_bus_config_t bus_config = {
.i2c_port = I2C_MASTER_NUM,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
esp_err_t ret = i2c_new_master_bus(&bus_config, &i2c_bus_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to create I2C master bus");
return ret;
}
i2c_device_config_t dev_config = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = MPU6050_ADDR,
.scl_speed_hz = I2C_MASTER_FREQ_HZ,
};
ret = i2c_master_bus_add_device(i2c_bus_handle, &dev_config, &mpu6050_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to add MPU6050 device to I2C bus");
return ret;
}
ESP_LOGI(TAG, "I2C master initialized successfully");
return ESP_OK;
}
esp_err_t mpu6050_i2c_deinit(void)
{
esp_err_t ret = ESP_OK;
if (mpu6050_handle != NULL) {
ret = i2c_master_bus_rm_device(mpu6050_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to remove MPU6050 device from I2C bus");
return ret;
}
mpu6050_handle = NULL;
}
if (i2c_bus_handle != NULL) {
ret = i2c_del_master_bus(i2c_bus_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to delete I2C master bus");
return ret;
}
i2c_bus_handle = NULL;
}
ESP_LOGI(TAG, "I2C master deinitialized successfully");
return ESP_OK;
}
esp_err_t mpu6050_init(void)
{
esp_err_t ret;
// 唤醒 MPU6050 (清除睡眠位)
ret = mpu6050_write_reg(MPU6050_REG_PWR_MGMT_1, 0x00);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to wake up MPU6050");
return ret;
}
vTaskDelay(pdMS_TO_TICKS(10));
// 设置采样率分频器 (1kHz / (1 + 0) = 1kHz)
ret = mpu6050_write_reg(MPU6050_REG_SMPLRT_DIV, 0x00);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set sample rate");
return ret;
}
// 设置低通滤波器
ret = mpu6050_write_reg(MPU6050_REG_CONFIG, 0x03);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set config");
return ret;
}
// 设置陀螺仪量程为 ±250dps
ret = mpu6050_write_reg(MPU6050_REG_GYRO_CONFIG, GYRO_FS_250DPS << 3);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set gyro config");
return ret;
}
// 设置加速度计量程为 ±2g
ret = mpu6050_write_reg(MPU6050_REG_ACCEL_CONFIG, ACCEL_FS_2G << 3);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set accel config");
return ret;
}
ESP_LOGI(TAG, "MPU6050 initialized successfully");
return ESP_OK;
}
esp_err_t mpu6050_read_accel(float *ax, float *ay, float *az)
{
uint8_t data[6];
esp_err_t ret = mpu6050_read_reg(MPU6050_REG_ACCEL_XOUT_H, data, 6);
if (ret != ESP_OK) {
return ret;
}
int16_t raw_x = (int16_t)((data[0] << 8) | data[1]);
int16_t raw_y = (int16_t)((data[2] << 8) | data[3]);
int16_t raw_z = (int16_t)((data[4] << 8) | data[5]);
*ax = raw_x / ACCEL_SENSITIVITY_2G;
*ay = raw_y / ACCEL_SENSITIVITY_2G;
*az = raw_z / ACCEL_SENSITIVITY_2G;
return ESP_OK;
}
esp_err_t mpu6050_read_gyro(float *gx, float *gy, float *gz)
{
uint8_t data[6];
esp_err_t ret = mpu6050_read_reg(MPU6050_REG_GYRO_XOUT_H, data, 6);
if (ret != ESP_OK) {
return ret;
}
int16_t raw_x = (int16_t)((data[0] << 8) | data[1]);
int16_t raw_y = (int16_t)((data[2] << 8) | data[3]);
int16_t raw_z = (int16_t)((data[4] << 8) | data[5]);
*gx = raw_x / GYRO_SENSITIVITY_250;
*gy = raw_y / GYRO_SENSITIVITY_250;
*gz = raw_z / GYRO_SENSITIVITY_250;
return ESP_OK;
}
esp_err_t mpu6050_read_temp(float *temp)
{
uint8_t data[2];
esp_err_t ret = mpu6050_read_reg(MPU6050_REG_TEMP_OUT_H, data, 2);
if (ret != ESP_OK) {
return ret;
}
int16_t raw_temp = (int16_t)((data[0] << 8) | data[1]);
*temp = (raw_temp / 340.0f) + 36.53f;
return ESP_OK;
}
5.3.1 基础依赖与全局配置
// 第一步:引入我们自己的头文件,必须放在第一个
#include "mpu6050.h"
// 第二步:引入依赖的ESP-IDF头文件,用到什么功能就引入什么
#include "esp_log.h" // 日志打印,调试必备
#include "driver/i2c_master.h"// I2C主机驱动,来自ESP-IDF driver组件
#include "freertos/FreeRTOS.h"// FreeRTOS核心库
#include "freertos/task.h" // FreeRTOS延时函数
// 第三步:定义日志标签,方便串口调试时区分日志来源
#define TAG "MPU6050_DRIVER"
5.3.2 定义核心寄存器地址与转换系数
这部分是和传感器通信的核心,每一个寄存器地址、转换系数,都必须从官方手册里找,不能瞎写。
// -------------------------- MPU6050核心寄存器地址(来自寄存器手册) --------------------------
// 电源管理寄存器1:用来唤醒传感器,地址0x6B
#define MPU6050_REG_PWR_MGMT_1 0x6B
// 采样率分频寄存器:配置数据采样率,地址0x19
#define MPU6050_REG_SMPLRT_DIV 0x19
// 配置寄存器:配置低通滤波,地址0x1A
#define MPU6050_REG_CONFIG 0x1A
// 陀螺仪配置寄存器:配置量程,地址0x1B
#define MPU6050_REG_GYRO_CONFIG 0x1B
// 加速度计配置寄存器:配置量程,地址0x1C
#define MPU6050_REG_ACCEL_CONFIG 0x1C
// 加速度计X轴高字节寄存器:加速度数据的起始地址,地址0x3B
#define MPU6050_REG_ACCEL_XOUT_H 0x3B
// 温度高字节寄存器:温度数据的起始地址,地址0x41
#define MPU6050_REG_TEMP_OUT_H 0x41
// 陀螺仪X轴高字节寄存器:陀螺仪数据的起始地址,地址0x43
#define MPU6050_REG_GYRO_XOUT_H 0x43
// -------------------------- 物理值转换系数(来自datasheet) --------------------------
// 加速度灵敏度:不同量程下,1g对应的数字量(LSB/g)
#define ACCEL_SENS_2G 16384.0f
#define ACCEL_SENS_4G 8192.0f
#define ACCEL_SENS_8G 4096.0f
#define ACCEL_SENS_16G 2048.0f
// 陀螺仪灵敏度:不同量程下,1°/s对应的数字量(LSB/(°/s))
#define GYRO_SENS_250 131.0f
#define GYRO_SENS_500 65.5f
#define GYRO_SENS_1000 32.8f
#define GYRO_SENS_2000 16.4f
5.3.3 定义静态句柄
I2C 总线和设备的句柄,是后续所有操作的核心,我们把它定义为静态全局变量,作用域仅限本文件,外部无法访问,符合封装原则:
// I2C总线句柄
static i2c_master_bus_handle_t i2c_bus_handle = NULL;
// MPU6050设备句柄
static i2c_master_dev_handle_t mpu6050_dev_handle = NULL;
5.3.4 封装底层寄存器读写函数
MPU6050 的所有操作,本质都是「通过 I2C 写寄存器」和「通过 I2C 读寄存器」。我们把这两个操作封装成静态函数,上层代码不用关心 I2C 的底层细节,只需要调用这两个函数即可。
这是 I2C 驱动的通用写法,所有 I2C 传感器都可以用这个思路封装:
/**
* @brief 静态函数:向MPU6050的单个寄存器写入1个字节数据
* @param reg 要写入的寄存器地址
* @param data 要写入的数据
* @return esp_err_t ESP_OK=成功,其他=失败
*/
static esp_err_t mpu6050_write_reg(uint8_t reg, uint8_t data)
{
// I2C写寄存器的格式:先发送寄存器地址,再发送要写入的数据
uint8_t write_buf[2] = {reg, data};
// 调用ESP-IDF的I2C发送函数,参数:设备句柄、发送缓冲区、长度、超时时间(-1=无限等待)
return i2c_master_transmit(mpu6050_dev_handle, write_buf, 2, -1);
}
/**
* @brief 静态函数:从MPU6050的寄存器连续读取多个字节数据
* @param reg 要读取的起始寄存器地址
* @param data 存放读取结果的缓冲区
* @param len 要读取的字节数
* @return esp_err_t ESP_OK=成功,其他=失败
*/
static esp_err_t mpu6050_read_reg(uint8_t reg, uint8_t *data, size_t len)
{
// I2C读寄存器的格式:先发送要读取的寄存器地址,再接收对应长度的数据
return i2c_master_transmit_receive(mpu6050_dev_handle, ®, 1, data, len, -1);
}
5.3.5 I2C 初始化与反初始化函数
这一步是实现硬件通信的基础,我们严格按照 ESP-IDF 官方 I2C API 的流程来写:
// 实现头文件里的I2C初始化函数
esp_err_t mpu6050_i2c_init(void)
{
// 第一步:配置I2C总线参数,结构体来自ESP-IDF的i2c_master.h
i2c_master_bus_config_t bus_config = {
.i2c_port = I2C_MASTER_NUM, // 我们在头文件里定义的I2C总线号
.sda_io_num = I2C_MASTER_SDA_IO, // SDA引脚号
.scl_io_num = I2C_MASTER_SCL_IO, // SCL引脚号
.clk_source = I2C_CLK_SRC_DEFAULT, // 默认时钟源,不用改
.glitch_ignore_cnt = 7, // 时钟毛刺过滤,默认值,增强稳定性
.flags.enable_internal_pullup = true, // 开启GPIO内部上拉,硬件没有外部上拉电阻必须开
};
// 第二步:创建I2C主机总线,获取总线句柄
esp_err_t ret = i2c_new_master_bus(&bus_config, &i2c_bus_handle);
// 错误处理:如果创建失败,打印错误日志,直接返回错误
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2C总线创建失败,错误码:%d", ret);
return ret;
}
// 第三步:配置MPU6050设备参数
i2c_device_config_t dev_config = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7, // MPU6050用7位I2C地址,固定
.device_address = MPU6050_ADDR, // 头文件里定义的设备地址
.scl_speed_hz = I2C_MASTER_FREQ_HZ, // I2C时钟频率
};
// 第四步:把MPU6050设备添加到I2C总线上,获取设备句柄
ret = i2c_master_bus_add_device(i2c_bus_handle, &dev_config, &mpu6050_dev_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "MPU6050设备添加失败,错误码:%d", ret);
return ret;
}
// 到这里,I2C初始化成功,打印日志
ESP_LOGI(TAG, "I2C总线初始化成功");
return ESP_OK;
}
// 实现头文件里的I2C反初始化函数
esp_err_t mpu6050_i2c_deinit(void)
{
esp_err_t ret = ESP_OK;
// 先移除设备,再删除总线,顺序不能反
if (mpu6050_dev_handle != NULL) {
ret = i2c_master_bus_rm_device(mpu6050_dev_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "MPU6050设备移除失败,错误码:%d", ret);
return ret;
}
mpu6050_dev_handle = NULL; // 清空句柄,避免野指针
}
if (i2c_bus_handle != NULL) {
ret = i2c_del_master_bus(i2c_bus_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2C总线删除失败,错误码:%d", ret);
return ret;
}
i2c_bus_handle = NULL;
}
ESP_LOGI(TAG, "I2C总线反初始化成功");
return ESP_OK;
}
5.3.6 MPU6050初始化函数
初始化函数的核心,是按照 MPU6050 手册里的上电流程,通过我们封装的mpu6050_write_reg函数,给对应的寄存器写入配置值。
// 实现头文件里的MPU6050初始化函数
esp_err_t mpu6050_init(void)
{
esp_err_t ret;
ESP_LOGI(TAG, "开始初始化MPU6050传感器");
// 第一步:唤醒MPU6050(重点!MPU6050上电默认进入睡眠模式)
// 手册说明:PWR_MGMT_1寄存器的BIT6是Sleep位,写0=唤醒,写1=睡眠
ret = mpu6050_write_reg(MPU6050_REG_PWR_MGMT_1, 0x00);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "MPU6050唤醒失败");
return ret;
}
// 延时10ms,等待传感器唤醒稳定
vTaskDelay(pdMS_TO_TICKS(10));
// 第二步:配置采样率分频器
// 手册公式:采样率 = 陀螺仪输出频率 / (1 + SMPLRT_DIV)
// 陀螺仪默认输出频率1kHz,写入0x00 → 采样率=1kHz
ret = mpu6050_write_reg(MPU6050_REG_SMPLRT_DIV, 0x00);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "采样率配置失败");
return ret;
}
// 第三步:配置低通滤波器(DLPF)
// 写入0x03 → 低通滤波带宽44Hz,降低噪声,平衡响应速度
ret = mpu6050_write_reg(MPU6050_REG_CONFIG, 0x03);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "低通滤波配置失败");
return ret;
}
// 第四步:配置陀螺仪量程
// 手册说明:GYRO_CONFIG寄存器的BIT4-BIT3是量程位,需要左移3位写入
// 我们用默认的±250°/s,对应宏GYRO_FS_250DPS=0
ret = mpu6050_write_reg(MPU6050_REG_GYRO_CONFIG, GYRO_FS_250DPS << 3);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "陀螺仪量程配置失败");
return ret;
}
// 第五步:配置加速度计量程
// 手册说明:ACCEL_CONFIG寄存器的BIT4-BIT3是量程位,左移3位写入
// 我们用默认的±2g,对应宏ACCEL_FS_2G=0
ret = mpu6050_write_reg(MPU6050_REG_ACCEL_CONFIG, ACCEL_FS_2G << 3);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "加速度计量程配置失败");
return ret;
}
// 所有配置完成,初始化成功
ESP_LOGI(TAG, "MPU6050传感器初始化完成");
return ESP_OK;
}
5.3.7 数据读取与转换函数
这部分的核心逻辑是:通过mpu6050_read_reg读取连续的寄存器数据→拼接高低字节为 16 位有符号整数→通过转换系数,把原始数字量转换成有物理意义的数值。
// 实现加速度读取函数
esp_err_t mpu6050_read_accel(float *ax, float *ay, float *az)
{
// 加速度数据共6个字节:X轴高+低字节,Y轴高+低字节,Z轴高+低字节
uint8_t raw_data[6] = {0};
// 从加速度起始寄存器,连续读取6个字节
esp_err_t ret = mpu6050_read_reg(MPU6050_REG_ACCEL_XOUT_H, raw_data, 6);
if (ret != ESP_OK) {
return ret;
}
// 拼接高低字节:MPU6050是大端模式,高字节在前,低字节在后
// 数据是16位有符号整数(int16_t),因为加速度有正负
int16_t raw_ax = (int16_t)((raw_data[0] << 8) | raw_data[1]);
int16_t raw_ay = (int16_t)((raw_data[2] << 8) | raw_data[3]);
int16_t raw_az = (int16_t)((raw_data[4] << 8) | raw_data[5]);
// 转换为物理值(单位:g):原始值 / 对应量程的灵敏度系数
*ax = raw_ax / ACCEL_SENS_2G;
*ay = raw_ay / ACCEL_SENS_2G;
*az = raw_az / ACCEL_SENS_2G;
return ESP_OK;
}
// 实现陀螺仪读取函数
esp_err_t mpu6050_read_gyro(float *gx, float *gy, float *gz)
{
// 陀螺仪数据共6个字节:X/Y/Z轴各2字节
uint8_t raw_data[6] = {0};
// 从陀螺仪起始寄存器,连续读取6个字节
esp_err_t ret = mpu6050_read_reg(MPU6050_REG_GYRO_XOUT_H, raw_data, 6);
if (ret != ESP_OK) {
return ret;
}
// 拼接高低字节
int16_t raw_gx = (int16_t)((raw_data[0] << 8) | raw_data[1]);
int16_t raw_gy = (int16_t)((raw_data[2] << 8) | raw_data[3]);
int16_t raw_gz = (int16_t)((raw_data[4] << 8) | raw_data[5]);
// 转换为物理值(单位:°/s)
*gx = raw_gx / GYRO_SENS_250;
*gy = raw_gy / GYRO_SENS_250;
*gz = raw_gz / GYRO_SENS_250;
return ESP_OK;
}
// 实现温度读取函数
esp_err_t mpu6050_read_temp(float *temp)
{
// 温度数据共2个字节
uint8_t raw_data[2] = {0};
// 从温度起始寄存器,连续读取2个字节
esp_err_t ret = mpu6050_read_reg(MPU6050_REG_TEMP_OUT_H, raw_data, 2);
if (ret != ESP_OK) {
return ret;
}
// 拼接高低字节
int16_t raw_temp = (int16_t)((raw_data[0] << 8) | raw_data[1]);
// 转换为摄氏温度:公式来自MPU6050官方datasheet
*temp = (raw_temp / 340.0f) + 36.53f;
return ESP_OK;
}
到这里,mpu6050.c的所有核心功能就全部实现了。
六、完善main.c
主函数是 ESP32 程序的入口,核心逻辑是:按顺序初始化→循环调用驱动接口读取数据→打印输出。
// 引入依赖头文件
#include <stdio.h>
#include "esp_log.h"
#include "mpu6050.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// 主函数日志标签
#define TAG "MPU6050_MAIN"
// ESP32程序的唯一入口,相当于单片机的main函数
void app_main(void)
{
ESP_LOGI(TAG, "===== ESP32-S3 MPU6050 驱动测试开始 =====");
// 第一步:初始化I2C总线
// ESP_ERROR_CHECK:如果函数返回不是ESP_OK,直接终止程序,打印错误
ESP_ERROR_CHECK(mpu6050_i2c_init());
// 第二步:初始化MPU6050传感器
ESP_ERROR_CHECK(mpu6050_init());
ESP_LOGI(TAG, "传感器初始化完成,开始循环读取数据");
// 定义变量,存放读取到的传感器数据
float ax, ay, az; // 加速度,单位g
float gx, gy, gz; // 陀螺仪,单位°/s
float temp; // 温度,单位℃
// 第三步:无限循环,定时读取数据
while (1) {
// 读取加速度数据
if (mpu6050_read_accel(&ax, &ay, &az) == ESP_OK) {
ESP_LOGI(TAG, "加速度 | X: %.3f g | Y: %.3f g | Z: %.3f g", ax, ay, az);
} else {
ESP_LOGE(TAG, "加速度数据读取失败");
}
// 读取陀螺仪数据
if (mpu6050_read_gyro(&gx, &gy, &gz) == ESP_OK) {
ESP_LOGI(TAG, "陀螺仪 | X: %.3f dps | Y: %.3f dps | Z: %.3f dps", gx, gy, gz);
} else {
ESP_LOGE(TAG, "陀螺仪数据读取失败");
}
// 读取温度数据
if (mpu6050_read_temp(&temp) == ESP_OK) {
ESP_LOGI(TAG, "芯片温度: %.2f ℃", temp);
} else {
ESP_LOGE(TAG, "温度数据读取失败");
}
// 打印分隔线,方便查看日志
ESP_LOGI(TAG, "----------------------------------------");
// 延时1000ms,也就是1秒读取一次
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
七、编译烧录测试
编译烧录成功后,打开串口监视器,可以看到每秒都有数据的输出:


更多推荐



所有评论(0)