在嵌入式系统固件开发中,硬件传统上是在头文件(.h 或 .hh)中描述的。nRF Connect SDK 借鉴了 Zephyr RTOS,使用一种更结构化、模块化的方法来描述硬件,即通过一种称为设备树(devicetree)的结构。TODAY!
设备树是一种描述硬件的分层数据结构。所描述的硬件可以是开发套件、SoC、SiP 或模块,定义了从开发套件上 LED 的 GPIO 配置到外设的内存映射位置等所有内容。设备树使用一种特定的格式,由连接在一起的节点 (nodes)组成,每个节点包含一组属性 (properties)。
以下部分衍生自设备树文档。
顾名思义,设备树是一种树状结构。这种树的人类可读文本格式称为 DTS(即 devicetree source)。
这是一个 DTS 文件示例:
/dts-v1/;
/ {
a-node {
subnode_label: a-sub-node {
foo = <3>;
};
};
};Devicetree上面的树有三个节点:
/a-node 的节点,它是根节点的子节点a-sub-node 的节点,它是其子节点 a-node节点可以被赋予标签 (labels),标签是唯一的简写,可用于在设备树的其他地方引用该标记节点。在上方,a-sub-node 具有标签 subnode_label。一个节点可以没有、有一个或多个节点标签。
设备树节点还可以具有属性 (properties)。属性是名称/值对。属性值可以是字符串、字节、数字的数组,或任何类型的混合。
节点 a-sub-node 有一个名为 foo 的属性,其值为单元格值 3。foo 的值的类型和大小由 DTS 中封闭的尖括号(< 和 >)暗示。如果传达真假信息,属性值可能为空。在这种情况下,属性的存在或缺失就足以说明问题。
设备树节点具有标识其在树中位置的路径 (paths)。与 Unix 文件系统路径类似,设备树路径是以斜杠 (/) 分隔的字符串,根节点的路径是单个斜杠:/。否则,每个节点的路径由该节点的祖先名称与节点自身名称连接而成,并以斜杠分隔。例如,a-sub-node 的完整路径是 /a-node/a-sub-node。
设备树绑定 (Devicetree bindings) 定义了 compatible 属性。它声明了对设备树节点内容的要求,并提供有关有效节点内容的语义信息。Zephyr 的设备树绑定定义为 YAML 文件。每个设备树节点必须有一个 compatible 属性。设备树节点通过 compatible 属性与其在设备树绑定中的定义进行匹配。
下面是一个设备树绑定文件 (.yaml) 的示例,它定义了名为 nordic,nrf-sample 的 compatible 属性,其中包含一个名为 num-sample 的整型必填属性。
compatible: "nordic,nrf-sample"
properties:
num-sample:
type: int
required: trueYAML下面是一个示例 DTS 文件 (.dts),其中节点 node0 被设置为 compatible nordic,nrf-sample。这意味着 node0 节点必须具有必填属性 num-sample,且该属性必须被分配一个整数值。否则,构建将失败。
node0 {
compatible = "nordic,nrf-sample";
num-sample = <3>;
};Devicetree设备树绑定随 SDK 一起提供在 <install_path>\zephyr\dts\bindings 中(参见 Nordic Semiconductor 设备的设备树绑定)。在某些情况下,您需要定义自己的 YAML 文件,例如在创建自定义驱动程序时。这在 nRF Connect SDK 中级课程中有所涵盖。
在设备树中添加别名是很常见的。/aliases 节点包含作为别名的属性,其中属性的名称是该别名的名称,属性的值是对设备树中某个节点的引用,见下文。
/ {
aliases {
subnode_alias = &subnode_label;
};
};Devicetree上面的代码片段将通过其标签 subnode_label 引用的节点 a-sub-node 分配给别名 subnode_alias。这里的目的是让您的 C/C++ 应用程序代码(例如:main.c)使用该别名。在板级 dts 文件中定义固定别名(例如:板上第一个 LED 的 led0)可以使应用程序代码更具可移植性,因为它可以避免硬编码变化的设备节点名称,并使应用程序代码能够更灵活地适应所用板卡的变化。
要在源代码中获取有关特定设备树节点的信息,您需要为其提供一个节点标识符 (node identifier)。这只是一个引用该节点的 C 宏。有多种方法可以获取节点标识符。
最常用的两种方法是通过宏 DT_NODELABEL() 获取节点标签,以及通过宏获取别名。 DT_ALIAS().
例如,要获取 a-sub-node 的节点标识符:
DT_NODELABEL(subnode-label)C要获取分配给特定设备树属性的值,我们可以使用宏 DT_PROP(). 。例如,要获取分配给 foo 属性的值:
DT_PROP(DT_NODELABEL(subnode-label), foo)C让我们通过一个实际例子来更好地理解这些概念。nRF52833 DK 有四个用户可配置的 LED(PCB 标记为 LED1 – LED4),连接到 GPIO 引脚 P0.13 – P0.16,如下面从 nRF52833 DK 原理图(可在开发套件页面的 Downloads – > Hardware Files 中找到)获取的截图所示。
nRF54 系列 DK(例如 nRF54L15 DK)上 LED 和按钮的 PCB 标记“Silkscreen on PCB”现在与其在设备树中的定义一致。
例如,PCB 标签“LED0”对应设备树中的“led0”。这与我们之前的开发套件有所不同,之前的开发套件使用“LED1”PCB 标记来引用“led0”设备树节点。
这些硬件详细信息都在 nRF52833 DK 的设备树文件中进行了描述。让我们检查一下这个文件,它位于 <install_path>\zephyr\boards\nordic\nrf52833dk\nrf52833dk_nrf52833.dts

<install_path>\zephyr\dts\arm\nordic 中的文件 nrf52833_qiaa.dtsi。使用此文件是因为它对应于 nRF52833 DK 上使用的 SoC 的封装变体和功能变体。DTSI 中的 I 代表 Include(包含)。dtsi 文件通常包含 SoC 级别的定义。它还包含引脚映射,定义在 nrf52833dk_nrf52833-pinctrl.dtsiled_0,节点标签为 led0。节点标签通常用于引用节点,例如 &led0。led_0 有两个属性:gpios 和 label。gpios 正通过 & 符号引用节点 gpio0。gpio0 定义在 SoC 设备树中,正如我们将在下一段中看到的。套件上 LED1 连接到 nRF52833 SoC 的 GPIO 引脚被定义为 GPIO 0 上的 pin 13 (P0.13),且为低电平有效。节点通常具有节点标签 (node label),但它也可以具有名为 label 的属性 (property)。例如,节点 led_0 具有节点标签 led0 和值为“Green LED 0”的标签属性。在这种情况下,label 属性添加了一个描述 LED 的人类可读字符串。使用 label 属性(例如 “Green LED 0″)来获取节点标识符的方法已弃用,不应再使用。相反,建议使用节点标签 (led0) 来获取节点标识符。
alias 节点也定义在 DK 设备树文件中,见下图。

我们可以从 DK 设备树中的 /aliases 节点看到,通过其节点标签引用为 &led0 的节点 led_0 被赋予了别名 led0。这听起来可能有些多余;然而,这样做是为了确保所有带有 LED 节点的板卡都为其 LED 提供一致的别名(例如第一个 LED 为 led0),以便应用程序代码(例如 main.c)可以在不同的板卡上编译,而无需您手动检查 DTS 文件并弄清楚不同板卡上 LED 使用的节点标签。
如果我们查看其他一些外设节点,您会注意到各种 pinctrl 属性(定义在 <install_path>\zephyr\dts\bindings\pinctrl\pinctrl-device.yaml 中)。例如,描述 UART0 外设的节点 &uart0 具有属性 pinctrl-0、pinctrl-1 和 pinctrl-names。

这是基于 Zephyr 的引脚控制 (pin control),并通过引脚控制设备树文件中定义的 &pinctrl 节点将特定的引脚配置分配给设备树节点。
设备树文件依赖于引脚控制设备树文件来获取各种节点(LED 和按钮除外)的引脚映射。如上图所示,&uart0 节点引用了在此文件中定义的节点 &uart0_default 和 &uart0_sleep。引脚控制设备树文件位于 <install_path>\zephyr\boards\nordic\nrf52833dk\nrf52833dk_nrf52833-pinctrl.dtsi。

&pinctrl 节点(定义在 <install_path>\zephyr\dts\bindings\pinctrl\nordic,nrf-pinctrl.yaml 中)在其子节点中包含所有设备引脚配置。uart0_default 编码了 UART0 外设默认状态的引脚配置。引脚控制 API 允许您根据状态为外设分配不同的引脚;两个标准状态是 default 和 sleep。psels 属性中指定引脚功能选择列表。group1 节点是这些组之一,指定了 UART_TX 和 UART_RTS 的引脚配置。group2 节点正在指定 UART_RX 和 UART_CTS 的引脚配置,并为它们两者设置 bias-pull-up 属性。现在,检查位于目录 <install_path>\zephyr\dts\arm\nordic 中的 SoC 变体设备树 nrf52833_qiaa.dtsi。

nrf52833.dtsi,该文件位于同一目录中。SoC 设备树包含所有外设和系统块的 SoC 级硬件描述。检查位于目录中的基础 SoC 设备树 nrf52833.dtsi <install_path>\zephyr\dts\arm\nordic.

gpio0 节点在此定义。compatible 属性指定其代表的硬件。驱动程序使用它来选择其支持的节点。这应该能让您对如何使用设备树描述和呈现硬件有一个基本的了解。要获取有关设备树的更多信息,您可以从此处下载设备树规范。