STM32F103C8T6驱动MPU6050传感器:从I2C通信到数据读取的保姆级教程

张开发
2026/6/22 18:44:59 15 分钟阅读
STM32F103C8T6驱动MPU6050传感器:从I2C通信到数据读取的保姆级教程
STM32F103C8T6驱动MPU6050传感器从硬件连接到数据解析的全流程实战在嵌入式开发领域运动传感器的应用越来越广泛。MPU6050作为一款集成了三轴加速度计和三轴陀螺仪的六轴传感器因其高性价比和易用性成为许多电子竞赛和智能硬件项目的首选。本文将带你从零开始使用STM32F103C8T6这款蓝桥杯和智能车竞赛常用的MCU完整实现MPU6050的驱动开发。1. 硬件准备与电路连接在开始编写代码之前正确的硬件连接是项目成功的基础。MPU6050与STM32F103C8T6主要通过I2C接口通信我们需要确保物理连接正确无误。所需材料清单STM32F103C8T6最小系统板蓝色药丸开发板MPU6050传感器模块杜邦线若干4.7kΩ上拉电阻2个USB转TTL模块用于调试输出电路连接示意图MPU6050引脚STM32F103C8T6引脚备注VCC3.3V电源正极GNDGND电源地SCLPB6I2C1时钟线SDAPB7I2C1数据线AD0不接或接地地址选择引脚注意MPU6050的I2C总线需要上拉电阻通常使用4.7kΩ电阻将SCL和SDA线分别上拉到3.3V。部分开发板可能已经内置上拉电阻需要确认。常见连接问题排查电源问题确保MPU6050工作在3.3V电压下5V可能会损坏传感器地址冲突AD0引脚悬空时地址为0x68接地为0x69上拉电阻如果通信不稳定检查上拉电阻是否连接正确2. I2C通信基础与STM32硬件I2C配置MPU6050通过I2C接口与主控通信STM32F103C8T6内置硬件I2C控制器我们可以直接使用库函数进行配置。2.1 I2C协议基本原理I2CInter-Integrated Circuit是一种同步、多主从的串行通信总线由Philips公司开发。其主要特点包括只需要两根线SCL时钟线和SDA数据线支持多主多从架构7位或10位设备地址标准模式100kHz快速模式400kHzI2C通信基本时序起始条件SCL高电平时SDA从高变低设备地址7位地址1位读写方向0写1读应答信号每个字节传输后接收方发送ACK数据传送先传高位后传低位停止条件SCL高电平时SDA从低变高2.2 STM32硬件I2C初始化以下是使用标准外设库初始化I2C1的代码示例#include stm32f10x_i2c.h void I2C1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; // 使能GPIOB和I2C1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // 配置PB6(SCL)和PB7(SDA)为复用开漏输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // I2C1配置 I2C_InitStructure.I2C_Mode I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 0x00; // 主模式不需要地址 I2C_InitStructure.I2C_Ack I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed 400000; // 400kHz I2C_Init(I2C1, I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); }关键参数说明I2C_DutyCycle_2设置时钟占空比为2:1标准模式I2C_ClockSpeed通信速率MPU6050最高支持400kHzGPIO_Mode_AF_OD复用开漏模式需要外部上拉电阻3. MPU6050寄存器配置与初始化MPU6050通过寄存器进行配置和数据读取了解关键寄存器的作用是正确使用传感器的前提。3.1 MPU6050关键寄存器电源管理寄存器PWR_MGMT_1, 0x6BBIT6: 休眠模式1-休眠0-正常工作BIT0-2: 时钟源选择配置寄存器CONFIG, 0x1ABIT0-2: 数字低通滤波器(DLPF)配置陀螺仪配置寄存器GYRO_CONFIG, 0x1BBIT3-4: 量程选择00-±250°/s01-±500°/s10-±1000°/s11-±2000°/s加速度计配置寄存器ACCEL_CONFIG, 0x1CBIT3-4: 量程选择00-±2g01-±4g10-±8g11-±16g采样率分频器SMPLRT_DIV, 0x19采样率 陀螺仪输出率 / (1 SMPLRT_DIV)3.2 MPU6050初始化流程以下是完整的MPU6050初始化函数#define MPU6050_ADDR 0xD0 // 默认地址左移一位写操作 void MPU6050_Init(void) { // 1. 解除休眠模式 I2C_WriteByte(MPU6050_ADDR, 0x6B, 0x00); Delay_ms(100); // 2. 配置陀螺仪量程 ±500°/s I2C_WriteByte(MPU6050_ADDR, 0x1B, 0x08); // 3. 配置加速度计量程 ±2g I2C_WriteByte(MPU6050_ADDR, 0x1C, 0x00); // 4. 配置数字低通滤波器 5Hz I2C_WriteByte(MPU6050_ADDR, 0x1A, 0x06); // 5. 配置采样率分频器 I2C_WriteByte(MPU6050_ADDR, 0x19, 0x07); // 1kHz/(71)125Hz // 6. 验证设备ID uint8_t id I2C_ReadByte(MPU6050_ADDR, 0x75); if(id ! 0x68) { printf(MPU6050初始化失败ID:0x%02X\r\n, id); } else { printf(MPU6050初始化成功\r\n); } }I2C读写函数实现// 向指定寄存器写入一个字节 void I2C_WriteByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) { while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // 发送起始条件 I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); // 发送设备地址(写) I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); // 发送寄存器地址 I2C_SendData(I2C1, regAddr); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 发送数据 I2C_SendData(I2C1, data); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 发送停止条件 I2C_GenerateSTOP(I2C1, ENABLE); } // 从指定寄存器读取一个字节 uint8_t I2C_ReadByte(uint8_t devAddr, uint8_t regAddr) { uint8_t data 0; // 先写入寄存器地址 while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, regAddr); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 重新发送起始条件切换为读模式 I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Receiver); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); // 接收数据(NACK) I2C_AcknowledgeConfig(I2C1, DISABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); data I2C_ReceiveData(I2C1); // 发送停止条件 I2C_GenerateSTOP(I2C1, ENABLE); I2C_AcknowledgeConfig(I2C1, ENABLE); return data; }4. 数据读取与处理MPU6050的传感器数据存储在特定的寄存器中我们需要正确读取并转换为实际物理量。4.1 原始数据读取MPU6050的加速度计、陀螺仪和温度数据都是16位有符号整数分布在两个8位寄存器中高字节和低字节。typedef struct { int16_t Accel_X; int16_t Accel_Y; int16_t Accel_Z; int16_t Gyro_X; int16_t Gyro_Y; int16_t Gyro_Z; float Temperature; } MPU6050_Data; void MPU6050_ReadData(MPU6050_Data *data) { uint8_t buf[14]; // 从0x3B开始连续读取14个字节 I2C_ReadMultiBytes(MPU6050_ADDR, 0x3B, buf, 14); // 加速度计数据大端模式 >void I2C_ReadMultiBytes(uint8_t devAddr, uint8_t regAddr, uint8_t *pData, uint8_t len) { // 先写入寄存器地址 while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, regAddr); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 重新发送起始条件切换为读模式 I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Receiver); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); // 读取多个字节 for(uint8_t i0; ilen; i) { if(i len-1) { // 最后一个字节发送NACK I2C_AcknowledgeConfig(I2C1, DISABLE); } while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); pData[i] I2C_ReceiveData(I2C1); } // 发送停止条件 I2C_GenerateSTOP(I2C1, ENABLE); I2C_AcknowledgeConfig(I2C1, ENABLE); }4.2 数据转换与校准原始数据需要根据量程设置转换为实际物理量并进行校准以提高精度。加速度计数据转换// 根据量程设置转换加速度计数据±2g float accel_scale 2.0 / 32768.0; // ±2g对应32768 float accelX_g >// 根据量程设置转换陀螺仪数据±500°/s float gyro_scale 500.0 / 32768.0; // ±500°/s对应32768 float gyroX_dps >void MPU6050_Calibrate(MPU6050_Data *offset) { MPU6050_Data data; int32_t sum[6] {0}; // 采集100次数据求平均 for(int i0; i100; i) { MPU6050_ReadData(data); sum[0] data.Accel_X; sum[1] data.Accel_Y; sum[2] data.Accel_Z; sum[3] data.Gyro_X; sum[4] data.Gyro_Y; sum[5] data.Gyro_Z; Delay_ms(10); } // 计算偏移量 offset-Accel_X sum[0] / 100; offset-Accel_Y sum[1] / 100; offset-Accel_Z sum[2] / 100 - 16384; // 减去1g(假设Z轴朝下) offset-Gyro_X sum[3] / 100; offset-Gyro_Y sum[4] / 100; offset-Gyro_Z sum[5] / 100; }使用校准数据修正测量值MPU6050_Data raw, offset, calibrated; // 校准传感器 MPU6050_Calibrate(offset); // 读取并修正数据 MPU6050_ReadData(raw); calibrated.Accel_X raw.Accel_X - offset.Accel_X; calibrated.Accel_Y raw.Accel_Y - offset.Accel_Y; calibrated.Accel_Z raw.Accel_Z - offset.Accel_Z; calibrated.Gyro_X raw.Gyro_X - offset.Gyro_X; calibrated.Gyro_Y raw.Gyro_Y - offset.Gyro_Y; calibrated.Gyro_Z raw.Gyro_Z - offset.Gyro_Z;5. 实际应用与调试技巧在实际项目中应用MPU6050时会遇到各种问题。以下是一些常见问题的解决方案和调试技巧。5.1 常见问题排查问题1I2C通信失败检查硬件连接是否正确特别是SCL和SDA线确认上拉电阻是否连接通常4.7kΩ使用逻辑分析仪或示波器检查I2C波形尝试降低I2C时钟频率如从400kHz降到100kHz问题2读取的数据异常检查电源是否稳定3.3V确认MPU6050是否已正确初始化检查量程设置是否合理进行传感器校准问题3数据噪声大增加数字低通滤波器设置进行多次采样取平均检查电路板是否有机械振动确保电源去耦电容(0.1μF)靠近MPU6050电源引脚5.2 数据可视化与调试通过串口将数据发送到上位机可以更直观地观察传感器数据变化。以下是使用串口输出数据的示例#include stm32f10x_usart.h void USART1_Init(uint32_t baudrate) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 使能USART1和GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置PA9(TX)为复用推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 配置PA10(RX)为浮空输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); // USART1配置 USART_InitStructure.USART_BaudRate baudrate; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART1, USART_InitStructure); USART_Cmd(USART1, ENABLE); } void USART1_SendChar(char ch) { while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); USART_SendData(USART1, ch); } void USART1_SendString(char *str) { while(*str) { USART1_SendChar(*str); } } void printf(const char *fmt, ...) { char buf[128]; va_list args; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); USART1_SendString(buf); }在主循环中输出传感器数据int main(void) { MPU6050_Data data, offset; // 系统初始化 SystemInit(); Delay_Init(); USART1_Init(115200); I2C1_Init(); MPU6050_Init(); // 校准传感器 MPU6050_Calibrate(offset); while(1) { // 读取传感器数据 MPU6050_ReadData(data); // 修正偏移 data.Accel_X - offset.Accel_X; data.Accel_Y - offset.Accel_Y; data.Accel_Z - offset.Accel_Z; data.Gyro_X - offset.Gyro_X; data.Gyro_Y - offset.Gyro_Y; data.Gyro_Z - offset.Gyro_Z; // 转换为实际物理量 float accelX_g data.Accel_X * (2.0 / 32768.0); float gyroX_dps data.Gyro_X * (500.0 / 32768.0); // 通过串口输出 printf(Accel: X%.2fg, Y%.2fg, Z%.2fg\r\n, accelX_g, accelY_g, accelZ_g); printf(Gyro: X%.2f°/s, Y%.2f°/s, Z%.2f°/s\r\n, gyroX_dps, gyroY_dps, gyroZ_dps); printf(Temp: %.1f°C\r\n\r\n, data.Temperature); Delay_ms(100); // 100ms采样间隔 } }5.3 进阶应用姿态解算MPU6050的数据可以用于计算设备的姿态俯仰角、横滚角。常用的算法有互补滤波和卡尔曼滤波。简单互补滤波实现float pitch 0, roll 0; // 俯仰角和横滚角 float dt 0.01; // 采样时间间隔(秒) float alpha 0.98; // 互补滤波系数 void UpdateAttitude(MPU6050_Data *data) { // 加速度计计算的角度 float accel_pitch atan2(data-Accel_Y,>

更多文章