一、模块介绍
W5500是一款以太网转SPI通信的芯片,芯片内部自带8个SOCKT通信,配置完成后可实现快速配置后可实现接入网络操作。下面是芯片厂家官网链接。
W5500官方网站_物联网芯片_以太网芯片_网络通信协议,轻松搞定嵌入式以太网!
二、芯片配置
1. SPI配置
根据数据手册,W5500支持SPI的模式0和模式3,数据高位在前,低位在后。
在STM32CubeMx上的配置如下所示,使能DMA的接收和发送:
生成的初始化代码
static void MX_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */
/* USER CODE END SPI1_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA2_CLK_ENABLE();
/* DMA interrupt init */
/* DMA2_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
/* DMA2_Stream3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
}
/**
* @brief SPI MSP Initialization
* This function configures the hardware resources used in this example
* @param hspi: SPI handle pointer
* @retval None
*/
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hspi->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspInit 0 */
/* USER CODE END SPI1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI1 GPIO Configuration
PB3 ------> SPI1_SCK
PB4 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SPI1 DMA Init */
/* SPI1_RX Init */
hdma_spi1_rx.Instance = DMA2_Stream0;
hdma_spi1_rx.Init.Channel = DMA_CHANNEL_3;
hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_rx.Init.Mode = DMA_NORMAL;
hdma_spi1_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hspi,hdmarx,hdma_spi1_rx);
/* SPI1_TX Init */
hdma_spi1_tx.Instance = DMA2_Stream3;
hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3;
hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_tx.Init.Mode = DMA_NORMAL;
hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hspi,hdmatx,hdma_spi1_tx);
/* SPI1 interrupt Init */
HAL_NVIC_SetPriority(SPI1_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
}
}
2. W5500模块配置
可以通过w5500_network_info_show函数确定W5500的通信是否正常。
以太网的配置MAC地址第一个字节必须是偶数,否则会有出现Ping不通的问题。
reg_wizchip_cris_cbfunc(SPI_CrisEnter, SPI_CrisExit); // 注册临界区函数
reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect); // 注册SPI片选信号函数
reg_wizchip_spi_cbfunc(SPI_ReadByte, SPI_WriteByte); // 注册读写函数
reg_wizchip_spiburst_cbfunc(SPI_BusRead, SPI_BusWrite);
这四个注册函数
reg_wizchip_spi_cbfunc是单个字节SPI读写的配置。
reg_wizchip_spiburst_cbfunc是多个字节SPI的读写配置。(可使用DMA的方式发送和接收)
void W5500_Reset(void)
{
W5500_RS_RESET;
HAL_Delay(50);
W5500_RS_SET;
HAL_Delay(50);
}
/**
* @brief set phy config if autonego is disable
* @param none
* @return none
*/
static void w5500_phy_init(void)
{
#ifdef USE_AUTONEGO
// no thing to do
#else
wiz_PhyConf conf;
conf.by = PHY_CONFBY_SW;
conf.mode = PHY_MODE_MANUAL;
conf.speed = PHY_SPEED_100;
conf.duplex = PHY_DUPLEX_FULL;
wizphy_setphyconf(&conf);
#endif
}
/**
* @brief initializes the network infomation
* @param none
* @return none
*/
static void w5500_network_info_init(void)
{
wiz_NetInfo info;
uint8_t mac[6] = DEFAULT_MAC_ADDR;
uint8_t ip[4] = DEFAULT_IP_ADDR;
uint8_t sn[4] = DEFAULT_SUB_MASK;
uint8_t gw[4] = DEFAULT_GW_ADDR;
uint8_t dns[4] = DEFAULT_DNS_ADDR;
memcpy(info.mac, mac, 6);
memcpy(info.ip, ip, 4);
memcpy(info.sn, sn, 4);
memcpy(info.gw, gw, 4);
memcpy(info.dns, dns, 4);
#ifndef USE_DHCP
info.dhcp = NETINFO_DHCP;
#else
info.dhcp = NETINFO_STATIC;
#endif
wizchip_setnetinfo(&info);
}
/**
* @brief read and show the network infomation
* @param none
* @return none
*/
void w5500_network_info_show(void)
{
wizchip_getnetinfo(&Showinfo);
}
/**
* @brief w5500 init
* @param none
* @return errcode
* @retval 0 success
* @retval -1 chip init fail
*/
int W5500_init(void)
{
/* W5500 hard reset */
W5500_Reset();
/* Register spi driver function */
reg_wizchip_cris_cbfunc(SPI_CrisEnter, SPI_CrisExit); // 注册临界区函数
reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect); // 注册SPI片选信号函数
reg_wizchip_spi_cbfunc(SPI_ReadByte, SPI_WriteByte); // 注册读写函数
reg_wizchip_spiburst_cbfunc(SPI_BusRead, SPI_BusWrite);
//W5500有16K的接收BUF和16K的发送BUF,填NULL就是默认每个Socket 2K
wizchip_init(NULL, NULL);
/* phy init */
w5500_phy_init();
// /* network infomation init */
w5500_network_info_init();
// /* show network infomation */
w5500_network_info_show();
reg_dhcp_cbfunc(NULL,NULL,NULL); //注册DHCP回调函数
return 0;
}
SPI的驱动函数
extern SPI_HandleTypeDef hspi1;
uint8_t SPI_ReadByte(void)
{
uint8_t value;
if (HAL_SPI_Receive_DMA(&hspi1, &value, 1) != HAL_OK)
{
value = 0;
}
while(hspi1.State !=HAL_SPI_STATE_READY);
return value;
}
void SPI_WriteByte(uint8_t byte)
{
HAL_SPI_Transmit_DMA(&hspi1, &byte, 1);
while(hspi1.State !=HAL_SPI_STATE_READY);
}
void SPI_BusRead(uint8_t *pBuf, uint16_t len)
{
if (!pBuf)
{
return;
}
HAL_SPI_Receive_DMA(&hspi1, pBuf, len);
while(hspi1.State !=HAL_SPI_STATE_READY);
}
void SPI_BusWrite(uint8_t *pBuf, uint16_t len)
{
uint8_t ret = 0;
if (!pBuf)
{
return;
}
ret = HAL_SPI_Transmit_DMA(&hspi1, pBuf, len);
while(hspi1.State !=HAL_SPI_STATE_READY);
}
/**
* @brief 进入临界区
* @retval None
*/
void SPI_CrisEnter(void)
{
//__set_PRIMASK(1);
}
/**
* @brief 退出临界区
* @retval None
*/
void SPI_CrisExit(void)
{
//__set_PRIMASK(0);
}
/**
* @brief 片选信号输出低电平
* @retval None
*/
void SPI_CS_Select(void)
{
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,GPIO_PIN_RESET);
}
/**
* @brief 片选信号输出高电平
* @retval None
*/
void SPI_CS_Deselect(void)
{
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,GPIO_PIN_SET);
}
3. W5500执行任务
两路SOCKT,执行ModbusTCP主机
static int8_t mbTCPRecv(uint8_t sockt, uint8_t *pucMBTCPFrame, uint16_t *usTCPLength)
{
uint16_t len;
uint16_t usTCPBufPos;
if(sockt > 7) return 0;
len = getSn_RX_RSR(sockt);
if (len > 0)
{
usTCPBufPos = recv(sockt, pucMBTCPFrame, len);
*usTCPLength = usTCPBufPos;
return 1;
}
else
{
return 0;
}
}
static int8_t mbTCPSend(uint8_t sockt, uint8_t *pucMBTCPFrame, uint16_t usTCPLength)
{
int32_t retVaule;
if (sockt > 7)
return 0;
// 发送数据
retVaule = send(sockt, pucMBTCPFrame, usTCPLength);
if (retVaule > 0)
{
return 1;
}
else
{
return (int8_t)retVaule;
}
}
void StartW5500Task(void const *argument)
{
uint8_t state = 0;
uint16_t len = 0;
for (;;)
{
for (uint8_t socket_i = 0; socket_i < 2; socket_i++)
{
LED_GREEN_Togg;
state = getSn_SR(socket_i);
switch (state)
{
case SOCK_SYNSENT:
break;
case SOCK_INIT:
listen(socket_i);
break;
case SOCK_LISTEN:
break;
case SOCK_ESTABLISHED:
if (getSn_IR(socket_i) & Sn_IR_CON)
{
setSn_IR(socket_i, Sn_IR_CON);
}
if (getSn_RX_RSR(socket_i))
{
mbTCPRecv(socket_i, gDATABUF, &len);
memcpy(stModbusTCP.bModbusRTU_RxBuf, gDATABUF, len <= MODBUS_TCP_RX_LEN ? len : 0);
Modbus_Parser(&stModbusTCP);
mbTCPSend(socket_i, stModbusTCP.bModbusRTU_TxBuf, stModbusTCP.bModbusRTU_TxLen);
}
break;
case SOCK_CLOSE_WAIT:
disconnect(socket_i);
break;
case SOCK_CLOSED:
case SOCK_FIN_WAIT:
close(socket_i);
socket(socket_i, Sn_MR_TCP, SOCK_PORT, Sn_MR_ND); // Sn_MR_ND
break;
default:
break;
}
osDelay(25);
}
}
}
4. W5500的DHCP获取IP地址
官方历程:从路由器获取动态IP地址_DHCP
reg_dhcp_cbfunc(NULL,NULL,NULL); //注册DHCP回调函数
DHCP_time_handler(); 函数是DHCP驱动的时间戳,DHCP获取超时会使用到,当前为1S
/**
* @brief 触发单次的DHCP,并获取IP地址
* 获取失败后,应该触发W5500静态IP地址配置
* @param[in] Showinfo Comment
* @return 0:获取失败 1:获取成功
*/
uint8_t DHCP_Get_IP(wiz_NetInfo *Showinfo)
{
uint8_t ret = 0;
uint8_t my_dhcp_retry = 0;
uint8_t dhcp_time = 0;
while (1)
{
switch (DHCP_run())
{
case DHCP_IP_LEASED:
//
// TO DO YOUR NETWORK APPs.
// 获取IP地址并给出
wizchip_getnetinfo(Showinfo);
return 1;
break;
case DHCP_FAILED:
case DHCP_STOPPED:
my_dhcp_retry++;
if (my_dhcp_retry >= 2) // 尝试2次停止DCHP
{
my_dhcp_retry = 0;
DHCP_stop(); // if restart, recall DHCP_init()
// network_init(); // apply the default static network and print out netinfo to serial
return 0;
}
break;
default:
break;
}
if(dhcp_time++ > 10)
{
dhcp_time = 0;
DHCP_time_handler(); //DHCP计时器
}
osDelay(100);
}
}
DHCP_init(SOCK_DHCP,gDATABUF); //按键触发后需要重新初始化
if(DHCP_Get_IP(&Showinfo) == 0) //DHCP获取失败
{
w5500_network_info_init(); //重新设置W5500IP地址
w5500_network_info_show();
}