做低功耗Wi-Fi传感器节点的时候,我想过好几条路线:ESP32方案上手快,但原厂SDK风格太重;单独用Linux加USB Wi-Fi网卡,硬件体积和功耗又不合适;手里正好有一块NUCLEO-U5A5ZJ-Q,Cortex-M33核跑起来绰绰有余,于是开始研究能不能挂一颗Nordic的nRF7002,直接在vanilla Zephyr主线里把Wi-Fi接口打通。折腾了几天,结果是可行的。这篇文章就是把整个NUCLEO-U5A5ZJ-Q与nRF7002的接口过程、设备树写法、Kconfig配置和实际踩坑完整记录下来,给那些同样想用Zephyr原生驱动把“非官方组合”跑起来的人做个参照。
1. 为什么把ST的板子和Nordic的Wi-Fi芯片凑到一起
1.1 项目最初的场景诉求
我手头这个项目要做的是一个电池供电的环境监测节点,数据量不大,但要求支持Wi-Fi连接、远程配置和OTA升级。MCU部分选定STM32U5A5ZJ-Q,原因很直接:160MHz的Cortex-M33,带TrustZone,有充足的安全存储和TrustZone配套的外设隔离能力,而且这颗料在ST的生态里属于新一代低功耗主力,合适的睡眠模式下电流非常低。板子就选了ST官方的NUCLEO-U5A5ZJ-Q,把它当成验证平台用。
Wi-Fi部分比较头疼。最常见的做法是选ESP32或者ESP32-C3这类SoC直接跑Wi-Fi协议栈,但这样等于系统里多了一个主控,要么双MCU通信,要么把业务逻辑全部搬到ESP32上。后者意味着应用层被绑定在乐鑫的SDK里,考虑到团队对代码的可移植性和长期维护性要求,我更倾向于保留STM32作为唯一主控,Wi-Fi只用一颗配套芯片,通过标准接口挂在STM32的SPI/QSPI总线上。这样协议栈跑在Zephyr的网络栈里,应用层和硬件解耦得干净。
1.2 nRF7002这颗companion IC和其他Wi-Fi模组的差异
nRF7002不是一颗SoC,它是一颗Wi-Fi 6 companion IC,中文叫法常见是“配套芯片”或“协处理器”。它的定位和ESP32完全不同,nRF7002本身没有应用处理器,所有Wi-Fi MAC层的控制、协议栈、网络栈逻辑都跑在主机MCU上,nRF7002只负责物理层、MAC层里对实时性要求极高的部分以及射频前端相关功能。主机MCU通过SPI或者QSPI接口向nRF7002下发命令和数据,nRF7002把802.11帧处理好之后,通过同样的总线把收到的包交给主机。
这种架构最大的好处是主机MCU可以完全掌控Wi-Fi协议栈。Zephyr里面对nRF7002的驱动实际上就是一个基于Zephyr net stack的上层驱动,配合底层的SPI/QSPI传输,把nRF7002抽象成一个普通的IEEE 802.11网络接口。应用代码只需要用Zephyr标准的socket API、net_mgmt接口,或者直接使用wifi_shell之类的工具连接网络,不需要关心nRF7002内部寄存器怎么操作。
另一个优势是功耗。nRF7002在休眠状态下的功耗非常低,支持WPA3、Wi-Fi 6的多个特性,对电池供电设备来说很有吸引力。相比之下,很多Wi-Fi模组跑起来之后空闲功耗就摆在那边,对低功耗场景不太友好。
1.3 “vanilla Zephyr”对于这个组合意味着什么
标题里“vanilla Zephyr”这个词,翻译过来就是“原味Zephyr”,意思是完全使用Zephyr上游主线代码,不打私有补丁、不用Nordic私有SDK、不走厂商闭源BSP,直接依赖Zephyr官方仓库里已经合入的nRF7002驱动和设备树支持。
为什么会强调这一点?因为如果走Nordic自家nRF Connect SDK (nRF Connect SDK,简称NCS),nRF7002的开箱体验要顺畅很多,Nordic在NCS里把驱动、固件、示例、shield定义都配好了。但NCS是基于Zephyr的一个长期维护分支,里面加入了不少Nordic自己维护的模块,跨厂商芯片组合的灵活性并没有那么高。vanilla Zephyr则更接近“社区通用主线”,任何厂商的板子,只要驱动合入主线,理论上都能用相同的机制跑起来。这次把ST的板子和Nordic的Wi-Fi芯片在vanilla Zephyr里接起来,正是要验证这种跨厂商组合在主线生态下的可行性。
实际做下来,主线Zephyr对nRF7002的支持确实已经可以用了。驱动入口在drivers/wifi/nrf700x,设备树compatible是nordic,nrf7002,只需要在板级overlay里把节点挂到合适的SPI总线上,再把供电、中断、固件加载相关GPIO配好,就能被驱动识别。整条链路不需要改任何Zephyr源码。
2. 硬件搭线:NUCLEO-U5A5ZJ-Q与nRF7002的连接方式
2.1 nRF7002的接口协议与引脚功能
nRF7002对外主要提供SPI和QSPI两种总线接口,具体用哪种由外部引脚配置决定。实际调试我建议先从SPI开始,因为SPI的引脚数量少、接线简单、调试门槛低,等SPI链路完全跑通之后,再考虑切换到QSPI提升吞吐量。
从nRF7002模组/芯片引出的关键信号大致如下:
| 信号名 | 方向 | 功能说明 |
|---|---|---|
| SPI_CLK / QSPI_CLK | 输入 | 时钟信号 |
| SPI_MOSI / QSPI_IO0 | 输入 | 主机到nRF7002的数据线 |
| SPI_MISO / QSPI_IO1 | 输出 | nRF7002到主机的数据线 |
| SPI_CS / QSPI_CS | 输入 | 片选信号,低有效 |
| IRQ | 输出 | 中断请求,通知主机有事件需要处理 |
| RESET | 输入 | 复位信号 |
| VIO | 电源 | IO电平参考电源,决定SPI接口电平 |
| VDD | 电源 | 芯片主电源 |
| ANT_CTRL1 / ANT_CTRL2 | 输出 | 外部天线开关控制 |
| FEM_CTRL | 输出 | 外部前端模块控制 |
如果用的是成品nRF7002模块,可能还会引出BUCK、VIO_REGulator等引脚,用于控制内部DC-DC和IO电压。这些引脚在Zephyr设备树里会有对应的GPIO属性,需要正确接好,否则驱动初始化时电压或供电状态不对,芯片根本起不来。
2.2 在NUCLEO板上挑选合适的SPI外设与GPIO
NUCLEO-U5A5ZJ-Q的Arduino排针上自带一组SPI接口,对应关系如下:
- D13 -> PA5,SPI1_SCK
- D12 -> PA6,SPI1_MISO
- D11 -> PA7,SPI1_MOSI
- D10 -> PA4,可用作SPI1_CS
这组引脚直接连到STM32U5的SPI1外设,能少走很多线,非常适合飞线调试。我最后选择的信号映射是:
| nRF7002信号 | NUCLEO-U5A5ZJ-Q引脚 | 备注 |
|---|---|---|
| SPI_CLK | PA5 | SPI1_SCK |
| SPI_MOSI | PA7 | SPI1_MOSI |
| SPI_MISO | PA6 | SPI1_MISO |
| SPI_CS | PA4 | SPI1_CS |
| IRQ | PA1 | 中断输入,低延时可触发 |
| RESET | PA0 | 复位控制 |
| VIO_REGulator | PA2 | 用于控制nRF7002的VIO电源 |
| BUCK_REGulator | PA3 | 用于控制nRF7002的DC-DC使能 |
| ANT_CTRL | PC0 | 天线开关,可选 |
| FEM_CTRL | PC1 | 外部前端控制,可选 |
这块板子的Arduino排针上还有5V和3.3V电源引脚,nRF7002模块的VDD可以直接接到3.3V排针。接线时尽量把信号线剪短,面包板上的长跳线在SPI时钟频率稍高时容易引入干扰,我最后全部换成了杜邦线直接对插,接触更可靠。
2.3 供电、电平转换和天线部分要注意的事
nRF7002的IO电平由VIO引脚决定,并不是固定3.3V。不同厂家出的nRF7002模组,VIO默认电压可能不同,有的模组厂把VIO固定到1.8V,有的固定到3.3V,有的留了跳线可以切换。接STM32的SPI引脚之前,一定要确认两边电平一致。如果nRF7002模组的VIO是1.8V,而STM32的IO配置成3.3V输出,长期使用有损坏风险,最好加一级电平转换芯片。我手上的模组VIO可以调到3.3V,所以直接连NUCLEO板的3.3V没问题。
天线部分同样不能大意。nRF7002是Wi-Fi 6芯片,工作在2.4GHz频段,天线周围的布局和走线直接影响灵敏度。如果只是调试连通性,可以用模组自带的PCB天线;如果要测试真实吞吐,尽量把模组放在远离大块铜皮和金属外壳的位置。使用外置天线时,ANT_CTRL引脚要接对,否则天线开关选错通路,信号会非常弱。
另外,nRF7002的RESET不是简单的上电复位。Zephyr驱动在初始化阶段会通过GPIO控制RESET,如果RESET引脚被其他外设复用或者悬空,驱动可能会卡在“等待芯片响应”这一步。安全做法是把RESET接到MCU的一个普通GPIO,并在设备树里明确声明,让驱动自己管理复位时序。
3. Zephyr项目初始化和内核选项配置
3.1 获取Zephyr并切换到适配的版本
nRF7002驱动在主线上已经存在,但版本差异影响很大。太老的版本驱动不完整,太新的版本可能又改变了Kconfig选项。建议先固定到一个经过验证的版本,我使用的是Zephyr v3.7.0,这个版本里drivers/wifi/nrf700x已经能比较稳定地工作。
初始化工程时,标准流程是:
west init -m https://github.com/zephyrproject-rtos/zephyr v3.7.0 cd zephyr west update west zephyr-export python3 -m pip install -r scripts/requirements.txt这里有一个值得注意的地方:west update会拉取所有manifest里列出的模块,包括hal、cmsis、crypto等,时间比较长,网络不好时容易中断。如果中断了,重复执行west update就好,west会断点续拉。
3.2 创建工程:基于board模板
推荐的做法是先直接使用Zephyr官方提供的wifi_shell示例作为起点,在它上面叠加自己的overlay文件,验证硬件链路没问题之后,再把工程复制出来改造成自己的应用。
west build -b nucleo_u5a5zj_q zephyr/samples/net/wifi/wifi_shell不过为了让后面的工程结构更清晰,我最终是新建了一个独立应用目录:
my_nrf7002_app/ ├── CMakeLists.txt ├── prj.conf ├── boards/ │ └── nucleo_u5a5zj_q.overlay └── src/ └── main.cCMakeLists.txt内容很简单:
cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(my_nrf7002_app) target_sources(app PRIVATE src/main.c)main.c一开始甚至不用写任何业务逻辑,只留一个打印即可。Wi-Fi连接功能全部通过Zephyr shell完成,这样验证链路最简单。
3.3 开启nRF7002驱动与网络栈的关键Kconfig
prj.conf是决定系统行为的关键文件。下面是我反复调整后最终使用的核心配置:
CONFIG_NRF7002=y CONFIG_NRF7002_FW_LOADER=y CONFIG_NRF7002_SPI=y CONFIG_WIFI=y CONFIG_WIFI_SHELL=y CONFIG_NETWORKING=y CONFIG_NET_L2_WIFI_MGMT=y CONFIG_NET_IPV4=y CONFIG_NET_DHCPV4=y CONFIG_NET_SHELL=y CONFIG_SHELL=y CONFIG_SHELL_BACKEND_SERIAL=y CONFIG_SERIAL=y CONFIG_WPA_SUPP=y CONFIG_WPA_SUPP_CRYPTO=y几个配置的用途我重点说一下。
CONFIG_NRF7002_SPI=y决定使用SPI作为nRF7002的底层传输。如果后续改用QSPI,需要打开CONFIG_NRF7002_QSPI=y并关闭SPI。这里容易踩坑的是,新版Zephyr里NRF7002配置项的位置在drivers/wifi/nrf700x/Kconfig下,不同版本可能还会拆出一些子选项,如果编译时发现CONFIG_NRF7002_SPI未定义,就去west build -t menuconfig里搜一下确认。
CONFIG_NET_L2_WIFI_MGMT=y是Zephyr Wi-Fi连接管理的核心,wifi shell、wifi管理事件都依赖它。CONFIG_WPA_SUPP=y则是启用wpa_supplicant,Zephyr里Wi-Fi连接、密钥协商、WPA3这些能力都走这个组件。
CONFIG_NET_DHCPV4=y要注意,很多Zephyr示例默认只开静态IP,不开DHCP。如果不在prj.conf里打开,连接路由器后不会自动获取IP,后面ping外网会很不方便。
CONFIG_NRF7002_FW_LOADER=y负责在驱动初始化时把nRF7002固件下载到芯片内部。固件文件本身不在Zephyr源码里,需要从Nordic的固件仓库单独获取,下一节会和设备树一起说。
4. 设备树overlay:把nRF7002挂到SPI总线上
4.1 理解nordic,nrf7002 compatible节点
Zephyr的驱动和设备树是绑定关系,nRF7002驱动在设备树中对应的compatible是nordic,nrf7002。驱动在初始化时,会从设备树节点读取SPI总线的配置、中断GPIO、电源GPIO、天线GPIO等属性,然后初始化内部状态机,尝试向nRF7002下载固件。
这意味着设备树节点几乎决定了整个硬件抽象层能否正确工作。属性名长什么样,直接决定驱动能不能找到对应的GPIO和电源控制。以Zephyr主线驱动为准,常见属性包括:
irq-gpios:nRF7002的中断输出引脚vio-regulator:控制VIO电源的regulator phandlebuck-regulator:控制DC-DC转换器的regulator phandleant-gpios:天线开关控制引脚fem-gpios:外部前端模块控制引脚wifi-fw-load:固件加载控制引脚txn-gpios:传输事务控制引脚
如果你的模组没有某些引脚对应的功能,属性可以不写,驱动会跳过对应逻辑。但irq-gpios是必须的,没有中断,驱动无法感知nRF7002的初始化完成事件,会一直等下去。
4.2 一份最小可用的overlay文件
我的boards/nucleo_u5a5zj_q.overlay内容如下:
/ { aliases { wifi = &nrf7002_spi; }; vio_reg: vio-regulator { compatible = "regulator-fixed"; regulator-name = "VIO"; regulator-always-on; enable-gpios = <&gpioa 2 GPIO_ACTIVE_HIGH>; }; }; &spi1 { pinctrl-0 = <&spi1_sck_pa5 &spi1_miso_pa6 &spi1_mosi_pa7>; pinctrl-names = "default"; cs-gpios = <&gpioa 4 GPIO_ACTIVE_LOW>; status = "okay"; nrf7002_spi: nrf7002@0 { compatible = "nordic,nrf7002"; reg = <0>; spi-max-frequency = <8000000>; irq-gpios = <&gpioa 1 GPIO_ACTIVE_HIGH>; vio-regulator = <&vio_reg>; buck-regulator = <&vio_reg>; wifi-fw-load = <&gpioa 3 GPIO_ACTIVE_HIGH>; status = "okay"; }; };这段overlay做了几件事。
第一,在根节点定义了一个固定电压regulator,名为vio-regulator,实质是把PA2引脚拉高作为VIO电源使能。regulator-always-on表示这个regulator在上电后一直保持使能,省去驱动里额外的上电序列逻辑。如果你希望驱动完全接管供电顺序,可以把regulator-always-on去掉,但调试初期没必要给自己找麻烦。
第二,把SPI1外设的引脚复用配置为PA5/PA6/PA7,并指定PA4作为片选。这里的spi1_sck_pa5这种pinctrl名不是随便写的,它必须存在于该板子的pinctrl定义文件里。NUCLEO-U5A5ZJ-Q的pinctrl节点定义在boards/arm/nucleo_u5a5zj_q/目录下,编不过时去那里找可用的节点名就行。
第三,在SPI1总线下添加了nordic,nrf7002子节点。reg = <0>对应片选序号0,也就是SPI1的cs-gpios数组里的第一个片选。spi-max-frequency初始设为8MHz,对飞线调试来说比较保险。实际调试通过后,我试着把频率调到16MHz,短距离飞线下也能稳定工作,再高就开始出现偶发固件加载失败。
4.3 如果走QSPI而不是SPI,设备树差异在哪
QSPI的接线比SPI多两根数据线,吞吐量却能提升不少。nRF7002在QSPI模式下使用四线IO,理论上频率可以更高,实际吞吐会明显优于8MHz SPI。不过STM32U5A5ZJ-Q的QSPI外设在Zephyr里通常以ospi1的形式暴露,设备树挂载方式类似,但pinctrl和父总线节点名完全不同。
如果后续要切换QSPI,overlay大致长这样:
&ospi1 { pinctrl-0 = <&ospi1_clk_pb2 ...>; pinctrl-names = "default"; status = "okay"; nrf7002_qspi: nrf7002@0 { compatible = "nordic,nrf7002"; reg = <0>; spi-max-frequency = <32000000>; irq-gpios = <&gpioa 1 GPIO_ACTIVE_HIGH>; ... }; };这里我没有把完整pinctrl写出来,因为不同板子的ospi引脚定义差别很大,需要根据实际原理图去查。调试策略上,我强烈建议先把SPI模式跑通,确认nRF7002固件加载、Wi-Fi连接、网络通信都没问题了,再迁移QSPI。如果一开始就上QSPI,硬件问题、设备树问题、驱动问题混在一起,很难定位。
5. 编译、烧录与Wi-Fi连接验证
5.1 west build的完整命令与常见告警
在应用目录下执行:
west build -b nucleo_u5a5zj_q如果是从零开始,加上-p always强制pristine构建:
west build -p always -b nucleo_u5a5zj_q编译过程如果出现undefined reference或者设备树节点找不到,多半是overlay里的pinctrl名不对,或者某个设备树属性写错。Zephyr有一个很有用的调试手段:在编译命令后面加-t ram_report或-t rom_report可以查看内存占用,west build -t menuconfig可以可视化查看所有Kconfig选项的实际状态。
烧录直接使用板上ST-LINK:
west flash烧录完成后,打开串口终端,波特率115200:
screen /dev/ttyACM0 115200如果使用的是Linux系统,注意有的发行版会默认被brltty服务占用USB串口,导致设备无法打开,这个坑我在后面专门说。
5.2 开机日志里应该看到什么
上电后,main.c的打印会出现,之后Zephyr会初始化网络栈和Wi-Fi驱动,此时串口日志大致会包含以下关键信息:
[00:00:00.000] *** Booting Zephyr OS build v3.7.0 *** [00:00:00.140] [0B;33m[00:00:00.140] <inf> wifi_nrf7002: nRF7002 initialized[0m看到nRF7002 initialized就说明驱动已经成功初始化,固件也已经加载完成。如果卡在这里或者直接报failed to load firmware,优先检查spi-max-frequency是否太高,以及IRQ引脚的GPIO配置是否正确。
之后输入:
uart:~$ wifi status如果驱动正常,会返回类似Disconnected或者扫描到的网络列表。wifi scan可以扫描附近热点:
uart:~$ wifi scan扫描结果会列出AP的SSID、信道、信号强度、加密方式等信息。能够扫描到热点,说明nRF7002的射频链路和固件都在工作。
5.3 通过shell连接Wi-Fi的路由状态
连接Wi-Fi的基本命令是:
uart:~$ wifi connect MySSID MyPassword连接成功后,shell会打印连接状态和分配的IP地址。如果需要手动查看网络接口和路由信息:
uart:~$ net iface uart:~$ net route如果启用了DHCP,net iface里能看到IPv4地址自动获取成功。接着可以ping一下网关或者外网:
uart:~$ net ping 192.168.1.1在连上Wi-Fi的同一段网络里,也可以用手机上安装的ping工具,从外部ping一下nRF7002节点获得的IP,验证双向通信正常。
还有一点值得留意:Zephyr的网络栈默认对IPv6的支持程度因配置而异。如果路由器发布了IPv6前缀并且CONFIG_NET_IPV6=y已经开启,可以用net ping测试一下IPv6地址,这对后续做IPv6物联网方案有参考价值。
6. 真实调试中踩过的几个坑
6.1 Linux下USB串口被brltty抢走
插上NUCLEO板,打开/dev/ttyACM0,screen启动后黑屏,或者直接报Device or resource busy,这是一个非常经典的Linux环境问题。dmesg里能看到类似这样的信息:
usb 1-3: usbfs: interface 0 claimed by ch341 while 'brltty' sets config #1原因很直接:Ubuntu/Debian系发行版默认安装的brltty盲文终端服务会识别某些USB串口设备并绑定,导致普通用户无法打开。解决方法是停用并禁用这个服务:
sudo systemctl stop brltty sudo systemctl disable brltty然后再重新插拔USB线即可正常打开。这个问题不是ST板子独有的,很多带USB转串口的开发板都会遇到,遇到“打不开串口”先查这个。
6.2 SPI速率与固件加载失败
我最初把spi-max-frequency设在32MHz,结果日志里反复出现:
[00:00:00.100] <err> wifi_nrf7002: nRF7002 failed to load firmware [00:00:00.200] <err> wifi_nrf7002: initialization failed排查过程比较痛苦。先检查了供电,3.3V正常,VIO_regulator也正常;检查了IRQ引脚,示波器上看不到nRF7002的响应脉冲;最后怀疑到SPI速率上。把spi-max-frequency降到8MHz之后,一次通过。
飞线和面包板对高速SPI来说是灾难。nRF7002的SPI接口可以支持几十MHz,但那是在PCB走线合理、阻抗匹配的情况下。手工飞线时,信号完整性完全靠不住。我的建议是:
- 初始8MHz起步
- 每一根信号线尽量短,CS/CLK/MOSI/MISO四根线不要并行捆在一起
- 如果接地点离得远,SPI总线容易振铃,可以考虑在CLK上串联一个33欧姆电阻
QSPI模式对信号要求更高,飞线调32MHz基本不现实,这也是我一直建议先SPI后QSPI的原因。
6.3 提示“connected”但IP始终ping不通
连接Wi-Fi成功后,wifi status显示Connected,但net iface里IPv4地址是空的,或者地址显示为0.0.0.0。这个坑最常见的原因是CONFIG_NET_DHCPV4没有打开,或者路由表为空。
还有一次,地址获取成功,但局域网内其他设备ping不通。查了很久,最后发现是路由器开启了AP隔离,无线客户端之间不能互访。这个问题和Zephyr无关,但很隐蔽,容易让人误判驱动有问题。验证时可以先ping网关,网关通了说明上行链路正常,再ping同一局域网内的其他设备,如果网关通而设备不通,大概率是路由器的隔离策略。
另外,如果连接的是企业级WPA2-Enterprise网络,wifi connect命令需要额外指定EAP参数,不能直接套用家庭网络的连接方式。这一步在Zephyr文档里有详细的说明,我就不展开了。
6.4 偶发复位后连接不上
调试中遇到一个比较诡异的现象:正常运行一段时间后,nRF7002突然不响应了,重新打开shell执行wifi connect也没反应,只能断电重启。后来发现是nRF7002的固件加载流程和MCU的复位时序存在竞争关系。
nRF7002作为外设,它的复位和主机MCU的复位不是完全同步的。如果MCU重启太快,nRF7002还处于上一次会话的某个状态,驱动发送固件加载命令时,nRF7002没有正确响应,可能卡死。这种情况下,驱动需要能够对nRF7002进行一次完整的外部复位。
解决思路比较朴素:给nRF7002的RESET引脚接一个GPIO,在设备树里声明,并让驱动初始化时先拉低RESET再释放。Zephyr驱动对RESET引脚的支持版本差异比较大,如果当前版本不支持,可以在自己的应用层通过GPIO操作来完成复位。我最后是在应用启动时手动操作一次RESET GPIO,确保nRF7002处于干净状态,再让驱动初始化,问题得到缓解。
6.5 低功耗模式下Wi-Fi模块睡死
项目目标毕竟是电池供电,所以我在完成基础连通后,尝试让STM32U5进入低功耗模式。结果发现nRF7002在宿主MCU进入睡眠后也不会主动唤醒Wi-Fi连接。因为nRF7002的中断线只有在Wi-Fi事件发生时才会拉高,而睡眠模式下的MCU如果没有配置成GPIO唤醒源,根本无法响应。
正确的做法是在设备树和驱动中把IRQ GPIO配置成可唤醒源,并且让Zephyr的电源管理子系统感知到nRF7002处于活动状态。这部分功能依赖具体的Zephyr版本,真正做产品时建议单独评估功耗需求,而不是简单把MCU睡眠。现在Zephyr主线里nRF7002驱动的电源管理支持还在持续完善,测试时需要多打日志观察。
7. 性能测试与后续进阶方向
7.1 吞吐量测试方法
连接稳定后,可以测一下实际吞吐量。简单方法是在Zephyr里跑一个iperf服务端或客户端。Zephyr有个示例叫samples/net/sockets/iperf,把它集成进工程后,在shell里启动iperf服务器,然后用电脑上的iperf3客户端去连。
我实测下来,SPI 8MHz模式下,nRF7002的UDP吞吐大约在2Mbps左右,TCP吞吐略低。这个数值已经足够应对传感器数据上报、远程配置、OTA等场景。切换到32MHz QSPI后,理论上能跑出更高吞吐,但飞线条件下我没有测出明显改善,可能信号完整性限制了速率上限。如果你的应用有大量视频或音频传输需求,建议直接用PCB板级设计来解决SPI走线问题,不要指望飞线。
7.2 低功耗实测思路
低功耗验证我建议分两步走。
第一步,先测静态功耗。让nRF7002进入sleep模式,观察整个系统的待机电流。NUCLEO板自带的ST-LINK、LED灯、电平转换芯片都会消耗额外电流,得到的数值只能作为相对参考,不能代表最终产品功耗。
第二步,再做动态功耗评估。Wi-Fi通信本身是突发性的,连接状态下,在Beacon间隔内的睡眠窗口,nRF7002的功耗会显著低于传输状态。如果要真正优化,需要统计一个完整上报周期内的平均电流,而不是只看峰值或只盯着某个瞬态。
7.3 后续方向:WPA3、多节点组网与自定义应用
跑通wifi_shell只是第一步,实际产品里不可能每台设备都靠人工输入命令连接网络。Zephyr提供net_mgmt事件机制,应用层可以监听NET_EVENT_WIFI_CONNECT_RESULT、NET_EVENT_WIFI_DISCONNECT_RESULT等事件,在代码里完成自动重连、失败告警、状态上报等功能。这也是从“板子能连Wi-Fi”走向“节点能用Wi-Fi”的关键一步。
安全方面,nRF7002支持WPA3,Zephyr里通过CONFIG_WPA_SUPP_CRYPTO=y和相关配置启用。如果搭建的测试路由器支持WPA3,建议实际验证一下连接流程,确保后续产品不会因为路由器安全策略升级而失效。
再往后,还可以考虑多节点组网。Zephyr对Wi-Fi Mesh的支持力度在增加,如果设备数量多,又没有独立网关,Mesh方案会比每个节点直连路由器更方便管理。不过nRF7002驱动对Mesh的支持目前还在发展中,做之前需要仔细确认所用Zephyr版本的能力边界。
这次把NUCLEO-U5A5ZJ-Q和nRF7002在vanilla Zephyr里接起来的整个过程,最深的体会是:厂商SDK不是必需品,主线Zephyr的跨厂商硬件抽象能力已经足以支撑“ST主控加Nordic射频”这种组合。但也不要指望它百分百零成本——设备树属性、Kconfig选项、固件加载路径这些环节仍然需要自己摸索和验证。如果你也准备做类似的跨厂商平台,建议先固定一个已验证的Zephyr版本,按照“SPI起步,8MHz起步,先跑通再优化”的节奏往前走。最后再分享一个实用小技巧:在overlay里给所有required regulator加上regulator-always-on,排查问题时可以少花很多时间在电源时序上。