本文是「Zephyr 内核从入门到精通」系列第 06 篇,也是基础篇的收官之作。前五篇分别讲了架构、开发环境、设备树、Kconfig,本篇不讲新概念,而是带你从零新建一个工程,把这些知识点串成一条能真正跑起来的线:按键 → 中断 → 翻转 LED → 串口打印日志。
全文按「建目录 → 写四个文件 → 编译 → 烧录 → 按键实测 → 排错」的顺序走,每一步都告诉你做什么、为什么、应该看到什么。新手照着抄就能跑通。建议先点赞收藏,打开终端跟着敲一遍。
目录
- 一、基础篇知识地图
- 二、核心:Zephyr 开发心智模型(三件套)
- 三、动手前:准备工作与目录规划
- 四、第一步:新建工程目录
- 五、第二步:写 CMakeLists.txt(构建脚本)
- 六、第三步:写 prj.conf(功能开关)
- 七、第四步:写 app.overlay(硬件描述,按需)
- 八、第五步:写 src/main.c(业务逻辑)
- 九、第六步:编译(west build)
- 十、第七步:烧录(west flash)
- 十一、第八步:按键实测与预期串口输出
- 十二、工程目录结构全景
- 十三、加强版排错表 + 两个「真相之源」
- 十四、基础篇能力自检清单
- 十五、总结与下一步
一、基础篇知识地图
先把前几篇的关系理顺,这是组装能力的前提。如果下面这张表你每一行都能讲清楚,本篇会非常轻松;如果有空白,建议回去补一篇。

| 篇目 | 解决的问题 | 一句话 |
|---|---|---|
| 02 架构 | Zephyr 怎么组织 | 四层解耦,换板不改码 |
| 03 开发环境 | 怎么跑起来 | Host 工具 + SDK,west 六步 |
| 04 设备树 | 硬件长什么样 | 硬件的「登记表」 |
| 05 Kconfig | 启用哪些功能 | 功能的「清单」 |
| 06 打通(本篇) | 怎么组合用 | 三件套协作,建一个完整工程 |
二、核心:Zephyr 开发心智模型(三件套)
这是基础篇最该内化的一张图。后面所有的代码、配置、报错,都能用它解释。

任何一个 Zephyr 功能 = 设备树(硬件)+ Kconfig(功能)+ 代码(逻辑)三者协作。
- 设备树(硬件):告诉系统用到哪些引脚、哪些外设节点。比如「LED 接在 GPIO0 的第 13 脚」。
- Kconfig(功能):告诉系统启用哪些子系统 / 驱动。比如「我要用 GPIO,要用日志」。
- 代码(逻辑):你的业务逻辑本身。比如「按键按下就翻转 LED」。
固定开发路径,永远是这三步顺序:
硬件(app.overlay)→ 功能(prj.conf)→ 逻辑(main.c)
为什么是这个顺序?因为代码(逻辑)要引用设备树里的节点、要调用被 Kconfig 打开的 API。底座没铺好,上层逻辑无从谈起。记住这条路径,你写任何新功能都不会乱。
本篇就严格按这个顺序,外加构建脚本 CMakeLists.txt,一共四个文件,把一个完整功能落地。
三、动手前:准备工作与目录规划
开始前确认两件事(这是 03 篇的内容,这里只做检查):
- 开发环境已就绪:能成功跑通官方 blinky 示例。也就是说
west、Zephyr SDK、Python 虚拟环境都装好了。 - 虚拟环境已激活。每次新开终端都要先激活,否则会报
west: command not found:
# Linux / macOS
source ~/zephyrproject/.venv/bin/activate
# Windows PowerShell
~\zephyrproject\.venv\Scripts\Activate.ps1
本篇用到的板子:以官方常见的 nrf52840dk 开发板为例(带板载 LED 和按键,开箱即用)。如果你用别的板子,只要这块板的 dts 里定义了 led0 和 sw0 两个别名(绝大多数官方板都有),代码一行都不用改——这就是「换板不改码」。后文凡是出现 <board> 的地方,都替换成你自己的板名即可。
板名的「v2 斜杠写法」:现在 Zephyr 用
<板名>/<SoC>的格式,比如nrf52840dk/nrf52840。老教程里nrf52840dk_nrf52840的下划线写法已经过时,新版本可能报board not found。不确定板名就执行west boards列出所有可用板子。
四、第一步:新建工程目录
我们要建一个独立的应用工程(不是改 Zephyr 源码,也不是改 samples)。找一个你喜欢的位置,比如家目录下:
# 进入你的工作区(任意位置都行,别放在 zephyr 源码里)
cd ~
# 新建工程主目录和源码子目录
mkdir -p my_button_led/src
mkdir -p my_button_led/boards
cd my_button_led
执行后你会得到这样一个空骨架:
my_button_led/
├── src/ # 放业务代码
└── boards/ # 放板级专属覆盖文件(可选,本篇可空着)
为什么要建
src/? Zephyr 约定业务代码放在src/下,CMakeLists.txt里也是这么引用的。boards/用来放某块板专属的 overlay/conf,本篇我们用工程根目录下的通用app.overlay,boards/留着先空着即可。
接下来,我们按「硬件 → 功能 → 逻辑」的顺序,加上构建脚本,依次创建四个文件。
五、第二步:写 CMakeLists.txt(构建脚本)
放哪:工程根目录 my_button_led/CMakeLists.txt(和 src/ 同级)。
作用:告诉 Zephyr 构建系统「我是一个 Zephyr 应用、我的工程叫什么、源码有哪些」。这是每个工程的入口,少了它 west build 根本不知道从哪开始。
完整内容(这是标准最小写法,几乎所有工程开头都长这样):
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(my_button_led)
target_sources(app PRIVATE src/main.c)
逐行解释(新手必看):
| 行 | 含义 |
|---|---|
cmake_minimum_required(VERSION 3.20.0) | 要求 CMake 版本不低于 3.20,这是现行 Zephyr 的硬性要求 |
find_package(Zephyr ...) | 找到并加载 Zephyr 构建框架,$ENV{ZEPHYR_BASE} 指向 Zephyr 源码根目录 |
project(my_button_led) | 声明工程名(随便起,建议和目录同名) |
target_sources(app PRIVATE src/main.c) | 把 src/main.c 加进编译。多个源文件就接着往后加 |
注意:
find_package必须在project()之前调用,顺序写反会报错。直接照抄上面的模板最稳。
六、第三步:写 prj.conf(功能开关)
放哪:工程根目录 my_button_led/prj.conf。
作用:这就是 Kconfig 三件套里的「功能清单」。我们这个例子要用到 GPIO(控制 LED、读按键)和日志系统(串口打印),所以要把这两个子系统打开。没打开就用,代码会编译失败或运行无效。
完整内容:
# 打开 GPIO 驱动子系统(控制 LED、读取按键、配置中断都需要它)
CONFIG_GPIO=y
# 打开日志系统(用于串口打印 LOG_INF 等)
CONFIG_LOG=y
# 日志默认级别:3 = INFO(数值越大越啰嗦:0关闭 1错误 2警告 3信息 4调试)
CONFIG_LOG_DEFAULT_LEVEL=3
逐项说明:
CONFIG_GPIO=y:开启 GPIO 子系统。gpio_pin_configure_dt、gpio_pin_toggle_dt、gpio_pin_interrupt_configure_dt这些 API 全靠它。不开这一行,链接阶段会找不到这些函数。CONFIG_LOG=y:开启日志框架。LOG_MODULE_REGISTER、LOG_INF靠它。CONFIG_LOG_DEFAULT_LEVEL=3:设成 INFO 级,确保我们的LOG_INF能打印出来。如果设成 1(只显示错误),LOG_INF的内容就不会出现,这是新手最容易踩的「日志怎么不打印」的坑。
小贴士:
y表示启用,=3是数值型配置。等号两边不要加空格,#开头是注释。
–
七、第四步:写 app.overlay(硬件描述,按需)
放哪:工程根目录 my_button_led/app.overlay。
作用:这是设备树「覆盖文件」,用来在不改板级 dts 的前提下,微调硬件描述。
好消息:绝大多数官方开发板的板级 dts 里已经定义好了 led0 和 sw0 别名(分别指向板载 LED 和按键)。如果你用的是这类板子,这一步可以完全跳过,连 app.overlay 文件都不用建,代码里用 DT_ALIAS(led0) 就能直接取到。
什么时候才需要写 overlay?当你想换引脚、或你的板子没定义这两个别名时。下面给一个示例(仅在需要改 LED 引脚时才用,否则别建这个文件):
/ {
aliases {
led0 = &my_led;
sw0 = &my_button;
};
};
&led0 {
/* 把 LED 改到 GPIO0 的第 13 脚,低电平点亮 */
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
};
怎么判断要不要建这个文件? 先什么都不建,直接进入下一步编译。如果编译报
DT_ALIAS(led0)之类的设备树错误,再回来建 overlay 补别名。对照build/zephyr/zephyr.dts这个「设备树真相之源」(后面会讲),搜索aliases节点就知道你的板子有没有led0/sw0。
本篇为了通用,假设你的板子已有 led0/sw0 别名,因此不创建 app.overlay。
八、第五步:写 src/main.c(业务逻辑)
放哪:my_button_led/src/main.c。
作用:真正的业务逻辑——配置 LED 和按键、给按键挂中断、按下时翻转 LED 并打日志。
注意看:整段代码里没有任何裸引脚号(像 13、GPIO0 这种),全部通过 DT_ALIAS 从设备树取。这就是「换板不改码」能成立的根本原因。
完整内容(复制即用):
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/logging/log.h>
/* 注册一个名为 demo 的日志模块,级别 INFO */
LOG_MODULE_REGISTER(demo, LOG_LEVEL_INF);
/* 从设备树取 LED 和按键的描述(端口 + 引脚 + 有效电平),代码中无任何裸引脚号 */
static const struct gpio_dt_spec led =
GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
static const struct gpio_dt_spec btn =
GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios);
/* 中断回调需要的数据结构 */
static struct gpio_callback btn_cb_data;
/* 按键中断回调:在这里翻转 LED 并打印日志 */
static void on_button_press(const struct device *dev,
struct gpio_callback *cb, uint32_t pins)
{
gpio_pin_toggle_dt(&led);
LOG_INF("button pressed, led toggled");
}
int main(void)
{
int ret;
/* 1) 确认设备就绪(驱动是否初始化成功) */
if (!gpio_is_ready_dt(&led)) {
LOG_ERR("LED device not ready");
return -1;
}
if (!gpio_is_ready_dt(&btn)) {
LOG_ERR("button device not ready");
return -1;
}
/* 2) 配置 LED 为输出,初始熄灭 */
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
if (ret != 0) {
LOG_ERR("failed to configure led, ret=%d", ret);
return -1;
}
/* 3) 配置按键为输入 */
ret = gpio_pin_configure_dt(&btn, GPIO_INPUT);
if (ret != 0) {
LOG_ERR("failed to configure button, ret=%d", ret);
return -1;
}
/* 4) 配置按键中断:上升沿(按下变为有效电平)触发 */
ret = gpio_pin_interrupt_configure_dt(&btn, GPIO_INT_EDGE_TO_ACTIVE);
if (ret != 0) {
LOG_ERR("failed to configure interrupt, ret=%d", ret);
return -1;
}
/* 5) 注册中断回调,并绑定到按键所在端口 */
gpio_init_callback(&btn_cb_data, on_button_press, BIT(btn.pin));
gpio_add_callback(btn.port, &btn_cb_data);
LOG_INF("system ready, press the button");
/* main 返回后,系统继续运行,中断照常工作 */
return 0;
}
逐段拆解(这是本篇最核心的代码,务必看懂):
| 步骤 | API | 干了什么 / 为什么 |
|---|---|---|
| 日志注册 | LOG_MODULE_REGISTER(demo, LOG_LEVEL_INF) | 声明本文件用日志,模块名 demo。必须有它,LOG_INF 才能用 |
| 取硬件 | GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios) | 从设备树 led0 别名的 gpios 属性里,把端口/引脚/电平打包成一个结构体 |
| 就绪检查 | gpio_is_ready_dt(&led) | 确认底层 GPIO 控制器初始化成功,避免对没准备好的硬件操作 |
| 配输出 | gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE) | 把 LED 引脚设为输出、初始为「无效」(熄灭) |
| 配输入 | gpio_pin_configure_dt(&btn, GPIO_INPUT) | 把按键引脚设为输入 |
| 配中断 | gpio_pin_interrupt_configure_dt(&btn, GPIO_INT_EDGE_TO_ACTIVE) | 设置成「跳到有效电平的那条边沿」触发中断,即按下瞬间触发 |
| 挂回调 | gpio_init_callback + gpio_add_callback | 把我们的 on_button_press 函数登记为中断处理函数。BIT(btn.pin) 表示只关心这一个引脚 |
| 翻转 | gpio_pin_toggle_dt(&led) | 在回调里翻转 LED 状态(亮→灭 / 灭→亮) |
为什么 main 返回 0 后系统不退出? Zephyr 是 RTOS,
main只是一个线程。它返回后内核继续运行,中断子系统照常工作,所以按键依然能触发回调。这和裸机main里写while(1)死循环是不同的思路。
三者依赖关系(一图看穿三件套):
| 代码里的元素 | 依赖谁 | 缺了会怎样 |
|---|---|---|
DT_ALIAS(led0) / DT_ALIAS(sw0) | 设备树里的别名节点 | 编译报错(找不到节点) |
gpio_* 系列 API | prj.conf 里 CONFIG_GPIO=y | 链接报错(找不到符号) |
LOG_* 宏 | prj.conf 里 CONFIG_LOG=y | 日志不打印 / 报错 |
少任意一环,要么编译失败,要么功能静默缺失。这正是三件套心智模型的价值——一眼看出一个功能在三个维度各需要做什么。
九、第六步:编译(west build)
四个文件(实际是 3 个 + 可选的 overlay)都写好了,现在编译。在工程根目录 my_button_led/ 下执行:
# -p always 表示每次都全新构建(pristine),改了配置/设备树务必加它
# -b 后面是板名,用 v2 斜杠写法
# 末尾的 . 表示「当前目录就是工程目录」
west build -p always -b nrf52840dk/nrf52840 .
把 nrf52840dk/nrf52840 换成你自己的板名。
为什么强烈建议加
-p always? Zephyr 的构建缓存很「记仇」:你改了prj.conf或设备树,旧缓存可能不刷新,导致「改了没反应」。-p always强制全新构建,能避开 90% 的玄学问题。代价只是慢一点点。
预期输出(成功时尾部大致长这样):
-- west build: building application
[1/150] Preparing syscall dependency handling
...
[148/150] Linking C executable zephyr/zephyr.elf
[149/150] Generating zephyr/zephyr.hex
Memory region Used Size Region Size %age Used
FLASH: 28456 B 1 MB 2.71%
RAM: 6240 B 256 KB 2.38%
IDT_LIST: 0 B 2 KB 0.00%
[150/150] Building done.
看到最后那张 Memory region 内存占用表 + Building done 就是成功了。生成的固件在 build/zephyr/zephyr.hex(或 .elf)。
如果这一步红字报错,先别慌,跳到第十三节「加强版排错表」对号入座,绝大多数是配置或板名问题。
十、第七步:烧录(west flash)
编译产物有了,烧进开发板。先用 USB 把板子连上电脑,然后在工程根目录执行:
west flash
west flash 会自动调用板子对应的烧录工具(nRF 板用 nrfjprog/J-Link,其它板各有后端),把固件写进 Flash。
预期输出(成功时大致如下):
-- west flash: rebuilding
-- west flash: using runner nrfjprog
-- runners.nrfjprog: Flashing file: build/zephyr/zephyr.hex
Parsing image file.
Erasing page ...
Applying system reset.
Run.
-- runners.nrfjprog: Board with serial number xxxxxxxxx flashed successfully.
看到 flashed successfully 就烧好了,板子会自动复位运行新程序。
【📷 截图位:终端 west flash 输出 flashed successfully + 开发板通过 USB 连接电脑的实物照片】
十一、第八步:按键实测与预期串口输出
固件跑起来了,但怎么看到日志?需要打开串口监视器。
1)打开串口。 nrf52840dk 自带 USB 转串口,连上后会出现一个串口设备。用任意串口工具连接,波特率 115200:
# Linux 下用 minicom(设备名按实际,可能是 /dev/ttyACM0)
minicom -D /dev/ttyACM0 -b 115200
# 也可以用 Zephyr 自带的(板子支持时)
# west espressif monitor # 仅 ESP 系列
Windows 用户可用 PuTTY / MobaXterm / 串口助手,选对 COM 口、波特率 115200。
2)按一下复位键,应当先看到启动日志:
*** Booting Zephyr OS build vX.Y.Z ***
[00:00:00.123,456] <inf> demo: system ready, press the button
这行 system ready, press the button 来自 main 的 LOG_INF,说明初始化全部成功。
3)按下板载按键,每按一次:
- 板载 LED 状态翻转(亮的灭、灭的亮);
- 串口打印一行日志:
[00:00:05.678,901] <inf> demo: button pressed, led toggled
[00:00:07.234,567] <inf> demo: button pressed, led toggled
[00:00:09.001,234] <inf> demo: button pressed, led toggled
每行格式说明:[时间戳] <inf> 模块名: 内容。<inf> 就是 INFO 级,对应我们的 LOG_INF。
到这里,三件套就被完整打通了:设备树描述了 LED/按键 → Kconfig 打开了 GPIO/日志 → 代码把它们组装成了「按键中断翻转 LED + 打印日志」的完整功能。恭喜,基础篇的核心技能你已经亲手验证过一遍了。
十二、工程目录结构全景
最终,一个标准 Zephyr 应用工程的最小结构如下(本篇我们建了其中加粗的部分):
my_button_led/
├── CMakeLists.txt # ★ 构建脚本(必需)
├── prj.conf # ★ Kconfig 功能配置(必需)
├── app.overlay # ☆ 设备树覆盖(可选,板子有 led0/sw0 时可不建)
├── boards/ # ☆ 板级专属 overlay / conf(可选)
│ └── nrf52840dk_nrf52840.overlay
├── src/
│ └── main.c # ★ 业务代码(必需)
└── build/ # 编译后自动生成,别手动改
└── zephyr/
├── .config # 真相之源 1:最终生效的 Kconfig
├── zephyr.dts # 真相之源 2:合并后的完整设备树
├── zephyr.elf # 固件(调试用)
└── zephyr.hex # 固件(烧录用)
记住:★ 三个文件是任何工程都少不了的最小集合;☆ 是按需;build/ 目录是自动产物,里面藏着排错的两把钥匙,下一节细说。
十三、加强版排错表 + 两个「真相之源」
基础阶段的问题高度集中,掌握定位套路远比死记报错重要。先记住排错决策树,再查下面分五层的速查表。

两个「真相之源」(必背)
无论遇到什么诡异问题,先问自己:这是配置问题还是硬件描述问题?然后去看对应的文件:
-
build/zephyr/.config—— Kconfig 的最终生效值。你在prj.conf写的、板子默认带的、依赖自动开的,全部合并后的真实结果都在这里。怀疑「某功能没开」就grep它,例如:grep CONFIG_GPIO build/zephyr/.config # 期望看到:CONFIG_GPIO=y -
build/zephyr/zephyr.dts—— 合并后的完整设备树。板级 dts + 你的 app.overlay 合并后的最终结果。怀疑「节点/别名/引脚不对」就搜它,例如搜aliases、搜led0、看节点的status是不是okay。
💡 习惯养成一句话:Kconfig 问题看
.config,设备树问题看zephyr.dts。 这两个文件能解决基础阶段绝大多数疑问,遇事不要瞎猜,先看真相之源。
加强版报错速查表(按五层定位)
| # | 层级 | 现象 / 报错 | 原因 | 解决办法 |
|---|---|---|---|---|
| 1 | 环境 | west: command not found | 没激活虚拟环境 | source .venv/bin/activate(Win: Activate.ps1) |
| 2 | 环境 | ZEPHYR_BASE 相关报错 / 找不到 Zephyr | 环境变量没设或路径错 | 在 zephyrproject 下重新激活环境,确认 west 能跑 |
| 3 | 配置 | board not found / unknown board | 板名写错或用了旧下划线写法 | 改用 v2 斜杠写法 xxx/yyy,先 west boards 查正确板名 |
| 4 | 编译 | undefined reference to gpio_pin_configure_dt(链接错) | prj.conf 没开 CONFIG_GPIO=y | 加上 CONFIG_GPIO=y,并 -p always 重编 |
| 5 | 编译 | LOG_INF / LOG_MODULE_REGISTER 未定义 | 没开日志或没注册模块 | prj.conf 加 CONFIG_LOG=y;源文件加 LOG_MODULE_REGISTER(...) |
| 6 | 编译 | 改了 prj.conf/overlay「改了没反应」 | 用了旧构建缓存 | west build -p always -b <board> . 全新构建 |
| 7 | 设备树 | 编译报 DT_ALIAS(led0) 相关错 / 节点不存在 | 板子没定义 led0/sw0 别名 | 写 app.overlay 在 aliases{} 里补别名;对照 zephyr.dts 确认 |
| 8 | 设备树 | 编译过但 LED/按键无反应,节点存在 | 设备树里该节点 status 不是 okay | 查 zephyr.dts,在 overlay 里把对应节点 status = "okay"; |
| 9 | 设备树 | overlay 改了引脚但不生效 | overlay 文件名/位置不对,没被加载 | 工程根放 app.overlay,或 boards/<board>.overlay;看 zephyr.dts 是否合并进去 |
| 10 | 配置 | 功能/外设确实没生效 | 对应 CONFIG_xxx 没开 | grep CONFIG_xxx build/zephyr/.config 确认是否为 y |
| 11 | 烧录 | west flash 报找不到 runner / 设备 | 板子没连、驱动缺失、被占用 | 检查 USB 连接、装好 J-Link/驱动、关掉占用串口的程序 |
| 12 | 烧录 | 烧录成功但板子没反应 | 没复位、跑的还是旧程序、供电不足 | 按复位键、换 USB 口/线,确认 flashed successfully |
| 13 | 逻辑/输出 | 串口无任何日志 | 波特率不对 / 串口选错 / 日志级别太低 | 波特率设 115200、选对 COM 口、CONFIG_LOG_DEFAULT_LEVEL=3 |
| 14 | 逻辑 | 启动日志有,但按键无 button pressed | 中断没配好 / 别名指错按键 / 触发边沿反了 | 检查 gpio_pin_interrupt_configure_dt 边沿;核对 sw0 指向的引脚 |
| 15 | 逻辑 | 按一次打印好多行(抖动) | 机械按键抖动 | 学到中断/定时器后加软件消抖;基础阶段可忽略 |
排查顺序建议:环境 → 配置 → 编译 → 设备树 → 烧录 → 逻辑,从前往后排。前面没通,后面无意义。每一层卡住,都先回到对应的「真相之源」看一眼。
十四、基础篇能力自检清单
逐条对照,全部能打勾,说明基础篇你已经真正过关,可以进内核篇:
- 能解释四层架构与「换板不改码」的原理
- 能独立搭好环境、跑通 blinky(真机 / QEMU)
- 能读懂设备树节点,会用 app.overlay 改硬件
- 能用 prj.conf / menuconfig 裁剪功能
- 能从零新建工程,写全 CMakeLists.txt / prj.conf / src/main.c
- 能用三件套独立实现一个完整功能(如本文的按键中断 + LED + 日志)
- 会用
west build -p always和west flash,能看懂构建/烧录输出 - 能用
.config和zephyr.dts两个「真相之源」自主排错
全部打勾,基础篇过关。哪怕只差一两条,回到对应章节再练一遍,性价比极高。
十五、总结与下一步
回顾一下本篇你亲手做了什么:
- 建立了 Zephyr 最重要的三件套协作心智模型:硬件(设备树)+ 功能(Kconfig)+ 逻辑(代码);
- 按固定路径从零建了一个完整工程:新建目录 → CMakeLists.txt → prj.conf →(按需 overlay)→ main.c;
- 走通了编译 → 烧录 → 串口实测全流程,亲眼看到「按键翻转 LED + 打印日志」;
- 掌握了一套分五层的排错方法,并牢记两个「真相之源」:
.config与zephyr.dts。
至此,「会用」这一关你已经通过。从下一篇起进入内核篇,第一篇《Zephyr 内核架构》:调度器怎么决定谁先跑、线程是怎么切换的、信号量/互斥量这些同步机制底层如何运转。我们将从「会用」走向「懂原理」,这才是吃透一个 RTOS 的分水岭
如果这个系列帮到了你,点赞 + 收藏 + 关注三连支持,内核篇马上开更。实践中遇到的报错,欢迎贴在评论区,我看到都会回。下一篇《Zephyr 内核架构》,内核篇,不见不散。
:手把手用一个完整工程打通设备树 + Kconfig + 代码(按键中断控制 LED + 串口日志)&spm=1001.2101.3001.5002&articleId=162292329&d=1&t=3&u=9f07515b53a749af94d332c5860a1103)
338

被折叠的 条评论
为什么被折叠?



