资讯动态

STM32+MPU6050六轴传感器完整实战:I2C读取、姿态解算与上位机显示

发布时间:2026/9/12 2:21:52 来源:尧图企业网站定制
简介MPU6050六轴传感器数据实时显示资源基于STM32ZET6实现三轴加速度与三轴角速度的采集并通过LCD实时展示内置配套上位机适合嵌入式初学者、物联网开发者以及无人机、机器人等姿态检测项目设计者。压缩包共94个文件以C源码与H头文件为主43个h、40个c附带STM32完整工程、hex烧录文件、匿名四轴上位机以及说明文档整体仅2.4MB轻量易下载。当前已有817人学习下载。通过该项目可完整掌握I2C驱动MPU6050的方式、加速度与角速度的数据读取格式、互补滤波姿态解算思路、LCD屏显驱动以及串口/上位机通信联动目录划分清晰方便按模块理解与二次开发。项目源码注释到位适合对照学习与移植。1. 为什么MPU6050这类六轴传感器值得把数据链路完整跑一遍MPU6050集成了三轴加速度计和三轴陀螺仪出货十几年依然是平衡车、低成本四轴、机器人关节姿态检测的入门首选。它的难点不在传感器本身而在“读不到有效数据”和“读到数据不知道对不对”这两件事上。这个项目把STM32F103ZET6、MPU6050、TFTLCD和匿名四轴上位机组在一起正好是一条完整的采集-解算-显示-上传链路对想做姿态控制的人来说是一个可以直接跑通的参考实现。它能帮你理清三件事一是I2C寄存器怎么配二是原始数据怎么变成角度三是数据如何通过串口送到上位机实时绘制波形。也适合做物联网或智能硬件开发的人快速验证传感器选型方案。2. MPU6050的I2C寄存器配置与STM32驱动实现2.1 I2C从设备地址与总线时序细节MPU6050的7位I2C地址由AD0引脚决定电路接地时地址是0x68接高电平则是0x69。这个地址参与总线上的每一次读写写寄存器时发送地址写位0xD0读数据时发送地址读位0xD1。如果PCB上AD0正好被电阻拉起而代码却写死0x68I2C通信会一直得不到ACK读出来的数据自然全是0xFF或0。所以拿到任何一块MPU6050模块第一步不是写驱动而是确认AD0引脚电平或者在代码里把设备地址做成宏方便替换。I2C总线上还有两个细节直接影响稳定性上拉电阻和速率。MPU6050数据手册约定标准模式100kHz、快速模式400kHzSTM32的IO配置成开漏输出后必须外接4.7kΩ上拉电阻到3.3V。很多人在STM32上配置了复用开漏模式之后忘了确认外部上拉结果SCL/SDA一直拉不高。这个工程的HARDWARE/MPU6050目录下用的是软件模拟I2C没有走STM32硬件I2C外设。原因也简单STM32F1系列的硬件I2C在使用上对中断优先级和时序要求更苛刻模拟I2C则更容易控制时序也方便用逻辑分析仪抓波形定位问题。2.2 关键寄存器配置与初始化代码初始化MPU6050之前先列出几个必配寄存器方便对照后续代码。PWR_MGMT_1是电源管理寄存器控制复位和睡眠SMPLRT_DIV和CONFIG决定采样率与滤波带宽GYRO_CONFIG和ACCEL_CONFIG决定量程直接影响后续的灵敏度换算。寄存器地址名称功能说明常用值0x6BPWR_MGMT_1电源管理、复位、睡眠控制0x00 唤醒0x19SMPLRT_DIV采样率分频0x07 得到1kHz采样率0x1ACONFIG数字低通滤波器配置0x06 约5Hz带宽0x1BGYRO_CONFIG陀螺仪量程选择0x18 ±2000°/s0x1CACCEL_CONFIG加速度计量程选择0x10 ±8g对应初始化代码如下这里用的是标准外设库写法MPU6050_WriteReg内部完成模拟I2C的起始、地址、寄存器地址和数据发送。void MPU6050_Init(void) { IIC_Init(); MPU6050_WriteReg(PWR_MGMT_1, 0x80); // 复位整个芯片 delay_ms(100); MPU6050_WriteReg(PWR_MGMT_1, 0x00); // 退出睡眠选择内部时钟 MPU6050_WriteReg(SMPLRT_DIV, 0x07); // 采样率 8000Hz / (17) 1kHz MPU6050_WriteReg(CONFIG, 0x06); // 开启低通滤波约5Hz带宽 MPU6050_WriteReg(GYRO_CONFIG, 0x18); // 陀螺仪量程 ±2000°/s灵敏度16.4 LSB/(°/s) MPU6050_WriteReg(ACCEL_CONFIG, 0x10); // 加速度计量程 ±8g灵敏度4096 LSB/g }第一行写0x80是让芯片内部所有寄存器回到复位值避免上一次运行留下的配置影响这次工作。写完后必须留出足够时间一般延时100ms再写0x00唤醒。如果跳过复位直接工作芯片可能还处于睡眠状态后面读加速度计数据会一直是0。CONFIG写0x06并不代表截止频率精确等于5Hz实际值会随温度有一点点漂移但对LCD显示和上位机观察足够如果后面要做姿态控制这个带宽需要适当提高否则控制环路的相位滞后会很严重。注意模拟I2C的GPIO不要选在JTAG复用引脚上调试器和I2C同时操作时会出现不可预期的干扰这是最小系统板上很常见的坑。2.3 连续读取14字节原始数据并换算物理量MPU6050把加速度计、温度、陀螺仪的寄存器连续排布在0x3B到0x48所以一次突发读取14个字节是最常用的方式。读取时只需要一个I2C起始信号写完寄存器地址后连续收数代码实现很短。void MPU6050_ReadData(int16_t* accel, int16_t* gyro) { uint8_t buf[14]; MPU6050_ReadRegs(0x3B, buf, 14); accel[0] (int16_t)((buf[0] 8) | buf[1]); accel[1] (int16_t)((buf[2] 8) | buf[3]); accel[2] (int16_t)((buf[4] 8) | buf[5]); // buf[6]~buf[7] 是温度寄存器本项目不处理 gyro[0] (int16_t)((buf[8] 8) | buf[9]); gyro[1] (int16_t)((buf[10] 8) | buf[11]); gyro[2] (int16_t)((buf[12] 8) | buf[13]); }读出来的原始值是带符号的16位整数范围由量程决定。以±2000°/s的陀螺仪量程为例每个单位代表16.4°/s真实角速度是raw/16.4加速度计在±8g下每个单位代表1/4096g。换算时要避免整数除法正确做法是先转成float再除。这套数据理论上输出率能到1kHz但LCD刷新和串口往往到不了这个频率所以实际循环里会加延时把输出频率降到20Hz到50Hz上位机波形看起来才平稳。3. 姿态解算互补滤波与四元数更新的工程实现3.1 加速度计测角和陀螺仪积分的互补逻辑加速度计静止时测到的是重力加速度的分量。板子水平放置时Z轴读到约1gX和Y约0g倾斜后重力被分解到三个轴上用atan2f(Y, Z)可以算出绕X轴的roll角用atan2f(-X, sqrt(YYZZ))算出绕Y轴的pitch角。这个方法只依赖当前重力方向不依赖时间静止时是准确的。问题在于物体一旦加减速运动加速度计读到的不再只是重力角度会跟着运动方向跳动甚至完全错误。陀螺仪输出的角速度不会受运动加速度干扰但它测的是变化率必须积分才有角度。积分一次就会引入零漂如果静止时陀螺仪有0.1°/s的零偏积分10秒就产生1°误差。互补滤波的核心是用一个系数α把两个来源结合对陀螺仪积分的角度做高通处理保留快速变化对加速度计计算的角度做低通处理约束长期漂移。α越接近1越信任陀螺仪动态响应好但收敛慢α越小越信任加速度计角度稳但动态迟钝。一般在0.95到0.98之间试。3.2 能直接落地的互补滤波代码工程里的main.c使用的是一阶互补滤波实现下面这个写法把计算和换算放在一起方便在LCD和串口里同时输出原始值和角度值。#define FILTER_ALPHA 0.96f #define RAD_TO_DEG 57.29578f float roll, pitch; void Attitude_Update(float ax, float ay, float az, float gyro_x, float gyro_y, float dt) { float roll_acc, pitch_acc; roll_acc atan2f(ay, az) * RAD_TO_DEG; pitch_acc atan2f(-ax, sqrtf(ay * ay az * az)) * RAD_TO_DEG; roll FILTER_ALPHA * (roll gyro_x * dt) (1 - FILTER_ALPHA) * roll_acc; pitch FILTER_ALPHA * (pitch gyro_y * dt) (1 - FILTER_ALPHA) * pitch_acc; }使用前要先按量程把原始值换算成度每秒。如果陀螺仪量程是±2000°/s传入的gyro_x应该是raw/16.4而不是寄存器原值。dt是相邻两次调用的时间差单位秒一般用一个毫秒时间戳算出来。代码里的atan2f是四象限反正切和atan的区别在于当分母是0时atan会除零而atan2f能正确返回±90°所以姿态角计算必须用atan2f。这个简化公式在roll或pitch接近±90°时会遇到奇点做翻转类应用就得用四元数。3.3 Mahony四元数更新的可替换方案一阶互补滤波覆盖不了yaw角因为Z轴上的航向角无法用重力加速度直接测量只能靠陀螺仪积分。实际项目里更常用Mahony算法它属于显式互补滤波用加速度计修正陀螺仪的横滚和俯仰误差再更新四元数还可以带陀螺仪零偏估计。相比扩展卡尔曼滤波要反复调Q和R矩阵Mahony在STM32F103上的计算量很小单次更新不到100μs是性价比最高的方案。在没有磁力计的场景下yaw方向完全依赖积分四元数更新会退化成只修正roll和pitch的形式。如果你想把解算放进MPU6050内部也可以加载DMP固件由传感器自己输出四元数主控只负责读取。DMP的好处是主控负载更低缺点是需要额外初始化流程且寄存器读写时序要求更严格。用USMART组件注册对应的DMP读取函数后可以在不重新编译的情况下切换两种解算方式对比效果。姿态解算方案主控开销动态性能调试难度一阶互补滤波低一般低Mahony四元数低较好中扩展卡尔曼滤波高好高MPU6050内部DMP最低稳定中4. LCD实时刷新与匿名四轴上位机的串口对接4.1 FSMC驱动的LCD接入方式HARDWARE/LCD目录下是TFTLCD驱动常见控制器有ILI9341或ILI9325。这类屏幕数据总线接在STM32的FSMC接口上通过地址线A2区分命令和数据地址0x60000000写命令0x60020000写数据。FSMC的好处是LCD刷屏不需要CPU逐位翻转IOCPU往内存地址写数据时序由硬件完成整屏刷新也能控制在几十毫秒内。初始化代码精简后如下主要通过寄存器命令配置扫描方向、像素格式和电源参数。void LCD_Init(void) { // 使能FSMC和GPIO时钟并配置为复用推挽输出 LCD_WriteReg(0xCF, 0x0000C100); LCD_WriteReg(0xED, 0x01640303); LCD_WriteReg(0xE8, 0x00010303); LCD_WriteReg(0xC0, 0x262626); LCD_WriteReg(0x36, 0x00A8); // RGB扫描方向 LCD_WriteReg(0x3A, 0x0055); // 16位像素格式 LCD_WriteReg(0x11, 0); // 退出睡眠模式 delay_ms(120); }每款屏的控制器不完全一样命令值不能从头抄到尾需要看屏的ID或驱动手册确认。0x36是扫描方向寄存器决定文字和图像是正向还是倒向显示接反了画面就是镜像的。实时显示传感器数据时我一般会固定一块刷新区域比如只刷新数值所在的行先用背景色矩形清掉旧数据再写新值避免整屏重绘的闪烁。数据刷新频率和LCD刷新率要匹配20Hz到30Hz就足够看到连续变化。4.2 匿名四轴上位机协议帧格式zip里带的匿名四轴上位机通过串口接收数据包协议帧结构非常固定解析起来也简单。以下是姿态数据功能字0x01的数据帧格式数据区由多个int16字段组成高位在前。字节位置内容说明0~10xAA 0xAA帧头20x01功能字0x01对应姿态数据30x1C数据区长度这里是28字节4~27int16数组加速度、角速度、角度等数据体28累加和前面所有字节累加溢出丢弃这套协议本身就是匿名官方上位机使用的不需要自己造上位机协议。实际使用时要特别注意两点串口波特率默认115200上位机软件运行在Windows上通过虚拟串口接入。如果帧头之前出现了调试打印信息或者波特率不匹配上位机就会一直停在连接状态。先把MPU6050的数据停了在main循环里只发一组固定角度确认上位机能画出曲线再接入真实传感器这样能把通信问题和解算问题分开处理。注意匿名上位机的版本较多V2.6和V4.x在功能字定义上有差异使用前先打开随包的协议文档确认长度字段含义避免按旧版本格式解析新版本数据。4.3 串口数据发送函数实现发送函数核心是拆字节和累加校验。下面这段代码把10个int16数值按高字节在前打包同时计算校验和。void Send_Ano(int16_t* data, uint8_t len) { uint8_t sum 0; uint8_t i; USART_SendByte(0xAA); USART_SendByte(0xAA); USART_SendByte(0x01); USART_SendByte(len * 2); for (i 0; i len; i) { USART_SendByte(data[i] 8); USART_SendByte(data[i] 0xFF); sum data[i] 8; sum data[i] 0xFF; } USART_SendByte(sum); }调用时传入一个长度为10的int16数组前3个放加速度原始值后3个放陀螺仪原始值再放roll、pitch、yaw剩余位置可以放温度或状态字正好填满匿名上位机的显示窗口。校验和只对数据区累加帧头和长度字节不参与这是匿名上位机V2.6之后形成的约定。如果发现数据偶尔丢帧先检查串口是否在115200波特率下稳定再降发送频率。20Hz到50Hz足够观察过高反而会让上位机绘图线程跟不上。5. USMART串口调试与MPU6050数据异常定位技巧5.1 USMART让函数调用变成串口命令USMART是工程里的调试组件把函数调用映射成串口命令。调试时不需重新编译烧录直接在上位机串口助手输入“MPU6050_Init()”就能重新初始化传感器输入“LCD_Clear(0xFFFF)”验证屏幕。注册方式就是往usmart_config.c的映射表里加一行函数名和函数指针。struct _m_usmart_nametab usmart_nametab[] { #if USMART_USE_WFUNCTIONS 1 (void*)MPU6050_Init, MPU6050_Init, (void*)MPU6050_ReadData, MPU6050_ReadData, (void*)Send_Ano, Send_Ano, #endif };USMART解析串口收到的字符串通过函数名查表执行。参数个数和函数原型绑定在usmart_str.c里带参数的函数也能调比如“MPU6050_WriteReg(0x6B, 0x00)”可以直接操作寄存器。调试时可以一边改角度解算参数一边从串口看结果不用反复刷flash。注意别注册带指针参数的自定义函数USMART会把地址数值当参数传进去容易跑到未定义状态。5.2 数据异常排查表与零偏校准数据异常基本集中在几个固定原因按硬件、驱动、算法顺序排查效率最高。现象可能原因优先处理读到的数据全为0芯片处于睡眠状态检查PWR_MGMT_1是否写0x00数据恒为0xFF或ACK异常AD0地址与代码不符测量AD0引脚电平角度持续漂移陀螺仪零偏未校准静止采样取平均作为零偏LCD有数字但上位机空白帧头或波特率不匹配先用固定数据包测试角度随运动剧烈跳动数字低通带宽过高调CONFIG寄存器或加大α最后一个技巧是零偏校准。上电后不要动板子连续采集1000次陀螺仪原始值求平均之后每次读取先减掉这个零偏。这比写复杂的滤波算法更直接能把静态漂移从每分钟几度降到几十秒不到1度。加速度计也可以用同样思路静止状态下计算三轴模长sqrt(ax²ay²az²)如果和1g偏差超过20mg需要做六面标定。拿这个工程做验证时我建议按USMART调原始数据、LCD调显示、上位机对比波形的顺序推进哪一步断了就回到对应寄存器去查而不是直接怀疑算法。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价