I2C
概述
Linux I2C 编程接口支持主机端(Master)和从机端(Slave)之间的总线交互。编程接口围绕两种驱动程序和两种设备结构构建。
I2C“适配器驱动程序”将控制器硬件抽象化。它通常绑定到一个物理设备(可能是 PCI 设备或 platform_device)并通过 struct i2c_adapter 表示每个 I2C 总线段。连接在每条 I2C 总线段上的 I2C 设备通过 struct i2c_client 表示,并绑定到一个 struct i2c_driver,该驱动程序遵循标准的 Linux 驱动模型。内核提供了各种函数以执行标准的 I2C 协议操作。
SMBus(System Management Bus)是 I2C 的一个同级协议。大多数 SMBus 系统也兼容 I2C。相比 I2C,SMBus 对电气特性要求更为严格,并标准化了一些协议消息和使用习惯。通常支持 I2C 的控制器也能执行大多数 SMBus 操作,但 SMBus 控制器未必支持所有 I2C 协议选项。Linux 内核提供了执行多种 SMBus 协议操作的接口,可以使用基础的 I2C 操作,也可以通过向不支持这些 I2C 操作的 i2c_adapter 发出 SMBus 命令。
Linux 内核中的 I2C 驱动框架如下:
关键特性
- 仅支持 HOST 模式
- 支持标准模式(100Kbit/s)和快速模式(400Kbit/s)
- 符合 Philips I2C 总线规范 版本
- 支持 7-bit 和 10-bit 寻址模式
- 支持 Start / Restart / Stop
- 16 字节 FIFO
- 单次传输长度 65535 字节
- 模块启用/禁用功能
- 可编程 SCL 时钟频率
- 8-bit 位宽数据访问
- 自动触发 Clock Stretching
运行原理
I2C 是一种双线双向串行总线,通过 SDA 和 SCL 两根线进行设备间的高效数据交换,适用于多个设备间的短距离通信。
- SDA 和 SCL 通过上拉电阻连接至正电源电压
- 当总线空闲时,两条线路均为高电平
- 连接到总线的设备输出端必须为 open-drain 或 open-collector,实现“线与”功能
I2C 支持以下速率:
- 标准模式:最高 100kbit/s
- 快速模式:最高 400kbit/s
- 高速模式:最高 3.4Mbit/s
I2C 数据传输格式
- 数据在 Master 和 Slave 之间通过 SDA 线逐字节传输
- 传输通过 SCL 线进行同步
- 每个数据字节长度为 8-bit, 字节数不受限制
- 每个字节后必须跟一个 ACK 位(应答位)
数据按 MSB(高位在前)顺序传输
- 若目标设备在操作前无法接收或发送完整字节,控制器将 SCL 拉低,进入等待状态
- 当设备准备好后,释放 SCL,总线继续传输
Start/Stop 信号
- 所有传输以 Start 信号开始,Stop 信号结束
- Start: 在 SCL 为高电平时,SDA 从高电平跳变为低电平
- Stop: 在 SCL 为高电平时,SDA 从低电平跳变为高电平
- Start 和 Stop 信号只能由 Host 发出
- 发出 Start 信号后,总线状态为 busy
- 发出 Stop 信号后,总线状态变为 idle
- 若 Host 发出 Start 后再次发出重复的 Start(Sr)而非 Stop,总线仍保持 busy 状态
I2C 7-bit 地址模式数据传输
Host 可以采用以下三种方式与 Slave 进行通信:
Host 向 Slave 发送多个字节
Host 接收 Slave 的多个字节
Host 使用 combine 模式读取 Slave 数据
API 说明
i2c/master_recv
/**
* i2c_master_recv - issue a single I2c message in master receive mode
* @client: Handle to slave device
* @buf: Where to store data read from slave
* @count: How many bytes to read, must be less than 64k since msg.len is u16
* Returns negative errno, or else the number of bytes read.
*/
static inline int i2c_master_recv(const struct i2c_client *client, char *buf, int count);
i2c/master_send
/**
* i2c_master_send - issue a single I2C message in master transmit mode
* @client: Handle to slave device
* @buf: Data that will be written to the slave
* @count: How many bytes to write, must be less than 64k since msg.len is u16
* Returns negative errno, or else the number of bytes written.
*/
static inline int i2c_master_send(const struct i2c_client *client, const char *buf, int count);
i2c_transfer
/**
* i2c_transfer - execute a single or combined I2C message
* @adap: Handle to I2C bus
* @msgs: One or more messages to execute before STOP is issued to terminate the
operation; each message begins with a START.
* @num: Number of messages to be executed.
* Returns negative errno, else the number of messages executed.
*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
i2c_smbus_xfer
/**
* i2c_smbus_xfer - execute SMBus protocol operations
* @adapter: Handle to I2C bus
* @addr: Address of SMBus slave on that bus
* @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC)
* @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE
* @command: Byte interpreted by slave
* @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL
* @data: Data to be read or written
* Returns a negative errno code else zero on success.
*/
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
unsigned short flags, char read_write,
u8 command, int protocol, union i2c_smbus_data *data);
i2c_smbus_read_byte、i2c_smbus_write_byte
/**
* i2c_smbus_read_byte - SMBus "receive byte" protocol
* @client: Handle to slave device
* Returns negative errno else the byte received from the device.
*/
s32 i2c_smbus_read_byte(const struct i2c_client *client);
/**
* i2c_smbus_write_byte - SMBus "send byte" protocol
* @client: Handle to slave device
* @value: Byte to be sent
* Returns negative errno else zero on success.
*/
s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value);
i2c_smbus_read_byte_data、i2c_smbus_write_byte_data
/**
* i2c_smbus_read_byte_data - SMBus "read byte" protocol
* @client: Handle to slave device
* @command: Byte interpreted by slave
* Returns negative errno else a data byte received from the device.
*/
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command);
/**
* i2c_smbus_write_byte_data - SMBus "write byte" protocol
* @client: Handle to slave device
* @command: Byte interpreted by slave
* @value: Byte being written
* Returns negative errno else zero on success.
*/
s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8
value);
i2c_smbus_read_word_data、i2c_smbus_write_word_data
/**
* i2c_smbus_read_word_data - SMBus "read word" protocol
* @client: Handle to slave device
* @command: Byte interpreted by slave
* Returns negative errno else a 16-bit unsigned "word" received from the
device.
*/
s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command);
/**
* i2c_smbus_write_word_data - SMBus "write word" protocol
* @client: Handle to slave device
* @command: Byte interpreted by slave
* @value: 16-bit "word" being written
* Returns negative errno else zero on success.
*/
s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, u16
value);
i2c_smbus_read_i2c_block_data、i2c_smbus_write_i2c_block_data
/**
* i2c_smbus_read_i2c_block_data - read a block of bytes
* @client: Handle to slave device
* @command: Byte interpreted by slave
* @length: Number of bytes to read
* @values: Pointer to where read bytes will be stored
* Returns the number of bytes read or negative errno.
*/
s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client,
u8 command, u8 length, u8 *values);
/**
* i2c_smbus_write_i2c_block_data - write a block of bytes
* @client: Handle to slave device
* @command: Byte interpreted by slave
* @length: Number of bytes to write
* @values: Pointer to data to write
* Returns zero on success or negative errno.
*/
s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client,
u8 command, u8 length, const u8 *values);
配置流程
步骤 1:配置设备树
MTT E300 有 19 组 I2Ccontroller,其设备树文件路径为:
linux-mthreads/arch/arm64/boot/dts/m1000/m1000-main.dtsi
其设备树节点如下:
i2c0: i2c@703000 {
compatible = "mthreads,M1000-i2c";
reg = <0x0 0x703000 0x0 0x1000>;
interrupts = <GIC_SPI 0x117 IRQ_TYPE_LEVEL_HIGH>;
clock-frequency = <400000>;
reset = <&rg RESET_PERI_I2C0>;
reset-names = "peri_i2c0_rst";
clocks = <&rg PERI_I2C0 FUNC_CLK>;
clock-names = "peri_i2c0_clk";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
其中 clock-frequency 属性表示 SCL 的通讯频率,值可为 100k(standard 模式)或者 400k(fast 模式)
为了区分 19 组寄存器,需要在 aliases 节点中指定每个 controller 在 Linux 系统中 I2C 的 bus 号:
aliases{
i2c0 = &i2c0;
i2c1 = &i2c1;
i2c2 = &i2c2;
i2c3 = &i2c3;
i2c4 = &i2c4;
i2c5 = &i2c5;
i2c6 = &i2c6;
i2c7 = &i2c7;
i2c8 = &i2c8;
i2c9 = &i2c9;
i2c10 = &i2c10;
i2c11 = &i2c11;
i2c12 = &i2c12;
i2c13 = &i2c13;
i2c14 = &i2c14;
i2c15 = &i2c15;
i2c16 = &i2c16;
i2c17 = &i2c17;
i2c18 = &i2c18;
}
I2C 外接了不同的外设,需要在设备树中配置子节点,参考配置如下:
&i2c5 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c5_default>;
};
rt5672: rt5672@1c {
#sound-dai-cells = <0>;
compatible = "realtek,rt5672";
reg = <0x1c>;
headset-gpios = <&gpio_peri MT_GPIO_GLB_NO(153) GPIO_ACTIVE_HIGH>;
status = "okay";
};
};
status为”okay”表示开启该设备树节点,”disabled”表示关闭设备树节点。如果- 没有
status设备树节点默认是开启状态。 - 使用 I2C 前请先配置
pinctrl(见 pinctrl 章节) 选中对应 GPIO pin 的 I2C 功能。子 - 节点中的
reg表示设备的从机地址。
步骤 2:配置内核
在 Linux 内核的配置菜单中要选中 M1000i2c 的驱动,路径如下:
| Symbol: I2C_MT [=y] |
| --- | --- |
| Type : tristate |
| Defined at drivers/i2c/busses/Kconfig:1482 |
| Prompt: mthreads I2C driver |
| Depends on: I2C [=y] && HAS_IOMEM [=y] |
| Location |
| -> Device Drivers |
| -> I2C support |
| -> I2C support (I2C [=y]) |
| -> I2C Hardware Bus support |
| (1) ->
> mthreads I2C driver (I2C_MT [=y])
同时要在内核中选中相应的 device 的外设驱动。
开发参考
slave 驱动示例
设备树配置示例:
&i2c11{
status = "okay";
TouchPad@0x2c {
compatible = "hid-over-i2c";
reg = <0x2c>;
hid-descr-addr = <0x20>;
interrupt-parent = <&gpio_peri>;
// interrupts = <99 IRQ_TYPE_LEVEL_LOW>;
interrupts = <MT_GPIO_GLB_NO(99) IRQ_TYPE_LEVEL_LOW>;
status = "okay";
};
};
驱动参考内核路径:
i2c-dev 驱动使用示例
也可以不编写 slave 驱动而直接使用 i2c-dev 驱动。i2c-dev.c 位于:linux-mthreads/drivers/i2c/i2c-dev.c,为 i2c 的万能设备驱动,该驱动会在加载时遍历所有的 i2c 总线,将其中 i2c_adapter_type 全部注册为字符设备,例如 /dev/i2c-0、/dev/i2c-1 等,应用程序可通过 open、write、ioctl 调用该驱动,该驱动则会通过 ioctl 调用底层的 adapter 驱动进行 i2c 外设的读写。在内核的配置菜单中需要选中 i2c-dev 驱动,其路径如下:
Symbol: I2C_CHARDEV [=y]
| Type : tristate
| Defined at drivers/i2c/Kconfig:51
| Prompt: I2C device interface
| Depends on: I2C [=y]
| Location:
| -> Device Drivers
| -> I2C support
| -> I2C support (I2C [=y])
| (1) -
> I2C device interface (I2C_CHARDEV [=y])
在 linux 系统启动后会在 /dev/ 目录下为每条 i2c 的 bus 生成相应的节点,例如 /dev/i2c-0、/dev/i2c-1,在应用层可以通过 ioctl 读写该节点进行 i2c 的通讯。