【简介】
在之前的 Zephyr 的文章中我们使用了GPIO 和 UART 的驱动,因为代码中没有直接调用外设的初始化接口,对应的外设是怎么被初始化的,我们以UART驱动为例了解下Uart驱动的编译初始化过程。
【UART 驱动文件的编译配置】
UART 的驱动在Zephyr 串行驱动设备(serial) 目录下,打开该目录下会有很多的芯片的串行设备驱动。
查看对应目录下的“CMakeLists.txt” 文件,编译系统是根据对应的配置宏来决定将对应的串口驱动加入工程编译。
# SPDX-License-Identifier: Apache-2.0 zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/uart.h) zephyr_library() # zephyr-keep-sorted-start zephyr_library_sources_ifdef(CONFIG_UART_MCHP_MEC5 uart_mchp_mec5.c) zephyr_library_sources_ifdef(CONFIG_UART_MCUX uart_mcux.c) zephyr_library_sources_ifdef(CONFIG_UART_MCUX_FLEXCOMM uart_mcux_flexcomm.c) zephyr_library_sources_ifdef(CONFIG_UART_MCUX_IUART uart_mcux_iuart.c) zephyr_library_sources_ifdef(CONFIG_UART_MCUX_LPSCI uart_mcux_lpsci.c) zephyr_library_sources_ifdef(CONFIG_UART_MCUX_LPUART uart_mcux_lpuart.c) zephyr_library_sources_ifdef(CONFIG_UART_MIV uart_miv.c) zephyr_library_sources_ifdef(CONFIG_UART_MSP432P4XX uart_msp432p4xx.c) zephyr_library_sources_ifdef(CONFIG_UART_NEORV32 uart_neorv32.c) zephyr_library_sources_ifdef(CONFIG_UART_NPCX uart_npcx.c) zephyr_library_sources_ifdef(CONFIG_UART_NRFX_UART uart_nrfx_uart.c) zephyr_library_sources_ifdef(CONFIG_UART_NS16550 uart_ns16550.c) zephyr_library_sources_ifdef(CONFIG_UART_NUMAKER uart_numaker.c) zephyr_library_sources_ifdef(CONFIG_UART_NUMICRO uart_numicro.c) zephyr_library_sources_ifdef(CONFIG_UART_NXP_S32_LINFLEXD uart_nxp_s32_linflexd.c) zephyr_library_sources_ifdef(CONFIG_UART_OPENTITAN uart_opentitan.c) zephyr_library_sources_ifdef(CONFIG_UART_PIPE uart_pipe.c) zephyr_library_sources_ifdef(CONFIG_UART_PL011 uart_pl011.c) zephyr_library_sources_ifdef(CONFIG_UART_PSOC6 uart_psoc6.c) zephyr_library_sources_ifdef(CONFIG_UART_QUICKLOGIC_USBSERIALPORT_S3B uart_ql_usbserialport_s3b.c) zephyr_library_sources_ifdef(CONFIG_UART_RA8_SCI_B uart_renesas_ra8_sci_b.c) zephyr_library_sources_ifdef(CONFIG_UART_RCAR uart_rcar.c) zephyr_library_sources_ifdef(CONFIG_UART_RENESAS_RZ_SCI uart_renesas_rz_sci.c) zephyr_library_sources_ifdef(CONFIG_UART_RENESAS_RZ_SCIF uart_renesas_rz_scif.c) zephyr_library_sources_ifdef(CONFIG_UART_RPI_PICO_PIO uart_rpi_pico_pio.c) zephyr_library_sources_ifdef(CONFIG_UART_RTS5912 uart_realtek_rts5912.c) zephyr_library_sources_ifdef(CONFIG_UART_RTT_DRIVER uart_rtt.c) zephyr_library_sources_ifdef(CONFIG_UART_RV32M1_LPUART uart_rv32m1_lpuart.c) zephyr_library_sources_ifdef(CONFIG_UART_RZT2M uart_rzt2m.c) zephyr_library_sources_ifdef(CONFIG_UART_SAM uart_sam.c) zephyr_library_sources_ifdef(CONFIG_UART_SAM0 uart_sam0.c) zephyr_library_sources_ifdef(CONFIG_UART_SCI_RA uart_renesas_ra_sci.c) zephyr_library_sources_ifdef(CONFIG_UART_SEDI uart_sedi.c) zephyr_library_sources_ifdef(CONFIG_UART_SI32_USART uart_si32_usart.c) zephyr_library_sources_ifdef(CONFIG_UART_SIFIVE uart_sifive.c) zephyr_library_sources_ifdef(CONFIG_UART_SILABS_EUSART uart_silabs_eusart.c) zephyr_library_sources_ifdef(CONFIG_UART_SILABS_USART uart_silabs_usart.c) zephyr_library_sources_ifdef(CONFIG_UART_SMARTBOND uart_smartbond.c) zephyr_library_sources_ifdef(CONFIG_UART_STELLARIS uart_stellaris.c) zephyr_library_sources_ifdef(CONFIG_UART_STM32 uart_stm32.c) zephyr_library_sources_ifdef(CONFIG_UART_SY1XX uart_sy1xx.c) zephyr_library_sources_ifdef(CONFIG_UART_TELINK_B91 uart_b91.c) zephyr_library_sources_ifdef(CONFIG_UART_WCH_USART uart_wch_usart.c) zephyr_library_sources_ifdef(CONFIG_UART_XEC uart_mchp_xec.c) zephyr_library_sources_ifdef(CONFIG_UART_XEN_HVC uart_hvc_xen.c) zephyr_library_sources_ifdef(CONFIG_UART_XEN_HVC_CONSOLEIO uart_hvc_xen_consoleio.c) zephyr_library_sources_ifdef(CONFIG_UART_XLNX_PS uart_xlnx_ps.c) zephyr_library_sources_ifdef(CONFIG_UART_XLNX_UARTLITE uart_xlnx_uartlite.c) zephyr_library_sources_ifdef(CONFIG_UART_XMC4XXX uart_xmc4xxx.c) zephyr_library_sources_ifdef(CONFIG_USART_GD32 usart_gd32.c) zephyr_library_sources_ifdef(CONFIG_USART_SAM usart_sam.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE uart_handlers.c) # zephyr-keep-sorted-stop if (CONFIG_UART_NRFX_UARTE) if (CONFIG_UART_NRFX_UARTE_LEGACY_SHIM) zephyr_library_sources(uart_nrfx_uarte.c) else() message(DEPRECATION "Do not set CONFIG_UART_NRFX_UARTE_LEGACY_SHIM=n as this option is deprecated.") zephyr_library_sources(uart_nrfx_uarte2.c) endif() endif() if(CONFIG_UART_NATIVE_PTY) zephyr_library_compile_definitions(NO_POSIX_CHEATS) zephyr_library_sources(uart_native_pty.c) if (CONFIG_NATIVE_APPLICATION) zephyr_library_sources(uart_native_pty_bottom.c) else() target_sources(native_simulator INTERFACE uart_native_pty_bottom.c) endif() endif() if(CONFIG_UART_NATIVE_TTY) zephyr_library_compile_definitions(NO_POSIX_CHEATS) zephyr_library_sources(uart_native_tty.c) if (CONFIG_NATIVE_APPLICATION) zephyr_library_sources(uart_native_tty_bottom.c) else() target_sources(native_simulator INTERFACE uart_native_tty_bottom.c) endif() endif()
对应的配置宏是如何生成的呢?我们试验使用的MCXN947 对应的“Kconfig.mcux_lpuart”kconfig 文件中定义该配置需要依赖 设备树中enable NXP_KILETIS 的配置项
我们查看下 build/zephyr 生成的.config 文件果然发现了该定义
从以下的生成配置文件的构建过程可以知道,zephyr 在编译系统的配置阶段会将DTS中的信息生成到 .config 配置文件中
我们查看DTS 文件中串口设备的配置如下
flexcomm2_lpuart2: lpuart@94000 { compatible = "nxp,lpuart"; reg = <0x94000 0x1000>; clocks = <&syscon MCUX_FLEXCOMM2_CLK>; /* DMA channels 4 and 5, muxed to LPUART2 RX and TX */ dmas = <&edma0 4 73>, <&edma0 5 74>; dma-names = "rx", "tx"; status = "disabled"; };
上述节点为status = "okay" 的节点,编译配置系统会将其转化为CONFIG_DT_HAS_"compatible"_ENABLE的配置项,对应为CONFIG_DT_HAS_NXP_LPUART_ENABLED。
我们关闭该配置开关,再次生成的.config 文件 已经没有了对应 “DT_HAS_NXP_KINETIS_UART_ENABLED” 的配置。
以下是官方文档对compatible 属性的作用描述,或根据该属性来管理使用的驱动文件。
【UART 驱动的初始化调用】
Zephyr 系统的初始化代码是通过\zephyr\kernel\init.c 文件完成的,z_sys_init_run_level->do_device_init 将初始化代码划分为以下几个level 然后通过do_device_init 调用对应的device的init 函数。
static const struct init_entry *levels[] = { __init_EARLY_start, __init_PRE_KERNEL_1_start, __init_PRE_KERNEL_2_start, __init_POST_KERNEL_start, __init_APPLICATION_start, #ifdef CONFIG_SMP __init_SMP_start, #endif /* CONFIG_SMP */ /* End marker */ __init_end, };
初始化相关的函数内容如下:
static int do_device_init(const struct device *dev) { int rc = 0; if (dev->ops.init != NULL) { rc = dev->ops.init(dev); /* Mark device initialized. If initialization * failed, record the error condition. */ if (rc != 0) { if (rc < 0) { rc = -rc; } if (rc > UINT8_MAX) { rc = UINT8_MAX; } dev->state->init_res = rc; } } dev->state->initialized = true; if (rc == 0) { /* Run automatic device runtime enablement */ (void)pm_device_runtime_auto_enable(dev); } return rc; } /** * @brief Execute all the init entry initialization functions at a given level * * @details Invokes the initialization routine for each init entry object * created by the INIT_ENTRY_DEFINE() macro using the specified level. * The linker script places the init entry objects in memory in the order * they need to be invoked, with symbols indicating where one level leaves * off and the next one begins. * * @param level init level to run. */ static void z_sys_init_run_level(enum init_level level) { static const struct init_entry *levels[] = { __init_EARLY_start, __init_PRE_KERNEL_1_start, __init_PRE_KERNEL_2_start, __init_POST_KERNEL_start, __init_APPLICATION_start, #ifdef CONFIG_SMP __init_SMP_start, #endif /* CONFIG_SMP */ /* End marker */ __init_end, }; const struct init_entry *entry; for (entry = levels[level]; entry < levels[level+1]; entry++) { const struct device *dev = entry->dev; int result = 0; sys_trace_sys_init_enter(entry, level); if (dev != NULL) { if ((dev->flags & DEVICE_FLAG_INIT_DEFERRED) == 0U) { result = do_device_init(dev); } } else { result = entry->init_fn(); } sys_trace_sys_init_exit(entry, level, result); } }
上述代码按照事先定义好的level进行初始话调用, 在编译阶段会将对应level 的设备驱动放到对应的区域进行管理,我们的uart 驱动API定义如下。
在编译时会将PRE_KERNEL_1阶段,从而完成init 接口的初始化调用。查看编译后的map 文件一可以看出我们在DTS使能的lpuart2/4 设备驱动已经被预编译到对应的section中了。
对应的linkfile 中对初始化的section,layout 布局定义如下