驱动之路#45:I2C-Tools 不止 i2cdetect

描述

 前面几篇文章,我们已经把 I2C 的基础概念聊得差不多了:通信机制、上拉电阻、SMBus、I2C 子系统架构,以及硬件 I2C 和软件 I2C 的区别。
 

但真正调试 I2C 外设时,光懂这些还不够。

实际工作中,经常会遇到这种情况:

  •  
  •  
  •  
  •  
设备树也配了;驱动也编进去了;供电看起来也正常;但设备就是不 probe,或者一直通信失败。

这时候怎么办?

不要一上来就改驱动,也不要立刻怀疑内核。

更高效的做法是:先用 I2C-tools 看看硬件和总线到底通不通。

这篇就聊聊 I2C-tools 在实际调试中的用法。

1. I2C-tools 是什么?

I2C-tools 是一套用户空间 I2C 调试工具。

常用命令主要有:

  •  
  •  
  •  
  •  
i2cdetecti2cgeti2cseti2cdump

它们的作用可以简单理解为:

  •  
  •  
  •  
  •  
i2cdetect:扫描总线和设备地址;i2cget   :读取单个寄存器;i2cset   :写入寄存器;i2cdump  :批量读取寄存器。

这几个命令,基本就够覆盖大多数 I2C 调试场景。

它最大的价值是:

不用写驱动,也能直接和 I2C 设备“对话”。

这在调试阶段非常香。

尤其是判断问题属于硬件、设备树、驱动,还是外设本身时,I2C-tools 往往能帮我们快速缩小范围。

2. 为什么调试时喜欢用 I2C-tools?

在项目开发中,调一个 I2C 外设,如果每改一次都要:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
改设备树;重新编译 dtb;烧录;重启;看日志;再改;再烧;再重启。
那效率会非常低。

而 I2C-tools 可以在系统起来后,直接在命令行访问 I2C 总线。

比如:

  •  
i2cdetect -y 2

几秒钟就能知道 I2C2 总线上有没有设备应答。

如果扫不到设备,那就优先查硬件、供电、复位、pinctrl、上拉、地址。

如果能扫到设备,但驱动不 probe,那就再去查设备树 compatible、reg、驱动匹配表。

这样思路就清楚很多。

所以 I2C-tools 的核心作用不是“替代驱动”,而是:在写驱动之前,先快速确认底层通信是否正常。

3. 使用前先搞清楚 3 个信息

用 I2C-tools 之前,至少要先知道三个东西。

  •  
  •  
  •  
1. 使用哪一路 I2C 总线;2. 外设的 I2C 地址是多少;3. 要读写哪个寄存器。

翻译成工程语言就是:

用哪个 I2C 控制器,访问哪个 I2C 设备,读写哪个寄存器。

比如:

  •  
  •  
  •  
I2C 总线:i2c-2设备地址:0x38寄存器地址:0x00

那么后面命令基本都是围绕这三个信息展开。

 

4. 查看系统有哪些 I2C 总线

先看开发板当前有哪些 I2C 总线:

  •  
i2cdetect -l
I2C总线

这里的 i2c-0 / 6,就对应 /dev/i2c-0 / 6

后面使用工具时,-y 0 里的 0 就是这个总线编号。

如果你预期应该有 /dev/i2c-0,但这里完全看不到,那就要先检查:

  •  
  •  
  •  
  •  
  •  
[ ] 对应 I2C 控制器设备树是否 status = "okay"[ ] pinctrl 是否配置正确;[ ] 内核是否打开 I2C 控制器驱动;[ ] i2c-dev 是否启用;[ ] 设备节点是否生成。

5. 扫描总线设备地址:i2cdetect

确认总线存在后,就可以扫描设备地址:

  •  
i2cdetect -y 6
I2C总线

其中:

  •  
  •  
-y:跳过交互确认;6 :I2C 总线编号。

输出结果里常见三种情况:

  •  
  •  
  •  
--:表示没有该地址对应的设备;UU:表示有该设备并且它已经有驱动程序;数值:表示有该设备但是没有对应的设备驱动。

(1)显示 --

表示这个地址没有设备应答。

如果整张表都是 --,说明总线上没有扫到任何设备。

这时先别急着改驱动,重点查:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
[ ] 外设供电是否正常;[ ] reset / enable 是否释放;[ ] SDA / SCL 是否接反;[ ] 上拉电阻是否存在;[ ] pinctrl 是否复用到 I2C;[ ] 设备地址是否搞错;[ ] 当前扫描的总线编号是否正确。

(2)显示具体数值

比如看到:38

说明地址 0x38 上有设备应答,并且当前没有被内核驱动占用。

这通常是好消息,说明硬件通信至少是通的。

(3)显示 UU

UU 表示该地址上有设备,但已经被内核驱动绑定。

比如某个触摸芯片已经被驱动 probe 成功,i2cdetect 可能就会显示 UU

这时候如果你再强行访问它,可能会提示设备忙。

6. 读取单个寄存器:i2cget

如果已经知道设备地址和寄存器地址,就可以用 i2cget 读取单个寄存器。

格式如下:

  •  
i2cget -y <bus> <chip_addr> <reg_addr>

比如读取 I2C2 总线上 0x38 设备的 0x00 寄存器:

  •  
i2cget -y 2 0x38 0x00

如果输出:0x18

说明访问成功,读到了寄存器值。

这个命令很适合验证:

  •  
  •  
  •  
  •  
[ ] 设备地址是否正确;[ ] 寄存器是否能正常读取;[ ] 外设是否已经初始化;[ ] I2C 通信是否正常。

不过注意,不是所有 I2C 设备都支持这种“寄存器地址 + 读取一个字节”的访问方式。

有些设备读写流程比较特殊,用 i2cget 不一定能直接读出来,这时候要结合芯片手册看通信格式。

7. 批量读取寄存器:i2cdump

如果想一次性看看设备寄存器分布,可以使用:

  •  
i2cdump -y 2 0x38

它会尝试批量读取设备寄存器。

输出类似:

  •  
  •  
  •  
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f0018 00 00 00 00 00 00 00 00 00 00 00 00 00 00 001000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

这个命令适合快速观察寄存器变化。

比如:

  •  
  •  
  •  
修改配置前 dump 一次;修改配置后 dump 一次;对比哪些寄存器发生变化。

但也要注意:

i2cdump 不是所有设备都适合乱扫。

有些设备的某些寄存器读一次会清状态,有些地址可能触发特殊行为。特别是 PMIC、触摸、摄像头 sensor 这类设备,批量 dump 前最好先看手册。

调试归调试,别把设备状态扫乱了。

8. 写寄存器:i2cset

如果需要写设备寄存器,可以使用 i2cset

格式如下:

  •  
i2cset -y <bus> <chip_addr> <reg_addr> <value>

比如向 I2C2 总线 0x38 设备的 0xE1 寄存器写入 0x08

  •  
i2cset -y 2 0x38 0xE1 0x08

写完后,可以再读回来确认:

  •  
i2cget -y 2 0x38 0xE1

当然,并不是所有寄存器都支持读回。有些寄存器是写命令型寄存器,写入后不会保存原值。

所以还是那句话:

i2cset 前一定要看芯片手册,别对着未知寄存器乱写。

尤其是 PMIC、电源管理、屏幕、电机驱动这类设备,乱写寄存器可能直接导致系统异常。

9. 设备被驱动占用怎么办?

前面说过,如果 i2cdetect 显示 UU,说明该设备已经绑定内核驱动。

这时候直接用 i2cget / i2cset 访问,可能会提示 busy。

有时候可以加 -f 强制访问:

  •  
i2cget -f -y 2 0x38 0x00

但这里要小心。

-f 是强制访问,可能和内核驱动同时操作同一个设备,导致状态混乱。

更稳妥的做法是:临时解绑驱动。

比如某个设备绑定在 4-005a 上,可以通过 sysfs unbind:

  •  
echo 4-005a > /sys/bus/i2c/drivers/drv260x-haptics/unbind

格式一般是:

  •  
<bus>-<addr>

比如:

  •  
4-005a:I2C4 总线,设备地址 0x5a

解绑后,再用 i2c-tools 访问设备会更安全一些。

调试完成后,如果需要重新绑定,可以再 bind 回去,或者重启系统。

 

10. 为什么不用写驱动也能访问 I2C?

很多人刚开始会疑惑:

i2c-tools 是应用层工具,应用层怎么能直接访问硬件?

这里就要回到前面讲过的 I2C 子系统架构了。

I2C总线

Linux 内核里有一个通用驱动:

  •  
drivers/i2c/i2c-dev.c

它会为每个 I2C adapter 创建字符设备节点:

  •  
  •  
  •  
  •  
/dev/i2c-0/dev/i2c-1/dev/i2c-2...

i2c-tools 本质上就是通过这些设备节点,调用内核提供的 I2C 访问接口。

大致链路是:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
i2c-tools   ↓/dev/i2c-X   ↓i2c-dev.c   ↓i2c-core   ↓I2C 控制器驱动   ↓I2C 外设

所以并不是没有驱动。

而是 Linux 内核已经帮我们准备好了一个通用 I2C 用户空间访问驱动。

这也是 i2c-tools 能工作的根本原因。

11. 一套常用调试流程

实际调试时,不用记太多花里胡哨的命令。

我一般按下面这个流程走:

# 1. 查看系统有哪些 I2C 总线
  •  
i2cdetect -l
# 2. 扫描目标总线
  •  
i2cdetect -y 2
# 3. 读取设备 ID / 状态寄存器
  •  
i2cget -y 2 0x38 0x00
# 4. 必要时写初始化寄存器
  •  
i2cset -y 2 0x38 0xE1 0x08
# 5. 批量查看寄存器
  •  
i2cdump -y 2 0x38

这套命令基本够应付大多数 I2C 外设的前期验证。

如果这些都不通,就别急着调驱动,先回头查:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
[ ] 总线编号是否正确;[ ] 设备地址是否正确;[ ] 供电是否正常;[ ] reset / enable 是否释放;[ ] SDA / SCL 是否接反;[ ] 上拉电阻是否正确;[ ] pinctrl 是否配置正确;[ ] 设备是否已被驱动占用。

12. 总结

I2C-tools 在嵌入式 Linux 调试里非常实用。

它不是用来替代正式驱动的,而是用来快速验证:

  •  
  •  
  •  
  •  
  •  
I2C 总线是否存在;设备地址是否应答;寄存器是否能读写;硬件连接是否基本正常;问题到底更像硬件问题还是驱动问题。

其中最常用的几个命令就是:

  •  
  •  
  •  
  •  
  •  
i2cdetect -l      查看 I2C 总线i2cdetect -y X    扫描指定总线i2cget            读取寄存器i2cset            写寄存器i2cdump           批量读取寄存器

最后再啰嗦一句:i2c-tools 很好用,但不要乱用。

尤其是 i2cset 和 i2cdump,对某些设备可能有副作用。调试前最好先看芯片手册,确认寄存器读写方式。

Linux 知识很多,不必一口气全背下来。
工具也是一样,真正高频有用的先掌握,剩下的用到再查。

能解决问题,就是好工具。

若有不严谨之处,欢迎交流;如果对你有帮助,也别手下留情,点赞、关注、转发安排一下。

(完)

下期可以继续聊:I2C 信号如何测试与分析?


本人专注 Linux 嵌入式全栈开发,有项目合作 / 技术支持 / 交个朋友,欢迎后台私信。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分