☰
Zephyr BSP: 20-构建 Zephyr BSP
2026/9/29 3:56:30 网站建设 项目流程

摘要:本文是 Zephyr 系列后半部分(20~40 篇)的总纲。前半部分(01~19 篇)解决「Zephyr 是怎么工作的」,从第 20 篇起,学习主线正式切换为「如何把公司自研 SoC 接入 Zephyr,做成一套可维护、可扩展、可 upstream 的 BSP」。文章先建立 Company SoC → Zephyr BSP 的全局地图,再给出 20~40 篇的完整路线图:从最小 SoC Port、CPU/Architecture 边界、Startup、中断控制器、时钟/复位,到 Devicetree、Binding、各外设驱动、Board、Kconfig、构建系统、链接脚本、烧录调试与 BSP 验证,最后讨论 Vendor HAL 边界、多芯片家族架构与 BSP 完整生命周期。

本文是 Zephyr 系列后半部分(20~40 篇)的总纲。如果说前 19 篇解决的是「Zephyr 是怎么工作的」,那么从第 20 篇起,学习主线将正式切换为「设计 Zephyr BSP」——把公司自研 SoC 接入 Zephyr,最终交付一套可维护、可扩展、可 upstream 的 BSP。这一转变不仅是技术栈的延伸,更是思维方式的升级:从读懂源码,到亲手搭建 SoC Port、Board、Driver、HAL、Devicetree、Kconfig 与 Build System 的完整链路。本文将先建立 Company SoC → Zephyr BSP 的全局地图,再给出 20~40 篇的完整路线图,并附上贯穿始终的核心架构图与统一的工程化学习模板,帮助你从「读者」成长为真正的 BSP Engineer。


目录导航

本文作为 Zephyr 系列后半部分的总纲,将按以下主线展开,你可以根据兴趣直接跳转到对应章节:

  • 学习主线切换:从「读 Zephyr」到「设计 Zephyr BSP」 —— 对比两个阶段在目标、核心问题、产出物、验证方式上的差异
  • 第一张核心图 —— Zephyr App → Drivers → HAL → SoC → Board 的全局架构图
  • 20~30 篇路线图 —— 从 BSP 全局架构到 Board Support Package 的逐篇推进
  • BSP 目录树 —— Company SoC Zephyr BSP 的完整目录结构与职责说明
  • BSP 验证清单 —— 各外设对应的 Zephyr sample、验证命令与通过标准
  • 学习方法模板 —— 每篇统一的工程化推进模板(①~⑧)
  • 最终能力目标 —— 从「会写 driver」到「看到新 SoC 能独立判断归属层」

后半部分的学习方法也从「读 Zephyr」转变为「设计 Zephyr BSP」,每篇采用统一的工程化模板推进。

20 篇开始,学习主线应该正式切换。

前 01~19 篇解决的是:

“Zephyr 是怎么工作的?”

从 20 篇开始解决:
“如果公司有一颗自己的 SoC,我如何把它接入 Zephyr,最终让公司内部开发者可以像使用 STM32/NXP 一样使用它?”

这两个阶段的目标完全不同。

20 篇开始:Company SoC → Zephyr BSP

我建议把后面的系列重新定义为:

01 ~19━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 理解 Zephyr Hardware / Driver / Device Model ↓ “知道 Zephyr 怎么工作” ↓20~ ? ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 把 Company SoC 做成 Zephyr BSP ↓ Architecture ↓ SoC ↓ Board ↓ Devicetree ↓ Kconfig ↓ Drivers ↓ HAL ↓ Build System ↓ Zephyr Applications ↓ “公司 SoC 成为 Zephyr 平台”

而且你真正的目标不是:
做一个能跑 blinky 的 demo。
而是:
建立一套可维护、可扩展、可 upstream 的 Company SoC Zephyr BSP。

20 — 从一个"假想公司 SoC"开始

我建议不要马上拿真实公司 SoC 开始改代码。

先建立一个非常重要的概念:

Company SoC │ ├── CPU │ ├── Clock / Reset │ ├── Interrupt Controller │ ├── UART │ ├── GPIO │ ├── Timer │ ├── SPI │ ├── I2C │ ├── DMA │ ├── Flash Controller │ └──...

然后问:

Zephyr 到底需要从公司 SoC 手里拿到什么?

答案可以先浓缩为:

SoC │ ├── CPU architecture support │ ├── SoC description │ ├── startup │ ├── interrupt │ ├── clock │ ├── system initialization │ └── peripheral drivers │ ↓ Board │ ↓ Application

所以第 20 篇最重要的不是写代码。
而是建立:
Zephyr BSP 的全局地图

**20 — 从理解 Zephyr 到打造自己的 SoC BSP:Company SoC → Zephyr 的完整路线图
**

学习主线切换:从「读 Zephyr」到「设计 Zephyr BSP」

为了更清楚地理解 20 篇之后学习主线的变化,下面用一张表格横向对比两个阶段在目标、核心问题、产出物、验证方式四个维度上的差异:

维度读 Zephyr(01~19 篇)设计 Zephyr BSP(20~40 篇)
目标理解 Zephyr 的 Hardware / Driver / Device Model,搞清楚「Zephyr 是怎么工作的」把公司自研 SoC 接入 Zephyr,建立一套可维护、可扩展、可 upstream 的 Company SoC Zephyr BSP
核心问题Zephyr 内部机制是什么?DeviceTree、Driver、Device Model 之间如何协作?公司 SoC 需要提供什么?Zephyr 要求什么?两者哪里不匹配?BSP 应该放哪一层解决?
产出物知识体系、阅读笔记、对 Zephyr 源码的理解可编译、可烧录、可验证的 SoC Port / Board / Driver / HAL / Devicetree / Kconfig / Build System 代码
验证方式读懂源码、跑通官方 sample、能解释机制用west build编译、west flash烧录,并让每一个 Zephyr Hardware Model 层(UART / GPIO / SPI / I2C / Timer / Interrupt / DMA)都得到验证

一句话总结:前 19 篇是「读 Zephyr」,目标是理解;20 篇以后是「设计 Zephyr BSP」,目标是交付一套真正能支撑公司产品开发的 SoC 平台。

这一篇作为整个后半部分的总纲。

第一张核心图

你后面所有文章都可以围绕这张图展开:

┌─────────────────────┐ │ Zephyr App │ │ │ │ main()│ │ device_is_ready()│ │ uart / gpio / spi │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ Zephyr Drivers │ │ │ │ UART / GPIO / SPI │ │ I2C / Timer / PWM │ └──────────┬──────────┘ │ Driver API │ ▼ ┌─────────────────────┐ │ Company SoC HAL │ │ │ │ Register Access │ │ Clock │ │ IRQ │ │ Reset │ └──────────┬──────────┘ │ ▼ ┌────────────────────────────────┐ │ Company SoC │ │ │ │ CPU / NVIC-like IRQ │ │ UART / GPIO / SPI / I2C │ │ Timer / DMA / Clock / Reset │ └───────────────┬────────────────┘ │ │ ┌─────────▼─────────┐ │ Board │ │ │ │ SoC + Crystal │ │ Flash + LED │ │ UART + Connector │ └───────────────────┘

然后再往下增加:

Zephyr BSP │ ┌───────────┼────────────┐ │ │ │ ▼ ▼ ▼ Architecture SoC Board │ │ │ │ │ ├── board.dts │ │ ├── board.yaml │ │ ├── Kconfig.board │ │ └── board.cmake │ │ │ ├── soc.c │ ├── soc.h │ ├── Kconfig │ ├── CMakeLists.txt │ └── dtsi │ └── CPU / ABI / IRQ / exception

然后 20~30 篇,我建议这样走

20 — BSP 全局架构

目标:

建立:

Architecture ↓ SoC ↓ Board ↓ Devicetree ↓ Driver ↓ Application

下面是一份完整的 Company SoC Zephyr BSP 目录树,覆盖了从 SoC 描述、Board 定义、外设驱动到 Devicetree Binding 的全部关键文件,并标注了每个目录/文件的职责:

zephyr/ ├── soc/ │ └── company/ │ └── my_soc/# SoC 层:描述一颗具体的 Company SoC│ ├── Kconfig.soc# SoC 选择入口,定义 CONFIG_SOC_COMPANY_MY_SOC│ ├── Kconfig.defconfig# SoC 默认配置(默认使能时钟、UART 等)│ ├── CMakeLists.txt# 声明该 SoC 需要编译的源文件│ ├── soc.c# SoC 初始化:时钟、复位、系统上电配置│ ├── soc.h# SoC 寄存器基址、IRQ 号、内存映射等宏定义│ ├── linker.ld# 该 SoC 的链接脚本(FLASH/SRAM 布局)│ └── dts/ │ └── company/ │ └── my_soc.dtsi# SoC 级 Devicetree:CPU、UART、GPIO 等外设节点│ ├── boards/ │ └── company/ │ └── my_board/# Board 层:基于该 SoC 的具体开发板│ ├── board.dts# 板级 Devicetree:使能外设、配置引脚、LED 等│ ├── board.yaml# Board 元数据:名称、SoC、兼容性声明│ ├── Kconfig.board# Board 选择入口,定义 CONFIG_BOARD_MY_BOARD│ ├── Kconfig.defconfig# Board 默认配置(默认使能板载外设)│ ├── board.cmake# 烧录/调试 runner 配置(openocd/jlink 等)│ └── CMakeLists.txt# Board 层编译声明│ ├── drivers/ │ ├── serial/ │ │ └── uart_company.c# Company UART 驱动:实现 struct uart_driver_api│ ├── gpio/ │ │ └── gpio_company.c# Company GPIO 驱动│ ├── spi/ │ │ └── spi_company.c# Company SPI 驱动│ ├── i2c/ │ │ └── i2c_company.c# Company I2C 驱动│ └── timer/ │ └── timer_company.c# Company Timer 驱动│ ├── dts/ │ └── bindings/ │ ├── serial/ │ │ └── company,my-uart.yaml# UART Binding:描述 compatible 与属性│ ├── gpio/ │ │ └── company,my-gpio.yaml# GPIO Binding│ ├── spi/ │ │ └── company,my-spi.yaml# SPI Binding│ └── i2c/ │ └── company,my-i2c.yaml# I2C Binding│ ├── include/ │ └── dt-bindings/ │ └── company/ │ ├── my_soc_irq.h# IRQ 号宏定义(供 dtsi 与驱动共用)│ ├── my_soc_clk.h# 时钟源/分频宏定义│ └── my_soc_pinctrl.h# 引脚复用宏定义│ └── modules/ └── hal/ └── company/# Vendor HAL 层(可选):寄存器访问、底层驱动├── CMakeLists.txt ├── Kconfig └── include/ └── company_hal.h# HAL 头文件:寄存器结构体、位域定义

说明:soc/与boards/是 Zephyr 构建系统的核心入口,drivers/与dts/bindings/负责把外设能力接入 Zephyr Hardware Model,include/dt-bindings/提供跨层共享的宏定义,modules/hal/则用于隔离公司已有的底层 HAL 代码,避免与 Zephyr 驱动层耦合。

回答:
一个新的 SoC 加进 Zephyr,到底需要增加哪些东西?

21 — Zephyr SoC Port 的最小骨架

开始真正写代码。

建立一个假想:

company/ └── my_soc/

然后研究:

soc/ arch/ boards/ drivers/ dts/

之间的关系。

最终得到:

Company SoC
↓
最小 Zephyr SoC Port
↓
能够编译

注意:
先编译,再运行。

22 — CPU / Architecture 到底由谁负责?

这是非常关键的一篇。

很多人会把:

SoC CPU Architecture Board

混在一起。

实际上应该理解成:

Architecture │ │ defines ▼ CPU execution model │ ▼ Company SoC │ ▼ Board

例如你的公司 SoC 如果是:

Cortex-M4

那么你不是重新实现 ARM Cortex-M4 architecture。

你主要是在:

ARM Cortex-M + Company SoC

上做 Zephyr SoC integration。

但如果未来是:

Company CPU Core

那么事情就完全不一样了。

这篇会把:

arch/arm/ soc/

之间的边界讲清楚。

23 — Startup:SoC 上电以后,Zephyr 第一行代码在哪里?

这一篇开始真正接触:

Reset ↓ Reset_Handler ↓ startup ↓ CPU initialization ↓ Zephyr kernel ↓ device initialization ↓ main()

你最终要能回答:

我的 Company SoC 上电以后,Zephyr 是怎么跑起来的?

24 — Interrupt Controller:自己的 SoC 如何接入 Zephyr IRQ?

这是 BSP 的核心之一。

从:

Hardware IRQ ↓ Interrupt Controller ↓ ISR ↓ Zephyr IRQ API ↓ Driver

一路追。

然后自己实现一个:

Company SoC IRQ

25 — Clock / Reset:SoC BSP 真正开始「像 SoC」

开始进入 SoC-specific 内容:

Clock │ ├── CPU clock ├── Bus clock ├── Peripheral clock └── PLL

以及:

Reset │ ├── UART reset ├── GPIO reset ├── SPI reset └──...

然后研究:

Clock Control Driver Reset Controller DeviceTree

怎么连接。

26 — Devicetree:把 Company SoC 描述给 Zephyr

这一篇非常重要。

你已经学过:

DT_NODELABEL dependency ordinal device handle struct device

现在反过来:
轮到你描述自己的 SoC。
例如:

soc{uart0:uart@40000000{compatible="company,my-uart";reg=<0x400000000x1000>;interrupts=<50>;status="okay";};};

然后追:

company,my-uart ↓ binding ↓ DTnode↓ driver ↓ DEVICE_DT_DEFINE()↓ struct device

这就是你前面 01~19 篇知识的第一次真正落地。

27 — Binding:告诉 Zephyr “我的硬件是什么”

研究:

dts/bindings/

例如:

company,my-uart.yaml

然后理解:

compatible properties required reg interrupts clocks resets

最终形成:

Company SoC Hardware ↓ DeviceTree Binding ↓ DeviceTree ↓ Driver

28 — Company UART Driver

这是第一个真正的:
Company SoC Driver

例如:

drivers/serial/ uart_company.c

实现:

struct uart_driver_api

然后:

UART Hardware ↓ Company UART Driver ↓ Zephyr UART API ↓ Application

最终:

const struct device\*uart=DEVICE_DT_GET(DT_NODELABEL(uart0));

就能工作。

29 — GPIO / SPI / I2C / Timer

这一篇开始建立:

Company Peripheral Drivers

例如:

drivers/ ├── serial/ ├── gpio/ ├── spi/ ├── i2c/ ├── timer/ └──...

重点不再是"怎么写一个 driver"。
而是:
如何建立一套 Company SoC driver architecture。

30 — Board Support Package

终于进入:
Board
例如:

boards/ └── company/ └── my_board/

包含:

board.yml board.cmake Kconfig.board Kconfig.defconfig my_board.dts my_board.yaml

然后:

Company SoC + Company Board ↓ west build ↓ Zephyr firmware

31 — Kconfig:把 Company SoC 变成可配置平台

开始处理:

CONFIG_COMPANY_SOC CONFIG_COMPANY_UART CONFIG_COMPANY_GPIO CONFIG_COMPANY_SPI

最终形成:

Kconfig │ ├── SoC selection ├── Driver selection ├── Hardware features └── Build options

这一步非常重要,因为公司真正做 BSP 后,不可能所有东西都硬编码。

32 — CMake / Build System

这一篇回答:
Zephyr 为什么知道应该编译我的 Company SoC?
追:

west build ↓ CMake ↓ BOARD ↓ SOC ↓ Kconfig ↓ CMakeLists.txt ↓ drivers ↓link↓ elf

你会真正理解:

BOARD=my_boardSOC=company_my_soc

到底是怎么影响整个 build system 的。

33 — Linker / Memory Map

这篇非常重要。

因为 SoC BSP 最终必须知道:

FLASH SRAM ROM TCM MMIO VECTOR TABLE

例如:

0x00000000 ┌───────────────┐ │ Boot / Vector │ ├───────────────┤ │ Flash │ │ │ ├───────────────┤ │ SRAM │ │ │ ├───────────────┤ │ Peripheral │ └───────────────┘

然后连接:

DeviceTree + Linker Script + SoC Memory Map

34 — Flash / Debug / Runner

你的 BSP 最终不能只:

build

还必须:

flash debug

所以研究:

board.cmake runner openocd jlink pyocd vendor programmer

形成:

west flash west debug

35 — 从 blinky 到真正的 BSP Validation

建立 BSP validation:

hello_world ↓ blinky ↓ UART ↓ GPIO ↓ Timer ↓ Interrupt ↓ SPI ↓ I2C ↓ DMA

下面是一张 BSP 验证清单,把每个外设对应的 Zephyr sample、验证命令和通过标准整理成表,方便逐项核对:

外设Zephyr Sample验证命令通过标准
UARTsamples/hello_worldwest build -b my_board samples/hello_world
west flash
串口输出Hello World!,无乱码、无丢字符
GPIOsamples/basic/blinkywest build -b my_board samples/basic/blinky
west flash
LED 按预期频率闪烁,gpio_pin_toggle()生效
SPIsamples/drivers/spi/spi_bitbang(或spi_flash)west build -b my_board samples/drivers/spi/spi_bitbang
west flash
SPI 收发数据一致,回环测试通过,无 CRC 错误
I2Csamples/drivers/i2c/i2c_scanwest build -b my_board samples/drivers/i2c/i2c_scan
west flash
能扫描到挂载设备地址,读写寄存器返回预期值
Timersamples/drivers/timer(或samples/synchronization)west build -b my_board samples/drivers/timer
west flash
定时中断按设定周期触发,计数误差在可接受范围内
Interruptsamples/drivers/interrupt_controller(或samples/philosophers)west build -b my_board samples/drivers/interrupt_controller
west flash
外部/定时中断能正确触发 ISR,IRQ 优先级与嵌套行为符合预期
DMAsamples/drivers/dmawest build -b my_board samples/drivers/dma
west flash
DMA 搬运数据与源数据完全一致,完成回调正常触发

说明:-b my_board需替换为你的实际 board 名称;若某个外设没有官方 sample,可基于samples/drivers/下最接近的示例改写,或参考tests/drivers/下的测试用例自行构造最小验证工程。

不是"demo 越多越好"。
而是:
每一个 Zephyr Hardware Model 层都得到验证。

36 — Company HAL 怎么放进 Zephyr?

这一步对公司项目尤其重要。
现实中你可能已经有:

Company SDK │ ├── HAL ├── CMSIS-like layer ├── register definitions ├── startup ├── drivers └── middleware

问题来了:
这些东西全部推倒重写成 Zephyr driver 吗?

答案通常不是简单的 yes/no。
需要研究:

Zephyr Driver │ ├────────── Company HAL │ └────────── Registers

什么时候:

Zephyr → HAL → Hardware

什么时候:

Zephyr Driver → Registers → Hardware

这是非常实际的 BSP architecture 问题。

37 — Vendor HAL / Zephyr Driver 的边界

进一步解决:

HAL Driver Subsystem Application

边界问题。
例如:

Application ↓ Zephyr API ↓ Zephyr Driver ↓ Company HAL ↓ Register

而不是:

Application ↓ Company HAL ↓ Register

否则最终你的"Zephyr BSP"只是把 Zephyr 当成启动器。

38 — Multi-Board / Multi-Chip
当公司有:

MySoC-A MySoC-B MySoC-C

以及:

Evaluation Board Reference Board Customer Board

以后怎么办?
建立:

SoC family │ ├── SoC A ├── SoC B └── SoC C │ ├── EVK ├── Reference └── Customer

这篇开始讨论:

如何避免 BSP 最后变成一坨复制粘贴。

39 — SoC Family Architecture

最终形成真正公司的结构:

soc/company/ │ ├── soc_a/ ├── soc_b/ ├── soc_c/ │ └── common/ ├── clock/ ├── reset/ ├── irq/ ├── pinctrl/ └──...

然后讨论:

common code SoC-specific code board-specific code

应该分别放在哪里。

40 — 公司 BSP 的完整生命周期

最后把整个流程串起来:

Company SoC

Hardware Specification

Zephyr Hardware Model

Architecture(22 篇)

SoC Port(21 篇)

Board(30 篇)

Devicetree(26 篇)

Binding(27 篇)

Kconfig(31 篇)

Drivers(28~29 篇)

Build System(32 篇)

Linker / ELF(33 篇)

Flash / Debug(34 篇)

BSP Validation(35 篇)

Zephyr App

更重要的是:20 篇以后学习方法要改变

前 19 篇你是在:
读 Zephyr。

20 篇以后你应该开始:
设计 Zephyr BSP。

所以每篇文章都最好采用同一个工程化模板:

① Zephyr 要求什么? ↓ ② Company SoC 提供什么? ↓ ③ 两者哪里不匹配? ↓ ④ BSP 应该放哪一层解决? ↓ ⑤ Zephyr 源码在哪里? ↓ ⑥ 最小代码是什么? ↓ ⑦ 怎么 build? ↓ ⑧ 怎么验证?

这个方法非常适合你最终的公司 SoC。
最终你要达到的能力
不是:

“我会写 Zephyr driver。”
而应该是看到一颗新的 SoC:

Company SoC │ ┌────────────────┼────────────────┐ ▼ ▼ ▼ CPU Memory Peripherals │ │ │ ▼ ▼ ▼ Architecture Linker Drivers/HAL │ │ │ └────────────────┼────────────────┘ ▼ SoC Port │ ▼ Board │ ▼ Devicetree │ ▼ Kconfig │ ▼ Build System │ ▼ Zephyr Firmware

你能够自己判断:

这个东西应该属于 Architecture、SoC、Board、Driver、HAL、Devicetree、Kconfig 还是 Build System?

这才是BSP Engineer / SoC Zephyr Porting真正需要掌握的能力。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询