简介:面向嵌入式系统、机器人及智能穿戴领域的研发人员,这份中文版寄存器参考文档围绕MPU-6000与MPU-6050惯性测量单元展开,重点解决底层寄存器配置、I2C通信和传感器数据读取中的理解与落地问题。压缩包内为1个PDF文件,大小约1.14MB,内容按寄存器映射与逐项说明组织,便于按地址检索。文档系统梳理采样率除法器、陀螺仪与加速度计配置、FIFO使能、中断控制、自由落体与移动/零运动检测阈值及持续时间等寄存器,并涉及辅助I2C电源选择、I2C主控与从机控制等接口细节。已有3640人学习,适合具备一定嵌入式硬件基础、熟悉I2C/SPI总线及加速度计、陀螺仪基本概念的工程师,在传感器集成、驱动调试与项目选型时对照查阅,从而更准确地完成初始化配置、数据输出设置与功能验证。
1. 手册版本比接线更容易让人出错
上个月帮人看一块六轴小板,代码一行没改,只换了个批次的芯片,读出来的加速度全是 0xFFFF。查到最后发现,问题既不在焊接,也不在上拉电阻,而在于手里那份中文版 MPU-6000/MPU-6050 寄存器映射与功能详解仍停留在早期修订,PWR_MGMT_1 的复位值、陀螺自检位都与新硅片对不上;初始化里那句「写完 0x6B 直接读数据」在新版上必然读不到数据,因为芯片上电默认就睡在 0x40。这颗 IMU 在嵌入式系统里出现频率太高,飞控、云台、手环、平衡车几乎人手一颗,可它偏偏属于「接上线能通、寄存器写错就不出数据」的器件,I2C 能扫到地址并不代表能出数。这里记录的是我把它从零跑通的一整套环境:总线怎么接、主机侧装什么、寄存器按什么顺序写、出问题先看哪几个字节,适合刚拿到传感器、准备照着手册点灯的人。
2. 环境清单:让一颗六轴传感器输出数据的四样东西
2.1 硬件最小集合与 I2C/SPI 的选型分界
先说清楚要装的是什么。这套环境不是装一个软件包,而是搭一条链路:传感器模块、供电与电平、主机总线、用户态读写工具,缺一环都读不出正确数据。我一般按下面这张清单备料,多一样浪费,少一样卡壳。
| 物件 | 关键参数 | 备注 |
|---|---|---|
| MPU-6050 或 MPU-6000 模块 | VDD 2.375–3.46V | 模块自带 LDO 的可以直接 5V 供电,裸芯片不行 |
| 上拉电阻 | 4.7kΩ 到 VDD(或 VLOGIC) | 模块板载了就不用外加,长排线时把阻值降到 2.2kΩ |
| 主机 | 树莓派、ESP32、STM32 均可 | 只要有一路硬件 I2C 控制器 |
| 杜邦线 | 尽量短 | 超过 20cm 就开始出现偶发 NAK |
两个型号只差两处,选型时经常被忽略。MPU-6050 只有 I2C,最高 400kHz,并且多一个 VLOGIC 引脚,用来单独定义接口电平;MPU-6000 除 I2C 外还支持最高 20MHz 的 SPI,只有一个 VDD 引脚,逻辑电平和模拟供电共用。所以主机是 1.8V 逻辑的 SoC,或者采样率要求高、I2C 带宽吃紧,就直接上 MPU-6000 走 SPI;只是做个姿态 Demo,6050 走 I2C 完全够。反过来,拿着一颗 6050 去接 SPI 四根线,怎么调都不会通,因为芯片根本没有这个接口。
接线只讲最容易错的两根。AD0 决定从机地址,接地是 0x68,接 VLOGIC 是 0x69,浮空是玄学,必须明确拉到一个电平。INT 引脚第一轮验证可以不接,但如果后面要用数据就绪中断或 FIFO 溢出中断,就得接到主机的 GPIO 上,并且注意 INT_PIN_CFG(0x37)默认是推挽、高电平有效、电平锁存,和很多教程里写的不一样。
2.2 主机侧工具链:i2c-tools 与 smbus2 的版本取舍
Linux 单板机是我最常用的验证平台,原因是命令行工具能让你在写代码之前就把「通不通」定死。装两样东西就够:i2c-tools 用来在命令行层面确认地址和单寄存器值,python3-smbus2 用来写初始化序列和批量读。
# 树莓派系:先启用 I2C-1 总线 sudo raspi-config nonint do_i2c 0 # 参数 0 表示启用,1 表示关闭 sudo apt-get update sudo apt-get install -y i2c-tools python3-smbus2 # 确认设备节点和权限:没有 /dev/i2c-1 就是总线没开 ls -l /dev/i2c-1 groups | grep -E 'i2c|gpio' # 普通用户要能访问,得在 i2c 组里raspi-config nonint do_i2c 0是非交互写法,省得在菜单里翻。设备节点存在但普通用户访问报 Permission denied,是没加组,sudo usermod -aG i2c $USER之后重新登录一次才生效,这条踩过不止一次。i2c-tools 的版本差异不大,4.x 都够用;smbus2 建议 0.4 以上,老版本在read_i2c_block_data传入长度超过 32 字节时行为不一致,而每次读 14 字节加速度、14 字节陀螺仪就正好在边界附近。
ESP32 那边更简单,MicroPython 自带 machine.I2C,Arduino 用 Wire,不需要额外安装,但要注意 Wire 默认时钟是 100kHz,要 400kHz 得显式调setClock(400000),否则高采样率下读取会拖慢整个循环。
2.3 手册怎么用:寄存器映射表里三列必须对着看
这份中文版寄存器映射与功能详解的价值,在于它把「地址」和「位域」放在同一行里。看表的时候我会同时盯三列:最左的十六进制地址(比如 0x1B)、寄存器名(GYRO_CONFIG)、以及后面八列的位定义。只看地址不看位域,是最常见的误用——比如 0x1C 里 bit7 到 bit5 是三个轴的自检使能,bit4 到 bit3 才是量程选择,直接写 0x03 和写 0x18 得到的是完全不同的结果。
手册开头还有两条约定必须记住,否则读表会读错。第一,以_H和_L结尾的寄存器名,分别存放 16 位量的高字节和低字节,比如 ACCEL_XOUT_H(0x3B)存的是 ACCEL_XOUT[15:8]。第二,寄存器名用大写,寄存器里的值用斜体大写,这两个别混。另外手册明确写了绝大多数寄存器复位值为 0x00,但有两个例外:寄存器 107(PWR_MGMT_1)复位值是 0x40,寄存器 117(WHO_AM_I)复位值是 0x68——这两个例外正是第 3 章后面所有排错的起点。
3. 打通 I2C:从扫地址到 0x75 确认芯片身份
3.1 扫地址之前先确认总线是干净的
接好线、上电,第一件事不是写代码,是扫地址。
# -y 跳过交互确认,1 是总线编号(树莓派新系统上是 1) i2cdetect -y 1正常输出里应该在 0x68 或 0x69 位置看到一个68或69。如果整张表全是--,按这个顺序查:供电有没有到模块(量 VDD 和 GND 之间是不是 3.3V)、SDA/SCL 有没有接反、上拉有没有装。如果位置上显示的是UU,说明这个地址已被内核驱动占用——树莓派默认会加载 inv-mpu6050 这类 IIO 驱动,一旦加载就把地址占住,用户态再读就全是错的。这种时候要么卸载驱动,要么把它拉进黑名单,具体做法放在第 5 章。
如果扫到的是 0x68 之外的地址,比如 0x69,不用怀疑,是 AD0 被拉高了。地址这件事必须和代码里的常量严格对应,改代码比改硬件省事。
3.2 读 WHO_AM_I(0x75) 确认真的是这颗芯片
地址扫到只是说明「某个设备在线」,是不是 MPU-60X0 还得问它自己。寄存器 117(0x75)就是干这个的,手册里写得很清楚:WHO_AM_I[6:1],复位值 0x68。
# 直接读一个字节,正常返回 0x68 i2cget -y 1 0x68 0x75 # 顺手确认 PWR_MGMT_1 当前值,应该是 0x40(芯片还在睡) i2cget -y 1 0x68 0x6bi2cget -y 1 0x68 0x75这条命令的含义是:在 1 号总线上,对从机 0x68,读寄存器 0x75 的一个字节。返回0x68就说明芯片型号和地址都对上了。如果返回0x00或者0xFF,先别怀疑芯片坏,多数是上拉缺失或者总线被别的设备拉住。如果返回0x98、0x70这类值,那是别的兼容型号,位定义不完全一样,不能照这份手册全盘套用。
换成 Python 写,逻辑一样,但更适合后面拼接初始化序列:
from smbus2 import SMBus BUS, ADDR = 1, 0x68 REG_WHO_AM_I = 0x75 REG_PWR_MGMT_1 = 0x6B with SMBus(BUS) as bus: who = bus.read_byte_data(ADDR, REG_WHO_AM_I) pwr = bus.read_byte_data(ADDR, REG_PWR_MGMT_1) print(f"WHO_AM_I=0x{who:02X} PWR_MGMT_1=0x{pwr:02X}")read_byte_data(addr, reg)是「写寄存器地址 → 重复起始 → 读一字节」的封装,等价于 i2cget。跑出来WHO_AM_I=0x68 PWR_MGMT_1=0x40,就说明链路完全通了,而且芯片确实还在睡眠模式,接下来必须是唤醒,不能直接读数据。
3.3 唤醒序列:0x40 复位值导致的第一步失败
手册里那句「设备上电后将进入睡眠模式」是整份文档里最容易被跳过的一句话,而 PWR_MGMT_1 的复位值 0x40 就是它的实现——bit6 的 SLEEP 位默认置 1,所有传感器采样都停了,数据寄存器保持不动。这时候你去读 0x3B,读到的是一堆不变化的值,很容易误判成「芯片没工作」。
正确的唤醒写法是两步,先软复位再选时钟源:
import time from smbus2 import SMBus BUS, ADDR = 1, 0x68 PWR_MGMT_1 = 0x6B with SMBus(BUS) as bus: # 1) bit7 DEVICE_RESET=1,把所有寄存器恢复到默认值 bus.write_byte_data(ADDR, PWR_MGMT_1, 0x80) time.sleep(0.1) # 复位需要时间,手册建议 100ms # 2) 退出睡眠并选时钟:CLKSEL=1 用 X 轴陀螺 PLL,比内部 8MHz 稳 bus.write_byte_data(ADDR, PWR_MGMT_1, 0x01) time.sleep(0.05) # 等 PLL 锁定 print(hex(bus.read_byte_data(ADDR, PWR_MGMT_1))) # 期望 0x01这里每个参数都有取舍。写 0x80 而不是 0x00,是因为你无法确定芯片之前被谁写过什么,软复位是唯一能把状态清干净的手段;手册修订记录里专门提过 107 寄存器复位值在 3.1 版被更正,说明这一位坑过不少人。CLKSEL 选 1(X 轴陀螺 PLL)是常见做法,内部 8MHz 振荡器温漂大,做长时间积分会明显漂;选 4 或 5 需要外部提供 32.768kHz 或 19.2MHz 参考,没有就别选,选了直接不出数。写完之后一定要回读确认,因为 I2C 写是没有返回值的,写完就信,是另一个常见翻车点。
唤醒之后再读一次 0x3B 附近的寄存器,值应该开始变化了。如果还是纹丝不动,回到 0x6B 看 bit6 是否真的清零。
4. 寄存器初始化:采样率、DLPF、量程三个参数
4.1 SMPLRT_DIV(0x19):8kHz 和 1kHz 的分岔
采样率寄存器是 25 号,地址 0x19,8 位无符号,公式手册里给得很直接:采样率 = 陀螺仪输出率 / (1 + SMPLRT_DIV)。关键在于分母里那个「陀螺仪输出率」不是固定值,它取决于 DLPF 有没有开:DLPF_CFG 为 0 或 7 时是 8kHz,其余配置下是 1kHz。
# 想要 100Hz 输出,且 DLPF 已启用(输出率 1kHz) # 1kHz / (1 + 9) = 100Hz bus.write_byte_data(ADDR, 0x19, 9)写 9 得到 100Hz,写 4 得到 200Hz,写 0 就是 1kHz 全速。这里有个细节值得单独说:手册明确写了加速度计输出速率固定 1kHz,如果采样率配到大于 1kHz(也就是 DLPF 关闭、SMPLRT_DIV 写 0 到 6 之间),同一个加速度样本会被重复输出到 FIFO 和传感器寄存器里多次。数据看起来「有」但时间戳对不上,做积分算位移时会凭空多出位移量。我一般把采样率压在 200Hz 以内,配合 44Hz 或 21Hz 的 DLPF,噪声和延迟比较平衡。
4.2 CONFIG(0x1A):DLPF_CFG 决定噪声与延迟
26 号寄存器地址 0x1A,高三位是 EXT_SYNC_SET(外部帧同步),低三位是 DLPF_CFG。大部分场景 EXT_SYNC_SET 写 0,也就是不用 FSYNC 引脚同步;只有多传感器严格对齐采样时刻时才需要接 FSYNC 并配这一位。
DLPF_CFG 的取舍看这张表,我把它抄在便签上贴过很久:
| DLPF_CFG | 加速度带宽 | 陀螺带宽 | 延迟 | 适用场景 |
|---|---|---|---|---|
| 0 | 260Hz | 256Hz | 0ms | 只要速度不要稳定,短时冲击检测 |
| 1 | 184Hz | 188Hz | 2.0ms | 高频振动分析 |
| 2 | 94Hz | 98Hz | 3.0ms | 通用姿态解算 |
| 3 | 44Hz | 42Hz | 4.9ms | 我的默认值 |
| 4 | 21Hz | 20Hz | 8.5ms | 慢速倾角、可穿戴 |
| 5 | 10Hz | 10Hz | 13.8ms | 静态倾角 |
| 6 | 5Hz | 5Hz | 19.0ms | 极慢变化,延迟明显 |
| 7 | 保留 | 保留 | — | 等价于关闭 DLPF,陀螺回到 8kHz |
# DLPF_CFG = 3:陀螺 42Hz、加速度 44Hz、延迟 4.9ms bus.write_byte_data(ADDR, 0x1A, 0x03)写 0x03 意味着同时关掉外部同步、打开 DLPF。要不要贪低带宽?做到 21Hz 以下时,用手晃一下板子,输出会明显「追不上」,做平衡车这类闭环控制会引入相位滞后,反而不稳。反过来 DLPF 关掉(写 7)虽然延迟为零,但陀螺输出率跳到 8kHz,采样率公式里的分母跟着变,前面算好的 SMPLRT_DIV 就全错了——这两个寄存器是绑定的,改一个必须回头检查另一个。
4.3 GYRO_CONFIG(0x1B) 与 ACCEL_CONFIG(0x1C):量程、自检位与高通滤波
量程决定后面换算时除多少,配错了数值不会报错,只会静静地差一个倍数。
| 寄存器 | 位域 | 取值 | 量程 | 灵敏度 |
|---|---|---|---|---|
| GYRO_CONFIG 0x1B | FS_SEL[4:3] | 0 | ±250 °/s | 131 LSB/(°/s) |
| 1 | ±500 °/s | 65.5 | ||
| 2 | ±1000 °/s | 32.8 | ||
| 3 | ±2000 °/s | 16.4 | ||
| ACCEL_CONFIG 0x1C | AFS_SEL[4:3] | 0 | ±2g | 16384 LSB/g |
| 1 | ±4g | 8192 | ||
| 2 | ±8g | 4096 | ||
| 3 | ±16g | 2048 |
# 陀螺 ±500 dps(FS_SEL=1),加速度 ±4g(AFS_SEL=1) bus.write_byte_data(ADDR, 0x1B, 0x08) # 0b0000_1000,bit4:3 = 01 bus.write_byte_data(ADDR, 0x1C, 0x08) # 0b0000_1000,同理0x08 这个值是把 1 左移 3 位得到的,别写成 0x03——那是把自检位点亮了。0x1B 的 bit7 到 bit5 是三个轴的陀螺自检使能,0x1C 的 bit7 到 bit5 是加速度自检使能,手册 3.2 修订专门补了这部分说明和自检步骤。自检的正确用法是:先读一组静止基线,再置位自检、读一组,比较差值是否落在规格范围内,读完必须清零。忘了清零的后果是三个轴一直带着内部激励信号,输出永远偏移。
0x1C 的低三位是 ACCEL_HPF,加速度高通滤波器,做倾角测量时必须写 0(复位值就是 0),否则重力直流分量会被滤掉,倾角直接算废。这一位在中文资料里经常被漏讲。
4.4 数据区 0x3B 到 0x48:一次读完,别单字节挨个读
数据寄存器是连续的:0x3B 到 0x40 是加速度三轴,0x41 和 0x42 是温度,0x43 到 0x48 是陀螺三轴,一共 14 个字节。手册里以_H/_L成对出现,高字节在前,也就是大端。
import time from smbus2 import SMBus BUS, ADDR = 1, 0x68 DATA_START = 0x3B def s16(v): """把 0..65535 的无符号值还原成有符号 16 位""" return v - 65536 if v & 0x8000 else v def read_all(bus): d = bus.read_i2c_block_data(ADDR, DATA_START, 14) ax = s16((d[0] << 8) | d[1]) ay = s16((d[2] << 8) | d[3]) az = s16((d[4] << 8) | d[5]) temp_raw = s16((d[6] << 8) | d[7]) gx = s16((d[8] << 8) | d[9]) gy = s16((d[10] << 8) | d[11]) gz = s16((d[12] << 8) | d[13]) return ax, ay, az, temp_raw, gx, gy, gz with SMBus(BUS) as bus: for _ in range(3): ax, ay, az, t, gx, gy, gz = read_all(bus) print(f"acc=({ax/16384:.3f},{ay/16384:.3f},{az/16384:.3f})g " f"gyro=({gx/65.5:.2f},{gy/65.5:.2f},{gz/65.5:.2f})dps " f"temp={t/340.0 + 36.53:.2f}C") time.sleep(0.1)read_i2c_block_data一次把 14 字节全部拿回来,这是关键。如果改成对 0x3B、0x3C 逐个读,两次读之间芯片可能已经更新了一轮采样,拼出来的高低字节属于不同时刻,数值会突然跳几百个 LSB,看起来像接触不良。做高频采样时这种错位尤其明显,属于典型的「代码能跑、数据不能信」。
换算里三个除数分别对应前面配的量程:±2g 用 16384,±4g 用 8192;陀螺 ±500dps 用 65.5;温度按TEMP_OUT/340 + 36.53算摄氏度。这三个系数必须和你实际写进 0x1B、0x1C 的值一致,改量程不改系数,是精度出问题最常见的原因。
5. 避坑记录:五个导致初始化失败的寄存器细节
5.1 总线层面的两种假故障
现象:i2cdetect里 0x68 位置显示UU,或者 Python 读取抛Remote I/O error,但线序和供电反复查过都没问题。原因:内核已经把 inv-mpu6050 这类 IIO 驱动绑定到该地址上,用户态再访问就是两个驱动抢同一颗芯片。解决:把模块拉黑,重启后重新扫。
# 屏蔽内核自带的 IMU 驱动,避免和用户态抢占同一个 I2C 地址 echo "blacklist inv-mpu6050" | sudo tee /etc/modprobe.d/blacklist-imu.conf sudo modprobe -r inv-mpu6050 2>/dev/null || true sudo reboot重启后再跑一次i2cdetect -y 1,UU应该变成正常数字。这一步不做,后面所有寄存器操作都白费。
现象:地址扫得到,读 WHO_AM_I 正常返回 0x68,但一读加速度就是0xFFFF或全零。原因:杜邦线过长加上只有一组 4.7k 上拉,400kHz 下边沿爬升不够,读到的是 NAK 后的填充值。解决:把时钟降到 100kHz 先验证,能通再往上调;同时把上拉降到 2.2k 或者缩短线长。总线速率在 i2c-tools 里可以临时设:sudo i2cdetect -y -r 1走的是低速路径,能扫到说明就是时序余量不够。
5.2 配置层面的三个错误组合
现象:传感器已经唤醒,读 0x3B 数据一直是同一个值不变化。原因:唤醒时只清了 SLEEP 位,没做软复位,芯片还带着上一轮遗留的配置,甚至可能处在循环采样模式。解决:按 3.3 的两步走,先写 0x80 复位,等 100ms,再写 0x01。写完回读 0x6B 确认是 0x01,而不是 0x41。
现象:改了 SMPLRT_DIV,实测输出速率却完全对不上,设 100Hz 出来像 800Hz。原因:CONFIG 里 DLPF_CFG 写的是 0 或 7,陀螺输出率是 8kHz 而不是 1kHz,公式分母变了。解决:先定 DLPF 再算分频,两个寄存器一起改。要 1kHz 以上采样率就必须接受 DLPF 关闭带来的噪声,要低噪声就别指望高速率。
现象:接着外部磁力计读不到数据,或者主控 I2C 直接卡住。原因:想用旁路模式直接访问辅助总线上的器件,却忘了 INT_PIN_CFG(0x37) 的 bit1 没置位;或者同时把 USER_CTRL(0x6A) 的 I2C_MST_EN 打开了,主控模式和旁路模式互相打架。解决:旁路模式下写0x37 = 0x02(I2C_BYPASS_EN),并且确保 0x6A 的 bit5 为 0;如果要用内部主控去轮询外部传感器,就别开旁路,改配 0x25 到 0x27 的 SLV0 三件套。这两种方式只能选一种,混着配必然读不出来。
5.3 换算与量程的错位
现象:静止放着,Z 轴读数在 0.25 附近而不是 1.0。原因:量程配成了 ±8g(AFS_SEL=2),换算却仍用 16384 除。解决:把量程和除数做成一个常量对,代码里只允许从同一处配置生成,别在两处各写一遍。我通常写一个SENS = {0: 16384, 1: 8192, 2: 4096, 3: 2048}的字典,量程编号即索引。
还有一条容易被当成玄学:读出来的陀螺零偏每次上电都不一样,几十 LSB 的差异很正常,那是器件本身的零点温漂,不是配置错。要消除只能做上电静止校准,采几百个样本求均值当偏移量存起来,这部分没有捷径。
6. 装完怎么验证:自检位加静止读数的一轮完整流程
环境搭完,最后一步不是「能读出数」,而是「能证明读出来的数是对的」。我固定跑两轮验证,第一轮查芯片内部通路,第二轮查物理量是否合理。
第一轮用自检位。手册 3.2 修订补充了陀螺和加速度的自检步骤,思路一致:记录静止时的原始值,把 0x1B 或 0x1C 的高三位置 1,再读一次,看变化量是否落在规格范围内,读完立刻清零。
import time from smbus2 import SMBus BUS, ADDR = 1, 0x68 def raw_gyro_x(bus): d = bus.read_i2c_block_data(ADDR, 0x43, 2) v = (d[0] << 8) | d[1] return v - 65536 if v & 0x8000 else v with SMBus(BUS) as bus: base = sum(raw_gyro_x(bus) for _ in range(20)) / 20.0 # 静止基线 bus.write_byte_data(ADDR, 0x1B, 0x80) # bit7 = XG_ST,点亮 X 轴自检 time.sleep(0.05) test = sum(raw_gyro_x(bus) for _ in range(20)) / 20.0 bus.write_byte_data(ADDR, 0x1B, 0x00) # 必须清零,否则零偏常驻 print(f"baseline={base:.0f} selftest={test:.0f} delta={test-base:.0f}")delta落在几十到几百 LSB 之间属于正常激励响应,接近 0 说明自检位没真正写进去(回读 0x1B 确认),数值爆到上万说明量程配小了、输出饱和。这一步能排除掉「芯片是好的但寄存器没生效」这类最难查的问题。
第二轮做物理量校验,把板子平放在桌面上静止十几秒,取均值看三个方向。
| 观测量 | 期望值 | 明显偏离时先查 |
|---|---|---|
| ACCEL_Z | 约 +1.0g(正放),-1.0g(反放) | AFS_SEL 与换算系数是否一致 |
| ACCEL_X / Y | 接近 0,几百毫g 以内 | 板子是否真的水平、有没有零偏 |
| GYRO 三轴 | 接近 0,几十 LSB 以内 | 是否做过上电静止校准 |
| TEMP_OUT | 接近室温,误差几度内 | 换算公式是否用 340 和 36.53 |
# 静止 2 秒取均值,一次性看三个物理量是否合理 import time from smbus2 import SMBus BUS, ADDR = 1, 0x68 N = 200 acc = [0, 0, 0]; gyr = [0, 0, 0]; tsum = 0 with SMBus(BUS) as bus: for _ in range(N): d = bus.read_i2c_block_data(ADDR, 0x3B, 14) s = lambda i: ((d[i] << 8) | d[i+1]) - (65536 if d[i] & 0x80 else 0) acc[0] += s(0); acc[1] += s(2); acc[2] += s(4) tsum += s(6) gyr[0] += s(8); gyr[1] += s(10); gyr[2] += s(12) time.sleep(0.01) print("acc(g) ", [round(v / N / 16384, 3) for v in acc]) print("gyro(dps)", [round(v / N / 131.0, 2) for v in gyr]) print("temp(C) ", round(tsum / N / 340.0 + 36.53, 2))这段代码里s(i)是就地定义的有符号转换,d[i] & 0x80判断高字节符号位,比先拼 16 位再判断更省一步。除数是按 ±2g 和 ±250dps 写死的,如果你改过量程记得同步改。跑完这三个打印,加速度 Z 接近 1、陀螺接近 0、温度接近室温,这条链路就算真正跑通了,后面接姿态解算、接 FIFO、接中断都有底。
我踩过最冤的一次,是自检位点亮后忘了清零,接着跑了半天姿态融合,越跑越偏,还以为是滤波参数的问题,翻了两天才回到 0x1B 那个字节。从那以后我每次改完量程或自检,都强制走一遍「回读寄存器 → 静止取均值 → 对照期望表」这套动作,宁可多花两分钟,也不要在数据里找幽灵。希望这套流程能帮你少走一遍弯路。
本文还有配套的精品资源,点击获取