第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

 

 

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