embassy-net-wiznet 0.3.0 演进解读:WIZnet SPI 以太网驱动的多芯片支持与中断寄存器重构
2026/9/24 15:50:03 网站建设 项目流程
  • 嵌入式
  • 物联网
  • 异步编程

【免费下载链接】embassy

Modern embedded framework, using Rust and async.

项目地址:https://gitcode.com/gh_mirrors/em/embassy
点击查看免费下载

本文围绕embassy-net-wiznet驱动 crate 的版本演进记录(即 embassy-net-wiznet/CHANGELOG.md),深入剖析其在 W5100S / W5500 / W6100 / W6300 四款 WIZnet SPI 以太网芯片上的实现细节,重点讲解 0.3.0 版本引入的SOCKET_INTR_CLR中断清除寄存器设计、W6100/W6300 的 MAC 过滤关闭策略,以及该驱动如何在 embassy-net 生态中完成 MACRAW 模式的数据收发。读完本文,你将掌握该驱动的版本演进脉络、各芯片寄存器差异、底层收发原理,并能依据源码快速完成驱动集成与芯片选型。

版本记录概览:从首发到多芯片支持

embassy-net-wiznet是 embassy-net 生态中面向 WIZnet 系列 SPI 以太网控制器的驱动,工作在MACRAW 模式下,即由芯片完成物理层与链路层的帧收发,而上层 TCP/IP 协议栈完全交给embassy-net处理。其 CHANGELOG 记录了如下版本历程:

版本日期核心内容
0.1.12025-08-14首个附带 changelog 的发布版本
0.2.12025-08-26维护版本(无条目记录)
0.3.02026-03-10新增实验性 W6100 驱动(禁用 MAC 过滤)、新增 W6300 驱动、引入SOCKET_INTR_CLR寄存器、升级 embassy-net-driver-channel 至 0.4.0
Unreleased待发布条目

从当前仓库的 Cargo.toml 可以看到,crate 版本号与 CHANGELOG 保持同步(version = "0.3.0"),且遵循语义化版本控制:0.3.0 是一次功能性演进,embassy-net-driver-channel随之从旧版升至0.4.0,这意味着依赖本驱动的下游项目在升级时需要同步调整embassy-net相关依赖版本。

0.3.0 的核心变更一:新增 W6100 与 W6300 芯片驱动

0.3.0 最大的变化是芯片支持面从两款扩展到四款。从 chip/mod.rs 的模块声明可以看出,驱动通过统一的Chiptrait 抽象屏蔽了不同芯片的寄存器布局差异:

mod w5500; pub use w5500::W5500; mod w5100s; pub use w5100s::W5100S; mod w6100; pub use w6100::W6100; mod w6300; use embedded_hal_async::spi::SpiDevice; pub use w6300::W6300;

Chiptrait 由公开的Chip与密封的SealedChip两层构成,SealedChip定义了驱动运行所需的全部关键常量与方法:

  • CHIP_VERSION:芯片版本号,用于初始化时校验芯片型号以及 SPI 通信是否正常;
  • COMMON_*SOCKET_*系列寄存器地址:覆盖模式、MAC 地址、中断、缓冲大小、收发指针、命令等寄存器;
  • BUF_SIZE:socket 收发缓冲区大小;
  • AUTO_WRAP:读写数据时硬件是否自动回卷(auto wrap);
  • rx_addr/tx_addr:将逻辑地址映射为 RX/TX 缓冲区的物理地址;
  • bus_read/bus_write:不同芯片各自实现的 SPI 总线帧格式。

四款芯片的参数对比

将 w5100s.rs、w5500.rs、w6100.rs、w6300.rs 中的常量汇总对比如下:

参数W5100SW5500W6100W6300
CHIP_VERSION0x510x040x460x11
Address类型u16(线性地址)(RegisterBlock, u16)(RegisterBlock, u16)(RegisterBlock, u16)
BUF_SIZE0x2000(8 KB)0x4000(16 KB)0x1000(4 KB)0x1000(4 KB)
AUTO_WRAPfalsetruetruetrue
SOCKET_MODE_VALUE(1<<2) \| (1<<6)(1<<2) \| (1<<7)0b0000_01110b0000_0111
SOCKET_INTR地址0x402(Socket0, 0x02)(Socket0, 0x0020)(Socket0, 0x0020)
SOCKET_INTR_CLR地址0x402(同SOCKET_INTR(Socket0, 0x02)(同SOCKET_INTR(Socket0, 0x0028)(独立寄存器)(Socket0, 0x0028)(独立寄存器)

其中CHIP_VERSION的差异(W5100S 为0x51、W5500 为0x04、W6100 为0x46、W6300 为0x11)正是 device.rs 中初始化校验的依据——读取COMMON_VERSION寄存器后与期望值比对,不匹配即返回InitError::InvalidChipVersion { expected, actual }。这既防止了选错芯片型号,也相当于一次 SPI 通路自检。

不同芯片的 SPI 帧格式

四款芯片的 SPI 访问协议各不相同,源码中通过各自bus_read/bus_write实现:

  • W5500(w5500.rs):3 字节控制帧(2 字节地址 + 1 字节块/读写控制位)后跟数据阶段;
  • W6100(w6100.rs):地址阶段拆分为 2 字节地址 + 1 字节控制段,即 4 阶段事务;
  • W6300(w6300.rs):在 W6100 基础上额外增加一个指令字节(写入时置位 bit5 表示写访问)与一个 dummy 阶段;
  • W5100S(w5100s.rs):使用 3 字节帧(高字节固定0x0F/0xF0区分读写 + 2 字节地址),地址空间为线性u16

这也是 CHANGELOG 中特别注明W6300 仅支持 Single SPI的原因——其 SPI 时序与 W5500/W6100 并不兼容,需要单独适配。

0.3.0 的核心变更二:SOCKET_INTR_CLR 寄存器的引入

0.3.0 变更记录中提到:

IntroducedSOCKET_INTR_CLRregister which is needed on W6100 and later models (on W5100/W5500 this is shared withSOCKET_INTRand the address is the same)

这是一个典型的跨代芯片寄存器语义差异处理:

  • 在 W5100S 与 W5500 上,写中断状态寄存器SOCKET_INTR0x402(Socket0, 0x02))即可同时清除中断标志,因此读与清除共用同一地址;
  • 在 W6100 与 W6300 上,芯片将中断状态寄存器(Sn_IR,地址(Socket0, 0x0020))与中断清除寄存器(Sn_IRCLR,地址(Socket0, 0x0028)分离为两个独立寄存器,必须写入Sn_IRCLR才能正确清除接收中断。

因此SealedChiptrait 中新增了独立的SOCKET_INTR_CLR常量(见 chip/mod.rs),并由 device.rs 中的reset_interrupt()统一调用:

async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> { let data = [code as u8]; self.bus_write(C::SOCKET_INTR_CLR, &data).await }

Interrupt::Receive定义为0b00100_u8。接收中断的清除在read_frame()中、实际读取数据之前完成,从而避免中断引脚持续拉低导致int.wait_for_low()永远等待。

0.3.0 的核心变更三:W6100/W6300 的 MAC 过滤为何被禁用

0.3.0 明确记载 W6100 驱动为experimental,且“禁用 MAC 过滤”(does not currently work with it enabled)。这一决定的背景可以从 w6100.rs 的源码注释中读到:

Note: Bit 7 is MAC filter. On the W5500 this is normally turned ON however the W6100 will not successfully retrieve an IP address with this enabled. Disabling for now and will have live with the extra noise.

W5500 的SOCKET_MODE_VALUE(1<<2) | (1<<7),bit7(MAC 过滤)默认开启;而 W6100 与 W6300 均使用0b0000_0111,即关闭了 bit7 的 MAC 过滤。原因是实测中发现 W6100/W6300 在开启 MAC 过滤后无法成功获取 IP 地址(这会让 DHCP/静态 IP 下的 ARP 与广播流量无法正常接收),因此暂时关闭,代价是驱动会收到不属于本机的多余帧(noise),需要上层协议栈自行过滤。

同理,w6300.rs 沿用了相同的SOCKET_MODE_VALUE = 0b0000_0111设定。这一取舍对集成者的实际影响是:在 W6100/W6300 上,网卡会收到更多无关广播帧,占用部分 RX 队列与中断开销,但对功能正确性无影响

底层收发流程:MACRAW 模式如何工作

驱动在 MACRAW 模式下,将整个 socket 视作一个原始以太网通道。初始化时(device.rs)依次完成:

  1. 通过COMMON_MODE寄存器复位芯片(写入0x80);
  2. 读取COMMON_VERSION校验CHIP_VERSION
  3. 启用中断引脚(写COMMON_SOCKET_INTR0x01),并通过SOCKET_INTR_MASK使能接收中断;
  4. 写入 MAC 地址(COMMON_MAC);
  5. 依据BUF_SIZE设置 socket 的 TX/RX 缓冲区大小寄存器;
  6. 写入SOCKET_MODE_VALUE进入 MACRAW 模式;
  7. 下发Command::Open0x01)打开 socket。

收帧路径(read_frame,device.rs)的关键点:

  • 读取SOCKET_RECVD_SIZE得到待收字节数,连续读两次取相等结果,以规避 SPI 读取过程中寄存器被硬件更新的竞态;
  • 帧头前两字节为帧长度(含这两字节自身),解析出真实载荷长度,同时做了防御处理:若长度 < 2 视为损坏帧头直接丢弃并推进读指针;若帧长超过传入缓冲区则截断并跳过剩余字节,防止越界;
  • AUTO_WRAP决定缓冲区回卷策略:W5500/W6100/W6300 由硬件自动回卷,直接按逻辑地址读写;W5100S 需在软件侧手动拆分为两段读写(见 device.rs 的read_bytes与 device.rs 的write_frame);
  • 收发完成后写入SOCKET_COMMAND寄存器下发Command::Send0x20)或Command::Receive0x40)。

发送路径(write_frame)会先忙等SOCKET_TX_FREE_SIZE大于等于待发帧长,避免覆盖未发送完的缓冲区。

后台驱动循环与 embassy-net 集成

驱动对外暴露的 API 非常精简(lib.rs):

  • State<N_RX, N_TX>:驱动状态,泛型参数为接收/发送包队列深度(以包为单位,而非字节);
  • Device:交给embassy-net协议栈使用的驱动句柄;
  • Runner::run():必须在后台任务中运行的事件循环,否则驱动不工作。

Runner::run()使用select3并发等待三路事件:中断引脚拉低(有新帧待收)、上层有包待发(tx_chan.tx())、以及 500ms 周期的链路状态轮询(Ticker::every(Duration::from_millis(500)))。收到帧后通过rx_chan.rx(p)送入embassy-net协议栈;is_link_up()则读取COMMON_PHY_CFG寄存器最低位判断物理链路状态,并同步给embassy-netLinkState::Up/Down)。

new()函数负责硬件复位(拉低RST引脚 1ms 后拉高,再等待 100ms 让芯片 PLL 锁定——注释特别说明 W5100S 最慢,需要 100ms),随后完成初始化并返回(Device, Runner)二元组。

实战:在 RP2040 上驱动 W5500-EVB-Pico

仓库在 examples/rp 中提供了完整的 W5500 示例(ethernet_w5500_tcp_server.rsethernet_w5500_tcp_client.rsethernet_w5500_udp.rsethernet_w5500_multisocket.rsethernet_w55rp20_tcp_server.rs等),依赖声明为:

embassy-net-wiznet = { version = "0.3.0", path = "../../embassy-net-wiznet", features = ["defmt"] }

以 ethernet_w5500_tcp_server.rs 为例,集成步骤为:

  1. 配置 SPI 与 GPIO:50 MHz 的 SPI0、片选PIN_17、中断输入PIN_21(上拉)、复位输出PIN_20
  2. 构造驱动:通过embassy_net_wiznet::new(mac_addr, state, spi, int, reset)得到(device, runner),其中State::<8, 8>::new()表示收发队列各 8 个包;mac 地址示例为[0x02, 0x00, 0x00, 0x00, 0x00, 0x00]
  3. 后台任务运行驱动:将Runner传入#[embassy_executor::task]中调用runner.run().await
  4. 接入协议栈embassy_net::Stack::new创建协议栈,stack.add_iface加入接口,iface.set_dhcpv4(Some(Default::default()))启用 DHCP,随后等待wait_config_up()
  5. 使用 TCP:创建TcpListener监听1234端口,循环accept后回显数据。

其他示例还展示了 UDP(ethernet_w5500_udp.rs)、多 socket 并发(ethernet_w5500_multisocket.rs)与 ICMP ping 应答(ethernet_w5500_icmp.rs)等场景,可作为移植到其他芯片(W6100/W6300 只需将chip::W5500换为对应类型)的参考模板。

升级到 0.3.0 的注意事项

从 CHANGELOG 与依赖关系可以归纳出升级要点:

  • embassy-net-driver-channel 必须升级到 0.4.0:0.3.0 起embassy-net-wiznet依赖 embassy-net-driver-channel 0.4.0,这是 crate 间的硬性版本绑定;
  • embassy-net 需配套升级:示例中embassy-net使用 0.9.1 且开启了medium-ethernettcpudpdhcpv4等特性,MACRAW 模式需要medium-ethernet特性支持;
  • API 层面无破坏性变更new()/Runner/State的调用形式在 0.3.0 中保持不变,新增芯片只需替换Chip泛型参数与引脚配置;
  • W6100 属实验性支持:MAC 过滤被关闭,若后续芯片固件或驱动修复了 IP 获取问题,可关注 Unreleased 段落的更新。

总而言之,0.3.0 通过SealedChiptrait 的寄存器抽象与SOCKET_INTR_CLR的引入,把驱动从两款老芯片平滑扩展到了 WIZnet 四代 SPI 以太网控制器,为低成本的no_std+ async 嵌入式以太网方案提供了统一而灵活的接入层。

  • 嵌入式
  • 物联网
  • 异步编程

【免费下载链接】embassy

Modern embedded framework, using Rust and async.

项目地址:https://gitcode.com/gh_mirrors/em/embassy
点击查看免费下载

相关推荐

上一篇:Rnote 开源手写笔记完整指南:矢量草图、PDF 批注与批量导出教程
下一篇:深入 go-fuse v2 的 fs 包:剖析一个“正确性优先”的高性能 FUSE 文件系统框架

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询