while (1)
{
if(HAL_GPIO_ReadPin(W5300_LINK_GPIO_Port, L_LINK)==1) // LINK——LED 为高电平
{
for(i=0;i<8;i++)
{
close(i);
}
}
else
{
W5300_UDP_Loopback(0,1000,Dip,1000, S_Buffer); //socket号,本机端口,目的IP,目的端口,收发缓存
}
}
一、硬件配置
1.1 stm32与W5300的硬件连接
我选择的是16位数据宽度,因此这个地方地址线需要串一位进行连接,板子的A9不需要连,W5300的A0位也不需要连接。
下面是W5300的排针引脚:需要注意的是J1和J2不要画反了
二、软件配置
软件的话我是用的是STM32CubeMX+keil5.32配置的。由于stm32外接w5300需要用到32的FSMC总线,这个总线可以用来外扩SRAM/FLASH/LCD的,这个地方W5300是可以当成SRAM来配置的。
1、以下是cubemx的配置过程:
1、首先得选择好自己的芯片型号
2、配置ICACHE
选择1-way就好了
3、配置RCC
高速时钟选择晶振
4、配置sys
开启系统基准时钟
5、配置FSMC总线
STM32的FSMC有一个存储区间,我们要用的是BANK1,BANK3一般用来外扩NAND FLASH,
然后BANK1里面又分了四个小区间每块64M,说明用户自己的话可以外扩4块SRAM类似的内存。
在这里我们选择BANK1里面的第三块,也就是NE3,这个可以随意选择,我第一块用来外扩SRAM了,这个后面再说。
chip select 选择 NE3
memory type 选择SRAM
address 选择10位的地址长度
data选择16位数据长度
然后不用勾选byte enable,不用高低位字节操作
使能write oporation 因为开了这个才能写数据
使能writefifo可以进行快速读写
下面两个图来自于W5300的数据手册,描述了地址建立时间和数据建立时间还有保持时间,一般就用到Address setup time、Address hold time、data setup time以及data hold time,我看到很多文章没有说明这个怎么来的,这个地方是根据你的系统时钟计算出来的,我在cubemx里面配置了160M的时钟频率。
下图中Address setup time最大需要7ns,那就直接用7ns来计算我们需要配置的数
7ns/(1/160M)=1.12,因此cubemx里面的address setuo time hclk可以配置为0或者1,其他类似。
6、配置串口
7、配置供电方式
配置为SMPS
8、生成代码
2、以下是keil5的编写过程
1、需要添加的文件及描述
IO_DEFINE.h 用于定义相应的管脚信息
#ifndef __IO_DEFINE_H_
#define __IO_DEFINE_H_
/******************************W5300接口定义********************************************/
/* W5300中断输入口定义PD3 */
#define W5300_INT GPIO_PIN_3
/* 对W5300复位信号输出口定义 PF7*/
#define W5300_RST GPIO_PIN_7
/* 数字信号输入口定义 */
#define D_INPUT1 GPIO_PIN_6
#define D_INPUT2 GPIO_PIN_7
/* 数字信号输出口定义 */
#define D_OUTPUT1 GPIO_PIN_11
#define D_OUTPUT2 GPIO_PIN_12
/* 以太网连接状态输入口定义 */
#define L_LINK GPIO_PIN_6
/* W5300片选定义 */
#define W5300_CS_NE3 GPIO_PIN_10//GPIO_PG10
/* W5300读使能定义 */
#define W5300_NOE PIO_PIN_4//GPIO_PD4
/* W5300写使能定义 */
#define W5300_NWE PIO_PIN_5//GPIO_PD5
/******************************SRAM接口定义********************************************/
#define SRAM_CS_NE1 GPIO_PIN_7//GPIO_PD7
#define SRAM_NBL0 GPIO_PIN_0//GPIO_PE0
#define SRAM_NBL1 GPIO_PIN_15//GPIO_PB15
/******************************其他引脚定义********************************************/
#define AMP_POWER GPIO_PIN_8//GPIO_PC8
#define IMX_POWER GPIO_PIN_9//GPIO_PC9
#define IMX_INT GPIO_PIN_5//GPIO_PA5
#define IMX_WAKE GPIO_PIN_4//GPIO_PA4
#endif
UDP_TR.H UDP头文件
#ifndef __UDP_TR_H
#define __UDP_TR_H
#include <stdint.h>
void W5300_Config(void);
void close(unsigned char sn);
unsigned short W5300_UDP_Loopback(unsigned char sn,unsigned short port,unsigned char Dip[],unsigned short Dport, unsigned short *buf);
void Read_sn_IR(unsigned char sn);
unsigned short Udp_rx_process(unsigned char sn, unsigned short buf[]);
void Udp_tx_process(unsigned char sn,unsigned char Dip[],unsigned short Dport, unsigned short buf[],unsigned short tx_size);
unsigned char Wait_before_send(unsigned sn);
unsigned int Socket_sn_UDP(unsigned char sn,unsigned short port,unsigned char Dip[],unsigned short Dport);
#endif
wizchip_conf.h wiznet官方的配置文件
W5300.h 官方的5300寄存器头文件
2、主函数编写
主要是进行W5300的初始化以及调用回环函数进行数据通信
W5300_Config();
while (1)
{
if(HAL_GPIO_ReadPin(W5300_LINK_GPIO_Port, L_LINK)==1) // LINK——LED 为高电平
{
for(i=0;i<8;i++)
{
close(i);
}
}
else
{
W5300_UDP_Loopback(0,1000,Dip,1000, S_Buffer); //socket号,本机端口,目的IP,目的端口,收发缓存
}
}
下面是main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
//#include "Device.h"
#include "IO_define.h" /* EVB IO definition */
#include "W5300.h"
#include "UDP_TR.h"
#include "wizchip_conf.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "SRAM1.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 ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
UART_HandleTypeDef huart1;
SRAM_HandleTypeDef hsram1;
SRAM_HandleTypeDef hsram3;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void SystemPower_Config(void);
static void MX_GPIO_Init(void);
static void MX_FMC_Init(void);
static void MX_ICACHE_Init(void);
static void MX_MEMORYMAP_Init(void);
static void MX_TIM2_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
/**************************************************/
/*****************UDP使用变量********************/
unsigned int Timer2_Counter;
unsigned int S0_Recv, Sn_SendOK[8], Sn_TimeOut[8];
unsigned short S_Buffer[800];
unsigned short UDP_Preamble[4];
/**************************************************/
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
unsigned char Dip[4]={192,168,0,40};//目的地址的IP,即PC端
unsigned short Dport = 1000;
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
int i;
/* USER CODE END 1 */
/* MCU Configuration------------s--------------------------------------------*/
/* 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();
/* Configure the System Power */
SystemPower_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_FMC_Init();
MX_ICACHE_Init();
MX_MEMORYMAP_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
W5300_Config();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
/* Infinite loop */
while (1)
{
if(HAL_GPIO_ReadPin(W5300_LINK_GPIO_Port, L_LINK)==1) // LINK——LED 为高电平
{
for(i=0;i<8;i++)
{
close(i);
}
}
else
{
W5300_UDP_Loopback(0,1000,Dip,1000, S_Buffer); //socket号,本机端口,目的IP,目的端口,收发缓存
}
}
/* USER CODE END 3 */
}
3、UDP_TR.C配置
主要用于配置UDP通信的函数
1、unsigned short W5300_UDP_Loopback(unsigned char sn,unsigned short port,unsigned char Dip[],unsigned short Dport, unsigned short *buf);
unsigned short W5300_UDP_Loopback(unsigned char sn,unsigned short port,unsigned char Dip[],unsigned short Dport, unsigned short *buf)
{
unsigned short *ptr;
unsigned short i;
unsigned short Recv_len;
unsigned short sn_dport;
unsigned char sn_dip[4];
Read_sn_IR(sn);//读取套接字中断寄存器的状态,以便后续根据中断状态采取相应的操作
ptr=(unsigned short*)Sn_SSR(sn);//看看当前socket是什么状态,已经打开还是关闭
i=*ptr;
i&=0x00ff;
switch(i)
{
case SOCK_UDP: // 套接字状态为 UDP
Recv_len=Udp_rx_process(sn,buf); // 接收数据
if(Recv_len<=0)
return 0;
// 以确保在发送数据之前,套接字已经处于可发送状态
if( Wait_before_send(sn)==0) //超时返回0
return 0;
//将目标 IP 地址和端口从 UDP_Preamble 数组中提取出来,然后调用 Udp_tx_process 函数来发送数据。
for(i=0;i<4;i++)
{
sn_dip[i]=Dip[i];
}
// sn_dip[0]=(unsigned char)(UDP_Preamble[0]/256);
// sn_dip[1]=(unsigned char)(UDP_Preamble[0]%256);
// sn_dip[2]=(unsigned char)(UDP_Preamble[1]/256);
// sn_dip[3]=(unsigned char)(UDP_Preamble[1]%256);
sn_dport = Dport;
// while(1)
// {
Udp_tx_process(sn,sn_dip,sn_dport, zm_Buffer,sizeof(zm_Buffer));// 发送数据
// }
break;
case SOCK_CLOSED: // SOCKET0不是UDP模式,处于关闭状态
//调用 Socket_sn_UDP 函数以配置 UDP 套接字参数,指定本地端口和目标 IP 地址及端口。
Socket_sn_UDP(sn,port,Dip,Dport);
//将套接字的已经准备好发送数据标志为 Sn_SendOK 设置为 1,同时将超时标志 Sn_TimeOut 设置为 0。
Sn_SendOK[sn]=1;//已经准备好发送数据标志
Sn_TimeOut[sn]=0;
break;
default: break;
}
return 1;
}
通过Sn_SSR(sn)等待标志位变为SOCK_UDP,就可以进行UDP读写,如果是SOCK_CLOSED的话需要新建一个socket来进行通信。
2、void Read_sn_IR(unsigned char sn);读取Sn_IR中断寄存器,判断socket0的状态,是否已连接或者超时
void Read_sn_IR(unsigned char sn)
{
unsigned short *ptr;
unsigned short j;
ptr=(unsigned short*)Sn_IR(sn);//端口中断寄存器,主要用到以下几种中断
j=*ptr;
*ptr=j;
if(j&Sn_IR_CON)// 建立连接了
{
}
if(j&Sn_IR_RECV)// 接收到了数据产生中断
{
}
if(j&Sn_IR_DISCON)// 未建立连接
{
ptr=(unsigned short*)Sn_CR(sn);//SOCKETn命令寄存器
*ptr=Sn_CR_DISCON;// 赋予未建立连接的状态
}
if(j&Sn_IR_SEND_OK)// 已发送完成产生中断
Sn_SendOK[sn]=1;//发送已完成,可以进行下一次发送了
if(j&Sn_IR_TIMEOUT)// 已超时
Sn_TimeOut[sn]=1;
}
3、Udp_rx_process和Udp_tx_process用于收发数据
unsigned short Udp_rx_process(unsigned char sn, unsigned short buf[])
{
unsigned short *ptr;
unsigned short rx_size;
unsigned short i,j;
ptr=(unsigned short*)Sn_RX_RSR(sn);// 看一下接收了多少个字节
rx_size=*ptr++;
rx_size=*ptr;
if(rx_size==0) /* 没有接收数据,返回0 */
return 0;
ptr=(unsigned short*)Sn_RX_FIFOR(sn);//检查Sn_RX_RSR之后,主机才可以通过Sn_RX_FIFOR读取Sn_RX_RSR字节长度的数据,
//完整UDP数据包格式为;4字节IP,2字节端口,2字节数据长度,+实际数据
UDP_Preamble[0]=*ptr; // 收到数据的目的IP
//ptr++;//程序这个地方得删掉ptr++,是我自作聪明了
UDP_Preamble[1]=*ptr;// 收到数据的目的IP
//ptr++;
UDP_Preamble[2]=*ptr;// 收到数据的目的端口
//ptr++;
UDP_Preamble[3]=*ptr;// 本包数据长度
rx_size= UDP_Preamble[3];
if(rx_size&0x0001)
i=(rx_size+1)/2;
else
i=rx_size/2;
for(j=0;j<i;j++)
buf[j]=*ptr;
ptr=(unsigned short*)Sn_CR(sn); /* Set RECV command */
*ptr=Sn_CR_RECV;// 更新接收寄存器额指针,开始接收数据
return rx_size;
}
/*函数作用:UDP发送函数
*输入参数:
sn 套接字编号
目的ip 端口
buf发送数据缓冲区
size数据大小
*/
void Udp_tx_process(unsigned char sn,unsigned char Dip[],unsigned short Dport, unsigned short buf[],unsigned short tx_size)
{
unsigned short *ptr;
unsigned short i,j;
/* 目的地址IP寄存器 */
ptr=(unsigned short*)Sn_DIPR(sn);
*ptr++=(unsigned short)(Dip[0]*256)+(unsigned short)Dip[1];
*ptr=(unsigned short)(Dip[2]*256)+(unsigned short)Dip[3];
/* 目的地址端口寄存器 */
ptr=(unsigned short*)Sn_DPORTR(sn);
*ptr=Dport;
if(tx_size&0x0001)
i=(tx_size+1)/2;
else
i=tx_size/2;
/* 发送FIFO缓冲区寄存器 */
ptr=(unsigned short*)Sn_TX_FIFOR(sn);
for(j=0;j<i;j++)
*ptr=buf[j];
//发送数据字节数寄存器
ptr=(unsigned short*)Sn_TX_WRSR(sn);
*ptr++=0;
*ptr=tx_size;
//数据已发送
ptr=(unsigned short*)Sn_CR(sn);
*ptr=Sn_CR_SEND;
}
4、Socket_sn_UDP用于新建socket
unsigned int Socket_sn_UDP(unsigned char sn,unsigned short port,unsigned char Dip[],unsigned short Dport)
{
unsigned short *ptr;
unsigned short i;
/* 设置板子的端口号 */
ptr=(unsigned short*)Sn_PORTR(sn);
*ptr=port;
/* 设置目的地址的IP */
ptr=(unsigned short*)Sn_DIPR(sn);
*ptr++=(unsigned short)(Dip[0]*256)+(unsigned short)Dip[1];
*ptr=(unsigned short)(Dip[2]*256)+(unsigned short)Dip[3];
/* 设置目的地址的端口号 */
ptr=(unsigned short*)Sn_DPORTR(sn);
*ptr=Dport;
/* 设置套接字对大段大小,是TCP通信重要参数 */
ptr=(unsigned short*)Sn_MSSR(sn);
*ptr=1472;
/* 设置socket0为udp模式 */
ptr=(unsigned short*)Sn_MR(sn);
*ptr=Sn_MR_UDP;
/* 打开socket0 */
ptr=(unsigned short*)Sn_CR(sn);
*ptr=Sn_CR_OPEN;
// Delay(20);
HAL_Delay(20);
ptr=(unsigned short*)Sn_SSR(sn);
i=*ptr;
//查看此时的状态是否是UDP模式
if((i&0x00ff)!=SOCK_UDP)
{
ptr=(unsigned short*)Sn_CR(sn);
*ptr=Sn_CR_CLOSE;
return FALSE;
}
Sn_SendOK[sn]=1;
Sn_TimeOut[sn]=0;
return TRUE;
}
5、Wait_before_send用于发送数据前等待寄存器是否准备好
unsigned char Wait_before_send(unsigned sn)
{
//还没准备好要发送数据,没超时 则进while
while((Sn_SendOK[sn]==0)&&(Sn_TimeOut[sn]==0))//根据Read_sn_IR函数可以知道他的状态
{
Read_sn_IR(sn);
if(Sn_TimeOut[sn])
{
Sn_TimeOut[sn] =0;
return 0;
}
else
Sn_SendOK[sn]=0;
}
return 1;//(Sn_SendOK[sn]==1)就直接返回
}
6、close用于关闭当前socket
void close(unsigned char sn)
{
unsigned short *ptr;
unsigned short i;
do
{
ptr=(unsigned short*)Sn_CR(sn); //关闭使用的socket Sn_CR(0) 括号内是socket号
*ptr=Sn_CR_CLOSE;
ptr=(unsigned short*)Sn_SSR(sn);//SOCKETn 状态寄存器
i=*ptr;
i&=0x00ff;
}while(i!=SOCK_CLOSED);
}
3、下载验证
最后可以将程序下载到板子里,进行UDP通信测试
ping一下板子
用网络调试助手,PC发送1,板子回复1-10,因为数据类型是short
用wireshark抓包测试,网速大概86M,感觉还行吧