目录
- I2C
- 介绍
- 配置
- 安装驱动
- 通信
- 创建&删除命令链接容器
- 起始时序
- 写数据
- 读数据
- 结束时序
- 开始命令
- mpu6050 硬件i2c驱动代码&调试
- 代码
- 调试
I2C
介绍
介绍部分可以看我写的【ESP32 idf 软件模拟I2C驱动MPU6050实现六轴加速度的获取】,这个是使用软件模拟的I2C时序从而实现的,这次是硬件idf实现。
步骤:
配置
代码:
uint8_t res;i2c_config_t i2c_config_InitStructure;i2c_config_InitStructure.clk_flags = 0; // 采用默认时钟i2c_config_InitStructure.master.clk_speed = 50000; // 通信速度 分为标准速度100kbps和快速400kbps,这里用50kbps即可i2c_config_InitStructure.mode = I2C_MODE_MASTER; // 主机模式i2c_config_InitStructure.scl_io_num = MPU6050_SCL_Pin; // 通信引脚,scli2c_config_InitStructure.scl_pullup_en = GPIO_PULLUP_ENABLE; // scl 上拉使能i2c_config_InitStructure.sda_io_num = MPU6050_SDA_Pin; // sda引脚i2c_config_InitStructure.sda_pullup_en = GPIO_PULLUP_ENABLE; // sda 上拉使能res = i2c_param_config(i2c_port, &i2c_config_InitStructure);//配置参数if (res == ESP_OK) {ESP_LOGI(TAG, "i2c_param_config success");} else {ESP_LOGE(TAG, "i2c_param_config failed with error: %d", res);}
安装驱动
代码
res = i2c_driver_install(i2c_port, I2C_MODE_MASTER, 0, 0, 0); // 安装驱动if (res == ESP_OK) {ESP_LOGI(TAG, "i2c_driver_install success");} else {ESP_LOGE(TAG, "i2c_driver_install failed with error: %d", res);}
参数一选择I2C资源,和上面配置的保持一致。
参数二选择主从模式。
如果是主机的话,后三个的参数都可以不需要,塞个0即可。
通信
创建&删除命令链接容器
删除命令容器可以减少资源浪费。
起始时序
写数据
写数据有以下两种方式,当然了,都是主模式使用的。
区别在于第一个函数是写一个Byte,而第二个函数可以写多个Byte。
读数据
结束时序
开始命令
既然上面的容器配置好了,必然需要开启该容器
mpu6050 硬件i2c驱动代码&调试
代码
MPU6050.c
/** @Author: i want to 舞动乾坤* @Date: 2024-07-26 08:52:56* @LastEditors: i want to 舞动乾坤* @LastEditTime: 2024-07-26 16:42:29* @FilePath: \i2c_hardware_driver_mpu6050\main\MPU6050.c* @Description: * * Copyright (c) 2024 by i want to 舞动乾坤, All Rights Reserved. */#include <stdint.h>
#include "MPU6050_REG.h"
#include <driver/i2c.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>#define MPU6050_Address 0x68 // MPU6050 的 7 位地址
#define MPU6050_SCL_Pin GPIO_NUM_22
#define MPU6050_SDA_Pin GPIO_NUM_21
#define i2c_port I2C_NUM_0static const char* TAG = "MPU6050";/*** @description: MPU6050写寄存器* @param {uint8_t} RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述* @param {uint8_t} Data 要写入寄存器的数据,范围:0x00~0xFF* @tip:设备地址通常是一个 7 位地址。当在软件中使用该地址时,通常需要将其左移一位(即乘以 2)以适应 I2C 总线传输格式。具体来说:I2C 总线上的设备地址总共是 8 位:前 7 位是设备的地址。第 8 位(最低位)是读/写位(R/W),用于指示这次操作是读还是写。0 表示写操作(I2C_MASTER_WRITE)。1 表示读操作(I2C_MASTER_READ)。所以,当你指定设备地址时,需要将其左移一位,并将读/写位添加到最低位。例如,对于一个 7 位设备地址 0x68:写操作:地址为 0x68 << 1 | I2C_MASTER_WRITE,即 0xD0。读操作:地址为 0x68 << 1 | I2C_MASTER_READ,即 0xD1。这是为什么需要左移一位的原因。* @return {*}无*/
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{uint8_t res;i2c_cmd_handle_t cmd = i2c_cmd_link_create(); // 创建链接,装载容器i2c_master_start(cmd); // 产生起始信号i2c_master_write_byte(cmd, (MPU6050_Address << 1) | I2C_MASTER_WRITE, true); // 发送从机地址,并产生应答i2c_master_write_byte(cmd, RegAddress, true); // 发送从机数据寄存器的地址,并产生应答i2c_master_write_byte(cmd, Data, true); // 写入数据 并产生应答i2c_master_stop(cmd); // 产生停止信号res = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_PERIOD_MS); // 启动容器,开始工作i2c_cmd_link_delete(cmd); // 删除链接容器,避免占用资源if (res == ESP_OK) {ESP_LOGI(TAG, "MPU6050_WriteReg success - RegAddress: 0x%02X, Data: 0x%02X", RegAddress, Data);} else {ESP_LOGE(TAG, "MPU6050_WriteReg failed with error: %d - RegAddress: 0x%02X, Data: 0x%02X", res, RegAddress, Data);}
}/*** @description: 读寄存器的数据* * @param {uint8_t} RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述* @return {*}读取寄存器的数据,范围:0x00~0xFF*/
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{uint8_t Data = 0;uint8_t res;i2c_cmd_handle_t cmd = i2c_cmd_link_create(); // 创建链接,装载容器i2c_master_start(cmd); // 产生起始信号i2c_master_write_byte(cmd, (MPU6050_Address << 1) | I2C_MASTER_WRITE, true); // 发送从机地址,并产生应答i2c_master_write_byte(cmd, RegAddress, true); // 发送从机数据寄存器的地址,并产生应答// 开始在该寄存器下读数据i2c_master_start(cmd); // 产生起始信号i2c_master_write_byte(cmd, (MPU6050_Address << 1) | I2C_MASTER_READ, true); // 发送从机地址,读写位为1,表示即将读取,并产生应答i2c_master_read_byte(cmd, &Data, I2C_MASTER_LAST_NACK); // 读一个字节的数据至Data内,并且非应答i2c_master_stop(cmd); // 发送停止信号res = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_PERIOD_MS); // 启动容器,开始工作i2c_cmd_link_delete(cmd); // 删除链接,保证资源不会被一直占用if (res == ESP_OK) {ESP_LOGI(TAG, "MPU6050_ReadReg success - RegAddress: 0x%02X, Data: 0x%02X", RegAddress, Data);} else {ESP_LOGE(TAG, "MPU6050_ReadReg failed with error: %d - RegAddress: 0x%02X, retrying...", res, RegAddress);vTaskDelay(10 / portTICK_PERIOD_MS); // 延迟10ms后重试}return Data;
}/*** @description: MPU5050初始化* @return {*}无*/
void MPU6050_Init(void)
{uint8_t res;i2c_config_t i2c_config_InitStructure;i2c_config_InitStructure.clk_flags = 0; // 采用默认时钟i2c_config_InitStructure.master.clk_speed = 50000; // 通信速度 分为标准速度100kbps和快速400kbps,这里用标准速度即可i2c_config_InitStructure.mode = I2C_MODE_MASTER; // 主机模式i2c_config_InitStructure.scl_io_num = MPU6050_SCL_Pin; // 通信引脚,scli2c_config_InitStructure.scl_pullup_en = GPIO_PULLUP_ENABLE; // scl 上拉使能i2c_config_InitStructure.sda_io_num = MPU6050_SDA_Pin; // sda引脚i2c_config_InitStructure.sda_pullup_en = GPIO_PULLUP_ENABLE; // sda 上拉使能res = i2c_param_config(i2c_port, &i2c_config_InitStructure);//配置参数if (res == ESP_OK) {ESP_LOGI(TAG, "i2c_param_config success");} else {ESP_LOGE(TAG, "i2c_param_config failed with error: %d", res);}res = i2c_driver_install(i2c_port, I2C_MODE_MASTER, 0, 0, 0); // 安装驱动if (res == ESP_OK) {ESP_LOGI(TAG, "i2c_driver_install success");} else {ESP_LOGE(TAG, "i2c_driver_install failed with error: %d", res);}MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01); // 唤醒mpu6050MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09); // 10分频MPU6050_WriteReg(MPU6050_CONFIG, 0x06); // 数字低通滤波器MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); // 陀螺仪寄存器MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18); // 加速度寄存器 最大量程
}/*** 函 数:MPU6050获取ID号* 参 数:无* 返 回 值:MPU6050的ID号*/
uint8_t MPU6050_GetID(void)
{return MPU6050_ReadReg(MPU6050_WHO_AM_I);}/*** 函 数:MPU6050获取数据* 参 数:AccX AccY AccZ 加速度计X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767* 参 数:GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767* 返 回 值:无* 具体选择转的角速度是多少 是通过比例公式计算出来的 读取的数据/32768 = x /满量程 求x*/
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;//读取加速度x轴寄存器的高八位DataH=MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);//读取加速度x轴寄存器的低八位DataL=MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);*AccX=(DataH<<8) | DataL;//读取//读取加速度y轴寄存器的高八位DataH=MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);//读取加速度y轴寄存器的低八位DataL=MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);*AccY=(DataH<<8) | DataL; //返回出去//读取加速度z轴寄存器的高八位DataH=MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);//读取加速度z轴寄存器的低八位 DataL=MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);*AccZ=(DataH<<8) | DataL; //返回出去//读取加速度z轴寄存器的高八位DataH=MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);//读取加速度z轴寄存器的低八位 DataL=MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);*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_H__
void MPU6050_Init(void);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);
void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data);
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);
uint8_t MPU6050_GetID(void);#endif
MPU6050_REG.h
#ifndef __MPU6050_REG_H__
#define __MPU6050_REG_H__
//存放MPU6050常用的寄存器地址
#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
/** @Author: i want to 舞动乾坤* @Date: 2024-07-25 21:29:11* @LastEditors: i want to 舞动乾坤* @LastEditTime: 2024-07-26 09:26:14* @FilePath: \i2c_hardware_driver_mpu6050\main\main.c* @Description: * * Copyright (c) 2024 by i want to 舞动乾坤, All Rights Reserved. */
#include <stdio.h>
#include "MPU6050.h"
#include <esp_log.h>
#include <freeRtos/FreeRTOS.h>
#include <freeRtos/task.h>
uint8_t ID; //定义用于存放ID号的变量
int16_t AX, AY, AZ, GX, GY, GZ; //定义用于存放各个数据的变量
void app_main(void)
{MPU6050_Init();ID=MPU6050_GetID();//获取设备IDESP_LOGI("MPU6050 ID","#%x\n",ID);while(1){MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);//把六个变量的地址传递过去//显示六元组数据ESP_LOGI("AX value is","%d\n",AX);ESP_LOGI("AY value is","%d\n",AY);ESP_LOGI("AZ value is","%d\n",AZ);ESP_LOGI("GX value is","%d\n",GX);ESP_LOGI("GY value is","%d\n",GY);ESP_LOGI("GZ value is","%d\n",GZ);vTaskDelay(1000/portTICK_PERIOD_MS);//1000ms获取一次}
}
调试
参考大佬文章:【快速上手ESP32(基于ESP-IDF&VSCode)】