2. 设备树基础:语法、节点、属性与核心概念
设备树(Device Tree)是一种描述硬件资源的数据结构,它使用节点(node)和属性(property)来描述系统中的设备、总线、中断控制器、时钟、GPIO 等硬件信息。在嵌入式 Linux 中,设备树是驱动与硬件之间的“桥梁”,尤其对于 V4L2 子系统,pinctrl 和时钟配置的正确性直接依赖于设备树的描述。
2.1 设备树语法概述
设备树源文件(.dts)采用树形结构,以“/”为根节点,每个节点代表一个硬件设备或总线。基本语法规则如下:
- 节点(node):以
node-name@unit-address形式定义,unit-address通常为寄存器或总线地址。 - 属性(property):键值对,值可以是字符串、整数、数组、二进制数据等。
- 标签(label):为节点起别名,便于引用,如
i2c0: i2c@ff000000。 - 包含文件:使用
#include引入 .dtsi 文件,实现 SoC 级描述复用。
关键点:设备树不是可执行代码,而是静态描述。内核在启动时解析设备树,生成 platform_device 等设备模型。
2.2 节点与属性详解
每个节点可以包含子节点和属性。以下是 V4L2 摄像头设备树中常见的节点与属性示例:
/ {
model = "Embedded Camera Board";
compatible = "vendor,board-rev1";
soc {
#address-cells = <1>;
#size-cells = <1>;
i2c2: i2c@ff130000 {
compatible = "snps,designware-i2c";
reg = <0xff130000 0x1000>;
interrupts = <0 38 4>;
clocks = <&clk_i2c2>;
#address-cells = <1>;
#size-cells = <0>;
camera_sensor: camera@10 {
compatible = "sony,imx219";
reg = <0x10>;
clocks = <&clk_cam_mclk>;
clock-names = "xvclk";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&cam_pins_default>;
pinctrl-1 = <&cam_pins_sleep>;
reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
port {
sensor_ep: endpoint {
remote-endpoint = <&csi_ep>;
data-lanes = <1 2>;
clock-noncontinuous;
};
};
};
};
};
};
2.3 核心属性:reg、interrupts、clocks、pinctrl
对于 V4L2 驱动,以下四个属性最为关键:
| 属性 | 作用 | V4L2 相关说明 |
|---|---|---|
reg |
描述设备地址空间(寄存器基址+长度) | 用于 i2c 设备地址或 CSI 控制器寄存器映射 |
interrupts |
指定中断号、触发类型 | CSI 接收完成中断、传感器帧同步中断 |
clocks / clock-names |
引用时钟源并命名 | MCLK(主时钟)频率直接影响传感器帧率 |
pinctrl-names / pinctrl-0 |
配置引脚复用状态 | 确保 I2C、CSI 数据线、时钟线引脚功能正确 |
实战提示:在调试 V4L2 摄像头时,如果设备无法识别,首先检查
reg 中的 I2C 地址是否与传感器数据手册一致;其次检查 clocks 是否已使能且频率正确。
2.4 设备树层级与 V4L2 设备关系
下图展示了设备树中摄像头传感器、CSI 控制器、时钟和 pinctrl 之间的层级关系:
2.5 属性值格式与编码
设备树属性值有多种编码方式,驱动解析时必须匹配:
| 属性值类型 | DTS 写法 | 内核解析 API | 示例 |
|---|---|---|---|
| 32 位整数 | <0x10> |
of_property_read_u32() |
I2C 设备地址 |
| 64 位整数 | <0x00000000 0x10000000> |
of_property_read_u64() |
64 位寄存器地址 |
| 字符串 | "sony,imx219" |
of_property_read_string() |
compatible 匹配 |
| 字符串数组 | "default", "sleep" |
of_property_read_string_index() |
pinctrl-names |
| 二进制数据 | [01 02 03 04] |
of_get_property() |
校准数据、MAC 地址 |
| phandle + 参数 | <&clk_i2c2 0> |
of_parse_phandle_with_args() |
时钟、中断、GPIO 引用 |
2.6 #address-cells 与 #size-cells
这两个属性决定了 reg 属性中地址和长度的编码单元数:
#address-cells = <1>:地址用 1 个 32 位单元表示。#size-cells = <1>:长度用 1 个 32 位单元表示。- 对于 64 位地址空间,使用
<2>。
常见错误:如果父节点的
#address-cells 与子节点 reg 的单元数不匹配,内核将无法正确解析地址,导致设备 probe 失败。例如,父节点设置 #address-cells = <2>,但子节点 reg = <0x10> 只给了一个单元,内核会报错。
2.7 中断属性详解
中断属性 interrupts 的格式依赖于父中断控制器的 #interrupt-cells:
// GIC(通用中断控制器)典型配置
interrupt-parent = <&gic>;
interrupts = <0 38 4>;
// 含义:SPI 类型(0),中断号 38,高电平触发(4)
// 简单中断控制器
interrupts = <10 2>;
// 含义:中断号 10,下降沿触发(2)
对于 V4L2 摄像头,CSI 控制器通常使用 SPI 中断,而传感器本身通过 I2C 通信,不直接使用中断线(但部分传感器支持帧同步中断)。
2.8 设备树编译与调试
设备树源文件需要编译为二进制 .dtb 文件供内核使用:
# 编译设备树
dtc -I dts -O dtb -o myboard.dtb myboard.dts
# 反编译查看
dtc -I dtb -O dts -o myboard.dts myboard.dtb
# 检查设备树是否被正确加载
ls /proc/device-tree/
cat /proc/device-tree/model
调试技巧:在 V4L2 驱动 probe 函数中添加
dev_info() 打印 of_node 的 name、compatible、reg 值,与设备树对比,快速定位属性解析问题。
2.9 设备树与 V4L2 驱动的绑定流程
下图展示了从设备树描述到 V4L2 设备注册的完整流程:
2.10 常见设备树错误与排查
| 错误现象 | 可能原因 | 排查方法 |
|---|---|---|
| 设备不 probe | compatible 不匹配或 reg 地址错误 | 检查 /sys/bus/platform/devices/ 或 /sys/bus/i2c/devices/ |
| 中断不触发 | interrupts 属性格式错误或中断号不对 | 查看 /proc/interrupts 确认中断号 |
| 时钟频率异常 | clocks 引用错误或时钟未使能 | 使用 clk_summary 调试文件 |
| 引脚功能异常 | pinctrl 配置错误或引脚复用冲突 | 检查 /sys/kernel/debug/pinctrl/ |
最佳实践:在开发阶段,建议在设备树中添加
status = "okay"; 显式使能节点;对于调试,可以使用 of_platform_populate() 手动触发设备创建,验证设备树解析逻辑。