Zephyr BSP: 01-Zephyr硬件模型教學

发布时间:2026/9/29 6:18:33
Zephyr BSP: 01-Zephyr硬件模型教學 摘要本文从零建立 Zephyr 硬件模型先不写代码。核心是分清ArchitectureCPU 大规则、SoC芯片、Board开发板、Driver外设驱动四个层次以 STM32F303RE 为例Cortex-M4 是 CPU coreSTM32F303RE 是 SoCNUCLEO-F303RE 是 Board同一 SoC 可放多个 BoardSoC 层管 CPU/中断/时钟/外设控制器Board 层管引脚与板载硬件Driver 则是软件控制外设的实现。最后给出层次关系 Architecture → SoC → Board → Peripheral → Driver目录导航01. 先建立一张完整关系图02. 第一件事理解 Architecture03. 用你已经做过的 STM32 来理解04. 第二件事什么是 SoC05. 第三个概念SoC Family / Series06. 第四个概念Board07. 为什么 Zephyr 要区分 SoC 和 Board08. 第五个概念Driver09. 把四个概念放在一起10. 那么公司 SoC 应该放在哪里11. 今天先做一个非常重要的练习12. 你现在只需要回答这 4 个并预告公司 SoC 在 Zephyr 中的落位与 4 个自测练习。Zephyr Hardware ModelArchitecture、SoC、Board 到 Driver这一篇先不写代码。目标是先建立一个以后给公司 SoC 做 Zephyr BSP 时能用的硬件模型。01. 先建立一张完整关系图你原来的图可以稍微调整一下。对于你未来的目标——把公司 SoC 加入 Zephyr 生态——我建议记住下面这张但这里有一个非常重要的地方Architecture、SoC、Board、Driver 是不同层次的东西。不要把它们混在一起。02. 第一件事理解 ArchitectureArchitecture 可以理解成CPU 的大规则。例如ARM └── ARM Cortex-M ├── Cortex-M0 ├── Cortex-M3 ├── Cortex-M4 ├── Cortex-M7 └── Cortex-M33另一边RISC-V └── RV32 ├── RV32I ├── RV32IM └── RV32IMCArchitecture 决定很多底层东西例如- CPU 指令集 - 寄存器模型 - exception / interrupt 机制 - stack - privilege - compiler toolchain - context switching - atomic operation - memory model所以ARM Cortex-M4属于Architecture / CPU architecture这一层。03. 用你已经做过的 STM32 来理解你之前使用的是NUCLEO-F303RE它的核心 MCU 是STM32F303RE它使用ARM Cortex-M4所以关系是Architecture │ └── ARM Cortex-M │ └── Cortex-M4 │ └── STM32F303RE这里Cortex-M4 不是 STM32。Cortex-M4 是 CPU core。STM32F303RE 才是 MCU/SoC。04. 第二件事什么是 SoCSoC**System on Chip**简单理解SoC 是一颗真正可以拿来做产品的芯片。例如STM32F303RE里面并不只有 CPU。大概可以想象成所以Cortex-M4只是 CPU core。而STM32F303RE是把CPU\ Memory\ Interrupt Controller\ Clock\ GPIO\ UART\ SPI\ I2C\ Timer\...集成在一起的芯片。05. 第三个概念SoC Family / Series公司做 SoC 的时候通常不会只做一颗芯片。比如假设你的公司有Company-X │ └── X1 Series │ ├── X100 ├── X101 ├── X102 └── X103那么X1 Series就是一个SoC Family / Series。而X100是具体的 SoC Variant。这对以后写 Zephyr 非常重要。因为你不会希望X100X101X102X103每一颗芯片都从零写一遍 BSP。而应该尽可能设计成SoC Family │ ├── common code │ ├── common clock │ ├── common interrupt │ ├── common UART │ └── variant-specific code │ ├── X100 ├── X101 └── X102这就是你以后做公司 Zephyr BSP 时非常重要的设计思想。06. 第四个概念Board这是最容易和 SoC 混淆的地方。例如NUCLEO-F303RE不是一个 SoC。它是一块开发板 Board里面放了一颗STM32F303RE所以NUCLEO-F303RE │ └── STM32F303RE │ └── Cortex-M4再进一步NUCLEO-F303RE │ ├── STM32F303RE │ ├── ST-LINK │ ├── USB │ ├── LED │ ├── Button │ ├── Arduino headers │ └── external headers因此Board 是一块具体硬件产品。SoC 是板子上的芯片。07. 为什么 Zephyr 要区分 SoC 和 Board因为同一个 SoC 可以放到很多不同的板子上。例如三个 Board 都使用STM32F303RE那么SoC 层负责CPU interrupt clock memory UART controller GPI SPI controller...Board 层负责LED 接在哪个 GPIO Button 接在哪个 GPIO UART 从哪个 pin 出来 SPI 接到了什么外设 晶振是多少 板子有什么硬件这就是为什么SoC BSP 和 Board BSP 不能混为一谈。08. 第五个概念Driver现在进入你已经学过的Custom DriverDriver 的位置更低一层。例如Application │ ↓ Zephyr API │ ↓ Driver │ ↓ Hardware Register │ ↓ UART Hardware例如应用程序uart_poll_out(dev,A);应用并不直接操作UART-TDRA;而是Application ↓ Zephyr UART API ↓ UART Driver ↓ SoC UART registers ↓ UART hardware所以Driver 是软件控制某个硬件 peripheral 的实现。09. 把四个概念放在一起现在看一遍这里已经出现一个非常重要的层次关系为了更直观地对比这四个层次下面用一张表格把它们放在一起|层次|定义|典型例子|主要职责|Zephyr 代码位置||---|---|---|---|---||**Architecture**|CPU 的大规则定义指令集、寄存器模型、中断机制等底层规范|ARM Cortex-M4、RISC-V RV32|指令集、寄存器模型、exception/interrupt 机制、stack、privilege、toolchain、context switching、atomic、memory model|arch/||**SoC**|一颗真正可以拿来做产品的芯片把 CPU 外设控制器集成在一起|STM32F303RE、公司 CX32|CPU、interrupt、clock、memory、UART/SPI/I2C 控制器等|soc/||**Board**|一块具体的硬件开发板/产品决定引脚与板载硬件|NUCLEO-F303RE|LED/Button 接在哪个 GPIO、UART 从哪个 pin 出来、晶振是多少、板子有什么硬件|boards/||**Driver**|软件控制某个硬件 peripheral 的实现|UART Driver、GPIO Driver|把 Zephyr API 翻译成对 SoC 寄存器的读写驱动具体外设|drivers/|Architecture ↓ SoC ↓ Board ↓ Peripheral ↓ Driver 为了更清晰地看清 **SoC 层** 与 **Board 层** 的边界下面再补一张按硬件资源拆分的职责对比表|硬件资源|SoC 层负责|Board 层负责||---|---|---||**GPIO**|提供 GPIO 控制器有几个端口、寄存器怎么读写、中断怎么配置|LED/Button 具体接到了哪个 GPIO 引脚||**UART**|提供 UART 控制器波特率、数据位、FIFO、寄存器操作|UART 从哪个 pin 出来、接到了哪个串口设备||**时钟Clock**|提供时钟源与分频PLL、系统时钟、外设时钟树|板子上晶振是多少 MHz、外部时钟接在哪||**中断Interrupt**|提供中断控制器NVIC、中断号、优先级、使能/挂起|哪个外设的中断在板子上接到了哪里如按键触发||**SPI / I2C**|提供 SPI/I2C 控制器模式、速率、寄存器操作|SPI/I2C 接到了什么外设如传感器、屏幕||**Memory**|提供 Flash/RAM 大小、地址映射、存储控制器|板子上外挂了多大 Flash/RAM、接在哪个片选|一句话总结**SoC 层回答芯片有什么、怎么用Board 层回答板子上把它接到了哪里。** 再换一个角度记忆这两层的边界|判断问题|属于哪一层||---|---||芯片上有几个 GPIO 端口寄存器怎么读写|**SoC 层**||LED / Button 具体接到了哪个 GPIO 引脚|**Board 层**||UART 控制器支持哪些波特率、FIFO 怎么用|**SoC 层**||UART 从板子上哪个 pin 出来、接到了什么设备|**Board 层**||系统时钟树、PLL 怎么分频|**SoC 层**||板子上晶振是多少 MHz、外部时钟接在哪|**Board 层**||中断控制器 NVIC 怎么配置、中断号是多少|**SoC 层**||哪个外设的中断在板子上接到了哪里如按键|**Board 层**|规律很简单**凡是芯片内部怎么设计的问题归 SoC凡是板子上怎么接线的问题归 Board。** Architecture ↓ SoC ↓ Board ↓ Peripheral ↓ Driver10. 那么公司 SoC 应该放在哪里这才是你这套学习路线真正的目标。假设你的公司以后有Company SoC │ └── CX32 │ ├── CX32A ├── CX32B └── CX32CCPU 是RISC-V RV32那么最终 Zephyr 结构可能类似这就是你现在学习 Zephyr 最终要走到的地方。11. 今天先做一个非常重要的练习先不要写代码。拿你已经熟悉的**NUCLEO-F303RE**回答下面 4 个问题。Q1它的 Architecture / CPU core 是什么NUCLEO-F303RE ↓ ?Q2它使用的 SoC / MCU 是什么NUCLEO-F303RE ↓ ?Q3NUCLEO-F303RE 是Architecture SoC Board Driver里面的哪一个Q4假设 NUCLEO-F303RE 上的 LED 接到了某个 GPIO。这个LED ↓ GPIO信息应该主要属于Board还是SoC12. 你现在只需要回答这 4 个不用查资料先凭你已经做过的 Zephyr / STM32 实验回答。格式直接这样Q1: Cortex-M4 Q2: STM32F303RE Q3: Board Q4: Board你回答以后我继续带你做02Zephyr 目录到底是怎么把 Architecture → SoC → Board 组织起来的。这一节会开始真正进入 Zephyr 源码例如zephyr/ ├── arch/ ├── soc/ ├── boards/ ├── drivers/ └── dts/下面先给你一张带注释的目录树把五个目录和前面四个层次对应起来zephyr/ ├── arch/# Architecture 层CPU 大规则│ ├── arm/# ARM 架构相关│ │ ├── core/# 通用 core 逻辑context switch、中断入口等│ │ └── cortex_m/# Cortex-M 系列M0/M3/M4/M7...│ └── riscv/# RISC-V 架构相关│ └── core/# RV32 通用逻辑│ ├── soc/# SoC 层芯片级集成│ ├── st/stm32/# ST 的 STM32 系列│ │ ├── stm32f3/# STM32F3 子系列│ │ │ └── soc.c# 时钟、中断、引脚复用等芯片初始化│ │ └── common/# STM32 各系列共享代码│ └──company/cx32/# 未来公司 SoC 放这里│ ├── cx32a/# CX32A variant│ └── cx32b/# CX32B variant│ ├── boards/# Board 层具体开发板│ ├── arm/nucleo_f303re/# NUCLEO-F303RE 这块板子│ │ ├── board.c# 板级初始化LED、Button 等│ │ ├── board.h# 板级引脚定义│ │ └── Kconfig.board# 板子配置│ └──company/...# 公司自己的板子│ ├── drivers/# Driver 层外设驱动实现│ ├── uart/# UART 驱动│ │ └── uart_stm32.c# STM32 UART 驱动操作 SoC 寄存器│ ├── gpio/# GPIO 驱动│ │ └── gpio_stm32.c# STM32 GPIO 驱动│ └── spi/# SPI 驱动│ └── spi_stm32.c# STM32 SPI 驱动│ └── dts/# 设备树描述硬件长什么样的桥梁├── bindings/# 设备树绑定定义├── arm/st/# ST SoC 的 dtsi 描述└── arm/nucleo_f303re.dts# 板级设备树引脚、外设连接对照关系一目了然arch/→ArchitectureCPU 指令集、中断、context switching 等大规则soc/→SoC芯片级的外设控制器、时钟、中断控制器boards/→Board具体板子的引脚、LED、Button、晶振drivers/→Driver把 Zephyr API 翻译成对 SoC 寄存器的读写dts/→ 设备树是连接Board 引脚与SoC 外设的接线图。再把这五个目录与四个层次的对应关系、以及各子目录的作用整理成一张表方便随时查阅目录对应层次子目录 / 文件作用arch/Architecturearm/core/通用 core 逻辑context switch、中断入口、异常处理arm/cortex_m/Cortex-M 系列M0/M3/M4/M7…的架构相关实现riscv/core/RISC-V RV32 通用逻辑soc/SoCst/stm32/stm32f3/soc.c芯片级初始化时钟、中断控制器、引脚复用st/stm32/common/STM32 各系列共享代码company/cx32/cx32a/未来公司 SoC 的 CX32A variantboards/Boardarm/nucleo_f303re/board.c板级初始化LED、Button 等板载硬件arm/nucleo_f303re/board.h板级引脚定义arm/nucleo_f303re/Kconfig.board板子配置选项drivers/Driveruart/uart_stm32.cSTM32 UART 驱动操作 SoC 寄存器gpio/gpio_stm32.cSTM32 GPIO 驱动spi/spi_stm32.cSTM32 SPI 驱动dts/设备树桥梁bindings/设备树绑定定义描述节点属性arm/st/ST SoC 的 dtsi 描述芯片级arm/nucleo_f303re.dts板级设备树引脚、外设连接板级记忆要点arch/管 CPU 大规则soc/管芯片内部boards/管板子接线drivers/管外设驱动dts/是连接 Board 引脚与 SoC 外设的接线图。参考答案对照前面四个层次这 4 个问题的答案如下Q1: Cortex-M4 Q2: STM32F303RE Q3: Board Q4: BoardQ4 解析为什么 LED 接 GPIO 的信息属于 Board 层关键在于GPIO 控制器本身属于 SoC但LED 接到了哪个 GPIO属于 Board。SoC 层只负责提供 GPIO 控制器这个外设它关心的是我有几个 GPIO、寄存器怎么读写、中断怎么配置这类芯片级能力但这颗 GPIO 具体接到了板子上的哪个 LED、哪个 Button是这块板子的设计者决定的换一块板子可能就接到别的引脚上去了。所以GPIO 控制器芯片上有几个、怎么用 → SoC 层 LED 接到了哪个 GPIO引脚怎么连 → Board 层这正是第 07 节强调的SoC BSP 和 Board BSP 不能混为一谈。在 Zephyr 里引脚连接关系写在boards/下的设备树.dts里而不是soc/里。13. 动手验证一个最小 LED 闪烁工程前面一直在讲概念现在用一个最小的 Zephyr 工程把Board 层和SoC 层的分工落到代码上。目标让 NUCLEO-F303RE 板载的 LED 闪烁。第一步在 Board 层用设备树声明 LED 引脚LED 接在哪个 GPIO属于Board 层的信息所以写在板级设备树boards/arm/nucleo_f303re/nucleo_f303re.dts里/ { aliases { led0 led0; }; leds { compatible gpio-leds; led0: led_0 { gpios gpioa 5 GPIO_ACTIVE_HIGH; /* Board 层LED 接在 PA5 */ label LD2; }; }; };注意这里只写了“LED 接到了 PA5”这是 Board 层决定的事情。至于gpioa这个 GPIO 控制器本身怎么工作、寄存器怎么读写属于SoC 层由soc/st/stm32/stm32f3/下的代码负责这里不需要关心。第二步在应用里通过 GPIO API 控制 LED应用代码只跟 Zephyr 的 GPIO API 打交道不直接碰寄存器#includezephyr/kernel.h#includezephyr/device.h#includezephyr/drivers/gpio.h/* 通过设备树节点拿 LED 对应的 GPIO 设备与引脚号 */#defineLED0_NODEDT_ALIAS(led0)staticconststructgpio_dt_specledGPIO_DT_SPEC_GET(LED0_NODE,gpios);intmain(void){/* 把 LED 引脚配置为输出 */gpio_pin_configure_dt(led,GPIO_OUTPUT_ACTIVE);while(1){gpio_pin_toggle_dt(led);/* 翻转 LED 电平 */k_msleep(500);/* 每 500ms 翻转一次 */}return0;}第三步对照前面四个层次看这段代码代码/配置属于哪一层为什么gpios gpioa 5 ...Board 层决定 LED 接到了 PA5换一块板子可能就换引脚gpioa这个 GPIO 控制器SoC 层芯片上有几个 GPIO、寄存器怎么读写由 SoC 决定gpio_pin_toggle_dt()Driver 层由 GPIO Driver 把 API 翻译成对 SoC 寄存器的读写GPIO_DT_SPEC_GET()设备树dts把 Board 层的引脚信息接线给应用代码一句话应用只写我要让 LED 闪Board 层告诉它LED 在 PA5SoC 层负责PA5 这个引脚怎么操作Driver 层负责把 API 翻译成寄存器读写。四层各司其职互不越界。到那一步你就会开始从会用 Zephyr转向理解 Zephyr BSP 是怎么设计的。