软件I2C读写MPU6050

软件I2C读写MPU6050

I2C

  1. 什么是I2C?
    在这里插入图片描述
    所有I2C设备的SCL连在一起,SDA连在一起,设备的SCL和SDA均要配置成开漏输出模式(因为I2C规定SDA和SCL的默认状态是高电平),SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右.I2C总线的示意图如下:
    在这里插入图片描述
  2. I2C时序基本单元
  • 起始条件&终止条件
    起始条件:SCL高电平期间,SDA从高电平切换到低电平
    在这里插入图片描述
    终止条件:SCL高电平期间,SDA从低电平切换到高电平.
    在这里插入图片描述

  • 发送一个字节
    SCL低电平期间,主机将数据位依次放到SDA线上(高位先行)然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节
    在这里插入图片描述

  • 接收一个字节
    SCL低电平期间从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA).
    在这里插入图片描述

  • 发送应答&接收应答
    发送应答: 主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答.
    在这里插入图片描述

接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA).

在这里插入图片描述

  1. I2C时序图
  • 指定地址写
    对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data).
    在这里插入图片描述
  • 当前地址读
    对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data).
    在这里插入图片描述
  • 指定地址读
    对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data).
    在这里插入图片描述
  1. I2C基本结构
    在这里插入图片描述

MPU6500

  1. 什么是MPU6500?
    MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景.在6轴姿态传感器中:
    3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度.
    3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度.
    在这里插入图片描述
  2. MPU6500参数
    ①16位ADC采集传感器的模拟信号,量化范围:-32768~32767
    ②加速度计满量程选择:±2、±4、±8、±16(g)
    ③陀螺仪满量程选择: ±250、±500、±1000、±2000(°/sec)
    ④可配置的数字低通滤波器
    ⑤可配置的时钟源
    ⑥可配置的采样分频
    ⑦I2C从机地址:1101000(AD0=0)、 1101001(AD0=1)

编写软件程序读写MPU6050

  • 接线图如下:
    在这里插入图片描述

  • 在Hardware文件夹下新建一个MyI2C.c和MyI2C.h文件
    MyI2C.c代码:

#include "stm32f10x.h"                  // Device header
#include "Delay.h" 

//BitAction是嵌入式开发(尤其STM32等微控制器平台)中常见的自定义枚举类型
//主要用于GPIO(通用输入输出)引脚的电平状态控制
void MyI2C_W_SCL(uint8_t BitVlaue)
{
	GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitVlaue);
	//I2C标准模式(100kHz)要求每个时钟脉冲的建立时间(如SCL高电平稳定时间、SCL低电平保持时间)不小于4.7微秒,
	//10微秒的延时确保了信号的稳定性,避免因时序过快导致的通信错误。
	//STM32 F1系列即使不加延时函数也可以
	Delay_us(10);			
}

//控制数据线的写入函数
void MyI2C_W_SDA(uint8_t BitVlaue)
{
	GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitVlaue);
	Delay_us(10);			
}

//控制数据线的读函数
uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitVlaue;
	BitVlaue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
	Delay_us(10);
	return BitVlaue;
}

//初始化
void MyI2C_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	//将pB10和PB11初始化为开漏输出模式,此模式不只能输出,还能输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);						//将PB10和PB11引脚初始化为开漏输出
	
	/*设置GPIO初始化后的默认电平*/
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}

//I2C的启动条件函数
void MyI2C_Start(void)
{
	//在这个启动函数开始时,SDA并不一定是高电平,所以要先将SDA设置为高电平,防止被误认为是终止信号
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

//I2C终止条件函数
void MyI2C_Stop(void)
{
	//在这个终止函数开始时,SDA并不一定是低电平,所以要先将SDA设置为低电平,防止被误认为是开始信号
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

//发送一个字节
void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for(i =0;i < 8;i ++)
	{
		//先将SDA的数据高位写入
		MyI2C_W_SDA(Byte & (0x80 >> i));
		//手动控制SCL,高位读取SDA(读取期间SDA不能有变化),低位让SDA变换数据
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}

}

//读取一个字节
uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i,Byte = 0x00;
	// 主机释放SDA(设置为高阻态),让从机驱动SDA
	MyI2C_W_SDA(1);
	for(i = 0;i < 8;i ++)
	{
		MyI2C_W_SCL(1);
		 // 读取SDA电平,MyI2C_R_SDA() == 1表示主机暂时解除对SDA线的控制(从机发送的当前位数据)
		if(MyI2C_R_SDA() == 1)
		{
			Byte |= (0x80 >> i); 
		}
		 // 主机拉低SCL,结束当前时钟周期(从机准备下一位数据)
		MyI2C_W_SCL(0);
	
	}
	return Byte;

}

//发送一个应答位
void MyI2C_SendACK(uint8_t AckBit)
{
		MyI2C_W_SDA(AckBit);
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);

}

//读取一个应答位
uint8_t MyI2C_ReceiveACK(void)
{
	uint8_t AckBit;
	// 主机释放SDA(设置为高阻态),让从机驱动SDA
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();
	MyI2C_W_SCL(0);
	
	return AckBit;
}

将I2C的各个关键部分配置完成后在调用函数即可实现数据的发送与接收了,这就要对I2C的时序图和基本时序单元有一个比较深入的了解.

  • 在Hardware文件夹下编写MPU6500.c(我的芯片是MPU6500,与MPU6050的芯片的参数基本吻合)和MPU6500.h文件.
    MPU6500.c代码:
#include "stm32f10x.h"                  // Device header
#include "MyI2C.h"
#include "MPU6500_Reg.h"

#define MPU6500_ADDRESS 0xD0

//写入从设备寄存器数据
void MPU6500_WriteReg(uint8_t RegAddress,uint8_t Data)
{
	//传输开始
	MyI2C_Start();
	
	//指定写入的从设备
	MyI2C_SendByte(MPU6500_ADDRESS);
	MyI2C_ReceiveACK();
	
	//指定写入的从设备的寄存器
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveACK();
	
	//指定写入的从设备的寄存器的数据
	MyI2C_SendByte(Data);
	MyI2C_ReceiveACK();
	
	//传输完成,停止
	MyI2C_Stop();

}

//在从设备寄存器中读取数据
uint8_t MPU6500_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	//传输开始
	MyI2C_Start();
	
	//指定写入的从设备
	MyI2C_SendByte(MPU6500_ADDRESS);
	MyI2C_ReceiveACK();
	
	//指定写入的从设备的寄存器
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveACK();
	
	//重复开始以读取数据
	MyI2C_Start();
	
	//指定读数据的从设备的地址,这里将从设备的地址的第八位改为1表示读数据
	MyI2C_SendByte(MPU6500_ADDRESS | 0x01);
	MyI2C_ReceiveACK();
	
	Data = MyI2C_ReceiveByte();
	//只读取一次数据,所以在读取完这此数据将ACK置为1,表示读取暂停
	MyI2C_SendACK(1);
	
	//传输完成,停止
	MyI2C_Stop();
	
	return Data;
}

//初始化
void MPU6500_Init(void)
{
    // 1. 初始化 I2C 通信接口(确保能与 MPU6500 通信)
    MyI2C_Init();
    
    // 2. 配置电源管理寄存器 1 (MPU6500_PWR_MGMT_1)
    //    - 清除睡眠模式(BIT6=0)
    //    - 选择 PLL 时钟源(X 轴陀螺仪参考时钟)
    //    - 温度传感器启用(默认已启用,但明确设置更安全)
    MPU6500_WriteReg(MPU6500_PWR_MGMT_1, 0x01);
    
    // 3. 配置电源管理寄存器 2 (MPU6500_PWR_MGMT_2)
    //    - 禁用加速度计和陀螺仪的低功耗模式(所有轴启用)
    MPU6500_WriteReg(MPU6500_PWR_MGMT_2, 0x00);
    
    // 4. 设置采样率分频器 (MPU6500_SMPLRT_DIV)
    //    - 公式:Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)
    //    - 例如:Gyro 输出率默认 8kHz,分频值为 0x09 → 8000/(9+1) = 800Hz
    MPU6500_WriteReg(MPU6500_SMPLRT_DIV, 0x09);
    
    // 5. 配置扩展寄存器 (MPU6500_CONFIG)
    //    - 设置数字低通滤波器(DLPF)带宽(例如 0x06 对应 5Hz 低通滤波)
    //    - 降低噪声,但可能影响高频信号响应
    MPU6500_WriteReg(MPU6500_CONFIG, 0x06);
    
    // 6. 配置陀螺仪量程 (MPU6500_GYRO_CONFIG)
    //    - 0x18 → ±2000°/s(最大量程,适合剧烈运动场景)
    MPU6500_WriteReg(MPU6500_GYRO_CONFIG, 0x18);
    
    // 7. 配置加速度计量程 (MPU6500_ACCEL_CONFIG)
    //    - 0x18 → ±16g(平衡量程与灵敏度)
    MPU6500_WriteReg(MPU6500_ACCEL_CONFIG, 0x18);
}

/**
  * 函    数:MPU6050获取数据
  * 参    数:AccX AccY AccZ 加速度计X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  * 参    数:GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  * 返 回 值:无
  */
void MPU6500_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;								//定义数据高8位和低8位的变量
	
	DataH = MPU6500_ReadReg(MPU6500_ACCEL_XOUT_H);		//读取加速度计X轴的高8位数据
	DataL = MPU6500_ReadReg(MPU6500_ACCEL_XOUT_L);		//读取加速度计X轴的低8位数据
	*AccX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6500_ReadReg(MPU6500_ACCEL_YOUT_H);		//读取加速度计Y轴的高8位数据
	DataL = MPU6500_ReadReg(MPU6500_ACCEL_YOUT_L);		//读取加速度计Y轴的低8位数据
	*AccY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6500_ReadReg(MPU6500_ACCEL_ZOUT_H);		//读取加速度计Z轴的高8位数据
	DataL = MPU6500_ReadReg(MPU6500_ACCEL_ZOUT_L);		//读取加速度计Z轴的低8位数据
	*AccZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6500_ReadReg(MPU6500_GYRO_XOUT_H);		//读取陀螺仪X轴的高8位数据
	DataL = MPU6500_ReadReg(MPU6500_GYRO_XOUT_L);		//读取陀螺仪X轴的低8位数据
	*GyroX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6500_ReadReg(MPU6500_GYRO_YOUT_H);		//读取陀螺仪Y轴的高8位数据
	DataL = MPU6500_ReadReg(MPU6500_GYRO_YOUT_L);		//读取陀螺仪Y轴的低8位数据
	*GyroY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6500_ReadReg(MPU6500_GYRO_ZOUT_H);		//读取陀螺仪Z轴的高8位数据
	DataL = MPU6500_ReadReg(MPU6500_GYRO_ZOUT_L);		//读取陀螺仪Z轴的低8位数据
	*GyroZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
}

//读取芯片的ID号
uint8_t MPU6500_GetID(void)
{
	return MPU6500_ReadReg(MPU6500_WHO_AM_I);
}


值得注意的是:
①在读取寄存器和写入寄存器要严格按照I2C的时序来操作.
②在函数void MPU6500_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);中,虽然DataH定义为8位,但是在执行代码DataH << 8后的8为并不会丢失,因为该函数的形参是16为的int16_t.

  • 在Hardware文件夹下编写MPU6500_Reg.c文件,此文件用来存储从设备MPU6500的重要功能单元(如陀螺仪和重力加速度的测量器)的寄存器地址
    MPU6500_Reg.c代码:
#ifndef __MPU6500_REG_H
#define __MPU6500_REG_H

#define	MPU6500_SMPLRT_DIV		0x19
#define	MPU6500_CONFIG			0x1A
#define	MPU6500_GYRO_CONFIG		0x1B
#define	MPU6500_ACCEL_CONFIG	0x1C
             
#define	MPU6500_ACCEL_XOUT_H	0x3B
#define	MPU6500_ACCEL_XOUT_L	0x3C
#define	MPU6500_ACCEL_YOUT_H	0x3D
#define	MPU6500_ACCEL_YOUT_L	0x3E
#define	MPU6500_ACCEL_ZOUT_H	0x3F
#define	MPU6500_ACCEL_ZOUT_L	0x40
#define	MPU6500_TEMP_OUT_H		0x41
#define	MPU6500_TEMP_OUT_L		0x42
#define	MPU6500_GYRO_XOUT_H		0x43
#define	MPU6500_GYRO_XOUT_L		0x44
#define	MPU6500_GYRO_YOUT_H		0x45
#define	MPU6500_GYRO_YOUT_L		0x46
#define	MPU6500_GYRO_ZOUT_H		0x47
#define	MPU6500_GYRO_ZOUT_L		0x48
             
#define	MPU6500_PWR_MGMT_1		0x6B
#define	MPU6500_PWR_MGMT_2		0x6C
#define	MPU6500_WHO_AM_I		0x75

#endif

### 软件实现 MPU6050I2C 读写 为了通过 I2C 协议对 MPU6050 进行读写操作,需要遵循其特定的寄存器映射和通信流程。以下是基于 STM32 平台的一个典型实现方法。 #### 初始化 I2C 接口 在开始与 MPU6050 通信之前,需初始化 I2C 外设并配置相应的 GPIO 引脚作为 SDA 和 SCL。以下是一个简单的初始化函数: ```c void I2C_Init(void) { // 配置 I2C 时钟、GPIO 模式以及速率设置 __HAL_RCC_I2C1_CLK_ENABLE(); // 启用 I2C1 时钟 __HAL_RCC_GPIOB_CLK_ENABLE(); // 启用 GPIOB 时钟 GPIO_InitTypeDef GPIO_InitStruct = {0}; // 配置 SDA (PB7) 和 SCL (PB6) 为开漏输出模式 GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; // 开漏复用功能 GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉电阻启用 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 初始化 I2C 周外设备 I2C_HandleTypeDef hi2c1; hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; // 设置标准模式或快速模式频率 hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0x00; // 不使用自己的地址 hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; if(HAL_I2C_Init(&hi2c1) != HAL_OK){ Error_Handler(); } } ``` #### 写入数据到指定寄存器 MPU6050 使用 I2C 地址 `0xD0` 或 `0xD2`(取决于 AD0 引脚的状态),可以通过发送目标寄存器地址及其对应的值完成写入操作。 ```c uint8_t MPU_Write(uint8_t reg_addr, uint8_t data) { HAL_StatusTypeDef status; status = HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, reg_addr, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY); return (status == HAL_OK ? 0 : 1); // 返回状态码表示成功与否 } ``` #### 从指定寄存器取数据 要从 MPU6050 中获取某个寄存器的内容,则可以调用如下函数: ```c uint8_t MPU_Read(uint8_t reg_addr) { uint8_t data; HAL_StatusTypeDef status; status = HAL_I2C_Mem_Read(&hi2c1, MPU6050_ADDR, reg_addr, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY); return (status == HAL_OK ? data : 0xFF); // 如果失败返回错误标志 } ``` 如果需要连续取多个字节的数据,可调整参数中的长度字段即可。 #### 示例应用:启动 MPU6050取加速度计原始数据 下面展示了一个完整的例子用于开启 MPU6050 设备,并尝试打印 X/Y/Z 方向上的未补偿加速数值。 ```c #include "stm32fxxx_hal.h" #define MPU6050_ADDR ((uint8_t)(0xD0 >> 1)) // 默认地址 #define SMPLRT_DIV ((uint8_t)0x19) #define GYRO_CONFIG ((uint8_t)0x1B) #define ACCEL_CONFIG ((uint8_t)0x1C) int main() { I2C_Init(); // 设置采样率分频器 MPU_Write(SMPLRT_DIV, 0x07); // 配置陀螺仪灵敏度 ±250°/s MPU_Write(GYRO_CONFIG, 0x00); // 配置加速度计范围 ±2g MPU_Write(ACCEL_CONFIG, 0x00); while (1) { int16_t ax, ay, az; // 获取 XYZ 加速轴数据 uint8_t buffer[6]; HAL_I2C_Mem_Read(&hi2c1, MPU6050_ADDR, 0x3B, I2C_MEMADD_SIZE_8BIT, buffer, sizeof(buffer), HAL_MAX_DELAY); ax = (buffer[0] << 8) | buffer[1]; ay = (buffer[2] << 8) | buffer[3]; az = (buffer[4] << 8) | buffer[5]; printf("Accel_X:%d Accel_Y:%d Accel_Z:%d\n", ax, ay, az); } return 0; } // 错误处理程序定义省略... ``` 以上代码片段展示了如何利用 HAL 库编写针对 MPU6050 的基本驱动逻辑[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

必胜的思想钢印

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值