2. Zephyr 网络配置基础:Kconfig、设备树与网络接口初始化

好,咱们正式开始动手之前,得先把地基打牢。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 文件被正确包含了。

核心原则: 设备树只描述硬件,不描述软件策略。别把 IP 地址写在 DTS 里,那是运行时的事。

2.3 网络接口(net_if)初始化

Kconfig 配好了,设备树写对了,接下来就是运行时初始化。Zephyr 的网络接口层 net_if 负责管理所有网卡实例。

2.3.1 初始化流程

系统启动时,net_if 的初始化是自动的。但你要知道它背后干了什么:

  1. 设备绑定: 根据设备树,找到对应的网络设备驱动。
  2. 接口创建: 调用 net_if_register() 注册一个网络接口实例。
  3. 链路层配置: 设置 MAC 地址、MTU 等参数。
  4. 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 知识结构总览

为了让你更直观地理解这三者的关系,我画了一张图。你可以把它当作本章的“地图”:

Zephyr 网络配置核心三要素 Kconfig 编译时功能裁剪 Device Tree 硬件管脚描述 net_if 运行时接口管理 三者协作关系: 1. Kconfig 决定是否编译网络协议栈和驱动 2. Device Tree 告诉驱动硬件外设的管脚和总线信息 3. net_if 在运行时创建接口、分配 IP、管理链路状态 prj.conf .dts / .dtsi 应用代码

2.5 实战:快速验证你的网络配置

理论说完了,咱们来点实际的。我建议你按这个步骤快速验证:

  1. 选一个开发板: 比如 qemu_cortex_m3 或 nucleo_f767zi,它们自带以太网支持。
  2. 配 Kconfig: 在 prj.conf 里打开 CONFIG_NETWORKING 和 CONFIG_NET_SOCKETS。
  3. 检查设备树: 确认 boards/arm/你的板子/你的板子.dts 里有以太网节点,且 status = "okay"。
  4. 编译运行: 用 west build -b 你的板子 samples/net/sockets/echo_server 试试。
  5. 看日志: 如果串口打印 net_if: IPv4 address: 192.168.1.100,恭喜你,配置成功了!
常见问题: 如果你发现 net_if_get_default() 返回 NULL,八成是设备树里网络节点的 status 没设成 "okay",或者 Kconfig 里没选对应的驱动。回去检查一下。

好了,这一章的内容就这些。Kconfig、设备树、net_if 这三板斧,是后续 MQTT 和 HTTP 开发的基础。你只要把这三者的关系理清楚,后面写应用代码就会顺畅很多。