Zephyr OS 驱动篇之设备初始化顺序

本文详细解析了ZephyrOS中的设备初始化顺序,包括不同级别设备的初始化流程及同一级别下优先级对初始化顺序的影响。

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

Zephyr OS 驱动篇之设备初始化顺序
在前面的 Zephyr OS 驱动篇之设备驱动模型 中已讲解了 Zephyr OS 中的设备驱动模型。Zephyr OS 将设备分为 PRIMARY、SECONDARY、NANOKERNEL 等五个等级,并在系统启动的相应阶段初始化该等级内的所有设备。那么问题来了,每个等级内有很多设备,它们的初始化时有依赖关系吗,即它们需要按照某个顺序初始化吗?

答案是:YES!

再看看设备的定义:
  1. #define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \
  2.                 level, prio, api) \
  3.     \
  4.     static struct device_config __config_##dev_name __used \
  5.     __attribute__((__section__(".devconfig.init"))) = { \
  6.         .name = drv_name, .init = (init_fn), \
  7.         .config_info = (cfg_info) \
  8.     }; \
  9.     \
  10.     static struct device (__device_##dev_name) __used \
  11.     __attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
  12.          .config = &(__config_##dev_name), \
  13.          .driver_api = api, \
  14.          .driver_data = data \
  15.     }
复制代码
在这个宏定义中,有两个参数至关重要,level 和 prio。这两个参数没有出现在具体的代码中,而是出现在 __attribute__ 属性中:
  1. __attribute__((__section__(".init_" #level STRINGIFY(prio))))
复制代码
编译器在预编译的时候会将 “#” 后面到参数转换为字符串,例如 #PRIMARY 将被转换为 “PRIMARY”。
TRINGIFY(s) 的作用也是将参数 s 转换为字符串 “s”, 其代码如下:
  1. #define _STRINGIFY(x) #x
  2. #define STRINGIFY(s) _STRINGIFY(s)
复制代码
举个例如,如果在调用 DEVICE_AND_API_INIT 时传入的参数 level 和 prio 分别为 PRIMARY 和 50,那么编译器就会将上面那段代码放到名为 .init_PRIMARY50 的段中。



现在转移视线,在 linker-defs.h 中有如下代码
  1. #define    DEVICE_INIT_SECTIONS()            \
  2.         __device_init_start = .;        \
  3.         DEVICE_INIT_LEVEL(PRIMARY)        \
  4.         DEVICE_INIT_LEVEL(SECONDARY)    \
  5.         DEVICE_INIT_LEVEL(NANOKERNEL)    \
  6.         DEVICE_INIT_LEVEL(MICROKERNEL)    \
  7.         DEVICE_INIT_LEVEL(APPLICATION)    \
  8.         __device_init_end = .;        \
  9.         DEVICE_BUSY_BITFIELD()        \

  10. DEVICE_INIT_LEVEL 的定义如下:
  11. #define DEVICE_INIT_LEVEL(level)                \
  12.         __device_##level##_start = .;            \
  13.         KEEP(*(SORT(.init_##level[0-9])));        \
  14.         KEEP(*(SORT(.init_##level[1-9][0-9])));    \
复制代码
在链接脚本文件 linker.ld 中将会调用上述代码。上面代码的大意是将所有的设备定义的代码按照设备等级(level)依次排列,且在每个等级中,按数字(prio)的大小从小到大依次排列。

假设系统一共定义了十个设备,它们的参数如下:

devicelevelprio
设备 APRIMARY     32
设备 BPRIMARY     24
设备 CNANOKERNEL     50
设备 DPRIMARY     30
设备 EMICROKERNEL     30
设备 FAPPLICATION     3
设备 GSECONDARY     18
设备 HPRIMARY     0
设备 ISECONDARY     44
设备 JPRIMARY     20


编译器会按照下面的顺序依次存放各个设备的代码:

devicelevelprio
设备 HPRIMARY     0
设备 JPRIMARY     20
设备 BPRIMARY     24
设备 DPRIMARY     30
设备 APRIMARY     32
设备 GSECONDARY     18
设备 ISECONDARY     44
设备 CNANOKERNEL     50
设备 EMICROKERNEL     30
设备 FAPPLICATION     3

所以系统启动时,各设备的初始化顺序是 H, J, B, D, A, G, I, C, E, F。

总结一下,在系统初始化设备时,除了要按照设备等级(level)排序外,在每个等级内部还要按照优先级(prio)排序。
<think>我们正在讨论Zephyr OS中如何创建和配置自定义开发板。根据引用[1]中关于board模块的描述,boards目录下按处理器架构划分,每个目录包含dts、Kconfig等配置文件。用户需要创建自定义开发板时,需要按照这个结构添加新板级支持。 创建自定义开发板的步骤: 1. 确定目标硬件平台(处理器架构和具体型号) 2. 在zephyrproject/zephyr/boards目录下创建新目录,目录结构为<架构>/<板卡名称>,例如arm/my_custom_board 3. 在板卡目录下创建必要的文件: - 板级设备树文件:<板卡名称>.dts(描述硬件资源) - Kconfig.board和Kconfig.defconfig:板级配置选项 - 板级支持包(BSP)代码(可选):如pinmux配置、时钟初始化等 4. 修改设备树绑定(如果需要新外设) 5. 测试构建 详细步骤: 步骤1:创建板级目录 假设我们基于STM32F407芯片创建名为"my_custom_board"的开发板,架构为arm。 在zephyr/boards/arm下创建目录my_custom_board。 步骤2:添加设备树文件 创建my_custom_board.dts文件,内容参考同系列其他开发板。例如: ```dts /dts-v1/; #include <st/f4/stm32f407Xg.dtsi> #include "my_custom_board-pinctrl.dtsi" // 引脚控制配置 / { model = "My Custom Board"; compatible = "mycompany,my-custom-board"; chosen { zephyr,console = &usart1; zephyr,shell-uart = &usart1; }; // 扩展板载外设配置,如LED、按钮等 leds { compatible = "gpio-leds"; led0: led_0 { gpios = <&gpioc 13 GPIO_ACTIVE_HIGH>; label = "User LED"; }; }; }; ``` 步骤3:添加Kconfig配置 创建Kconfig.board文件定义板级符号: ``` config BOARD_MY_CUSTOM_BOARD bool "My Custom Board" depends on SOC_STM32F407XE ``` 创建Kconfig.defconfig文件设置默认配置: ``` if BOARD_MY_CUSTOM_BOARD config BOARD default "my_custom_board" endif # BOARD_MY_CUSTOM_BOARD ``` 步骤4:添加引脚控制配置(可选) 如果需要自定义引脚复用,创建pinctrl.dtsi文件,例如my_custom_board-pinctrl.dtsi。 步骤5:测试构建 使用west命令构建: ``` west build -b my_custom_board samples/hello_world ``` 注意事项: - 如果硬件与现有开发板相似,可以复制相近的板级目录并修改。 - 设备树绑定文件(YAML)位于dts/bindings目录,如果添加新外设需要编写绑定文件[^1]。 - 板级支持包(BSP)代码可以放在board目录下的源文件中,例如board.c,实现初始化函数如board_init()。 通过以上步骤,用户可以为自定义硬件平台添加支持,并利用Zephyr的模块化特性构建应用程序[^3]。 相关问题: 1. 如何为自定义开发板添加设备驱动支持? 2. 在Zephyr中如何调试自定义开发板的启动问题? 3. 如何优化自定义开发板的电源管理配置?</think>### Zephyr OS 自定义开发板创建与配置指南 在 Zephyr 中创建自定义开发板需要遵循特定的目录结构和配置文件要求,以下是详细步骤: #### 1. 创建开发板目录结构 在 `zephyrproject/zephyr/boards/` 目录下创建符合架构规范的新目录: ```bash zephyrproject/zephyr/boards/ └── <架构类型>/ └── <自定义板名称>/ # 例如:arm/my_custom_board ├── doc/ ├── dts/ # 设备树配置 │ ├── arm/ │ │ └── <soc型号>.dtsi # SoC级定义 │ └── <自定义板名称>.dts # 板级定义 ├── Kconfig.board # 板级配置选项 ├── Kconfig.defconfig # 默认配置 └── support/ # 板级支持包(BSP) └── board.c # 硬件初始化代码 ``` 引用[1]指出:*"boards板级相关的代码和配置文件,boards下一层级目录结构与arch很相似,依据不同的处理器架构划分"*[^1] #### 2. 核心配置文件详解 **(1) 设备树文件(.dts/.dtsi)** - **板级定义** (`my_custom_board.dts`): ```dts /dts-v1/; #include <st/f4/stm32f407Xg.dtsi> // 引用SoC定义 / { model = "My Custom Board"; compatible = "mycompany,my-custom-board"; chosen { zephyr,console = &usart1; // 指定调试串口 zephyr,shell-uart = &usart1; }; leds { compatible = "gpio-leds"; led0: led_0 { gpios = <&gpioc 13 GPIO_ACTIVE_HIGH>; label = "User LED"; }; }; }; ``` - **SoC级定义**:继承芯片厂商提供的标准定义 **(2) Kconfig 配置文件** - `Kconfig.board` 定义板级选项: ```kconfig config BOARD_MY_CUSTOM_BOARD bool "My Custom Board" depends on SOC_STM32F407XE ``` - `Kconfig.defconfig` 设置默认选项: ```kconfig if BOARD_MY_CUSTOM_BOARD config BOARD default "my_custom_board" config UART_1 default y endif ``` **(3) 板级支持包(BSP)** 在 `board.c` 中实现硬件初始化: ```c #include <zephyr/init.h> #include <zephyr/drivers/gpio.h> static int board_init(const struct device *dev) { /* 初始化板载LED */ const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_NODELABEL(led0), gpios); gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE); return 0; } SYS_INIT(board_init, POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY); ``` #### 3. 构建与测试 1. **注册开发板**: ```bash west build -b my_custom_board samples/hello_world ``` 2. **裁剪配置**(引用[2]): *"通过仔细选择必要的内核模块和功能,去除不必要的组件...在Zephyr的配置文件中,通过修改CONFIG_*宏控制模块启用"*[^2] ```bash west build -t menuconfig # 图形化配置 ``` 3. **验证启动**: ```bash west flash # 烧录程序 minicom -D /dev/ttyACM0 # 查看串口输出 ``` #### 4. 自定义外设集成 当添加新硬件时: 1. 在 `dts/` 中定义设备节点: ```dts &i2c1 { status = "okay"; custom_sensor: sensor@1a { compatible = "mycompany,custom-sensor"; reg = <0x1a>; }; }; ``` 2. 在 `drivers/` 目录创建对应驱动 3. 添加 YAML 绑定文件(`dts/bindings/sensor/mycompany,custom-sensor.yaml`) #### 5. 最佳实践 1. **模块化设计**(引用[3]): *"模块化:针对受限制的物联网设备而设计,通过Kconfig裁剪功能选项"*[^3] 2. **复用现有配置**:从类似开发板复制基础配置 3. **版本控制**:将自定义板配置纳入Git管理 4. **持续集成**:添加板级测试用例到 `tests/` 目录 > **调试技巧**:使用 `west build -t guiconfig` 可视化配置依赖,通过 `CONFIG_DEBUG_DEVICETREE=y` 查看设备树解析日志。 --- ### 相关问题 1. 如何为自定义开发板添加设备树绑定(Device Tree Binding)? 2. 在资源受限设备上如何优化自定义板的Zephyr内核大小? 3. 如何利用Zephyr的电源管理框架为自定义板实现低功耗设计? 4. 自定义开发板出现启动失败时,如何进行系统级调试?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值