w5500 一篇就够了
基于STM32F103C8T6上搭载的w5500, 实现了udp、tcp Server、tcp Client、mqtt、http、dhcp、dns
嵌入式学习交流Q群 679912988
介绍
w5500是一款集成了TCP/IP协议栈的以太网控制器,具有高速、低功耗、低成本、易用、可靠、安全等特点。
基于STM32F103C8T6上搭载的w5500, 实现了udp、tcp Server、tcp Client、mqtt、http、dhcp、dns 的完整项目地址
https://github.com/hub-yu/ModbusDevice
硬件功能验证
在移植库之前,排查硬件异常,确保w5500的硬件连接正确。
以下代码示例对w5500的寄存器直接操作,调整合适的ip后,正常情况下应该是网口灯亮,且可以ping通的
static uint16_t spi_rw(uint16_t data)
{
uint32_t count = 10000;
while ((count) && (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET))
count--;
if (count == 0)
return 0xffff;
SPI_I2S_SendData(SPI2, data);
count = 10000;
while ((count) && (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET))
count--;
if (count == 0)
return 0xffff;
uint16_t recv = SPI_I2S_ReceiveData(SPI2);
return recv;
}
uint16_t wiz_write_buf(uint32_t addrbsb, uint8_t *buf, uint16_t len)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
spi_rw((addrbsb & 0x00FF0000) >> 16);
spi_rw((addrbsb & 0x0000FF00) >> 8);
spi_rw((addrbsb & 0x000000F8) + 4);
for (uint16_t i = 0; i < len; i++)
spi_rw(buf[i]);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
return len;
}
uint16_t wiz_read_buf(uint32_t addrbsb, uint8_t *buf, uint16_t len)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
spi_rw((addrbsb & 0x00FF0000) >> 16);
spi_rw((addrbsb & 0x0000FF00) >> 8);
spi_rw((addrbsb & 0x000000F8));
for (uint16_t i = 0; i < len; i++)
buf[i] = spi_rw(0x00);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
return len;
}
void reset_net(void)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_11); // min 500us
vTaskDelay(pdMS_TO_TICKS(10));
GPIO_SetBits(GPIOB, GPIO_Pin_11); // max 1ms
vTaskDelay(pdMS_TO_TICKS(10));
}
void set_mac()
{
uint8_t mac[6] = {0x00, 0x22, 0x33, 0x44, 0x55, 0x66};
wiz_write_buf(0x000900, mac, 6);
wiz_read_buf(0x000900, mac, 6);
LOG_INFO("W5500 mac: %02x.%02x.%02x.%02x.%02x.%02x\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
void set_ip()
{
uint8_t gateway[4] = {192, 168, 1, 1};
uint8_t mask[4] = {255, 255, 255, 0};
uint8_t ip[4] = {192, 168, 1, 50};
wiz_write_buf(0x000100, gateway, 4);
wiz_write_buf(0x000500, mask, 4);
wiz_write_buf(0x000F00, ip, 4);
wiz_read_buf(0x000F00, ip, 4);
wiz_read_buf(0x000500, mask, 4);
wiz_read_buf(0x000100, gateway, 4);
LOG_INFO("W5500 ip: %d.%d.%d.%d\r\n", ip[0], ip[1], ip[2], ip[3]);
LOG_INFO("W5500 mask: %d.%d.%d.%d\r\n", mask[0], mask[1], mask[2], mask[3]);
LOG_INFO("W5500 gateway: %d.%d.%d.%d\r\n", gateway[0], gateway[1], gateway[2], gateway[3]);
}
void main() {
...
reset_net();
set_mac();
set_ip();
...
while(1) {
...
}
}
官方库移植
在使用w5500时,官方提供的库是一个很好的起点。它包含了对w5500的基本操作和配置的支持。
下载地址:https://github.com/Wiznet/ioLibrary_Driver
上不去github的小伙伴可以从鄙人网盘,直接下载即可
名称: ioLibrary_Driver-master.zip
大小: 6461214 字节 : 6309 KiB
SHA256: 33943e196776e24469210117a22c91628fb843689bcdcbfc0d46b0dfc1cc0307
这里分享 tcp/udp 测试工具 sokit
下载地址:https://github.com/sinpolib/sokit/releases
也同时放到了网盘上,直接下载即可
名称: sokit-1.3-win32-chs.zip
大小: 3983986 字节 : 3890 KiB
SHA256: 2a871139fe4122378a31c257842186e6e9f0f621e371293ad248f7dcaf7545ab
需要注意和修改的地方如下
在 wizchip_conf.h 中修改 网卡信号及通讯方式
#ifndef _WIZCHIP_
// NOTE_LIHAN: Some sections of this code are not yet fully defined.
#define _WIZCHIP_ W5500 // W5100, W5100S, W5200, W5300, W5500, 6300
#endif
...
#ifndef _WIZCHIP_IO_MODE_
//#define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_SPI_FDM_
#define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_SPI_
#endif
在 wizchip_conf.c 中修改回调函数类型
/**
* @\ref _WIZCHIP instance
*/
//
//M20150401 : For a compiler didnot support a member of structure
// Replace the assignment of struct members with the assingment of array
//
/*
_WIZCHIP WIZCHIP =
{
.id = _WIZCHIP_ID_,
.if_mode = _WIZCHIP_IO_MODE_,
.CRIS._enter = wizchip_cris_enter,
.CRIS._exit = wizchip_cris_exit,
.CS._select = wizchip_cs_select,
.CS._deselect = wizchip_cs_deselect,
// .IF.BUS._read_byte = wizchip_bus_readbyte,
// .IF.BUS._write_byte = wizchip_bus_writebyte
.IF.SPI._read_byte = wizchip_spi_readbyte,
.IF.SPI._write_byte = wizchip_spi_writebyte
};
*/
_WIZCHIP WIZCHIP =
{
_WIZCHIP_IO_MODE_,
_WIZCHIP_ID_ ,
{
wizchip_cris_enter,
wizchip_cris_exit
},
{
wizchip_cs_select,
wizchip_cs_deselect
},
{
{
//M20150601 : Rename the function
//wizchip_bus_readbyte,
//wizchip_bus_writebyte
wizchip_bus_readdata,
wizchip_bus_writedata
},
}
};
tcp client 示例
以下内容是STM32F103C8T6的标准库代码
- 配置w5500 ip:192.168.1.49端口自增的TCP客户端
- 连接到192.168.1.4的TCP服务器5000端口。
| W5500 | STM32F103C8T6 |
|---|---|
| MOSI | PB15 SPI2_MOSI |
| MISO | SPB14 SPI2_MISO |
| SCK | PB13 SPI2_SCK |
| NSS | PB12 CS |
| RESET | PB11 |
| INT | 未使用 |
#include "net.h"
#include "log.h"
#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "stream_buffer.h"
#include "w5500.h"
#include "socket.h"
#include "wizchip_conf.h"
#include <string.h>
static uint16_t spi_rw(uint16_t data)
{
uint32_t count = 10000;
while ((count) && (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET))
count--;
if (count == 0)
return 0xffff;
SPI_I2S_SendData(SPI2, data);
count = 10000;
while ((count) && (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET))
count--;
if (count == 0)
return 0xffff;
uint16_t recv = SPI_I2S_ReceiveData(SPI2);
return recv;
}
static void cris_en()
{
}
static void cris_ex()
{
}
static void cs_sel()
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}
static void cs_desel()
{
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
static uint8_t spi_read()
{
return spi_rw(0x00);
}
static void spi_write(uint8_t data)
{
spi_rw(data);
}
void reset_net(void)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_11); // min 500us
vTaskDelay(pdMS_TO_TICKS(10));
GPIO_SetBits(GPIOB, GPIO_Pin_11); // max 1ms
vTaskDelay(pdMS_TO_TICKS(10));
setPHYCFGR(0xb8); // reset PHY
vTaskDelay(pdMS_TO_TICKS(10));
}
void get_common_regs()
{
LOG_INFO("Mode: 0x%02x\r\n", getMR()); // 配置模式寄存器
uint8_t mac[6] = {0};
getSHAR(mac);
LOG_INFO("mac: %02x.%02x.%02x.%02x.%02x.%02x\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); // 网卡MAC地址
uint8_t ip[4] = {0};
getSIPR(ip);
LOG_INFO("ip: %d.%d.%d.%d\r\n", ip[0], ip[1], ip[2], ip[3]); // 网卡IP地址
uint8_t mask[4] = {0};
getSUBR(mask);
LOG_INFO("mask: %d.%d.%d.%d\r\n", mask[0], mask[1], mask[2], mask[3]); // 网卡子网掩码
uint8_t gateway[4] = {0};
getGAR(gateway);
LOG_INFO("gateway: %d.%d.%d.%d\r\n", gateway[0], gateway[1], gateway[2], gateway[3]); // 网卡网关地址
// vTaskDelay(pdMS_TO_TICKS(1000));
LOG_INFO("INTLEVEL: 0x%02x\r\n", getINTLEVEL()); // 中断触发延时 𝐼𝐴𝑊𝑇 = (𝐼𝑁𝑇𝐿𝐸𝑉𝐸𝐿 + 1) ×(1/𝑃𝐿𝐿𝑐𝑙𝑘3 × 4) (when INTLEVEL > 0)
LOG_INFO("IR: 0x%02x\r\n", getIR()); // 状态中断标志
LOG_INFO("IMR: 0x%02x\r\n", getIMR()); // 状态中断掩码
LOG_INFO("SIR: 0x%02x\r\n", getSIR()); // socket中断标志
LOG_INFO("SIMR: 0x%02x\r\n", getSIMR()); // socket中断掩码
LOG_INFO("RTR: 0x%04x\r\n", getRTR()); // 重发延时 100us × RTR
LOG_INFO("RCR: 0x%02x\r\n", getRCR()); // 重发次数 RCR + 1
vTaskDelay(pdMS_TO_TICKS(200));
LOG_INFO("PTIMER: 0x%02x\r\n", getPTIMER()); // ppp链路控制协议请求定时器 25ms × PTIMER
LOG_INFO("PMAGIC: 0x%02x\r\n", getPMAGIC()); // PMAGIC configures the 4bytes magic number to be used in LCP echo request
uint8_t pppoe_mac[6] = {0x00, 0x22, 0x33, 0x44, 0x55, 0x66};
getPHAR(pppoe_mac); // PHAR should be written to the PPPoE server hardware address acquired in PPPoE connection process
LOG_INFO("PHAR: %02x.%02x.%02x.%02x.%02x.%02x\r\n", pppoe_mac[0], pppoe_mac[1], pppoe_mac[2], pppoe_mac[3], pppoe_mac[4], pppoe_mac[5]);
LOG_INFO("PSID: 0x%04x\r\n", getPSID()); // PSID should be written to the PPPoE sever session ID acquired in PPPoE connectionprocess.
LOG_INFO("PMRU: 0x%02x\r\n", getPMRU()); // PMRU configures the maximum receive unit of PPPoE
uint8_t unreachable_ip[4] = {0, 0, 0, 0};
getUIPR(unreachable_ip);
LOG_INFO("UIPR: %d.%d.%d.%d\r\n", unreachable_ip[0], unreachable_ip[1], unreachable_ip[2], unreachable_ip[3]); // 不可达 IP 地址寄存器
LOG_INFO("UPORTR: %d\r\n", getUPORTR()); // 不可达端口寄存器
LOG_INFO("PHYCFGR: 0x%02x\r\n", getPHYCFGR()); // configures PHY operation
LOG_INFO("version: %d\r\n", getVERSIONR()); // W5500 版本号 always 0x04
}
void get_socket_regs(uint8_t ch)
{
LOG_INFO("ch[%d] ##############\r\n", ch);
LOG_INFO("ch[%d] MR: 0x%02x\r\n", ch, getSn_MR(ch)); // Socket n Mode Register
LOG_INFO("ch[%d] CR: 0x%02x\r\n", ch, getSn_CR(ch)); // Socket n Command Register
LOG_INFO("ch[%d] IR: 0x%02x\r\n", ch, getSn_IR(ch)); // Socket n Interrupt Register
LOG_INFO("ch[%d] SR: 0x%02x\r\n", ch, getSn_SR(ch)); // Socket n Status Register
LOG_INFO("ch[%d] IMR: 0x%02x\r\n", ch, getSn_IMR(ch)); // Socket n Interrupt Mask
LOG_INFO("ch[%d] PORT: %d\r\n", ch, getSn_PORT(ch)); // Socket n Source Port Register
uint8_t dhar[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
getSn_DHAR(ch, dhar); // Socket n Destination Hardware Address Register
LOG_INFO("ch[%d] DHAR: %02x.%02x.%02x.%02x.%02x.%02x\r\n", ch, dhar[0], dhar[1], dhar[2], dhar[3], dhar[4], dhar[5]);
uint8_t dipr[4] = {0x00, 0x00, 0x00, 0x00};
getSn_DIPR(ch, dipr); // Socket n Destination IP Address Register
LOG_INFO("ch[%d] DIPR: %d.%d.%d.%d\r\n", ch, dipr[0], dipr[1], dipr[2], dipr[3]);
LOG_INFO("ch[%d] DPORT: %d\r\n", ch, getSn_DPORT(ch)); // Socket n Destination Port Register
LOG_INFO("ch[%d] MSSR: 0x%04x\r\n", ch, getSn_MSSR(ch)); // Socket n Maximum Segment Size Register
LOG_INFO("ch[%d] TOS: %d\r\n", ch, getSn_TOS(ch)); // Socket n IP Type of Service Register
LOG_INFO("ch[%d] TTL: %d\r\n", ch, getSn_TTL(ch)); // Socket n TTL Register
LOG_INFO("ch[%d] RXBUF_SIZE: %d\r\n", ch, getSn_RXBUF_SIZE(ch)); // Socket n RX Buffer Size Register
LOG_INFO("ch[%d] TXBUF_SIZE: %d\r\n", ch, getSn_TXBUF_SIZE(ch)); // Socket n TX Buffer Size Register
LOG_INFO("ch[%d] TX_FSR: 0X%04x\r\n", ch, getSn_TX_FSR(ch)); // Socket n TX Free Size Register
LOG_INFO("ch[%d] TX_RD: 0X%04x\r\n", ch, getSn_TX_RD(ch)); // Socket n TX Read Pointer Register
LOG_INFO("ch[%d] TX_WR: 0X%04x\r\n", ch, getSn_TX_WR(ch)); // Socket n TX Write Pointer Register
LOG_INFO("ch[%d] RX_RSR: 0X%04x\r\n", ch, getSn_RX_RSR(ch)); // Socket n Received Size Register
LOG_INFO("ch[%d] RX_RD: 0X%04x\r\n", ch, getSn_RX_RD(ch)); // Socket n RX Read Data Pointer Register
LOG_INFO("ch[%d] RX_RX_WR: 0X%04x\r\n", ch, getSn_RX_WR(ch)); // Socket n RX Write Pointer Register
LOG_INFO("ch[%d] RX_FRAG: 0X%04x\r\n", ch, getSn_FRAG(ch)); // Socket n Fragment Register
LOG_INFO("ch[%d] RX_KPALVTR: 0X%02x\r\n", ch, getSn_KPALVTR(ch)); // Socket n Keep Alive Time Register
LOG_INFO("ch[%d] RxMAX: 0X%02x\r\n", ch, getSn_RxMAX(ch)); //
LOG_INFO("ch[%d] TxMAX: 0X%02x\r\n", ch, getSn_TxMAX(ch)); //
}
void net_task(void *arg)
{
reg_wizchip_cris_cbfunc(cris_en, cris_ex); // 注册用于进入和退出临界区的回调函数
reg_wizchip_cs_cbfunc(cs_sel, cs_desel); // 注册用于选择和取消选择SPI时钟的回调函数
reg_wizchip_spi_cbfunc(spi_read, spi_write); // 注册用于通过SPI接口读写字节的回调函数
reset_net();
if(getVERSIONR() != 0x04) {
LOG_ERROR("W5500 version error: 0x%02x\r\n", getVERSIONR());
return;
}
uint8_t mac[6] = {0x00, 0x22, 0x33, 0x44, 0x55, 0x66};
uint8_t gateway[4] = {192, 168, 1, 1};
uint8_t mask[4] = {255, 255, 255, 0};
uint8_t ip[4] = {192, 168, 1, 49};
setGAR(gateway);
setSUBR(mask);
setSHAR(mac);
setSIPR(ip);
get_common_regs();
for(uint8_t i = 0; i < 1; i++) {
vTaskDelay(pdMS_TO_TICKS(1000));
get_socket_regs(i);
}
uint8_t remote_ip[4] = {192, 168, 1, 4};
LOG_INFO("socket %d\r\n", socket(1, Sn_MR_TCP, 0, Sn_MR_ND));
LOG_INFO("connect %d\r\n", connect(1, remote_ip, 5000));
uint8_t buff[100];
while (1)
{
vTaskDelay(pdMS_TO_TICKS(100));
if (getSn_IR(1) & Sn_IR_CON)
setSn_IR(1, Sn_IR_CON);
uint16_t len = getSn_RX_RSR(1);
if(len == 0)
continue;
memset(buff, 0, sizeof(buff));
recv(1, buff, len);
LOG_INFO("%s\r\n", buff);
send(1, buff, len);
}
}
void net_init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure = {
.GPIO_Pin = GPIO_Pin_15 | GPIO_Pin_14 | GPIO_Pin_13, // SCK, MISO, MOSI
.GPIO_Mode = GPIO_Mode_AF_PP, // Alternate function push-pull
.GPIO_Speed = GPIO_Speed_50MHz, // 50 MHz
};
GPIO_Init(GPIOB, &GPIO_InitStructure);
SPI_InitTypeDef SPI_InitStructure = {
.SPI_Direction = SPI_Direction_2Lines_FullDuplex, // Bidirectional mode
.SPI_Mode = SPI_Mode_Master, // Master mode
.SPI_DataSize = SPI_DataSize_8b, // 8 bits per transfer
.SPI_CPOL = SPI_CPOL_Low, // Clock polarity low
.SPI_CPHA = SPI_CPHA_1Edge, // Clock phase first edge
.SPI_NSS = SPI_NSS_Soft, // Software NSS management
.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2, // 36 MHz clock (72 MHz / 2)
.SPI_FirstBit = SPI_FirstBit_MSB, // MSB first
.SPI_CRCPolynomial = 7 // CRC polynomial
};
SPI_Init(SPI2, &SPI_InitStructure);
SPI_Cmd(SPI2, ENABLE);
// 定义RES CS引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_11; // 选择要控制的GPIO引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置引脚速率为50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 设置引脚模式为通用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure); // 调用库函数,初始化GPIO
GPIO_SetBits(GPIOB, GPIO_Pin_12 | GPIO_Pin_11);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_ResetBits(GPIOB, GPIO_Pin_9);
xTaskCreate(net_task, NET_TASK_NAME, NET_TASK_STACK_SIZE, NULL, NET_TASK_PRIORITY, NULL);
}
常见问题
网口灯不亮
复位引脚拉低再拉高之后,网口在插入网线连接交换机后就应该亮起,且芯片温度会升高,如果灯不亮,可能是以下原因:
- 检查芯片温度, 没有温度说明压根就没有工作,比如晶振没有起振,复位引脚未正常配置,或其他硬件问题
- 检查电源供电(仅用用仿真器供电可能达不到整体功耗要求)
- 检查SPI是否正常,若SPI通讯正常,可能是网卡的外围部分电容焊接时连在一起导致电容损耗,导致灯不亮。
灯亮ping不通
复位成功后,配置完ip地址后,ping不通,可能是以下原因:
- 检查网卡是否配置正确
- 检查网口是否接到路由器
- 检查SPI是否正常,是否有异常波形导致SPI通信失败,测试获取w5500版本是否为4
更多推荐



所有评论(0)