Zephyr最简工程配置指南

Zephyr最简工程配置指南:x86平台+QEMU运行

引言

在Zephyr RTOS开发中,配置一个最简工程是学习和测试的最佳起点。本文将详细介绍如何配置一个最小化的Zephyr工程,仅使用x86平台,并在QEMU模拟器中运行。

这种配置方式具有以下优势:

  • 快速入门:无需硬件设备即可体验Zephyr
  • 资源高效:仅包含必要组件
  • 易于调试:QEMU提供完整的调试支持
  • 跨平台:可在Windows、Linux、macOS上运行

一、环境准备

1.1 安装必要依赖

bash 复制代码
# Ubuntu/Debian系统
sudo apt-get update && sudo apt-get install -y \
    git \
    cmake \
    ninja-build \
    gperf \
    ccache \
    python3 \
    python3-pip \
    python3-venv \
    device-tree-compiler \
    qemu-system-x86  # QEMU x86模拟器

# macOS系统(使用Homebrew)
brew install git cmake ninja gperf ccache python dtc qemu

# Windows系统
# 建议使用WSL2或直接安装Zephyr SDK

1.2 安装West工具

bash 复制代码
# 安装West
pip install west

# 验证安装
west --version

1.3 获取Zephyr源码(最小化)

bash 复制代码
# 创建工作目录
mkdir -p ~/zephyrproject && cd ~/zephyrproject

# 初始化West项目(只获取核心仓库)
west init -m https://github.com/zephyrproject-rtos/zephyr --mr main

# 配置只下载必要组件
west config manifest.project-filter -- -hal_* -lib_* -modules_*

# 更新代码(仅核心仓库)
west update --narrow

# 安装Python依赖
pip install -r zephyr/scripts/requirements.txt

二、最简工程结构

2.1 创建工程目录

bash 复制代码
# 创建工程目录
mkdir -p ~/zephyr-app && cd ~/zephyr-app

# 创建必要文件
touch CMakeLists.txt prj.conf src/main.c

2.2 工程文件结构

复制代码
zephyr-app/
├── CMakeLists.txt           # CMake配置文件
├── prj.conf                 # Kconfig配置文件
└── src/
    └── main.c               # 主程序入口

2.3 CMakeLists.txt配置

cmake 复制代码
# CMakeLists.txt

# 最低CMake版本要求
cmake_minimum_required(VERSION 3.20.0)

# 包含Zephyr构建系统
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

# 项目名称
project(my_minimal_app)

# 添加源文件
target_sources(app PRIVATE src/main.c)

2.4 prj.conf配置(最简配置)

conf 复制代码
# prj.conf - 最简配置

# 基础配置
CONFIG_SOC_POSIX=y
CONFIG_BOARD_QEMU_X86=y

# 内核配置
CONFIG_KERNEL=y
CONFIG_INIT_STACKS=y

# 控制台输出
CONFIG_CONSOLE=y
CONFIG_SERIAL=y
CONFIG_UART_CONSOLE=y

# 调试配置
CONFIG_DEBUG=y
CONFIG_DEBUG_OPTIMIZATIONS=y

# 系统时钟
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=100000000

# 定时器
CONFIG_TIMER=y
CONFIG_SYS_CLOCK_TICKS_PER_SEC=100

三、编写最简应用代码

3.1 main.c 最小实现

c 复制代码
/* src/main.c - 最简Zephyr应用 */

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

/* 定义线程栈大小 */
#define STACKSIZE 1024

/* 定义线程优先级 */
#define PRIORITY 7

/* 线程函数 */
void main_thread(void)
{
    int count = 0;
    
    while (1) {
        printk("Hello Zephyr! Count: %d\n", count++);
        k_msleep(1000);
    }
}

/* 定义线程 */
K_THREAD_DEFINE(main_tid, STACKSIZE,
                main_thread, NULL, NULL, NULL,
                PRIORITY, 0, K_NO_WAIT);

/* 主入口函数 */
void main(void)
{
    printk("Zephyr minimal app started!\n");
}

3.2 代码解释

部分 说明
#include <zephyr/kernel.h> 内核API头文件
#include <zephyr/sys/printk.h> 打印函数头文件
K_THREAD_DEFINE 定义静态线程
k_msleep() 毫秒级延时函数
printk() 内核打印函数

四、编译配置

4.1 设置环境变量

bash 复制代码
# 设置Zephyr基础路径
export ZEPHYR_BASE=~/zephyrproject/zephyr

# 设置SDK路径(如果需要)
# export ZEPHYR_SDK_INSTALL_DIR=/opt/zephyr-sdk-0.17.2

# 验证设置
echo $ZEPHYR_BASE

4.2 编译命令

bash 复制代码
# 进入工程目录
cd ~/zephyr-app

# 编译(指定qemu_x86目标板)
west build -b qemu_x86 .

# 查看编译结果
ls -la build/zephyr/

4.3 编译输出

bash 复制代码
# 编译成功输出示例
[100%] Linking C executable zephyr/zephyr.elf
[100%] Built target zephyr

五、在QEMU中运行

5.1 运行命令

bash 复制代码
# 方式1:使用west run命令
west build -t run

# 方式2:手动运行QEMU
qemu-system-i386 \
    -cpu qemu32 \
    -nographic \
    -kernel build/zephyr/zephyr.elf \
    -machine pc

# 方式3:带调试端口运行
qemu-system-i386 \
    -cpu qemu32 \
    -nographic \
    -kernel build/zephyr/zephyr.elf \
    -machine pc \
    -s -S

5.2 运行输出

bash 复制代码
# 预期输出
SeaBIOS (version rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org)
Booting from ROM..
*** Booting Zephyr OS build zephyr-v3.5.0-xxx ***
Zephyr minimal app started!
Hello Zephyr! Count: 0
Hello Zephyr! Count: 1
Hello Zephyr! Count: 2
...

5.3 退出QEMU

bash 复制代码
# 按 Ctrl+A 然后按 X 退出QEMU

六、调试配置

6.1 使用GDB调试

bash 复制代码
# 启动QEMU并监听调试端口
qemu-system-i386 \
    -cpu qemu32 \
    -nographic \
    -kernel build/zephyr/zephyr.elf \
    -machine pc \
    -s -S &

# 启动GDB
gdb-multiarch build/zephyr/zephyr.elf

# GDB命令
(gdb) target remote localhost:1234
(gdb) break main
(gdb) continue
(gdb) info threads
(gdb) next
(gdb) print count

6.2 VS Code调试配置

创建 .vscode/launch.json:

json 复制代码
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Zephyr QEMU Debug",
            "type": "cppvsdbg",
            "request": "launch",
            "program": "${workspaceFolder}/build/zephyr/zephyr.elf",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "preLaunchTask": "build",
            "MIMode": "gdb",
            "miDebuggerPath": "gdb-multiarch",
            "miDebuggerServerAddress": "localhost:1234",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ]
        }
    ]
}

七、进阶配置

7.1 添加shell支持

修改 prj.conf:

conf 复制代码
# 添加shell支持
CONFIG_SHELL=y
CONFIG_SHELL_BACKEND_SERIAL=y
CONFIG_SHELL_PROMPT="zephyr> "

运行效果:

bash 复制代码
*** Booting Zephyr OS build zephyr-v3.5.0-xxx ***
Zephyr minimal app started!
zephyr> help
Available commands:
  help    : print this help
  kernel  : kernel commands
  version : print kernel version
zephyr> kernel threads
Threads:
  0x200003e0 main_tid  prio=7
  0x20000100 idle     prio=31
zephyr>

7.2 添加定时器功能

c 复制代码
/* 在main.c中添加定时器 */

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

#define TIMER_STACKSIZE 512
#define TIMER_PRIORITY 5

struct k_timer my_timer;

void timer_expiry_fn(struct k_timer *timer_id)
{
    printk("Timer expired!\n");
}

void timer_stop_fn(struct k_timer *timer_id)
{
    printk("Timer stopped!\n");
}

K_TIMER_DEFINE(my_timer, timer_expiry_fn, timer_stop_fn);

void main(void)
{
    printk("Starting timer...\n");
    k_timer_start(&my_timer, K_SECONDS(1), K_SECONDS(1));
    
    while (1) {
        k_msleep(500);
    }
}

7.3 添加线程间通信

c 复制代码
/* 添加信号量示例 */

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

#define STACKSIZE 1024
#define PRIORITY 7

K_SEM_DEFINE(my_sem, 0, 1);

void thread1(void)
{
    while (1) {
        printk("Thread 1: Waiting for semaphore\n");
        k_sem_take(&my_sem, K_FOREVER);
        printk("Thread 1: Got semaphore\n");
        k_msleep(1000);
    }
}

void thread2(void)
{
    while (1) {
        printk("Thread 2: Giving semaphore\n");
        k_sem_give(&my_sem);
        k_msleep(2000);
    }
}

K_THREAD_DEFINE(thread1_tid, STACKSIZE, thread1, NULL, NULL, NULL, PRIORITY, 0, K_NO_WAIT);
K_THREAD_DEFINE(thread2_tid, STACKSIZE, thread2, NULL, NULL, NULL, PRIORITY + 1, 0, K_NO_WAIT);

void main(void)
{
    printk("Thread communication example\n");
}

八、常见问题与解决方案

8.1 QEMU未找到

问题 :qemu-system-i386: command not found

解决:

bash 复制代码
# Ubuntu/Debian
sudo apt-get install qemu-system-x86

# macOS
brew install qemu

# 验证安装
qemu-system-i386 --version

8.2 编译错误 - 缺少工具链

问题 :arm-zephyr-eabi-gcc: command not found

解决:

bash 复制代码
# 对于x86平台,通常不需要额外工具链
# 如果需要SDK,安装Zephyr SDK
wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.17.2/zephyr-sdk-0.17.2-setup.run
chmod +x zephyr-sdk-0.17.2-setup.run
./zephyr-sdk-0.17.2-setup.run --include-i686 --include-qemu

8.3 链接错误 - undefined reference

问题 :undefined reference to 'printk'

解决:

conf 复制代码
# 在prj.conf中添加
CONFIG_CONSOLE=y
CONFIG_SERIAL=y
CONFIG_UART_CONSOLE=y

8.4 QEMU运行无输出

问题:运行QEMU后没有输出

解决:

bash 复制代码
# 确保使用-nographic选项
qemu-system-i386 -nographic -kernel build/zephyr/zephyr.elf

# 检查串口配置
# 在prj.conf中添加
CONFIG_UART_CONSOLE_ON_DEV_NAME="uart0"

九、最简工程模板

9.1 完整工程打包

bash 复制代码
# 创建模板目录结构
mkdir -p zephyr-minimal-app/{src,config}
cd zephyr-minimal-app

# 创建CMakeLists.txt
cat > CMakeLists.txt << 'EOF'
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(minimal_app)
target_sources(app PRIVATE src/main.c)
EOF

# 创建prj.conf
cat > prj.conf << 'EOF'
CONFIG_SOC_POSIX=y
CONFIG_BOARD_QEMU_X86=y
CONFIG_KERNEL=y
CONFIG_INIT_STACKS=y
CONFIG_CONSOLE=y
CONFIG_SERIAL=y
CONFIG_UART_CONSOLE=y
CONFIG_DEBUG=y
CONFIG_DEBUG_OPTIMIZATIONS=y
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=100000000
CONFIG_TIMER=y
CONFIG_SYS_CLOCK_TICKS_PER_SEC=100
EOF

# 创建main.c
cat > src/main.c << 'EOF'
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

#define STACKSIZE 1024
#define PRIORITY 7

void main_thread(void)
{
    int count = 0;
    while (1) {
        printk("Hello Zephyr! Count: %d\n", count++);
        k_msleep(1000);
    }
}

K_THREAD_DEFINE(main_tid, STACKSIZE, main_thread, NULL, NULL, NULL, PRIORITY, 0, K_NO_WAIT);

void main(void)
{
    printk("Zephyr minimal app started!\n");
}
EOF

echo "Minimal Zephyr app created successfully!"

9.2 使用模板

bash 复制代码
# 使用模板创建工程
mkdir -p ~/myapp && cd ~/myapp
cp -r /path/to/zephyr-minimal-app/* .

# 设置环境变量
export ZEPHYR_BASE=~/zephyrproject/zephyr

# 编译运行
west build -b qemu_x86 .
west build -t run

十、性能优化建议

10.1 优化编译配置

conf 复制代码
# prj.conf - 优化配置

# 关闭调试信息(发布版本)
CONFIG_DEBUG=n
CONFIG_DEBUG_OPTIMIZATIONS=n
CONFIG_OPTIMIZE_FOR_SIZE=y

# 关闭不需要的功能
CONFIG_ASSERT=n
CONFIG_LOG=n
CONFIG_SHELL=n

# 优化内存
CONFIG_HEAP_MEM_POOL_SIZE=2048
CONFIG_KERNEL_MEM_POOL_SIZE=1024

10.2 优化代码

c 复制代码
// 优化后的main.c

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

#define STACKSIZE 512  // 减小栈大小
#define PRIORITY 7

void main_thread(void)
{
    static int count;  // 使用静态变量节省栈空间
    
    for (;;) {
        printk("Count: %d\n", count++);
        k_sleep(K_MSEC(1000));  // 使用k_sleep替代k_msleep
    }
}

K_THREAD_DEFINE(main_tid, STACKSIZE, main_thread, NULL, NULL, NULL, PRIORITY, 0, K_NO_WAIT);

void main(void) { }  // 简化主函数

结束语

通过本文的介绍,您已经掌握了如何配置一个最简的Zephyr工程:

步骤 内容
环境准备 安装QEMU、West工具
工程结构 CMakeLists.txt + prj.conf + src/main.c
编译命令 west build -b qemu_x86 .
运行命令 west build -t run
调试方法 GDB + QEMU远程调试

最简工程的优势:

  1. 快速上手:无需硬件设备,QEMU即可运行
  2. 资源高效:只包含必要组件,编译速度快
  3. 易于学习:代码量少,便于理解Zephyr核心概念
  4. 可扩展性:可以逐步添加功能

建议在掌握最简工程后,尝试添加更多功能:

  • Shell交互
  • 定时器
  • 线程间通信
  • 文件系统
  • 网络功能

参考资料:

相关推荐
wotaifuzao12 天前
(十)BLE GATT与ATT深度解剖-从属性表到CCCD-订阅收不到数据的终极答案
低功耗蓝牙·ble·nrf52·zephyr·gatt·cccd
星源~12 天前
zephyr-Linux环境下搭建步骤
linux·mcu·嵌入式开发·zephyr
dozenyaoyida21 天前
Zephyr BLE 上行链路源码拆解:一个空中报文怎么从 radio 跑到应用回调(NCS v3.2.1)
controller·嵌入式开发·ble·host·zephyr·协议栈·软链路层
dozenyaoyida23 天前
Zephyr BLE GATT Server 注册到收发调用链源码解析(NCS v3.2.1)
ble·zephyr·gatt·ble协议栈
iini2 个月前
nRF Connect SDK 开发新体验:VS Code + Claude Code + DeepSeek + Nordic MCP 全流程(蓝牙开发实例)
agent·ai编程·nrf connect sdk·zephyr·蓝牙开发·deepseek·mcp·claude code·nordic mcp
fitpolo2 个月前
详解Zephyr设备树与设备驱动模型
zephyr
乐鑫科技 Espressif2 个月前
用 AI 开发 Zephyr-IoT 应用
人工智能·物联网·esp32·乐鑫科技·zephyr
固执的你2 个月前
串口中断接收协议数据并用状态机解析_printk()打印内容
zephyr
fitpolo2 个月前
Zephyr的SPI
zephyr
toradexsh3 个月前
基于 NXP i.MX8M Plus 测试 Zephyr RTOS
arm·rtos·nxp·imx8mp·zephyr