linux内核中的I2C
0、说明
介绍I2C基本协议,以及在linux内核中I2C驱动程序框架。
I2C驱动基于标准了总线设备驱动模型,因此本文仅示意分析I2C驱动框架,驱动实现细节按照总线设备驱动模型来实现,不做过多分析。
1、I2C基础
只有两脚
- SCK
- SDA
根据引脚可以推断,一个数据脚为半双工,且数据脚方向为双向。没有片选信号,因此数据通信需要带有地址信息。
开始和结束信号
时钟为高电平期间,数据线从高变低是开始,从低变高是结束。
数据信号
时钟为高器件,数据线不允许变化,变化就是开始和结束。时钟为低电平器件,数据bit进行变化传输。
ACK
通信完成一个字节后,从设备或者主设备主动将SDA拉低从而产生一个ACK信号给对方,默认情况下被外部上拉置位高电平。
读写时序
2、内核中的I2C驱动
架构
遵循总线设备驱动模型。总线驱动在设置config支持i2c后在内核启动阶段被注册。与SPI总线类似,设备树中描述控制器及其下的设备,被转换为控制器及i2c_client设备。并注册不同类型的设备驱动,完成匹配。
i2c_bus_type(总线)、i2c_adpater(控制器)、i2c_client(设备)、驱动(i2c_driver)
设备树控制器描述
i2c1: i2c@021a0000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
reg = <0x021a0000 0x4000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_I2C1>;
status = "disabled";
};
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
mag3110@0e {
compatible = "fsl,mag3110";
reg = <0x0e>;
position = <2>;
};
fxls8471@1e {
compatible = "fsl,fxls8471";
reg = <0x1e>;
position = <0>;
interrupt-parent = <&gpio5>;
interrupts = <0 8>;
};
};
总线驱动
控制器驱动
设备驱动
总结
与SPI驱动类似,I2C完全符合总线设备驱动模型。I2C通信只有2根型号,可产生开始信号,停止信号,数据传输,ACK反馈等。