资讯详情

Zephyr:undefined reference to `__device_dts_ord_xx

我最近为开发板开发了一个wm89xx由于编码器驱动程序,Zephyr我对多媒体的支持较少,所以我打算自己做Zephyr开发Driver,我的注册方式是drivertree,注册代码如下:

#define WM89XX_CONFIG_I2C(inst) \         {                       \                 .bus.i2c = I2C_DT_SPEC_INST_GET(inst), \                 .bus_io = &wm89xx_bus_io_i2c,           \         } #define WM89XX_DEFINE(inst)\         static struct WM89XX_Data wm89xx_data_##inst; \         static const struct WM89XX_Config wm89xx_config_##inst = \                                                 WM89XX_CONFIG_I2C(inst); \         DEVICE_DT_INST_DEFINE(inst,     \                               wm89xx_init,\                               NULL,\                               &wm89xx_data_##inst,\                               &wm89xx_config_##inst,\                               POST_KERNEL,\                               MY_INIT_PRIO,\                               &wm89xx_api);  DT_INST_FOREACH_STATUS_OKAY(WM89XX_DEFINE)

但我在编译时遇到了一个问题:

:/home/zhihao/zephyrproject/zephyr/drivers/wm89xx/src/wm89xx.c:39: undefined reference to `__device_dts_ord_92' collect2: error: ld returned 1 exit status

提醒我未定义的引用设备dts ord 所以我仔细研究了我的注册代码,最后发现问题在于这个注册宏:

DEVICE_DT_INST_DEFINE(inst,     \                               wm89xx_init,\                               NULL,\                               &wm89xx_data_##inst,\                               &wm89xx_config_##inst,\                               POST_KERNEL,\                               MY_INIT_PRIO,\                               &wm89xx_api);

于是我去查了一下Zephyr查看这个宏的实现(为了让大家更好的理解,我把代码中的注释翻译成中文):

/** *@def DEVICE_DT_INST_DEFINE * *@brief Like DEVICE_DT_为DT_DRV_COMPAT兼容的实例定义 * *@param inst实例号。这被替换为 *<tt>DT\u DRV\u COMPAT(inst)</tt>定义设备的调用。 * *@param。。。定义设备预期的其他参数。 */ #define DEVICE_DT_INST_DEFINE(inst, ...) \         DEVICE_DT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__)

通过以上的定义和描述,我理解了device_ dts_ ord_92,其中92是树中设备的ID,所以我认为这个函数是一个自动生成的函数,所以理论上应该是zephyr结构工具自动生成,但我不知道为什么这个函数不是为我的驱动程序项目生成的,所以我检查了生成zephyr.map文件(在build/zephyr目录下)

zephyr.map:2282:                0x0000000008003b60                __device_dts_ord_18 zephyr.map:2286:                0x0000000008003b78                __device_dts_ord_274 zephyr.map:2287:                0x0000000008003b90                __device_dts_ord_273 zephyr.map:2288:                0x0000000008003ba8                __device_dts_ord_19 zephyr.map:2289:                0x0000000008003bc0                __device_dts_ord_272 zephyr.map:2290:                0x0000000008003bd8                __device_dts_ord_271 zephyr.map:2291:                0x0000000008003bf0                __device_dts_ord_270 zephyr.map:2292:                0x0000000008003c08                __device_dts_ord_269 zephyr.map:2293:                0x0000000008003c20                __device_dts_ord_268 zephyr.map:2294:                0x0000000008003c38                __device_dts_ord_49 zephyr.map:2295:                0x0000000008003c50                __device_dts_ord_267 zephyr.map:2296:                0x0000000008003c68                __device_dts_ord_69 zephyr.map:2299:                0x0000000008003c80                __device_dts_ord_45 zephyr.map:2302:                0x0000000008003c98                __device_dts_ord_68 zephyr.map:2303:                0x0000000008003cb0                __device_dts_ord_65 zephyr.map:2313:                0x0000000008003ce0                __device_dts_ord_94 zephyr.map:2318:                0x0000000008003d10                __device_dts_ord_42

我的函数没有导入映射,所以我相信构建器没有为我生成这个函数

我又检查了一遍DTS文件中的定义。因为编码器被用作I2C所以我在I2C下声明了它

&i2c3 {         pinctrl-0 = <&i2c3_scl_ph7 &i2c3_sda_ph8>;         status = "okay";         clock-frequency = <I2C_BITRATE_FAST>;          wm89xx@1a{                 compatible = "micro,wm89xx";                 reg = <0x1a>;                 label = "wm89xx";                 status = "okay";         }; };

我认为上述DTS定义应该没有问题

所以我回来了zephyr DEVICE_DT_INST_DEFINE,在定义源代码的阶段,我发现它被称为DEVICE_ DT_ DEFINE,所以我又检查了它的源代码

最后,我找到了这个宏函数,它是最终注册驱动的宏

/* 与DEVICE_DEFINE类似,但使用节点id跟随开发人员的名称  * 来自外部devicetree的依赖句柄  */ #define Z_DEVICE_DEFINE(node_id, dev_name, drv_name, init_fn, pm_action_cb, \                         data_ptr, cfg_ptr, level, prio, api_ptr, ...)   \         Z_DEVICE_DEFINE_PRE(node_id, dev_name, pm_action_cb, __VA_ARGS__) \         COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static))              \                 const Z_DECL_ALIGN(struct device                       \
                DEVICE_NAME_GET(dev_name) __used                        \
        __attribute__((__section__(".z_device_" #level STRINGIFY(prio)"_"))) = { \
                .name = drv_name,                                       \
                .config = (cfg_ptr),                                    \
                .api = (api_ptr),                                       \
                .state = &Z_DEVICE_STATE_NAME(dev_name),                \
                .data = (data_ptr),                                     \
                Z_DEVICE_DEFINE_INIT(node_id, dev_name)                 \
        };                                                              \
        BUILD_ASSERT(sizeof(Z_STRINGIFY(drv_name)) <= Z_DEVICE_MAX_NAME_LEN, \
                     Z_STRINGIFY(DEVICE_NAME_GET(drv_name)) " too long"); \
        Z_INIT_ENTRY_DEFINE(DEVICE_NAME_GET(dev_name), init_fn,         \
                (&DEVICE_NAME_GET(dev_name)), level, prio)

通过对源代码的分析,我知道'Z_DEVICE_ DEFINE_ P'注册诸如电源管理之类的驱动程序函数,并使用'COND_ CODE_ 1'确定驱动程序是否已传入电源函数指针,'Z_DECL_ALIGN'用于做段对其。

真正引起我注意的是这个宏:

Z_DEVICE_DEFINE_INIT(node_id, dev_name) 

展开并进入,这个宏是这样描述的:

/*初始生成提供与设备对象关联的记录
*使用其devicetree序号,并提供依赖项序号。
*这些是作为弱定义提供的(以防止引用
*在编译原始对象文件时被捕获),以及
*在不同的pass1节中(将替换为
*后处理)。
*
*(实验上)还需要提供明确的对齐
*在每个对象上。否则x86-64版本将引入填充
*单个对象中同一输入区域中的对象之间
*文件,这些文件将保留在后续链接中
*空间,并导致相对于pass2的聚合大小更改
*当所有对象都位于同一输入部分时。
*
*如果设备句柄更改大小,则生成断言将失败,这将导致
*指链接器脚本和中的对齐指令
*“gen_handles.py`必须更新。
*/

我注意到这句话:“使用其devicetree序号,并提供依赖项序号”和Z_DEVICE_DEFINE中的描述:“但采用节点id和开发人员名称,以及来自设备树外部的后续依赖句柄”

因此,我认为它需要一些联想。我的设备在I2C DTS下声明,包括我的驱动程序中使用的一些I2C设备代码。因此,我认为有必要设置一些选项来启用I2C支持,但DTS中的“status”已被写为“ok”,因此我在示例中引用了一些编译后的示例

我使用的是'sensor/bme280'的示例。我发现在这个例子中有一个Kconf

```config
config LOG
        default y

config LOG_PRINTK
        default y

config SENSOR_LOG_LEVEL
        default 4

#默认情况下启用SPI和I2C支持,以便示例可以与
#设备以任意方式连接。如果需要,可以覆盖这些默认值
#需要。
config SPI
        default y

config I2C
        default y

source "Kconfig.zephyr"
```

我开始尝试。我发现当我将'config I2C'设置为N时,会出现与我相同的问题,这让我非常惊喜,因为大致已经找到了问题,然后我在我的驱动程序kconfig中将I2C设置为y时,我的驱动项目被编译通过了!

所以我猜原因是'Z_DEVICE_DEFINE'宏DEFINE获取依赖设备节点的属性,它使用'COND_CODE_1'这个宏执行条件编译,这个条件编译中就会判断附加依赖设备节点的Driver是否被编译进来了,如果没有被编译则不会为子依赖设备生成device_dts_ord函数,而驱动需要device_dts_ord函数来注册到Zephyr内核里去,所以在最终link时找不到这个函数。

为了进一步验证我的猜测,我将app项目中的'prj.conf'里添加到`CONF_I2C=y`,发现效果与`Kconfig`中的效果相同,所以我的结论基本确定:当你的驱动设备是使drivertree方式从dts文件中获取注册信息时,如果这个设备依赖某个父节点设备那么需要在编译时将父节点的驱动一并编译进来,否则Z_DEVICE_DEFINE中的COND_CODE_1条件宏判定依赖父节点的驱动没有被编译那么不会生成device_dts_ord函数。

这个问题我已经提交到了Zephyr官方的Issue中,大家可以在这里找到我提交的issue:undefined reference to `__device_dts_ord_xx' · Issue #41677 · zephyrproject-rtos/zephyr · GitHub

标签: 2290连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台