4. 设备树实例:I2C控制器
I2C(Inter-Integrated Circuit)总线是嵌入式Linux系统中使用最广泛的低速外设总线之一,连接着触摸屏、传感器、EEPROM、RTC等大量从设备。在设备树中正确描述I2C控制器及其从设备,是驱动正常工作的前提。本节将以实际项目为例,详细讲解I2C控制器设备树节点的编写方法,涵盖时钟频率、从设备地址、时序参数等关键属性。
4.1 I2C控制器设备树节点结构
一个完整的I2C控制器节点通常包含以下层次结构:
- 控制器节点:描述I2C总线控制器本身的硬件信息(寄存器基址、中断号、时钟频率等)。
- 从设备子节点:挂载在该总线上的每个从设备,包含设备地址、时序参数、中断引脚等。
- 引脚控制(pinctrl):描述I2C总线使用的硬件引脚(SCL、SDA)及其复用功能。
关键原则:设备树中的I2C节点描述的是硬件连接关系,而非驱动行为。驱动通过读取设备树属性来获取硬件参数,从而完成初始化与通信配置。
4.2 核心属性详解
I2C控制器节点及其从设备子节点中,以下属性最为关键:
| 属性名 | 所属节点 | 说明 | 示例值 |
|---|---|---|---|
compatible |
控制器 | 匹配驱动,格式为"厂商,型号" | "fsl,imx6q-i2c" |
reg |
控制器 | 寄存器基地址与长度 | <0x021a0000 0x4000> |
interrupts |
控制器 | 中断号与触发类型 | <0 36 IRQ_TYPE_LEVEL_HIGH> |
clock-frequency |
控制器 | I2C总线时钟频率(单位Hz) | <100000> |
reg |
从设备 | 7位或10位从设备地址 | <0x1a> |
i2c-scl-rising-time-ns |
从设备 | SCL上升时间(纳秒) | <300> |
i2c-scl-falling-time-ns |
从设备 | SCL下降时间(纳秒) | <100> |
4.3 完整设备树实例
以下是一个基于i.MX6Q平台的I2C控制器节点实例,挂载了触摸屏控制器和温度传感器两个从设备:
/* I2C1控制器节点 */
&i2c1 {
compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
reg = <0x021a0000 0x4000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_I2C1>;
clock-names = "ipg";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
/* 总线时钟频率:100kHz标准模式 */
clock-frequency = <100000>;
/* 从设备1:触摸屏控制器 FT5x06 */
touchscreen@38 {
compatible = "edt,ft5x06";
reg = <0x38>;
interrupt-parent = <&gpio1>;
interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
touchscreen-size-x = <1024>;
touchscreen-size-y = <600>;
/* 时序参数:根据硬件手册设定 */
i2c-scl-rising-time-ns = <300>;
i2c-scl-falling-time-ns = <100>;
};
/* 从设备2:温度传感器 LM75 */
temperature@48 {
compatible = "national,lm75";
reg = <0x48>;
vs-supply = <®_3v3>;
/* 时序参数:快速模式 */
i2c-scl-rising-time-ns = <150>;
i2c-scl-falling-time-ns = <50>;
};
};
常见错误:
clock-frequency 属性必须放在控制器节点内,而不是从设备子节点中。从设备节点中的时序参数(如 i2c-scl-rising-time-ns)用于帮助控制器驱动计算时序寄存器值,而非设置总线频率。
4.4 时序参数的作用与配置
I2C时序参数直接影响总线通信的可靠性,尤其在高速模式(400kHz)或快速模式+(1MHz)下更为关键。控制器驱动根据这些参数计算SCL高电平和低电平的保持时间:
- i2c-scl-rising-time-ns:SCL信号从低电平上升到高电平所需时间。该值越大,驱动需要插入的等待周期越多。
- i2c-scl-falling-time-ns:SCL信号从高电平下降到低电平所需时间。通常比上升时间短。
- i2c-sda-hold-time-ns(可选):SDA数据保持时间,用于满足从设备的建立时间要求。
时序参数的典型取值范围如下:
| 总线模式 | 时钟频率 | 典型上升时间 | 典型下降时间 |
|---|---|---|---|
| 标准模式 | 100 kHz | 300-1000 ns | 100-300 ns |
| 快速模式 | 400 kHz | 100-300 ns | 30-100 ns |
| 快速模式+ | 1 MHz | 50-150 ns | 20-50 ns |
调试技巧:如果I2C通信出现随机失败或数据错误,首先检查时序参数是否与硬件手册匹配。使用示波器测量SCL/SDA的实际上升/下降时间,然后更新设备树中的对应值。不匹配的时序参数会导致总线时序违规,表现为ACK丢失或数据位错误。
4.5 I2C控制器设备树结构图
下图展示了I2C控制器节点、从设备子节点以及引脚控制之间的层次关系:
4.6 从设备地址的编码规则
I2C从设备地址在设备树中使用 reg 属性表示,需要注意以下规则:
- 7位地址:直接写入十六进制值,如
reg = <0x48>。这是最常用的格式。 - 10位地址:需要设置
reg为两个单元格,第一个单元格包含地址格式标志。例如reg = <0x00000002 0x01a0>,其中0x00000002表示10位地址格式。 - 地址左移:某些硬件手册将地址写为8位格式(左移1位),但设备树中必须使用7位格式。例如手册写
0x90,设备树中应写0x48。
地址陷阱:7位地址范围是
0x08 到 0x77(保留地址除外)。地址 0x00 到 0x07 以及 0x78 到 0x7F 是I2C规范保留的通用呼叫和特殊用途地址,不应分配给普通从设备。
4.7 驱动中读取设备树属性的方法
在I2C控制器驱动中,通过以下API读取设备树中的时序参数:
#include <linux/i2c.h>
#include <linux/of.h>
static int my_i2c_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct i2c_adapter *adap;
u32 clock_freq;
int ret;
/* 读取总线时钟频率 */
ret = of_property_read_u32(np, "clock-frequency", &clock_freq);
if (ret) {
/* 未设置时使用默认值100kHz */
clock_freq = 100000;
dev_warn(&pdev->dev, "clock-frequency not set, using 100kHz\n");
}
/* 读取时序参数(从设备子节点中获取) */
struct device_node *child;
u32 rise_time, fall_time;
for_each_child_of_node(np, child) {
if (!of_property_read_u32(child, "i2c-scl-rising-time-ns", &rise_time)) {
dev_info(&pdev->dev, "child %s: rise_time=%dns\n",
child->name, rise_time);
}
if (!of_property_read_u32(child, "i2c-scl-falling-time-ns", &fall_time)) {
dev_info(&pdev->dev, "child %s: fall_time=%dns\n",
child->name, fall_time);
}
}
/* 根据时序参数配置I2C控制器硬件寄存器 */
configure_i2c_timing(adap, clock_freq, rise_time, fall_time);
return 0;
}
4.8 调试I2C设备树节点的方法
当I2C设备树节点配置不正确时,驱动可能无法正常探测从设备。以下是常用的调试方法:
| 调试方法 | 命令/操作 | 预期输出 |
|---|---|---|
| 查看设备树节点 | ls /proc/device-tree/soc/aips-bus@*/i2c@*/ |
显示所有I2C控制器节点 |
| 检查节点属性 | cat /proc/device-tree/.../clock-frequency |
输出时钟频率值(如 100000) |
| 扫描I2C总线 | i2cdetect -y <bus-number> |
显示所有检测到的从设备地址 |
| 查看驱动绑定 | ls /sys/bus/i2c/devices/ |
显示已绑定的I2C设备列表 |
| 动态调试输出 | echo 'file i2c-core-of.c +p' > /sys/kernel/debug/dynamic_debug/control |
打印设备树解析过程的调试信息 |
实战案例:某项目中使用FT5x06触摸屏,I2C通信偶尔失败。通过示波器测量发现SCL上升时间约为450ns,而设备树中设置为300ns。将
i2c-scl-rising-time-ns 更新为450后,通信稳定性显著提升。这个案例说明时序参数必须与实际硬件测量值匹配,不能随意照搬参考设计。
4.9 本章小结
I2C控制器设备树节点的编写需要关注三个核心层面:控制器本身的硬件资源描述、从设备地址与中断配置、以及时序参数的精确设定。时钟频率决定了总线的工作模式,从设备地址必须使用7位格式且避开保留地址,时序参数则直接影响通信的可靠性。通过本节的学习,你应该能够独立编写和调试I2C控制器的设备树节点,为后续的驱动开发打下坚实基础。