跳到主要内容

I2C

介绍I2C的功能和使用方法。

模块介绍

I2C 总线是一种两线式串行总线,用于连接微控制器及其外围设备,多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短。每个设备都有自己的唯一地址,且I2C为半双工,任意时刻只能有一个主机并进行单行通信。

功能介绍

i2c
Linux 中 I2C 体系结构上图所示,共分成了三个层次:

  1. 用户空间,包括所有使用I2C 设备的应用程序;
  2. 内核,也就是驱动部分;
  3. 硬件,指实际物理设备,包括了 I2C 控制器和 I2C 外设。

其中,Linux 内核中的 I2C 驱动程序从逻辑上主要实现:

  • I2C framework 提供一种 “访问 I2C slave devices” 的方法。由于这些 slave devices 由 I2C controller 控制,因而主要由 I2C controller 驱动实现这一目标。
  • 在 I2C framework 内部,有 I2C core、I2C busses、I2C algos 和 I2C muxes 四个模 块。
  • I2C core 使用 I2C adapter 和 I2C algorithm 两个子模块抽象 I2C controller 的功能。
  • I2C busses 是各个 I2C controller drivers 的集合,位于 drivers/i2c/busses/目录下, i2c-k1.c。
  • I2C algos 包含了一些通用的 I2C algorithm,所谓的 algorithm,是指 I2C 协议的通信方 法,用于实现 I2C 的 read/write 指令。

源码结构介绍

控制器驱动代码在drivers/i2c/目录下:

drivers/i2c/
|-- i2c-core-of.c #I2C子系统核心文件,提供相关的接口函数
|-- i2c-boardinfo.c
|-- i2c-core-base.c
|-- i2c-core-slave.c
|-- i2c-core-smbus.c
|-- i2c-dev.c #I2C子系统的设备相关文件,用于注册相关的设备文件
|-- busses/i2c-k1x.c #k1平台的I2C控制器驱动代码

关键特性

特性

特性特性说明
支持9组I2C支持9组I2C接口
支持DMA主机模式下支持DMA数据传输
支持100k / 400k/ 1.5M三种速度模式支持三种速度模式,可通过dts配置
支持总线仲裁多主机模式下支持总线仲裁

配置介绍

主要包括驱动使能配置和dts配置

CONFIG配置

CONFIG_I2C 和 CONFIG_I2C_SPACEMIT_K1X,默认情况,此选项为Y

Device Drivers
I2C support
I2C support (I2C [=y])
I2C Hardware Bus support
Spacemit k1x I2C adapter (I2C_SPACEMIT_K1X [=y])

CONFIG_I2C_SLAVE为I2C的从设备模式,比如支持

Device Drivers
I2C support
I2C support (I2C [=y])
I2C slave support (I2C_SLAVE [=y])

CONFIG_I2C_CHARDEV为I2C字符设备

Device Drivers
I2C support
I2C support (I2C [=y])
I2C device interface (I2C_CHARDEV [=y])

dts配置

i2c总线配置如下:

配置说明
spacemit,i2c-fast-mode400K速度模式
spacemit,i2c-high-mode1.5M速度模式
spacemit,dma-disable关闭DMA传输

总线默认速率为 100K,如需切换速度模式,在 dts 中加入对应配置即可;

如切换为 fast mode,配置如下:

i2c6: i2c@d4018800 {
spacemit,i2c-fast-mode;
};

“spacemit,dma-disable”配置表示不使用 DMA 传输,在 dts 中去掉该配置即可打开 DMA 传输;

以 i2c6 为例,关闭 DMA 传输,k1-x.dtsi 中配置如下:

i2c6: i2c@d4018800 {
spacemit,dma-disable;
};

pinctrl

查看开发板原理图,找到 i2c 控制器使用的 pin 组。

以 i2c6 为例,假设原理图中分别使用 gpio56/57 作为 SCL/SDA,且配置可使用 k1-x_pinctrl.dtsi 中已定义的 pinctrl_i2c6_2 组,则方案 dts 配置如下。

&i2c6 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c6_2>;
};

i2c 设备配置

以 touchscreen 设备为例分析 i2c 设备的 dts 如何配置。

设备类型

确认设备类型以及使用的驱动。

选择 touchscreen 设备的 compatible 配置为“goodix,gt9xx”。

设备地址

确认设备的 i2c 通信地址-7 bit。

查询原理图得到 touchscreen 设备地址为 0x5d,配置如下。

gt9xx@5d {
compatible = "goodix,gt9xx";
reg = <0x5d>;
}
通信频率

确认设备通信频率。

touchscreen 通信频率支持 100K,选择挂载在 i2c bus6 下,去掉“spacemit,i2c-fast-mode”,“spacemit,i2c-high-mode”配置,使用默认速率 100K。

设备控制信号

在方案原理图中查询设备所使用的控制信号。

touchscreen 的 reset / irq 信号分别为 gpio 114/58,irq-flags 配置为所需的中断触发方式,如:2 为下降沿触发,配置如下。

gt9xx@5d {
reset-gpios = <&gpio 114 GPIO_ACTIVE_HIGH>;
irq-gpios = <&gpio 58 GPIO_ACTIVE_HIGH>;
irq-flags = <2>;
};
设备 dts

touchscreen 设备地址为 0x5d,gpio 114/58 分别为 reset/irq 信号,下降沿触发中断,通信频率为 100K,配置触摸屏相应参数。

设备 dts 配置如下:

gt9xx@5d {
compatible = "goodix,gt9xx";
reg = <0x5d>;
reset-gpios = <&gpio 114 GPIO_ACTIVE_HIGH>;
irq-gpios = <&gpio 58 GPIO_ACTIVE_HIGH>;
irq-flags = <2>;
touchscreen-max-id = <11>;
touchscreen-size-x = <1200>;
touchscreen-size-y = <1920>;
touchscreen-max-w = <512>;
touchscreen-max-p = <512>;
};

dts示例

综合上述信息,i2c6 连接 i2c touchscreen 设备,方案 dts 配置如下。

&i2c6 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c6_2>;
status = "okay";

gt9xx@5d {
compatible = "goodix,gt9xx";
reg = <0x5d>;
reset-gpios = <&gpio 114 GPIO_ACTIVE_HIGH>;
irq-gpios = <&gpio 58 GPIO_ACTIVE_HIGH>;
irq-flags = <2>;
touchscreen-max-id = <11>;
touchscreen-size-x = <1200>;
touchscreen-size-y = <1920>;
touchscreen-max-w = <512>;
touchscreen-max-p = <512>;
};
};

接口介绍

API介绍

内核态:I2C的读写通信都是使用linux标准接口,请参考kernel目录下Documentation/i2c/writing-clients.rst文档说明,关于发送和接收部分有详细的介绍。
用户态:从用户态可以通过"/dev/i2c-%d"节点访问总线上所有的设备。demo示例是一个简单的访问i2c设备读写,具体可以参考Documentation/i2c/dev-interface.rst文档

demo示例

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>

#define I2C_DEV_FILE "/dev/i2c-1" // I2C设备文件路径,通常为/dev/i2c-1
#define DEVICE_ADDR 0x68 // I2C设备的地址,示例为0x68<根据实际设备修改>

// 写数据到I2C设备
int write_to_i2c(int file, uint8_t reg, uint8_t value) {
uint8_t buffer[2];
buffer[0] = reg; // 寄存器地址
buffer[1] = value; // 写入的值

if (write(file, buffer, 2) != 2) {
perror("I2C write failed");
return -1;
}
return 0;
}

// 从I2C设备读取数据
int read_from_i2c(int file, uint8_t reg, uint8_t *data, size_t len) {
if (write(file, &reg, 1) != 1) {
perror("I2C write register failed");
return -1;
}

if (read(file, data, len) != len) {
perror("I2C read failed");
return -1;
}
return 0;
}

int main() {
int file;
uint8_t data[2]; // 用于存储读取到的数据

// 打开I2C设备文件
if ((file = open(I2C_DEV_FILE, O_RDWR)) < 0) {
perror("Failed to open I2C bus");
exit(1);
}

// 设置I2C从设备的地址
if (ioctl(file, I2C_SLAVE, DEVICE_ADDR) < 0) {
perror("Failed to acquire bus access and/or talk to slave");
close(file);
exit(1);
}

if (write_to_i2c(file, 0x6B, 0x00) < 0) {
close(file);
exit(1);
}
printf("Write 0x00 to register 0x6B\n");

if (read_from_i2c(file, 0x75, data, 1) < 0) {
close(file);
exit(1);
}
printf("Read data 0x%02x from register 0x75\n", data[0]);

// 关闭I2C设备文件
close(file);

return 0;
}

Debug介绍

sysfs

debugfs

测试介绍

I2C tool是一个开源工具,可自行下载进行交叉编译,代码下载地址 编译后会生成i2cdetect,i2cdump,i2cset,i2cget等工具,可以直接用命令行调试使用:

  • i2cdetect:用来列举i2c bus和上面所有的设备
  • i2cdump:显示i2c设备上所有register的值
  • i2cset:读取i2c设备某个register的值
  • i2cget:写入i2c设备某个register的值

FAQ