天天看点

NCS-OS系列3 :devicetree介绍NCS-OS系列3 :devicetree介绍前言一、devicetree概念二、语法和结构三、Unit address示例四、重要属性介绍五、Aliases and chosen nodes六、输入输出文件

NCS-OS系列3 :devicetree介绍

文章目录

  • NCS-OS系列3 :devicetree介绍
  • 前言
  • 一、devicetree概念
  • 二、语法和结构
  • 三、Unit address示例
    • 3.1 内存映射外设
    • 3.2 I2C外设
    • 3.3 SPI外设
    • 3.4 存储器
    • 3.5 内存映射flash
    • 3.6 内存分区
  • 四、重要属性介绍
    • 4.1 compatible
    • 4.2 label
    • 4.3 reg
    • 4.4 status
    • 4.5 interrupts
  • 五、Aliases and chosen nodes
  • 六、输入输出文件
    • 6.1 输入文件
    • 6.2 脚本和工具
    • 6.3 输出文件
    • 6.4 写属性值

前言

ncs 相关文章,部分为原始文档翻译,水平有限,如果有错误,欢迎指出。

本文参考链接:

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

一、devicetree概念

devicetree

是描述硬件的层次化的数据结构。Devicetree specification定义了它的源代码和二进制表示。Zephyr使用

devicetree

来描述其支持的单板上可用的硬件,以及该硬件的初始配置。

有两种类型的

devicetree

输入文件:

devicetree sources

devicetree bindings

devicetree sources

包含

devicetree

本身。

devicetree bindings

描述它的内容,包括数据类型。编译系统使用

devicetree sources

devicetree bindings

来创建一个生成的C头文件。生成的头文件的内容被

devicetree.h

API分离出来,可以使用该API从

devicetree

中获取信息。

下面是一个简单的示例图:

NCS-OS系列3 :devicetree介绍NCS-OS系列3 :devicetree介绍前言一、devicetree概念二、语法和结构三、Unit address示例四、重要属性介绍五、Aliases and chosen nodes六、输入输出文件

所有Zephyr和应用程序的源代码文件都可以include并使用

devicetree.h

。这包括设备驱动程序、应用程序、测试、内核等。

API本身是基于C宏定义的。宏名都以

DT_

开头。通常,在Zephyr源文件中看到一个以

DT_

开头的宏,那么它可能是一个

devicetree.h

宏。生成的C头文件也包含以

DT_

开头的宏,这些可能会在编译器的错误信息中看到。生成的宏和非生成的宏可以通过这种方式来区分:生成的宏有一些小写字母,而

devicetree.h

宏的名字都是大写字母。

二、语法和结构

如名字所示,

devicetree

是一个树型结构,这个树的人类可读的文本格式称为DTS(用于

devicetree source

),并在Devicetree specification中定义。

下面是一个示例的DTS文件:

/dts-v1/;
/ {
        a-node {
                subnode_label: a-sub-node {
                        foo = <3>;
                };
        };
};
           

/ dts-v1 /;

表示文件的内容是使用DTS语法的版本1,它取代了现在已经过时的“版本0”。

这个树有三个节点:

  • 1个根节点:

    /

  • 一个根节点的子节点,名字为:

    a-node

  • 一个

    a-node

    的子节点,名字为:

    a-sub-node

    节点可以

节点可以被赋予

labels

,标签是用来在设备树的其他地方引用标签节点的唯一的简写。上面的DTS文件中,

a-sub-node

label

就是

subnode_label

。一个节点可以有0、1或多个节点

label

Devicetree节点具有标识其在树中的位置的路径。与Unix文件系统路径一样,Devicetree路径是由斜杠(

/

)分隔的字符串,根节点的路径是单个斜杠:

/

。每个节点的路径将由节点祖先节点的名称与节点自己的名称(用斜杠分隔)连接而成。例如,到

a-sub-node

的全路径为

“/a-node/a-sub-node”

节点

a-sub-node

有一个名为

foo

的属性,它的值是一个值为

3

的单元。

foo

值的大小和类型由DTS中的尖括号(

<和>

)包含。有关更多属性值的示例,请参阅下面的 Writing property values 部分。

在实际应用中,

devicetree

节点通常对应于某些硬件,节点的层次结构反映了硬件的物理布局。假设一块板,它有三个I2C外设连接到一个SoC上的I2C总线控制器上,如下所示:

NCS-OS系列3 :devicetree介绍NCS-OS系列3 :devicetree介绍前言一、devicetree概念二、语法和结构三、Unit address示例四、重要属性介绍五、Aliases and chosen nodes六、输入输出文件

对应的I2C总线控制器和每个I2C外围设备的节点将出现在

devicetree

中。反映硬件的布局,I2C外设节点将是总线控制器节点的子节点。类似的规则也适用于表示其他类型的硬件。

上面的DTS文件将会如下所示:

/dts-v1/;
/ {
        soc {
                i2c-bus-controller {
                        i2c-peripheral-1 {
                        };
                        i2c-peripheral-2 {
                        };
                        i2c-peripheral-3 {
                        };
                };
        };
};
           

属性在实际应用中用于描述或配置节点表示的硬件。例如,I2C外设节点具有一个属性,其值是总线上的外设地址。

下面是一个和上面功能一样的树,但其中包含使用I2C设备时可能看到的真实节点名称和属性:

NCS-OS系列3 :devicetree介绍NCS-OS系列3 :devicetree介绍前言一、devicetree概念二、语法和结构三、Unit address示例四、重要属性介绍五、Aliases and chosen nodes六、输入输出文件

上面的

devicetree

转换成DTS文件如下所示:

/dts-v1/;

/ {
        soc {
                i2c@40003000 {
                        compatible = "nordic,nrf-twim";
                        label = "I2C_0";
                        reg = <0x40003000 0x1000>;

                        apds9960@39 {
                                compatible = "avago,apds9960";
                                label = "APDS9960";
                                reg = <0x39>;
                        };
                        ti_hdc@43 {
                                compatible = "ti,hdc", "ti,hdc1010";
                                label = "HDC1010";
                                reg = <0x43>;
                        };
                        mma8652fc@1d {
                                compatible = "nxp,fxos8700", "nxp,mma8652fc";
                                label = "MMA8652FC";
                                reg = <0x1d>;
                        };
                };
        };
};
           

除了显示更真实的名称和属性外,上面的示例还引入了一个新的

devicetree

概念:

unit addresses

unit addresses

是在“at”符号(

@

)之后的节点名的部分,如

[email protected]

中的

40003000

,或

[email protected]

中的

39

unit addresses

是可选的:soc节点没有

unit addresses

三、Unit address示例

devicetree

中,

Unit address

给出了在其父节点的地址空间中的节点地址。下面是一些不同类型硬件的

Unit address

示例

3.1 内存映射外设

外设的寄存器映射基地址。例如,名称为

[email protected]

的节点代表一个寄存器映射基地地址为

0x40003000

的I2C控制器。

3.2 I2C外设

I2C总线上的外设地址。例如,上一节中I2C控制器的子节点

[email protected]

的I2C地址是

0x39

3.3 SPI外设

代表外围设备的芯片

CS线

的索引。(如果没有芯片

CS线

,则使用0。)

3.4 存储器

物理起始地址。例如,一个名为

[email protected]

的节点表示从物理地址

0x2000000

开始的RAM。

3.5 内存映射flash

像RAM一样,物理起始地址。例如,名称为

[email protected]

的节点代表物理起始地址为

0x8000000

的flash设备。

3.6 内存分区

这适用于

devicetree

用于存储flash分区表的时候。

unit address

flash

中分区的开始偏移量。例如,以这个闪存设备及其分区为例:

flash@8000000 {
    /* ... */
    partitions {
            partition@0 { /* ... */ };
            partition@20000 {  /* ... */ };
            /* ... */
    };
};
           

名为

[email protected]

的节点从其flash设备的开始处偏移量为0,因此其基址为

0x8000000

。类似地,名为

[email protected]

的节点的基址是

0x8020000

四、重要属性介绍

4.1 compatible

compatible

是节点所代表的硬件设备的名称,推荐的格式是

"vendor,device"

,如

“avago,apds9960”

,或这些的序列,如

“ti,hdc”

“ti,hdc1010”

。vendor部分是厂商名称的缩写。文件

dts/bindings/vendor-prefixes.txt

中包含了一个通用的的供应商名称列表。device部分通常取自设备的datasheet。

当硬件的表现是通用的时,它也可以是像

gpio-keys

mmio-sram

fixed-clock

这样的值。

编译系统使用

compatible

属性为节点找到正确的

bindings

。设备驱动程序使用

devicetree.h

来查找具有相关

compatibles

的节点,以便确定要管理的可用硬件。

compatible

属性可以有多个值。当设备是一个通用系列的特定实例时,会使用附加值以允许系统从最特定的设备驱动程序到最不特定的设备驱动程序匹配。

在Zephyr的绑定语法中,此属性的类型为

string-array

4.2 label

label

是根据Zephyr的设备驱动程序模型实现的设备的名称。这个值可以传递给

device_get_binding()

来检索相应的驱动程序级结构

device*

。这个指针可以通过应用程序代码传递给正确的设备驱动程序API来与设备交互。例如,调用

device_get_binding("I2C_0")

将返回一个指向设备结构的指针,该结构可以传递给

I2C API

函数,如

i2c_transfer()

。生成的C头文件同样会包含一个可以描述这个字符串的宏。

4.3 reg

reg

是用于给设备寻址的信息。该值是给到特定设备的(即根据

compatible

属性不同而不同)。

reg

属性是一个由

(address, length)

对组成的序列。每一对被称为一个

“register block”

。下面是一些常见的模式:

  • 通过内存映射I/O寄存器访问的设备(如

    [email protected]

    ):

    address

    通常是I/O寄存器空间的基址,而

    length

    是寄存器所占用的字节数。
  • I2C设备(如

    [email protected]

    及其系列):

    address

    是I2C总线上的从地址。没有

    length

    值。
  • SPI设备:

    address

    是芯片CS线,没有

    length

    上面描述的

    reg

    属性和

    unit addresses

    之间有一些相似之处,

    reg

    属性可以被看作是设备中比

    unit addresses

    更详细的可寻址资源。

4.4 status

status

是描述节点是否启用的字符串,devicetree规范允许该属性的值可以为

“okay”

“disabled”

“reserved”

“fail”

“fail-sss”

。目前只有

“okay”

“disabled”

值与Zephyr相关,使用其他值会导致未定义的行为。

如果一个节点的

status

属性是

“okay”

或者没有定义(即在

devicetree source

中不存在),那么它就被认为是

enabled

。状态为

“disabled”

的节点被显式禁用。(为了向后兼容,值

“ok”

被视为与

“okay”

相同,但这种用法已弃用),在分配和初始化Zephyr驱动模型中的相应

struct device

时,必须启用与物理设备对应的Devicetree节点。

4.5 interrupts

interrupts

是由设备产生的中断的信息,编码为一个或多个中断指示符的数组。每个中断指示符都有一定数量的单元。更多细节请参见 Devicetree Specification release v0.3中section 2.4, Interrupts and Interrupt Mapping章节。

五、Aliases and chosen nodes

除了

node label

之外,还有另外两种方法可以引用特定的节点,而无需指定其整个路径: 通过

alias

chosen node

下面的devicetree使用了这两个概念:

/dts-v1/;

/ {
     chosen {
             zephyr,console = &uart0;
     };
     aliases {
             my-uart = &uart0;
     };
     soc {
             uart0: serial@12340000 {
                     ...
             };
     };
};
           

/alias

/chosen

节点并不指向实际的硬件设备,它们的目的是指定

devicetree

中的其他节点。

上面的dts文件中,

my_uart

是路径为

/soc/[email protected]

的设备的alias,通过使用它的

node label

uart0

zephyr,console

这个

chosen node

被设置为同样的值。

Zephyr示例应用程序有时使用别名以允许以通用方式来取代应用程序使用的特定硬件设备。例如,Blinky例程使用这个来抽象LED通过

led0 alias

闪烁。

/chosen

节点属性用于配置系统或子系统范围的值。更多信息,请参阅Chosen nodes。

六、输入输出文件

本节更详细地描述了本介绍开头的图中所示的输入和输出文件。

NCS-OS系列3 :devicetree介绍NCS-OS系列3 :devicetree介绍前言一、devicetree概念二、语法和结构三、Unit address示例四、重要属性介绍五、Aliases and chosen nodes六、输入输出文件

6.1 输入文件

有四种类型的devicetree输入文件:

  • sources (.dts)
  • includes (.dtsi)
  • overlays (.overlay)
  • bindings (.yaml)

ncs目录中的设备树文件是这样的:

ncs/zephyr/boards/<ARCH>/<BOARD>/<BOARD>.dts
ncs/zephyr/dts/common/skeleton.dtsi
ncs/zephyr/dts/<ARCH>/.../<SOC>.dtsi
ncs/zephyr/dts/bindings/.../binding.yaml
           

还有:

ncs/nrf/boards/<ARCH>/<BOARD>/<BOARD>.dts
ncs/nrf/dts/bindings/.../binding.yaml
           

一般来说,每一个支持的

BOARD

,都要有一个对应的

BOARD.dts

文件来描述它的硬件结构,例如

reel_board

的dts文件为

boards/arm/reel_board/reel_board.dts

BOARD.dts

包括一个或多个

.dtsi

文件,这些

.dtsi

文件或者通过

include

其他

.dtsi

文件来描述运行Zephyr的CPU或片上系统。它们还可以描述由多个板共享的其他常见硬件特征。除了这些,

BOARD.dts

也描述了板子的具体硬件。

dts/common

目录包含

skeleton.dtsi

skeleton.dtsi

是用于定义完整

devicetree

的最小包含文件。特定体系结构的子目录(

dts/<ARCH>

)包含了针对

CPUs

SoCs

的对

skeleton.dtsi

扩展的

.dtsi

文件。

C预处理器在所有

devicetree

文件上运行以展开宏引用,而

include

通常使用

#include <filename>

指令来完成,即使DTS有

/include/ "<filename>"

语法。

BOARD.dts

可以使用

overlays

文件进行扩展或修改。Overlay也是DTS文件,

overlay

扩展只是一个是目的更明确的惯例。Overlays调整基础的

devicetree

以适应不同的目的:

  • Zephyr应用程序可以使用

    overlay

    来启用默认禁用的外围设备,为特定的应用程序选择板上的传感器,等等。通过使用

    Kconfig

    ,可以重新配置内核和设备驱动程序,而无需修改源代码。
  • 在定义

    Shields

    时也使用

    Overlays

    编译系统自动获取存储在特定位置的

    .overlay

    文件。也可以通过

    DTC_OVERLAY_FILE

    这个CMake变量显式列出要包含的

    overlays

    。详情参见Set devicetree overlay。

编译系统将

BOARD.dts

和其他

.overlay

文件进行合并,

overlay

放在最后,这依赖于DTS允许合并

devicetree

中节点的重复定义的语法。见示例:FRDM-K64F and Hexiwear K64,这是一个在

.dtsi

文件的环境中如何工作的示例,但原理与

overlays

相同,把

.overlay

文件的内容放在最后允许它们覆盖

BOARD.dts

Devicetree bindings(

YAML文件

)本质上是

glue

。它们以一种允许编译系统生成设备驱动程序和应用程序可用的C宏的方式来描述

devicetree sources

,

includes

, 和

overlays

的内容。

dts/bindings

目录包含了相关的

bindings

信息。

Zephyr目前使用

dts_fixup.h

文件将

devicetree_unfixed.h

中的宏重命名为C代码当前使用的名称。默认情况下,构建系统在

zephyr/boards/

zephyr/soc/

目录中查找修复文件。修复文件的存在是由于历史原因,新代码通常应该避免使用它们。

6.2 脚本和工具

下面的库和脚本位于

scripts/dts/

中,它们使用输入文件来创建输出文件。

dtlib.py : 底层DTS解析库

edtlib.py : 位于dtlib之上的库,它使用

bindings

来解析属性,并提供

devicetree

的更高层次视图,使用dtlib进行DTS解析。

gen_defines.py :使用edtlib从

devicetree

bindings

生成C预处理器宏的脚本

另外,如果在您的系统上安装了标准的dtc(

devicetree compiler

)工具,那么它将在最终的

devicetree

上运行,这是为了捕获错误或警告。输出信息没有被使用。板子可能需要传递dtc附加标志,例如

warning suppression

。单板目录中可以包含一个名为

pre_dt_board.cmake

的文件,它配置了这些额外的标志,像这样:

6.3 输出文件

下面这些是在应用程序编译文件夹中创建的

<build>/zephyr/include/generated/devicetree_unfixed.h

生成的宏和描述

devicetree

的附加注释,通过

devicetree.h

进行

include

<build>/zephyr/include/generated/devicetree_fixups.h

dts_fix.h

文件的内容,通过

devicetree.h

进行

include

<build>/zephyr/zephyr.dts

最终合并后的

devicetree

。这个文件由

gen_definitions .py

输出,作为调试目的使用,没有其他用处。

<build>/zephyr/<BOARD>.dts.pre.tmp

预处理和连接的DTS

sources

overlays

。这是一个中间输出文件,用于创建

zephyr.dts

devicetree_unfixed.h

6.4 写属性值

下面是一些以DTS格式写入属性值的示例方法。为了尽量简单,省略了一些细节;如果对细节感兴趣,请参阅devicetree specification。

32位无符号整数数组或

cells

,可以写在尖括号(

<和>

)之间,并用空格分隔:

foo属性值有三个

cells

,值依次为

0xdeadbeef

1234

。注意,这里允许使用十六进制数和十进制数,并且可以混合使用。由于Zephyr将DTS转换为C源文件,因此没有必要在这里指定单个cell的字节顺序。

64位整数按大端顺序写成两个32位单元格。值

0xaaaa0000bbbb1111

写为

<0xaaaa0000 0xbbbb1111>

允许使用圆括号、算术操作符和位操作符。下面

bar

属性包含一个值为64的单元格:

字符串用双引号括起来:

字符串数组用逗号分隔:

字节数组以十六进制形式写入,方括号(

[和]

)之间不包含前导0x。下面的属性

a-byte-array

是三个字节0x00, 0x01, 0xab:

属性可以通过它们的

phandles

引用

devicetree

中的其他节点。可以使用

&label

写一个

phandle

,就像在这个

devicetree

片段中:

baz: device@0 {
        /* ... */
};
device@1 {
        sibling = <&baz 1 2>;
        /* ... */
};
           

节点

[email protected]

sibling

属性包含三个单元格:

  • [email protected]

    节点的

    phandle

    。每个

    phandle

    占据1个cell。

    baz

    标签用于在

    sibling

    属性中写入phandle值

    &baz

  • 值1和2,每个都按照这个顺序在自己的单元格中。