Linux下的设备树源文件(DTS, Device Tree Source)是描述硬件设备信息的关键文本格式,用于替代传统的硬编码板级支持包(BSP),实现硬件描述与内核代码的解耦,提高系统的可移植性和维护性,以下从基本概念、结构、编译、使用流程及调试等方面详细介绍DTS的使用方法。
DTS的基本概念与作用
在Linux内核早期,不同硬件平台的设备信息(如外设地址、中断号、时钟频率等)需直接写在内核代码中,导致每个板卡都需要修改内核源码,维护成本高,设备树通过树状结构的文本文件(.dts)统一描述硬件拓扑和设备属性,内核启动时解析该文件并动态生成设备,无需修改内核代码即可适配不同硬件,DTS文件通常位于内核源码的arch/<架构>/boot/dts/
目录下,对应板卡的文件名通常以<board-name>.dts
命名(如imx6ull.dts
)。
DTS的基本结构
DTS采用树状层级结构,每个节点代表一个设备或总线,通过属性(property)描述设备的特性,核心结构包括:
根节点(/)
设备树的顶层节点,以表示,定义了全局属性,如地址和大小单元格数(#address-cells
和#size-cells
),用于描述子节点的地址空间。
子节点(Sub-nodes)
根节点下可包含多个子节点,代表硬件上的总线、控制器或外设。/soc
节点通常用于片上系统(SoC)的外设控制器,/memory
节点定义系统内存。
属性(Properties)
节点通过键值对形式的属性描述硬件信息,常见属性包括:
compatible
:字符串列表,用于驱动匹配(如"arm,cortex-a7"
),内核通过该属性查找对应的设备驱动。reg
:寄存器地址映射,格式为<地址 长度>
,需配合#address-cells
和#size-cells
解析。interrupts
:中断号,格式为<中断类型 中断标志 中断号>
,具体取决于架构和中断控制器。clocks
:时钟源,引用/clocks
节点中的时钟。status
:设备状态,如"okay"
(启用)或"disabled"
(禁用)。
示例:简单DTS片段
/dts-v1/; // 声明设备树版本 / { #address-cells = <1>; // 子节点reg属性中地址占用1个cell(32位) #size-cells = <1>; // 子节点reg属性中长度占用1个cell memory@0x80000000 { // 内存节点,基地址0x80000000 device_type = "memory"; reg = <0x80000000 0x20000000>; // 地址0x80000000,长度512MB }; soc { // SoC节点 compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; uart0: uart@0x02000000 { // UART控制器节点 compatible = "ns16550a"; reg = <0x02000000 0x1000>; interrupts = <1 2>; clock-frequency = <1843200>; status = "okay"; }; }; };
DTS的编译与使用
DTS文本文件需编译为二进制格式(DTB, Device Tree Blob)才能被内核识别,常用工具为dtc
(Device Tree Compiler)。
安装dtc工具
基于Debian/Ubuntu的系统可通过以下命令安装:
sudo apt install device-tree-compiler
编译DTS为DTB
使用dtc
命令编译,基本格式为:
dtc -I dts -O dtb -o output.dtb input.dts
-I dts
:输入格式为DTS源文件。-O dtb
:输出格式为DTB二进制文件。-o output.dtb
:指定输出文件名。
若在内核源码中编译,可使用make dtbs
命令,生成的DTB文件位于arch/<架构>/boot/dts/
目录下。
设备树的使用流程
- 硬件适配:根据硬件手册修改DTS文件,定义外设地址、中断、时钟等属性。
- 编译DTB:通过
dtc
或内核Makefile生成DTB文件。 - 烧录与加载:将DTB文件烧录到设备的启动存储介质(如eMMC、SD卡),或通过U-Boot的
fdt
命令加载(如fdt addr ${dtb_addr} ${dtb_size}
)。 - 内核解析:内核启动时读取DTB文件,通过
of
(Open Firmware)接口解析设备信息,动态创建platform_device
结构,驱动程序通过of_
系列API(如of_platform_populate()
)获取设备属性并初始化硬件。
设备树常用节点与属性说明
以下为常见节点及属性的详细说明,可通过表格形式清晰呈现:
属性/节点名 | 作用 | 示例 |
---|---|---|
#address-cells |
子节点reg属性中地址字段占用的cell数量 | #address-cells = <1>; (32位地址) |
#size-cells |
子节点reg属性中长度字段占用的cell数量 | #size-cells = <1>; (32位长度) |
compatible |
设备兼容性字符串,用于驱动匹配 | compatible = "ti,omap3-uart"; |
reg |
设备寄存器地址和长度 | reg = <0x44e09000 0x1000>; |
interrupts |
中断号、类型和标志 | interrupts = <0 24 4>; (IRQ 24,电平触发) |
clocks |
设备使用的时钟源 | clocks = <&uart1_clocks>; |
status |
设备状态(”okay”/”disabled”) | status = "okay"; |
/chosen |
内核启动时传递的运行时参数(如启动命令行、initrd地址) | chosen { bootargs = "console=ttyS0"; }; |
/memory |
系统内存定义 | memory@0x80000000 { reg = <0x80000000 0x40000000>; }; |
设备树的修改与调试
修改DTS
-
添加设备节点:在父节点(如
/soc
)下添加新节点,定义compatible
、reg
等属性,例如添加I2C传感器:i2c@0x021a0000 { compatible = "ti,omap4-i2c"; reg = <0x021a0000 0x1000>; interrupts = <0 76 0x4>; status = "okay"; tmp102: tmp102@48 { compatible = "ti,tmp102"; reg = <0x48>; status = "okay"; }; };
-
修改设备状态:通过
status = "disabled";"
禁用不需要的设备(如未使用的外设)。
调试方法
- 查看设备树信息:内核启动后,
/proc/device-tree
目录下以节点形式保存解析后的设备树属性,可通过hexdump -C /proc/device-tree/soc/uart@0x02000000/reg
查看寄存器地址。 - 反编译DTB:将DTB反编译为DTS格式,便于检查编译后的内容:
dtc -I dtb -O dts -o output.dts input.dtb
- 设备树绑定文档:内核源码
Documentation/devicetree/bindings/
目录下存放了各设备的属性规范(如serial.txt
定义UART的compatible
字符串),编写DTS时需参考文档确保属性正确。
Linux下的DTS通过树状结构和属性描述硬件信息,实现了硬件与内核代码的分离,大幅提升了系统的可移植性,使用DTS需掌握其基本结构、编译方法、节点属性定义及调试技巧,并结合硬件手册和内核绑定文档进行适配,最终通过内核解析动态生成设备,驱动程序通过OF API获取硬件信息完成初始化。
相关问答FAQs
Q1: DTS和DTB有什么区别?
A: DTS(Device Tree Source)是文本格式的设备树源文件,可读可编辑,用于描述硬件拓扑和设备属性;DTB(Device Tree Blob)是DTS编译后的二进制文件,专供内核解析使用,内核无法直接读取DTS,必须通过dtc
工具将其编译为DTB,并在启动时加载到内存中解析。
Q2: 如何在设备树中添加自定义设备节点?
A: 添加自定义设备节点需遵循以下步骤:
-
确定硬件信息:从硬件手册获取设备的基地址、中断号、时钟频率等关键参数。
-
选择父节点:根据设备连接的总线类型(如I2C、SPI)选择合适的父节点(如
/i2c@0x021a0000
)。 -
定义节点属性:在父节点下添加子节点,设置
compatible
(需与驱动程序匹配)、reg
(设备地址/ID)、interrupts
(中断号)等必要属性。 -
编译与烧录:使用
dtc
编译DTS为DTB,烧录到设备并重启内核,内核会自动解析节点并加载驱动(若compatible
匹配)。
添加一个自定义LED节点:leds { compatible = "gpio-leds"; status = "okay"; led-user { label = "user-led"; gpios = <&gpio1 18 GPIO_ACTIVE_LOW>; default-state = "off"; }; };
原创文章,发布者:酷番叔,转转请注明出处:https://cloud.kd.cn/ask/37160.html