拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32与GY87九轴传感器读取实战:寄存器配置、HAL库实现与避坑指南

简介这套STM32读取GY87模块的源码包专为需要快速获取MPU6050六轴姿态、HMC5883地磁及BMP180气压温度数据的嵌入式开发者准备。资源内置完整驱动共6个文件3个头文件与3个C源文件压缩包仅4KB结构精简便于直接移植到现有工程。目前已1162人学习代码覆盖I2C接口配置、传感器初始化、原始数据读取及校准处理等关键环节并以中断方式保证实时读取。针对MPU6050实现了加速度与角速度解算针对HMC5883完成磁场强度采集针对BMP180则包含温度与气压换算流程可帮助开发者避开常见寄存器配置陷阱。无论是用于无人机姿态导航、可穿戴设备还是物联网环境监测这套代码都能显著降低多传感器驱动开发门槛让使用者快速完成数据融合与上层应用调试整体代码风格清晰注释简明便于二次开发。 把 STM32 和 GY87 这颗九轴传感器接起来读取数据是很多做四轴飞行器、平衡小车、姿态解算项目的朋友绕不开的第一步。GY87 看起来只是一块蓝底小板子实际集成了 MPU6050 陀螺仪加速度计和 HMC5883L 磁力计能输出加速度、角速度、磁场强度三类原始数据配合算法之后就是稳定的姿态角。我自己最开始做平衡小车时想着 GM87 就是个大号 MPU6050照着教程抄代码结果加速度计和陀螺仪输出正常磁力计却一直读到 0排查了整整一个晚上最后发现是数据输出路径理解错了。这篇文章就把从硬件接线、寄存器配置到代码实现、常见坑位一次性讲清楚适合刚入手 STM32 传感器的同学也适合想快速移植到项目里的老手直接抄作业。1. 拆解GY87模块两颗芯片与数据传输路径GY87 这个名字本身不是芯片型号而是模块商给 PCB 起的名字。拿到板子后先别急着接线把上面的丝印看清楚。绝大多数 GY87 模块上可以看到 U1 是 MPU6050U2 是 HMC5883L一颗负责六轴惯性数据一颗负责三轴地磁数据两组合起来才是九轴。1.1 板载芯片MPU6050与HMC5883LMPU6050 集成三轴加速度计和三轴陀螺仪内部还有 DMP 运动处理器可以直接输出四元数但绕不开的是初始化和配置。HMC5883L 则是一颗早期的三轴磁力计I2C 接口、测量范围从 ±0.88Ga 到 ±8.1Ga虽然性能比不上现在的磁力计芯片但做航向角、做电子罗盘完全够用。这两颗芯片在模块上通常共用一条 I2C 总线但地址不同MPU6050 的 7 位地址是 0x68如果 AD0 拉高则是 0x69HMC5883L 是 0x1E。注意 HMC5883L 的从机地址是 0x1E换算成 8 位写地址就是 0x3C但 STM32 的 HAL 库里HAL_I2C_Mem_Read第一个参数传 7 位地址即可别在这里多移一位否则数据全不对。1.2 数据输出路径与地址规划这里有个 GY87 系列模块最容易隐藏的差异部分新版 GY87 把 HMC5883L 直接挂在主 I2C 总线上而部分旧版设计是把 HMC5883L 接在 MPU6050 的 AUX I2C 辅助总线上。如果你在代码里直接读 0x1E 地址读不到磁力计数据但读 MPU6050 一切正常就要怀疑模块是不是走了辅助 I2C 通路。判断方法很简单用万用表量 HMC5883L 的 SCL/SDA 引脚是否与模块排针的 SCL/SDA 连通如果连通说明是直连方案直接访问 0x1E 就行如果不通则说明需要先配置 MPU6050 的 I2C Master 模式通过0x6A、0x24、0x25等寄存器转发访问。对新手来说我建议一律先把模块型号和原理图查清楚再动代码不然一晚上搭进去都排查不出来。从实际项目角度考虑九轴传感器比六轴多出来的三轴磁力计主要用来解决航向角漂移问题。陀螺仪长时间积分会产生累积误差加速度计可以修正横滚和俯仰但无法修正偏航这时候就需要磁力计提供一个绝对的地磁参考。这也是为什么许多姿态解算库如 Madgwick、Mahony输入都包含磁力计分量。2. 硬件接线与上电前的检查STM32 读取 GY87 的接线非常固定但越是简单越容易在细节上翻车。很多同学上来就把模块插到面包板上杜邦线随便一怼然后发现读不出数据首先怀疑代码实际上问题往往出在电平和共地上。2.1 供电、电平与滤波GY87 模块的 VCC 通常支持 3.3V 或 5V但板上芯片的逻辑电平是 3.3V。如果使用 5V 供电某些模块的 HMC5883L 可能会因为内部 LDO 压降发抖导致读数不稳。我的建议是直接用 STM32 开发板的 3.3V 输出给模块供电虽然磁力计工作时会有几毫安的电流波动但 3.3V LDO 通常扛得住。SCL 和 SDA 引脚属于开漏结构需要上拉电阻。GY87 板上一般已经焊了 10kΩ 上拉电阻但如果你的 I2C 总线上挂了很多设备线一长波形就会被拉垮此时可以在 SCL、SDA 对地并联 100pF 电容帮助滤除高频噪声或者在模块端再补一个 4.7kΩ 上拉。放心I2C 对多上拉并不敏感别补到 1kΩ 以下就行。另外一点接线时让 GND 线尽可能粗短。GY87 的磁场测量对电流噪声非常敏感如果 GND 线细长电机驱动或继电器动作时地弹噪声会被磁力计直接读出来表现就是磁场强度数据里跳出一个很大的尖峰。我调试平衡小车时遇到过这个问题后来把所有传感器地线单独汇总到一个星形接地点数据立刻干净了很多。2.2 I2C上拉电阻与地址冲突GPIO 配置成开漏输出时I2C 需要外部上拉才能产生高电平。如果用的是 STM32 内部上拉虽然寄存器配置里能开但内部上拉阻值偏大在 400kHz 高速模式下波形上升沿会很慢容易出现通信不稳定。稳妥做法是板载上拉不动外部再加一路 4.7kΩ 到 10kΩ 上拉总线速度先用 100kHz确认没问题后再尝试 400kHz。如果总线上还有其他 I2C 设备比如 OLED、EEPROM、温湿度传感器就需要提前规划好地址。HMC5883L 地址固定为 0x1EMPU6050 为 0x68 或 0x69 取决于 AD0 引脚。我把 GY87 和 0x3C 的 OLED 挂在同一条 I2C1 上三个地址互不冲突实测完全没问题。但若你的板子把 MPU6050 的 AD0 默认拉高了地址就会变成 0x69代码里写死 0x68 就会一直 NACK读不到任何数据。焊接的问题也要注意GY87 的引脚间距其实是标准的 2.54mm但有些模块焊盘上会涂一层薄薄的助焊剂插到面包板前最好用酒精擦一下否则接触不良导致 SDA 偶尔拉不低折腾半天排除不了。3. 寄存器配置与单位换算这是很多人翻车的地方STM32 读取 GY87本质上就是通过 I2C 读写芯片内部寄存器。理解几个关键寄存器比抄一百行代码都有用。传感器初始化不到位、量程设错、灵敏度系数算错都会让后续数据完全没法用。3.1 MPU6050的初始化寄存器序列MPU6050 上电后处于睡眠模式必须先唤醒。初始化顺序通常是这样首先写电源管理寄存器 1地址 0x6B把 bit6 清零即解除睡眠。这个寄存器复位值默认是 0x40也就是睡眠模式如果不写这一步后面读出来的全是 0 或者固定值这也是新手最常见的“数据全零”原因。接着写采样率分频寄存器0x19比如要得到 1kHz 采样率写 0x04 表示把内部 1kHz 除以 5 得到 200Hz。再写配置寄存器0x1A把数字低通滤波器设为 0x03对应 44Hz 带宽可以有效滤除机械振动带来的高频噪声。然后是量程配置这个非常关键。加速度计配置寄存器是 0x1C取值 0x00 对应 ±2g0x08 对应 ±4g0x10 对应 ±8g0x18 对应 ±16g。陀螺仪配置寄存器是 0x1B取值 0x00 对应 ±250°/s0x08 对应 ±500°/s0x10 对应 ±1000°/s0x18 对应 ±2000°/s。我调试平衡小车时陀螺仪用的是 ±2000°/s因为小车倾倒瞬间角速度常常很大用 ±250°/s 会直接削顶。功能寄存器地址常用配置值量程灵敏度电源管理10x6B0x00唤醒-采样率分频0x190x04200Hz-数字低通滤波0x1A0x0344Hz-加速度计量程0x1C0x00±2g16384 LSB/g陀螺仪量程0x1B0x18±2000°/s16.4 LSB/(°/s)初始化完成后可以通过读 0x75WHO_AM_I寄存器确认通信正常MPU6050 的返回值应当是 0x68。这不是必须步骤但强烈建议加上能帮你快速区分接线问题还是芯片故障。3.2 HMC5883L的量程配置与读数顺序HMC5883L 的配置相对简单。配置寄存器 A0x00控制采样平均次数和数据输出速率典型值是 0x70表示 8 次平均、15Hz 输出。配置寄存器 B0x01控制增益也就是量程0x20 对应 ±1.3Ga这是最常用且精度较高的档位。模式寄存器0x02写 0x00 表示连续测量模式。这里有个容易忽略的细节HMC5883L 的数据寄存器从 0x03 开始连续 6 个字节依次是 X 高、X 低、Z 高、Z 低、Y 高、Y 低注意顺序是 X、Z、Y不是 X、Y、Z。很多教程直接照搬 MPU6050 的六字节顺序导致 X、Y、Z 轴互相错位。另外HMC5883L 的测量输出是 16 位有符号数每次读取前最好先读状态寄存器0x09的 bit0如果为 1 表示数据已就绪再读取可以避免读到新旧数据混拼的半截结果。增益系数的换算我直接用 ti 手册里的典型值±1.3Ga 档位下每 LSB 对应 1.16mGa有些版本标 1090 LSB/Gauss本质一样。如果你想校准得更精细可以把传感器放水平旋转 360 度记录 X、Y 的最大最小值用(max - min) / 2做零偏补偿这样航向角至少能准确到 ±3 度以内。3.3 把原始ADC值换算成物理量MPU6050 和 HMC5883L 的原始输出都是 16 位有符号补码范围为 -32768 到 32767。加速度计的换算公式是g raw / 16384当量程为 ±2g 时因为满量程 32768 对应 2g。陀螺仪同理deg_per_s raw / 16.4量程 ±2000 时。磁力计的换算公式是gauss raw * 1.16 / 1000或反向使用灵敏度系数。我这里给一个实际计算例子读到的加速度原始值是 8192那么实际加速度为 8192 / 16384 0.5g陀螺仪原始值是 164那么角速度为 164 / 16.4 10°/s。注意陀螺仪输出的是角速度不是角度要得到角度必须积分而积分过程要非常小心连续积分几十秒后零偏会被不断放大。我在代码里给陀螺仪做零偏校准上电静止时连续采样 200 次求平均作为初始零偏之后再减去这个平均值再积分。需要留意的是磁力计原始输出对温度变化存在漂移但短期使用影响不大。如果你做的是航向保持或导航项目建议在代码里定期执行一次性硬磁校准把周围电机磁性物质带来的固定偏置消掉。4. 基于HAL库的读取代码实现说到具体代码STM32 开发环境建议直接用 CubeMX 生成 I2C 基础工程HAL 库封装的HAL_I2C_Mem_Read和HAL_I2C_Mem_Write很适合做传感器寄存器读写。下面是我在实际项目中验证过的实现方案。4.1 用CubeMX生成基础工程在 CubeMX 里选择你的 STM32 型号启用 I2C1默认参数即可如果需要更稳定可以把时钟速度设为 100kHz。同时打开 USART1 的串口中断模式方便通过 printf 打印传感器原始数据。GPIO 部分不用手动配置CubeMX 会自动把 I2C 引脚设为开漏复用模式。生成的工程里main.c的while循环前需要进行传感器初始化。初始化顺序建议是先确认 MPU6050 的 WHO_AM_I再分别写入 0x6B、0x19、0x1A、0x1C、0x1B 寄存器HMC5883L 则写 0x00、0x01、0x02 三个寄存器。如果是在 FreeRTOS 工程里操作I2C 初始化建议放在任务创建前一次性完成不要在多个任务里并发访问同一个 I2C 外设。4.2 读寄存器的底层函数封装#include main.h extern I2C_HandleTypeDef hi2c1; #define MPU6050_ADDR (0x68 1) #define HMC5883L_ADDR (0x1E 1) uint8_t MPU_ReadReg(uint8_t reg, uint8_t *buf, uint16_t len) { return HAL_I2C_Mem_Read(hi2c1, MPU6050_ADDR, reg, I2C_MEMADD_SIZE_8BIT, buf, len, 100); } uint8_t MPU_WriteReg(uint8_t reg, uint8_t val) { return HAL_I2C_Mem_Write(hi2c1, MPU6050_ADDR, reg, I2C_MEMADD_SIZE_8BIT, val, 1, 100); } uint8_t HMC_ReadReg(uint8_t reg, uint8_t *buf, uint16_t len) { return HAL_I2C_Mem_Read(hi2c1, HMC5883L_ADDR, reg, I2C_MEMADD_SIZE_8BIT, buf, len, 100); }注意 HAL 库里HAL_I2C_Mem_Read的内部会在地址参数上自动完成 7 位到 8 位的转换所以传给函数的地址一定要用 7 位地址0x68、0x1E而不是左移后的值。我在代码注释里保留了 1的写法是为了提醒自己这里传的是从机地址实际使用中直接用 0x68 和 0x1E 即可。4.3 主循环读取与数据验证读取流程并不复杂用结构体把三组数据组织起来最直观typedef struct { int16_t ax; int16_t ay; int16_t az; int16_t gx; int16_t gy; int16_t gz; int16_t mx; int16_t my; int16_t mz; } mpu9axis_t;初始化和读取函数可以这样写void GY87_Init(void) { uint8_t id 0; MPU_ReadReg(0x75, id, 1); if (id ! 0x68) { printf(MPU6050 not found\\r\\n); } MPU_WriteReg(0x6B, 0x00); // 唤醒 MPU_WriteReg(0x19, 0x04); // 采样率200Hz MPU_WriteReg(0x1A, 0x03); // 低通44Hz MPU_WriteReg(0x1C, 0x00); // 加速度计±2g MPU_WriteReg(0x1B, 0x18); // 陀螺仪±2000°/s uint8_t cfg[3] {0x70, 0x20, 0x00}; HAL_I2C_Mem_Write(hi2c1, HMC5883L_ADDR, 0x00, I2C_MEMADD_SIZE_8BIT, cfg, 3, 100); } void GY87_ReadRaw(mpu9axis_t *data) { uint8_t buf[14]; MPU_ReadReg(0x3B, buf, 14); // MPU6050: 加速度6字节温度2字节陀螺仪6字节 style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门