14-以太网通信与W5500.md 9.3 KB


tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(高级篇)V2.0.1 — 第2章:以太网 + 配套代码05~09" author: "尚硅谷研究院" date: 2026-07-15

created: 2026-07-15

以太网通信与W5500

用生活理解:W5500 就像一个自带"同声传译"的网卡芯片——你只需要用 SPI 告诉它"我要连哪台机器、哪个端口",它自己就把 TCP/IP 协议栈这些复杂工作全干了。你的单片机完全不用关心三次握手、重传、分片这些事,只管发/收数据就好。


互联网基础

OSI 7 层模型 vs TCP/IP 4 层模型

OSI 7 层 TCP/IP 4 层 代表协议 功能
应用层 应用层 HTTP, FTP, MQTT, DNS 为用户提供网络应用服务
表示层 SSL/TLS 数据加密/解密、压缩
会话层 SOCKS 建立/管理/终止会话
传输层 传输层 TCP, UDP 端到端通信、可靠传输
网络层 网络层 IP, ICMP, ARP 路由寻址、分组转发
数据链路层 网络接口层 Ethernet, MAC 帧封装、介质访问
物理层 以太网物理层 比特流传输、信号编码

TCP vs UDP

特性 TCP UDP
连接 面向连接(三次握手) 无连接
可靠性 可靠(确认重传) 不可靠(最大努力交付)
顺序 按序交付 不保证顺序
速度 较慢 快速
适用场景 Web、文件传输、邮件 视频流、DNS、IoT 传感器数据

常见协议

协议 端口 功能
HTTP 80 Web 网页访问
HTTPS 443 加密 Web 访问
DNS 53 域名→IP 解析
DHCP 67/68 自动分配 IP 地址
MQTT 1883 IoT 轻量级消息协议

W5500 芯片详解

W5500 是韩国 WIZnet 公司生产的全硬件 TCP/IP 协议栈芯片。不需要在单片机端运行 TCP/IP 协议栈——W5500 内部硬件实现。

特性
接口 SPI(最高 80MHz,可变长数据帧模式)
协议 TCP, UDP, ICMP, IPv4, ARP, IGMP, PPPoE
Socket 8 个独立 Socket(同时支持 8 个连接)
内部 RAM 32KB(Tx/Rx 可分配给各 Socket)
MAC + PHY 内置(10/100Mbps 自适应)
封装 LQFP48

参考:W5500 数据手册(WIZnet 官网)

W5500 接入框图

STM32F103ZET6                           W5500
┌──────────────┐                      ┌──────────┐    ┌──────┐
│ SPI2_SCK     ├────── PB13(SCK) ────→│ SCLK     │    │RJ45 │
│ SPI2_MOSI    ├────── PB15(MOSI) ───→│ MOSI     ├────┤带有 │
│ SPI2_MISO    ├────── PB14(MISO) ←──┤ MISO     │    │网络 │
│ PD3(CS)      ├────────────────────→│ nSCS     │    │变压 │
│ PG6(INT)     │←────────────────────┤ INTn     │    │器  │
│ PG7(RST)     ├────────────────────→│ RSTn     │    └──────┘
│              │                      │          │
│ SPI2         │                      │内置MAC+PHY│
└──────────────┘                      └──────────┘

W5500 内部架构

SPI 接口 ←→ 寄存器管理单元 ←→ 通用寄存器(模式/MAC/IP等)
                          ←→ Socket 寄存器(8个Socket,各512字节)
                          ←→ Tx/Rx 存储器(32KB 可分配)
                          ←→ TCP/IP 内核(硬件实现)
                                  ↓
                               MAC + PHY ←→ 以太网口

W5500 SPI 可变长数据帧模式

W5500 的 SPI 数据帧与普通 SPI Flash 不同,分为控制字节 + 数据

帧格式: [地址偏移(1B)] [控制字节(1B)] [数据(N字节)]

控制字节:
  [7:5] = 块选择 (000=通用寄存器, 001=Socket寄存器, 010=Tx缓冲区, 011=Rx缓冲区)
  [4:2] = 读写模式
  [1]   = 可变长模式 1=VDM (推荐)
  [0]   = 1=读, 0=写

W5500 官方库移植步骤

  1. 从 WIZnet 官网下载 W5500 官方库
  2. 复制 Ethernet/ 目录到项目
  3. 修改 wizchip_conf.h 中的 SPI 接口函数
  4. 实现以下底层函数:

    void SPI_CS_Select(void);      // 芯片选择
    void SPI_CS_Deselect(void);    // 取消片选
    uint8_t SPI_ReadWrite(uint8_t byte); // SPI 读写1字节
    
  5. 调用 wizchip_init()wizchip_setnetinfo() 初始化


实验:W5500 TCP Server

软件设计

项目路径stm32/05_ethernet_test_register ~ stm32/09_ethernet_webserver_register

文件:stm32/05_ethernet_test_register/User/spi.c(SPI 初始化)

#include "spi.h"

// W5500 使用 STM32 SPI2:PB13(SCK), PB14(MISO), PB15(MOSI)
void SPI_Init(void)
{
    RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;       // SPI2 在 APB1
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;

    // PB13(SCK) 复用推挽, PB14(MISO) 浮空输入, PB15(MOSI) 复用推挽
    GPIOB->CRH |= GPIO_CRH_CNF13_1;            // PB13 复用: CNF=10
    GPIOB->CRH |= GPIO_CRH_MODE13;             // MODE=11(50MHz)
    GPIOB->CRH &= ~GPIO_CRH_CNF14;             // PB14 浮空: CNF=01
    GPIOB->CRH |= GPIO_CRH_CNF14_0;
    GPIOB->CRH |= GPIO_CRH_CNF15_1;            // PB15 复用: CNF=10
    GPIOB->CRH |= GPIO_CRH_MODE15;

    // SPI2 配置:模式0(CPOL=0,CPHA=0), 主机, 分频=8 → 4.5MHz
    SPI2->CR1 = 0;                              // 复位配置
    SPI2->CR1 |= SPI_CR1_SSM | SPI_CR1_SSI;   // 软件 NSS
    SPI2->CR1 |= SPI_CR1_MSTR;                 // 主机模式
    SPI2->CR1 |= SPI_CR1_BR_0 | SPI_CR1_BR_1; // BR=011: PCLK/16=2.25MHz
    SPI2->CR1 |= SPI_CR1_SPE;                  // SPI 使能
}

uint8_t SPI_ReadWrite(uint8_t byte)
{
    while (!(SPI2->SR & SPI_SR_TXE)) {}
    SPI2->DR = byte;
    while (!(SPI2->SR & SPI_SR_RXNE)) {}
    return SPI2->DR;
}

文件:stm32/09_ethernet_webserver_register/User/App_ETH.c(W5500 初始化与应用)

#include "w5500/wizchip_conf.h"
#include "w5500/socket.h"

// 网络配置
wiz_NetInfo netInfo = {
    .mac = {0x00, 0x08, 0xDC, 0x12, 0x34, 0x56},  // MAC 地址
    .ip  = {192, 168, 1, 100},                      // IP 地址
    .sn  = {255, 255, 255, 0},                      // 子网掩码
    .gw  = {192, 168, 1, 1},                        // 默认网关
    .dns = {8, 8, 8, 8},                            // DNS 服务器
};

void W5500_Init(void)
{
    // 1. 硬件复位 W5500
    RSTn_LOW;  Delay_ms(1);
    RSTn_HIGH; Delay_ms(10);

    // 2. 注册 SPI 通信函数
    reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect);
    reg_wizchip_spi_cbfunc(SPI_ReadWrite);

    // 3. W5500 初始化
    uint8_t tmp;
    wizchip_init(NULL, NULL);

    // 4. 配置网络参数
    wizchip_setnetinfo(&netInfo);

    // 5. 配置 WOL 和 PPPoE(默认关闭)
}

// 创建 TCP Server,监听 80 端口
void HTTP_Server_Init(void)
{
    int32_t sock = socket(0, Sn_MR_TCP, 80, 0);  // Socket0, TCP, 80端口
    if (sock < 0) { printf("Socket create failed\r\n"); return; }

    listen(sock);                                   // 开始监听
    printf("HTTP Server listening on port 80\r\n");

    while (1)
    {
        int32_t client = accept(sock);              // 等待客户端连接
        if (client >= 0)
        {
            printf("Client connected\r\n");
            uint8_t buffer[1460];
            int32_t len = recv(client, buffer, sizeof(buffer));  // 接收 HTTP 请求

            // 回复 HTTP 响应
            char *response = "HTTP/1.1 200 OK\r\n"
                             "Content-Type: text/html\r\n\r\n"
                             "<h1>Hello from STM32 + W5500!</h1>";
            send(client, (uint8_t *)response, strlen(response));

            close(client);                           // 关闭连接
        }
    }
}

核心函数速查表(W5500 Socket API)

函数 说明
socket(sn, protocol, port, flag) 创建 Socket,返回句柄(-1 失败)
close(sn) 关闭 Socket
listen(sn) TCP Server 开始监听
connect(sn, ip, port) TCP Client 连接远端
accept(sn) 接受连接(返回新 Socket 句柄)
send(sn, data, len) 发送数据
recv(sn, buffer) 接收数据(返回实际接收长度)
setsockopt(sn, option, value) 设置 Socket 选项
getsockopt(sn, option) 获取 Socket 状态

常见问题与避坑

  1. W5500 不工作 → 确认 SPI 通信(示波器看 SCK/MOSI)、检查晶振(25MHz)是否起振
  2. Ping 不通 → 检查 IP/子网掩码/网关配置、检查以太网物理层(RJ45 链接灯亮?)
  3. TCP 连接频繁断开 → W5500 在无数据时可能进入节能模式,需定期发送心跳包
  4. W5500 官方库移植注意 → 必须实现 SPI_CS_Select/DeselectSPI_ReadWrite,并注册到库中
  5. Socket 资源不足 → W5500 最多 8 个 Socket,每个需要分配 Tx/Rx 缓冲区,注意 32KB 的总量分配
  6. HTTP 响应只发一次 → HTTP/1.1 默认为 keep-alive,如需短连接在响应头加 Connection: close