☰
RISC-V上运行Zephyr与Linux:从工具链到实操的完整指南
2026/10/5 11:23:40 网站建设 项目流程

RISC-V上跑Zephyr和Linux,这条路我替你先蹚了一遍

如果你最近在关注嵌入式或者RISC-V方向,大概率已经看到了Zephyr和Linux这两个名字频繁出现在同一个句子里的情况。RISC-V指令集架构这些年从核到板子到软件生态,进展快得有点超出预期,而Zephyr作为一款轻量级实时操作系统,在RISC-V平台上的支持已经相当成熟,Linux则代表了功能型操作系统的天花板。问题是,这两者怎么跑起来、先跑哪个、跑完怎么用,网上资料虽然不少,但大多要么太零散,要么直接默认你已经懂了一堆前置知识。这篇文章就是来填这个坑的,我会从环境准备讲起,把RISC-V上运行Zephyr和Linux的完整流程拆开揉碎,带你把工具链、编译、运行、排查整条链路走一遍。

先说清楚这篇文章适合谁。如果你刚接触RISC-V,想在QEMU或者真实开发板上把Zephyr和Linux跑起来,验证一下自己写的驱动或者应用,那这篇文章就是给你准备的。如果你已经在做交叉编译、嵌入式Linux,但没在RISC-V上玩过,这里同样能帮你省下一大段摸索时间。整套流程不依赖特定厂商的开发板,默认用QEMU模拟器就能完整复现,对硬件投入的要求几乎是零。

1. 内容整体设计与思路拆解

1.1 RISC-V平台为什么值得专门跑一套双系统

RISC-V和ARM、x86最大的区别在于,它是一个开放指令集架构,任何公司、任何团队都能基于它设计自己的处理器核,不需要向某一家公司支付授权费。这个特性导致RISC-V生态里出现了大量的碎片化处理器实现,但同时也催生了丰富的软件栈适配。你现在拿到的任何一块RISC-V开发板、任何一个QEMU模拟的RISC-V机器,几乎都能找到对应的Zephyr或者Linux支持。

这里涉及到一个比较关键的架构认知:Zephyr和Linux在RISC-V上跑的层次不一样。RISC-V定义了三个特权级——机器模式(M-mode)、监管者模式(S-mode)、用户模式(U-mode)。Zephyr这种实时操作系统默认直接跑在M-mode,拥有对硬件的完全控制权,适合裸机环境、资源受限场景。Linux则需要MMU支持,跑在S-mode,由M-mode的固件或者Bootloader先启动,引导Linux内核进入S-mode执行,然后再调度U-mode的用户进程。这就是为什么这两套系统虽然跑在同一块RISC-V芯片上,但启动流程、内存布局、外设访问方式通通不一样。

1.2 方案选型的考量:先跑Zephyr还是先跑Linux

很多刚接触RISC-V的朋友会问,为什么不能直接用Linux,非要先折腾Zephyr?这个问题的答案取决于你的应用场景。Zephyr面向的是MCU级别的设备,它的调度延迟、内存占用、启动时间都远远优于Linux——典型情况下,Zephyr的内核镜像可以压缩到几十KB级别,启动时间以毫秒计。Linux则相反,即使裁剪到最精简,内核加根文件系统也得几MB起步,启动时间至少是百毫秒级,但它带来了完善的进程管理、网络协议栈、文件系统、驱动模型。

在实际的RISC-V产品设计里,一个常见的组合方式是:小核跑Zephyr处理实时中断和底层控制,大核跑Linux负责应用逻辑和网络通信,两个核之间通过共享内存或者Mailbox通信。所以,在RISC-V上把Zephyr和Linux都跑通,不只是为了学技术,更是为后续异构多核方案打基础。这也是我在这篇文章里把两套系统都覆盖的原因,先分别跑通,再谈协同。

2. 运行环境准备:工具链与模拟器的选择

2.1 开发机环境与必备软件清单

写这篇文章时,我默认你的开发机是Ubuntu 22.04或者更新的发行版,Windows用户可以先去装WSL2,体验基本一致。我们需要准备的核心工具链分三部分:第一是RISC-V交叉编译器,用于把Zephyr和Linux的源码编译成RISC-V可执行文件;第二是QEMU模拟器,用于在没有实体板子的情况下运行RISC-V固件和操作系统;第三是Zephyr SDK或者Zephyr的west工具,用于管理Zephyr源码和编译流程。

具体安装命令不做太多赘述,核心的是把以下包装好:

# 基础依赖 sudo apt update sudo apt install git make gcc g++ pkg-config libsdl2-dev \ libglib2.0-dev libpixman-1-dev ninja-build python3-pip \ device-tree-compiler flex bison # 安装QEMU,RISC-V的system模式模拟器 sudo apt install qemu-system-misc # 安装riscv64交叉编译器 sudo apt install gcc-riscv64-unknown-elf gcc-riscv64-linux-gnu

注意,gcc-riscv64-unknown-elf针对的是裸机开发,编译Zephyr以及RISC-V的M-mode固件;gcc-riscv64-linux-gnu则用于编译Linux内核和用户态程序,这两者不要搞混。Ubuntu仓库里还有gcc-riscv64-unknown-linux-gnu这种命名变体,其实就是Linux交叉工具链,使用时看一下自己机器上的包名。

2.2 QEMU的virt板卡:一套配置通吃Zephyr和Linux

QEMU提供的RISC-V虚拟板卡中,最常用的是virt机型,它既能模拟RV32也能模拟RV64,而且内存、中断控制器、串口、网卡这些外设都配置得比较完整,Zephyr和Linux都把它作为第一优先级的模拟目标。为什么选virt而不是选某个具体开发板?原因有两个:一是virt板卡不依赖特定厂商的BSP,Zephyr仓库里直接就有qemu_riscv32和qemu_riscv64这两个工程配置,Linux内核的defconfig里也有对应的riscv defconfig,开箱即用;二是virt板卡的设备树是公开的,后续自己加外设、改内存布局都很方便。

当我们用QEMU启动RISC-V虚拟机时,实际上就是在模拟一颗完整的RISC-V处理器,包括中断控制器PLIC、定时器CLINT、串口UART、PCIe总线等等。Zephyr和Linux都通过设备树或者板级配置文件来感知这些硬件资源。理解这点很重要,因为后面遇到外设初始化失败、内存映射不对等问题,排查方向就是设备树和内存配置。

2.3 Zephyr SDK与west工具链安装

Zephyr现在的构建系统基于west和CMake,west是一个Python写的多仓库管理工具,负责拉取Zephyr主仓库以及各个子模块。安装流程是:

# 安装west pip3 install west # 拉取Zephyr源码,这里用v3.6版本,稳定且适配RISC-V west init -m https://github.com/zephyrproject-rtos/zephyr --mr v3.6-branch zephyrproject cd zephyrproject west update

这里有一个容易踩坑的点:west update会拉取海量子仓库,国内网络环境下耗时很长,建议设置代理或者耐心等待。拉取完成后,Zephyr项目根目录下会有zephyr、modules、tools等目录。接着需要安装Zephyr SDK,SDK里面包含了Zephyr专用的RISC-V工具链、QEMU、OpenOCD等。下载地址在Zephyr官网,选择最新的SDK版本,解压后运行setup脚本:

cd ~/Downloads tar xvf zephyr-sdk-0.16.5_linux-x86_64.tar.xz cd zephyr-sdk-0.16.5 ./setup.sh -t all

安装完成后,用west build命令编译Zephyr工程时,它会自动找到SDK里的riscv64工具链,不需要额外指定CROSS_COMPILE环境变量。所以整个Zephyr工具链的搭建,核心就是west + SDK两件事,比Linux交叉编译简单不少。

3. RISC-V上运行Zephyr的完整实操

3.1 从hello_world到自定义板级配置

Zephyr在RISC-V上的最小工程就是hello_world,编译和运行都能在QEMU上直接复现。先看最基础的跑通步骤:

cd ~/zephyrproject/zephyr # 先编译RISC-V 32位版本的hello_world west build -b qemu_riscv32 samples/hello_world # 运行 west build -t run

命令执行后,QEMU会启动一个模拟的RISC-V 32位环境,串口上打印出Hello World!。这里的qemu_riscv32是什么?它是Zephyr自带的板级配置,在boards/riscv/qemu_riscv32目录下,定义了CPU核数、内存地址、串口地址、中断控制器类型、时钟频率等信息。对于64位版本同理,qemu_riscv64对应的就是RV64IMAC配置。

看到Hello World不代表你已经理解Zephyr在RISC-V上是怎么启动的。整个启动流程包括四个关键阶段:复位向量进入__start函数,配置栈指针;把.data段从Flash拷贝到RAM,清零.bss段;调用z_cstart初始化内核对象和调度器;最后启动线程执行main函数。对于RISC-V平台,Zephyr还在最前面加了M-mode异常向量和M-mode初始化代码,用来配置中断、时钟、串口。

3.2 配置内核参数:内存、频率与串口

如果你用的是真实开发板,光靠hello_world是远远不够的,至少需要了解Zephyr的prj.conf和设备树配置文件。举个例子,在开发板上跑Zephyr时,往往需要修改以下几个关键参数:

# prj.conf 中的常见配置 CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=10000000 CONFIG_SERIAL=y CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y CONFIG_MAIN_STACK_SIZE=4096

CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC要匹配你的板载时钟频率,如果这个值不对,后续驱动里的延时、定时器全都会乱套。CONFIG_MAIN_STACK_SIZE决定了主线程栈大小,如果在main里定义了较大的局部数组,栈不够用就会触发栈溢出错误。这类问题在QEMU上不太容易暴露,因为QEMU的内存和外设都是虚拟的,与实际硬件还是有不少差别,所以跑真实开发板时一定要耐心核对板级设备树,特别是CLINT和PLIC的中断编号。

3.3 编译自己的应用程序:模块结构与系统调用

Zephyr的开发方式不是写一个独立main函数就行,它更接近Linux内核模块的组织方式。每个应用至少包含CMakeLists.txt、prj.conf、src/main.c,通过west build来构建。一个比较有代表性的应用是操作GPIO和UART:

#include <zephyr/kernel.h> #include <zephyr/device.h> #include <zephyr/drivers/gpio.h> #include <zephyr/drivers/uart.h> void main(void) { const struct device *uart_dev; uart_dev = DEVICE_DT_GET(DT_NODELABEL(uart0)); if (!device_is_ready(uart_dev)) { printk("uart0 not ready\n"); return; } const struct device *gpio_dev; gpio_dev = DEVICE_DT_GET(DT_NODELABEL(gpio0)); gpio_pin_configure(gpio_dev, 0, GPIO_OUTPUT); gpio_pin_set(gpio_dev, 0, 1); printk("RISC-V Zephyr GPIO test\n"); }

这里用到的DT_NODELABEL是从设备树节点中获取设备引用,Zephyr在编译时会根据设备树自动生成对应的宏。在qemu_riscv32上运行这个程序,uart0节点存在,gpio0对应的控制器是RISC-V的平台级GPIO控制器,但QEMU不一定实现了GPIO的具体行为,所以代码能编译、能运行,但GPIO引脚状态在模拟器里没有实际意义。真正的验证需要sipeed之类的真实开发板。

3.4 在Zephyr上使用RISC-V中断与定时器的要点

RISC-V的中断体系与ARM的NVIC差异巨大,Zephyr对这两者都做了抽象。在RISC-V平台上,外部中断通过PLIC控制器分发,定时器中断由CLINT提供。Zephyr的驱动层把PLIC和CLINT封装在arch层,应用层通常只用k_timer或者k_sem,不需要直接操作CSR寄存器。但如果要做裸机级中断响应,就需要了解一下mtvec和meie等CSR的设置,在这点上Zephyr的arch/riscv/core目录下的代码是很好的学习素材。

我实际使用中发现,Zephyr的RISC-V移植在某些真实开发板上需要额外确认CONFIG_RISCV_PLIC是否开启,以及设备树中PLIC节点是否使能。如果中断不触发,十有七八是PLIC节点没有配置正确,或者中断号被设备树里别的节点占用了。

4. RISC-V上运行Linux的完整实操

4.1 编译Linux内核:配置与交叉编译

Linux在RISC-V上的支持已经有相当长时间了,主线内核从5.x开始就已经包含RISC-V架构。相比Zephyr,Linux的编译更传统,直接用make交叉编译即可。以在QEMU virt上运行Linux为例,步骤如下:

# 拉取Linux内核源码,选一个稳定版本,比如6.6 LTS git clone --depth 1 --branch v6.6 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git cd linux # 生成RISC-V默认配置 make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- defconfig # 编译,-j参数按CPU核数调整 make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc)

编译完成后会在arch/riscv/boot/目录下生成Image文件,这就是我们要加载到QEMU里的Linux内核镜像。与ARM的zImage类似,RISC-V的Linux镜像有压缩和非压缩两种,QEMU可以直接加载Image,如果做开发板启动,通常需要配合U-Boot。这里必须强调一下,CROSS_COMPILE要指向Linux交叉工具链,而不是裸机工具链,否则编译器链接时会找不到Linux所需的系统库接口。

4.2 构建根文件系统:初始化与busybox

内核只是半条命,Linux要真正跑起来还需要根文件系统。最小化为两个组件:init进程和动态库。最方便的方式是用busybox构建一个极简根文件系统,大概步骤如下:

# 下载busybox git clone --depth 1 --branch 1_36_1 https://git.busybox.net/busybox cd busybox make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- defconfig # 开启静态编译,避免目标的动态库依赖 make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- menuconfig # 在Settings选项中选中 Build static binary (no shared libs) sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc) make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- install

make install默认装在_install目录,里面包含bin、sbin、usr等目录和init链接到bin/busybox。接下来构造根文件系统目录树,创建必要的设备节点和挂载点:

cd _install mkdir -p proc sys dev etc cat > init << 'EOF' #!/bin/sh mount -t proc none /proc mount -t sysfs none /sys mknod -m 666 /dev/null c 1 3 echo "Welcome to RISC-V Linux" exec /bin/sh EOF chmod +x init

在QEMU中,为了简化操作,用initramfs方式把整个根文件系统打包进内存,启动时内核直接解压到内存盘里运行。这种方式不需要模拟硬盘设备,对初学者非常友好。打包命令:

cd _install find . | cpio -o -H newc | gzip > ../rootfs.cpio.gz

启动Linux的命令:

qemu-system-riscv64 -M virt -m 512M \ -kernel arch/riscv/boot/Image \ -initrd linux/rootfs.cpio.gz \ -nographic -append "console=ttyS0 rdinit=/init"

如果一切顺利,你会看到Linux内核打印启动日志,并在最后进入shell提示符。这个过程在QEMU virt上非常稳定,基本不会碰到硬件兼容性问题。

4.3 设备树在启动流程中的角色

QEMU virt启动Linux时,QEMU会自动生成一个设备树二进制文件传给内核。你可以主动抓取这个设备树,也可以用自己编译好的设备树:

qemu-system-riscv64 -M virt -machine dumpdtb=virt.dtb dtc -I dtb -O dts virt.dtb -o virt.dts

查看virt.dts能直观看到内存起始地址、UART串口、中断控制器PLIC、定时器CLINT、PCIe等节点的配置信息。Linux内核启动时通过设备树识别硬件资源,如果设备树里没有对应的节点,对应的驱动就不会加载。这就是为什么在RISC-V平台,设备树的修改能力几乎是搞Linux开发的必备技能。

一个常见的需求是改设备树里内存的大小。比如QEMU virt板默认内存地址从0x80000000开始,大小512M,你可以通过-m参数修改,但内核的物理内存探测范围必须包含这一地址。如果内存起始地址不对,内核启动时会直接挂死在分区检测阶段,怎么排查都不对劲。改设备树后可以重新用dtc编译为dtb,再用-dtb参数传给QEMU。

4.4 上手RISC-V Linux驱动的三个示例

在RISC-V上写Linux驱动与ARM平台大同小异,区别主要在于寄存器地址和中断号要从设备树获取。这里提供一个平台驱动的最小骨架,用来操作一个虚拟外设:

#include <linux/module.h> #include <linux/platform_device.h> #include <linux/of_address.h> static int virt_device_probe(struct platform_device *pdev) { struct resource *res; void __iomem *base; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); /* 在这里配置/读取寄存器 */ pr_info("RISC-V virt device probed\n"); return 0; } static int virt_device_remove(struct platform_device *pdev) { pr_info("RISC-V virt device removed\n"); return 0; } static const struct of_device_id virt_device_of_match[] = { { .compatible = "myvendor,virt-device" }, { } }; MODULE_DEVICE_TABLE(of, virt_device_of_match); static struct platform_driver virt_device_driver = { .probe = virt_device_probe, .remove = virt_device_remove, .driver = { .name = "virt_device", .of_match_table = virt_device_of_match, }, }; module_platform_driver(virt_device_driver); MODULE_LICENSE("GPL");

编译这个驱动的命令是:

make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- M=$(pwd) modules

把生成的.ko文件放进根文件系统,在RISC-V Linux里insmod即可加载。驱动里通过设备树拿到的res就是你在virt.dts里为这个外设分配的地址段。这种驱动开发模式和ARM、x86完全一致,所以如果你之前有Linux驱动经验,转到RISC-V几乎是无痛的。

5. Zephyr与Linux的协同思路与硬件抽象对比

5.1 两者在RISC-V上面的资源占用对比

跑通之后,很多人关心的是资源占用。我实测的结果:Zephyr编译出的hello_world镜像大约50KB左右,内存占用更小,RAM大概几十KB;Linux内核Image在10MB左右,运行起来占用内存60MB以上,考虑到QEMU分配的512M内存,实际最小启动内存可以压到128M,但再低就容易OOM了。这一对比清晰地展现了两个系统的定位差异。

项目Zephyr (qemu_riscv32)Linux (qemu_riscv64)
内核镜像大小约50KB约10MB
最小RAM要求几十KB至少128MB
启动时间毫秒级数百毫秒以上
实时性硬实时一般
驱动模型极简设备模型Linux设备模型
多任务调度优先级抢占CFS调度
适用场景传感器、控制、裸机文件系统、网络、复杂应用

5.2 openSBI在RISC-V引导链路中的作用

前面提到,Linux在RISC-V上运行在S-mode,需要一个M-mode固件负责初始化硬件、跳转内核,这就是OpenSBI。QEMU实测时可以不用显式加载OpenSBI,因为QEMU内置了对应的M-mode固件,不过在实际开发板上,BootROM会加载U-Boot SPL,然后SPL加载OpenSBI和U-Boot,U-Boot再加载Linux内核。OpenSBI提供SBI调用接口,给S-mode的Linux使用,比如时钟、定时器、远程中断。如果直接在裸机上跑Linux,没有OpenSBI,内核起不来,因为早期的初始化工作必须靠在M-mode下执行。

理解这个链路之后,再回头看Zephyr和Linux在RISC-V上的定位就清晰了:Zephyr自己把M-mode的初始化全部干了,不需要OpenSBI;Linux则必须依赖OpenSBI或者类似的固件。如果哪一天你想把Zephyr同时用作M-mode固件来引导Linux,也是可行的,只需要在Zephyr中实现SBI的一部分接口,然后把Linux当作S-mode的负载来跳转,这个概念在学术界和工业界都有案例。

5.3 异构多核场景下的通信方案

最后提一嘴真正让这个组合发挥价值的方向:异构多核。现在不少RISC-VSoC把大小核集成在一起,Linux跑大核,Zephyr跑小核。共享资源可以是固定地址的共享内存,也可以是通过IPI中断触发的事件通知机制。在Zephyr侧可以用openamp模块实现远程处理器消息传递,Linux侧则用rpmsg框架。两者通过一个简单协议交换数据包。这个方向水很深,但最初的基础就是让两个系统都能独立跑起来,然后才能谈通信。我建议先把本文的流程熟练到闭着眼能复现,再去碰OpenAMP。

6. 常见问题与排查技巧实录

6.1 工具链不匹配导致的编译错误

我在跑Zephyr时出现过最典型的编译错误是头文件路径不对,表现为找不到zephyr/kernel.h这类文件。原因通常是zephyr-base环境变量没有设置,或者路径拼写有误。Zephyr的构建系统在west对仓库初始化后,会自动探测源码根路径,但我见过有人用west build -p强制清理后,把Zephyr仓库路径搞乱了。解决方法是重新执行west init和west update,并确保在项目根目录执行west build。

Linux这边常见的坑是CROSS_COMPILE写错。如果你用gcc-riscv64-unknown-elf去编译Linux内核,会在链接阶段遇到无数个mno-omit-leaf-frame-pointer之类的平台选项不兼容错误,原因在于这个编译器没有内置Linux ABI相关的支持。解决方式就是切换到riscv64-linux-gnu-gcc,比如Ubuntu的包名是gcc-riscv64-linux-gnu。

6.2 内核启动挂死:设备树与内存配置

在QEMU跑Linux时,如果启动过程打印到Machine model: riscv-virtio就算成功,然后卡在Waiting for root device /dev/root。这个问题通常是根文件系统参数没写对。我们用initramfs时,-append参数里要明确rdinit=/init,否则内核会去找硬盘设备上的root,找不到自然挂起。还有一种情况是-m 512M和DTS中memory节点大小不一致,如果QEMU自动生成设备树,一般会匹配,但如果是自己编译的dtb,就可能出现内存映射回环问题。

Zephyr启动挂死的情况也有,我以前遇到最多的是串口没有输出。串口没有输出,不代表内核没跑,可能是CONFIG_SERIAL没开启,或者控制台输出函数被Kernel早期初始化阶段卡住了。此时建议先打开CONFIG_LOG级别到debug,并确认设备树里chosen节点的stdout-path指向的串口节点有效。

6.3 常见问题速查表

现象可能原因解决方法
west build失败,提示找不到SDK未安装或未设置Zephyr SDK重新运行SDK的setup.sh,确认环境变量
Zephyr能编译,但QEMU启动黑屏无声板级配置中串口未使能打开CONFIG_UART_CONSOLE,检查chosen节点
Linux编译报错:unknown architectureARCH未设置为riscvmake ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu-
Linux启动后无法识别PCIe设备设备树PCIe节点缺失更新设备树或使用QEMU生成的dtb
QEMU启动Linux显示cannot access memoryrootfs路径或内存参数错误检查-initrd路径,调整-m大小
RISC-V开不了SMP多核内核config未开启SMP开启CONFIG_SMP,并确认PLIC中断配置

6.4 提高调试效率的几个习惯

调试RISC-V系统,比起加printk,我更推荐先熟练使用QEMU的-d参数。-d in_asm能看到反汇编,-d int能抓到每一次中断,再配合-monitor stdio进去敲info registers,定位异常现场非常快。Leveraging QEMU的gdb支持也很关键,启动时加-s -S,然后用gdb连接1234端口,可直接对内核下断点,比在任何实体开发板上调试都方便。

另一个经验是不要把代码改到你觉得逻辑完美再跑,最好是把“工具链检查—最小镜像—最小系统—功能迭代”切成小步进,每步验证。我看到太多人一次性改了一堆代码,结果Zephyr起不来、Linux也起不来,最后只能从头排查,心态很容易崩。

7. 结束语:从模拟器到真实开发板的迁移之路

我自己在这套技术栈里踩过不少坑,最深的体会是:不要被工具链吓退,也不要因为模拟器上跑通了就觉得万事大吉。QEMU的virt平台虽然方便,但毕竟是虚拟环境,真实开发板上的时钟频率、外设时序、中断响应都可能和模拟器有差异。我的建议是,先用模拟器把Zephyr和Linux的构建、启动、交互流程彻底跑通,理解操作系统在这颗转译内核上是怎么被引导起来的,然后再考虑入手真正的RISC-V开发板,把同样的镜像烧进去验证差异性。这篇文章从工具链到操作细节,再到问题排查,基本覆盖了从零到一的过程。如果卡在环境搭建,不妨按顺序逐条比对;如果对某个环节有更深的疑问,欢迎留言,我后续可以针对驱动开发或者Zephyr内核调度再单独展开。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询