第4篇 zephyr 設備樹(devicetree)

目錄

摘要

1 zephyr中設備樹編譯流程 

2 開發設備樹需要了解的文件

2.1 dts文件

2.2 dtsi文件

2.3 bindings文件

2. 4 設備樹頭文件devicetree.h

2.4.1 devicetree.h說明

2.4.2 devicetree.h使用示例

3 nrf52840dk_nrf52840設備樹

3.1 主要包括文件

3.2 bindings文件作用

3.2.1 nrf52840dk_nrf52840設備樹片段

3.2.2 設備樹需要的bindings片段

3.2.3 bindings文件命名規則

4 在zephyr工程樹外應用目錄下存放設備樹bindings存放

5 參考鏈接


本學筆記基於zephyr 工程版本 2.2.99,主機環境爲ubuntu18.04,開發平臺 nrf52840dk_nrf52840

摘要

    zephyr類似於Linux通過設備樹來管理硬件,但是與Linux不同,zephyr不是直接使用DTB(設備樹編譯後的二進制文件),因爲運行zephyr的硬件大部分是資源受限的嵌入式系統,很多MCU的資源都不夠支撐運行一個DTB框架,所以zephyr工程直接將設備樹通過腳本處理成c語言頭文件。zephyr裏面供應用程序調用的設備樹API都是一些宏定義或宏函數。前面提到的設備樹腳本生成的C語言頭文件用於支撐設備樹API的實現。

     本筆記不介紹設備樹的基本語法,只介紹設備樹在zephyr工程中如何使用。所以不懂設備樹基本語法的童鞋閱讀本文將會比較吃力,在這裏建議讀者先自行學習設備樹的基本語法。

1 zephyr中設備樹編譯流程 

   

2 開發設備樹需要了解的文件

2.1 dts文件

    dts文件是設備樹源文件,類似於c語言的.c文件。多用於描述一個完整的板級硬件。

2.2 dtsi文件

    dtsi是設備樹頭文件,類似c語言的.h文件。多用於描述一個硬件模塊,供板級dts或soc級dtsi引用。所以可以把一個固定硬件的設備信息放到.dtsi文件中,比如一個soc,一個型號的soc他的硬件資源是不會變的,所以可以統一寫在一個dtsi中供板級dts文件引用。再一個soc模塊會包括cpu IP核和uart ip等。那麼可以把一個cpu的的硬件資源或者uart資源可以寫在dtsi文件中,供soc的dtsi引用。一般情況下,一個板級文件可以定義在多個dts文件中,在設備樹編譯階段,會由腳本合併多個dts文件。

2.3 bindings文件

       在zephyr工程bingings的文件類型是.yaml文件形式存在,bindings顧名思義,綁定或者膠水的意思。他作爲c語言和設備樹之前的橋樑存在。bingdings可以解釋設備樹中設備節點的屬性代表什麼意思。在zephyr工程中,前文摘要中提到的將設備樹轉換爲c語言頭文件,就是根據bindings文件中定義的規則來轉換的。後面會以nrf52840的設備樹示例來說明。

      yaml文件介紹即使用可以參考:   https://blog.csdn.net/huohongpeng/article/details/105551920

2. 4 設備樹頭文件devicetree.h

2.4.1 devicetree.h說明

   在zephyr中使用設備樹,只要包括一個devicetree.h頭文件就可以了。這裏面主要定義了設備樹供c語言調用的API。前文提到使用這些api需要設備樹轉換的頭文件來支持,所以這裏面還包括由腳本將設備樹轉換成的頭文件。後面舉例說明。

   devicetree.h路徑 zephyr/include/devicetree.h

/*
    省略500字....
*/
#ifndef DEVICETREE_H
#define DEVICETREE_H

#include <devicetree_unfixed.h>
#include <devicetree_legacy_unfixed.h>
#include <devicetree_fixups.h>

#include <sys/util.h>

/*
    省略500字....
*/
#define DT_NODELABEL(label) DT_CAT(DT_N_NODELABEL_, label)

#define DT_CAT(node_id, prop_suffix) node_id##prop_suffix

#define DT_PROP(node_id, prop) DT_CAT(node_id, _P_##prop)

#define DT_LABEL(node_id) DT_PROP(node_id, label)
/*
    省略數不清的字....
*/

#endif 

    可以看到devicetree.h 中包含了   devicetree_unfixed.h, devicetree_legacy_unfixed.h, devicetree_fixups.h,這三個文件是在編譯時產生的,這三個文件中包含的都是一系列宏定義,用於支撐設備樹API的實現,這三個文件在工程編譯後包含在:

build/zephyr/include/generated/路徑下面。這三個文件每個板級工程都是不一樣的

devicetree_unfixed.h :這個文件是根據工程中的設備樹生成的。把設備樹的節點信息轉換爲宏定義等,供設備樹API使用。

devicetree_legacy_unfixed.h :   這個文件也是根據工程中的設備樹生成的。也是把節點信息轉換爲宏定義,這裏面的宏定義不是供設備樹API使用,而是直接供C語言使用,與設備樹API+devicetree_unfixed.h是一樣的效果。但是官方發佈聲明稱,目前已經不再推薦使用此方式,後期的zephyr版本中會棄用。這裏在新的zephyr應用中建議不要在使用這種方式。這種方式存在的主要原因是很多已經存在的老的驅動源碼中使用了這種方式。

devicetree_fixups.h :這個文件是devicetree_legacy_unfixed.h 中的別名,比如

#define DT_UART_0_NAME			DT_NORDIC_NRF_UART_UART_0_LABEL

 在zephyr的uart驅動程序中,不同芯片的驅動有相同的部分,通過引用別名隱藏硬件細節。

2.4.2 devicetree.h使用示例

1 nrf52840dk_nrf52840.dts設備樹片段

	leds {
		compatible = "gpio-leds";
		led0: led_0 {
			gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
			label = "Green LED 0";
		};
		led1: led_1 {
			gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
			label = "Green LED 1";
		};
		led2: led_2 {
			gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;
			label = "Green LED 2";
		};
		led3: led_3 {
			gpios = <&gpio0 16 GPIO_ACTIVE_LOW>;
			label = "Green LED 3";
		};
	};

2 devicetree.h定義API片段

#define DT_NODELABEL(label) DT_CAT(DT_N_NODELABEL_, label)

#define DT_CAT(node_id, prop_suffix) node_id##prop_suffix

#define DT_PROP(node_id, prop) DT_CAT(node_id, _P_##prop)

#define DT_LABEL(node_id) DT_PROP(node_id, label)

3 devicetree_unfixed.h 宏定義片段

#define DT_N_NODELABEL_led0 DT_N_S_leds_S_led_0
#define DT_N_S_leds_S_led_0_P_label "Green LED 0"

4 在c語言中可以使用如下語句讀取led_0的lable屬性

a. 通過DT_NODELABEL() API標籤找到led_0節點,我們這裏命名爲LED0_NODE_ID

#define LED0_NODE_ID    DT_NODELABEL(led0)

根據設備樹API定義,這個DT_NODELABEL(led0)展開以後是DT_N_NODELABEL_led0,可以在devicetree_unfixed.h文件中找到。

b.通過DT_NODELABEL() API找到led_0節點內部的label屬性,我們這裏命名爲LED0_NODE_LABLE

#define LED0_NODE_LABLE    DT_LABEL(LED0_NODE_ID)

根據設備樹API定義,這個DT_LABEL(LED0_NODE_ID)展開:

步驟1:替換LED0_NODE_ID,變爲DT_LABEL( DT_N_S_leds_S_led_0);

步驟2:展開DT_LABEL,變爲DT_PROP(DT_N_S_leds_S_led_0, label);

步驟3:展開DT_PROP,變爲DT_CAT(DT_N_S_leds_S_led_0, _P_label);

步驟4:展開DT_CAT, 變爲DT_N_S_leds_S_led_0_P_label

 在devicetree_unfixed.h中DT_N_S_leds_S_led_0_P_label被定義爲"Green LED 0",與設備樹中label能對應上,這是C語言引用設備樹過程。

3 nrf52840dk_nrf52840設備樹

3.1 主要包括文件

    zephyr/boards/arm/nrf52840dk_nrf52840/nrf52840dk_nrf52840.dts

        zephyr/dts/arm/nordic/nrf52840_qiaa.dtsi

            zephyr/dts/arm/nordic/nrf52840.dtsi

                 zephyr/dts/arm/nordic/nrf5_common.dtsi

                 zephyr/dts/arm/armv7-m.dtsi

                    zephyr/dts/common/skeleton.dtsi

所以nrf52840dk_nrf52840的設備樹主要又以上文件生成,當然還可以通過.overlay文件覆蓋以上文件的節點定義,這裏不再細說。

3.2 bindings文件作用

3.2.1 nrf52840dk_nrf52840設備樹片段

/ {
    soc {
		gpio0: gpio@50000000 {
			compatible = "nordic,nrf-gpio";
			gpio-controller;
			reg = <0x50000000 0x200
			       0x50000500 0x300>;
			#gpio-cells = <2>;
			label = "GPIO_0";
			status = "disabled";
		};
    }

    leds {
	    compatible = "gpio-leds";
	    led0: led_0 {
		    gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
		    label = "Green LED 0";
	    };
		led1: led_1 {
			gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
			label = "Green LED 1";
		};
		led2: led_2 {
			gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;
			label = "Green LED 2";
		};
		led3: led_3 {
			gpios = <&gpio0 16 GPIO_ACTIVE_LOW>;
			label = "Green LED 3";
		};
	};
	/* These aliases are provided for compatibility with samples */
	aliases {
		led0 = &led0;
		led1 = &led1;
		led2 = &led2;
		led3 = &led3;
		pwm-led0 = &pwm_led0;
		sw0 = &button0;
		sw1 = &button1;
		sw2 = &button2;
		sw3 = &button3;
	};
}

&gpio0 {
	status = "okay";
};

3.2.2 設備樹需要的bindings片段

  yaml文件介紹即使用可以參考:   https://blog.csdn.net/huohongpeng/article/details/105551920

gpio-leds.yaml

description: GPIO LEDs parent node

compatible: "gpio-leds"

child-binding:
    description: GPIO LED child node
    properties:
       gpios:
          type: phandle-array
          required: true
       label:
          required: true
          type: string
          description: Human readable string describing the device (used by Zephyr for API name)

nordic,nrf-gpio.yaml

description: NRF5 GPIO node

compatible: "nordic,nrf-gpio"

include: [gpio-controller.yaml, base.yaml]

properties:
    reg:
      required: true

    label:
      required: true

    "#gpio-cells":
      const: 2

gpio-cells:
  - pin
  - flags

gpio-controller.yaml

properties:
    "gpio-controller":
      type: boolean
      required: true
      description: Convey's this node is a GPIO controller
    "#gpio-cells":
      type: int
      required: true
      description: Number of items to expect in a GPIO specifier
    ngpios:
      type: int
      required: false
      default: 32
      description: Number of gpios supported

base.yaml

文件比較長不在列出,可以在源碼zephyr/dts/bindings/base/ 路徑下找到。

3.2.3 bindings文件命名規則

    編譯系統在編譯設備樹時通過設備設備樹節點下的compatible的屬性找到設備節點對應的bindings文件,比如compatible = "gpio-leds"; 那麼編譯設備樹時編譯系統一般就會去dts/bindings路徑下找到gpio-leds.yaml文件。在gpio-leds.yaml定義了

label屬性示例:

label:
    required: true
    type: string
    description: Human readable string describing the device (used by Zephyr for API name)

對label屬性的示例說明:

required:可以是false,也可以是ture。當是ture時,定義的設備樹節點必須包括label這個屬性,如果是false,則label是可選的屬性。

type:定義屬性的類型,這裏定義了label是sting類型,還可以 <string | int | boolean | array | uint8-array | string-array |phandle | phandles | phandle-array | path | compound>類型。

description:對這個屬性的一個說明。

gpios屬性示例:

       gpios:
          type: phandle-array
          required: true

phandle-array這類型的屬性是比較複雜的,我這裏結合示例說明,在leds設備樹中有如下定義:

gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;

要想知道<>裏面的每個字段代表什麼含義,就需要知道gpios的屬性含義,而想知道這種帶xxxxs的屬性的含義,就需要找到

#xxxx-cells和xxxx-cells的定義,注意這裏沒有s,比如gpios就需要找到#gpio-cells和gpio-cells。他倆的定義一般根據gpios的第一個元素去找到,對於phandle-array的第一個元素是標籤索引(handle類型),那麼就先找到gpio0是誰的標籤。去設備樹文件中找到gpio0節點,找到以後去這個節點中找的compatible屬性,根據compatible = "nordic,nrf-gpio";去找到對應的bindings文件,這個示例的bindings文件是nordic,nrf-gpio.yaml。然後找到#gpio-cells和gpio-cells的說明。

#gpio-cells:代表gpios中除了&gpio0 ,之外的其他類型個數,其他類型爲int類型。

比如#gpio-cells = 3,那麼gpios的格式就是<標籤值  整數1 整數2 整數3>

比如#gpio-cells = 2,那麼gpios的格式就是<標籤值  整數1 整數2>

要想在c語言中引用整數1和整數2,就要知道整數1和整數2的含義。

而這裏面的整數1和整數2代表的含義又gpio-cells進行說明:

gpio-cells:
  - pin        
  - flags

這裏整數1是pin,整數2是flags。

在c語言中可以使用DT_PHA_BY_IDX(node_id, pha, idx, cell) 這個API去引用,比如

DT_PHA_BY_IDX(DT_NODELABEL(led0), gpios, 0, pin 這個返回的結果就是15。

4 在zephyr工程樹外應用目錄下存放設備樹bindings存放

 正常情況下,我們可以將bindings文件存放在zephyr/dts/bindings路徑下,然後編譯系統這可以找到這些bindings文件,但是 zephyr工程一直建議開發者在開發時不改變zephyr源碼樹,那麼我們將自己定義的bindings放在哪裏呢?

    如果我們需要自己定義bindings文件,我們可以在應用路徑下創建dts/bindings目錄,然後把自己定義的bindings文件放入裏面,編譯系統可以找到它。

   官方介紹,還可以通過DTS_ROOT指定dts設備樹路徑,在編譯時加上"-DDTS_ROOT=xxx/xxx",但是我去嘗試用這種方式去定義自己的bindings文件,並沒有成功。

5 參考鏈接

https://docs.zephyrproject.org/latest/guides/dts/index.html

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章