1. 项目背景与硬件选型
第一次用STM32做物联网项目时,我盯着满桌子的网线、开发板和示波器发呆——这堆东西真能变成Web服务器?事实证明,STM32F103ZET6+ENC28J60这对组合确实能打。作为性价比超高的Cortex-M3芯片,STM32F103ZET6的SPI接口跑在18MHz时,配合ENC28J60的10Mbps网速完全够用。有次我拿它做了个智能鱼缸控制器,通过网页就能远程喂食和调水温。
ENC28J60这个老将虽然不如W5500这类新秀方便,但它胜在价格便宜(某宝20块就能拿下),而且SPI接口只需要4根线。记得第一次接线时,我把MOSI和MISO接反了,结果死活ping不通板子,后来用逻辑分析仪抓包才发现问题。这里给个忠告:PA6接MISO,PA7接MOSI,千万别像我一样犯低级错误。
2. 硬件连接实战
2.1 核心接线指南
ENC28J60的接线其实就5个关键点:
- SPI接口:SCLK(PA5)、MOSI(PA7)、MISO(PA6)
- 片选信号:CS(PA4),注意初始化时要拉高
- 复位引脚:RST(PG15),低电平有效
建议用杜邦线连接时给网口端加个磁环,我实测能降低电磁干扰。有一次在工业现场,没加磁环导致HTTP请求老是超时,后来发现是变频器干扰了网络信号。
2.2 外设扩展方案
除了基本联网功能,我通常会加这些外设增强实用性:
- DS18B20温度传感器:单总线接PG9,注意上拉电阻
- LED状态指示:PG13~PG14,用于显示网络状态
- 蜂鸣器报警:PB8,高电平触发
// GPIO初始化示例 void GPIO_Config(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; // ENC28J60片选 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_SetBits(GPIOA,GPIO_Pin_4); // 默认不选中 // 蜂鸣器控制 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_Init(GPIOB, &GPIO_InitStructure); }3. UIP协议栈移植要点
3.1 协议栈裁剪技巧
UIP原生代码有近万行,但实际我们只需要这些核心文件:
- uip.c - 协议栈主逻辑
- uip_arp.c - ARP协议处理
- uiplib.c - 简易API封装
- timer.c - 超时管理
在uip-conf.h里把UIP_CONF_MAX_CONNECTIONS改为2就够了,毕竟STM32的内存才64KB。我曾贪心设为5,结果网页刷新时经常死机。
3.2 内存优化实战
ENC28J60的8KB缓冲区要合理分配:
#define UIP_CONF_BUFFER_SIZE 1500 #define UIP_CONF_RECEIVE_WINDOW 500网页内容最好压缩到3KB以内。有个取巧的办法:把HTML中的空格和注释全删掉,我用这个方法把页面从4.2KB压到了1.8KB。
4. Web服务器实现详解
4.1 网页嵌入黑科技
HTML转C数组的秘诀:
- 用Notepad++把HTML文件另存为UTF-8无BOM格式
- 使用WinHex打开,复制十六进制数据
- 粘贴到
web_content.c中:
const char index_html[] = { 0x3C,0x21,0x44,0x4F, // <!DO 0x43,0x54,0x59,0x50, // CTYP ... // 剩余数据 };4.2 请求处理实战
在uip_callback()里处理HTTP请求:
if(uip_connected()) { if(strncmp(uip_appdata,"GET / ",5)==0) { uip_send(index_html,sizeof(index_html)); } else if(strncmp(uip_appdata,"GET /ctrl?led=on",15)==0) { GPIO_SetBits(GPIOB,GPIO_Pin_8); // 开蜂鸣器 uip_send("HTTP/1.1 200 OK\r\n\r\nOK",22); } }5. 调试避坑指南
5.1 常见问题排查
- Ping不通:检查
ENC28J60_Init()返回值,确保MAC地址正确写入 - 网页加载不全:用Wireshark抓包看TCP窗口大小
- 随机复位:在SPI读写函数里加延时,STM32F103的SPI时钟不能超过18MHz
5.2 性能优化
- 启用TCP快速重传:
#define UIP_CONF_TCP_FAST_RETRANSMIT 1 - 调整ARP缓存时间:
#define UIP_CONF_ARPTAB_SIZE 2 - 关闭DEBUG输出,能提升20%吞吐量
最后分享个真实案例:有个网友的网页按钮点击没反应,最后发现是浏览器缓存了旧版HTML。解决办法是在HTTP头加Cache-Control: no-cache。这种细节问题特别考验耐心,建议每次修改都按Ctrl+F5强制刷新浏览器。