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

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

~/workspace

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

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

cd ~/workspace

mkdir -p blinky/src
cd blinky

touch CMakeLists.txt
touch prj.conf
touch src/main.c

现在:

tree

应该是:

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

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

nano CMakeLists.txt

写入:

cmake_minimum_required(VERSION 3.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
编辑:

nano src/main.c

先写:

#include <zephyr/kernel.h>

int main(void)
{
    while (1) {
        printk("Hello Zephyr!\n");
        k_msleep(1000);
    }

    return 0;
}

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

这一步非常重要。


第 4 步:prj.conf
编辑:

nano prj.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 -b nucleo_f303re .

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

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

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


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

ls

你会看到:

CMakeLists.txt
prj.conf
src
build

进入:

cd build

看看:

ls

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

zephyr/

进入:

ls zephyr

你会看到类似:

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 -b 115200

应该看到:

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 = <&gpioa 5 GPIO_ACTIVE_HIGH>;
    label = "User LD2";
};

以及:

aliases {
    led0 = &green_led_2;
};

所以我们可以画出:

DT_ALIAS(led0)
      │
      ▼
green_led_2
      │
      ▼
gpioa
  pin 5
      │
      ▼
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/blinky
nano prj.conf

改成:

CONFIG_GPIO=y
CONFIG_PRINTK=y

为什么加:

CONFIG_GPIO=y

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


2. 修改 main.c
现在:

nano src/main.c

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

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/devicetree.h>

#define LED_NODE DT_ALIAS(led0)

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

int main(void)
{
    int ret;

    ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
    if (ret < 0) {
        printk("Failed to configure LED\n");
        return 0;
    }

    while (1) {
        gpio_pin_toggle_dt(&led);
        k_msleep(1000);
    }

    return 0;
}

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

我们逐行拆。


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 = <&gpioa 5 GPIO_ACTIVE_HIGH>;

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

GPIO Controller → GPIOA
Pin             → 5
Active          → 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
        ↓
    1000 ms
        ↓
gpio_pin_toggle_dt()
        ↓
      OFF
        ↓
    1000 ms
        ↓
      repeat

7. 编译
现在执行:

cd ~/workspace/blinky
west build -b nucleo_f303re .

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

west build -b nucleo_f303re . -p always

-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 + Pin 5
   │
   ▼
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 真正值得学习的地方。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值