ICM20948 设备树完整指南
目录
方案概述
将ICM20948传感器信息添加到Linux设备树(Device Tree)中,使其成为系统硬件描述的一部分。
方案优势
| 优势 | 说明 |
|---|---|
| ✅ 系统集成 | 设备在系统启动时自动识别 |
| ✅ 标准化 | 符合Linux设备模型规范 |
| ✅ 内核驱动 | 可以使用内核提供的IIO驱动 |
| ✅ 自动配置 | 无需手动指定I2C设备路径 |
| ✅ 电源管理 | 支持系统电源管理功能 |
方案B的劣势
| 劣势 | 说明 |
|---|---|
| ⚠️ 复杂度高 | 需要了解设备树和内核编译 |
| ⚠️ 编译耗时 | 需要重新编译内核/设备树 |
| ⚠️ 需要烧录 | 需要更新开发板上的设备树 |
| ⚠️ 风险较高 | 错误配置可能导致系统无法启动 |
设备树基础知识
什么是设备树(Device Tree)?
设备树是一种描述硬件配置的数据结构,用于告诉Linux内核系统中有哪些硬件设备以及如何配置它们。
设备树文件类型
| 文件类型 | 扩展名 | 说明 |
|---|---|---|
| 设备树源文件 | .dts |
人类可读的文本格式 |
| 设备树包含文件 | .dtsi |
可被其他dts文件包含的公共部分 |
| 设备树二进制文件 | .dtb |
编译后的二进制格式,内核使用 |
设备树节点结构
dts
node_name@address {
compatible = "manufacturer,device-model";
reg = <address>;
property1 = <value>;
property2 = "string-value";
status = "okay";
};
ICM20948 相关的设备树属性
| 属性 | 说明 | 示例 |
|---|---|---|
compatible |
设备兼容性字符串 | "invensense,icm20948" |
reg |
I2C设备地址 | <0x68> 或 <0x69>(默认0x68) |
interrupt-parent |
中断控制器 | <&gpio0> |
interrupts |
中断配置 | <RK_PA0 IRQ_TYPE_EDGE_RISING> |
status |
设备状态 | "okay" 或 "disabled" |
准备工作
1. 备份重要文件
bash
# 在SDK目录下备份设备树文件
cd ~/atk_dlrv1126b_linux6.1_sdk/kernel
cp arch/arm64/boot/dts/rockchip/rv1126b-alientek.dtsi \
arch/arm64/boot/dts/rockchip/rv1126b-alientek.dtsi.backup
2. 确认硬件连接信息
在开始之前,确认以下信息:
bash
# 在开发板上执行
# 1. 确认ICM20948连接的I2C总线
i2cdetect -y 2
# 2. 确认I2C地址(应该是0x68或0x69)
# 从i2cdetect输出中查看
# 3. 确认是否有中断引脚连接(可选)
# 查看硬件原理图
硬件信息:
- I2C总线:
i2c2 - I2C地址:
0x68 - 中断引脚:(如果没有需求,可以不配置)
3. 设置编译环境
bash
# 进入SDK内核目录
cd ~/atk_dlrv1126b_linux6.1_sdk/kernel
# 加载交叉编译环境
cross_comp
详细实施步骤
步骤1:修改设备树文件
1.1 打开设备树文件
bash
cd ~/atk_dlrv1126b_linux6.1_sdk/kernel
vi arch/arm64/boot/dts/rockchip/rv1126b-alientek.dtsi
1.2 找到i2c2节点
在文件中找到 &i2c2 节点:
dts
&i2c2 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c2m0_pins>;
at8563: rtc@51 {
compatible = "nxp,pcf8563";
reg = <0x51>;
pinctrl-names = "default";
pinctrl-0 = <&at8563_int>;
interrupt-parent = <&gpio0>;
interrupts = <RK_PB1 IRQ_TYPE_LEVEL_LOW>;
#clock-cells = <0>;
};
};
1.3 添加ICM20948节点
在 at8563 节点后面添加ICM20948节点:
方案1:基本配置(无中断)
dts
&i2c2 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c2m0_pins>;
at8563: rtc@51 {
compatible = "nxp,pcf8563";
reg = <0x51>;
pinctrl-names = "default";
pinctrl-0 = <&at8563_int>;
interrupt-parent = <&gpio0>;
interrupts = <RK_PB1 IRQ_TYPE_LEVEL_LOW>;
#clock-cells = <0>;
};
icm20948: imu@68 {
compatible = "invensense,icm20948";
reg = <0x68>;
status = "okay";
};
};
方案2:完整配置(带中断)
如果您的硬件连接了ICM20948的INT引脚到GPIO,可以使用完整配置:
dts
&i2c2 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c2m0_pins>;
at8563: rtc@51 {
compatible = "nxp,pcf8563";
reg = <0x51>;
pinctrl-names = "default";
pinctrl-0 = <&at8563_int>;
interrupt-parent = <&gpio0>;
interrupts = <RK_PB1 IRQ_TYPE_LEVEL_LOW>;
#clock-cells = <0>;
};
icm20948: imu@68 {
compatible = "invensense,icm20948";
reg = <0x68>;
interrupt-parent = <&gpio0>;
interrupts = <RK_PA0 IRQ_TYPE_EDGE_RISING>;
status = "okay";
};
};
注意:
RK_PA0需要根据实际连接的GPIO引脚修改- 如果没有连接中断引脚,使用方案1即可
1.4 保存文件
bash
# 在vi中保存并退出
:wq
步骤2:编译设备树
2.1 编译内核
bash
# 编译kernel
cd ~/atk_dlrv1126b_linux6.1_sdk
./build.sh kernel
# 编译kernel后的输出文件为boot.img
2.2 检查编译结果
bash
# 查看生成的img文件
ls -la ./rockdev
# 找到生成的img文件
/home/alientek/atk_dlrv1126b_linux6.1_sdk/output/firmware/boot.img
步骤3:部署设备树到开发板
-
将开发板连接至虚拟机,可正常
adb shell。 -
在[SDK]/rockdev文件夹下查看输出软链接文件:
alientek@alientek:~/atk_dlrv1126b_linux6.1_sdk$ ls ./rockdev boot.img MiniLoaderAll.bin misc.img parameter.txt recovery.img rootfs.img uboot.img update.img userdata.img -
adb shell登录开发板并执行reboot loader重启进入到 Loader 模式。 -
设备名称为fuzhou rockchip usb download gadget,使其连接到虚拟机。
注意到 Loader 的模式下是不能用 adb shell 登录开发版的,同时串口也失效了。
-
在 SDK 路径下执行指令:sudo ./rkflash.sh uboot。【更换为对应的映像文件】
-
等待烧写完毕,烧写完毕后,拨动开发板上的电源开关重启开发板。
步骤4:验证设备树加载
4.1 检查设备树节点
bash
# 重启后,在开发板上执行
# 1. 查看设备树中的ICM20948节点
ls /sys/firmware/devicetree/base/i2c@ff410000/imu@68/
# 2. 查看compatible属性
cat /sys/firmware/devicetree/base/i2c@ff410000/imu@68/compatible
# 应该输出:invensense,icm20948
# 3. 查看reg属性
cat /sys/firmware/devicetree/base/i2c@ff410000/imu@68/reg | hexdump -C
# 应该显示:00 00 00 68(即地址0x68)
4.2 检查内核日志
bash
# 查看内核启动日志中关于ICM20948的信息
dmesg | grep -i icm
dmesg | grep -i imu
dmesg | grep -i "i2c.*68"
可能的输出:
- 如果有内核驱动:会看到驱动加载信息
- 如果没有内核驱动:可能看不到相关信息(这是正常的,Linux不支持该驱动)
验证与测试
方法1:使用用户空间程序(方案A)
即使添加了设备树节点,仍然可以使用之前的用户空间程序:
bash
cd /root/icm
LD_LIBRARY_PATH=/root/icm ./simple_gyro_test
应该仍然正常工作!
方法2:检查IIO子系统(如果有内核驱动)
bash
# 查看IIO设备
ls /sys/bus/iio/devices/
# 如果ICM20948有内核驱动,会看到类似:
# iio:device0
# 查看设备属性
cat /sys/bus/iio/devices/iio:device0/name
# 应该输出:icm20948
# 读取陀螺仪数据
cat /sys/bus/iio/devices/iio:device0/in_anglvel_x_raw
cat /sys/bus/iio/devices/iio:device0/in_anglvel_y_raw
cat /sys/bus/iio/devices/iio:device0/in_anglvel_z_raw
注意: Linux 6.1内核可能没有ICM20948的IIO驱动,这种情况下不会看到IIO设备。
方法3:使用i2cdetect验证
bash
# 检查I2C设备是否仍然可见
i2cdetect -y 2
预期结果:
- 地址0x68处应该显示
68或UU UU表示设备被内核驱动占用(如果有驱动)68表示设备存在但未被驱动占用
问题排查
问题1:编译设备树失败
错误信息:
Error: xxx.dtsi:123: syntax error
解决方法:
-
检查设备树语法,特别是:
- 大括号是否匹配
- 分号是否缺失
- 属性值格式是否正确
-
使用dtc工具检查语法:
bash
dtc -I dts -O dtb arch/arm64/boot/dts/rockchip/rv1126b-alientek.dtsi
问题2:系统无法启动
症状: 替换dtb后系统无法启动或卡在启动画面
解决方法:
- 恢复备份的dtb文件:
bash
# 通过串口或恢复模式进入系统
cp /boot/rv1126b-alientek.dtb.backup /boot/rv1126b-alientek.dtb
sync
reboot
-
检查设备树配置是否有错误
-
从简单配置开始,逐步添加属性
问题3:设备树节点不存在
症状: /sys/firmware/devicetree/base/ 下找不到ICM20948节点
可能原因:
- dtb文件没有正确替换
- 设备树编译时没有包含修改
- 节点路径不正确
解决方法:
bash
# 1. 确认dtb文件时间戳
ls -l /boot/rv1126b-alientek.dtb
# 2. 反编译当前使用的dtb查看内容
dtc -I dtb -O dts /boot/rv1126b-alientek.dtb > /tmp/current.dts
grep -A 10 icm20948 /tmp/current.dts
# 3. 如果没有找到,说明dtb没有正确更新
问题4:用户空间程序无法访问设备
症状: 添加设备树后,simple_gyro_test 报错 "Failed to detect ICM-20948"
可能原因:
- 设备被内核驱动占用(i2cdetect显示UU)
解决方法:
bash
# 1. 检查是否有驱动占用
i2cdetect -y 2
# 2. 如果显示UU,卸载内核驱动
lsmod | grep icm
rmmod icm20948 # 如果有的话
# 3. 或者在设备树中禁用该节点
# 修改 status = "disabled";
问题5:找不到IIO设备
症状: /sys/bus/iio/devices/ 目录为空
原因: Linux 6.1内核可能没有编译ICM20948的IIO驱动
解决方法:
- 检查内核配置:
bash
cd ~/atk_dlrv1126b_linux6.1_sdk/kernel
grep ICM20948 .config
- 如果没有驱动,有两个选择:
- 继续使用用户空间方案(方案A)
- 编译并加载ICM20948内核驱动(高级)
方案对比
方案A vs 方案B 详细对比
| 对比项 | 方案A(用户空间) | 方案B(设备树) |
|---|---|---|
| 实施难度 | ⭐ 简单 | ⭐⭐⭐⭐ 复杂 |
| 所需时间 | 10分钟 | 1-2小时 |
| 需要重启 | ❌ 不需要 | ✅ 需要 |
| 风险等级 | 低 | 中等 |
| 系统集成 | 手动 | 自动 |
| 灵活性 | 高 | 中等 |
| 适用场景 | 快速原型、测试 | 产品化、长期使用 |
| 维护成本 | 低 | 中等 |
何时使用方案A?
✅ 推荐使用方案A的情况:
- 快速验证硬件功能
- 原型开发阶段
- 不想修改系统配置
- 需要灵活调整参数
- 只需要基本的数据读取功能
何时使用方案B?
✅ 推荐使用方案B的情况:
- 产品化阶段
- 需要系统自动识别设备
- 想使用内核IIO驱动
- 需要电源管理功能
- 长期稳定运行
混合方案
您可以同时使用两种方案:
- 添加设备树节点(方案B)
- 在设备树中设置
status = "disabled" - 继续使用用户空间程序(方案A)
这样既保持了系统的规范性,又保留了灵活性。
高级主题
1. 添加内核驱动支持
如果您想使用内核IIO驱动,需要:
bash
# 1. 检查内核配置
cd ~/atk_dlrv1126b_linux6.1_sdk/kernel
make menuconfig
# 2. 启用IIO子系统和ICM20948驱动
# Device Drivers -->
# Industrial I/O support -->
# Inertial measurement units -->
# <M> Invensense ICM20948 I2C driver
# 3. 编译内核模块
make modules
# 4. 安装模块到开发板
make modules_install INSTALL_MOD_PATH=/path/to/rootfs
2. 自定义设备树属性
您可以添加自定义属性来配置传感器:
dts
icm20948: imu@68 {
compatible = "invensense,icm20948";
reg = <0x68>;
/* 自定义属性 */
invensense,gyro-range = <250>; /* 陀螺仪量程:250 dps */
invensense,accel-range = <2>; /* 加速度计量程:2g */
invensense,sample-rate = <100>; /* 采样率:100Hz */
status = "okay";
};
文档版本: 1.0
最后更新: 2026-02-10
适用平台: RV1126B + Linux 6.1.141