Zephyr undefined reference to `__device_dts_ord_<N>‘

文章讨论了在Zephyr操作系统中,通过设备树访问GPIO-leds和gpio-keys节点时遇到的错误。错误源于使用了错误的DEVICE_DT_GET宏来获取特定设备节点。通过对比正确的设备结构体指针获取方式和错误提示,分析表明问题在于驱动未被正确编译或宏使用不当。解决方案包括检查Kconfig配置以确保驱动被编译,以及正确使用设备树宏来访问设备节点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实验现象

  • 以下为设备树定义
#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即可。
    • 第二种则是上面遇到的问题,宏的使用方式不正确,需要确认该节点的实例被驱动程序所创建,否则就会提示上述错误。
<think>嗯,用户遇到了“undefined reference to `pm_state_force'”这个编译错误,我需要帮他们解决这个问题。首先,我应该回忆一下这个函数通常是在哪个库或者头文件里定义的。根据之前的知识,Zephyr RTOS中的电源管理(Power Management, PM)相关函数可能需要特定的配置选项才能启用。 用户可能在代码中调用了`pm_state_force`,但编译器找不到这个函数的实现,这说明链接阶段出了问题。可能的原因有几个:首先,检查是否包含了正确的头文件,比如`#include <zephyr/pm/pm.h>`。不过用户可能已经包含了这个头文件,所以接下来需要确认Zephyr的配置是否正确。 在Zephyr项目中,很多功能需要通过Kconfig选项来启用。`pm_state_force`函数可能依赖于`CONFIG_PM``CONFIG_PM_STATE_LOCK`这样的配置。所以用户需要在prj.conf或者项目配置文件中设置这些选项。如果用户没有启用这些配置,即使包含了头文件,编译器也无法找到对应的实现,导致链接错误。 另外,Zephyr的版本也是一个可能的问题点。如果用户使用的是较旧的Zephyr版本,`pm_state_force`函数可能不存在或者名称有变化。比如,某些版本中电源管理的API可能会有变动,导致函数被重命名或移除。这时候需要建议用户检查他们的Zephyr版本,并对照官方文档确认API的兼容性。 还有可能用户在代码中错误地调用了函数,比如参数不正确或者函数签名不匹配。比如,`pm_state_force`可能需要特定的参数类型或数量,如果用户传递的参数不正确,可能会导致编译器无法识别函数,尽管这种情况通常会产生不同的错误信息。 接下来,我应该逐步引导用户检查这些可能性。首先确认配置是否正确,特别是`CONFIG_PM``CONFIG_PM_STATE_LOCK`是否启用。然后建议他们检查Zephyr的版本,确保使用的版本支持该函数。如果问题依旧存在,可能需要查看官方文档或示例代码,确认函数的使用方式是否正确。 此外,用户可能在之前的讨论中提到了创建自定义板并处理功耗问题,所以他们可能是在尝试配置低功耗模式时遇到了这个问题。这时候,除了解决编译错误,还需要确保他们的电源管理配置正确,以实际降低功耗,而不仅仅是编译通过。可能需要提醒用户在解决编译问题后,继续按照之前的建议进行功耗测试和优化。 最后,总结解决步骤:检查配置选项、验证Zephyr版本、确认函数用法正确,并参考官方示例。如果这些步骤都正确,可能需要进一步检查构建系统是否包含必要的源文件或库,或者是否存在拼写错误等低级错误。</think>--- ### **问题分析** 当编译Zephyr项目时出现`undefined reference to 'pm_state_force'`错误,表明链接器无法找到该函数的实现。此函数属于Zephyr电源管理(PM)子系统,常见原因如下: --- ### **1. 配置缺失检查** #### (1) **启用电源管理核心功能** 在`prj.conf`中添加: ```conf CONFIG_PM=y # 启用电源管理子系统 CONFIG_PM_STATE_LOCK=y # 允许强制锁定电源状态 ``` #### (2) **依赖关系验证** 确认以下关联配置已启用: ```conf CONFIG_PM_DEVICE=y # 设备级电源控制 CONFIG_PM_DEVICE_RUNTIME=y # 动态设备电源管理 ``` --- ### **2. Zephyr版本兼容性排查** | 版本范围 | 关键差异 | 适配建议 | |----------------|------------------------------------|---------------------------| | **Zephyr < 3.0** | `pm_state_force`未实现 | 升级到**v3.0+** | | **Zephyr ≥ 3.2** | 函数签名变更为`pm_state_force(uint8_t state)` | 修改调用方式 | --- ### **3. 代码实现修正** #### (1) **标准调用方式** ```c #include <zephyr/pm/pm.h> // 必须包含头文件 void main(void) { /* 强制进入特定低功耗状态 */ pm_state_force(0, &(struct pm_state_info){ .state = PM_STATE_SUSPENDED_TO_RAM // 选择目标状态 }); } ``` #### (2) **常见错误修正** - **错误1**:未初始化`pm_state_info`结构体 **修复**:显式设置所有字段,例如: ```c .state = PM_STATE_SUSPENDED_TO_RAM, .exit_latency_us = 100, .entry_latency_us = 100 ``` - **错误2**:未启用状态对应的功能 **修复**:在`prj.conf`中启用目标电源状态: ```conf CONFIG_PM_STATE_SUSPENDED_TO_RAM=y ``` --- ### **4. 构建系统检查** #### (1) **清除编译缓存** ```bash rm -rf build/ # 删除旧编译目录 west build --pristine # 强制全量重新编译 ``` #### (2) **查看符号表验证** 编译后检查`zephyr.map`文件,搜索`pm_state_force````bash arm-none-eabi-nm build/zephyr/zephyr.elf | grep pm_state_force ``` **预期输出**:应显示函数地址(如 `00000000 T pm_state_force`) --- ### **5. 官方参考实现对比** 在Zephyr源码中检查示例实现: ```bash # 查看函数定义位置 grep -rn 'pm_state_force(' ${ZEPHYR_BASE}/zephyr/drivers/pm/ ``` --- ### **典型解决方案** 1. **配置修复**:90%的问题由缺失`CONFIG_PM``CONFIG_PM_STATE_LOCK`引起 2. **API更新**:若使用新版Zephyr,替换为`pm_state_force(PM_STATE_SUSPENDED_TO_RAM)` 3. **依赖补齐**:当使用自定义板时,需在`<board>.dts`中定义电源管理节点 完成上述修正后,重新编译应能消除该链接错误。若仍存在问题,建议提供完整的**编译日志**和**prj.conf内容**以进一步分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

咕咚.萌西

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

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

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

打赏作者

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

抵扣说明:

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

余额充值