WIZnet W55MH32 多功能测试固件
更新于 2026年9月28日
本篇总结
W55MH32 作为 HTTP 服务器,通过网页配置界面实现外设控制、SD 卡读写、USB 串口通信及网络参数配置。
硬件平台:本例程基于 W55MH32L-EVB 开发板运行。
采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB
本示例演示 W55MH32 用户配置界面的实现方法,搭配 W55MH32,并结合实战例程,讲解如何以 W55MH32 作为 HTTP 服务器、PC 端作为 HTTP 客户端,通过网页配置界面完成外设功能控制、SD 卡读写及网络参数配置等操作。该例程用到的其他网络协议(例如 DHCP)以及 W55MH32 的初始化过程,请参考 Network Install 章节,这里不再赘述。
前置工具准备
开始本例程前,请确认以下工具已安装:
- WIZ UartTool V1.0:串口调试工具:点击下载
- 浏览器:访问 W55MH32 网页配置界面(Chrome/Edge/Firefox 均可)
Web 网页简介
Web(World Wide Web,万维网)是一种基于超文本和 HTTP 的全球性、动态交互的、跨平台的分布式图形信息系统,为用户提供图形化、易于访问的信息浏览界面。W55MH32 多功能测试固件正是利用 Web 技术,在芯片内部搭建轻量级 HTTP 服务器,静态网页以直接响应返回,LED 控制、网络配置等动态操作通过 CGI 响应处理。
- Web 页面组成:HTML(定义结构)、CSS(控制样式)、JavaScript(实现交互)、Meta 信息(提供元数据)
- 三种交互方式:HTTP 请求、表单提交、AJAX(局部刷新无需重载整页)
- 两种响应处理:直接响应(返回静态资源)、CGI 响应(调用外部程序生成动态内容)
W55MH32 中的 Web 实现
W55MH32 内部搭建轻量级 HTTP 服务器,静态网页以直接响应返回,LED 控制、网络配置等动态操作通过 CGI 响应处理,并借助 AJAX 与后端 CGI 接口交互实现数据实时更新和指令下发,让用户通过浏览器即可完成设备配置与功能测试。
硬件连接与拓扑
请注意:
测试需要 PC 端和 W55MH32 处于同一网段。
- Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
- 网线连接开发板 RJ45 网口 到路由器 LAN 口
│ 路由器(DHCP Server) │
│ 192.168.1.1 │
└────┬──────────────────┬───────────┘
│ │
┌────┴──────┐ ┌────┴──────┐
│ W55MH32 │ │ PC │
│ HTTP Srv │ │ 浏览器 │
│ IP: DHCP │ │ IP: DHCP │
└───────────┘ └───────────┘
↑ ↑
DEBUG USB ──→ WIZ UartTool
工程结构
工程位于 SDK 内 1.SDK\ModuleDemo\WZTOE\HttpServer\,核心文件如下:
HttpServer/
├── Project/
│ └── Project.uvprojx ← Keil 工程文件
└── USER/
├── main.c ← 外设初始化 + HTTP Server + TIM3 中断 + 主循环
├── httpUtil.c/h ← CGI 请求处理(predefined_set/get_cgi_processor)
└── httpServer.c/h ← HTTP Server 核心逻辑
步骤1:外设及网络配置初始化
文件:USER/main.c — 先完成 MCU 底层外设(时钟、延时、中断、串口、USB、定时器、GPIO、I2C)初始化,再初始化 WIZnet TCP/IP 协议栈,从 EEPROM 读取网络参数并配置。
wiz_NetInfo net_info;
/* hardware initialization */
rcc_clk_config();
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
console_usart_init(115200);
usb_init();
delay_ms(200);
printf("%s factory firmware\r\n", _WIZCHIP_ID_);
tim3_init();
user_gpio_init();
i2c_CfgGpio();
MYI2C_Init(&SENx, 1000, 0x38);
/* wiztoe init */
wiz_toe_init();
#if DEFAULT_MAC_EN == 1
getSHAR(default_net_info.mac);
#endif
wiz_phy_link_check();
check_eeprom_network_info(&default_net_info);
network_init(ethernet_buf, &default_net_info);
wizchip_getnetinfo(&net_info);check_eeprom_network_info() 函数检查 EEPROM 中是否有网络地址信息,如果有则赋值给 default_net_info 结构体:
uint8_t check_eeprom_network_info(wiz_NetInfo *net_info)
{
wiz_NetInfo eeprom_net_info = {0};
if (ee_CheckDevice(EEPROM_DEV_ADDR) == 1)
{
printf("No serial EEPROM detected!\r\n");
return 0;
}
ee_ReadBytes((uint8_t *)&eeprom_net_info, 0, sizeof(eeprom_net_info));
if (eeprom_net_info.mac[0] == 0x00 && eeprom_net_info.mac[1] == 0x08 && eeprom_net_info.mac[2] == 0xdc)
{
memcpy(net_info, &eeprom_net_info, sizeof(wiz_NetInfo));
return 1;
}
return 0;
}步骤2:注册网页内容及 HTTP Server 初始化
文件:USER/main.c — 通过 reg_httpServer_webContent() 注册网页内容,再调用 httpServer_init() 完成 HTTP 服务器初始化。
reg_httpServer_webContent((uint8_t *)"index.html", (uint8_t *)HTML_PAGE);
httpServer_init(http_tx_ethernet_buf, http_rx_ethernet_buf, _WIZCHIP_SOCK_NUM_, socknumlist);步骤3:注册 TIM3 中断驱动
文件:USER/main.c — TIM3 定时器中断处理函数,在 1ms 中断周期内分别实现 20ms 按键检测、100ms 温湿度采集、1000ms DHCP/HTTP 定时任务。
void TIM3_IRQHandler(void)
{
static uint32_t tim3_1ms_count = 0;
static uint8_t tim3_100ms_count = 0;
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
tim3_1ms_count++;
tim3_100ms_count++;
btn_timer_cnt++;
if (btn_timer_flag && btn_timer_cnt >= 20)
{
btn_timer_flag = 0;
if ((GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_6) == RESET))
{
btn_cnt++;
}
}
if (tim3_100ms_count >= 100)
{
tim3_100ms_count = 0;
MYI2C_Handle(&SENx);
}
if (tim3_1ms_count >= 1000)
{
DHCP_time_handler();
httpServer_time_handler();
tim3_1ms_count = 0;
}
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}步骤4:运行 HTTP 和 USB 功能及重启检测
文件:USER/main.c — 主循环持续运行 httpServer_run() 处理 HTTP 请求,检测 reboot_flag 置位时复位系统,同时运行 usb_run() 处理 USB 虚拟串口数据。
while (1)
{
httpServer_run(SOCKET_ID);
if (reboot_flag)
{
NVIC_SystemReset();
}
usb_run();
}usb_run() 作为 USB 虚拟串口核心处理函数,检测 USB 连接状态变化并处理数据收发:
void usb_run(void)
{
uint16_t len;
if (usbstatus != bDeviceState)
{
usbstatus = bDeviceState;
if (usbstatus == CONFIGURED)
{
usb_conn_flag = 1;
printf("USB connected\r\n");
}
else
{
usb_conn_flag = 0;
printf("USB disconnected\r\n");
}
}
if (USB_USART_RX_STA & 0x8000)
{
len = USB_USART_RX_STA & 0x3FFF;
strncpy((char *)(usb_data[data_cnt]), (char *)USB_USART_RX_BUF, len);
usb_printf("Tx->:%s\r\n", usb_data[data_cnt]);
USB_USART_RX_STA = 0;
if (data_cnt < 18)
{
data_cnt++;
}
else
{
data_cnt = 0;
}
}
}步骤5:请求内容处理(CGI 接口)
文件:USER/httpUtil.c — POST 方式由 predefined_set_cgi_processor() 处理(LED/蜂鸣器控制、网络配置、USB 发送、SD 卡写入);GET 方式由 predefined_get_cgi_processor() 处理(EEPROM 测试、网络信息、更新页面、SD 卡文件列表)。
uint8_t predefined_set_cgi_processor(uint8_t *uri_name, uint8_t *uri, uint8_t *buf, uint16_t *len)
{
uint8_t ret = 1;
if (strcmp((const char *)uri_name, "set_io_state.cgi") == 0)
{
uint8_t *param;
uint8_t name[10];
uint8_t val;
param = get_http_param_value((char *)uri, "name");
if (param)
{
strcpy((char *)name, (char *)param);
param = get_http_param_value((char *)uri, "val");
if (param) val = (uint8_t)ATOI(param, 10);
if (strcmp((char *)name, "led") == 0)
{ if (val == 1) led_on(); else led_off(); }
if (strcmp((char *)name, "beep") == 0)
{ if (val == 1) beep_on(); else beep_off(); }
}
*len = sprintf((char *)buf, "console.log('%s is %s');", name, val ? "on" : "off");
}
else if (strcmp((const char *)uri_name, "set_netinfo.cgi") == 0)
{
set_netinfo(uri, buf, len);
}
else if (strcmp((const char *)uri_name, "web_send_usb_data.cgi") == 0)
{
uint8_t *param;
if (usb_conn_flag)
{
param = get_http_param_value((char *)uri, "data");
if (param)
{
*len = sprintf((char *)buf, "$('usb-textbox').value='Tx->:'+$('usb-input').value+'\\n'+$('usb-textbox').value;");
usb_printf("Rx->:%s\r\n", param);
}
}
else
{
*len = sprintf((char *)buf, "alert('USB未连接');");
}
}
else if (strcmp((const char *)uri_name, "put_sd.cgi") == 0)
{
uint8_t filename[255];
uint16_t filelen;
uint8_t *param;
param = get_http_param_value((char *)uri, "filename");
if (param) strcpy((char *)filename, (char *)param);
param = get_http_param_value((char *)uri, "filelen");
if (param) filelen = ATOI(param, 10);
param = get_http_param_value((char *)uri, "filedata");
if (put_sd(filename, param, filelen))
*len = sprintf((char *)buf, "alert('存储成功');readContent();");
else
*len = sprintf((char *)buf, "alert('存储文件失败!请检查SD卡是否正确插入!')");
}
else
{
ret = 0;
}
return ret;
}uint8_t predefined_get_cgi_processor(uint8_t *uri_name, uint8_t *buf, uint16_t *len)
{
uint8_t ret = 1;
if (strcmp((const char *)uri_name, "check_eeprom.cgi") == 0)
{
if (ee_Test())
*len = sprintf((char *)buf, "alert('eeprom测试通过!');");
else
*len = sprintf((char *)buf, "alert('eeprom测试失败!');");
}
else if (strcmp((const char *)uri_name, "get_netinfo.cgi") == 0)
{
make_json_netinfo(buf, len);
}
else if (strcmp((const char *)uri_name, "get_update_page.cgi") == 0)
{
make_json_update_page(buf, len);
}
else if (strcmp((const char *)uri_name, "get_sd.cgi") == 0)
{
uint8_t temp[255] = {0};
uint8_t i;
if (get_sd_filename() == 0)
{
*len = sprintf((char *)buf, "alert('SD卡未插入或识别异常!');");
}
else
{
memset(send_data, 0, sizeof(send_data));
for (i = 0; i < file_cnt; i++)
{
sprintf((char *)temp, "%s\\n", file_name[i]);
strcat((char *)send_data, (char *)temp);
}
*len = sprintf((char *)buf, "$('sd-textbox').value=\"%s\";", send_data);
memset(send_data, 0, sizeof(send_data));
memset(file_name, 0, sizeof(file_name));
file_cnt = 0;
}
}
else
{
ret = 0;
}
return ret;
}步骤6:重启设备
文件:USER/main.c — 当网络配置被修改后再进行复位操作,避免出现客户端请求后 W55MH32 未响应就重启导致客户端请求超时。
if (reboot_flag)
{
NVIC_SystemReset();
}运行验证
烧录例程运行后,首先进行 PHY 链路检测,然后通过 DHCP 获取网络地址并打印网络地址信息,最后 HTTP Server 开始监听客户端请求。打开 WIZ UartTool V1.0,连接 DEBUG USB 对应 COM 口(115200 / 8N1),典型输出如下:
W55MH32 factory firmware
PHY is linked.
DHCP : IP Assignment from DHCP Server
IP : 192.168.1.8
GW : 192.168.1.1
SN : 255.255.255.0
DNS : 192.168.1.1
HTTP Server started
在 PC 端浏览器中输入 W55MH32 的 IP 地址,打开网页配置界面,分别对 SD 卡读写、USB 串口功能以及网络配置参数修改进行测试。



常见问题
Q: W55MH32 用户配置界面支持哪些功能
配置界面集成了温湿度实时显示、LED 与蜂鸣器控制、EEPROM 测试、SD 卡文件读写、网络参数(IP/子网掩码/网关/DNS)配置、USB 虚拟串口通信等功能,全部通过浏览器 AJAX 与后端 CGI 接口交互完成。
Q: CGI 请求中 GET 和 POST 方式有什么区别
GET 方式由 predefined_get_cgi_processor() 处理,用于获取数据(网络信息、温湿度、SD 卡文件列表、EEPROM 测试);POST 方式由 predefined_set_cgi_processor() 处理,用于下发控制指令(LED/蜂鸣器控制、网络参数保存、USB 数据发送、SD 卡文件写入)。
Q: 如何修改网络参数并使其生效
在配置界面网络配置区域选择 DHCP 或静态模式,填写 IP/子网掩码/网关/DNS 后点击"保存并配置"。前端校验 IPv4 格式后通过 set_netinfo.cgi 下发,为避免请求超时,网络配置修改后先置位 reboot_flag,待当前请求处理完成后再执行 NVIC_SystemReset() 复位使新参数生效。
Q: SD 卡文件读写有什么限制
网页端通过 FileReader 读取本地 .txt 文件,受 HTTP 请求长度限制,文件内容长度需小于 100 字符才能通过 put_sd.cgi 写入 SD 卡;读取时通过 get_sd.cgi 获取文件名列表。文件过大时会提示"文件过大!"。
总结
- 网页配置方案:W55MH32 作为 HTTP 服务器,PC 端浏览器作为客户端,通过 HTML_PAGE 宏构建完整用户配置网页
- 核心交互:网页通过 AJAX 与后端 CGI 接口交互,实现局部刷新而无需重载整页
- 初始化流程:外设初始化 → 协议栈初始化 → EEPROM 读取网络参数 → 注册网页内容与 HTTP Server → TIM3 中断驱动定时任务
- CGI 处理:predefined_set_cgi_processor() 处理 POST 控制指令,predefined_get_cgi_processor() 处理 GET 数据查询
- 功能覆盖:温湿度显示、LED/蜂鸣器控制、EEPROM 测试、SD 卡读写、网络参数配置、USB 串口通信
- 稳定可靠:网络配置修改后延迟复位避免请求超时,为嵌入式设备网络化管控提供简洁高效的实现思路