MPU6050姿态传感器原理与软件I2C通信实现
- MPU6050概述
MPU6050是一款集成了三轴加速度计和三轴陀螺仪的6轴运动处理设备,能够测量设备在X、Y、Z轴上的加速度和角速度数据。通过算法融合这些数据,可以计算出设备的姿态角度,广泛应用于平衡车、无人机等需要实时姿态感知的智能设备中。
- 三轴加速度计:检测设备在三个正交方向上的线性加速度
- 三轴陀螺仪:测量设备围绕三个轴的旋转速率

1.1 技术规格
- 16位模数转换器,数据范围:-32768~32767
- 加速度测量范围:±2g、±4g、±8g、±16g可选
- 陀螺仪测量范围:±250°/s、±500°/s、±1000°/s、±2000°/s可选
- 可编程数字低通滤波器
- 多种时钟源选择
- 可配置采样频率
I2C从机地址:1101000(AD0=0)、1101001(AD0=1) 在编程实现中,I2C地址有两种常见表示方式。以1101000为例,第一种方式是将7位二进制直接转换为十六进制,即0x68。在I2C通信中,第一个字节的高7位是从机地址,最低位是读写位。因此,如果将0x68视为从机地址,发送第一个字节时需左移1位后再与读写位进行或运算。第二种方式是将0x68左移1位后的值作为从机地址,即0xD0,此时发送时若为写操作直接使用0xD0,读操作则使用0xD1(或上0x01)。
1.2 硬件连接
MPU6050模块原理图 
XCL、XDA:这两个引脚用于扩展传感器,通常连接磁力计或气压计等外部设备。MPU6050的主机接口可以直接访问这些扩展芯片的数据,并通过其内部的DMP(数字运动处理器)单元进行数据融合和姿态解算。
1.3 内部结构

- 软件模拟I2C实现
2.1 连接方式
SCL连接到PB10,SDA连接到PB11。由于本节使用软件模拟I2C,即通过普通GPIO口手动控制电平变化,无需STM32内部外设支持,因此端口可自由选择,例如SCL接PA8,SDA接PA9等。
根据I2C协议规范,SCL和SDA线均需要上拉电阻,但模块已内置上拉电阻,无需外部添加。XCL和XDA为扩展接口,本应用中不使用。AD0引脚用于修改从机地址最低位,模块内部已接下拉电阻,悬空时相当于接地。INT为中断输出引脚,本应用中不使用。

2.2 代码实现
程序架构设计如下:
- 首先构建I2C通信层的.c和.h文件。在通信层中,实现GPIO初始化和六个基本时序单元(起始信号、停止信号、发送字节、接收字节、发送应答、接收应答)。
- 然后创建MPU6050的.c和.h文件。基于I2C通信层,实现指定地址读写功能,进而实现寄存器配置和数据读取功能。
- 最后在main.c中调用MPU6050模块,完成初始化、数据获取和显示。
由于使用软件模拟I2C,无需使用STM32的I2C库函数,只需基本的GPIO读写函数即可。
SoftI2C.c(避免与库函数重名)
#include "stm32f10x.h"
#include "Delay.h"
void SoftI2C_Initialize(void)
{
// 完成两个任务:
// ① 将SCL和SDA配置为开漏输出模式;
// ② 将SCL和SDA设置为高电平。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
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);
GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}
// 封装GPIO操作函数
void SoftI2C_Set_SCL(uint8_t Level)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)Level);
Delay_us(10);
}
void SoftI2C_Set_SDA(uint8_t Level)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)Level);
Delay_us(10);
}
uint8_t SoftI2C_Get_SDA(void)
{
uint8_t Level;
Level = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
Delay_us(10);
return Level;
}
// 实现I2C的六个基本时序单元
//
// 1. 起始条件
void SoftI2C_Start(void)
{
SoftI2C_Set_SDA(1);
SoftI2C_Set_SCL(1);
SoftI2C_Set_SDA(0);
SoftI2C_Set_SCL(0);
}
// 2. 停止条件
void SoftI2C_Stop(void)
{
SoftI2C_Set_SDA(0);
SoftI2C_Set_SCL(1);
SoftI2C_Set_SDA(1);
}
// 3. 发送一个字节
void SoftI2C_TransmitByte(uint8_t Data)
{
uint8_t i;
for(i = 0; i < 8; i++)
{
SoftI2C_Set_SDA(Data & (0x80 >> i));
SoftI2C_Set_SCL(1);
SoftI2C_Set_SCL(0);
}
}
// 4. 接收一个字节
uint8_t SoftI2C_ReceiveByte(void)
{
uint8_t i, Data = 0x00;
SoftI2C_Set_SDA(1);
for(i = 0; i < 8; i++)
{
SoftI2C_Set_SCL(1);
if(SoftI2C_Get_SDA() == 1)
{
Data |= (0x80 >> i);
}
SoftI2C_Set_SCL(0);
}
return Data;
}
// 5. 发送应答位
void SoftI2C_SendAck(uint8_t Ack)
{
SoftI2C_Set_SDA(Ack);
SoftI2C_Set_SCL(1);
SoftI2C_Set_SCL(0);
}
// 6. 接收应答位
uint8_t SoftI2C_ReceiveAck(void)
{
uint8_t Ack;
SoftI2C_Set_SDA(1);
SoftI2C_Set_SCL(1);
Ack = SoftI2C_Get_SDA();
SoftI2C_Set_SCL(0);
return Ack;
}
SoftI2C.h
#ifndef __SOFTI2C_H
#define __SOFTI2C_H
void SoftI2C_Initialize(void);
void SoftI2C_Start(void);
void SoftI2C_Stop(void);
void SoftI2C_TransmitByte(uint8_t Data);
uint8_t SoftI2C_ReceiveByte(void);
void SoftI2C_SendAck(uint8_t Ack);
uint8_t SoftI2C_ReceiveAck(void);
#endif
MotionSensor.c
#include "stm32f10x.h"
#include "SoftI2C.h"
#include "SensorRegisters.h"
#define SENSOR_ADDRESS 0xD0
// 向指定寄存器写入数据
void Sensor_WriteRegister(uint8_t Reg, uint8_t Value)
{
SoftI2C_Start();
SoftI2C_TransmitByte(SENSOR_ADDRESS);
SoftI2C_ReceiveAck();
SoftI2C_TransmitByte(Reg);
SoftI2C_ReceiveAck();
SoftI2C_TransmitByte(Value);
SoftI2C_ReceiveAck();
SoftI2C_Stop();
}
// 从指定寄存器读取数据
uint8_t Sensor_ReadRegister(uint8_t Reg)
{
uint8_t Value;
SoftI2C_Start();
SoftI2C_TransmitByte(SENSOR_ADDRESS);
SoftI2C_ReceiveAck();
SoftI2C_TransmitByte(Reg);
SoftI2C_ReceiveAck();
SoftI2C_Start();
SoftI2C_TransmitByte(SENSOR_ADDRESS | 0x01);
SoftI2C_ReceiveAck();
Value = SoftI2C_ReceiveByte();
SoftI2C_SendAck(1);
SoftI2C_Stop();
return Value;
}
void Sensor_Initialize(void)
{
SoftI2C_Initialize();
Sensor_WriteRegister(PWR_REG1, 0x01);
Sensor_WriteRegister(PWR_REG2, 0x00);
Sensor_WriteRegister(SAMPLE_RATE, 0x09);
Sensor_WriteRegister(CONFIG_REG, 0x06);
Sensor_WriteRegister(GYRO_CONFIG, 0x18);
Sensor_WriteRegister(ACCEL_CONFIG, 0x18);
}
uint8_t Sensor_GetDeviceID(void)
{
return Sensor_ReadRegister(WHO_AM_I_REG);
}
// 读取传感器数据
void Sensor_ReadMotionData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
int16_t *RotX, int16_t *RotY, int16_t *RotZ)
{
uint8_t HighByte, LowByte;
HighByte = Sensor_ReadRegister(ACCEL_X_HIGH);
LowByte = Sensor_ReadRegister(ACCEL_X_LOW);
*AccX = (HighByte << 8) | LowByte;
HighByte = Sensor_ReadRegister(ACCEL_Y_HIGH);
LowByte = Sensor_ReadRegister(ACCEL_Y_LOW);
*AccY = (HighByte << 8) | LowByte;
HighByte = Sensor_ReadRegister(ACCEL_Z_HIGH);
LowByte = Sensor_ReadRegister(ACCEL_Z_LOW);
*AccZ = (HighByte << 8) | LowByte;
HighByte = Sensor_ReadRegister(GYRO_X_HIGH);
LowByte = Sensor_ReadRegister(GYRO_X_LOW);
*RotX = (HighByte << 8) | LowByte;
HighByte = Sensor_ReadRegister(GYRO_Y_HIGH);
LowByte = Sensor_ReadRegister(GYRO_Y_LOW);
*RotY = (HighByte << 8) | LowByte;
HighByte = Sensor_ReadRegister(GYRO_Z_HIGH);
LowByte = Sensor_ReadRegister(GYRO_Z_LOW);
*RotZ = (HighByte << 8) | LowByte;
}
MotionSensor.h
#ifndef __MOTIONSENSOR_H
#define __MOTIONSENSOR_H
void Sensor_WriteRegister(uint8_t Reg, uint8_t Value);
uint8_t Sensor_ReadRegister(uint8_t Reg);
void Sensor_Initialize(void);
void Sensor_ReadMotionData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
int16_t *RotX, int16_t *RotY, int16_t *RotZ);
uint8_t Sensor_GetDeviceID(void);
#endif
SensorRegisters.h
#ifndef __SENSORREGISTERS_H
#define __SENSORREGISTERS_H
#define SAMPLE_RATE_REG 0x19
#define CONFIG_REG 0x1A
#define GYRO_CONFIG_REG 0x1B
#define ACCEL_CONFIG_REG 0x1C
#define ACCEL_X_HIGH 0x3B
#define ACCEL_X_LOW 0x3C
#define ACCEL_Y_HIGH 0x3D
#define ACCEL_Y_LOW 0x3E
#define ACCEL_Z_HIGH 0x3F
#define ACCEL_Z_LOW 0x40
#define TEMPERATURE_HIGH 0x41
#define TEMPERATURE_LOW 0x42
#define GYRO_X_HIGH 0x43
#define GYRO_X_LOW 0x44
#define GYRO_Y_HIGH 0x45
#define GYRO_Y_LOW 0x46
#define GYRO_Z_HIGH 0x47
#define GYRO_Z_LOW 0x48
#define PWR_REG1 0x6B
#define PWR_REG2 0x6C
#define WHO_AM_I_REG 0x75
#endif
main.c
#include "stm32f10x.h"
#include "Delay.h"
#include "Display.h"
#include "MotionSensor.h"
int16_t AccX, AccY, AccZ, RotX, RotY, RotZ;
int main(void)
{
Display_Initialize();
Sensor_Initialize();
Display_ShowString(1, 1, "ID:");
Display_ShowHexNum(1, 4, Sensor_GetDeviceID(), 2);
Display_ShowString(1, 7, "Acc|Rot");
Display_ShowString(2, 1, "X |");
Display_ShowString(3, 1, "Y |");
Display_ShowString(4, 1, "Z |");
while(1)
{
Sensor_ReadMotionData(&AccX, &AccY, &AccZ, &RotX, &RotY, &RotZ);
Display_ShowSignedNum(2, 3, AccX, 5);
Display_ShowSignedNum(3, 3, AccY, 5);
Display_ShowSignedNum(4, 3, AccZ, 5);
Display_ShowSignedNum(2, 10, RotX, 5);
Display_ShowSignedNum(3, 10, RotY, 5);
Display_ShowSignedNum(4, 10, RotZ, 5);
}
}
其他相关头文件和实现代码可参考:Display.h(OLED调试工具)、Delay.h(延时函数实现)