简介:本资源是一套面向嵌入式开发初学者与中级工程师的STM32+W5100以太网通信实战工程,聚焦W5100在STM32平台上的完整驱动实现与TCP/IP应用开发。资源解决的核心问题是:如何基于硬件TCP/IP协议栈芯片W5100,快速构建稳定、可复用的嵌入式网络功能,适用于HTTP服务器、数据上传、远程控制等物联网典型场景。压缩包共176个文件,含33个头文件(.h)定义寄存器与API接口、28个C源文件(.c)实现SPI驱动、Socket管理及网络协议调用逻辑,以及Keil工程关键文件(.uvproj、.uvopt、.axf、.hex等),总大小1.16MB,结构完整,支持直接编译下载运行。已有718人学习下载,配套工程已适配STM3210E-EVAL评估板,包含初始化配置、中断响应、多Socket并发处理及典型应用示例代码,便于读者理解底层通信机制并快速迁移至自有硬件平台。
1. 项目概述:为什么W5100仍是STM32嵌入式网络开发的“稳压器”
在STM32生态里,提到以太网通信,很多人第一反应是“用LAN8720+PHY+MAC”或者“直接上ESP32/ESP8266”,但如果你正在做一款需要长期稳定运行、不依赖Wi-Fi信号、对电磁干扰敏感、又不想啃TCP/IP协议栈的工业现场设备——比如PLC扩展模块、温湿度数据采集网关、或带本地Web配置界面的电机控制器——那W5100几乎就是你绕不开的“老伙计”。它不是最先进,但足够可靠;不是集成度最高,但足够透明;不是功耗最低,但足够省心。我亲手调试过37块不同批次的W5100模组,从正点原子的ALIENTEK开发板到自己PCB打样焊死的定制板,从-20℃冷库环境到45℃配电柜内部,它没让我在客户现场重启过一次。这不是玄学,而是因为它把物理层(PHY)、数据链路层(MAC)和网络层(IP/TCP/UDP)全封装进一颗芯片里,STM32只需要通过SPI口喂它指令、收它状态,剩下的——ARP响应、ICMP ping应答、TCP三次握手重传、UDP校验和计算——全由它自己硬逻辑完成。你不用写LwIP移植代码,不用配时钟树让ETH外设跑稳,更不用为PHY寄存器读写时序抓狂。它就像一个自带说明书的黑盒子,插上就通,断电即停,故障率低到可以忽略。而标题里反复出现的“stm32w5100”这个连写词,恰恰暴露了开发者的真实痛点:不是在找教程,是在找能立刻跑起来、不卡死、不丢包、不莫名其妙掉线的最小可行方案。所以这篇内容不讲理论堆砌,不列芯片手册原文,只讲我在产线调试、客户返修、小批量试产中踩出来的每一步实操细节——从SPI引脚怎么接才不串扰,到W5100复位后必须等待的150ms黄金窗口,再到HAL库里那个容易被忽略的HAL_SPI_TransmitReceive()超时参数怎么设才不导致socket卡死。如果你手头正有一块STM32F103C8T6最小系统板和一块W5100模块,接下来十分钟就能让它ping通局域网。
2. 硬件连接与底层驱动设计:SPI不是接上就能通的
2.1 W5100核心引脚定义与STM32引脚映射陷阱
W5100虽是SPI从机,但它的引脚设计藏着几个极易被新手忽略的“反直觉”细节。先看最关键的四根SPI线:
- SCLK:标准SPI时钟,接STM32的SPIx_SCK(如PA5)。注意:W5100最高支持8MHz时钟,但实测超过4MHz时,在长排线(>15cm)或未加磁珠的PCB上易出现CRC校验失败。我建议F1系列默认用2MHz,F4系列可尝试3.33MHz(APB2=72MHz时分频系数选22)。
- MISO/MOSI:这里有个大坑——W5100的MISO引脚标为“SO”,MOSI标为“SI”,但很多国产模块丝印会印反!我拆过5家不同厂商的模块,有3家把SO/SI位置焊反。验证方法很简单:用万用表测模块背面走线,确认SO确实连到STM32的MISO引脚(如PA6),SI连到MOSI(如PA7)。一旦接反,初始化阶段就读不到W5100的版本号(0x04),后续所有操作都会返回0xFF。
- SS(片选):必须接STM32任意GPIO(如PA4),且绝不能用SPI硬件NSS!因为W5100的SS是低电平有效,且要求在每次SPI传输前至少保持低电平100ns,传输结束后保持高电平至少50ns。HAL库的
HAL_SPI_Transmit()默认会自动控制NSS,但它的时序精度受中断延迟影响,实测在FreeRTOS任务切换时可能抖动。我的做法是:全程手动控制SS引脚,用HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)拉低,传输完立刻HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)拉高,中间不加任何延时——靠SPI硬件自动满足建立/保持时间。 - RST(复位):这是最容易被忽视的“保命键”。W5100上电后必须执行一次硬复位,且复位脉冲宽度需≥2μs,之后必须等待至少150ms才能开始SPI通信。很多教程只说“拉低再拉高”,却没提这150ms的等待。我曾因跳过此步,在100块板子中出现12块无法初始化,现象是
Sn_SR寄存器始终为0x00。正确做法:HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); HAL_Delay(1); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); HAL_Delay(150);
再看两个关键电源引脚:
- VDD/VDDQ:W5100是3.3V器件,但VDDQ(数字I/O口)必须严格等于VDD(内核电压),否则SPI通信会紊乱。有些模块把VDDQ接到STM32的3.3V,VDD接到外部LDO,结果VDDQ比VDD高0.1V,导致MISO数据错乱。务必用万用表实测两引脚压差≤10mV。
- GND:必须与STM32共地,且单独铺铜。我见过最典型的故障:W5100模块GND与STM32 GND之间只用一根细走线连接,当电机启动时产生地弹,W5100直接复位。解决方案:在模块下方打4个以上GND过孔,形成低阻抗接地平面。
2.2 STM32 SPI外设配置的三个致命参数
HAL库配置SPI看似简单,但以下三个参数若设错,会导致W5100通信间歇性失败:
- Data Size(数据宽度):必须设为
SPI_DATASIZE_8BIT。W5100所有寄存器读写都是字节操作,即使读取16位寄存器(如Sn_TX_FSR),也需分两次读取高低字节。设成16位会导致SPI发送两个字节时W5100只认第一个,第二个被丢弃。 - Clock Phase(CPHA):必须设为
SPI_PHASE_1EDGE(即采样在第一个边沿)。W5100数据在SCLK上升沿采样,下降沿输出,对应CPOL=0, CPHA=0。但HAL库默认CPHA=0,这点常被忽略。 - NSS Management(NSS管理):必须设为
SPI_NSS_SOFT。如前所述,硬件NSS不可靠,必须软件控制。同时,在MX_SPI1_Init()函数中,将hspi1.Init.NSS = SPI_NSS_SOFT;,并确保hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;(低电平有效)。
提示:初始化完成后,务必用逻辑分析仪抓取SPI波形,验证SCLK空闲时为低电平(CPOL=0),数据在SCLK上升沿采样(CPHA=0),SS信号在每次传输前后有清晰的低-高跳变。没有示波器?用STM32的GPIO翻转模拟SPI时序,用万用表测SS引脚电平变化是否稳定。
2.3 W5100寄存器映射与内存模型解析
W5100不是简单的SPI Flash,它采用“寄存器+内存”双地址空间模型,理解这点是驱动编写的核心。其地址空间分为三部分:
- Common Register(公共寄存器):地址0x0000~0x001F,用于配置MAC地址、网关、子网掩码、重试次数等全局参数。例如
SHAR[6](源硬件地址)位于0x0009~0x000E,必须写入6字节MAC地址(如{0x00,0x08,0xDC,0x12,0x34,0x56}),否则ARP请求发不出去。 - Socket Register(Socket寄存器):地址0x0020~0x004F,每个Socket(最多8个)有独立的控制寄存器,如
Sn_MR(模式寄存器)、Sn_CR(命令寄存器)、Sn_SR(状态寄存器)。关键点:Sn_CR是写触发寄存器,写入0x01即执行OPEN命令,执行后Sn_SR会从0x00变为0x13(SOCK_INIT),此时才能配置端口。 - TX/RX Buffer(收发缓冲区):这才是真正的“内存”。W5100内置16KB RAM,分为8个Socket的TX/RX缓冲区(如Socket0 TX起始地址0x4000,大小2KB)。向TX缓冲区写入数据后,必须写
Sn_CR=0x20(SEND命令)才能真正发出。这里有个经典误区:很多人以为写完数据就自动发送,其实必须显式触发。
我画了个简化的内存映射图(文字版)帮助理解:
0x0000 ~ 0x001F: Common Reg (MAC/GW/SUBNET) 0x0020 ~ 0x004F: Socket0 Reg | Socket1 Reg | ... | Socket7 Reg 0x4000 ~ 0x5FFF: Socket0 TX Buffer (2KB) 0x6000 ~ 0x7FFF: Socket0 RX Buffer (2KB) 0x8000 ~ 0x9FFF: Socket1 TX Buffer (2KB) ... 0xE000 ~ 0xFFFF: Socket7 RX Buffer (2KB)所有SPI读写操作,都是先发2字节地址(高位在前),再发数据。例如读Socket0状态寄存器Sn_SR(地址0x0022):
- SS拉低
- 发送0x00(高字节地址)
- 发送0x22(低字节地址)
- 发送0x00(dummy byte,W5100在此时返回
Sn_SR值) - SS拉高
2.4 手动SPI读写函数的健壮性设计
基于上述原理,我写的底层SPI读写函数不依赖HAL库的高级API,而是直接操作SPI寄存器,确保时序精准:
// W5100底层读字节函数 uint8_t w5100_read_byte(uint16_t addr) { uint8_t tx_buf[3], rx_buf[3]; // 构造SPI命令:[ADDR_H][ADDR_L][DUMMY] tx_buf[0] = (addr >> 8) & 0xFF; // 高字节地址 tx_buf[1] = addr & 0xFF; // 低字节地址 tx_buf[2] = 0x00; // dummy byte,W5100在此返回数据 HAL_GPIO_WritePin(W5100_CS_GPIO_Port, W5100_CS_Pin, GPIO_PIN_RESET); // 手动SPI发送(避免HAL库中断延迟) for(int i=0; i<3; i++) { while(!(__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_TXE))); // 等待发送寄存器空 hspi1.Instance->DR = tx_buf[i]; // 写入DR寄存器 while(!(__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_RXNE))); // 等待接收寄存器满 if(i==2) rx_buf[i] = hspi1.Instance->DR; // 只在第3次读取有效数据 else hspi1.Instance->DR; // 清空RX寄存器 } HAL_GPIO_WritePin(W5100_CS_GPIO_Port, W5100_CS_Pin, GPIO_PIN_SET); return rx_buf[2]; } // W5100底层写字节函数 void w5100_write_byte(uint16_t addr, uint8_t data) { uint8_t tx_buf[3]; tx_buf[0] = (addr >> 8) & 0xFF; tx_buf[1] = addr & 0xFF; tx_buf[2] = data; HAL_GPIO_WritePin(W5100_CS_GPIO_Port, W5100_CS_Pin, GPIO_PIN_RESET); for(int i=0; i<3; i++) { while(!(__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_TXE))); hspi1.Instance->DR = tx_buf[i]; while(!(__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_TXE))); // 等待发送完成 } HAL_GPIO_WritePin(W5100_CS_GPIO_Port, W5100_CS_Pin, GPIO_PIN_SET); }注意:这段代码绕过了HAL库的中断机制,直接操作SPI_DR寄存器,确保每个字节发送间隔严格可控。实测在72MHz主频下,单字节读写耗时约1.2μs,完全满足W5100的时序要求(tCYC≥100ns)。如果你坚持用HAL库,务必把
HAL_SPI_TransmitReceive()的Timeout参数设为至少10ms,否则在SPI总线繁忙时会超时返回错误。
3. 初始化流程与网络参数配置:150ms后的生死时速
3.1 W5100初始化状态机详解
W5100的初始化不是一蹴而就的“写寄存器”过程,而是一个严格的五步状态机,每一步失败都会导致后续全部瘫痪。我把它拆解为可逐行调试的步骤:
Step 1:硬复位与等待(150ms)
如前所述,RST引脚拉低1ms后拉高,再HAL_Delay(150)。这是整个流程的基石,跳过则必败。
Step 2:读取版本号验证芯片在线
读地址0x001F(VERSIONR寄存器),正常返回值应为0x04(W5100 V1.0)。如果返回0xFF,检查SPI接线、电源、RST时序。我遇到过3次0xFF:一次是MISO接反,一次是VDDQ电压偏高,一次是PCB上RST引脚被焊锡短路到GND。
Step 3:配置MAC地址(SHAR)
向0x0009~0x000E连续写入6字节MAC。关键点:必须按顺序写,且不能写入全0或全F的MAC(会被交换机过滤)。我习惯用{0x00,0x08,0xDC,0x00,0x00,0x01}作为基础MAC,最后两位自增区分设备。
Step 4:配置网络参数(GAR/SUBR/SHAR)
GAR[4](网关地址):0x0001~0x0004,写入如{192,168,1,1}SUBR[4](子网掩码):0x0005~0x0008,写入如{255,255,255,0}SIPR[4](本机IP):0x000F~0x0012,写入如{192,168,1,100}
注意:这些寄存器写入后不会立即生效,必须执行CR=0x01(RESET命令)才能加载。很多教程漏掉这一步,导致IP配置无效。
Step 5:Socket初始化(OPEN命令)
以Socket0为例:
- 写
Sn_MR=0x02(TCP模式)或0x04(UDP模式)到0x0020 - 写
Sn_PORT[2]=0x01,0xBB(端口40000)到0x0024~0x0025 - 写
Sn_CR=0x01(OPEN命令)到0x0022 - 循环读
Sn_SR(0x0022),直到返回0x13(SOCK_INIT)
实操心得:Step 5最容易卡在
Sn_SR始终为0x00。此时不要盲目重试,先用逻辑分析仪抓SPI波形,确认Sn_CR写入值确实是0x01;再检查Sn_MR是否在OPEN前已正确写入。我曾因Sn_MR写错成0x00(默认值),导致OPEN命令被忽略。
3.2 IP地址冲突与DHCP的务实取舍
标题里没提DHCP,但实际项目中这是高频问题。W5100不支持DHCP客户端,它只能静态IP。这意味着你的设备必须预设IP,或通过串口/按键配置。很多开发者想“偷懒”用DHCP,结果发现W5100手册里根本没DHCP相关寄存器。我的建议是:
- 小批量设备(<100台):出厂烧录唯一MAC+固定IP(如192.168.1.100~199),用Excel生成批量BIN文件。
- 大批量设备:增加一个“配置模式”按钮,长按3秒进入AP模式,手机连上设备热点(W5100可配为TCP Server),网页填IP后保存到STM32 Flash。
- 绝对不要用“随机IP”:有人用RTC秒数生成IP后三位,结果在局域网内撞IP,导致ping不通。W5100没有ARP冲突检测,撞了就静默丢包。
3.3 TCP Server模式下的连接管理实战
W5100最常用场景是做TCP Server(如远程Modbus网关)。其连接管理逻辑与PC端完全不同,必须理解它的“半开连接”特性:
- 当客户端发起SYN,W5100自动回复SYN-ACK,并将Socket状态置为
SOCK_ESTABLISHED(0x17),此时Sn_IR(中断寄存器)的CON位被置1。 - 但W5100不会主动通知STM32有新连接,你必须轮询
Sn_IR。我通常在主循环里每10ms读一次Sn_IR,如果Sn_IR & 0x01为真,则调用w5100_accept()处理。 w5100_accept()本质是:读Sn_SOUR_PORT(源端口)和Sn_DIPR[4](对方IP),然后清Sn_IR=0x01。- 关键陷阱:W5100的RX缓冲区是共享的,如果客户端发来100字节,但你只读了50字节,剩余50字节会留在缓冲区,下次读取时会拼接在新数据前。必须严格按
Sn_RX_RSR(接收就绪大小)寄存器指示的字节数读取,读完再清中断。
我写的连接处理伪代码:
if(w5100_read_byte(0x0022) == 0x17) { // SOCK_ESTABLISHED uint8_t ir = w5100_read_byte(0x0022 + 0x0002); // Sn_IR offset if(ir & 0x01) { // CON interrupt // 读取客户端IP和端口 uint8_t dipr[4]; for(int i=0; i<4; i++) dipr[i] = w5100_read_byte(0x0022 + 0x001C + i); uint16_t port = (w5100_read_byte(0x0022 + 0x001A) << 8) | w5100_read_byte(0x0022 + 0x001B); // 清中断 w5100_write_byte(0x0022 + 0x0002, 0x01); // 启动接收 client_connected = 1; } }4. 数据收发与应用层实现:从裸寄存器到HTTP服务
4.1 TCP发送的“三段式”流程与缓冲区管理
W5100的TCP发送不是send()那么简单,而是严格的三段式操作,任何一步出错都会导致数据滞留:
Stage 1:检查TX缓冲区空间
读Sn_TX_FSR(地址0x0022+0x0020,2字节),得到当前可用TX字节数。例如返回0x07D0(2000),说明还能发2000字节。如果小于待发数据长度,必须等待。
Stage 2:向TX缓冲区写入数据
TX缓冲区起始地址由Sn_TX_WR(0x0022+0x0024)指示。假设Sn_TX_WR=0x4000,则向0x4000~0x4000+len-1写入数据。注意:W5100的TX缓冲区是环形的,写满后自动回绕,但必须保证写入地址不越界(最大2KB)。
Stage 3:触发SEND命令
写Sn_CR=0x20,W5100开始将TX缓冲区数据打包发送。发送完成后,Sn_TX_WR会自动更新为新的写指针位置。关键点:Sn_CR=0x20是“触发”而非“确认”,你必须轮询Sn_IR的SENDOK位(bit1)或Sn_SR是否仍为0x17,才能认为发送成功。
我封装的发送函数:
uint8_t w5100_tcp_send(uint8_t sock, uint8_t *buf, uint16_t len) { uint16_t free_size; uint16_t wr_ptr; // 1. 获取空闲空间 free_size = w5100_read_word(sock*0x100 + 0x0020); // Sn_TX_FSR if(free_size < len) return 0; // 缓冲区不足 // 2. 获取写指针 wr_ptr = w5100_read_word(sock*0x100 + 0x0024); // Sn_TX_WR // 3. 向TX缓冲区写入数据(地址=TX_BASE + wr_ptr) uint16_t tx_base = 0x4000 + sock*0x2000; for(int i=0; i<len; i++) { w5100_write_byte(tx_base + (wr_ptr+i)%2048, buf[i]); } // 4. 更新写指针 wr_ptr = (wr_ptr + len) % 2048; w5100_write_word(sock*0x100 + 0x0024, wr_ptr); // 5. 触发SEND w5100_write_byte(sock*0x100 + 0x0022, 0x20); // Sn_CR=SEND // 6. 等待SENDOK uint32_t timeout = HAL_GetTick() + 1000; while((w5100_read_byte(sock*0x100 + 0x0022 + 0x0002) & 0x02) == 0) { if(HAL_GetTick() > timeout) return 0; // 超时 } w5100_write_byte(sock*0x100 + 0x0022 + 0x0002, 0x02); // 清SENDOK中断 return 1; }注意:
w5100_read_word()是读2字节的封装函数,高位在前。sock*0x100是因为每个Socket寄存器偏移100H。这个函数实测在100KB/s吞吐量下稳定运行,但若网络拥塞,SENDOK可能延迟数百ms,因此超时设为1秒是安全的。
4.2 HTTP Server的极简实现:1KB内存跑出Web页面
标题里没提HTTP,但“stm32 http库”是热搜词,说明需求真实存在。W5100本身不解析HTTP,但我们可以用STM32的有限RAM(F103只有20KB)实现一个极简HTTP Server。核心思路:只响应GET /,返回固定HTML,不处理POST、Cookie、长连接。
内存分配策略:
- TX缓冲区:2KB(W5100提供)
- RX缓冲区:2KB(W5100提供)
- STM32 RAM:仅用256字节作HTTP解析缓冲区
HTTP响应构造:
const char http_response[] = "HTTP/1.1 200 OK\r\n" "Content-Type: text/html\r\n" "Connection: close\r\n\r\n" "<html><body><h1>STM32+W5100</h1>" "<p>Uptime: %d s</p></body></html>";用sprintf()动态填入运行时间,总长度<512字节。
状态机解析GET请求:
不依赖字符串匹配,用状态机逐字节判断:
- 状态0:等待'G'
- 状态1:收到'G',等'E'
- 状态2:收到'E',等'T'
- 状态3:收到'T',等' '(空格)
- 状态4:收到空格,等'/'
- 状态5:收到'/',等' '(GET / HTTP/1.1)
- 状态6:确认是GET /,准备发送响应
这样只需10字节状态变量,比strstr()省上百字节RAM。
4.3 UDP广播与心跳包的工业级实践
在工业现场,TCP的三次握手太慢,UDP才是王道。W5100的UDP模式(Sn_MR=0x04)配置更简单,但有两个关键点:
- 绑定端口后,必须设置
Sn_IMR=0x02(RECV中断使能),否则Sn_IR的RECV位永不置1。 - UDP无连接,但W5100会记录最后一次通信的对方IP/端口,
Sn_DIPR和Sn_DPORT寄存器会自动更新。这意味着你可以用同一个Socket回应不同客户端,无需为每个客户端开新Socket。
我做的设备心跳包协议:
- 设备上电后,每5秒向255.255.255.255:5000广播UDP包,内容为
"ALIVE|MAC|UPTIME" - PC端监听5000端口,收到即显示设备在线
- W5100发送代码:
// 设置目标IP为广播地址 w5100_write_byte(0x0022 + 0x001C, 0xFF); // DIPR[0] w5100_write_byte(0x0022 + 0x001D, 0xFF); // DIPR[1] w5100_write_byte(0x0022 + 0x001E, 0xFF); // DIPR[2] w5100_write_byte(0x0022 + 0x001F, 0xFF); // DIPR[3] w5100_write_word(0x0022 + 0x001A, 5000); // DPORT // 发送数据 w5100_udp_send(0, (uint8_t*)"ALIVE|0008DC123456|1234", 25);实操心得:广播包在跨网段时无效,所以必须确保设备与PC在同一子网。另外,某些交换机默认禁用广播,需在管理界面开启“Broadcast Storm Control”。
5. 常见问题与排查技巧实录:37块板子踩出的避坑清单
5.1 “Ping不通”问题的七层排查法
这是最高频问题,我按OSI模型从下到上梳理:
| 层级 | 检查项 | 工具/方法 | 典型现象 |
|---|---|---|---|
| 物理层 | RST引脚电平 | 万用表测RST对GND电压 | 上电后RST始终为低,W5100不工作 |
| 数据链路层 | MAC地址是否全0 | 读0x0009~0x000E | arp -a看不到设备MAC |
| 网络层 | IP配置是否生效 | 读0x000F~0x0012 | ping自己IP不通,但能ping通网关 |
| 网络层 | 子网掩码是否正确 | 读0x0005~0x0008 | ping同网段设备通,ping网关不通 |
| 传输层 | Socket是否OPEN | 读0x0022(Sn_SR) | 返回0x00,说明未初始化成功 |
| 应用层 | 是否响应ICMP | 抓包看是否有Echo Reply | ping通但无回复,W5100未启用ICMP |
| 应用层 | ARP请求是否发出 | 抓包看是否有ARP Request | ping不通,Wireshark显示ARP timeout |
最隐蔽的是第7项:W5100默认启用ICMP(IMR=0x01),但如果Sn_MR配置错误或CR=0x01未执行,ICMP会被屏蔽。解决方法:在初始化最后,写IMR=0x01(0x001E)。
5.2 “间歇性丢包”的电磁兼容(EMC)根源
在工厂现场,W5100丢包往往不是代码问题,而是EMC设计缺陷。我总结出三大元凶:
- 电源纹波过大:W5100对VDD纹波敏感,>50mV峰峰值就会导致SPI误码。解决方案:在W5100 VDD引脚就近加10μF钽电容+100nF陶瓷电容,且钽电容ESR<1Ω。
- SPI走线过长:>10cm的SPI线相当于天线,易耦合电机噪声。解决方案:SPI走线包地,长度<5cm,SCLK线加10Ω串联电阻。
- 网口变压器未隔离:很多廉价模块用非隔离变压器,导致STM32地与网线地共模电压超标。解决方案:换用Pulse HX5008或HALO HX5008,确保隔离耐压≥1500V。
提示:用示波器FFT功能测SCLK频谱,如果在10MHz附近有尖峰,基本确定是EMC问题。此时不要改代码,先查PCB。
5.3 “Socket卡死”的寄存器级诊断
W5100最让人抓狂的是Socket突然卡在SOCK_ESTABLISHED但不再收发数据。此时不要重启,先读关键寄存器:
Sn_SR(0x0022):确认是否仍为0x17Sn_IR(0x0022+0x0002):看是否有TIMEOUT(bit3)或DISCON(bit4)置1Sn_TX_FSR(0x0022+0x0020):是否为0(TX缓冲区满)Sn_RX_RSR(0x0022+0x0026):是否为0(RX缓冲区空)
如果Sn_IR & 0x08为真,说明TCP重传超时,此时应执行Sn_CR=0x10(CLOSE命令),而不是强行SEND。我写的自动恢复函数:
if(w5100_read_byte(0x0022 + 0x0002) & 0x08) { // TIMEOUT w5100_write_byte(0x0022, 0x10); // CLOSE HAL_Delay(10); w5100_write_byte(0x0022, 0x01); // OPEN again }5.4 W5100与W5500的选型对比实战表
虽然标题是W5100,但常被问及W5500。我用同一块STM32F103板实测对比:
| 项目 | W5100 | W5500 | 我的选择理由 |
|---|---|---|---|
| SPI速度 | 最高8MHz | 最高80MHz | F1系列用不上高速,W5100更稳 |
| 内存 | 16KB | 32KB | W5100的16KB对TCP Server足够 |
| 功耗 | 120mA@3.3 |
本文还有配套的精品资源,点击获取