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即可。
    • 第二种则是上面遇到的问题,宏的使用方式不正确,需要确认该节点的实例被驱动程序所创建,否则就会提示上述错误。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咕咚.萌西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值