你有沒有想過:同一份Linux內核鏡像,為啥能在不同型號的開發板上跑起來?比如一塊ARM架構的開發板,今天換個顯示屏、明天加個傳感器,內核不用重新編譯就能識別新硬件——這背后,設備樹(Devicetree)功不可沒。
很多嵌入式工程師剛接觸設備樹時,總被“節點”“屬性”“綁定規范”這些術語繞暈。其實設備樹的本質特別簡單:它就是硬件和內核之間的“翻譯官”,把硬件的“長相”和“能力”寫成標準化的文件,讓內核不用“硬編碼”就能讀懂硬件。
今天咱們用“人話+流程圖”拆解設備樹,從“為什么需要它”到“內核怎么用它”,一次講透核心邏輯。
一、先搞懂:沒有設備樹時,Linux有多“難”?
在設備樹出現前,Linux適配硬件靠的是“硬編碼”——把硬件參數(比如串口地址、中斷號)直接寫進內核代碼里。比如要支持一款新開發板,工程師得:
1.在內核中新增一個“板級文件”,寫死該板子的所有硬件配置;
2.編譯內核時選擇對應板子的配置,生成專屬鏡像;
3.要是換個硬件(比如把串口從UART1換成UART2),就得修改代碼、重新編譯。
這種方式的痛點太明顯了:一款硬件對應一個內核鏡像,嵌入式廠商要維護幾十上百個鏡像,成本極高。
而設備樹的出現,徹底解決了這個問題:它把硬件描述從內核中“剝離”出來,做成獨立的DTB文件(設備樹二進制文件)。內核啟動時讀取DTB,就能動態識別硬件——從此實現“一個內核鏡像適配N種硬件”。
二、設備樹的核心:3層結構,像給硬件畫“家譜”
設備樹的結構特別像一棵“硬件家譜”,最核心的是3個概念:節點(Node)、屬性(Property)、路徑(Path)。咱們用一個簡單的例子看懂:
/* 設備樹源碼(DTS文件)示例 *// { //根節點:代表整個硬件系統compatible ="ti,omap3-beagleboard","ti,omap3"; //屬性:告訴內核這是哪款硬件chosen { //子節點:專門存儲運行時配置bootargs ="console=ttyS0,115200"; //屬性:內核命令行(指定串口控制臺)};soc { //子節點:代表SoC(系統級芯片)compatible ="simple-bus"; //屬性:說明這是“簡單內存映射總線”uart0: serial@4806a000{ //子節點:串口設備(@后是基地址)compatible ="ti,omap3-uart"; //屬性:告訴內核用什么驅動reg = <0x4806a0000x1000>; //屬性:地址范圍(基地址+大小)interrupts = <72>; //屬性:中斷號};};};
簡單理解:
?節點:對應一個硬件模塊(如根節點=整個系統、uart0 =串口),用節點名@地址命名(地址可選,用于區分同類型設備);
?屬性:描述硬件的具體參數,格式是鍵=值(值可以是字符串、數字、二進制),比如compatible是“設備兼容性標識”,reg是“內存/ IO地址”;
?路徑:像文件路徑一樣定位節點,比如串口節點的路徑是/soc/uart0。
記住一個關鍵原則:設備樹只描述“硬件有什么、參數是多少”,不包含任何驅動邏輯——驅動靠“匹配設備樹屬性”來關聯硬件。
三、內核怎么用設備樹?3步流程+ 1張圖講透
設備樹的生命周期從“編譯”到“內核使用”,分為3個關鍵階段。咱們結合流程圖,一步步看內核是如何通過設備樹識別并控制硬件的。
第一步:設備樹文件的“變身”(編譯階段)
工程師寫的是DTS文件(設備樹源碼,人類可讀),但內核只能識別DTB文件(設備樹二進制,機器可讀)。這個轉換靠工具dtc(Device Tree Compiler)完成:
dtc -Idts -O dtb -o my_board.dtbmy_board.dts
最終生成的DTB文件,會和內核鏡像一起放在開發板的啟動分區(比如boot分區)。
第二步:啟動時傳遞DTB(引導階段)
開發板上電后,先運行引導程序(如U-Boot),引導程序做兩件關鍵的事:
1.初始化硬件(比如內存、串口);
2.把DTB文件加載到內存的指定地址,然后啟動內核,并告訴內核“DTB在內存的哪里”。
這一步就像:引導程序把“硬件家譜”(DTB)遞給內核,說“這是你要管理的硬件,先看看說明書”。
第三步:內核解析DTB,創建設備(內核初始化階段)
這是最核心的階段,內核通過3個關鍵步驟,把DTB中的“硬件描述”變成可操作的“設備實例”。咱們用流程圖+通俗解釋拆解:

咱們把每個階段掰開揉碎講:
階段1:平臺識別——內核先搞清楚“我跑在哪個板子上”
內核啟動后,首先要確定自己跑在什么硬件上(比如是BeagleBoard還是樹莓派),這一步靠根節點的compatible屬性。
比如根節點的compatible = "ti,omap3-beagleboard", "ti,omap3",這個屬性是“從具體到通用”的列表:
?第一個值“ti,omap3-beagleboard”:精確匹配“TI的omap3系列BeagleBoard開發板”;
?第二個值“ti,omap3”:兼容“TI的omap3系列所有板子”。
內核會遍歷自己的“平臺描述庫”,找到和compatible最匹配的項——比如找到BeagleBoard的初始化邏輯,就執行對應的硬件初始化(如設置時鐘、電源)。
階段2:運行時配置——內核獲取“啟動參數”
設備樹中的/chosen節點是專門給內核傳參數的“通道”,最常用的是bootargs屬性(內核命令行)。
比如bootargs = "console=ttyS0,115200 loglevel=8",意思是:
?console=ttyS0,115200:把串口0(UART0)作為控制臺,波特率115200;
?loglevel=8:顯示所有級別的內核日志(方便調試)。
內核會解析這些參數,完成基礎配置——比如初始化串口控制臺,讓開發者能通過串口看到內核啟動日志。
階段3:創建設備——內核把“硬件描述”變成“可操作設備”
這是設備樹的最終目的:內核根據DTB中的節點,動態創建“設備實例”,再讓驅動去匹配這些設備。
關鍵函數是of_platform_populate(),它的邏輯很簡單:
1.從指定節點(默認是根節點)開始,遍歷所有子節點;
2.對每個包含compatible屬性的節點,創建一個“平臺設備”(platform_device);
3.驅動通過of_match_table(設備樹匹配表),根據compatible屬性找到對應的設備,完成“驅動-設備”綁定。
舉個例子:串口節點uart0的compatible = "ti,omap3-uart",內核會:
?創建一個名為serial@4806a000的平臺設備;
?串口驅動的of_match_table中,正好有“ti,omap3-uart”這一項,于是驅動和設備綁定;
?綁定后,驅動就能通過設備樹中的reg(地址)、interrupts(中斷號),控制串口硬件收發數據。
四、記住3個關鍵問題,避免踩坑
1.設備樹能替代驅動嗎?
不能!設備樹只描述硬件參數,驅動才是控制硬件的“大腦”。比如設備樹告訴內核“串口在0x4806a000地址”,但怎么發數據、收數據,還得靠串口驅動實現。
2.compatible屬性寫錯了會怎樣?
驅動找不到設備!比如把“ti,omap3-uart”寫成“ti,omap4-uart”,串口驅動的匹配表中沒有這個值,設備就會處于“未綁定”狀態,無法使用。
3.DTB文件放錯位置會怎樣?
內核啟動失敗!引導程序如果沒加載DTB,或者內核沒找到DTB,會報“Cannot find device tree”錯誤,然后卡住——因為內核不知道自己要管理什么硬件。
五、總結:設備樹的本質是“硬件標準化描述”
其實設備樹的核心價值,就在于“標準化”:
?對硬件廠商:按規范寫DTS,不用改內核代碼;
?對內核開發者:按規范寫驅動,不用適配每款硬件;
?對嵌入式工程師:換硬件只換DTB,不用重新編譯內核。
記住一句話:設備樹是“硬件的說明書”,驅動是“讀懂說明書并操作硬件的人”——兩者配合,才能讓Linux在千變萬化的硬件上跑起來。
如果看完還是有點暈,建議找一款簡單的開發板,打開它的DTS文件,對照本文的流程逐行看:根節點的compatible、chosen節點的bootargs、外設節點的reg和interrupts——慢慢就會發現,設備樹其實沒那么復雜~
-
嵌入式
+關注
關注
5202文章
20523瀏覽量
335531 -
Linux
+關注
關注
88文章
11782瀏覽量
219252 -
設備樹
+關注
關注
0文章
45瀏覽量
3587
發布評論請先 登錄
水表界的“翻譯官”:讓CCLinkIE和Modbus TCP“無障礙聊天”!
一張圖看懂USB充電協議
EtherCAT轉Profinet網關:紡織業設備互聯的“翻譯官”
CAN收發器:總線信號的“翻譯官”
工業網關:連接工業現場與數字世界的“翻譯官”
逆變器是什么?廣州郵科如何用“電力翻譯官”守護通信命脈
智能“翻譯官”:MODBUS轉PROFIBUS網關,解鎖攪拌站無人裝載新紀元
"網關”成頂流!PROFINET轉CC-LINK,汽車產線的“翻譯官”出圈記
Linux設備樹到底是啥?一張圖看懂硬件適配的「翻譯官」
評論