Zephyr undefined reference to `__device_dts_ord_<N>‘
·
实验现象
- 以下为设备树定义
#include <zephyr/dt-bindings/gpio/gpio.h>
/{
leds{
compatible = "gpio-leds";
led0: blue_led{
gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
label = "blue led";
};
};
buttons{
compatible = "gpio-keys";
key0: boot_key{
gpios = <&gpio0 0 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
label = "user key0";
};
};
};
- 错误的设备节点获取方式
const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(led0));
/* 展开后的结果依次为
DEVICE_DT_GET(DT_NODELABEL(led0)) -> DEVICE_DT_GET(DT_N_S_leds_S_blue_led) -> &__device_dts_ord_21
*/
- 以下为错误提示,通过全局查找时发现如下提示,原因可能为驱动未被编译
error: undefined reference to `__device_dts_ord_21'
/**
* @brief Get a <tt>const struct emul*</tt> from a devicetree node identifier
*
* @details Returns a pointer to an emulator object created from a devicetree
* node, if any device was allocated by an emulator implementation.
*
* If no such device was allocated, this will fail at linker time. If you get an
* error that looks like <tt>undefined reference to __device_dts_ord_<N></tt>,
* that is what happened. Check to make sure your emulator implementation is
* being compiled, usually by enabling the Kconfig options it requires.
*
* @param node_id A devicetree node identifier
* @return A pointer to the emul object created for that node
*/
参考样例
- 参考 zephyr/samples/drivers/led_xec/ 例子中对设备结构体指针的获取方式进行修改,可正确获取到设备结构体
const struct device *dev = DT_FOREACH_STATUS_OKAY(gpio_leds, DEVICE_DT_GET);
/* 展开的结果依次为
DT_FOREACH_STATUS_OKAY(gpio_leds, DEVICE_DT_GET) -> DEVICE_DT_GET(DT_N_S_leds) -> &__device_dts_ord_20
*/
/* 下面是map中的映射关系,可以看到编译之后的led设备节点为 __device_dts_ord_20 */
.z_device_POST_KERNEL90_
0x000000003f400ef8 0x18 zephyr/drivers/led/libdrivers__led.a(led_gpio.c.obj)
0x000000003f400ef8 __device_dts_ord_20
问题分析
表层分析
- 从前面的分析可以看出两种方式访问的不是同一个节点,再参考手册中对 DT_FOREACH_STATUS_OKAY 的描述,该宏将依次查找 compatible 属性与 gpio_leds 匹配的节点,而 led0 是leds 的子节点,因此上述两种方式访问的并不是同一个节点,此时如果我们要使用 DEVICE_DT_GET 访问 led0,尝试获取 leds 节点:
const struct device *dev = DEVICE_DT_GET(DT_PATH(leds));
/* 展开后的结果依次为
DEVICE_DT_GET(DT_NODELABEL(led0)) -> DEVICE_DT_GET(DT_N_S_leds_S_blue_led) -> &__device_dts_ord_20
*/
- DEVICE_DT_GET 访问的是某一个设备节点,可通过如下宏创建实例
- DEVICE_DEFINE(dev_id, name, init_fn, pm, data, config, level, prio, api)
- DEVICE_DT_DEFINE(node_id, init_fn, pm, data, config, level, prio, api, …)
- DEVICE_DT_INST_DEFINE(inst, …)
- led0 并未创建出实例,创建的实例为 compatible 属性为 “espressif,esp32-gpio” 的 gpio0 和 gpio1,分别为两组I/O控制器,从这里也说明 DEVICE_DT_GET(DT_NODELABEL(led0)) 的用法不正确。
深层分析
- 设备树编译后,输出了一堆根据不同用途来命名的宏,例如获取设备结构体,访问节点属性,他们都有一套不同的命名规范,用户层访问时通过将用户输入的信息按照同样的规范组合成一个宏,通过该宏间接进行访问。
- 一旦将用户输入的信息按照规则组合后与设备树编译后的信息不一致,如果访问的信息不是变量,在预处理阶段就会提示符号未定义,如果访问的信息是变量,在链接阶段会提示未定义的变量。
/* 以 DT_PATH() 为例,如果输入的路径不正确,就会出现如下提示 */
error: 'DT_N_S_leds_S_blue_led' undeclared here (not in a function); did you mean 'DT_N_S_leds_S_blue_led_ORD'?
/* 以 DEVICE_DT_GET() () 为例,如果输入节点ID不对,就会出现如下提示 */
error: undefined reference to `__device_dts_ord_21'
- 出现宏不存在的提示首先需要确认设备树中的描述是否正确,然后再检查应用程序中访问节点的宏使用是否正确,如果此时依然无法排查问题可以在生成的devicetree_generated.h 中查找提示不存在的宏,不过不是完全匹配,而是去掉尾缀的部分查找,例如 DT_N_S_leds_S_blue_led_ORD 只查找 DT_N_S_leds 开头的部分,查看其中是否有自己想访问的节点,如果存在则说明应用程序中宏的使用存在问题。
- 出现变量未定义的问题有两种情况
- 第一种是自定义的驱动未创建实例,或者是驱动对应的源文件未被编译,在Zephyr中驱动的编译是通过Kconfig进行配置的,如果对应选项的依赖不满足时,自然程序也就未被编译,这种情况只需要查找对应的Kconfig选项是否为y即可,如果不是则通过menuconfig或者指定不满足的依赖为y即可。
- 第二种则是上面遇到的问题,宏的使用方式不正确,需要确认该节点的实例被驱动程序所创建,否则就会提示上述错误。
更多推荐


所有评论(0)