2. Zephyr与网络协议栈基础
好,咱们正式开始动手之前,得先把地基打牢。Zephyr 的网络协议栈虽然强大,但配置起来确实有点绕。我刚开始接触的时候,也被 Kconfig 和 Device Tree 这两兄弟搞得晕头转向。
说白了,你要让网口跑起来,得搞定三件事:编译时选什么功能(Kconfig)、硬件管脚怎么接(Device Tree)、运行时怎么启动(net_if)。咱们一个一个来拆。
2.1 Kconfig 网络选项详解
Kconfig 是 Zephyr 的“编译开关总闸”。你想想看,一个 RTOS 要支持从 WiFi 到 LoRa 几十种通信方式,总不能把所有代码都编译进去吧?Kconfig 就是让你按需裁剪的。
2.1.1 核心网络协议栈开关
我个人习惯,先把最基础的 IP 协议栈打开。下面这几个选项,几乎是所有网络应用的基础:
| Kconfig 选项 | 说明 | 我的建议 |
|---|---|---|
CONFIG_NETWORKING |
总开关,必须开 | y |
CONFIG_NET_IPV4 |
IPv4 支持 | y(大部分场景) |
CONFIG_NET_IPV6 |
IPv6 支持 | 按需,物联网建议开 |
CONFIG_NET_TCP |
TCP 协议 | MQTT/HTTP 必须 |
CONFIG_NET_UDP |
UDP 协议 | CoAP/DNS 需要 |
CONFIG_NET_SOCKETS |
Socket API 支持 | 必须开 |
prj.conf 里先写 CONFIG_NETWORKING=y,然后 make menuconfig 进去看看依赖关系。Zephyr 的 Kconfig 有自动选择依赖的功能,省心不少。
2.1.2 缓冲区与内存配置
嗯,这里要注意。网络协议栈吃内存,尤其是 MQTT 要发大包的时候。我曾经在一个 STM32F4 项目上,因为没配大缓冲区,MQTT 连接一建立就崩。
# 数据包缓冲区大小,默认 256 字节,MQTT 建议 1024+
CONFIG_NET_BUF_DATA_SIZE=2048
# 接收/发送描述符数量,默认 4,并发高时建议 8-16
CONFIG_NET_PKT_RX_COUNT=16
CONFIG_NET_PKT_TX_COUNT=16
# TCP 窗口大小,影响吞吐量
CONFIG_NET_TCP_RCV_WND_SIZE=4096
CONFIG_NET_BUF_DATA_SIZE 设成 4096,结果 RAM 不够,编译直接报错。建议先看芯片的 SRAM 大小,再算总账。Zephyr 的 gen_isr_tables 也会吃内存,别全给网络栈了。
2.2 设备树(Device Tree)网络节点配置
设备树,说白了就是告诉 Zephyr:“你的以太网控制器接在哪个 SPI 上?中断脚是 PA0 还是 PB1?” 它跟 Kconfig 的分工很明确:Kconfig 管“要不要”,设备树管“怎么接”。
2.2.1 常见网络外设的 DTS 节点
我拿一个典型的以太网控制器 W5500 举例。它走 SPI 接口,在设备树里长这样:
/ {
spi1: &spi1 {
status = "okay";
cs-gpios = &gpioa 4 GPIO_ACTIVE_LOW;
w5500: w5500@0 {
compatible = "wiznet,w5500";
reg = <0>;
spi-max-frequency = <30000000>;
int-gpios = &gpiob 1 GPIO_ACTIVE_LOW;
reset-gpios = &gpiob 0 GPIO_ACTIVE_LOW;
};
};
};
你看,compatible 是驱动匹配的关键字,reg 是 SPI 片选号,int-gpios 是中断脚。这些信息,Zephyr 的驱动框架会自动解析。
2.2.2 如何确认设备树是否生效
我有个习惯:编译完后,去 build/zephyr/zephyr.dts 看一眼。这个文件是最终生成的设备树,所有宏定义都在里面。比如搜索 w5500,如果节点还在,说明你的 DTS 文件被正确包含了。
2.3 网络接口(net_if)初始化
Kconfig 配好了,设备树写对了,接下来就是运行时初始化。Zephyr 的网络接口层 net_if 负责管理所有网卡实例。
2.3.1 初始化流程
系统启动时,net_if 的初始化是自动的。但你要知道它背后干了什么:
- 设备绑定: 根据设备树,找到对应的网络设备驱动。
- 接口创建: 调用
net_if_register()注册一个网络接口实例。 - 链路层配置: 设置 MAC 地址、MTU 等参数。
- IP 地址分配: 静态 IP 或 DHCP 获取。
2.3.2 手动配置 IP 地址
虽然 DHCP 方便,但我做调试时更喜欢静态 IP。代码里这样写:
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_core.h>
void my_net_init(void)
{
struct net_if *iface;
struct in_addr addr;
struct in_addr netmask;
struct in_addr gw;
// 获取默认网络接口(通常是第一个以太网口)
iface = net_if_get_default();
// 配置 IPv4 地址
if (net_addr_pton(AF_INET, "192.168.1.100", &addr)) {
printk("Invalid address\n");
return;
}
net_addr_pton(AF_INET, "255.255.255.0", &netmask);
net_addr_pton(AF_INET, "192.168.1.1", &gw);
net_if_ipv4_setup(iface, &addr, &netmask, &gw);
}
prj.conf 里加 CONFIG_NET_LOG=y,然后看串口日志。Zephyr 的网络栈会打印 net_if 的初始化状态,比如 MAC 地址、IP 分配结果。一眼就能看出问题在哪。
2.4 知识结构总览
为了让你更直观地理解这三者的关系,我画了一张图。你可以把它当作本章的“地图”:
2.5 实战:快速验证你的网络配置
理论说完了,咱们来点实际的。我建议你按这个步骤快速验证:
- 选一个开发板: 比如
qemu_cortex_m3或nucleo_f767zi,它们自带以太网支持。 - 配 Kconfig: 在
prj.conf里打开CONFIG_NETWORKING和CONFIG_NET_SOCKETS。 - 检查设备树: 确认
boards/arm/你的板子/你的板子.dts里有以太网节点,且status = "okay"。 - 编译运行: 用
west build -b 你的板子 samples/net/sockets/echo_server试试。 - 看日志: 如果串口打印
net_if: IPv4 address: 192.168.1.100,恭喜你,配置成功了!
net_if_get_default() 返回 NULL,八成是设备树里网络节点的 status 没设成 "okay",或者 Kconfig 里没选对应的驱动。回去检查一下。
好了,这一章的内容就这些。Kconfig、设备树、net_if 这三板斧,是后续 MQTT 和 HTTP 开发的基础。你只要把这三者的关系理清楚,后面写应用代码就会顺畅很多。