Zephyr 快速指南
DTS 快速指南
DTS 详细介绍请参考 Zephyr 配置系统
DTS 语法和使用简介
修改 DTS 配置
Zephyr 中 DTS 是分散在多个文件中的, 详见 unit-address, 修改 DTS 时修改哪个文件要视情况而定
到对应 test/sample 源码目录下(test 通常是
zephyr/tests/*/boards, sample 通常是zephyr/samples/*/boards)新增(如果没有)对应 board 的 overlay 文件并进行修改如下 overlay 例子是增加一个 led 节点, 设置相关 gpio 口, 并为该节点取别名
led0:
/ {
aliases {
led0 = &led_0;
};
gpio-led {
compatible = "gpio-leds";
led_0: led_0 {
gpios = <&gpioa 25 0>;
};
};
};
如下例子是修改已有 DTS 中的配置(将
status改为okay)
&gpioa {
status = "okay";
};
在对应 board 级 dts 目录新增 overlay 文件, 文件命名规则:
<origin_board_dts_basename>_<revision>.overlay, 其中revision支持多种形式修改同目录下的
revision.cmake(如果没有则创建), 通过DEFAULT_REVISION参数指定对应 overlay 文件
board_check_revision(
FORMAT_LETTER
DEFAULT_REVISION D # <revision> is D
)
board_check_revision(
FORMAT_MAJOR.MINOR.PATH
DEFAULT_REVISION 2.0.0 # <revision> is 2.0.0
)
board_check_revision(
FORMAT_NUMBER
DEFAULT_REVISION 2 # <revision> is 2
)
如何确认 DTS 最终配置是否正确
Zephyr DTS 因为涉及多个文件, 同一个配置项可能会被多处设置或重置, 所以直接看某个文件不能反映最终的结果, 如:
检查修改(overlay)是否生效
检查某个设备的状态(
okay或disable)或属性设置是否符合预期
此时可以查看 build/zephyr/zephyr.dts 文件, 它是最终生成的 DTS 文件
如何查看某个设备的 binding 文件
binding 文件介绍请参考 DTS binding
到定义设备的 dts 中找到其 compatible 字段, 如:
compatible = "realtek,ameba-rcc";查找名为
realtek,ameba-rcc.ymal文件, 确认其中compatible值为"realtek,ameba-rcc", 则该文件为对应 binding 文件
注意
这种方法只有 binding 文件命名遵循了与 compatible 字段相同的规则才可以使用这种方法, 但这种规则在 zephyr 中不是强制的, 若不一致需要通过方法 2
定位到
devicetree_generated.h文件, 在文件开头位置找到所需设备完整节点名称, 如/soc/gpio@41010000
* Node dependency ordering (ordinal and path):
* 0 /
* 1 /aliases
* 2 /chosen
* 3 /soc
* 4 /soc/interrupt-controller@e000e100
* 5 /soc/gpio@41010000
* 6 /buttons
* 7 /buttons/button_0
* 8 /clocks
* 9 /clocks/clk_sys
* 10 /cpus
继续在文件中检索
Devicetree node: /soc/gpio@41010000, 找到如下位置:
/*
* Devicetree node: /soc/gpio@41010000
*
* Node identifier: DT_N_S_soc_S_gpio_41010000
*
* Binding (compatible = realtek,ameba-gpio):
* $ZEPHYR_BASE/dts/bindings/gpio/realtek,ameba-gpio.yaml
*
* (Descriptions have moved to the Devicetree Bindings Index
* in the documentation.)
*/
第 7 行就是该设备所用的 binding 文件的完整路径
在代码中如何访问 DTS
访问 DTS 通常是为了获取某个节点的属性或者驱动, 可以参照如下步骤:
假如有 DTS 内容如下:
/ {
soc {
serial0: serial@40002000 {
reg = <0x4000200 0x100>
status = "okay";
current-speed = <115200>;
/* ... */
};
};
aliases {
my-serial = &serial0;
};
chosen {
zephyr,console = &serial0;
};
};
首先获取某个节点标志符. DTS 中有多种方法标志一个节点, 所以也对用多种方法获取节点:
/* Option 1: by node label */
#define MY_SERIAL DT_NODELABEL(serial0)
/* Option 2: by alias */
#define MY_SERIAL DT_ALIAS(my_serial)
/* Option 3: by chosen node */
#define MY_SERIAL DT_CHOSEN(zephyr_console)
/* Option 4: by path */
#define MY_SERIAL DT_PATH(soc, serial_40002000)
上述代码中宏
MY_SERIAL即表示节点标识符, 利用它可以进一步得到节点属性或驱动
/* Demo 1: conditional compilation based on node status */
#if DT_NODE_HAS_STATUS(MY_SERIAL, okay)
//Desired code when node is okay(enabled)
#endif
/* Demo 2: get normal property */
u32 speed = DT_REG_ADDR(MY_SERIAL); //=0x4000200
/* NOTE: some property have exclusive macro */
/* Demo 3: get reg info */
u32 addr = DT_REG_ADDR(MY_SERIAL); //=0x4000200
u32 size = DT_REG_SIZE(MY_SERIAL); //=0x100
const struct device *const dev = DEVICE_DT_GET(MY_SERIAL);
if (!device_is_ready(dev)) {
/* Not ready, do not use */
return -ENODEV;
}
uart_poll_out(dev, 'a');
更多进阶用法参考 扩展阅读
在 Kconfig 中如何访问 DTS
参考: Kconfig 快速指南
在 CMake 中如何访问 DTS
参考: CMake 快速指南
如何定位 undefined reference 的 DTS 符号问题
DTS 中的定义的节点会在驱动代码中被定义为某个符号, 其标识符是按照某些编码规范生成, 如
__device_dts_ord_22实际使用中可能会遇到类似如下的编译错误:
/opt/rtk-toolchain/asdk-12.3.1-4431/linux/newlib/bin/../lib/gcc/arm-none-eabi/12.3.1/../../../../arm-none-eabi/bin/ld.bfd: app/libapp.a(test_counter.c.obj):(.rodata.devices+0x0): undefined reference to `__device_dts_ord_22'
出现该问题的原因通常是驱动层之上的代码引用了一个符号(驱动实例), 但链接时没有找到该驱动实例的符号定义(代码实现)
这里无法直接通过名字
__device_dts_ord_22定位到相关的代码区域, 下面给出一个针对上述错误的排查流程
首先定位出问题的 DTS 节点: 通过
test_counter.c.obj可以确定引用该符号的源文件是test_counter.c, 此时可以选择直接查看该文件, 分如下两种情况:如果只有一个驱动实例引用, 就可以通过代码确定对应的 DTS 节点信息(节点名称, label 等), 例如可能有如下代码:
#define I2C_DEV_NODE DT_ALIAS(i2c_0) const struct device *const i2c_dev = DEVICE_DT_GET(I2C_DEV_NODE);
此时可以很容易判断是
i2c_0这个 DTS 节点对应设备的驱动定义有问题如果代码复杂, 有多个驱动实例引用或复杂的预处理逻辑, 则可以通过生成预处理文件直接精确定位 DTS 节点, 步骤如下
在文件
build/compile_commands.json中查找test_counter.c.obj, 可以定位到如下一个位置(表示部分省略 xxx 内容):{ "directory": "/xxx/build", "command": "/xxx/arm-none-eabi-gcc xxx -o CMakeFiles/app.dir/src/test_counter.c.obj -c /xxx/zephyr/tests/drivers/counter/counter_basic_api/src/test_counter.c", "file": "/xxx/zephyr/tests/drivers/counter/counter_basic_api/src/test_counter.c", "output": "CMakeFiles/app.dir/src/test_counter.c.obj" },
其中
command字段是编译test_counter.c的完整命令, 需要对完整的该命令稍作修改:-o输出路径,-c改为-E,然后在终端执行:/xxx/arm-none-eabi-gcc xxx -o test_counter.i -E /xxx/zephyr/tests/drivers/counter/counter_basic_api/src/test_counter.c",
此时会在执行目录生成一个文件
test_counter.i, 打开该文件搜索未定义符号__device_dts_ord_22可以找到如下内容:static const struct device *const devices[] = { # 63 "/xxx/zephyr/tests/drivers/counter/counter_basic_api/src/test_counter.c" # 124 "/xxx/zephyr/tests/drivers/counter/counter_basic_api/src/test_counter.c" (&__device_dts_ord_22), (&__device_dts_ord_23), (&__device_dts_ord_24), (&__device_dts_ord_25), # 144 "/xxx/zephyr/tests/drivers/counter/counter_basic_api/src/test_counter.c" };
通过上述第 4, 5 行可知
__device_dts_ord_22是在源文件 124 行引用的, 定位到该行代码如下:#ifdef CONFIG_COUNTER_TMR_AMEBA DEVS_FOR_DT_COMPAT(realtek_ameba_counter) #endif
由此可知是
compatiable为realtek,ameba-counter的 DTS 节点对应设备的驱动定义有问题
排查 DTS 节点对应的驱动定义问题, 可以参考以下思路:
DTS 节点不存在或未使能(
status未设置为okay), 可以参考 如何确认 DTS 最终配置是否正确DTS 节点对应驱动代码未加入编译, 这里要检查相关驱动的
CMakeLists.txt或 Kconfig 配置DTS 节点驱动代码中编码问题, 例如
DT_DRV_COMPAT宏定义是否正确等
Kconfig 快速指南
Kconfig 中获取 DTS 中的信息
Zephyr 提供了系列接口支持在 Kconfig 中访问 DTS 中的信息: Devicetree-related Functions.
如下是一些使用示例:
config AMEBA_PSRAM
def_bool y if $(dt_nodelabel_enabled,psram) # init bool value by node state
config AMEBA_PSRAM_SIZE
hex
depends on AMEBA_PSRAM
default $(dt_nodelabel_reg_size_hex,psram) # init hex value by node reg's size cell
对应 DTS:
psram: memory@60000000 {
compatible = "zephyr,memory-region";
device_type = "memory";
reg = <0x60000000 DT_SIZE_M(4)>;
zephyr,memory-region = "PSRAM";
status = "disabled";
};
CMake 快速指南
CMake 中获取 DTS 中的信息
Zephyr 提供了系列接口支持在 CMake 中访问 DTS 中的信息, 其函数实现可以参考: zephyr/cmake/modules/extensions.cmake
如下是一些 api 说明:
API |
Description |
|---|---|
|
Function for retrieving the node path for the node having nodelabel |
|
Get a node path for an /aliases node property |
|
Tests whether a node with path <path> exists in the devicetree |
|
Tests whether <path> refers to a node which exists in the devicetree, and has a status property matching the <status> argument |
|
Get a devicetree property value. The value will be returned in the <var> parameter |
|
Get a list of paths for the nodes with the given compatible. The value will be returned in the <var> parameter |
|
Get the number of register blocks in the node's reg property |
|
Get the base address of the register block at index <idx>, or with name <name> |
|
Get the size of the register block at index <idx>, or with name <name> |
|
Test if the devicetree's /chosen node has a given property <prop> which contains the path to a node |
|
Get a node path for a /chosen node property |
NVIC 快速指南
NVIC 详细介绍请参考 NVIC 介绍
zephyr 中断使用说明
在 Zephyr 中开发驱动使用中断需要完成几个关键步骤, 即可正常响应中断。
下面将给出一个使用示例, 并列举其中的关键步骤。
代码示例
DTS 配置:
中断设备树配置示例1nvic: interrupt-controller@e000e100 { 2 #address-cells = < 0x1 >; 3 compatible = "arm,v8.1m-nvic"; 4 reg = < 0xe000e100 0xc00 >; 5 interrupt-controller; 6 #interrupt-cells = < 0x2 >; 7 arm,num-irq-priority-bits = < 0x3 >; 8 phandle = < 0x1 >; 9}; 10timer0: counter@40819000 { 11 compatible = "realtek,ameba-counter"; 12 reg = <0x40819000 0x30>; 13 clocks = <&rcc AMEBA_LTIM0_CLK>; 14 interrupts = <7 0>; 15 clock-frequency = <32768>; 16 status = "disabled"; 17};
C 代码实现:
中断驱动代码实现示例1#define DT_DRV_COMPAT realtek_ameba_counter 2... 3 4void counter_ameba_isr(const struct device *dev) 5{ 6} 7 8#define TIMER_IRQ_CONFIG(n) \ 9 static void irq_config_##n(const struct device *dev) \ 10 { \ 11 IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), counter_ameba_isr, \ 12 DEVICE_DT_INST_GET(n), 0); \ 13 irq_enable(DT_INST_IRQN(n)); \ 14 } 15 16#define AMEBA_COUNTER_INIT(n) \ 17 TIMER_IRQ_CONFIG(n) \ 18 ... 19 20DT_INST_FOREACH_STATUS_OKAY(AMEBA_COUNTER_INIT);
关键步骤
确认硬件支持, DTS 中配置中断信息。
如上 中断设备树配置示例 中高亮行 14 行所示设置驱动的中断属性,
interrupts有两个入参, 第一个是中断号, 第二个是中断优先级。驱动代码中实现中断服务函数, 注册中断时需要用到, 如上 中断驱动代码实现示例 4-6 行。
驱动代码中从 DTS 获取中断优先级等属性, 注册中断时需要用到, 如上 中断驱动代码实现示例 11 行。 详细介绍请参考 获取中断属性。
/*获取中断号*/ DT_INST_IRQN(n); /*获取中断优先级*/ DT_INST_IRQ(n, priority);
驱动代码中注册中断, 如上 中断驱动代码实现示例 11 行。 详细介绍请参考 中断注册。
驱动代码中启用中断, 如上 中断驱动代码实现示例 13 行。 详细介绍请参考 启用/关闭中断。
文件系统 快速指南
文件系统详细介绍参考 文件系统 介绍
LitteFS on FLASH
样例路径: zephyr/samples/subsys/fs/littlefs/
使用以下命令编译 LitteFS on FLASH 样例:
./nuwa.py build -a zephyr/samples/subsys/fs/littlefs/ -d rtl8721f_evb
当需要自定义 DTS 时,可参考 LittleFS on FLASH 样例 配置。
可通过最终生成的 DTS 文件 build/zephyr/zephyr.dts 进一步检查 DTS 配置的正确性:
spic 的 status = "okay"
LitteFS 使用的 partition 起始地址和大小符合预期
如果遇到链接错误,可能是某些功能未使能。
可以通过 build/zephyr/include/generated/zephyr/autoconf.h 检查最终的 conf 配置。
LitteFS on FLASH 需要以下配置处于使能状态:
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
FatFS on SD
样例路径: zephyr/samples/subsys/fs/fs_sample/
使用以下命令编译 FatFS on SD 样例:
./nuwa.py build -a zephyr/samples/subsys/fs/fs_sample/ -d rtl8721f_evb
如果遇到链接错误,可能是某些功能未使能。
可以通过 build/zephyr/include/generated/zephyr/autoconf.h 检查最终的 conf 配置。
FatFS on SD 需要以下配置处于使能状态:
CONFIG_FILE_SYSTEM=y
CONFIG_FAT_FILESYSTEM_ELM=y
CONFIG_DISK_ACCESS=y
CONFIG_DISK_DRIVERS=y
Settings 快速指南
Settings 详细介绍参考 Settings 介绍
样例路径: zephyr/samples/subsys/settings/
使用以下命令编译 Settings 样例:
./nuwa.py build -a zephyr/samples/subsys/settings/ -d rtl8721f_evb
可参考 settings 配置 配置 DTS。