ICM20948 九轴驱动避坑指南:磁力计 AK09916 读取全解析

张开发
2026/5/19 8:21:41 15 分钟阅读
ICM20948 九轴驱动避坑指南:磁力计 AK09916 读取全解析
1. 前言与问题背景在开发惯性导航、姿态解算AHRS时ICM20948凭借其出色的功耗表现和集成的 AK09916 磁力计成为了非常受欢迎的 9 轴传感器。但在实际驱动开发中很多人包括我都会遇到一个非常头疼的问题加速度计和陀螺仪的数据读取都很正常但磁力计数据要么全是 0要么读出来一个死数据一直不更新或者是一堆乱码本文将带你一步步梳理 ICM20948 读取九轴数据的正确方式重点解决磁力计 AK09916 无法正常读取的底层时序逻辑问题。2. 为什么磁力计这么难读ICM20948 和 AK09916 其实是封装在同一个芯片里的两个独立 Die。主控 MCU如 STM32/GD32通过 SPI/I2C 和 ICM20948 通信而 ICM20948 内部自带了一个I2C Master主控制器专门负责去和内部的 AK09916 磁力计通信。读取磁力计失败通常有以下三大天坑#问题描述1指令没有下发成功没有正确地将 AK09916 配置为连续测量模式2I2C 仲裁与时序冲突内部 I2C Master 读得太快一直霸占总线导致 AK09916 的 ADC 转换被干扰永远无法准备好数据3被 DRDY 标志卡死这是最容易踩的坑下面会重点讲解3. 正确的初始化流程I2C Master 配置为了让磁力计正常工作需要按顺序完成以下几步开启 ICM20948 内部的 I2C Master并关闭 Bypass 模式降低I2C Master 对磁力计的轮询速度给磁力计喘息的时间配置SLV0连续循环读取磁力计数据使用SLV1对磁力计进行单次配置软复位 → 设置为 100Hz 连续测量模式核心初始化代码如下// -------------------- BANK0: 启用 I2C Master并关闭 Bypass -------------------- ICM20948_SelectBank(ICM20948_BANK_0_E); // 打开 FIFO_EN(bit6) I2C_MST_EN(bit5) // 注意不要开启 I2C_MST_P_NSR确保每次读取有 Stop 位避免时序冲突 GDSPI0WriteReg(ICM20948_USER_CTRL_E, 0x40 | 0x20); delay_1ms(10); // 关闭 Bypass0x0F 寄存器确保内部 Master 接管 I2C 总线 GDSPI0WriteReg(0x0F, 0x00); delay_1ms(10); // -------------------- BANK3: 配置 I2C Master 和 SLV 接口 -------------------- ICM20948_SelectBank(ICM20948_BANK_3_E); GDSPI0WriteReg(I2C_MST_CTRL_E, 0x07); // I2C Master 时钟 345.6kHz // 【关键降频】配置 SLV0 读取延迟让 I2C Master 慢点轮询 GDSPI0WriteReg(0x01, 0x01); // I2C_MST_DELAY_CTRL启用 SLV0_DELAY GDSPI0WriteReg(0x15, 0x09); // I2C_SLV4_CTRL设置采样分频 // 【关键分组】配置 SLV0 连续读取 9 字节ST1 到 ST2 GDSPI0WriteReg(I2C_SLV0_ADDR_E, 0x80 | 0x0C); // 0x0C 为 AK09916 地址0x80 表示读 GDSPI0WriteReg(I2C_SLV0_REG_E, 0x10); // 从 ST10x10开始读 // Enable(bit7) GRP(bit4) Len9 // GRP1 确保这 9 字节作为一个原子事务读取 GDSPI0WriteReg(I2C_SLV0_CTRL_E, 0x80 | 0x10 | 9); // -------------------- 使用 SLV1 下发单次配置软复位 -------------------- GDSPI0WriteReg(I2C_SLV1_ADDR_E, 0x0C); // AK09916 写模式 GDSPI0WriteReg(I2C_SLV1_REG_E, 0x32); // CNTL3 寄存器 GDSPI0WriteReg(I2C_SLV1_DO_E, 0x01); // 触发软复位 GDSPI0WriteReg(I2C_SLV1_CTRL_E, 0x80 | 1); // Enable SLV1len1 delay_1ms(20); GDSPI0WriteReg(I2C_SLV1_CTRL_E, 0x00); // 用完必须关闭 SLV1 delay_1ms(10); // -------------------- 使用 SLV1 下发单次配置100Hz 连续测量 -------------------- GDSPI0WriteReg(I2C_SLV1_REG_E, 0x31); // CNTL2 寄存器 GDSPI0WriteReg(I2C_SLV1_DO_E, 0x08); // 0x08 100Hz 连续测量模式 GDSPI0WriteReg(I2C_SLV1_CTRL_E, 0x80 | 1); // Enable SLV1len1 delay_1ms(20); GDSPI0WriteReg(I2C_SLV1_CTRL_E, 0x00); // 用完关闭4. 磁力计读取千万别被 DRDY 卡死❌ 错误写法新手常见踩坑很多人的代码是这样写的读取 ST1 寄存器判断if (st1 0x01)即 DRDY 置位才去解析磁力计 X/Y/Z 数据。这在 ICM20948 里是绝对的深坑 原理解析由于底层 I2C Master 在后台以极高速度比如 1ms 一次自动轮询 AK09916而主控 MCU 可能以 5Hz200ms的慢速去读取 ICM20948 的缓存EXT_SLV_SENS_DATA。这意味着在你读取的空隙底层已经刷新了几十次数据这就导致 ST1 的 DRDY0x01瞬间被清零取而代之的通常是DOR0x02数据覆盖位。如果你死死卡住st1 0x01就会觉得数据永远没准备好。✅ 正确做法不卡 DRDY只看溢出位// 切换到 BANK0获取外部传感器数据缓存 ICM20948_SelectBank(ICM20948_BANK_0_E); // 直接读取 I2C Master 搬运过来的 9 个字节 uint8_t st1 GDSPI0ReadReg(ICM20948_EXT_SLV_SENS_DATA_00_E); // ST1状态可不判断 uint8_t hxl GDSPI0ReadReg(ICM20948_EXT_SLV_SENS_DATA_01_E); // HXL uint8_t hxh GDSPI0ReadReg(ICM20948_EXT_SLV_SENS_DATA_02_E); // HXH uint8_t hyl GDSPI0ReadReg(ICM20948_EXT_SLV_SENS_DATA_03_E); // HYL uint8_t hyh GDSPI0ReadReg(ICM20948_EXT_SLV_SENS_DATA_04_E); // HYH uint8_t hzl GDSPI0ReadReg(ICM20948_EXT_SLV_SENS_DATA_05_E); // HZL uint8_t hzh GDSPI0ReadReg(ICM20948_EXT_SLV_SENS_DATA_06_E); // HZH (void)GDSPI0ReadReg(ICM20948_EXT_SLV_SENS_DATA_07_E); // TMPS忽略 uint8_t st2 GDSPI0ReadReg(ICM20948_EXT_SLV_SENS_DATA_08_E); // ST2 // ST2 的 bit3HOFL为 0说明磁场未溢出数据有效 if ((st2 0x08) 0) { int16_t mx (int16_t)((hxh 8) | hxl); int16_t my (int16_t)((hyh 8) | hyl); int16_t mz (int16_t)((hzh 8) | hzl); // AK09916 灵敏度固定为 0.15 uT/LSB float MagX_uT mx * 0.15f; float MagY_uT my * 0.15f; float MagZ_uT mz * 0.15f; }5. 如何验证磁力计数据是否正确读出数据后可以按照以下标准进行验证① 静止时微弱跳动地磁场十分微弱加上传感器底噪放在桌面上静止时数据应该在±1 ~ ±3 μT之间微小波动绝对不会像死机一样纹丝不动。② 模长合理通常环境下地磁场总强度X2Y2Z2≈30∼60 μTX2Y2Z2​≈30∼60 μT若桌面附近有铁制品可能飙到 100 μT属正常现象。③ 水平旋转测试Yaw 轴将板子平放于桌面缓慢旋转 360°。你会观察到Z 轴数据基本不变X 轴和 Y 轴会像正弦波一样交替变化出现以上现象说明磁力计彻底调通了✅6. 总结ICM20948 是一款强大的九轴芯片但它的 I2C Master 机制容易让人迷惑。避坑三连 ⚠️✅别开I2C_MST_P_NSR必须保留 Stop 位否则时序混乱✅配置SLV0_CTRL时必须加上GRP标志位0x10保证 9 字节原子读取✅读取数据时别用if (st1 0x01)卡脖子直接看 ST2 的溢出位HOFL即可希望这篇文章能帮你节省几个通宵的调试时间如果对你有帮助欢迎点赞收藏⭐

更多文章