资讯详情

资讯详情

建站行业动态 · 设计趋势 · 数字化升级干货

nRF5340双核开发实战:构建与应用核心空固件指南

nRF5340双核开发实战:构建与应用核心空固件指南 1. 项目缘起为什么需要一个“空”固件在嵌入式开发尤其是基于nRF5340这类双核SoC的项目中我们常常会遇到一个看似简单却至关重要的需求让其中一个核心“安静”下来。你可能正在开发一个复杂的应用其中网络协议栈运行在nRF5340的网络核心上而应用核心则负责处理业务逻辑。在开发初期或者在对网络核心进行独立调试时你并不希望应用核心执行任何代码甚至不希望它干扰网络核心的启动和运行。这时一个专门为应用核心准备的“空固件”就成为了必需品。这个“空固件”并非指一个完全空白的、不包含任何指令的程序。相反它是一个经过精心设计的、最小化的可执行镜像。它的核心任务非常明确安全地初始化应用核心的必要硬件环境然后让核心进入一个确定的、低功耗的休眠状态或者在一个无限循环中空转确保它不会访问共享资源、不会产生中断、不会执行任何可能影响另一个核心的操作。这就像给一个房间上锁并贴上“施工中请勿进入”的标识确保其他部分的工作不受干扰。在nRF5340 DK板子上运行这样一个空固件是进行双核协同开发、模块化调试以及功耗优化测试的基础步骤。很多开发者第一次接触双核芯片时会忽略对闲置核心的管理导致出现一些难以排查的异常比如共享内存被意外修改、外设冲突、或者功耗异常。因此掌握如何构建和烧录这样一个“空”固件是玩转nRF5340的必修课。2. 环境搭建与工具链选型要在nRF5340 DK上运行任何固件首先需要一个健全的开发环境。这里我们选择nRF Connect SDK作为开发框架它不仅官方支持nRF5340而且集成了Zephyr RTOS提供了对双核架构的原生支持是完成这个任务最直接、最可靠的工具。2.1 安装nRF Connect SDK官方推荐使用nRF Connect for Desktop应用中的Toolchain Manager来管理SDK安装。这种方法能自动处理依赖包括编译器、CMake、Python环境等避免手动配置带来的各种“玄学”问题。下载与安装前往Nordic Semiconductor官网下载并安装nRF Connect for Desktop。安装完成后打开它并在“Manager”页面找到并安装“Toolchain Manager”。安装SDK打开Toolchain Manager你会看到一个可用的SDK版本列表例如v2.6.x。选择一个稳定的版本通常建议选择最新的LTS版本或较新的稳定版点击“Install”。这个过程会下载约几个GB的数据包括SDK本身、GCC工具链、必要的Python包等请确保网络通畅。设置开发目录安装完成后Toolchain Manager会提示你选择一个工作目录。建议创建一个独立的文件夹例如C:\ncs或~/ncs所有项目都将在此目录下进行。注意强烈建议使用Toolchain Manager进行安装。虽然你也可以手动安装Zephyr环境但对于nRF5340官方SDK包含了许多Nordic特有的驱动、库和配置手动搭建环境极易出现兼容性问题尤其是涉及双核映像生成时。2.2 验证安装与准备DK板安装完成后我们需要验证环境并准备好硬件。打开开发终端在Toolchain Manager中找到你安装的SDK版本旁边会有一个“Open terminal”按钮。点击它这将打开一个已经配置好所有环境变量如ZEPHYR_BASE,PATH的终端。后续所有命令都需在此终端中执行。连接DK板使用USB线将nRF5340 DK板连接到电脑。DK板上有两个USB接口一个标记为J2用于编程和调试另一个标记为J3用于串口通信。我们只需要连接J2即可它会同时提供调试和虚拟串口功能。连接后电脑应能识别到两个设备一个J-Link调试器和一个CDC ACM串口。检查设备连接在终端中可以使用nrfjprog --ids命令来查看连接的J-Link设备。如果看到设备列表中有你的板子通常显示为[SEGGER J-Link]说明硬件连接和驱动正常。3. 创建与构建“空”应用核心固件nRF Connect SDK使用CMake构建系统并提供了丰富的示例。对于我们的需求最接近的起点是empty_app_core示例或者我们可以从一个最基础的hello_world示例改造而来。这里我们以创建一个独立的应用核心项目为例。3.1 创建项目目录结构我们不直接修改SDK自带的示例而是创建一个属于自己的项目这样更清晰也便于版本管理。在SDK工作目录外例如你的个人项目目录~/my_projects创建一个新文件夹例如empty_app_core_project。进入该文件夹创建以下基本结构empty_app_core_project/ ├── CMakeLists.txt ├── prj.conf └── src/ └── main.c这是Zephyr项目最精简的结构。CMakeLists.txt告诉构建系统如何编译prj.conf是Kconfig配置文件用于设置内核和驱动选项src/main.c是我们的源代码。3.2 编写核心源代码main.c“空”固件的源代码非常简单其核心思想是初始化后让核心进入一个永久的、无害的等待状态。#include zephyr/kernel.h void main(void) { /* 可选的早期初始化或打印信息 */ printk(Application core is entering idle state.\n); /* * 核心逻辑让应用核心进入永久空闲状态。 * 方案1使用k_cpu_idle()但更推荐方案2。 */ /* 方案2在一个低优先级的线程中循环或直接让主线程空转 */ while (1) { /* 调用k_cpu_idle()允许CPU进入低功耗模式 */ k_cpu_idle(); /* 或者使用k_busy_wait()进行忙等待功耗高不推荐用于产品 */ // k_busy_wait(1000); // 等待1毫秒 } }代码解析与选型理由printk用于在串口输出一条信息方便我们确认固件已启动。在最终产品中可移除。while (1)一个无限循环防止main函数返回在Zephyr中main返回会导致线程终止。k_cpu_idle()这是关键所在。这个函数是Zephyr内核提供的它会将CPU置于低功耗空闲状态等待中断唤醒。由于我们这个“空”固件没有使能任何定时器中断或外设中断调用它后CPU功耗会降到很低。这比纯粹的while(1);忙等待要专业得多后者会导致CPU全速运行功耗很高。为什么不直接是空的main函数一个完全空的main函数在Zephyr中行为可能未定义或者链接器可能报错。显式地写一个while(1)循环并调用k_cpu_idle()是一种明确、安全且低功耗的做法。3.3 配置项目prj.conf这个配置文件决定了固件包含哪些功能。对于“空”固件我们需要尽可能精简。# 启用打印输出用于调试 CONFIG_PRINTKy CONFIG_STDOUT_CONSOLEy # 主线程配置 CONFIG_MAIN_STACK_SIZE1024 # 使用较小的栈空间 # 重要禁用不需要的外设和驱动减少固件体积和潜在冲突 # 例如禁用蓝牙、网络等 # CONFIG_BTn # CONFIG_NETWORKINGn # 内核基础功能保持启用 CONFIG_MULTITHREADINGy CONFIG_CPU_IDLEy # 确保CPU空闲功能启用k_cpu_idle()才有效这个配置非常精简只保留了最基础的控制台输出和线程调度功能并明确启用了CPU空闲模式。3.4 编写构建脚本CMakeLists.txt这个文件将我们的项目与Zephyr构建系统连接起来。# 指定所需CMake版本和项目名称 cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(empty_app_core) # 将源代码文件添加到目标 target_sources(app PRIVATE src/main.c)关键点说明find_package(Zephyr ...)这行至关重要它引入了Zephyr的所有构建定义。$ENV{ZEPHYR_BASE}环境变量是在我们通过Toolchain Manager打开的终端中自动设置的。project(empty_app_core)定义项目名称这也会影响最终生成的二进制文件名称。target_sources将我们的main.c文件添加到名为app的目标中这是Zephyr的默认应用目标。4. 构建、烧录与验证现在我们有了完整的项目文件可以开始构建了。4.1 构建固件在项目目录empty_app_core_project下打开之前通过Toolchain Manager启动的终端。创建构建目录并配置通常采用“影子构建”out-of-tree build。mkdir build cd build接着使用CMake配置项目并指定目标开发板和核心。这是区分应用核心与网络核心构建的关键步骤。cmake -GNinja -DBOARDnrf5340dk_nrf5340_cpuapp ..参数解析-GNinja指定使用Ninja作为构建后端它比Make更快。-DBOARDnrf5340dk_nrf5340_cpuappBOARD参数不仅指定了板子型号nrf5340dk还通过后缀_cpuapp明确指定了这是为应用核心构建。如果为网络核心构建则需要使用_cpunet后缀。..表示CMakeLists.txt在上一级目录。执行编译ninja如果一切顺利编译完成后你会在build目录下找到多个生成的文件其中最关键的是zephyr.hex: Intel HEX格式的合并映像。zephyr.elf: ELF格式文件包含调试信息。zephyr.bin: 纯二进制文件常用于烧录。4.2 烧录固件到DK板烧录工具我们使用nrfjprog它是Nordic命令行工具集的一部分已在环境内。连接板子并擦除确保DK板通过J2口连接并处于可编程状态通常无需额外操作。首先擦除芯片nrfjprog -f nrf53 --eraseall-f nrf53指定芯片系列--eraseall会擦除整个芯片的Flash包括两个核心的区域。烧录应用核心固件nrfjprog -f nrf53 --program build/zephyr.hex --sectorerase--program指定要烧录的HEX文件。--sectorerase按扇区擦除比全擦除更快。由于我们刚执行了--eraseall这里用sectorerase或--chiperase都可以。重要这个命令会将固件烧录到应用核心的Flash区域。nRF5340的存储空间是物理划分的HEX文件中的地址信息会自动决定数据写入哪个区域。复位并运行nrfjprog -f nrf53 --reset此命令会让芯片复位并开始执行。4.3 验证固件运行如何确认我们的“空”固件在正确运行呢串口监听打开一个串口终端工具如PuTTY、Tera Term或screen/minicom在Linux/macOS上。找到DK板对应的串口端口在Windows设备管理器中是COMx在Linux/macOS上是/dev/ttyACMx设置波特率为115200数据位8停止位1无校验。观察输出给板子复位或重新上电。你应该在串口终端看到一行输出*** Booting Zephyr OS build ... ***Zephyr启动标志紧接着就是我们代码中打印的Application core is entering idle state.。之后终端将再无输出。这正是我们期望的行为——应用核心打印完信息后就进入k_cpu_idle()循环安静地待着不执行任何其他操作。功耗验证进阶你可以使用DK板上的电流测量接口或一个万用表测量板子的总电流。运行这个“空”固件时整个芯片的功耗应该显著低于运行一个复杂应用比如带蓝牙的应用。这间接证明了应用核心确实进入了低功耗状态。5. 高级话题双核映像管理与网络核心状态在实际项目中你很少会只烧录一个“空”的应用核心固件。更常见的场景是网络核心运行着实际的协议栈如蓝牙而应用核心运行着业务逻辑或另一个“空”固件。这就涉及到双核映像的管理。5.1 理解nRF5340的双核启动流程nRF5340的启动有一个固定的顺序芯片上电或复位后网络核心首先启动。网络核心从自己的Flash起始地址开始执行代码。它的启动代码通常由nRF Connect SDK提供会进行一些基础初始化然后检查应用核心的复位向量是否有效。如果应用核心的复位向量有效即应用核心Flash区域已编程网络核心会释放应用核心的复位信号启动应用核心。此后两个核心独立运行通过共享内存IPC进行通信。我们的“空”应用核心固件在上述流程的第3步被启动。它不会去主动干扰网络核心的启动过程。5.2 构建与烧录包含双核的完整HEX文件nRF Connect SDK提供了一个强大的工具west可以方便地构建多核映像。构建一个多核示例我们以samples/hello_world为例但它默认只构建应用核心。为了演示我们可以构建一个已知的多核应用比如某些蓝牙示例它们会同时生成两个核心的映像。使用west build在示例目录下使用west命令构建它会自动处理依赖和映像合并。west build -b nrf5340dk_nrf5340_cpuapp samples/hello_world即使这个示例很简单构建系统也会为cpuapp生成固件。对于真正的多核应用如bluetooth目录下的peripheral_hr构建系统会同时编译网络核心的固件通常来自modules/.../bluetooth相关的库和启动代码。生成合并HEX在构建目录下west最终会生成一个merged.hex文件或zephyr.hex本身已经是合并的。这个HEX文件包含了分配给两个核心的代码和数据地址互不冲突。烧录合并HEX使用nrfjprog烧录这个合并的HEX文件它会一次性将两个核心的固件写入各自对应的Flash区域。nrfjprog -f nrf53 --program build/zephyr.hex --sectorerase --reset5.3 当应用核心“空”时网络核心在做什么这是一个很好的问题。如果你烧录了只有应用核心是“空”固件而网络核心是一个实际协议栈的合并映像那么网络核心会正常启动并运行其协议栈例如蓝牙开始广播。网络核心会启动应用核心。应用核心启动后打印信息并进入空闲循环。此时系统是正常工作的。网络核心完全不受应用核心“空闲”状态的影响它可以正常处理射频、协议事件等。应用核心只是作为一个“安静”的伙伴核心存在消耗极低的功耗。这种状态在以下场景非常有用功耗测试单独测量网络核心运行协议栈时的功耗基线。网络核心功能调试排除应用核心业务逻辑带来的干扰专注调试蓝牙连接、数据吞吐量等。分阶段开发先集中精力开发和稳定网络核心的功能应用核心的代码后续再集成。6. 常见问题排查与实操心得在实践过程中你可能会遇到一些问题。这里分享一些排查思路和我踩过的坑。6.1 烧录失败“Cannot connect to J-Link”现象执行nrfjprog命令时提示连接失败。排查检查USB连接确认USB线连接的是DK板的J2口并且连接牢固。尝试更换USB线或电脑端口。检查驱动在Windows设备管理器中查看“通用串行总线控制器”下是否有“J-Link driver”相关的设备且没有黄色叹号。可以尝试重新安装J-Link驱动通常随nRF Connect SDK或SEGGER软件包安装。板子状态确保DK板已供电USB供电或外部供电。尝试按一下板子上的RESET按钮。其他软件占用关闭可能占用J-Link的IDE如Segger Embedded Studio, VSCode with nRF Connect扩展或其他调试软件。6.2 串口无输出现象固件烧录成功但串口终端没有任何打印信息。排查确认串口端口和波特率这是最常见的原因。仔细检查设备管理器中的COM口号并在终端软件中正确选择。波特率固定为115200。检查代码配置确认prj.conf中CONFIG_PRINTK和CONFIG_STDOUT_CONSOLE设置为y。检查硬件流控制在串口终端软件中确保硬件流控制RTS/CTS被禁用。nRF5340 DK的VCOM默认不使用硬件流控如果终端软件启用了它可能会阻塞发送。换用不同的终端软件有时某个终端软件会有兼容性问题可以换一个试试如从PuTTY换到Tera Term或使用screen /dev/ttyACM0 115200。6.3 应用核心似乎没有启动现象只看到网络核心的启动日志如果有但看不到应用核心的打印信息。排查确认烧录的HEX文件确保你烧录的是包含应用核心代码的HEX文件。如果只烧录了网络核心的固件应用核心区域是空的自然不会启动。检查构建目标回顾构建命令是否使用了_cpuapp后缀如果错误地构建了_cpunet目标生成的是网络核心固件。查看网络核心日志一些网络核心的启动代码会在释放应用核心复位时打印调试信息。你可以尝试在构建网络核心固件时启用更详细的日志CONFIG_LOGyCONFIG_LOG_DEFAULT_LEVEL4看看是否有“Starting application core”之类的消息。6.4 个人实操心得版本一致性是生命线nRF Connect SDK、工具链、甚至Python包的版本必须严格匹配。使用Toolchain Manager是避免此问题的最佳实践。手动升级或混用版本是灾难的源头尤其是涉及双核通信IPC时。“空”固件不意味着“零配置”即使是最简单的空循环也需要一个正确的prj.conf。忘记CONFIG_CPU_IDLEy可能会导致功耗达不到预期。建议从SDK中一个已知能运行的最小示例如hello_world的配置开始修改。善用west命令对于nRF Connect SDK项目west比直接调用cmake和ninja更友好。例如west build -b nrf5340dk_nrf5340_cpuapp可以一键完成配置和编译。west flash命令可以自动调用nrfjprog完成擦除、烧录和复位更加便捷。调试双核的思维当系统行为异常时要养成“分核排查”的习惯。先尝试让其中一个核心通常是应用核心运行像本文这样的“空”或极简固件看问题是否消失从而定位问题是出在某个核心的代码上还是双核交互上。这个“空”固件就是你工具箱里一个重要的隔离测试工具。

相关资讯