用Device tree overlay掌控Beaglebone Black的硬件資源

簡介

device tree是linux 3.x開始使用的加載硬件資源的方式,這裏說的硬件資源既包括片上的諸如GPIO、PWM、I2C、ADC等資源,也包括外部拓展的如FLASH、LCD等。

device tree主要作用是將某個硬件外設與相應的驅動程序進行綁定,關於它在實際使用中的地位可以參考我的日誌《嵌入式linux如何操作硬件》。使用時首先需要編寫一個.dts文件(device tree source),在文件中說明我要設置的硬件和它的各種屬性,然後編譯這個.dts文件生成對應的二進制文件.dtb,系統啓動時就會加載這個device tree並配置各種硬件資源。

實際上Beaglebone Black自帶系統中/boot目錄下已經包含了一些編譯好的.dtb文件,從文件名來看似乎每個.dtb文件都能配置一款beagleboard.org的開發板,其中有一個叫做am335x-boneblack.dtb的文件,沒猜錯的話應當負責了Beaglebone black的缺省硬件配置。但因爲已經編譯成了二進制文件,所以我們無法讀取其內容。

那麼我們如果想要自己修改某些功能改怎麼辦呢?我們肯定不能重新編譯一個am335x-boneblack.dtb代替原來的文件,那樣會瘋掉的。不過我們可以使用device tree overlay來動態重定義某些功能。device tree overlay與device tree類似,同樣是編寫一個.dts文件,編譯成.dtbo文件(末尾的o應該代表overlay)。不同的是我們不必把它放到/boot目錄中去,它也不必在啓動時加載,而可以在需要時隨時進行動態加載。另外device tree overlay的.dts文件跟device tree的.dts文件格式還是有一點區別的,下面要介紹的是device tree overlay的.dts。接下來我們上機操作一下。

編寫.dts文件

用ssh連接好Beaglebone black以後,我們先來找找Angstrom系統自帶的.dts文件,看看它們長什麼樣子。用下面的命令搜索一下dts結尾的文件

  1. # find / -name *dts  
  1. /lib/firmware/cape-bone-dvi-00A0.dts  
  2. /lib/firmware/bone_pwm_P8_45-00A0.dts  
  3. /lib/firmware/BB-SPI1A1-00A0.dts  
  4. /lib/firmware/BB-ADC-00A0.dts  
  5. /lib/firmware/BB-I2C1A1-00A0.dts  
  6. /lib/firmware/BB-BONE-SERL-01-00A1.dts  
  7. /lib/firmware/cape-bone-dvi-00A2.dts  
  8. /lib/firmware/bone_pwm_P8_13-00A0.dts  
  9. /lib/firmware/cape-bone-hexy-00A0.dts  
  10. /lib/firmware/BB-BONE-LCD7-01-00A2.dts  
  11. ...  

我們發現它們都在同一個目錄內,/lib/firmware,事實上系統自帶的dts文件確實全部都在這個目錄中,從文件名上我們會發現這裏幾乎包含了所有Beaglebone硬件資源的overlay,也包含了一些官方硬件外設(如lcd屏等,它們管自己的外設叫做cape)的overlay,因此以後有需要就可以直接到這裏找了。下面隨便打開其中一個看看(BB-UART1-00A0.dts)

  1. /* 
  2.  * Copyright (C) 2013 CircuitCo 
  3.  * 
  4.  * Virtual cape for UART1 on connector pins P9.24 P9.26 
  5.  * 
  6.  * This program is free software; you can redistribute it and/or modify 
  7.  * it under the terms of the GNU General Public License version 2 as 
  8.  * published by the Free Software Foundation. 
  9.  */  
  10. /dts-v1/;  
  11. /plugin/;  
  12.   
  13.   
  14. / {  
  15.     compatible = "ti,beaglebone""ti,beaglebone-black";  
  16.   
  17.   
  18.         /* identification */  
  19.         part-number = "BB-UART1";  
  20.         version = "00A0";  
  21.   
  22.   
  23.         /* state the resources this cape uses */  
  24.         exclusive-use =  
  25.                 /* the pin header uses */  
  26.                 "P9.24",        /* uart1_txd */  
  27.                 "P9.26",        /* uart1_rxd */  
  28.                 /* the hardware ip uses */  
  29.                 "uart1";  
  30.   
  31.   
  32.         fragment@0 {  
  33.                 target = <&am33xx_pinmux>;  
  34.                 __overlay__ {  
  35.                         bb_uart1_pins: pinmux_bb_uart1_pins {  
  36.                                 pinctrl-single,pins = <  
  37.                                         0x184 0x20 /* P9.24 uart1_txd.uart1_txd MODE0 OUTPUT (TX) */  
  38.                                         0x180 0x20 /* P9.26 uart1_rxd.uart1_rxd MODE0 INPUT (RX) */  
  39.                                 >;  
  40.                         };  
  41.                 };  
  42.         };  
  43.   
  44.   
  45.         fragment@1 {  
  46.                 target = <&uart2>; /* really uart1 */  
  47.                 __overlay__ {  
  48.                         status = "okay";  
  49.                         pinctrl-names = "default";  
  50.                         pinctrl-0 = <&bb_uart1_pins>;  
  51.                 };  
  52.         };  
  53. };  

它的語法跟c語言有點類似。我先從中抽掉不重要的內容,把它寫成下面的僞代碼

  1. / {  
  2.         fragment@0 {  
  3.                 target = <&am33xx_pinmux>;  
  4.                 __overlay__ {  
  5.                         bb_uart1_pins: pinmux_bb_uart1_pins {  
  6.                                 pinctrl-single,pins = <  
  7.                                         0x184 0x20 /* P9.24 uart1_txd.uart1_txd MODE0 OUTPUT (TX) */  
  8.                                         0x180 0x20 /* P9.26 uart1_rxd.uart1_rxd MODE0 INPUT (RX) */  
  9.                                 >;  
  10.                         };  
  11.                 };  
  12.         };  
  13.   
  14.   
  15.         fragment@1 {  
  16.                 target = <&uart2>;  
  17.                 __overlay__ {  
  18.                         status = "okay";  
  19.                         pinctrl-names = "default";  
  20.                         pinctrl-0 = <&bb_uart1_pins>;  
  21.                 };  
  22.         };  
  23. };  

從這裏就能看出.dts文件的結構了——是一個樹形結構。第一行的/代表根,下面的fragment@0和fragment@1是其兩個分支節點。每個fragment節點下面又各有一個__overlay__節點(這些節點的名字都是固定的)。每個fragment節點下面相鄰的target說明這個節點要修改的對象,在__overlay__節點下面的內容闡明瞭要修改的屬性。

具體來說,am33xx_pinmux可以定義芯片功能複用引腳的具體功能,它使用了pinctrl-single,pins這個驅動,其中第一項0x184代表要修改的引腳,第二項0x20代表要修改成哪個功能。這裏把P9.24和P9.26兩個引腳定義成了uart1的TX和RX。uart2這個target則使能了uart1(這個uart2實際上對應的是硬件的uart1)。

如果把樹形結構什麼的都忽略掉,就會發現其實它實現了我之前用寄存器乾的事:定義引腳功能,然後使能串口。

瞭解了dts文件的基本框架,我們再把之前丟掉的細節拿回來說明一下。(這些細節有些是非常重要的,實際使用中一定不要隨意丟掉!)

首先這兩行說明了dts的版本號,聲明瞭這個文件的內容是一個plugin

  1. /dts-v1/;  
  2. /plugin/;  

根節點下面的一行說明了它的適用平臺,這個是必須要寫的。

  1. compatible = "ti,beaglebone""ti,beaglebone-black";  

接下來的部分說明了這個device tree overlay的名字和版本號(版本號似乎只能是00A0)

  1. /* identification */  
  2. part-number = "BB-UART1";  
  3. version = "00A0";  

再下面的部分說明了要使用的引腳和硬件設備

  1. /* state the resources this cape uses */  
  2.         exclusive-use =  
  3.                 /* the pin header uses */  
  4.                 "P9.24",        /* uart1_txd */  
  5.                 "P9.26",        /* uart1_rxd */  
  6.                 /* the hardware ip uses */  
  7.                 "uart1";  

接下來就是device tree overlay的具體內容,前面已經簡單解釋過了,但似乎還是看不太明白,自己也寫不出來。實際上我們並不需要自己從頭開始寫,因爲在系統/lib/firmware目錄中已經自帶了很多.dts文件,我們只需要在它們的基礎上進行修改就行了。需要提示一點,在.dts文件裏我們經常會看到target = <&ocp>,這裏的ocp是on chip peripherals的縮寫,我猜想可能是用來描述連接到芯片的其他外設的(如按鍵、lcd等)。

另外,part-number = "BB-UART1"這句中的BB-UART1是我們下面加載這個device tree要用的名字。

編譯.dts文件

寫好.dts文件以後需要用dtc編譯器編譯一下,生成.dtbo文件才能使用。

假設我們寫好了一個名爲ADAFRUIT-SPI0-00A0.dts的文件,編譯指令如下

  1. # dtc -O dtb -o ADAFRUIT-SPI0-00A0.dtbo -b 0 -@ ADAFRUIT-SPI0-00A0.dts  

然後就會生成ADAFRUIT-SPI0-00A0.dtbo文件。下面解釋一下各個參數

-O dtb 聲明輸出格式爲dtb文件

-o 輸出文件名

-b 設置啓動CPU

-@ (我不太清楚這項是幹嘛的,似乎是overlay專有的一項)

注意文件的命名,一定是“程序名-版本號.dtbo(.dts)”的形式。

編譯完成以後,一定要把.dtbo文件放到/lib/firmware目錄下才能使用

  1. # cp ADAFRUIT-SPI0-00A0.dtbo /lib/firmware  

DT overlay的使用 (加載和卸載)

所有已經加載的overlay列表都在/sys/devices/bone_capemgr.*/slots這個文件中。(bone_capemgr.*中的*號實際是一個數字,但是每次系統啓動時這個數字可能會變化,所以我們用通配符*代替。)我們打開這個文件看一看
  1. # cat /sys/devices/bone_capemgr.*/slots  
  1. 0: 54:PF---   
  2. 1: 55:PF---   
  3. 2: 56:PF---   
  4. 3: 57:PF---   
  5. 4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G  
  6. 5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI  
我們看到系統已經自動加載了兩個overlay,eMMC和HDMI。下面我們把之前講解的BB-UART1-00A0.dtbo加載一下,方法是
  1. # echo BB-UART1 > /sys/devices/bone_capemgr.*/slots  
然後我們再打開slots文件看看有什麼變化
  1. 0: 54:PF---   
  2. 1: 55:PF---   
  3. 2: 56:PF---   
  4. 3: 57:PF---   
  5. 4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G  
  6. 5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI  
  7. 6: ff:P-O-L Override Board Name,00A0,Override Manuf,BB-UART1  
會發現多了一項,說明加載成功了,下面就可以使用外設了。

外設使用完畢以後,如何卸載呢?一種方法是重啓系統,另一種是

  1. # echo -6 > /sys/devices/bone_capemgr.*/slots  

但是在最近的Angstrom系統中,用這種方法會導致kernel panic,然後ssh會斷開,相信今後這個問題應該會解決的。


來源:http://blog.chinaunix.net/uid-17188120-id-4076230.html

發佈了17 篇原創文章 · 獲贊 14 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章