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远程调试 |
最简工程的优势:
- 快速上手:无需硬件设备,QEMU即可运行
- 资源高效:只包含必要组件,编译速度快
- 易于学习:代码量少,便于理解Zephyr核心概念
- 可扩展性:可以逐步添加功能
建议在掌握最简工程后,尝试添加更多功能:
- Shell交互
- 定时器
- 线程间通信
- 文件系统
- 网络功能
参考资料: