Lazy loaded image
I2C IMU 驱动实战(IIO 子系统)
Words 2820Read Time 8 min
2026-3-5
本文从零开始,逐步完成一个挂载在 I2C 总线上的虚拟 IMU(加速度计)IIO 驱动,覆盖 DT binding → i2c_driver probe/remove → iio_dev 注册 → iio_info / sysfs 验证 全流程。

1. 前置条件

确保以下环境已就绪(参考同级页面的 QEMU + 交叉编译工具链搭建指南):
交叉编译器 arm-linux-gnueabihf-gcc 可用
已编译 v6.1 内核源码,且开启 CONFIG_IIOCONFIG_I2C 支持
QEMU mcimx6ul-evk(Cortex-A7)能正常启动 BusyBox rootfs
rootfs 中包含 iio_info(来自 libiio 或 iio-utils)
为什么选 IIO? IIO(Industrial I/O)是 Linux 为传感器类设备设计的标准子系统,提供统一的 sysfs 接口和触发缓冲机制,是 IMU、ADC、DAC 等设备的首选框架。

2. IIO 子系统关键概念

概念
说明
iio_dev
代表一个 IIO 设备实例,类比字符设备中的 cdev
iio_chan_spec
描述一路传感器通道(如 X/Y/Z 加速度),包含类型、修饰符、索引等
iio_info
驱动操作集,包含 read_raw / write_raw 等回调,用户读 sysfs 时触发
devm_iio_device_alloc
分配 iio_dev + 私有数据,生命周期由 devm 自动管理
iio_device_register
将 iio_dev 注册进内核,对应 /sys/bus/iio/devices/iio:deviceX

3. 驱动源码

3.1 IMU IIO 驱动 — fake_imu.c

3.2 关键结构说明

元素
作用
FAKE_IMU_ACCEL_CHANNEL
消除三轴通道定义的重复代码,统一声明 type / modified / mask / scan_index
info_mask_separate
标记 每通道独立 的属性,RAW 值各轴不同 → 对应 in_accel_x_raw 等节点
info_mask_shared_by_type
标记 同类型通道共享 的属性,SCALE 对所有加速度轴一致 → 对应单一 in_accel_scale 节点
scan_index
为后续触发缓冲(Triggered Buffer)预留通道索引,决定数据在缓冲区中的排列顺序
i2c_device_id[]
非 DT 环境的回退匹配表,配合 MODULE_DEVICE_TABLE(i2c, ...) 支持传统 board file 注册
iio_priv(indio_dev)
取出紧跟在 iio_dev 后面的私有数据指针,替代全局变量
devm_iio_device_register
注册 IIO 设备,由 devm 管理生命周期,remove 时自动注销
module_i2c_driver
宏展开为 module_init/module_exit,自动注册/注销 i2c_driver
info_mask_separate vs info_mask_shared_by_type:RAW 值是每个轴独立读取的(X/Y/Z 各不同),因此放在 separate;SCALE 转换系数对所有加速度通道一致(同一芯片量程相同),放在 shared_by_type 可避免生成冗余的 in_accel_x_scale / in_accel_y_scale / in_accel_z_scale,只暴露一个 in_accel_scale

4. Makefile

路径说明KERNDIR 使用仓库内相对路径 ../01_qemu_env_build/linux-6.1,确保在仓库目录结构下直接 make 即可编译,无需手动指定内核路径。

5. 编译模块

编译成功后确认产出:
注意 depends: industrialio:IIO 核心是一个独立模块,加载 fake_imu.ko 前必须先加载 industrialio.ko(或内核内建)。

6. 设备树配置

6.1 I2C 节点说明

I2C 设备必须作为 I2C 控制器节点的子节点 声明,并提供 reg(7-bit 地址)属性。

6.2 创建 Overlay 源文件 — fake-imu-overlay.dts

6.3 编译 Overlay

6.4 备选:直接修改主 DTB


7. 一键构建脚本 — build.sh

仓库提供了 build.sh,自动完成 编译模块 → 注入 DT 节点 → 打包 rootfs 全流程:
运行方式
脚本执行后产出三个关键文件:
  • fake_imu.ko — 编译好的驱动模块
  • imx6ul-14x14-evk-imu.dtb — 包含 fake_imu 节点的设备树
  • rootfs.cpio.gz — 打包好的根文件系统

7.1 手动打包(备选)

如果不使用 build.sh,可手动完成:

8. QEMU 内加载验证

8.1 启动 QEMU — run_qemu.sh

仓库提供了一键启动脚本,使用 mcimx6ul-evk(Cortex-A7)平台:
运行
平台说明:使用 mcimx6ul-evki.MX6UL EVK)而非 sabrelite,QEMU 对该机型的 I2C 控制器(i2c@21a0000)支持更完整,设备树注入后能正确触发 probe。

8.2 挂载文件系统 & 加载模块

8.3 确认 probe 调用

8.4 查看 IIO sysfs 接口

注意 sysfs 节点命名变化:由于 SCALE 使用 info_mask_shared_by_type,sysfs 只生成一个共享的 in_accel_scale,而非三个独立的 in_accel_x_scale / in_accel_y_scale / in_accel_z_scale

8.5 自动化验证脚本 — verify_imu.sh

仓库提供了一键验证脚本,在 QEMU 内执行:
verify_imu.sh 复制到 rootfs 后,QEMU 内直接运行:
预期输出:

8.6 使用 iio_info 验证(可选)


9. 验证清单

make 编译无 warning,产出 fake_imu.ko
modinfo 输出 depends: industrialio
file fake_imu.ko 显示 ARM ELF relocatable
设备树中 i2c1 节点下存在 fake_imu@68 子节点
insmoddmesg 显示 probe OK, addr=0x68
/sys/bus/iio/devices/iio:device0/name 输出 fake_imu
in_accel_z_raw 读取返回 1000
in_accel_scale 读取返回 0.009806(共享节点,非 in_accel_x_scale
verify_imu.sh 全部步骤通过
rmmoddmesg 显示 remove called

10. 常见问题排查

现象
原因
解决方案
insmod: can't insert: unknown symbol iio_device_register
IIO 核心未加载
insmod industrialio.ko,或内核配置 CONFIG_IIO=y
probe 未调用,无 probe OK 日志
DT 节点不在 i2c 控制器下,或 compatible 不匹配
确认 &i2c1 { fake_imu@68 { compatible = "myvendor,fake-imu"; ... } } 结构正确
/sys/bus/iio/devices/ 为空
iio_device_register 失败或 sysfs 未挂载
检查 dmesg 错误信息;确认 mount -t sysfs none /sys
in_accel_x_raw 返回 -EINVAL
read_raw 回调未处理该 mask
检查 iio_chan_spec.info_mask_separate 是否包含 BIT(IIO_CHAN_INFO_RAW)
iio_info 找不到设备
工具版本与内核 ABI 不匹配,或 /dev/iio:device0 缺失
确认内核开启 CONFIG_IIO_BUFFER,或改用 sysfs 直接读取验证
insmod: invalid module format
vermagic 不匹配
确保 KERNDIR 指向 QEMU 启动时使用的同一内核树

11. 向真实硬件(MPU-6050)迁移

完成虚拟驱动验证后,迁移到真实 IMU 只需替换以下部分:
  1. probe 中添加 WHO_AM_I 寄存器检测
    1. read_raw 中替换为真实寄存器读取
      1. DT compatible 改为 invensense,mpu6050
      1. 添加电源管理devm_regulator_get + pm_ops

      12. 下一步扩展方向

      1. 触发缓冲(Triggered Buffer) — 接入 IIO 触发器,实现中断驱动的连续数据采集
      1. libiio 用户态应用 — 用 iio_channel_read 替代 sysfs 读取,支持高频采样
      1. IIO 事件机制 — 配置阈值告警,通过 iio_push_event 上报超量程事件
      1. 对接 MIPI Camera 流水线 — IMU + 摄像头时间戳同步,构建完整感知平台
      上一篇
      Data Structure and Algorithm
      下一篇
      用面试拷问嵌入式技术栈

      Comments
      Loading...