全部例程

中断示例

W6300

更新于 2026年10月14日

本篇总结

在 W6300 上使用 TOE 中断功能实现 TCP 回环数据测试,通过 INTn 引脚配合平台中断层处理连接、断开、收发等事件。

🔑 MH2457 + QSPI、UART 调试、硬件 TOE 引擎、TCP、中断

硬件平台:本例程基于 W6300-EVB-M4 开发板运行。

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W6300-EVB-M4 开发板

本示例演示如何在 W6300 上使用 TOE 中断功能,并通过实战例程,为大家讲解如何通过中断进行回环数据测试。该例程用到的其他网络协议(例如 DHCP)以及 W6300 的初始化过程,请参考 Network Install 章节,这里不再赘述。

前置工具准备

开始本例程前,请确认以下工具已安装:

TOE 中断简介

TOE(TCP/IP Offload Engine)中断机制通过 INTn 引脚在连接建立、数据接收、发送完成等事件发生时主动通知处理器,相比轮询方式具有实时性强、事件驱动、提高系统效率的特点。

  • 网络唤醒:Magic Packet 触发中断唤醒设备
  • 网络配置:数据接收后按需读取,免持续轮询
  • 错误处理:IP 冲突、网络不可达时立即响应
  • 双类中断源:通用中断(IP 冲突、Magic Packet 等)与 Socket 中断(发送完成、接收数据、建立/关闭连接等)

使用中断接收数据的基本流程

初始化中断引脚(W6300 的 INTn 接 MH2457 PC0)→ 开启对应 Socket 中断功能 → 设置 Socket 中断源 → 在平台中断层记录中断标志并在主循环中处理。

硬件连接与拓扑

请注意:

测试需要 PC 端和 W6300 处于同一网段。

  1. Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
  2. 网线连接开发板 RJ45 网口 到路由器 LAN 口
┌─────────────────────────────────┐
│ 路由器(DHCP Server) │
│ 192.168.1.1 │
└────┬──────────────────┬───────────┘
│ │
┌────┴──────┐ ┌────┴──────┐
│ W6300 │ │ PC │
│ TCP:5000 │ │SocketTester│
│ IP: DHCP │ │ IP: DHCP │
└────┬──────┘ └───────────┘
│ QSPI ↑
┌────┴──────┐ DEBUG USB
│ MH2457 │─────────→ WIZ UartTool
│ MCU │
└───────────┘
网络拓扑:MH2457 通过 QSPI 驱动 W6300,开发板与 PC 在同一局域网内,通过路由器互通

工程结构

工程核心文件如下:

Interrupt/
├── main.c                    ← 系统入口:串口、GPIO、QSPI 初始化 + user_run()
└── USER/
    ├── user_main.c           ← 网络地址定义 + 中断使能 + 主循环
    ├── interrupt.c/h         ← wizchip_ISR、loopback_tcps_interrupt 状态机
    ├── wiz_platform.c/h      ← 平台适配:debug_usart_init、wizchip_gpio_init、hspi_init
    ├── wiz_platform_irq.c/h  ← 平台中断层:IRQ 初始化、irq_take
    └── wiz_interface.c/h     ← W6300 驱动接口:wizchip_initialize、network_init、PHY 检测、软件定时器

步骤1:初始化中断功能

文件:USER/interrupt.c — W6300 的 INTn 接在 MH2457 的 PC0 上,中断由平台层 wiz_platform_irq_init() 初始化。同时 W6300 的全局中断输出使能受 CHIP lock 保护,需要先 CHIPUNLOCK() 再置位 SYCR1_IEN。

static void interrupt_backend_init(void)
{
    if (irq_backend_initialized != 0U)
    {
        return;
    }

    wiz_platform_irq_init();

    /* W6300 global interrupt output enable is protected by CHIP lock. */
    CHIPUNLOCK();
    setSYCR1((uint8_t)(getSYCR1() | SYCR1_IEN));
    CHIPLOCK();

    irq_backend_initialized = 1U;
}

在成功触发 TOE 中断后,会调用 wizchip_ISR() 函数,在这里会记录中断标志位并清空中断标志。

步骤2:开启中断功能

文件:USER/user_main.c — 通过 setSIMR() 开启 Socket 全局中断屏蔽,并通过 setSn_IMR() 开启 Socket 的中断功能。

setSIMR(0xffU);
setSn_IMR(SOCKET_ID, 0x0fU);

中断屏蔽寄存器说明

setSIMR(0xff) 开启所有 Socket 的全局中断;setSn_IMR(SOCKET_ID, 0x0f) 开启 Socket 的低 4 位中断(CON/DISCON/RECV/SENDOK)。

步骤3:编写中断处理函数

文件:USER/interrupt.c — 在 TOE 成功触发中断后,通过平台中断层 wiz_platform_irq_take() 判断中断是否到来,进而调用 wizchip_ISR() 函数,读取 SIR 寄存器判断中断来源,记录中断标志位到 I_STATUS 数组,并清空对应 Socket 的中断标志。

void wizchip_ISR(void)
{
    uint8_t sir_val;
    uint8_t tmp;
    uint8_t sn;
    uint8_t simr;

    sir_val = getSIR();
    if (sir_val == 0U)
    {
        return;
    }

    simr = getSIMR();
    setSIMR(0x00U);

    for (sn = 0U; sn < _WIZCHIP_SOCK_NUM_; sn++)
    {
        if ((sir_val & IR_SOCK(sn)) != 0U)
        {
            tmp = getSn_IR(sn);
            I_STATUS[sn] |= tmp;
            tmp &= 0x0FU;
            setSn_IRCLR(sn, tmp);
        }
    }

    setSIMR(simr);
}

先记录再清空

先读取 Sn_IR 寄存器保存到 I_STATUS 数组,再用 setSn_IRCLR() 清空标志。这样主循环可依据 I_STATUS 判断中断类型并执行相应处理,避免在中断上下文中处理耗时操作,保证中断响应的实时性。

步骤4:主循环中运行 TCP 回环服务器

文件:USER/interrupt.c — 进入函数后,首先通过平台中断层判断是否有中断到来。如果 Socket 处于关闭状态,则启动一个 TCP 服务器。接着,根据记录的中断标志位判断是否触发了中断,并根据中断类型进行相应的处理:连接建立、断开、接收数据、发送完成。

void loopback_tcps_interrupt(uint8_t sn, uint8_t *buf, uint16_t port)
{
    uint16_t len = 0U;
    uint8_t destip[4];
    uint16_t destport;
    int32_t ret;

    interrupt_backend_init();

    if (wiz_platform_irq_take() != 0U)
    {
        wizchip_ISR();
    }

    if (ch_status[sn] == closed_status)
    {
        printf("%d:TCP server start\r\n", sn);
        ch_status[sn] = ready_status;

        if (socket(sn, Sn_MR_TCP4, port, 0x00U) != sn)
        {
            ch_status[sn] = closed_status;
        }
        else
        {
            /* W6300 may reset socket interrupt mask when OPEN is issued. */
            I_STATUS[sn] = 0U;
            setSn_IRCLR(sn, 0x1FU);
            setSn_IMR(sn, 0x0FU);
            setSIMR((uint8_t)(getSIMR() | IR_SOCK(sn)));
            printf("%d:Socket opened\r\n", sn);
            (void)listen(sn);
            printf("%d:Listen, TCP server loopback, port [%d]\r\n", sn, port);
        }
    }

    if ((I_STATUS[sn] & Sn_IR_CON) != 0U)
    {
        getSn_DIPR(sn, destip);
        destport = getSn_DPORT(sn);
        printf("%d:Connected - %d.%d.%d.%d : %d\r\n",
               sn, destip[0], destip[1], destip[2], destip[3], destport);
        ch_status[sn] = connected_status;
        I_STATUS[sn] &= (uint8_t)~Sn_IR_CON;
    }

    if ((I_STATUS[sn] & Sn_IR_DISCON) != 0U)
    {
        printf("%d:Socket disconnected\r\n", sn);
        if (getSn_RX_RSR(sn) > 0U)
        {
            len = getSn_RX_RSR(sn);
            if (len >= ETHERNET_BUF_MAX_SIZE)
            {
                len = ETHERNET_BUF_MAX_SIZE - 1U;
            }
            ret = recv(sn, buf, len);
            if (ret <= 0)
            {
                return;
            }
            len = (uint16_t)ret;
            buf[len] = 0x00U;
            printf("%d:recv data:%s\r\n", sn, buf);
            I_STATUS[sn] &= (uint8_t)~Sn_IR_RECV;
            (void)send(sn, buf, len);
        }
        (void)disconnect(sn);
        ch_status[sn] = closed_status;
        I_STATUS[sn] = 0U;
    }

    if ((I_STATUS[sn] & Sn_IR_RECV) != 0U)
    {
        I_STATUS[sn] &= (uint8_t)~Sn_IR_RECV;
        if (getSn_RX_RSR(sn) > 0U)
        {
            len = getSn_RX_RSR(sn);
            if (len >= ETHERNET_BUF_MAX_SIZE)
            {
                len = ETHERNET_BUF_MAX_SIZE - 1U;
            }
            ret = recv(sn, buf, len);
            if (ret <= 0)
            {
                return;
            }
            len = (uint16_t)ret;
            buf[len] = 0x00U;
            printf("%d:recv data:%s\r\n", sn, buf);
            (void)send(sn, buf, len);
        }
    }

    if ((I_STATUS[sn] & Sn_IR_SENDOK) != 0U)
    {
        I_STATUS[sn] &= (uint8_t)~Sn_IR_SENDOK;
    }

    /* Poll only for close recovery; all data work stays in main-loop context. */
    if ((getSn_SR(sn) == SOCK_CLOSED) && (ch_status[sn] != closed_status))
    {
        ch_status[sn] = closed_status;
        I_STATUS[sn] = 0U;
    }
}

中断事件处理逻辑

函数根据 I_STATUS[sn] 中的中断标志位依次处理:SOCK_CLOSED 启动 TCP 服务器、Sn_IR_CON 处理连接建立、Sn_IR_DISCON 处理断开(先接收残留数据再断开)、Sn_IR_RECV 接收并回传数据、Sn_IR_SENDOK 清除发送完成标志。

运行验证

烧录例程运行后,首先进行 PHY 链路检测,然后打印网络地址信息,接着启动 TCP 中断回环服务器监听 5000 端口。打开 WIZ UartTool V1.0,连接 DEBUG USB 对应 COM 口(115200 / 8N1),典型输出如下:

MH2457 W6300 QSPI example start
W6300 Interrupt example
PHY link
The current Mbtis speed : 100Mbps
The current Duplex Mode : Full-Duplex
DHCP running
DHCP success!
====================================================================================================
 W6300 network configuration : DHCP

 MAC         : 00:08:DC:12:34:56
 IP          : 192.168.2.24
 Subnet Mask : 255.255.255.0
 Gateway     : 192.168.2.1
 DNS         : 192.168.2.1
====================================================================================================

1:TCP server start
1:Socket opened
1:Listen, TCP server loopback, port [5000]

请注意

测试实例需要 PC 端和 W6300 处于同一网段。使用 SocketTester 作为 TCP Client 连接 W6300 的 5000 端口,发送数据后 W6300 通过中断接收并回传。

WIZ UartTool 串口输出(PHY + DHCP + TCP 中断接收)+ SocketTester TCP 客户端 — 中断回环测试

常见问题

Q: 中断方式与轮询方式有什么区别

  • 轮询方式:处理器需要不断读取寄存器状态,占用大量 CPU 资源。
  • 中断方式:由事件驱动,处理器平时运行主程序,事件发生时通过 INTn 引脚通知处理器立即响应,显著提高系统响应速度与资源利用率。

Q: W6300 的中断引脚接在哪里

W6300 的 INTn 引脚接在 MH2457 的 PC0 上。中断由平台层 wiz_platform_irq_init() 初始化,主循环中通过 wiz_platform_irq_take() 判断中断是否到来,再调用 wizchip_ISR() 处理。同时 W6300 的全局中断输出使能受 CHIP lock 保护,需要 CHIPUNLOCK() 后置位 SYCR1_IEN 再 CHIPLOCK()。

Q: 中断标志位为什么要先记录再清空

在 wizchip_ISR() 中先读取 Sn_IR 寄存器保存到 I_STATUS 数组,再用 setSn_IRCLR() 清空标志。这样主循环可依据 I_STATUS 判断中断类型并执行相应处理,避免在中断上下文中处理耗时操作,保证中断响应的实时性。

Q: 断开连接中断触发后为什么要先接收数据

当收到 Sn_IR_DISCON(断开连接)中断时,可能缓冲区中还有未读取的数据。因此先检查 getSn_RX_RSR(sn) 是否大于 0,若有数据则先接收并回传,再调用 disconnect() 关闭连接,确保数据不丢失。

总结

  • 中断机制:TOE 通过 INTn 引脚向处理器输出中断信号,支持实时、事件驱动的处理方式,相比轮询大幅提升系统效率
  • 中断源:通用中断源包括 IP 冲突、目标不可达、PPPoE 关闭、Magic Packet;Socket 中断源包括发送完成、超时、接收数据、关闭连接、建立连接
  • 实现流程:平台层初始化中断引脚 → CHIPUNLOCK 置位 SYCR1_IEN → 开启 SIMR/Sn_IMR 中断屏蔽 → wizchip_ISR 记录并清空标志 → 主循环依据 I_STATUS 运行 TCP 回环服务器
  • 回环测试:在主循环中根据中断标志处理连接、断开、接收、发送完成事件,实现 TCP 数据回传验证

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5

Andy

W5500 技术支持专家