STM32 W5500以太网通讯

对于内含以太网MAC部分的芯片,可以外部增加以太网PHY芯片和连接器,实现以太网通讯。对于内部没有以太网MAC部分的芯片,通过W5500 SPI转Ethernet芯片实现以太网通讯是比较好的方式, 从而STM32F0各低端系列都能实现以太网通讯。采用W5500芯片,可以不占用MCU的内存部分做buffer,对于MCU业务逻辑时序紧张的场景也有使用价值。MCU和W5500可以通过SPI的DMA方式,实现较快的收发逻辑;W5500最大支持到80Mbps的SPI接口速度,实际情况根据MCU的SPI接口速率和PCB高速布线质量进行调整。

W5500写读Buffer的要点笔记

  1. 每个SOCKET有自己的写缓冲和读缓冲操作命令/地址,如下:
  2. esp32以太网 esp32以太网w5500传输速度_W5500

  3. 每个SOCKET的写缓冲大小(Sn_TXBUF_SIZE)和读缓冲大小(Sn_RXBUF_SIZE)可调整,写缓冲和读缓冲分离。因为8个socket共享写缓冲和读缓冲,8个socket总的写缓冲大小或读缓冲大小不超过16KB。
  4. 写时可进行burst操作。因此首地址的设置,有辅助的信息寄存器:
    ⑴Sn_TX_FSR : 用于查阅写缓冲区域剩余自由空间,可为Sn_TX_WR和Sn_TX_RD的差值
    ⑵Sn_TX_WR : 写缓冲写入的数据指针,指示已经写到的地址
    ⑶Sn_TX_RD :写缓冲发送的数据指针,指示发送改到了哪个地址
  5. 读时可进行burst操作。因此首地址的设置,有辅助的信息寄存器:
    ① Sn_RX_RSR :用于查阅读缓冲剩余的未读取的数据空间大小,可为Sn_RX_WR和Sn_RX_RD的差值
    ② Sn_RX_WR :接收数据的边界指针,接收到数据时,指针自动后移,指示前面的部分为已收到的数据。
    ③ Sn_RX_RD : 接收数据读取后的边界指针,当进行了接收缓冲区读取后,该指针手动升级到已读取的缓冲区后的地址
  6. 写缓冲和读缓冲都是循环FIFO,因此越界后翻到0地址继续进行。

需要注意的情况:

  1. 是否有不断开连接方式可以将缓冲区复位? 答:不能,且Socket端口打开和连接后的初始化地址也不是0地址,且每次初始化地址还不同。
  2. 越界情况的处理?答:循环缓冲区,控制写入时序,否则会产生对还未发送数据区域的覆盖。

发送数据操作逻辑:

  1. 通过读取Sn_TX_WR寄存器获得当前的写缓冲区指针,通过读取Sn_TX_RD寄存器获得当前的写缓冲区发送数据指针,初始时二者一致表示没有未发送完成的数据。
  2. 通过从Sn_TX_WR寄存器得到的发送缓冲区地址,写入要发送的数据,发送写数据命令和写数据之间的时间间隔不能过大,否则W5500接收SPI数据失败。
  3. 向Sn_TX_WR寄存器写入后续发送要达到的发送缓冲区地址,即当前发送缓冲区地址+要发送的字节数。实际上,这个写操作后,Sn_TX_WR寄存器并未立即更新,而是要等到发送缓冲区数据指令发送后,才会更新。
  4. 发送缓冲区数据发送指令,此时Sn_TX_WR寄存器更新,W5500从Sn_TX_RD地址开始发送数据,并实时更新Sn_TX_RD地址,等到Sn_TX_RD地址与Sn_TX_WR寄存器值相同,则完成并停止发送。

读数据操作逻辑:

  1. 通过读取Sn_RX_RD寄存器得到读缓冲区可读取区域首地址
  2. 通过读取Sn_RX_RSR寄存器得到读缓冲区未读的接收到的数据长度
  3. 通过可读取区域首地址读取读缓冲区里的数据,可小于等于Sn_RX_RSR寄存器里的长度,大于则读到无效数据
  4. 向Sn_RX_RD写入新的可读取区域首地址,为之前的首地址+已读取字节数。但此时Sn_RX_RD还未更新,还需发送下述一条指令进行触发更新。
  5. 通过向Socket的命令寄存器(Command Register)发送RECV指令,触发Sn_RX_RD更新,同时Sn_RX_RSR也被更新。
  6. 如果没有更新Sn_RX_RD寄存器指针,则再次收到数据时会继续顺序放置,同时Sn_RX_WR和Sn_RX_RSR继续增加。因此前后不同次接收到的数据仍然可以都读取出来。

实际嵌入式时序控制时,需要接收中断控制和读取W5500内部状态寄存器,进行时序调度。

STM32 W5500访问逻辑设计

这里设计一个例程,采用STM32H743VIT6连接W5500, 同时实现虚拟串口(VCOM)作为测试辅助:

  1. STM32上电后实现对W5500的配置,并读取特定寄存器以判断访问是否成功
  2. 网络连通后,用TCP测试工具,发送任意数据到STM32, STM32接收到后转发到VCOM;另外STM32将预设的特定数据发送到TCP测试工具。注意这里不是TCP端口的数据简单回环。

STM32CUBEIDE配置

采用STM32CUBEIDE进行工程配置,选择SPI1作为W5500通讯接口,STM32H743VIT6的SPI1能达到80MHz的速率,但测试环境采用短杜邦线连接,达不到80MHz信号完整性传输质量,实测60MHz能够正确响应,因此相应的时钟采用120MHz的2分频实现。

使能外部8MHz晶振时钟,整体的时钟配置如下,包括了SPI和USB VCOM的部分:

esp32以太网 esp32以太网w5500传输速度_以太网转SPI_02


esp32以太网 esp32以太网w5500传输速度_stm32_03


SPI1配置,W5500连接所用管脚为:

  • PD1: W5500_RST
  • PD3: W5500_SCS
  • PD5: W5500_INT
  • PB3: W5500_SCK
  • PB4: W5500_MISO
  • PB5: W5500_MOSI

esp32以太网 esp32以太网w5500传输速度_stm32_04


esp32以太网 esp32以太网w5500传输速度_SPI转以太网_05


esp32以太网 esp32以太网w5500传输速度_esp32以太网_06


esp32以太网 esp32以太网w5500传输速度_以太网转SPI_07


esp32以太网 esp32以太网w5500传输速度_W5500_08


esp32以太网 esp32以太网w5500传输速度_以太网转SPI_09


esp32以太网 esp32以太网w5500传输速度_SPI转以太网_10


esp32以太网 esp32以太网w5500传输速度_W5500_11


USB虚拟串口的配置:

esp32以太网 esp32以太网w5500传输速度_以太网转SPI_12


esp32以太网 esp32以太网w5500传输速度_以太网转SPI_13


esp32以太网 esp32以太网w5500传输速度_W5500_14


esp32以太网 esp32以太网w5500传输速度_SPI转以太网_15


esp32以太网 esp32以太网w5500传输速度_esp32以太网_16


然后生成基本代码:

esp32以太网 esp32以太网w5500传输速度_stm32_17

W5500访问库函数设计

这里设计W5500开通两个SOCKET端口,测试中只用到第一个SOCKET端口。

新建w5500.h文件,代码如下:

/*
 * PD1: W5500_RST
 * PD3: W5500_SCS
 * PD5: W5500_INT
 *
 * PB3: W5500_SCK
 * PB4: W5500_MISO
 * PB5: W5500_MOSI
 *
 *
 */
#ifndef  _W5500_H_
#define  _W5500_H_

#define PY_W5500_RST {HAL_GPIO_WritePin(GPIOD, GPIO_PIN_1, GPIO_PIN_RESET);HAL_Delay(5);HAL_GPIO_WritePin(GPIOD, GPIO_PIN_1, GPIO_PIN_SET);HAL_Delay(5);}
#define PY_W5500_CS_EN HAL_GPIO_WritePin(GPIOD, GPIO_PIN_3, GPIO_PIN_RESET);
#define PY_W5500_CS_DEN HAL_GPIO_WritePin(GPIOD, GPIO_PIN_3, GPIO_PIN_SET);

void PY_W5500_Config(uint8_t ah, uint8_t al, uint8_t ctl, uint8_t data);
void PY_W5500_Read(uint8_t ah, uint8_t al, uint8_t ctl, uint8_t* data, uint16_t length);
void PY_W5500_Send(uint8_t ah, uint8_t al, uint8_t ctl, uint8_t* data, uint16_t length);
void PY_W5500_TCPSERVER_2SOCKETS(void);
void PY_W5500_SOCKET0_CLEARRECINT(void);

#endif

新建w5500.c文件,代码如下:

#include "stm32h7xx_hal.h"
#include <stdio.h>
#include <string.h>
#include "w5500.h"
#include "spi.h"

void PY_W5500_Config(uint8_t ah, uint8_t al, uint8_t ctl, uint8_t data)
{
	extern uint8_t PY_SPIDMA__Status;
	extern uint8_t* PY_CMD_Seg;


	PY_W5500_CS_EN
	PY_SPIDMA__Status=1;
	PY_CMD_Seg[0]=ah;PY_CMD_Seg[1]=al;PY_CMD_Seg[2]=ctl|0x04;PY_CMD_Seg[3]=data;
	HAL_SPI_Transmit_DMA(&hspi1, PY_CMD_Seg,4);
	while(PY_SPIDMA__Status==1) ;
	PY_W5500_CS_DEN
	HAL_Delay(1);
}

void PY_W5500_Read(uint8_t ah, uint8_t al, uint8_t ctl, uint8_t* data, uint16_t length)
{
	extern uint8_t PY_SPIDMA__Status;
	extern uint8_t* PY_CMD_Seg;
	PY_W5500_CS_EN
	PY_SPIDMA__Status=1;
	PY_CMD_Seg[0]=ah;PY_CMD_Seg[1]=al;PY_CMD_Seg[2]=ctl&0xfb;
	HAL_SPI_Transmit_DMA(&hspi1, PY_CMD_Seg,3);
	while(PY_SPIDMA__Status==1) ;

	PY_SPIDMA__Status=1;
	HAL_SPI_Receive_DMA(&hspi1, data, length);
	while(PY_SPIDMA__Status==1) ;

	PY_W5500_CS_DEN
}

void PY_W5500_Send(uint8_t ah, uint8_t al, uint8_t ctl, uint8_t* data, uint16_t length)
{
	extern uint8_t PY_SPIDMA__Status;
	extern uint8_t* PY_CMD_Seg;

	    PY_W5500_CS_EN
		PY_SPIDMA__Status=1;
		PY_CMD_Seg[0]=ah;PY_CMD_Seg[1]=al;PY_CMD_Seg[2]=ctl|0x04;
		HAL_SPI_Transmit_DMA(&hspi1, PY_CMD_Seg,3);
		while(PY_SPIDMA__Status==1) ;

		PY_SPIDMA__Status=1;
		HAL_SPI_Transmit_DMA(&hspi1, data, length);
		while(PY_SPIDMA__Status==1) ;
		PY_W5500_CS_DEN
}



void PY_W5500_TCPSERVER_2SOCKETS(void)
{
/*
 * Only one socket receiving interrupt is opened for cmd recognition. Other interrupt sources are closed. To use polling for Tx status.
 */
extern uint8_t PY_SPIDMA__Status;
extern uint8_t* PY_CMD_Seg;
extern uint8_t* PY_DataR_Seg;
extern uint8_t* PY_DataW_Seg;

//Common register:mode register
 PY_W5500_Config(0x00, 0x00, 0x04, 0x00);
//Common register:gateway IP address register
 PY_W5500_Config(0x00, 0x01, 0x04, 192); //IP: 192.168.1.252
 PY_W5500_Config(0x00, 0x02, 0x04, 168);
 PY_W5500_Config(0x00, 0x03, 0x04, 1);
 PY_W5500_Config(0x00, 0x04, 0x04, 252);
//Common register:subnet mask register
 PY_W5500_Config(0x00, 0x05, 0x04, 255);
 PY_W5500_Config(0x00, 0x06, 0x04, 255);
 PY_W5500_Config(0x00, 0x07, 0x04, 255);
 PY_W5500_Config(0x00, 0x08, 0x04, 0);
//Common register:source hardware address register
 PY_W5500_Config(0x00, 0x09, 0x04, 0x00);
 PY_W5500_Config(0x00, 0x0a, 0x04, 0x08);
 PY_W5500_Config(0x00, 0x0b, 0x04, 0xdc);
 PY_W5500_Config(0x00, 0x0c, 0x04, 0x01);
 PY_W5500_Config(0x00, 0x0d, 0x04, 0x02);
 PY_W5500_Config(0x00, 0x0e, 0x04, 0x03);
 //Common register:source IP address register
 PY_W5500_Config(0x00, 0x0f, 0x04, 192);
 PY_W5500_Config(0x00, 0x10, 0x04, 168);
 PY_W5500_Config(0x00, 0x11, 0x04, 1);
 PY_W5500_Config(0x00, 0x12, 0x04, 252);
 //Common register:interrupt low level interval register
 PY_W5500_Config(0x00, 0x13, 0x04, 0x00);
 PY_W5500_Config(0x00, 0x14, 0x04, 0x13);
 //Common register:(connection) interrupt register
 PY_W5500_Config(0x00, 0x15, 0x04, 0x00);
 //Common register:(connection) interrupt mask register
 PY_W5500_Config(0x00, 0x16, 0x04, 0xf0);
 //Common register:(socket)interrupt register
 PY_W5500_Config(0x00, 0x17, 0x04, 0x00);
 //Common register:(socket) interrupt mask register
 PY_W5500_Config(0x00, 0x18, 0x04, 0xc0);
 //Common register:retry time-value register
 PY_W5500_Config(0x00, 0x19, 0x04, 0x07);
 PY_W5500_Config(0x00, 0x1a, 0x04, 0xd0);
 //Common register:retry count register
 PY_W5500_Config(0x00, 0x1b, 0x04, 0x07);


 //Socket 0 register:mode register
 PY_W5500_Config(0x00, 0x00, 0x0c, 0x21);
 //Socket 0 register:source port number register
 PY_W5500_Config(0x00, 0x04, 0x0c, 0x03); //Port:1000
 PY_W5500_Config(0x00, 0x05, 0x0c, 0xe8);
 //Socket 0 register:maxium segment size register
 PY_W5500_Config(0x00, 0x12, 0x0c, 0x05);
 PY_W5500_Config(0x00, 0x13, 0x0c, 0xb4);
 //Socket 0 register:ip type of service register
 PY_W5500_Config(0x00, 0x15, 0x0c, 0x5a);
 //Socket 0 register:time to live register
 PY_W5500_Config(0x00, 0x16, 0x0c, 64);
 //Socket 0 register:rx buffer size register
 PY_W5500_Config(0x00, 0x1e, 0x0c, 2);
 //Socket 0 register:tx buffer size register
 PY_W5500_Config(0x00, 0x1f, 0x0c, 2);
 //Socket 0 register:keep alive time register
 PY_W5500_Config(0x00, 0x2f, 0x0c, 0x18);

 //Socket 1 register:mode register
 PY_W5500_Config(0x00, 0x00, 0x2c, 0x21);
 //Socket 1 register:source port number register
 PY_W5500_Config(0x00, 0x04, 0x2c, 0x07); //Port:2000
 PY_W5500_Config(0x00, 0x05, 0x2c, 0xD0);
 //Socket 1 register:maxium segment size register
 PY_W5500_Config(0x00, 0x12, 0x2c, 0x05);
 PY_W5500_Config(0x00, 0x13, 0x2c, 0xb4);
 //Socket 1 register:ip type of service register
 PY_W5500_Config(0x00, 0x15, 0x2c, 0x5a);
 //Socket 1 register:time to live register
 PY_W5500_Config(0x00, 0x16, 0x2c, 64);
 //Socket 1 register:rx buffer size register
 PY_W5500_Config(0x00, 0x1e, 0x2c, 2);
 //Socket 1 register:tx buffer size register
 PY_W5500_Config(0x00, 0x1f, 0x2c, 2);
 //Socket 1 register:keep alive time register
 PY_W5500_Config(0x00, 0x2f, 0x2c, 0x18);

 //interrupt mask registers
 PY_W5500_Config(0x00, 0x16, 0x04, 0x00);
 PY_W5500_Config(0x00, 0x18, 0x04, 0x01);
 PY_W5500_Config(0x00, 0x2c, 0x0c, 0x04);


 //Socket 0 : open
 PY_W5500_Config(0x00, 0x01, 0x0c, 0x01);  //open socket
 HAL_Delay(10);
 PY_W5500_Config(0x00, 0x01, 0x0c, 0x02);  //socket server listening

 //PY_W5500_Config(0x00, 0x01, 0x0c, 0x40);

 //Socket 1 : open
 PY_W5500_Config(0x00, 0x01, 0x2c, 0x01);  //open socket
 HAL_Delay(10);
 PY_W5500_Config(0x00, 0x01, 0x2c, 0x02);  //socket server listening


}

void PY_W5500_SOCKET0_CLEARRECINT(void)
{
	 PY_W5500_Config(0x00, 0x02, 0x0c, 0x04);
}

main.c文件设计

对于W5500,配置为只有接收数据中断发送到STM32的中断接收管脚(PD5)。STM32收到中断后,读取数据转发到虚拟串口,另外把预设的一组值发送到以太网口。

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>© Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
//SPI1 DMA buffer address can't be arranged in the region of 0x20000000 ~ 0x20020000

/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "spi.h"
#include "usb_device.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "w5500.h"
#include "string.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
uint8_t PY_W5500_Ver_Addr_SegH=0x00, PY_W5500_Ver_Addr_SegL=0x39;
uint8_t PY_W5500_Ver_Ctrl_Seg=0x00;

uint8_t* PY_CMD_Seg;
uint8_t* PY_DataR_Seg;
uint8_t* PY_DataW_Seg;

uint8_t PY_SPIDMA__Status = 0;

uint8_t cmd=0;

uint8_t rx_addr_h, rx_addr_l;
uint16_t rx_len;

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
  PY_CMD_Seg = 0x38000000;
  PY_DataR_Seg = 0x38000004;
  PY_DataW_Seg = 0x30000000;

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USB_DEVICE_Init();
  MX_SPI1_Init();
  /* USER CODE BEGIN 2 */
  PY_W5500_RST

  PY_CMD_Seg[0]=PY_W5500_Ver_Addr_SegH;
  PY_CMD_Seg[1]=PY_W5500_Ver_Addr_SegL;
  PY_CMD_Seg[2]=PY_W5500_Ver_Ctrl_Seg;

  PY_DataR_Seg[0]=0; PY_DataR_Seg[1]=0;

  PY_W5500_CS_EN
  PY_SPIDMA__Status=1;
  HAL_SPI_Transmit_DMA(&hspi1, PY_CMD_Seg, 3);
  while(PY_SPIDMA__Status==1) ;
  PY_SPIDMA__Status=1;
  HAL_SPI_Receive_DMA(&hspi1, PY_DataR_Seg, 1);
  while(PY_SPIDMA__Status==1) ;
  PY_W5500_CS_DEN

  if(PY_DataR_Seg[0]!=0x04)
  {
	  while(1)
	  {
		  CDC_Transmit_FS(PY_DataR_Seg, 1);
		  HAL_Delay(1000);
	  }
  }

  PY_W5500_TCPSERVER_2SOCKETS();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  //CDC_Transmit_FS(PY_DataR_Seg, 2);
	  HAL_Delay(1000);
	  if(cmd==1)
	  {
		  cmd=0;
		  PY_W5500_SOCKET0_CLEARRECINT();

		  /*
		   * Receiving
		   */
		  PY_W5500_Read(0x00, 0x28, 0x08, PY_DataR_Seg, 2);
		  rx_addr_h=PY_DataR_Seg[0];rx_addr_l=PY_DataR_Seg[1];
		  PY_W5500_Read(0x00, 0x26, 0x08, PY_DataR_Seg, 2);
		  rx_len=(((uint16_t)PY_DataR_Seg[0])<<8)|((uint16_t)PY_DataR_Seg[1]);
		  HAL_Delay(200);

		  PY_W5500_Read(rx_addr_h, rx_addr_l, 0x18, PY_DataR_Seg, rx_len);
		  CDC_Transmit_FS(PY_DataR_Seg, rx_len);

		  PY_W5500_Config(0x00, 0x28, 0x0c, ((((uint16_t)(rx_addr_h))<<8)|((uint16_t)(rx_addr_l))+rx_len)>>8);
		  PY_W5500_Config(0x00, 0x29, 0x0c, ((((uint16_t)(rx_addr_h))<<8)|((uint16_t)(rx_addr_l))+rx_len));
		  PY_W5500_Config(0x00, 0x01, 0x0c, 0x40);
		  HAL_Delay(200);

		  /*
		   * Sending
		   */
		  PY_DataW_Seg[0]=0;PY_DataW_Seg[1]=1;PY_DataW_Seg[2]=2;PY_DataW_Seg[3]=3;PY_DataW_Seg[4]=4;PY_DataW_Seg[5]=5;PY_DataW_Seg[6]=6;PY_DataW_Seg[7]=7;

		  PY_W5500_Read(0x00, 0x22, 0x08, PY_DataR_Seg, 2);
		  PY_W5500_Read(0x00, 0x24, 0x08, PY_DataR_Seg, 2);

		  PY_W5500_Send(PY_DataR_Seg[0], PY_DataR_Seg[1], 0x14, PY_DataW_Seg, 8);

		  PY_W5500_Config(0x00, 0x24, 0x0c, ((((uint16_t)(PY_DataR_Seg[0]))<<8)|((uint16_t)(PY_DataR_Seg[1]))+8)>>8);
		  PY_W5500_Config(0x00, 0x25, 0x0c, ((((uint16_t)(PY_DataR_Seg[0]))<<8)|((uint16_t)(PY_DataR_Seg[1]))+8));
		  HAL_Delay(1);

			  PY_W5500_Read(0x00, 0x22, 0x08, PY_DataR_Seg, 2);
			  PY_W5500_Read(0x00, 0x24, 0x08, PY_DataR_Seg, 2);


			HAL_Delay(1);
			PY_W5500_Config(0x00, 0x01, 0x0c, 0x20);
			HAL_Delay(1);

			  PY_W5500_Read(0x00, 0x22, 0x08, PY_DataR_Seg, 2);
			  PY_W5500_Read(0x00, 0x24, 0x08, PY_DataR_Seg, 2);

	  }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Supply configuration update enable
  */
  HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
  /** Macro to configure the PLL clock source
  */
  __HAL_RCC_PLL_PLLSOURCE_CONFIG(RCC_PLLSOURCE_HSE);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 480;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_1;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
	extern uint8_t PY_SPIDMA__Status;
	PY_SPIDMA__Status = 0;
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
	extern uint8_t PY_SPIDMA__Status;
	PY_SPIDMA__Status = 0;
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
	extern uint8_t PY_SPIDMA__Status;
	PY_SPIDMA__Status = 0;
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	extern uint8_t cmd;

	if(GPIO_Pin==GPIO_PIN_5){

		cmd=1;

	}

}
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

编译并完成下载程序。

通讯测试

连接网线,并将本地IP做静态指定:

esp32以太网 esp32以太网w5500传输速度_esp32以太网_18


芯片重启跑起来后,网络接通,然后打开TCP测试工具,STM32是以太网Server角色,这里电脑端则设置为Client角色:

esp32以太网 esp32以太网w5500传输速度_以太网转SPI_19


esp32以太网 esp32以太网w5500传输速度_W5500_20


打开一个串口工具并连接:

esp32以太网 esp32以太网w5500传输速度_esp32以太网_21


TCP工具端连接STM32, 并发送“GOOD", 接收端设置为16进制接收:

esp32以太网 esp32以太网w5500传输速度_esp32以太网_22


这样,从电脑TCP发送端发送出去的"GOOD"被STM32收到后,转发到串口;STM32另外将十六进制00 01 02 03 04 05 06 07发送到电脑TCP接收端。