一、前情提要

最近接触到了W5500,自己前后跑通也花了两天时间,之前做的硬件开发一直没有留痕,顾从这个项目开始决定写博客留痕

二、硬件配置

1.芯片型号

STM32L475VET6

2.开发软件

STM32CubeMx+keil5

3.STM32CubeMx配置

首先配置时钟源,我这里采用的是外部8M的高速时钟源。

选择HSE外部时钟源,锁相环倍频等参数如图所示

因为这款芯片的最大工作频率是80M,我直接给上最大了。

然后将对应的外部时钟引脚,UART串口引脚(方便调试)以及SPI通信用的引脚配置好。

我这里SPI片选用的是软件片选,所以对应的IO口设置成推挽输出就可以。

SPI配置的是8位数据传输,片选是低电平空闲,上升沿有效,下面是具体的工作模式。SPI的工作模式至少要时钟源二分频,所以最大是40M。

三、官方库移植

1.W5500官方库移植

https://github.com/Wiznet/ioLibrary_Driver

这是Wiznet官方给出的驱动库,里面已经定义好了相关的底层IO以及函数,我们只需要配置一些空的实现具体移植的函数即可。

下载好这个包后只需将Ethernet复制到你的对应工程文件夹下,然后在keil里面添加对应C文件和H文件路径。

只需要添加这三个文件即可。

四、软件代码

接下来进行具体代码的编写

首先打开wizchip_conf.c这个文件,包含对应的头文件

#include "wizchip_conf.h"
#include "main.h"
#include "stm32l4xx.h"
#include "stm32l4xx_hal.h"
#include "spi.h"

然后编写需要用到的几个函数

void 	  wizchip_cris_enter(void)           {__set_PRIMASK(1);}//关中断进入临界区
void 	  wizchip_cris_exit(void)            {__set_PRIMASK(0);}//开中断进入临界区

void 	wizchip_cs_select(void)            {HAL_GPIO_WritePin(W5500_CS_GPIO_Port, W5500_CS_Pin, GPIO_PIN_RESET);}//片选
void 	wizchip_cs_deselect(void)          {HAL_GPIO_WritePin(W5500_CS_GPIO_Port, W5500_CS_Pin, GPIO_PIN_SET);}//关片选

uint8_t wizchip_spi_readbyte(void)//SPI读取单字节        
{
	uint8_t data;
	if(HAL_SPI_Receive(&hspi2,&data,1,1000) != HAL_OK)
	{
		data = 0;
	}
	return data;
}
void 	wizchip_spi_writebyte(uint8_t wb)//SPI写单字节 
{
	HAL_SPI_Transmit(&hspi2,&wb,1,1000);
}
void 	wizchip_spi_readburst(uint8_t* pBuf, uint16_t len)//SPI读多字节 
{
	if (!pBuf) 
	{
		return;
  }
	
	HAL_SPI_Receive(&hspi2, pBuf, len, 1000);
}
void 	wizchip_spi_writeburst(uint8_t* pBuf, uint16_t len)//SPI写多字节 
{
	if (!pBuf) 
	{
		return;
  }
	
  HAL_SPI_Transmit(&hspi2, pBuf, len, 1000);
}

这几个函数在wizchip_conf.c已经定义好了,只需要找到并且填入对应的内容就行。

//函数绑定
void w5500_regFunc(void)
{
	reg_wizchip_cris_cbfunc(wizchip_cris_enter, wizchip_cris_exit);
	reg_wizchip_cs_cbfunc(wizchip_cs_select, wizchip_cs_deselect);
	reg_wizchip_spi_cbfunc(wizchip_spi_readbyte, wizchip_spi_writebyte);
	reg_wizchip_spiburst_cbfunc(wizchip_spi_readburst, wizchip_spi_writeburst);
}

然后需要自己写一个函数绑定的函数,如果你写在wizchip_conf.c里,记得在wizchip_conf.h进行函数声明,写在主函数也可以。

函数配置完成以后就可以进行w5500配置了。

/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "w5500.h"
#include "wizchip_conf.h"
#include "string.h"
#include "socket.h"
/* USER CODE END Includes */

老规矩,先记得引用头文件

/* USER CODE BEGIN 0 */

wiz_NetInfo gWIZNETINFO = { .mac = {0x78,0x83,0x68,0x88,0x56,0x72},
                            .ip =  {192,168,3,77},
                            .sn =  {255,255,255,0},
                            .gw =  {192,168,3,1},
							.dns = {8,8,8,8},
							.dhcp = NETINFO_STATIC};

static void W5500_RESET(void)
{
	HAL_GPIO_WritePin(W5500_RST_GPIO_Port, W5500_RST_Pin, GPIO_PIN_SET);
	HAL_Delay(10);
	HAL_GPIO_WritePin(W5500_RST_GPIO_Port, W5500_RST_Pin, GPIO_PIN_RESET);
	HAL_Delay(50);
	HAL_GPIO_WritePin(W5500_RST_GPIO_Port, W5500_RST_Pin, GPIO_PIN_SET);
	HAL_Delay(10);
}

//初始化芯片参数
void ChipParametersConfiguration(void)
{
    uint8_t tmp;
    uint8_t memsize[2][8] = {{2,2,2,2,2,2,2,2},{2,2,2,2,2,2,2,2}};
    if(ctlwizchip(CW_INIT_WIZCHIP,(void*)memsize) == -1)
    {
				printf("WIZCHIP Initialized fail.\r\n");
        while(1);
    }

    do{
        if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp) == -1)
        {
					printf("Unknown PHY Link stauts.\r\n");
          while(1);
        }
    }while(tmp == PHY_LINK_OFF);
}

//初始化网络参数 mac ip等
void NetworkParameterConfiguration(void)  //Intialize the network information to be used in WIZCHIP
{
  uint8_t tmpstr[6];

  ctlnetwork(CN_SET_NETINFO, (void*)&gWIZNETINFO);
  ctlnetwork(CN_GET_NETINFO, (void*)&gWIZNETINFO);
  ctlwizchip(CW_GET_ID,(void*)tmpstr);
	
	printf("\r\n=== %s NET CONF ===\r\n",(char*)tmpstr);
	printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n",gWIZNETINFO.mac[0],gWIZNETINFO.mac[1],gWIZNETINFO.mac[2],gWIZNETINFO.mac[3],gWIZNETINFO.mac[4],gWIZNETINFO.mac[5]);
	printf("SIP: %d.%d.%d.%d\r\n", gWIZNETINFO.ip[0],gWIZNETINFO.ip[1],gWIZNETINFO.ip[2],gWIZNETINFO.ip[3]);
	printf("GAR: %d.%d.%d.%d\r\n", gWIZNETINFO.gw[0],gWIZNETINFO.gw[1],gWIZNETINFO.gw[2],gWIZNETINFO.gw[3]);
	printf("SUB: %d.%d.%d.%d\r\n", gWIZNETINFO.sn[0],gWIZNETINFO.sn[1],gWIZNETINFO.sn[2],gWIZNETINFO.sn[3]);
	printf("DNS: %d.%d.%d.%d\r\n", gWIZNETINFO.dns[0],gWIZNETINFO.dns[1],gWIZNETINFO.dns[2],gWIZNETINFO.dns[3]);
	printf("======================\r\n");

}


void W5500_ChipInit(void)
{
	W5500_RESET();//硬件复位W5500
	w5500_regFunc();//绑定SPI函数
	
	ChipParametersConfiguration();
	NetworkParameterConfiguration();
}
/* USER CODE END 0 */

通过以上函数就能对W5500实现初始化。

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */
	
  /* 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_LPUART1_UART_Init();
  MX_SPI2_Init();
  /* USER CODE BEGIN 2 */
	
	W5500_ChipInit();//初始化W5500
	
	uint8_t remote_ip[4]={192,168,3,76};
	uint16_t remote_port=9090;
	uint16_t local_port=6060;

	uint16_t len=0;
	
  uint8_t buf[]="Tx";
	uint8_t buffer[128];
//	buf[0] = 0x55;
//	buf[1] = 0xAA;
//	uint16_t leng = 2;
	

  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	while(1)																 		// Socket状态机
	{
		switch(getSn_SR(0))															// 获取socket0的状态
		{
			case SOCK_UDP:												   			// Socket处于初始化完成(打开)状态
//				printf("Open Socket sucessful.\r\n");
//				sendto(0,buffer,sizeof(buffer), remote_ip, remote_port);
//				printf("Send data sucessful.\r\n");
				HAL_Delay(100);
				if(getSn_IR(0) & Sn_IR_RECV)
				{
					setSn_IR(0, Sn_IR_RECV);								 			// Sn_IR的RECV位置1
				}
																								// 数据回环测试程序:数据从远程上位机发给W5500,W5500接收到数据后再回给远程上位机
				if((len=getSn_RX_RSR(0))>0)
				{ 
					memset(buffer,0,len+1);
					recvfrom(0,buffer, sizeof(buffer), remote_ip,&remote_port);					// W5500接收来自远程上位机的数据,并通过SPI发送给MCU
					printf("receive data sucessful.\r\n");
					printf("%s\r\n",buffer);							   									// 串口打印接收到的数据
					sendto(0,buffer,sizeof(buffer), remote_ip, remote_port);		  				// 接收到数据后再回给远程上位机,完成数据回传
				}
			break;
			case SOCK_CLOSED:														// Socket处于关闭状态
				socket(0,Sn_MR_UDP,local_port,0);									// 打开Socket0,并配置为UDP模式,打开一个本地端口
				printf("Open Socket failed.\r\n");
			break;
		}
	}
  /* USER CODE END 3 */
}

主函数先进行发送测试,然后进行回调测试。

五、电脑配置

网口和电脑正确连线后,打开控制面板->网络和internet->网络和共享中心->更改适配器设置->对应的以太网

双击这个

输入你设置好的相应参数,不会找Ip可以输入cmd打开命令行面板输入ipconfig -all查看ip信息,查看端口是否被占用输入netstat -aon|findstr "端口"就可以。

注意!

1.这里输入的ip地址是你电脑的ip,也是你设置的remote_ip,即远程ip,千万别设置成w5500的本地ip,我当时在这踩了坑。

2.电脑ip和W5500的ip需要在同一网段,比如192.168.3.x,除了x,其它都要相同,mac地址符合规则就行,网关可以都用电脑的默认网关,DNS无所谓,直接填阿里云的8.8.8.8就行

3.关闭电脑防火墙,不然可能出现时不时断一下的情况

以上工作都完成后,对板子进行烧录复位,在命令行输入ping w5500ip

成功ping通后,打开网络调试助手,输入对应ip和端口

通信成功!

作者:我不南郭

物联沃分享整理
物联沃-IOTWORD物联网 » STM32+W5500实现UDP通信

发表回复