跳到主要内容

3. I2C 使用

I2C(Inter-Integrated Circuit,集成电路间通信)是一种广泛使用的串行通信协议,主要用于在主控设备(如微控制器、处理器)和外围设备(如传感器、显示屏、存储器等)之间建立数据通信。它具有简单、高效的特点,非常适合短距离、低速的数据传输。

3.1 I2C 通讯的工作原理

I2C 使用两根线进行通信:

  • SCL(串行时钟线): 由主设备产生的时钟信号,用于同步数据传输。
  • SDA(串行数据线): 用于在设备之间传输数据。

I2C 是一种多主多从协议,这意味着可以在总线上连接多个主设备和从设备,但同一时间只能由一个主设备控制通信。

3.2 I2C 使用实例

3.2.1 i2c引脚

ArmSoM系列板子40pin上都有I2C外设,以下以 CM5-IO 为例子,CM5-IO 40PIN定义

I2C引脚功能
I2C3_SDA_M03i2c3 数据线
I2C3_SCL_M05i2c3 时钟信号
I2C7_SDA_M119i2c7 数据线
I2C7_SCL_M123i2c7 钟信号
I2C4_SDA_M327i2c4 数据线
I2C4_SCL_M329i2c4 时钟信号
I2C6_SDA_M289i2c6 数据线
I2C6_SCL_M288i2c6 时钟信号

3.2.2 使能IIC通信接口

I2C接口在默认情况是关闭状态的,需要使能才能使用

在 Armbian 操作系统中,/boot/armbianEnv.txt 文件用于配置系统启动时的参数和设备树插件。你可以通过编辑该文件来启用或禁用 I2C 设备树插件,确保 I2C 总线可以在启动时正确加载。

如果你希望检查或启用 I2C 相关设备树插件,可以按照以下步骤操作:

  • 查看设备树插件配置

打开文件: 通过终端打开 /boot/armbianEnv.txt 文件,使用文本编辑器如 nano 或 vim,例如:

root@armsom-cm5-io:/home/armsom# sudo nano /boot/armbianEnv.txt

这里以激活 i2c3-m0 为例,将 rk3576-i2c3-m0 打开如下:

overlays=rk3576-i2c3-m0

其中 overlays 行指定了设备树覆盖(Device Tree Overlay),i2c0 和 i2c1 是两个常见的 I2C 总线设备树插件。如果没有这些内容,你可以手动添加。

编辑完成后,保存文件并退出编辑器 重启系统使配置生效:

// 先执行sync
root@armsom-cm5-io:/home/armsom# sync
root@armsom-cm5-io:/home/armsom# sudo reboot

rockchip-i2c

提示
  • Q: 如果直接拔掉电源进行重启,是否可能导致文件未修改或 overlay 系统启动失败?
  • A: 当直接拔掉电源或强制关机时,可能会出现文件未能及时从内存(RAM)同步到存储设备(如硬盘、SSD)的情况。这是因为操作系统通常会将数据暂存于内存中,并定期将这些数据写入磁盘。为避免此问题,建议在关机前执行 “sync” 命令,确保所有数据已写入磁盘,再拔掉电源或关机。
  • 验证 I2C 是否启用

重启后,你可以通过以下命令检查 I2C 是否正常工作:

检查 I2C 设备: 使用 i2cdetect 工具检查 I2C 总线是否可用:

root@armsom-cm5-io:/home/armsom# sudo apt-get install i2c-tools  # 如果没有安装,先安装 i2c-tools
root@armsom-cm5-io:/home/armsom# sudo i2cdetect -y 3
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

-y 3 指的是检查第一个 I2C 总线。如果返回了设备地址的列表,表示 I2C 已启用并正常工作。

提示
  • 确保硬件本身支持并启用了相应的 I2C 总线。如果是自定义硬件,确保连接正确且硬件支持相应的 I2C 接口。
  • 不同的 Armbian 版本和硬件平台可能在设备树插件的名称上有所不同,因此请根据具体平台调整设备树覆盖项。

3.3 I2C 驱动参数配置

I2C 的参数配置最主要就是 I2C 频率的配置,可配 I2C frequency 除了与芯片有关外,主要是由 I2C SCL rise time 决定的,因为 I2C 协议标准里面对上升沿和下降沿时间有规定要求特别是上升沿时间,如果超过了协议规定的最大值,则 I2C 通讯可能失败,下面是协议里面规定的最大最小值范围,下图表示了二者之间的关系:

i2c-fre

上升沿 Tr 和下降沿 Tf,需要用示波器测量,参考下面示图:

i2c-fre1

3.3.1 i2c-rk3x.c 配置

i2c-rk3x.c 驱动的配置都在 DTS,参考文件 Documentation/devicetree/bindings/i2c/i2c-rk3x.txt。重点说明其 中配置项,i2c-scl-rising-time-ns,i2c-scl-falling-time-ns:

  • clock-frequency: 默认 frequency 为 100k 可不配置,其它 I2C 频率需要配置,最大可配置频率由
  • i2c-scl-rising-time-ns 决定;例如配置 400k,clock-frequency=<400000>。
  • i2c-scl-rising-time-ns:SCL 上升沿时间由硬件决定,改变上拉电阻可调节该时间,需通过示波器量测,参考上图;例如测得 SCL 上升沿 365ns,i2c-scl-rising-time-ns=<365>。(默认可以不配置,但必须保证当前的上升沿时间不能超过所配置频率下的 I2C 标准所定义的最大上升沿时间)
  • i2c-scl-falling-time-ns: SCL 下降沿时间, 一般不变, 等同于 i2c-sda-falling-time-ns。(默认也可以不配 置)
&i2c1 {
status = "okay";
i2c-scl-rising-time-ns = <265>;
i2c-scl-falling-time-ns = <11>;
clock-frequency = <400000>;

es8316: es8316@10 {
#sound-dai-cells = <0>;
compatible = "everest,es8316";
reg = <0x10>;
clocks = <&cru SCLK_I2S_8CH_OUT>;
clock-names = "mclk";
spk-con-gpio = <&gpio0 11 GPIO_ACTIVE_HIGH>;
hp-det-gpio = <&gpio4 28 GPIO_ACTIVE_LOW>;
};
};

3.4 GPIO 模拟 I2C

I2C 用 GPIO 模拟,内核已经有实现,请参考文档:Documentation/devicetree/bindings/i2c/i2c-gpio.txt 下面是使用的例子,dts 下配置 I2C 节点。

i2c@4 {
compatible = "i2c-gpio";
gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>, /* sda */
<&gpio5 8 GPIO_ACTIVE_HIGH>; /* scl */
i2c-gpio,delay-us = <2>; /* ~100 kHz */
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&i2c4_gpio>;
status = "okay";

gt9xx: gt9xx@14 {
compatible = "goodix,gt9xx";
reg = <0x14>;
touch-gpio = <&gpio5 11 IRQ_TYPE_LEVEL_LOW>;
reset-gpio = <&gpio5 10 GPIO_ACTIVE_HIGH>;
max-x = <1200>;
max-y = <1900>;
tp-size = <911>;
tp-supply = <&vcc_tp>;
status = "okay";
};
};

一般不推荐使用 GPIO,效率不高。

3.5 I2C 常见问题

i2c-rk3x.c 驱动 如果调用 I2C 传输接口返回值为 -6(-ENXIO)时候,表示为 NACK 错误,即对方设备无应答响应,这种情 况一般为外设的问题,常见的有以下几种情况:

  • I2C 地址错误;
  • I2C slave 设备处于不正常工作状态,比如没有上电,错误的上电时序以及设备异常等;
  • I2C 时序不符合 slave 设备所要求也会产生 NACK 信号,比如 slave 设备需要的是 stop 信号,而不是repeat start 信号的时候;
  • I2C 总线受外部干扰导致的,用示波器测量可以看到是一个 ACK 波形。

当出现 I2C 的 log:"timeout, ipd: 0x00, state: 1"时,此时 I2C 控制器工作异常,无法产生中断状态,start 时序无法发出,有以下几种可能:

  • I2C SCL 或者 SDA Pin 脚 iomux 错误;
  • I2C 的上拉电压不对,如电压不够或者上拉电源没有等;
  • I2C Pin 脚被外设拉住,电压不对;
  • I2C 时钟未开,或者时钟源太小;
  • I2C 同时配置了 CON_START 和 CON_STOP 位。

当出现 I2C 的 log:"timeout, ipd: 0x10, state: 1"时,此时 I2C 控制器工作正常,但是 cpu 无法响应 I2C 中 断,此时可能 cpu0 被阻塞了(一般 I2C 中断都在 cpu0 上面,通过 cat /proc/interrups 可以查看),或者 可能是 I2C 中断位被关闭了。

当出现 I2C 的 log 类似:"timeout, ipd: 0x80, state: 1"时,看到 ipd 为 0x80 打印,可以说明当前 SCL 被 slave 拉住,要判断被哪个 slave 拉住:

  • 一是排除法,适用于外设不多的情况,而且复现概率高;
  • 二是需要修改硬件,在 SCL 总线上串入电阻,通过电阻两端产生的压差来确定,电压更低的那端外设为拉低的 slave,电阻的选取以不影响 I2C 传输且可以看出压差为标准,一般上拉电阻的 1/20以上都可以,如果是 host 拉低也可以看出。另外在此基础上通过示波器来抓取波形更加直观,比较不同 slave 和 host 的低电平大小,与最后出问题时的低电平大小比较,相等的就是拉低总线的"元凶"。

常见的情况是 sda 被拉低,证明是谁拉低的,同样参考上面 “SCL 被拉低" 的方法两种。