ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

轻松学习Zephyr: 04-从零构建Blinky

轻松学习Zephyr: 04-从零构建Blinky

你现在不应该“跑官方 sample”,而应该从空目录自己写一个最小 Blinky。这样才能真正理解 Zephyr 的工程结构、CMake、Kconfig、Devicetree 和 GPIO API。
我们就从:

~/workspace

开始,不复制 samples/basic/blinky。

第 1 步:创建空工程
先执行:

cd~/workspacemkdir-pblinky/srccdblinkytouchCMakeLists.txttouchprj.conftouchsrc/main.c

现在:

tree

应该是:

blinky/ ├── CMakeLists.txt ├── prj.conf └── src └── main.c

第 2 步:自己写 CMakeLists.txt
编辑:

nanoCMakeLists.txt

写入:

cmake_minimum_required(VERSION3.20.0)find_package(Zephyr REQUIRED HINTS$ENV{ZEPHYR_BASE})project(blinky)target_sources(app PRIVATE src/main.c)

这里先不要急着背。
我们现在只理解:

find_package(Zephyr REQUIRED HINTS$ENV{ZEPHYR_BASE})

意思是:

找到 Zephyr 构建系统。

而:

project(blinky)

表示:

我的应用程序叫 blinky。

最后:

target_sources(app PRIVATE src/main.c)

表示:

把 src/main.c 加入这个应用程序。


第 3 步:先写最简单的 main.c
编辑:

nanosrc/main.c

先写:

#include<zephyr/kernel.h>intmain(void){while(1){printk("Hello Zephyr!\n");k_msleep(1000);}return0;}

注意:
现在还没有 GPIO。
我们先确认:
“我自己创建的 Zephyr 工程能不能编译、运行。”

这一步非常重要。


第 4 步:prj.conf
编辑:

nanoprj.conf

写:

CONFIG_PRINTK=y

这里第一次接触 Kconfig。
它的作用是:

prj.conf ↓ Kconfig ↓ Zephyr configuration ↓ 编译系统

CONFIG_PRINTK=y 表示:

我要启用 Zephyr 的 printk 功能。


第 5 步:第一次编译
你之前已经配置好了 Zephyr 环境,所以先确认:

echo$ZEPHYR_BASE

应该类似:

/home/cxicc/zephyrproject/zephyr

然后进入工程:

cd~/workspace/blinky

执行:

west build-bnucleo_f303re.

注意这里的:.
非常重要。
它表示:
当前目录就是我的 application。

而不是:
west build -b nucleo_f303re samples/basic/blinky

我们现在完全不使用官方 Blinky。


第 6 步:看看发生了什么
如果成功:

ls

你会看到:

CMakeLists.txt prj.conf src build

进入:

cdbuild

看看:

ls

里面会出现大量 Zephyr 构建文件。
特别关注:

zephyr/

进入:

lszephyr

你会看到类似:

zephyr.elf zephyr.bin zephyr.hex

这时候你应该理解:

~/workspace/blinky │ │ west build ▼ ~/workspace/blinky/build │ ▼ zephyr.elf zephyr.bin zephyr.hex

第 7 步:烧录
回到工程目录:

cd~/workspace/blinky

然后:
west flash
如果烧录成功,再打开串口:

minicom-D/dev/ttyACM0-b115200

应该看到:

Hello Zephyr!Hello Zephyr!Hello Zephyr!Hello Zephyr!

每秒一次。

To exit or abort minicom normally, press Ctrl+A, then press X, and
hit Enter to confirm


到这里先不要写 Blinky
这一步看起来很简单,但是实际上我们已经自己完成了:

空目录 ↓ CMakeLists.txt ↓ prj.conf ↓ main.c ↓ west build ↓ Zephyr ↓ zephyr.elf ↓ west flash ↓ STM32

这才是你应该掌握的第一层。


第 8 步:下一步才加入 GPIO
等上面的 Hello Zephyr! 成功以后,我们把:

printk("Hello Zephyr!\n");

换成:

GPIO ↓ LED ↓ Toggle

但是这里会遇到一个非常关键的问题:
我们的 main.c 怎么知道 Nucleo-F303RE 的 LED 在哪个 GPIO?

这就会自然进入:

Device Tree

然后我们自己写:
app.overlay
最终自己的工程会变成:

~/workspace/blinky/ ├── CMakeLists.txt ├── prj.conf ├── app.overlay └── src/ └── main.c

然后实现:

const struct gpio_dt_spec led=GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);

再:

gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);

最后:

gpio_pin_toggle_dt(&led);

这样你就会真正理解:

app.overlay ↓ Device Tree ↓ DT_ALIAS(led0)↓ GPIO specification ↓ GPIO API ↓ STM32 GPIO Driver ↓ GPIO hardware ↓ LED

这才是这一课真正要学的东西。


在我们不要急着写代码,先把 Device Tree 这条关系搞明白。
你找到:

led0=&green_led_2;

这句话的意思是:

led0 │ └──>green_led_2

也就是说,应用程序以后写:

DT_ALIAS(led0)

实际上就是在寻找:

green_led_2

现在我们已经把 Nucleo-F303RE 的 LED 完整链路找出来了。
你得到的核心信息是:

green_led_2: led_2{gpios=<&gpioa5GPIO_ACTIVE_HIGH>;label="User LD2";};

以及:

aliases{led0=&green_led_2;};

所以我们可以画出:

DT_ALIAS(led0)│ ▼ green_led_2 │ ▼ gpioa pin5│ ▼ PA5 │ ▼ User LD2

而且:

GPIO_ACTIVE_HIGH

意味着:

PA5=1→ LED ON PA5=0→ LED OFF

第9步: 现在开始自己写 Blinky
注意:我们暂时不需要 app.overlay。
这是一个很重要的学习点。
因为你的 Nucleo-F303RE 已经在 board Device Tree 中定义好了 led0:

led0=&green_led_2;

所以我们的应用可以直接使用它。


1. 修改 prj.conf
现在:

cd~/workspace/blinkynanoprj.conf

改成:

CONFIG_GPIO=yCONFIG_PRINTK=y

为什么加:

CONFIG_GPIO=y

因为我们的程序马上要调用 GPIO API。


2. 修改 main.c
现在:

nanosrc/main.c

把原来的内容全部删除,写成:

#include<zephyr/kernel.h>#include<zephyr/device.h>#include<zephyr/drivers/gpio.h>#include<zephyr/devicetree.h>#defineLED_NODEDT_ALIAS(led0)staticconststructgpio_dt_specled=GPIO_DT_SPEC_GET(LED_NODE,gpios);intmain(void){intret;ret=gpio_pin_configure_dt(&led,GPIO_OUTPUT_INACTIVE);if(ret<0){printk("Failed to configure LED\n");return0;}while(1){gpio_pin_toggle_dt(&led);k_msleep(1000);}return0;}

现在先不要急着背这段代码。

我们逐行拆。


3. 第一层:找到 LED
这一行:

#define LED_NODE DT_ALIAS(led0)

对应你刚才找到的:

aliases{led0=&green_led_2;};

所以:

DT_ALIAS(led0)

最终找到:

green_led_2


4. 第二层:取得 GPIO 信息
这一段:

static const struct gpio_dt_spec led=GPIO_DT_SPEC_GET(LED_NODE, gpios);

它读取:

gpios=<&gpioa5GPIO_ACTIVE_HIGH>;

因此 led 里面包含的信息大概就是:

GPIO Controller → GPIOA Pin →5Active → HIGH

也就是:

PA5

5. 第三层:配置 GPIO

gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);

意思:

把这个 GPIO 配置成输出,并且初始状态为 inactive。

因为你的 Device Tree 是:

GPIO_ACTIVE_HIGH

所以:

INACTIVE → PA5=0→ LED OFF

6. 第四层:Toggle
真正让 LED 闪烁的是:

gpio_pin_toggle_dt(&led);

每执行一次:

OFF → ON

下一次:

ON → OFF

然后:

k_msleep(1000);

等待 1 秒。
所以:

gpio_pin_toggle_dt()↓ ON ↓1000ms ↓ gpio_pin_toggle_dt()↓ OFF ↓1000ms ↓ repeat

7. 编译
现在执行:

cd~/workspace/blinky west build-bnucleo_f303re.

如果之前已经 build 过,建议这一次:

west build-bnucleo_f303re.-palways

-p always 表示重新生成构建配置,避免我们修改 prj.conf 后受到旧配置影响。


8. 烧录
编译成功以后:

west flash

然后观察 Nucleo-F303RE 上的 LD2。
应该:

亮 ↓1秒 ↓ 灭 ↓1秒 ↓ 亮 ↓...

  1. 这一阶段你真正应该理解的东西
    现在我们已经从:
Hello Zephyr

走到了:

main.c │ │ DT_ALIAS(led0)▼ Device Tree │ │ led0=&green_led_2 ▼ green_led_2 │ │ gpios ▼ GPIOA + Pin5│ ▼ GPIO Driver │ ▼ STM32F303RE │ ▼ LD2

这就是为什么我建议你不要直接跑官方 samples/basic/blinky。
官方 sample 跑起来很容易,但你很容易只学会:

>**摘要**:本文通过从零构建 Zephyr Blinky 项目的完整过程,系统讲解了 Zephyr RTOS 的核心构建机制。不同于直接运行官方示例,我们从空目录开始,逐步创建 CMakeLists.txt、prj.conf、main.c 等文件,深入理解 Zephyr 工程结构、CMake 配置、Kconfig 系统、Device Tree 设备树和 GPIO API 的完整链路。重点包括:1)如何通过`find_package(Zephyr)`集成 Zephyr 构建系统;2)Kconfig 配置项(如 CONFIG_PRINTK、CONFIG_GPIO)的作用;3)Device Tree 中 LED 别名(led0=&green_led_2)到具体 GPIO(PA5)的映射关系;4)使用`DT_ALIAS(led0)``GPIO_DT_SPEC_GET`获取设备树信息;5)完整的 GPIO 配置与闪烁实现。最终实现从空目录到可烧录运行的 LED 闪烁程序,掌握 Zephyr 应用开发的底层原理。 west build west flash

现在你是在自己构建:

CMake + Kconfig + Device Tree + GPIO API + Application

这才是 Zephyr 真正值得学习的地方。

返回列表