tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(高级篇)V2.0.1 — 第2章:以太网 + 配套代码05~09" author: "尚硅谷研究院" date: 2026-07-15
用生活理解:W5500 就像一个自带"同声传译"的网卡芯片——你只需要用 SPI 告诉它"我要连哪台机器、哪个端口",它自己就把 TCP/IP 协议栈这些复杂工作全干了。你的单片机完全不用关心三次握手、重传、分片这些事,只管发/收数据就好。
| OSI 7 层 | TCP/IP 4 层 | 代表协议 | 功能 |
|---|---|---|---|
| 应用层 | 应用层 | HTTP, FTP, MQTT, DNS | 为用户提供网络应用服务 |
| 表示层 | ↓ | SSL/TLS | 数据加密/解密、压缩 |
| 会话层 | ↓ | SOCKS | 建立/管理/终止会话 |
| 传输层 | 传输层 | TCP, UDP | 端到端通信、可靠传输 |
| 网络层 | 网络层 | IP, ICMP, ARP | 路由寻址、分组转发 |
| 数据链路层 | 网络接口层 | Ethernet, MAC | 帧封装、介质访问 |
| 物理层 | ↓ | 以太网物理层 | 比特流传输、信号编码 |
| 特性 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接(三次握手) | 无连接 |
| 可靠性 | 可靠(确认重传) | 不可靠(最大努力交付) |
| 顺序 | 按序交付 | 不保证顺序 |
| 速度 | 较慢 | 快速 |
| 适用场景 | Web、文件传输、邮件 | 视频流、DNS、IoT 传感器数据 |
| 协议 | 端口 | 功能 |
|---|---|---|
| HTTP | 80 | Web 网页访问 |
| HTTPS | 443 | 加密 Web 访问 |
| DNS | 53 | 域名→IP 解析 |
| DHCP | 67/68 | 自动分配 IP 地址 |
| MQTT | 1883 | IoT 轻量级消息协议 |
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 官网)
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│
└──────────────┘ └──────────┘
SPI 接口 ←→ 寄存器管理单元 ←→ 通用寄存器(模式/MAC/IP等)
←→ Socket 寄存器(8个Socket,各512字节)
←→ Tx/Rx 存储器(32KB 可分配)
←→ TCP/IP 内核(硬件实现)
↓
MAC + PHY ←→ 以太网口
W5500 的 SPI 数据帧与普通 SPI Flash 不同,分为控制字节 + 数据:
帧格式: [地址偏移(1B)] [控制字节(1B)] [数据(N字节)]
控制字节:
[7:5] = 块选择 (000=通用寄存器, 001=Socket寄存器, 010=Tx缓冲区, 011=Rx缓冲区)
[4:2] = 读写模式
[1] = 可变长模式 1=VDM (推荐)
[0] = 1=读, 0=写
Ethernet/ 目录到项目wizchip_conf.h 中的 SPI 接口函数实现以下底层函数:
void SPI_CS_Select(void); // 芯片选择
void SPI_CS_Deselect(void); // 取消片选
uint8_t SPI_ReadWrite(uint8_t byte); // SPI 读写1字节
调用 wizchip_init() 和 wizchip_setnetinfo() 初始化
项目路径: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); // 关闭连接
}
}
}
| 函数 | 说明 |
|---|---|
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 状态 |
SPI_CS_Select/Deselect 和 SPI_ReadWrite,并注册到库中Connection: close