STM32 W5500以太网通讯
对于内含以太网MAC部分的芯片,可以外部增加以太网PHY芯片和连接器,实现以太网通讯。对于内部没有以太网MAC部分的芯片,通过W5500 SPI转Ethernet芯片实现以太网通讯是比较好的方式。采用W5500芯片,可以不占用MCU的内存部分做buffer,对于MCU业务逻辑时序紧张的场景也有使用价值。MCU和W5500可以通过SPI的DMA方式,实现较快的收发逻辑;W5500最大支持到80Mbps的SPI接口速度,实际情况根据MCU的SPI接口速率和PCB高速布线质量进行调整。
W5500写读Buffer的要点笔记
-
每个SOCKET有自己的写缓冲和读缓冲操作命令/地址,如下: -
每个SOCKET的写缓冲大小(Sn_TXBUF_SIZE)和读缓冲大小(Sn_RXBUF_SIZE)可调整,写缓冲和读缓冲分离。因为8个socket共享写缓冲和读缓冲,8个socket总的写缓冲大小或读缓冲大小不超过16KB。 -
写时可进行burst操作。因此首地址的设置,有辅助的信息寄存器: ⑴Sn_TX_FSR : 用于查阅写缓冲区域剩余自由空间,可为Sn_TX_WR和Sn_TX_RD的差值 ⑵Sn_TX_WR : 写缓冲写入的数据指针,指示已经写到的地址 ⑶Sn_TX_RD :写缓冲发送的数据指针,指示发送改到了哪个地址 -
读时可进行burst操作。因此首地址的设置,有辅助的信息寄存器: ① Sn_RX_RSR :用于查阅读缓冲剩余的未读取的数据空间大小,可为Sn_RX_WR和Sn_RX_RD的差值 ② Sn_RX_WR :接收数据的边界指针,接收到数据时,指针自动后移,指示前面的部分为已收到的数据。 ③ Sn_RX_RD : 接收数据读取后的边界指针,当进行了接收缓冲区读取后,该指针手动升级到已读取的缓冲区后的地址 -
写缓冲和读缓冲都是循环FIFO,因此越界后翻到0地址继续进行。
需要注意的情况:
- 是否有不断开连接方式可以将缓冲区复位? 答:不能,且Socket端口打开和连接后的初始化地址也不是0地址,且每次初始化地址还不同。
- 越界情况的处理?答:循环缓冲区,控制写入时序,否则会产生对还未发送数据区域的覆盖。
发送数据操作逻辑:
- 通过读取Sn_TX_WR寄存器获得当前的写缓冲区指针,通过读取Sn_TX_RD寄存器获得当前的写缓冲区发送数据指针,初始时二者一致表示没有未发送完成的数据。
- 通过从Sn_TX_WR寄存器得到的发送缓冲区地址,写入要发送的数据,发送写数据命令和写数据之间的时间间隔不能过大,否则W5500接收SPI数据失败。
- 向Sn_TX_WR寄存器写入后续发送要达到的发送缓冲区地址,即当前发送缓冲区地址+要发送的字节数。实际上,这个写操作后,Sn_TX_WR寄存器并未立即更新,而是要等到发送缓冲区数据指令发送后,才会更新。
- 发送缓冲区数据发送指令,此时Sn_TX_WR寄存器更新,W5500从Sn_TX_RD地址开始发送数据,并实时更新Sn_TX_RD地址,等到Sn_TX_RD地址与Sn_TX_WR寄存器值相同,则完成并停止发送。
读数据操作逻辑:
- 通过读取Sn_RX_RD寄存器得到读缓冲区可读取区域首地址
- 通过读取Sn_RX_RSR寄存器得到读缓冲区未读的接收到的数据长度
- 通过可读取区域首地址读取读缓冲区里的数据,可小于等于Sn_RX_RSR寄存器里的长度,大于则读到无效数据
- 向Sn_RX_RD写入新的可读取区域首地址,为之前的首地址+已读取字节数。但此时Sn_RX_RD还未更新,还需发送下述一条指令进行触发更新。
- 通过向Socket的命令寄存器(Command Register)发送RECV指令,触发Sn_RX_RD更新,同时Sn_RX_RSR也被更新。
- 如果没有更新Sn_RX_RD寄存器指针,则再次收到数据时会继续顺序放置,同时Sn_RX_WR和Sn_RX_RSR继续增加。因此前后不同次接收到的数据仍然可以都读取出来。
实际嵌入式时序控制时,需要接收中断控制和读取W5500内部状态寄存器,进行时序调度。
STM32 W5500访问逻辑设计
这里设计一个例程,采用STM32H743VIT6连接W5500, 同时实现虚拟串口(VCOM)作为测试辅助:
- STM32上电后实现对W5500的配置,并读取特定寄存器以判断访问是否成功
- 网络连通后,用TCP测试工具,发送任意数据到STM32, STM32接收到后转发到VCOM;另外STM32将预设的特定数据发送到TCP测试工具。注意这里不是TCP端口的数据简单回环。
STM32CUBEIDE配置
采用STM32CUBEIDE进行工程配置,选择SPI1作为W5500通讯接口,STM32H743VIT6的SPI1能达到80MHz的速率,但测试环境采用短杜邦线连接,达不到80MHz信号完整性传输质量,实测60MHz能够正确响应,因此相应的时钟采用120MHz的2分频实现。
使能外部8MHz晶振时钟,整体的时钟配置如下,包括了SPI和USB VCOM的部分: SPI1配置,W5500连接所用管脚为:
- PD1: W5500_RST
- PD3: W5500_SCS
- PD5: W5500_INT
- PB3: W5500_SCK
- PB4: W5500_MISO
- PB5: W5500_MOSI
USB虚拟串口的配置: 然后生成基本代码:
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做静态指定: 芯片重启跑起来后,网络接通,然后打开TCP测试工具,STM32是以太网Server角色,这里电脑端则设置为Client角色: 打开一个串口工具并连接: TCP工具端连接STM32, 并发送“GOOD", 接收端设置为16进制接收: 这样,从电脑TCP发送端发送出去的"GOOD"被STM32收到后,转发到串口;STM32另外将十六进制00 01 02 03 04 05 06 07发送到电脑TCP接收端。
参考例程
上述设计的例程下载: https://download.csdn.net/download/hwytree/22007730
–End–
|