Interrupt
本篇文章,我们将详细介绍如何在W5500芯片上面使用W5500中断功能,并通过实战例程,为大家讲解如何通过中断进行回环数据测试。
该例程用到的其他网络协议,例如DHCP,请参考相关章节。有关初始化过程,请参考Network Install章节,这里将不再赘述。
W5500中断简介
在W5500中断模式下,可以设置多种中断事件,例如网络建立连接、数据接收、数据发送完成等。当某个事件发生时,通过PD8引脚通知给处理器,处理可以立即响应中断,并进行相应的处理。这种方式可以提高系统的响应速度和效率,减少处理器在轮询状态下的资源浪费。
中断特点
- 实时性:在中断事件发生后,处理器会暂停当前正在执行的任务,立刻响应中断,当存在多个中断处理时,处理器会基于优先级进行对应处理。
- 提高系统效率:如果没有中断机制,处理器需要不断轮询寄存器状态,以检查是否需要进行处理,这会占用大量的处理器资源,而中断方式可以让处理器专注运行主程序,需要处理时再去处理相应的任务,提高了系统资源的利用率。
- 事件驱动:中断是由事件驱动的,这些事件可以是外部的(如外部设备的操作、信号变化等),也可以是内部的(如定时器溢出、计算结果异常等)
- 中断服务程序:每个中断源通常都有对应的中断服务程序,它们是专门为处理该中断事件而编写的一段独立的程序代码。
W5500中断应用场景
- 网络唤醒:启用魔术包中断后,当W5500接收到魔术包时会触发中断,进而通知设备执行唤醒操作。
- 网络配置:(启用Socket接收中断后,W5500在接收到数据时触发中断,系统在中断触发后才进行消息读取配置,无需持续轮询,从而节省系统资源。
- 错误处理:当遇到错误时(例如IP地址冲突,网络不可达),W5500可以立即进行处理,提高系统的稳定性。
W5500中断源
通用中断源:
- IP冲突:在收到ARP请求时,发现发送方IP与本地IP重复时触发中断。
- 目标不可抵达:在接收到ICMP(目的端口不可达)包后触发中断。
- PPPoE连接关闭:在PPPoE模式下,PPPoE连接断开时触发中断。
- Magic Packet:当网络唤醒模式启用并通过UDP接收到Magic Packet网络唤醒包时触发中断。
Socket 中断源:
- 发送完成:成功发送数据给对方时触发中断。
- 超时:当ARP超时或者TCP超时时触发中断。
- 接收到数据:接收到对方数据时触发中断。
- 关闭连接:接收到对方的FIN 或 FIN/ACK包时触发中断。
- 建立连接::成功与对方建立连接时触发中断。
使用中断接收数据的流程
- 初始化中断引脚;
-
开启对应Socket的中断功能,相关寄存器及描述如下:
-
设置Socket中断源,相关寄存器及描述如下:
- 编写中断处理函数,在接收到数据时进行处理。
实现过程
接下来,我们看看如何在W5500上实现中断回环数据测试。
步骤1:初始化中断引脚
void wizchip_int_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);
/* PD_09 -> INT */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource9);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
EXTI_InitStructure.EXTI_Line = EXTI_Line9;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
在成功触发W5500中断后,会调用wizchip_ISR()函数,在这里会记录中断标志位并清空中断标志。
步骤2:开启中断功能
setSIMR(0x01); // enable socket0 interrupt
setSn_IMR(SOCKET_ID, 0x0f);//enable all interrupt functions of socket 0.
步骤3:编写中断处理函数
在成功触发W5500中断后,会调用wizchip_ISR()函数,在这里会记录中断标志位并清空中断标志。
void EXTI9_5_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line9) == SET)
{
if (GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_9) == RESET)
{
wizchip_ISR();
printf("!!!\r\n");
}
}
EXTI_ClearITPendingBit(EXTI_Line9);
}
/**
* @brief Determine the interrupt type and store the value in I STATUS
* @param none
* @return none
*/
void wizchip_ISR(void)
{
uint8_t SIR_val = 0;
uint8_t tmp, sn;
SIR_val = getSIR();
if (SIR_val != 0xff)
{
setSIMR(0x00);
for (sn = 0; sn < _WIZCHIP_SOCK_NUM_; sn++)
{
tmp = 0;
if (SIR_val & IR_SOCK(sn))
{
tmp = getSn_IR(sn);
if(tmp != 0x03)//error interrupt
{
I_STATUS[sn] |= tmp;
tmp &= 0x0f;
setSn_IR(sn, tmp);
}
}
}
setSIMR(0xff);
}
}
步骤4:主循环中运行TCP回环服务器
void loopback_tcps_interrupt(uint8_t sn, uint8_t *buf, uint16_t port)
{
uint16_t len = 0;
uint8_t destip[4];
uint16_t destport;
if (I_STATUS[sn] == SOCK_CLOSED)
{
if (!ch_status[sn])
{
#ifdef INTERRUPT_DEBUG
printf("%d:TCP server start\r\n", sn);
#endif
ch_status[sn] = ready_status;
if (socket(sn, Sn_MR_TCP, port, 0x00) != sn)
{
ch_status[sn] = closed_status;
}
else
{
#ifdef INTERRUPT_DEBUG
printf("%d:Socket opened\r\n", sn);
#endif
listen(sn);
#ifdef INTERRUPT_DEBUG
printf("%d:Listen, TCP server loopback, port [%d]\r\n", sn, port);
#endif
}
}
}
if (I_STATUS[sn] & Sn_IR_CON)
{
getSn_DIPR(sn, destip);
destport = getSn_DPORT(sn);
#ifdef INTERRUPT_DEBUG
printf("%d:Connected - %d.%d.%d.%d : %d\r\n", sn, destip[0], destip[1], destip[2], destip[3], destport);
#endif
ch_status[sn] = connected_status;
I_STATUS[sn] &= ~(Sn_IR_CON);
}
if (I_STATUS[sn] & Sn_IR_DISCON)
{
printf("%d:Socket disconnected\r\n", sn);
if ((getSn_RX_RSR(sn)) > 0)
{
if (len > ETHERNET_BUF_MAX_SIZE)
{
len = ETHERNET_BUF_MAX_SIZE;
}
recv(sn, buf, len);
buf[len] = 0x00;
printf("%d:recv data:%s\r\n", sn, buf);
I_STATUS[sn] &= ~(Sn_IR_RECV);
send(sn, buf, len);
}
disconnect(sn);
ch_status[sn] = closed_status;
I_STATUS[sn] &= ~(Sn_IR_DISCON);
}
if (I_STATUS[sn] & Sn_IR_RECV)
{
setIMR(0x00);
I_STATUS[sn] &= ~(Sn_IR_RECV);
setIMR(0xff);
if ((len = getSn_RX_RSR(sn)) > 0)
{
if (len > ETHERNET_BUF_MAX_SIZE)
{
len = ETHERNET_BUF_MAX_SIZE;
}
len = recv(sn, buf, len);
buf[len] = 0x00;
printf("%d:recv data:%s\r\n", sn, buf);
send(sn, buf, len);
}
}
if (I_STATUS[sn] & Sn_IR_SENDOK)
{
I_STATUS[sn] &= ~(Sn_IR_SENDOK);
}
}
进入函数后,首先检查Socket状态。如果Socket处于关闭状态,则启动一个TCP服务器。接着,根据记录的中断标志位判断是否触发了中断,并根据中断类型进行相应的处理。
运行结果
请注意:
测试实例需要PC端和W5500处于同一网段。
烧录例程运行后,首先可以看到进行了PHY链路检测,然后打印了设置的网络地址信息,接着是TCP中断回环测试,W5500接收到Socket数据后触发中断并进行回传。如下图所示:

总结
本文讲解了如何在 W5500 芯片上使用 W5500 中断功能并进行回环数据测试,通过实战例程展示了从初始化中断引脚、处理中断、开启中断功能到在主循环中运行 TCP 回环服务器的完整过程。文章详细介绍了 W5500 中断的概念、特点、应用场景、中断源以及使用中断接收数据的流程,帮助读者理解其在提升系统响应速度和资源利用率方面的实际应用价值。
下一篇文章将讲解在 W5500 芯片上实现以太网测速功能,通过实战例程讲解如何借助 Jperf 工具进行以太网速率测试,敬请期待!