設備樹是一種數據結構,包含多個節點,用于描述硬件設備及其配置信息,它通常用于嵌入式系統中,尤其是在Linux操作系統中,幫助操作系統識別和管理硬件資源,設備樹不是代碼,而是一種用數據描述硬件信息的方式
設備樹通常是以一種樹形結構來表示硬件各個部分的層次關系
設備樹(Device Tree),將這個詞分開就是“設備”和“樹”,描述設備樹的文件叫做 DTS(Device Tree Source),這個 DTS 文件采用樹形結構描述板級設備,也就是開發板上的設備信息,比如CPU 數量、 內存基地址、IIC 接口上接了哪些設備、SPI 接口上接了哪些設備等等
樹的主干就是系統總線,IIC 控制器、GPIO 控制器、SPI 控制器等都是接
到系統主線上的分支。IIC 控制器有分為 IIC1 和 IIC2 兩種,其中 IIC1 上接了 FT5206 和 AT24C02這兩個 IIC 設備,IIC2 上只接了 MPU6050 這個設備。DTS 文件的主要功能就是按圖所示的結構來描述板子上的設備信息,DTS 文件描述設備信息是有相應的語法規則要求的
設備樹的結構:
設備樹的結構采用類似樹形結構,包含多個節點,每個節點代表一個硬件設備或設備的某些特性。每個節點可以包含一些屬性,描述該設備的詳細信息。
例如:
節點:代表硬件設備,比如CPU、內存、串口、存儲、網絡接口等。
屬性:描述該設備的特性或配置信息,如設備的地址、類型、IRQ(中斷請求)、驅動程序等。
設備樹的語法:
設備樹通常使用一種簡潔的描述語言(Device Tree Source,簡稱DTS)來表示。DTS文件是純文本文件,后綴通常為.dts,它們被編譯成二進制的設備樹二進制格式(Device Tree Blob,簡稱DTB),該二進制文件會被操作系統加載和使用。
簡單的設備樹示例:
為什么要有設備樹?
1、硬件和內核解耦
以前,硬件信息(比如CPU型號、內存地址、外設位置)直接寫在內核代碼里。換一塊硬件板子,就得重新改內核、重新編譯。設備樹把硬件信息抽離出來,變成一個單獨的文件(.dts),內核只需讀取這個文件就能適配不同硬件。
2、支持多種硬件平臺
比如樹莓派3和樹莓派4的硬件不同,但可以用同一個內核+不同的設備樹文件啟動,內核無需為每塊板子單獨寫代碼。
3、方便維護
廠商更新硬件時,只需修改設備樹文件(描述硬件),不用動內核代碼(驅動邏輯)。
設備樹的作用
1、告訴內核硬件在哪里
比如:“CPU是四核的”、“內存從地址0x80000000開始”、“I2C控制器在地址0x40005000,連著觸摸屏和溫度傳感器”。
2、描述硬件之間的關系
比如:“USB控制器掛載在PCI總線的第3個插槽”、“GPIO引腳12連接了LED燈”。
3、配置硬件參數
比如:“屏幕分辨率是1920x1080”、“以太網MAC地址是00:11:22:33:44:55”。
假設嵌入式板子上有一個LED燈,連接在GPIO的第5個引腳,沒有設備樹時,需要在驅動代碼里硬編碼gpio5,換到gpio6就得改代碼、重新編譯內核;
有設備樹時,設備樹文件里寫gpios = <&gpio 5 0>; 驅動代碼只需讀取設備樹中的gpios屬性,自動適配到gpio5。換引腳時只需改設備樹,內核代碼不用動
設備樹就像硬件的“身份證”+“說明書”,讓內核能動態識別硬件,而不是把硬件信息寫死在內核里。它的核心作用:解耦硬件配置和內核代碼,讓Linux能靈活適配不同硬件
示例代碼 alphaled 節點 alphaled { #address-cells = <1>; #size-cells = <1>; compatible = "atkalpha-led"; status = "okay"; reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */ 0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */ 0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */ 0X0209C000 0X04 /* GPIO1_DR_BASE */ 0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */ };
這個節點描述的是一個LED燈的硬件控制信息
它需要告訴內核:“LED燈的位置在哪里?如何配置硬件寄存器才能控制它亮滅?” 就像給內核一張 “LED操作手冊” ,說明控制這個LED需要操作哪些寄存器(開關)。
#address-cells = <1>;
#size-cells = <1>;
作用:指定“地址”和“長度”用幾個數字表示(單位是32位,即4字節)。
這里都用1個數字。類比:假設你要描述一本書的位置:地址 = 書架編號(1個數字)長度 = 占用的格子數(1個數字)為何重要:后續的 reg 屬性依賴這兩個值來解析地址和長度。
compatible = "atkalpha-led";
作用:匹配內核中的驅動程序!內核會尋找支持 "atkalpha-led"
的驅動來操作這個設備。類比:告訴內核:“這個LED要用說明書編號為‘atkalpha-led’的驅動來操作”。關鍵點:驅動代碼里必須有對應的兼容性標識,否則設備無法被識別!
status = "okay";
作用:啟用這個設備。如果設為 "disabled",內核會忽略它。類比:給設備通電(okay)或斷電(disabled)。
reg = < 0X020C406C 0X04 // CCM_CCGR1(時鐘控制寄存器)0X020E0068 0X04 // SW_MUX_GPIO1_IO03(引腳復用控制)0X020E02F4 0X04 // SW_PAD_GPIO1_IO03(引腳電氣屬性配置)0X0209C000 0X04 // GPIO1_DR(GPIO數據寄存器)0X0209C004 0X04 // GPIO1_GDIR(GPIO方向寄存器)
>;
作用:列出控制這個LED所需的所有寄存器地址和長度(單位:字節)。逐項解釋:CCM_CCGR1 (0X020C406C)
控制時鐘的開關。LED所在的GPIO模塊需要時鐘才能工作,類似“總電源開關”。SW_MUX_GPIO1_IO03 (0X020E0068)
配置引腳功能。比如將某個引腳設置為“GPIO模式”而非其他功能(如UART)。SW_PAD_GPIO1_IO03 (0X020E02F4)
配置引腳的電氣屬性,如上拉/下拉電阻、驅動強度等。GPIO1_DR (0X0209C000)
GPIO數據寄存器。寫0或1控制引腳輸出電平(低電平亮/滅,高電平反之)。GPIO1_GDIR (0X0209C004)
GPIO方向寄存器。設置引腳為輸入(0)或輸出(1),這里需設為輸出模式。類比:你要控制一臺電視,需要知道:電源開關位置(CCM_CCGR1)遙控器配對方式(SW_MUX)音量默認設置(SW_PAD)換臺按鈕(GPIO_DR)按鈕功能分配(GPIO_GDIR)為什么需要這么多寄存器?
硬件控制是精細活:
在嵌入式系統中,控制一個LED可能需要多個步驟:開時鐘:GPIO模塊需要時鐘信號才能工作。配引腳功能:確保這個引腳被用作GPIO,而不是其他功能(比如串口)。配電氣屬性:避免信號干擾,確保穩定。設GPIO方向:輸出模式才能控制電平。寫數據寄存器:輸出高/低電平控制LED亮滅。
通常,Linux內核提供了更簡潔的GPIO控制方法,比如:
led {
compatible = “gpio-leds”;
led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; // 直接指定GPIO引腳 };
內核會自動處理時鐘、復用等配置,無需手動寫寄存器地址。
文檔寫法是**“底層直操作”**,通常用于特定需求或學習目的。