STM32+W5500网络通信

一、Modbus/TCP协议

在这里插入图片描述
在这里插入图片描述

1.查询报文

00 6D 00 00 00 06 01 03 00 00 00 01
00 6D 查询编号
00 00 协议
00 06 数据包长度
01 设备编号
03 功能码
00 00 起始地址
00 01 查询寄存器个数

2.响应报文

00 6D 00 00 00 05 01 03 02 00 17
00 6D 查询编号
00 00 协议
00 05 数据包长度
01 设备地址
03 功能码
02 数据长度
00 17 数据值

二、代码

1.初始化从机网络

void Load_Net_Parameters(void)
{
	Gateway_IP[0] = 192;//加载网关参数
	Gateway_IP[1] = 168;
	Gateway_IP[2] = 1;
	Gateway_IP[3] = 1;

	Sub_Mask[0]=255;//加载子网掩码
	Sub_Mask[1]=255;
	Sub_Mask[2]=255;
	Sub_Mask[3]=0;

	Phy_Addr[0]=0x0c;//加载物理地址
	Phy_Addr[1]=0x29;
	Phy_Addr[2]=0xab;
	Phy_Addr[3]=0x7c;
	Phy_Addr[4]=0x00;
	Phy_Addr[5]=0x01;

	IP_Addr[0]=192;//加载本机IP地址
	IP_Addr[1]=168;
	IP_Addr[2]=1;
	IP_Addr[3]=199;

	S0_Port[0] = 0x13;//加载端口0的端口号5000 
	S0_Port[1] = 0x88;

	S0_Mode=TCP_SERVER;//加载端口0的工作模式,TCP服务端模式
}

2.简单响应函数

返回一个全局变量data,并且每次响应后自增1。

void Process_Socket_Data(SOCKET s)
{
	int len;
	unsigned char msg[11]={0x00,0x00,0x00 ,0x00, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00, 0x70};
	len=sizeof(msg);
	unsigned short size;
	size=Read_SOCK_Data_Buffer(s, Rx_Buffer);
	memcpy(Tx_Buffer, Rx_Buffer, size);
	
	//打印查询报文
	for (int j=0;j<size;j++){
		 printf("0x%02X ",Tx_Buffer[j]);
	}

	//写响应报文
	//检验码
	msg[0]=Tx_Buffer[0];
	msg[1]=Tx_Buffer[1];
	
	//协议
	msg[2]=0x00;
	msg[3]=0x00;
	
	//数据包长度
	msg[4]=0x00;
	msg[5]=0x05;
	
	//设备编号
	msg[6]=Tx_Buffer[6];
	//功能码
	msg[7]=Tx_Buffer[7];
	//数据长度
	msg[8]=0x02;
	
	//低八位
	msg[10]=data&0XFF;
	//高八位
	msg[9]=data>>8;
	
	memcpy(Tx_Buffer, msg, len);	
	//发送响应报文
	Write_SOCK_Data_Buffer(0, Tx_Buffer, len);
	data++;
}

3.main函数循环等待连接

 while (1)
  {
		W5500_Socket_Set();//W5500端口初始化配置

		W5500_Interrupt_Process();//W5500中断处理程序框架
		
		if((S0_Data & S_RECEIVE) == S_RECEIVE)//如果Socket0接收到数据
		{
			S0_Data&=~S_RECEIVE;
			Process_Socket_Data(0);//W5500接收并发送接收到的数据
		}
		//从机状态标志
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);

  }

三、结果

使用modbus poll建立连接
设置id、地址、读取位数
在这里插入图片描述
在这里插入图片描述
成功读取到data的值
在这里插入图片描述

四、参考

https://blog.csdn.net/qq_47281915/article/details/121927294?spm=1001.2014.3001.5501

版权声明:本文为CSDN博主「Ivan@Xiang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xyf_fate/article/details/122146471

一、W5500介绍

W5500 芯片是一款集成全硬件 TCP/IP 协议栈的嵌入式以太网控制器,同时也是一颗工业级以太网控制芯片。 是韩国WIZnet(微知纳特)发布全硬件TCP/IP协议栈以太网接口芯片。
W5500 支持高速标准4线SPI接口与主机进行通信,该 SPI 速率理论上可以达到 80MHz。其内部还集成了以太网数据链路层(MAC)和10BaseT/100BaseTX 以太网物理层(PHY),支持自动协商(10/100-Based全双工/半双工)、掉电模式和网络唤醒功能。与传统软件协议栈不同,W5500内嵌的8个独立硬件 Socket 可以进行8路独立通信,该8路Socket的通信效率互不影响,可以通过 W5500 片上32K 字节的收/发缓存灵活定义各个Socket的大小。

二、资源分享

1.链接:https://pan.baidu.com/s/1DzLA2Vp_Qwon9VWQdQ6g5Q

提取码:mvwi

2.文件内容
在这里插入图片描述
需要安装调试软件,可以按照教程去做例子!

三、例子程序

1.STM32与W5500接线方法

PA15->W5500_RST(源程序使用的是PC5,这里没有该引脚修改为PA15)

PC4->W5500_INT(使用寄存器查询方式的例程时,此引脚可以不接,其他例程可能涉及修改引脚)

PA4->W5500_SCS

PA5->W5500_SCK

PA6->W5500_MISO

PA7->W5500_MOSI

具体例程的调试过程请参考压缩包中NiRen_W5500模块用户手册(用Adobe Reader打开).pdf文件。

2.这里我调试了查询寄存器方式中的客户端模式例程,调试结果如下:

在这里插入图片描述

四、实现应用层modbus、httpd协议编程

(一)modbus

main函数:
在这里插入图片描述
在main函数中有w5500的配置函数 W5500_Configuration() ,在配置函数中可以修改自己的IP地址及网关信息等

(二)HTTPS

main函数
在这里插入图片描述

GPIO初始化

void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO , ENABLE);
  // Port A output
  GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0|GPIO_Pin_1| GPIO_Pin_2 |GPIO_Pin_3; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
//  GPIO_ResetBits(GPIOA, GPIO_Pin_0);
//  GPIO_ResetBits(GPIOA, GPIO_Pin_1);
//  GPIO_SetBits(GPIOA, GPIO_Pin_2); // led off
//  GPIO_SetBits(GPIOA, GPIO_Pin_3); // led off
  // Port B output;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB, GPIO_Pin_9);
  // Port C input
//  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
//  GPIO_Init(GPIOC, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//控制flash
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB, GPIO_Pin_7);	
}

W5500配置

void WIZ_SPI_Init(void)
{
	SPI_InitTypeDef   SPI_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO , ENABLE);	
  // Port B output
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB, GPIO_Pin_12);
  /* Configure SPIy pins: SCK, MISO and MOSI */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	  /* SPI Config -------------------------------------------------------------*/
	  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
	  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	  SPI_InitStructure.SPI_CRCPolynomial = 7;

	  SPI_Init(SPI2, &SPI_InitStructure);
	  SPI_Cmd(SPI2, ENABLE);
}

五、总结

学习了W5500网络模块的特点,实现了完成TCP数据通信,了解了应用层modbus、httpd(web服务)协议编程

六、参考链接

https://blog.csdn.net/qq_45659777/article/details/121952778

七、modbus和HTTPS源码

modbus:链接:https://pan.baidu.com/s/10VrYkhB0dTRqNc83EKRzag

提取码:r3pe

https:链接:https://pan.baidu.com/s/1-M5QIznHG9nnXtyigtBiDA

提取码:jf6u

版权声明:本文为CSDN博主「死妖阿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_49297422/article/details/122200457

一、W5500以太网模块

1.1 简介

  • NiRen_W5500模块是一款基于WIZnet W5500芯片的以太网模块,是泥人电子继 NiRen_W5100模块后设计的一块性能更好、性价比更高的以太网模块。模块集成硬件化TCP/IP协议;内部32K字节存储器作TX/RX缓存;支持10/100Mbps 的传输速率;支持8个独立端口同时运行;同时模块还支持3.3V或5V电源供电,5V供电时还可以输出3.3V电源,方便用户在不同的单片机系统中使用;模块与单片机系统的通讯方式是简单、方便的SPI通信。

1.2 模块排针功能表

在这里插入图片描述

1.3 W5500芯片资源介绍

  • W5500芯片是一种采用全硬件TCP/IP协议栈的嵌入式以太网控制器,它能使嵌入式系统通过SPI(串行外设接口)接口轻松地连接到网络。W5500特别适合那些需要使用单片机来实现互联网功能的客户,而这就需要单片机系统具有完整的TCP/IP协议栈和10/100Mbps 以太网网络层(MAC)和物理层(PHY)。W5500是由已经通过市场考验的全硬件TCP/IP协议栈、及以太网网络层和物理层的整合而成。其全硬件的TCP/IP协议栈全程支持TCP、UDP、IPv4、ICMP、ARP、IGMP和 PPPoE协议,而且已经连续多年在各种实际应用中得以证明。W5500使用32KB缓存作为其数据通信内存。通过使用W5500,用户只需通过使用一个简单的socket程序就能实现以太网的应用,而不再需要处理一个复杂的以太网控制器了。SPI(串行外设接口)提供了轻松与外部MCU连接的接口。W5500支持高达8OMHZ的SPI 接口间通信。为了降低系统功率的消耗,W5500提供了网络唤醒和休眠模式。W5500收到原始以太网数据包形式的magic packet时将被唤醒。

1.4 接线方式

在这里插入图片描述

1.5代码调试

  • 在代码中添加本地ip地址、掩码及网关。
#include "stm32f10x.h"		
#include "W5500.h"			
#include <string.h>

void RCC_Configuration(void);		//设置系统时钟为72MHZ(这个可以根据需要改)
void NVIC_Configuration(void);		//STM32中断向量表配配置
void Timer2_Init_Config(void);		//Timer2初始化配置
void System_Initialization(void);	//STM32系统初始化函数(初始化STM32时钟及外设)
void Delay(unsigned int d);			//延时函数(ms)

unsigned int Timer2_Counter=0; //Timer2定时器计数变量(ms)
unsigned int W5500_Send_Delay_Counter=0; //W5500发送延时计数变量(ms)

/*******************************************************************************
* 函数名  : W5500_Initialization
* 描述    : W5500初始货配置
* 输入    : 无
* 输出    : 无
* 返回值  : 无
* 说明    : 无
*******************************************************************************/
void W5500_Initialization(void)
{
	W5500_Init();		//初始化W5500寄存器函数
	Detect_Gateway();	//检查网关服务器 
	Socket_Init(0);		//指定Socket(0~7)初始化,初始化端口0
}

/*******************************************************************************
* 函数名  : Load_Net_Parameters
* 描述    : 装载网络参数
* 输入    : 无
* 输出    : 无
* 返回值  : 无
* 说明    : 网关、掩码、物理地址、本机IP地址、端口号、目的IP地址、目的端口号、端口工作模式
*******************************************************************************/
void Load_Net_Parameters(void)
{
	Gateway_IP[0] = 192;//加载网关参数
	Gateway_IP[1] = 168;
	Gateway_IP[2] = 0;
	Gateway_IP[3] = 1;

	Sub_Mask[0]=255;//加载子网掩码
	Sub_Mask[1]=255;
	Sub_Mask[2]=255;
	Sub_Mask[3]=0;

	Phy_Addr[0]=0x34;//加载物理地址
	Phy_Addr[1]=0x4b;
	Phy_Addr[2]=0x50;
	Phy_Addr[3]=0x00;
	Phy_Addr[4]=0x00;
	Phy_Addr[5]=0x00;

	IP_Addr[0]=192;//加载本机IP地址
	IP_Addr[1]=168;
	IP_Addr[2]=0;
	IP_Addr[3]=178;

	S0_Port[0] = 0x13;//加载端口0的端口号5000 
	S0_Port[1] = 0x88;

	S0_DIP[0]=192;//加载端口0的目的IP地址
	S0_DIP[1]=168;
	S0_DIP[2]=64;
	S0_DIP[3]=1;
	
	S0_DPort[0] = 0x17;//加载端口0的目的端口号6000
	S0_DPort[1] = 0x70;

	S0_Mode=TCP_CLIENT;//加载端口0的工作模式,TCP客户端模式
}

  • 将代码烧录及STM32F103开发板中,打开调试工具
    在这里插入图片描述

二、W5500实现modbus协议编程

  • Modbus TCP通讯首先需要下载W5500的驱动源码,可以到WIZnet的官网下载
    解压目录
  • 下载FreeModbus源码,然后放到自己的工程中
    在这里插入图片描述
  • 修改porttcp中的代码
static UCHAR    aucTCPBuf[MB_TCP_BUF_SIZE];	  
BOOL
xMBTCPPortInit( USHORT usTCPPort )
{
    BOOL bOkay = FALSE;
     
    // 侦听端口 Modbus-TCP 端口
	socket_init(SOCK_TCP_PORT,Sn_MR_TCP_TCP,local_tcp_port++,Sn_MR_ND_TCP);
	listen_tcp_socket(SOCK_TCP_PORT);
    
    bOkay = TRUE;
    return bOkay;
}
BOOL
xMBPortTCPPool( void )
{  
	unsigned short int us_rlen;
	unsigned char i;
	i=get_tcp_socket_state(SOCK_TCP_PORT);

	if(i==SOCK_ESTABLISHED_TCP)
	{
		if(get_tcp_socket_irq(SOCK_TCP_PORT) & Sn_IR_CON_TCP)		//查看中断有没有发生
		{
			clear_tcp_socket_irq(SOCK_TCP_PORT, Sn_IR_CON_TCP); 	/*清除接收中断标志位*/						         
		}
		us_rlen=get_tcp_rx_buffer_size(SOCK_TCP_PORT); 				//获取接收到的数据字节				  	         /*定义len为已接收数据的长度*/
		if(us_rlen==0)												//没有接收到数据
			return FALSE;											//返回
		else
		{
			recv_tcp_socket_data(SOCK_TCP_PORT,aucTCPBuf,us_rlen); 	//接收数据						   		         /*接收来自Server的数据*/
			printf("receive\r\n");
			usTCPBufLen=us_rlen;
		}
		( void )xMBPortEventPost( EV_FRAME_RECEIVED );			//发送已接收到新数据到Modbus-TCP状态机
	}
	else if(i==SOCK_CLOSED_TCP)									//如果socket关闭
	{
		socket_init(SOCK_TCP_PORT,Sn_MR_TCP_TCP,local_tcp_port++,Sn_MR_ND_TCP);//重新初始化
	}
	else if(i==SOCK_INIT_TCP)								//如果socket初始化完毕,监听端口
	{
		listen_tcp_socket(SOCK_TCP_PORT);
	}
	else if(i==SOCK_CLOSE_WAIT_TCP)							//如果socket等待关闭,关闭socket连接
	{
		close_tcp_socket(SOCK_TCP_PORT);
	}

	return TRUE;
}



三、W5500实现web服务协议编程

3.1基本原理

  • 实现的web服务的功能,STM32 W5500配置入网后,通过DHCP动态获取IP地址,在电脑浏览器地址栏输入这个IP地址,可以获取到一个简单form表单的页面,在表单中输入数据,提交到W5500web服务,返回一个结果。
    在这里插入图片描述
int main(void)
{
	u32 dhcp_timestamp;
	u8 mac[6]={0, };
	DHCP_Get dhcp_get;
	u16 len;
	u8 buffer[BUFFER_SIZE];
	char http_method[16];
	char http_uri[64];
	char http_body[256];
	u8 res_code;
	
	systick_configuration();
	init_led();
	
	init_system_spi();
	func_w5500_reset();
	
	init_hardware_usart2_dma(9600);
	
	getMacByLockCode(mac);
	setSHAR(mac);
	
	sysinit(txsize, rxsize);
	setRTR(2000);
  setRCR(3);
	
	//DHCP
	for(;func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) != 0;);	
	if(func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) == 0)
	{
		setSUBR(dhcp_get.sub);
		setGAR(dhcp_get.gw);
		setSIPR(dhcp_get.lip);
		close(1);
	}
	dhcp_timestamp = get_systick_timestamp();
 
	for(;;)
	{
		if(get_systick_timestamp() - dhcp_timestamp > 59*1000)// 1 min dhcp
		{
			dhcp_timestamp = get_systick_timestamp();
			if(func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) == 0)
			{
				setSUBR(dhcp_get.sub);
				setGAR(dhcp_get.gw);
				setSIPR(dhcp_get.lip);
				close(1);
			}
		}
		
		switch(getSn_SR(SOCK_TCPS))
		{
			case SOCK_CLOSED:
				socket(SOCK_TCPS, Sn_MR_TCP, 80, Sn_MR_ND);
				break;
			case SOCK_INIT:
				listen(SOCK_TCPS);
				break;		
			case SOCK_ESTABLISHED:		
				if(getSn_IR(SOCK_TCPS) & Sn_IR_CON)
				{
					setSn_IR(SOCK_TCPS, Sn_IR_CON);
				}
				len = getSn_RX_RSR(SOCK_TCPS);
				if(len>0)
				{
					memset(buffer, 0, BUFFER_SIZE);
					len = recv(SOCK_TCPS, buffer, len);
					//analysis tcp msg, and package the feedback msg
					if(len > 0)
					{
						res_code = func_analysis_http_request(buffer, len, http_method, http_uri, http_body);
						memset(buffer, 0, sizeof(buffer));
						if(res_code == 0)
						{
							if(strcmp("GET", http_method) == 0 && strcmp("/", http_uri) == 0)
							{
								func_package_http_response(buffer, &len, sizeof(buffer), HTML_CONTENT, strlen(HTML_CONTENT));
								send(SOCK_TCPS, buffer, len);					
							}
							else if(strcmp("POST", http_method) == 0 && strcmp("/sn_config.action", http_uri) == 0)
							{
								func_package_http_response(buffer, &len, BUFFER_SIZE, HTML_RESULT_OK, strlen(HTML_RESULT_OK));
								send(SOCK_TCPS, buffer, len);
							}
							else
							{
								memcpy(buffer, TEXT_TEMPLATE_ERR, strlen(TEXT_TEMPLATE_ERR));
								send(SOCK_TCPS, buffer, strlen(TEXT_TEMPLATE_ERR));
							}
							disconnect(SOCK_TCPS);
						}
						else
						{
							memcpy(buffer, TEXT_TEMPLATE_ERR, strlen(TEXT_TEMPLATE_ERR));
							send(SOCK_TCPS, buffer, strlen(TEXT_TEMPLATE_ERR));
							disconnect(SOCK_TCPS);
						}
					}
					
				}
				break;
			case SOCK_CLOSE_WAIT:
				close(SOCK_TCPS);
				break;
		}
		
		func_led1_on();
		delay_ms(500);
		func_led1_off();
		delay_ms(500);
				
	}
}
 
u8 func_analysis_http_request(u8* buffer, u16 len_recv, char* method, char* uri, char* data_body)
{
	char chs[BUFFER_SIZE] = {0, };
	char *res, *end;
	if(len_recv > 0)
	{
		memcpy(chs, buffer, 3);
		res = strstr(chs, "GET");
		if(strcmp("GET", res) == 0)
		{
			memcpy(method, "GET", strlen("GET"));
			
		}
		else
		{
			memset(chs, 0, BUFFER_SIZE);
			memcpy(chs, buffer, 4);
			res = strstr(chs, "POST");
			if(strcmp("POST", res) == 0)
			{
				memcpy(method, "POST", strlen("POST"));
				
			}
			else
			{
				return 1;
			}
		}
		
		memset(chs, 0, BUFFER_SIZE);
		memcpy(chs, buffer, len_recv + 1);
		res = strchr(chs, '/');
		if(res != NULL)
		{
			end = strchr(res, ' ');
			if(end != NULL)
			{
				memcpy(uri, res, end - res);
			}			
		}
		
		memset(chs, 0, BUFFER_SIZE);
		memcpy(chs, buffer, len_recv + 1);
		res = strstr(chs, "\r\n\r\n");
		if(res != NULL)
		{
			if(strlen(res) > 4)
			{
				memcpy(data_body, res + 4, strlen(res) - 4);
			}			
		}
		
	}
	return 0;
}
 
u8 func_package_http_response(u8* buffer, u16 *len_ret, u16 len_buf, char* cont, u16 len_cont)
{
	memset(buffer, 0, BUFFER_SIZE);
	*len_ret = sprintf((char*)buffer, TEXT_TEMPLATE_OK, len_cont, cont);
	return 0;
}

版权声明:本文为CSDN博主「Sue hiroshi」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tauvan/article/details/122178518

W5500网络模块简介

W5500网络扩展板集成了一个硬件TCP/IP协议协芯片W5500以及一个含有网络变压器的RJ-45(HR911105A)。其中,W5500是一款全硬件TCP/IP嵌入式以太网控制器,以嵌入式系统提供了更加简易的互联网连接方案。

使用测试工具调试

使用TCP&UDP测试工具进行调试。
将W5500网络模块与STM32F103板子连接:
参考代码中的注释:
在这里插入图片描述
PC5引脚可以更换为其它引脚。
在keil中编译运行后,打开调试工具,在服务端模式即可有:
请添加图片描述

实现应用层modbus、httpd(web服务)协议编程

.1 modbus协议编程

main函数:
在这里插入图片描述
在main函数中有w5500的配置函数 W5500_Configuration() ,在配置函数中可以修改自己的IP地址及网关信息等。

.2 web协议编程

引脚初始化:

void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO , ENABLE);
  // Port A output
  GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0|GPIO_Pin_1| GPIO_Pin_2 |GPIO_Pin_3; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
//  GPIO_ResetBits(GPIOA, GPIO_Pin_0);
//  GPIO_ResetBits(GPIOA, GPIO_Pin_1);
//  GPIO_SetBits(GPIOA, GPIO_Pin_2); // led off
//  GPIO_SetBits(GPIOA, GPIO_Pin_3); // led off
  // Port B output;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB, GPIO_Pin_9);
  // Port C input
//  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
//  GPIO_Init(GPIOC, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//控制flash
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB, GPIO_Pin_7);	
}

串口初始化:

void USART1_Init(void)
{
  USART_InitTypeDef USART_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO , ENABLE);

	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  // Configure the USART1_Rx as input floating
  GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IN_FLOATING ;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	
  USART_InitStructure.USART_BaudRate = 115200;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No ;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //Enable rx enable, 
  
  /* Configure the USARTx */ 
  USART_Init(USART1, &USART_InitStructure);
  /* Enable the USARTx */
  USART_Cmd(USART1, ENABLE);
}

main函数:

int main(void)
{
	Systick_Init(72);
	GPIO_Configuration(); //GPIO configuration
	USART1_Init(); //115200@8-n-1
	printf("W5500 EVB initialization over.\r\n");
	Reset_W5500();
	WIZ_SPI_Init();
	printf("W5500 initialized!\r\n");
	if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7))
	{
		DefaultSet();//出厂值
	}
	else
	{
 		get_config();//read config data from flash
	}
	printf("Firmware ver%d.%d\r\n",ConfigMsg.sw_ver[0],ConfigMsg.sw_ver[1]);
	if(ConfigMsg.debug==0) ConfigMsg.debug=1;

	set_network();
	printf("Network is ready.\r\n");
	while(1)
	{
		if(ConfigMsg.JTXD_Control == 0)
		  	do_http();
		else
		  	JTXD_do_http();
		if(reboot_flag)
			NVIC_SystemReset();
//        reboot();
        
	}
}

源码:提取码:z56u

参考

https://blog.csdn.net/qq_45659777/article/details/121952778?spm=1001.2014.3001.5501

https://blog.csdn.net/tcjy1000/article/details/25474511

版权声明:本文为CSDN博主「Pilipalaaaaa」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_46359931/article/details/122146850

1. W5500模块实现数据通信

  1. 引脚初始化,此连线方式用于三个模块功能的整体实现
    在这里插入图片描述
  1. PA3 -> W5500_RST
  2. PA4 -> W5500_SCS
  3. PA5 -> W5500_SCK
  4. PA6 -> W5500_MISO
  5. PA7 -> W5500_MOSI

1. 基本原理

  1. 基本介绍
  1. Niren_W5500模块是一款基于WIZnet W5500芯片的以太网模块,是泥人电子继 Niren_W5100模块后设计的一块性能更好、性价比更高的以太网模块。模块集成硬件化TCP/IP协议:内部32K字节存储器作TX/RX
  2. 缓存:支持10/100Mbps的传输速率;支持8个独立端口同时运行;同时模块还支持3.3V或5V电源供电,5V供电时还可以输出3.3V电源,方便用户在不同的单片机系统中使用;模块与单片机系统的通讯方式是简单、方便的SPI通信。
  1. 实现思路
    • W5500内部是硬件TCP/IP协议栈,对外(MCU)只是提供了操作socket的能力,内部支持8个独立的socket,每一个socket通过Socket n寄存器区控制(0≤n≤7)。
    • 所以在编写基于Socket的网络应用程序时,可以按照查询Socket状态寄存器实现一个状态机的思路来实现。

2. 程序实现

  1. 修改main.c函数
    修改为根据本机ip的实际地址
    在这里插入图片描述
    • 可修改以太网本地ip地址,实现网络的整体搭建链接 在这里插入图片描述
      实验过程中使用固定ip,后改回默认
  2. SPI
void SPI_Configuration(void)
{
	GPIO_InitTypeDef 	GPIO_InitStructure;
	SPI_InitTypeDef   	SPI_InitStructure;	   

  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1 | RCC_APB2Periph_AFIO, ENABLE);	

	/* 初始化SCK、MISO、MOSI引脚 */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);

	/* 初始化CS引脚 */
	GPIO_InitStructure.GPIO_Pin = W5500_SCS;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_Init(W5500_SCS_PORT, &GPIO_InitStructure);
	GPIO_SetBits(W5500_SCS_PORT, W5500_SCS);

	/* 初始化配置STM32 SPI1 */
	SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;	//SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode=SPI_Mode_Master;							//设置为主SPI
	SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b;						//SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low;							//时钟悬空低
	SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge;							//数据捕获于第1个时钟沿
	SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;								//NSS由外部管脚管理
	SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_2;	//波特率预分频值为2
	SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;					//数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial=7;								//CRC多项式为7
	SPI_Init(SPI1,&SPI_InitStructure);									//根据SPI_InitStruct中指定的参数初始化外设SPI1寄存器

	SPI_Cmd(SPI1,ENABLE);	//STM32使能SPI1
}
  1. 程序运行,关闭虚拟端口,同时启动调试工具
    在这里插入图片描述
    修改默认ip,并连接网线,连接到设定网络,启动服务,测试tcp通信

    • 创建链接 在这里插入图片描述
    • 启动服务器 在这里插入图片描述

2. DHCP自动获取IP

1. 基本原理

  1. DHCP基本定义
    DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)。是一个局域网的网络协议,使用UDP协议工作。 DHCP主要有两个用途:
    • 给内部网络或网络服务供应商自动分配IP地址
    • 给用户或者内部网络管理员作为对所有计算机作中央管理的手段。
  2. 分配方式
    • 自动分配方式
      DHCP服务器为主机指定一个永久性的IP地址,一旦DHCP客户端第一次成功从DHCP服务器端租用到IP地址后,就可以永久性的使用该地址。
    • 动态分配方式
      DHCP服务器给主机指定一个具有时间限制的IP地址,时间到期或主机明确表示放弃该地址时,该地址可以被其他主机使用。手工分配方式(Manual Allocation),客户端的IP地址是由网络管理员指定的,DHCP服务器只是将指定的IP地址告诉客户端主机。
  3. ip获取
    在这里插入图片描述
    • 寻找DHCP服务器
    • 分配IP地址
    • 接受IP地址
    • IP地址分配确认
    • 获取到IP后广播ARP

2. 程序实现

  1. device.c函数配置地址
void set_network(void)
{
  uint8 ip[4];
  setSHAR(ConfigMsg.mac);/*配置Mac地址*/
  setSUBR(ConfigMsg.sub);/*配置子网掩码*/
  setGAR(ConfigMsg.gw);/*配置默认网关*/
  setSIPR(ConfigMsg.lip);/*配置Ip地址*/

  //Init. TX & RX Memory size of w5500
  sysinit(txsize, rxsize); /*初始化8个socket*/
  
  setRTR(2000);/*设置溢出时间值*/
  setRCR(3);/*设置最大重新发送次数*/
  

  getSIPR (ip);

  printf("IP : %d.%d.%d.%d\r\n", ip[0],ip[1],ip[2],ip[3]);

  getSUBR(ip);

  printf("SN : %d.%d.%d.%d\r\n", ip[0],ip[1],ip[2],ip[3]);

  getGAR(ip);

  printf("GW : %d.%d.%d.%d\r\n", ip[0],ip[1],ip[2],ip[3]);
 
}

默认ip地址
在这里插入图片描述

  1. lip:本机地址
  2. sub: 子网掩码
  3. gw: 默认网关
  1. 程序运行
    • 构建生成hex文件,烧录 在这里插入图片描述
    • 断电,boot0置0后,启动串口调试程序,使串口函数可以正常使用 在这里插入图片描述

3. modbus协议

1. 基本原理

  1. 定义
    Modbus是Modicon(施耐德)公司于1979年开发的串行通信协议。它最初设计用于公司的可编程逻辑控制器(PLC)。 Modbus是一种开放式协议
  • 公开发表并且无著作权要求;
  • 易于部署和维护;
  • 对供应商来说,修改移动本地的比特或字节没有很多限制;
  1. 版本
    • Modbus RTU(Remote Terminal Unit 远程终端单元):这种方式常采用RS-485做为物理层,一般利用芯片的串口实现数据报文的收发,报文数据采用二进制数据进行通信。

    • Modbus ASCII :报文使用 ASCII 字符。ASCII 格式使用纵向冗余校验和。Modbus ASCII 报文由冒号 (":")开始 和换行符 (CR/LF) 结尾构成。

    • Modbus TCP/IP 或 Modbus TCP :这是一种 Modbus 变体版本,使用 TCP/IP 网络进行通信,通过 502 端口进行连接。报文不需要校验和计算,因为以太网底层已经实现了CRC32 数据完整性校验。

    • Modbus over TCP/IP 或 Modbus over TCP 或 Modbus RTU/IP :这也是一种 Modbus 变体,与 Modbus TCP 的不同之处在于,与 Modbus RTU 一样,校验和包含在报文中。

    • Modbus UDP:也有在UDP上传输Modbus报文的,不过需要做错误重传机制,这么干的应该不多。

  2. 传输方式
    • 控制器能设置为两种传输模式(ASCII或RTU)中的任何一种在标准的Modbus网络通信。用户选择想要的模式,包括串口通信参数(波特率、校验方式等),在配置每个控制器的时候,在一个Modbus网络上的所有设备都必须选择相同的传输模式和串口参数。
      ASCII模式
      在这里插入图片描述
    • RTU模式
      在这里插入图片描述
  3. modbus表
    在这里插入图片描述
    在这里插入图片描述

2. 实现过程

  1. 思路
    • 实现TCP服务器,这个服务器用于在系统中轮询处理,从W5500获取数据和发送数据给W5500都需要通过这部分来实现。
    • TCP服务器得到数据后,我们需要解析数据,并根据解析的上位数据决定进一步的动作,还需要生成返回信息。这部分对应功能就是Modbus TCP服务器的实现。
    • 根据Modbus TCP服务器解析出的Modbus消息,需要决定下一步的动作,这个具体动作根据功能码的不同可能有不同需求,所以我们需要根据具体的要求实现不同功能码的动作。
  2. 程序实现
    main函数
int main(void)
{
	unsigned char i;

	/* Initialize STM32F103 */
	System_Initialization();//系统配置
	SysTick_Init();//启动系统滴答定时器 SysTick

	/* Config W5500 */
	W5500_Configuration();//W5500配置
	Delay_ms(200);//延时等待

	/* Modbus-TCP Init */
    eMBTCPInit(MB_TCP_PORT_USE_DEFAULT); //端口依赖事件模块初始化
	Delay_ms(200); //延时等待
	
	/* Enable Modbus-TCP Stack */    
    eMBEnable();//激活协议栈	
    

    printf("\r\nModbus-TCP Start!\r\n");
    printf("IP:192.168.1.190\r\n");


	while(1)
	{
		
		i=Read_SOCK_1_Byte(0,Sn_SR);  //读W5500状态
		if(i==0)	  
		{
			do
			{
				Delay_ms(100);//延时等待
			
			}while(Socket_Listen(0)==FALSE);//设置“Socket n”为“TCP服务器模式”
		}
		else if(i==SOCK_ESTABLISHED)		 //建立TCP连接
		{
		eMBPoll();//启动modbus侦听
		BSP_LED();//线圈控制LED灯
		}
				
	}
}

w5500参数配置

/* W5500 configuration */
void W5500_Configuration()
{
	unsigned char array[6];

	GPIO_SetBits(GPIO_W5500_RST_PORT, GPIO_W5500_RST_Pin);//上拉
	Delay_ms(100);    /*delay 100ms 使用systick 1ms时基的延时*/
    //等待以太网链路
	while((Read_1_Byte(PHYCFGR)&LINK)==0); 		/* Waiting for Ethernet Link */

	Write_1_Byte(MR, RST);//写入W5500普通寄存器一个字节
	Delay_ms(20);		/*delay 20ms */

	/* Set Gateway IP as: 192.168.1.1 */
	array[0]=192;
	array[1]=168;
	array[2]=1;
	array[3]=1;
	Write_Bytes(GAR, array, 4);//设置网关IP

	/* Set Subnet Mask as: 255.255.255.0 */
	array[0]=255;
	array[1]=255;
	array[2]=255;
	array[3]=0;
	Write_Bytes(SUBR, array, 4);//设置子网掩码

	/* Set MAC Address as: 0x48,0x53,0x00,0x57,0x55,0x00 */
	array[0]=0x48;
	array[1]=0x53;
	array[2]=0x00;
	array[3]=0x57;
	array[4]=0x55;
	array[5]=0x00;
	Write_Bytes(SHAR, array, 6);//设置MAC地址

	/* Set W5500 IP as: 192.168.1.128 */
	array[0]=192;
	array[1]=168;
	array[2]=1;
	array[3]=190;
	Write_Bytes(SIPR, array, 4);//设置W5500的IP地址
}

在这里插入图片描述
在这里插入图片描述

4. STM32+W5500实现web服务

1. 基本原理

  1. 实现概述
    • STM32 W5500配置入网后,通过DHCP动态获取IP地址,在电脑浏览器地址栏输入这个IP地址,可以获取到一个简单form表单的页面,在表单中输入数据,提交到W5500web服务
      在这里插入图片描述
  2. http请求过程
    在这里插入图片描述

2. 程序实现

  1. W5500模块内部自己实现了TCP/IP协议,在外部只需要通过他提供的接口把相关的参数传递进去就行了,物理地址,本机IP,本机端口,目标端口,目标IP
    STM32F103C8芯片SPI初始化
void SPI1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1 |    RCC_APB2Periph_AFIO, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);  

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;       
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;  
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;    
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        //
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;           
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  
    SPI_InitStructure.SPI_CRCPolynomial = 7;    
    SPI_Init(SPI1, &SPI_InitStructure); 

    SPI_Cmd(SPI1, ENABLE); //ʹÄÜSPIÍâÉè

}   
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{
    assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
    SPI1->CR1&=0XFFC7;
    SPI1->CR1|=SPI_BaudRatePrescaler;   
    SPI_Cmd(SPI1,ENABLE); 

} 
  1. w5500模块主要参数
#ifdef __DEF_IINCHIP_PPP__
   #include "md5.h"
#endif

static uint8 I_STATUS[MAX_SOCK_NUM];
static uint16 SSIZE[MAX_SOCK_NUM]; /**< Max Tx buffer size by each channel */
static uint16 RSIZE[MAX_SOCK_NUM]; /**< Max Rx buffer size by each channel */

uint8 getISR(uint8 s)
{
  return I_STATUS[s];
}
void putISR(uint8 s, uint8 val)
{
   I_STATUS[s] = val;
}

uint16 getIINCHIP_RxMAX(uint8 s)
{
   return RSIZE[s];
}
uint16 getIINCHIP_TxMAX(uint8 s)
{
   return SSIZE[s];
}
void IINCHIP_CSoff(void)
{
  WIZ_CS(LOW);
}
void IINCHIP_CSon(void)
{
   WIZ_CS(HIGH);
}
u8  IINCHIP_SpiSendData(uint8 dat)
{
   return(SPI2_SendByte(dat));
}

void IINCHIP_WRITE( uint32 addrbsb,  uint8 data)
{
   IINCHIP_ISR_DISABLE();                        // Interrupt Service Routine Disable
   IINCHIP_CSoff();                              // CS=0, SPI start
   IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16);// Address byte 1
   IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8);// Address byte 2
   IINCHIP_SpiSendData( (addrbsb & 0x000000F8) + 4);    // Data write command and Write data length 1
   IINCHIP_SpiSendData(data);                    // Data write (write 1byte data)
   IINCHIP_CSon();                               // CS=1,  SPI end
   IINCHIP_ISR_ENABLE();                         // Interrupt Service Routine Enable
}

uint8 IINCHIP_READ(uint32 addrbsb)
{
   uint8 data = 0;
   IINCHIP_ISR_DISABLE();                        // Interrupt Service Routine Disable
   IINCHIP_CSoff();                              // CS=0, SPI start
   IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16);// Address byte 1
   IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8);// Address byte 2
   IINCHIP_SpiSendData( (addrbsb & 0x000000F8))    ;// Data read command and Read data length 1
   data = IINCHIP_SpiSendData(0x00);             // Data read (read 1byte data)
   IINCHIP_CSon();                               // CS=1,  SPI end
   IINCHIP_ISR_ENABLE();                         // Interrupt Service Routine Enable
   return data;    
}

uint16 wiz_write_buf(uint32 addrbsb,uint8* buf,uint16 len)
{
   uint16 idx = 0;
   if(len == 0) printf("Unexpected2 length 0\r\n");

   IINCHIP_ISR_DISABLE();
   IINCHIP_CSoff();                              // CS=0, SPI start
   IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16);// Address byte 1
   IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8);// Address byte 2
   IINCHIP_SpiSendData( (addrbsb & 0x000000F8) + 4);    // Data write command and Write data length 1
   for(idx = 0; idx < len; idx++)                // Write data in loop
   {
     IINCHIP_SpiSendData(buf[idx]);
   }
   IINCHIP_CSon();                               // CS=1, SPI end
   IINCHIP_ISR_ENABLE();                         // Interrupt Service Routine Enable    

   return len;  
}

uint16 wiz_read_buf(uint32 addrbsb, uint8* buf,uint16 len)
{
  uint16 idx = 0;
  if(len == 0)
  {
    printf("Unexpected2 length 0\r\n");
  }

  IINCHIP_ISR_DISABLE();
  //SPI MODE I/F
  IINCHIP_CSoff();                                  // CS=0, SPI start
  IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16);// Address byte 1
  IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8);// Address byte 2
  IINCHIP_SpiSendData( (addrbsb & 0x000000F8));    // Data write command and Write data length 1
  for(idx = 0; idx < len; idx++)                    // Write data in loop
  {
    buf[idx] = IINCHIP_SpiSendData(0x00);
  }
  IINCHIP_CSon();                                   // CS=1, SPI end
  IINCHIP_ISR_ENABLE();                             // Interrupt Service Routine Enable
  
  return len;
}
  1. http请求
void do_http(void)
{
  uint8 ch=SOCK_HTTP;
  uint16 len;

  st_http_request *http_request;
  memset(rx_buf,0x00,MAX_URI_SIZE);
  http_request = (st_http_request*)rx_buf;		// struct of http request  
  /* http service start */
  switch(getSn_SR(ch))
  {
    case SOCK_INIT:
      listen(ch);
      break;
    case SOCK_LISTEN:
      break;
    case SOCK_ESTABLISHED:
    //case SOCK_CLOSE_WAIT:
      if(getSn_IR(ch) & Sn_IR_CON)
      {
        setSn_IR(ch, Sn_IR_CON);
      }
      if ((len = getSn_RX_RSR(ch)) > 0)		
      {
        len = recv(ch, (uint8*)http_request, len); 
        *(((uint8*)http_request)+len) = 0;
        proc_http(ch, (uint8*)http_request); // request is processed
        disconnect(ch);
      }
      break;
    case SOCK_CLOSE_WAIT:   
      if ((len = getSn_RX_RSR(ch)) > 0)
      {
        //printf("close wait: %d\r\n",len);
        len = recv(ch, (uint8*)http_request, len);       
        *(((uint8*)http_request)+len) = 0;
        proc_http(ch, (uint8*)http_request); // request is processed
      }
      disconnect(ch);
      break;
    case SOCK_CLOSED:
      socket(ch, Sn_MR_TCP, 80, 0x00);    /* reinitialize the socket */
      break;
    default:
    break;
  }// end of switch
}


void JTXD_do_http(void)
{
  uint8 ch=SOCK_HTTP;
  uint16 len;

  st_http_request *http_request;
  memset(rx_buf,0x00,MAX_URI_SIZE);
  http_request = (st_http_request*)rx_buf;		// struct of http request
  
  /* http service start */
  switch(getSn_SR(ch))
  {
    case SOCK_INIT:
      listen(ch);
      break;
    case SOCK_LISTEN:

      break;
    case SOCK_ESTABLISHED:
    //case SOCK_CLOSE_WAIT:
      if(getSn_IR(ch) & Sn_IR_CON)
      {
        setSn_IR(ch, Sn_IR_CON);
      }
      if ((len = getSn_RX_RSR(ch)) > 0)		
      {
        len = recv(ch, (uint8*)http_request, len); 
        *(((uint8*)http_request)+len) = 0;
        JTXD_proc_http(ch, (uint8*)http_request); // request is processed
        disconnect(ch);
      }
      break;
    case SOCK_CLOSE_WAIT:   
      if ((len = getSn_RX_RSR(ch)) > 0)
      {
        //printf("close wait: %d\r\n",len);
        len = recv(ch, (uint8*)http_request, len);       
        *(((uint8*)http_request)+len) = 0;
        JTXD_proc_http(ch, (uint8*)http_request); // request is processed
      }
      disconnect(ch);
      break;
    case SOCK_CLOSED:                   
      socket(ch, Sn_MR_TCP, 80, 0x00);    /* reinitialize the socket */
      break;
    default:
    break;
  }// end of switch
}
  1. 修改默认参数地址
    • main.c函数
      在这里插入图片描述
    • 修改flash.c初始默认设置地址
      在这里插入图片描述
    • 同理修改device.c文件ip地址等 在这里插入图片描述
  2. 程序烧录运行
    • 构建生成hex文件,烧录 在这里插入图片描述
    • 断电,boot0置0后,启动串口调试程序,使串口函数可以正常使用
      在这里插入图片描述
    • 连接测试
      ping +设定ip地址在这里插入图片描述
  3. 启动网页服务器
    在这里插入图片描述

5. 总结

  1. 本次实验中在对w5500模块操作中,存在较多的问题,在进行DHCP获取ip与web服务器搭建过程中出现问题较多
    • 在使用校园网的局域网络时,因为网络端口限制问题,对于动态获取与ping连接等存在较多问题,同时会出现拒绝访问现象
      在这里插入图片描述
    • 实验中尽量避免使用端口局限性较多的网络,同时在进行网络数据发送中,需要关闭自虚拟网络端口,否则可能出现网络丢包现象
  2. 在进行网络端口访问中,同时应保证网络联通与电压稳定性,在进行DHCP获取ip过程中因线路问题导致电压不稳定,获取ip只能显示部分

参考

STM32F103基于W5500实现modbus简单TCP通信
通讯接口应用笔记3:使用W5500实现Modbus TCP服务器
基于w5500实现TCP/IP协议后应用层开发
基于STM32和W5500的Modbus TCP通讯
DHCP 原理以及IP获取过程
STM32 W5500 HTTP Server 微型web服务实现
通讯接口应用笔记3:使用W5500实现Modbus TCP服务器
Modbus协议最基础概念详细介绍
图文详解Modbus-RTU协议

版权声明:本文为CSDN博主「鹤引」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_47554309/article/details/122144707

一、W5500介绍

W5500 芯片是一款集成全硬件 TCP/IP 协议栈的嵌入式以太网控制器,同时也是一颗工业级以太网控制芯片。 是韩国WIZnet(微知纳特)发布全硬件TCP/IP协议栈以太网接口芯片。
W5500 支持高速标准4线SPI接口与主机进行通信,该 SPI 速率理论上可以达到 80MHz。其内部还集成了以太网数据链路层(MAC)和10BaseT/100BaseTX 以太网物理层(PHY),支持自动协商(10/100-Based全双工/半双工)、掉电模式和网络唤醒功能。与传统软件协议栈不同,W5500内嵌的8个独立硬件 Socket 可以进行8路独立通信,该8路Socket的通信效率互不影响,可以通过 W5500 片上32K 字节的收/发缓存灵活定义各个Socket的大小。

二、资源分享

1.链接:https://pan.baidu.com/s/1DzLA2Vp_Qwon9VWQdQ6g5Q

提取码:mvwi

2.文件内容
在这里插入图片描述
需要安装调试软件,可以按照教程去做例子!

三、例子程序

1.STM32与W5500接线方法

PA15->W5500_RST(源程序使用的是PC5,这里没有该引脚修改为PA15)

PC4->W5500_INT(使用寄存器查询方式的例程时,此引脚可以不接,其他例程可能涉及修改引脚)

PA4->W5500_SCS

PA5->W5500_SCK

PA6->W5500_MISO

PA7->W5500_MOSI

具体例程的调试过程请参考压缩包中NiRen_W5500模块用户手册(用Adobe Reader打开).pdf文件。

2.这里我调试了查询寄存器方式中的客户端模式例程,调试结果如下:

在这里插入图片描述

四、实现应用层modbus、httpd协议编程

(一)modbus

main函数:
在这里插入图片描述
在main函数中有w5500的配置函数 W5500_Configuration() ,在配置函数中可以修改自己的IP地址及网关信息等

(二)HTTPS

main函数
在这里插入图片描述

GPIO初始化

void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO , ENABLE);
  // Port A output
  GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0|GPIO_Pin_1| GPIO_Pin_2 |GPIO_Pin_3; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
//  GPIO_ResetBits(GPIOA, GPIO_Pin_0);
//  GPIO_ResetBits(GPIOA, GPIO_Pin_1);
//  GPIO_SetBits(GPIOA, GPIO_Pin_2); // led off
//  GPIO_SetBits(GPIOA, GPIO_Pin_3); // led off
  // Port B output;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB, GPIO_Pin_9);
  // Port C input
//  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
//  GPIO_Init(GPIOC, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//控制flash
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB, GPIO_Pin_7);	
}

W5500配置

void WIZ_SPI_Init(void)
{
	SPI_InitTypeDef   SPI_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO , ENABLE);	
  // Port B output
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB, GPIO_Pin_12);
  /* Configure SPIy pins: SCK, MISO and MOSI */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	  /* SPI Config -------------------------------------------------------------*/
	  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
	  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	  SPI_InitStructure.SPI_CRCPolynomial = 7;

	  SPI_Init(SPI2, &SPI_InitStructure);
	  SPI_Cmd(SPI2, ENABLE);
}

五、总结

学习了W5500网络模块的特点,实现了完成TCP数据通信,了解了应用层modbus、httpd(web服务)协议编程

六、参考链接

https://blog.csdn.net/qq_45659777/article/details/121952778

七、modbus和HTTPS源码

modbus:链接:https://pan.baidu.com/s/10VrYkhB0dTRqNc83EKRzag

提取码:r3pe

https:链接:https://pan.baidu.com/s/1-M5QIznHG9nnXtyigtBiDA

提取码:jf6u

版权声明:本文为CSDN博主「死妖阿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_49297422/article/details/122200457

一、W5500以太网模块

1.1 简介

  • NiRen_W5500模块是一款基于WIZnet W5500芯片的以太网模块,是泥人电子继 NiRen_W5100模块后设计的一块性能更好、性价比更高的以太网模块。模块集成硬件化TCP/IP协议;内部32K字节存储器作TX/RX缓存;支持10/100Mbps 的传输速率;支持8个独立端口同时运行;同时模块还支持3.3V或5V电源供电,5V供电时还可以输出3.3V电源,方便用户在不同的单片机系统中使用;模块与单片机系统的通讯方式是简单、方便的SPI通信。

1.2 模块排针功能表

在这里插入图片描述

1.3 W5500芯片资源介绍

  • W5500芯片是一种采用全硬件TCP/IP协议栈的嵌入式以太网控制器,它能使嵌入式系统通过SPI(串行外设接口)接口轻松地连接到网络。W5500特别适合那些需要使用单片机来实现互联网功能的客户,而这就需要单片机系统具有完整的TCP/IP协议栈和10/100Mbps 以太网网络层(MAC)和物理层(PHY)。W5500是由已经通过市场考验的全硬件TCP/IP协议栈、及以太网网络层和物理层的整合而成。其全硬件的TCP/IP协议栈全程支持TCP、UDP、IPv4、ICMP、ARP、IGMP和 PPPoE协议,而且已经连续多年在各种实际应用中得以证明。W5500使用32KB缓存作为其数据通信内存。通过使用W5500,用户只需通过使用一个简单的socket程序就能实现以太网的应用,而不再需要处理一个复杂的以太网控制器了。SPI(串行外设接口)提供了轻松与外部MCU连接的接口。W5500支持高达8OMHZ的SPI 接口间通信。为了降低系统功率的消耗,W5500提供了网络唤醒和休眠模式。W5500收到原始以太网数据包形式的magic packet时将被唤醒。

1.4 接线方式

在这里插入图片描述

1.5代码调试

  • 在代码中添加本地ip地址、掩码及网关。
#include "stm32f10x.h"		
#include "W5500.h"			
#include <string.h>

void RCC_Configuration(void);		//设置系统时钟为72MHZ(这个可以根据需要改)
void NVIC_Configuration(void);		//STM32中断向量表配配置
void Timer2_Init_Config(void);		//Timer2初始化配置
void System_Initialization(void);	//STM32系统初始化函数(初始化STM32时钟及外设)
void Delay(unsigned int d);			//延时函数(ms)

unsigned int Timer2_Counter=0; //Timer2定时器计数变量(ms)
unsigned int W5500_Send_Delay_Counter=0; //W5500发送延时计数变量(ms)

/*******************************************************************************
* 函数名  : W5500_Initialization
* 描述    : W5500初始货配置
* 输入    : 无
* 输出    : 无
* 返回值  : 无
* 说明    : 无
*******************************************************************************/
void W5500_Initialization(void)
{
	W5500_Init();		//初始化W5500寄存器函数
	Detect_Gateway();	//检查网关服务器 
	Socket_Init(0);		//指定Socket(0~7)初始化,初始化端口0
}

/*******************************************************************************
* 函数名  : Load_Net_Parameters
* 描述    : 装载网络参数
* 输入    : 无
* 输出    : 无
* 返回值  : 无
* 说明    : 网关、掩码、物理地址、本机IP地址、端口号、目的IP地址、目的端口号、端口工作模式
*******************************************************************************/
void Load_Net_Parameters(void)
{
	Gateway_IP[0] = 192;//加载网关参数
	Gateway_IP[1] = 168;
	Gateway_IP[2] = 0;
	Gateway_IP[3] = 1;

	Sub_Mask[0]=255;//加载子网掩码
	Sub_Mask[1]=255;
	Sub_Mask[2]=255;
	Sub_Mask[3]=0;

	Phy_Addr[0]=0x34;//加载物理地址
	Phy_Addr[1]=0x4b;
	Phy_Addr[2]=0x50;
	Phy_Addr[3]=0x00;
	Phy_Addr[4]=0x00;
	Phy_Addr[5]=0x00;

	IP_Addr[0]=192;//加载本机IP地址
	IP_Addr[1]=168;
	IP_Addr[2]=0;
	IP_Addr[3]=178;

	S0_Port[0] = 0x13;//加载端口0的端口号5000 
	S0_Port[1] = 0x88;

	S0_DIP[0]=192;//加载端口0的目的IP地址
	S0_DIP[1]=168;
	S0_DIP[2]=64;
	S0_DIP[3]=1;
	
	S0_DPort[0] = 0x17;//加载端口0的目的端口号6000
	S0_DPort[1] = 0x70;

	S0_Mode=TCP_CLIENT;//加载端口0的工作模式,TCP客户端模式
}

  • 将代码烧录及STM32F103开发板中,打开调试工具
    在这里插入图片描述

二、W5500实现modbus协议编程

  • Modbus TCP通讯首先需要下载W5500的驱动源码,可以到WIZnet的官网下载
    解压目录
  • 下载FreeModbus源码,然后放到自己的工程中
    在这里插入图片描述
  • 修改porttcp中的代码
static UCHAR    aucTCPBuf[MB_TCP_BUF_SIZE];	  
BOOL
xMBTCPPortInit( USHORT usTCPPort )
{
    BOOL bOkay = FALSE;
     
    // 侦听端口 Modbus-TCP 端口
	socket_init(SOCK_TCP_PORT,Sn_MR_TCP_TCP,local_tcp_port++,Sn_MR_ND_TCP);
	listen_tcp_socket(SOCK_TCP_PORT);
    
    bOkay = TRUE;
    return bOkay;
}
BOOL
xMBPortTCPPool( void )
{  
	unsigned short int us_rlen;
	unsigned char i;
	i=get_tcp_socket_state(SOCK_TCP_PORT);

	if(i==SOCK_ESTABLISHED_TCP)
	{
		if(get_tcp_socket_irq(SOCK_TCP_PORT) & Sn_IR_CON_TCP)		//查看中断有没有发生
		{
			clear_tcp_socket_irq(SOCK_TCP_PORT, Sn_IR_CON_TCP); 	/*清除接收中断标志位*/						         
		}
		us_rlen=get_tcp_rx_buffer_size(SOCK_TCP_PORT); 				//获取接收到的数据字节				  	         /*定义len为已接收数据的长度*/
		if(us_rlen==0)												//没有接收到数据
			return FALSE;											//返回
		else
		{
			recv_tcp_socket_data(SOCK_TCP_PORT,aucTCPBuf,us_rlen); 	//接收数据						   		         /*接收来自Server的数据*/
			printf("receive\r\n");
			usTCPBufLen=us_rlen;
		}
		( void )xMBPortEventPost( EV_FRAME_RECEIVED );			//发送已接收到新数据到Modbus-TCP状态机
	}
	else if(i==SOCK_CLOSED_TCP)									//如果socket关闭
	{
		socket_init(SOCK_TCP_PORT,Sn_MR_TCP_TCP,local_tcp_port++,Sn_MR_ND_TCP);//重新初始化
	}
	else if(i==SOCK_INIT_TCP)								//如果socket初始化完毕,监听端口
	{
		listen_tcp_socket(SOCK_TCP_PORT);
	}
	else if(i==SOCK_CLOSE_WAIT_TCP)							//如果socket等待关闭,关闭socket连接
	{
		close_tcp_socket(SOCK_TCP_PORT);
	}

	return TRUE;
}



三、W5500实现web服务协议编程

3.1基本原理

  • 实现的web服务的功能,STM32 W5500配置入网后,通过DHCP动态获取IP地址,在电脑浏览器地址栏输入这个IP地址,可以获取到一个简单form表单的页面,在表单中输入数据,提交到W5500web服务,返回一个结果。
    在这里插入图片描述
int main(void)
{
	u32 dhcp_timestamp;
	u8 mac[6]={0, };
	DHCP_Get dhcp_get;
	u16 len;
	u8 buffer[BUFFER_SIZE];
	char http_method[16];
	char http_uri[64];
	char http_body[256];
	u8 res_code;
	
	systick_configuration();
	init_led();
	
	init_system_spi();
	func_w5500_reset();
	
	init_hardware_usart2_dma(9600);
	
	getMacByLockCode(mac);
	setSHAR(mac);
	
	sysinit(txsize, rxsize);
	setRTR(2000);
  setRCR(3);
	
	//DHCP
	for(;func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) != 0;);	
	if(func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) == 0)
	{
		setSUBR(dhcp_get.sub);
		setGAR(dhcp_get.gw);
		setSIPR(dhcp_get.lip);
		close(1);
	}
	dhcp_timestamp = get_systick_timestamp();
 
	for(;;)
	{
		if(get_systick_timestamp() - dhcp_timestamp > 59*1000)// 1 min dhcp
		{
			dhcp_timestamp = get_systick_timestamp();
			if(func_dhcp_get_ip_sub_gw(1, mac, &dhcp_get, 500) == 0)
			{
				setSUBR(dhcp_get.sub);
				setGAR(dhcp_get.gw);
				setSIPR(dhcp_get.lip);
				close(1);
			}
		}
		
		switch(getSn_SR(SOCK_TCPS))
		{
			case SOCK_CLOSED:
				socket(SOCK_TCPS, Sn_MR_TCP, 80, Sn_MR_ND);
				break;
			case SOCK_INIT:
				listen(SOCK_TCPS);
				break;		
			case SOCK_ESTABLISHED:		
				if(getSn_IR(SOCK_TCPS) & Sn_IR_CON)
				{
					setSn_IR(SOCK_TCPS, Sn_IR_CON);
				}
				len = getSn_RX_RSR(SOCK_TCPS);
				if(len>0)
				{
					memset(buffer, 0, BUFFER_SIZE);
					len = recv(SOCK_TCPS, buffer, len);
					//analysis tcp msg, and package the feedback msg
					if(len > 0)
					{
						res_code = func_analysis_http_request(buffer, len, http_method, http_uri, http_body);
						memset(buffer, 0, sizeof(buffer));
						if(res_code == 0)
						{
							if(strcmp("GET", http_method) == 0 && strcmp("/", http_uri) == 0)
							{
								func_package_http_response(buffer, &len, sizeof(buffer), HTML_CONTENT, strlen(HTML_CONTENT));
								send(SOCK_TCPS, buffer, len);					
							}
							else if(strcmp("POST", http_method) == 0 && strcmp("/sn_config.action", http_uri) == 0)
							{
								func_package_http_response(buffer, &len, BUFFER_SIZE, HTML_RESULT_OK, strlen(HTML_RESULT_OK));
								send(SOCK_TCPS, buffer, len);
							}
							else
							{
								memcpy(buffer, TEXT_TEMPLATE_ERR, strlen(TEXT_TEMPLATE_ERR));
								send(SOCK_TCPS, buffer, strlen(TEXT_TEMPLATE_ERR));
							}
							disconnect(SOCK_TCPS);
						}
						else
						{
							memcpy(buffer, TEXT_TEMPLATE_ERR, strlen(TEXT_TEMPLATE_ERR));
							send(SOCK_TCPS, buffer, strlen(TEXT_TEMPLATE_ERR));
							disconnect(SOCK_TCPS);
						}
					}
					
				}
				break;
			case SOCK_CLOSE_WAIT:
				close(SOCK_TCPS);
				break;
		}
		
		func_led1_on();
		delay_ms(500);
		func_led1_off();
		delay_ms(500);
				
	}
}
 
u8 func_analysis_http_request(u8* buffer, u16 len_recv, char* method, char* uri, char* data_body)
{
	char chs[BUFFER_SIZE] = {0, };
	char *res, *end;
	if(len_recv > 0)
	{
		memcpy(chs, buffer, 3);
		res = strstr(chs, "GET");
		if(strcmp("GET", res) == 0)
		{
			memcpy(method, "GET", strlen("GET"));
			
		}
		else
		{
			memset(chs, 0, BUFFER_SIZE);
			memcpy(chs, buffer, 4);
			res = strstr(chs, "POST");
			if(strcmp("POST", res) == 0)
			{
				memcpy(method, "POST", strlen("POST"));
				
			}
			else
			{
				return 1;
			}
		}
		
		memset(chs, 0, BUFFER_SIZE);
		memcpy(chs, buffer, len_recv + 1);
		res = strchr(chs, '/');
		if(res != NULL)
		{
			end = strchr(res, ' ');
			if(end != NULL)
			{
				memcpy(uri, res, end - res);
			}			
		}
		
		memset(chs, 0, BUFFER_SIZE);
		memcpy(chs, buffer, len_recv + 1);
		res = strstr(chs, "\r\n\r\n");
		if(res != NULL)
		{
			if(strlen(res) > 4)
			{
				memcpy(data_body, res + 4, strlen(res) - 4);
			}			
		}
		
	}
	return 0;
}
 
u8 func_package_http_response(u8* buffer, u16 *len_ret, u16 len_buf, char* cont, u16 len_cont)
{
	memset(buffer, 0, BUFFER_SIZE);
	*len_ret = sprintf((char*)buffer, TEXT_TEMPLATE_OK, len_cont, cont);
	return 0;
}

版权声明:本文为CSDN博主「Sue hiroshi」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tauvan/article/details/122178518

W5500网络模块简介

W5500网络扩展板集成了一个硬件TCP/IP协议协芯片W5500以及一个含有网络变压器的RJ-45(HR911105A)。其中,W5500是一款全硬件TCP/IP嵌入式以太网控制器,以嵌入式系统提供了更加简易的互联网连接方案。

使用测试工具调试

使用TCP&UDP测试工具进行调试。
将W5500网络模块与STM32F103板子连接:
参考代码中的注释:
在这里插入图片描述
PC5引脚可以更换为其它引脚。
在keil中编译运行后,打开调试工具,在服务端模式即可有:
请添加图片描述

实现应用层modbus、httpd(web服务)协议编程

.1 modbus协议编程

main函数:
在这里插入图片描述
在main函数中有w5500的配置函数 W5500_Configuration() ,在配置函数中可以修改自己的IP地址及网关信息等。

.2 web协议编程

引脚初始化:

void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO , ENABLE);
  // Port A output
  GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0|GPIO_Pin_1| GPIO_Pin_2 |GPIO_Pin_3; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
//  GPIO_ResetBits(GPIOA, GPIO_Pin_0);
//  GPIO_ResetBits(GPIOA, GPIO_Pin_1);
//  GPIO_SetBits(GPIOA, GPIO_Pin_2); // led off
//  GPIO_SetBits(GPIOA, GPIO_Pin_3); // led off
  // Port B output;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB, GPIO_Pin_9);
  // Port C input
//  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
//  GPIO_Init(GPIOC, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//控制flash
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB, GPIO_Pin_7);	
}

串口初始化:

void USART1_Init(void)
{
  USART_InitTypeDef USART_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO , ENABLE);

	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  // Configure the USART1_Rx as input floating
  GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IN_FLOATING ;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	
  USART_InitStructure.USART_BaudRate = 115200;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No ;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //Enable rx enable, 
  
  /* Configure the USARTx */ 
  USART_Init(USART1, &USART_InitStructure);
  /* Enable the USARTx */
  USART_Cmd(USART1, ENABLE);
}

main函数:

int main(void)
{
	Systick_Init(72);
	GPIO_Configuration(); //GPIO configuration
	USART1_Init(); //115200@8-n-1
	printf("W5500 EVB initialization over.\r\n");
	Reset_W5500();
	WIZ_SPI_Init();
	printf("W5500 initialized!\r\n");
	if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7))
	{
		DefaultSet();//出厂值
	}
	else
	{
 		get_config();//read config data from flash
	}
	printf("Firmware ver%d.%d\r\n",ConfigMsg.sw_ver[0],ConfigMsg.sw_ver[1]);
	if(ConfigMsg.debug==0) ConfigMsg.debug=1;

	set_network();
	printf("Network is ready.\r\n");
	while(1)
	{
		if(ConfigMsg.JTXD_Control == 0)
		  	do_http();
		else
		  	JTXD_do_http();
		if(reboot_flag)
			NVIC_SystemReset();
//        reboot();
        
	}
}

源码:提取码:z56u

参考

https://blog.csdn.net/qq_45659777/article/details/121952778?spm=1001.2014.3001.5501

https://blog.csdn.net/tcjy1000/article/details/25474511

版权声明:本文为CSDN博主「Pilipalaaaaa」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_46359931/article/details/122146850

1. W5500模块实现数据通信

  1. 引脚初始化,此连线方式用于三个模块功能的整体实现
    在这里插入图片描述
  1. PA3 -> W5500_RST
  2. PA4 -> W5500_SCS
  3. PA5 -> W5500_SCK
  4. PA6 -> W5500_MISO
  5. PA7 -> W5500_MOSI

1. 基本原理

  1. 基本介绍
  1. Niren_W5500模块是一款基于WIZnet W5500芯片的以太网模块,是泥人电子继 Niren_W5100模块后设计的一块性能更好、性价比更高的以太网模块。模块集成硬件化TCP/IP协议:内部32K字节存储器作TX/RX
  2. 缓存:支持10/100Mbps的传输速率;支持8个独立端口同时运行;同时模块还支持3.3V或5V电源供电,5V供电时还可以输出3.3V电源,方便用户在不同的单片机系统中使用;模块与单片机系统的通讯方式是简单、方便的SPI通信。
  1. 实现思路
    • W5500内部是硬件TCP/IP协议栈,对外(MCU)只是提供了操作socket的能力,内部支持8个独立的socket,每一个socket通过Socket n寄存器区控制(0≤n≤7)。
    • 所以在编写基于Socket的网络应用程序时,可以按照查询Socket状态寄存器实现一个状态机的思路来实现。

2. 程序实现

  1. 修改main.c函数
    修改为根据本机ip的实际地址
    在这里插入图片描述
    • 可修改以太网本地ip地址,实现网络的整体搭建链接 在这里插入图片描述
      实验过程中使用固定ip,后改回默认
  2. SPI
void SPI_Configuration(void)
{
	GPIO_InitTypeDef 	GPIO_InitStructure;
	SPI_InitTypeDef   	SPI_InitStructure;	   

  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1 | RCC_APB2Periph_AFIO, ENABLE);	

	/* 初始化SCK、MISO、MOSI引脚 */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);

	/* 初始化CS引脚 */
	GPIO_InitStructure.GPIO_Pin = W5500_SCS;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_Init(W5500_SCS_PORT, &GPIO_InitStructure);
	GPIO_SetBits(W5500_SCS_PORT, W5500_SCS);

	/* 初始化配置STM32 SPI1 */
	SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;	//SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode=SPI_Mode_Master;							//设置为主SPI
	SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b;						//SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low;							//时钟悬空低
	SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge;							//数据捕获于第1个时钟沿
	SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;								//NSS由外部管脚管理
	SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_2;	//波特率预分频值为2
	SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;					//数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial=7;								//CRC多项式为7
	SPI_Init(SPI1,&SPI_InitStructure);									//根据SPI_InitStruct中指定的参数初始化外设SPI1寄存器

	SPI_Cmd(SPI1,ENABLE);	//STM32使能SPI1
}
  1. 程序运行,关闭虚拟端口,同时启动调试工具
    在这里插入图片描述
    修改默认ip,并连接网线,连接到设定网络,启动服务,测试tcp通信

    • 创建链接 在这里插入图片描述
    • 启动服务器 在这里插入图片描述

2. DHCP自动获取IP

1. 基本原理

  1. DHCP基本定义
    DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)。是一个局域网的网络协议,使用UDP协议工作。 DHCP主要有两个用途:
    • 给内部网络或网络服务供应商自动分配IP地址
    • 给用户或者内部网络管理员作为对所有计算机作中央管理的手段。
  2. 分配方式
    • 自动分配方式
      DHCP服务器为主机指定一个永久性的IP地址,一旦DHCP客户端第一次成功从DHCP服务器端租用到IP地址后,就可以永久性的使用该地址。
    • 动态分配方式
      DHCP服务器给主机指定一个具有时间限制的IP地址,时间到期或主机明确表示放弃该地址时,该地址可以被其他主机使用。手工分配方式(Manual Allocation),客户端的IP地址是由网络管理员指定的,DHCP服务器只是将指定的IP地址告诉客户端主机。
  3. ip获取
    在这里插入图片描述
    • 寻找DHCP服务器
    • 分配IP地址
    • 接受IP地址
    • IP地址分配确认
    • 获取到IP后广播ARP

2. 程序实现

  1. device.c函数配置地址
void set_network(void)
{
  uint8 ip[4];
  setSHAR(ConfigMsg.mac);/*配置Mac地址*/
  setSUBR(ConfigMsg.sub);/*配置子网掩码*/
  setGAR(ConfigMsg.gw);/*配置默认网关*/
  setSIPR(ConfigMsg.lip);/*配置Ip地址*/

  //Init. TX & RX Memory size of w5500
  sysinit(txsize, rxsize); /*初始化8个socket*/
  
  setRTR(2000);/*设置溢出时间值*/
  setRCR(3);/*设置最大重新发送次数*/
  

  getSIPR (ip);

  printf("IP : %d.%d.%d.%d\r\n", ip[0],ip[1],ip[2],ip[3]);

  getSUBR(ip);

  printf("SN : %d.%d.%d.%d\r\n", ip[0],ip[1],ip[2],ip[3]);

  getGAR(ip);

  printf("GW : %d.%d.%d.%d\r\n", ip[0],ip[1],ip[2],ip[3]);
 
}

默认ip地址
在这里插入图片描述

  1. lip:本机地址
  2. sub: 子网掩码
  3. gw: 默认网关
  1. 程序运行
    • 构建生成hex文件,烧录 在这里插入图片描述
    • 断电,boot0置0后,启动串口调试程序,使串口函数可以正常使用 在这里插入图片描述

3. modbus协议

1. 基本原理

  1. 定义
    Modbus是Modicon(施耐德)公司于1979年开发的串行通信协议。它最初设计用于公司的可编程逻辑控制器(PLC)。 Modbus是一种开放式协议
  • 公开发表并且无著作权要求;
  • 易于部署和维护;
  • 对供应商来说,修改移动本地的比特或字节没有很多限制;
  1. 版本
    • Modbus RTU(Remote Terminal Unit 远程终端单元):这种方式常采用RS-485做为物理层,一般利用芯片的串口实现数据报文的收发,报文数据采用二进制数据进行通信。

    • Modbus ASCII :报文使用 ASCII 字符。ASCII 格式使用纵向冗余校验和。Modbus ASCII 报文由冒号 (":")开始 和换行符 (CR/LF) 结尾构成。

    • Modbus TCP/IP 或 Modbus TCP :这是一种 Modbus 变体版本,使用 TCP/IP 网络进行通信,通过 502 端口进行连接。报文不需要校验和计算,因为以太网底层已经实现了CRC32 数据完整性校验。

    • Modbus over TCP/IP 或 Modbus over TCP 或 Modbus RTU/IP :这也是一种 Modbus 变体,与 Modbus TCP 的不同之处在于,与 Modbus RTU 一样,校验和包含在报文中。

    • Modbus UDP:也有在UDP上传输Modbus报文的,不过需要做错误重传机制,这么干的应该不多。

  2. 传输方式
    • 控制器能设置为两种传输模式(ASCII或RTU)中的任何一种在标准的Modbus网络通信。用户选择想要的模式,包括串口通信参数(波特率、校验方式等),在配置每个控制器的时候,在一个Modbus网络上的所有设备都必须选择相同的传输模式和串口参数。
      ASCII模式
      在这里插入图片描述
    • RTU模式
      在这里插入图片描述
  3. modbus表
    在这里插入图片描述
    在这里插入图片描述

2. 实现过程

  1. 思路
    • 实现TCP服务器,这个服务器用于在系统中轮询处理,从W5500获取数据和发送数据给W5500都需要通过这部分来实现。
    • TCP服务器得到数据后,我们需要解析数据,并根据解析的上位数据决定进一步的动作,还需要生成返回信息。这部分对应功能就是Modbus TCP服务器的实现。
    • 根据Modbus TCP服务器解析出的Modbus消息,需要决定下一步的动作,这个具体动作根据功能码的不同可能有不同需求,所以我们需要根据具体的要求实现不同功能码的动作。
  2. 程序实现
    main函数
int main(void)
{
	unsigned char i;

	/* Initialize STM32F103 */
	System_Initialization();//系统配置
	SysTick_Init();//启动系统滴答定时器 SysTick

	/* Config W5500 */
	W5500_Configuration();//W5500配置
	Delay_ms(200);//延时等待

	/* Modbus-TCP Init */
    eMBTCPInit(MB_TCP_PORT_USE_DEFAULT); //端口依赖事件模块初始化
	Delay_ms(200); //延时等待
	
	/* Enable Modbus-TCP Stack */    
    eMBEnable();//激活协议栈	
    

    printf("\r\nModbus-TCP Start!\r\n");
    printf("IP:192.168.1.190\r\n");


	while(1)
	{
		
		i=Read_SOCK_1_Byte(0,Sn_SR);  //读W5500状态
		if(i==0)	  
		{
			do
			{
				Delay_ms(100);//延时等待
			
			}while(Socket_Listen(0)==FALSE);//设置“Socket n”为“TCP服务器模式”
		}
		else if(i==SOCK_ESTABLISHED)		 //建立TCP连接
		{
		eMBPoll();//启动modbus侦听
		BSP_LED();//线圈控制LED灯
		}
				
	}
}

w5500参数配置

/* W5500 configuration */
void W5500_Configuration()
{
	unsigned char array[6];

	GPIO_SetBits(GPIO_W5500_RST_PORT, GPIO_W5500_RST_Pin);//上拉
	Delay_ms(100);    /*delay 100ms 使用systick 1ms时基的延时*/
    //等待以太网链路
	while((Read_1_Byte(PHYCFGR)&LINK)==0); 		/* Waiting for Ethernet Link */

	Write_1_Byte(MR, RST);//写入W5500普通寄存器一个字节
	Delay_ms(20);		/*delay 20ms */

	/* Set Gateway IP as: 192.168.1.1 */
	array[0]=192;
	array[1]=168;
	array[2]=1;
	array[3]=1;
	Write_Bytes(GAR, array, 4);//设置网关IP

	/* Set Subnet Mask as: 255.255.255.0 */
	array[0]=255;
	array[1]=255;
	array[2]=255;
	array[3]=0;
	Write_Bytes(SUBR, array, 4);//设置子网掩码

	/* Set MAC Address as: 0x48,0x53,0x00,0x57,0x55,0x00 */
	array[0]=0x48;
	array[1]=0x53;
	array[2]=0x00;
	array[3]=0x57;
	array[4]=0x55;
	array[5]=0x00;
	Write_Bytes(SHAR, array, 6);//设置MAC地址

	/* Set W5500 IP as: 192.168.1.128 */
	array[0]=192;
	array[1]=168;
	array[2]=1;
	array[3]=190;
	Write_Bytes(SIPR, array, 4);//设置W5500的IP地址
}

在这里插入图片描述
在这里插入图片描述

4. STM32+W5500实现web服务

1. 基本原理

  1. 实现概述
    • STM32 W5500配置入网后,通过DHCP动态获取IP地址,在电脑浏览器地址栏输入这个IP地址,可以获取到一个简单form表单的页面,在表单中输入数据,提交到W5500web服务
      在这里插入图片描述
  2. http请求过程
    在这里插入图片描述

2. 程序实现

  1. W5500模块内部自己实现了TCP/IP协议,在外部只需要通过他提供的接口把相关的参数传递进去就行了,物理地址,本机IP,本机端口,目标端口,目标IP
    STM32F103C8芯片SPI初始化
void SPI1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1 |    RCC_APB2Periph_AFIO, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);  

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;       
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;  
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;    
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        //
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;           
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  
    SPI_InitStructure.SPI_CRCPolynomial = 7;    
    SPI_Init(SPI1, &SPI_InitStructure); 

    SPI_Cmd(SPI1, ENABLE); //ʹÄÜSPIÍâÉè

}   
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{
    assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
    SPI1->CR1&=0XFFC7;
    SPI1->CR1|=SPI_BaudRatePrescaler;   
    SPI_Cmd(SPI1,ENABLE); 

} 
  1. w5500模块主要参数
#ifdef __DEF_IINCHIP_PPP__
   #include "md5.h"
#endif

static uint8 I_STATUS[MAX_SOCK_NUM];
static uint16 SSIZE[MAX_SOCK_NUM]; /**< Max Tx buffer size by each channel */
static uint16 RSIZE[MAX_SOCK_NUM]; /**< Max Rx buffer size by each channel */

uint8 getISR(uint8 s)
{
  return I_STATUS[s];
}
void putISR(uint8 s, uint8 val)
{
   I_STATUS[s] = val;
}

uint16 getIINCHIP_RxMAX(uint8 s)
{
   return RSIZE[s];
}
uint16 getIINCHIP_TxMAX(uint8 s)
{
   return SSIZE[s];
}
void IINCHIP_CSoff(void)
{
  WIZ_CS(LOW);
}
void IINCHIP_CSon(void)
{
   WIZ_CS(HIGH);
}
u8  IINCHIP_SpiSendData(uint8 dat)
{
   return(SPI2_SendByte(dat));
}

void IINCHIP_WRITE( uint32 addrbsb,  uint8 data)
{
   IINCHIP_ISR_DISABLE();                        // Interrupt Service Routine Disable
   IINCHIP_CSoff();                              // CS=0, SPI start
   IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16);// Address byte 1
   IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8);// Address byte 2
   IINCHIP_SpiSendData( (addrbsb & 0x000000F8) + 4);    // Data write command and Write data length 1
   IINCHIP_SpiSendData(data);                    // Data write (write 1byte data)
   IINCHIP_CSon();                               // CS=1,  SPI end
   IINCHIP_ISR_ENABLE();                         // Interrupt Service Routine Enable
}

uint8 IINCHIP_READ(uint32 addrbsb)
{
   uint8 data = 0;
   IINCHIP_ISR_DISABLE();                        // Interrupt Service Routine Disable
   IINCHIP_CSoff();                              // CS=0, SPI start
   IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16);// Address byte 1
   IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8);// Address byte 2
   IINCHIP_SpiSendData( (addrbsb & 0x000000F8))    ;// Data read command and Read data length 1
   data = IINCHIP_SpiSendData(0x00);             // Data read (read 1byte data)
   IINCHIP_CSon();                               // CS=1,  SPI end
   IINCHIP_ISR_ENABLE();                         // Interrupt Service Routine Enable
   return data;    
}

uint16 wiz_write_buf(uint32 addrbsb,uint8* buf,uint16 len)
{
   uint16 idx = 0;
   if(len == 0) printf("Unexpected2 length 0\r\n");

   IINCHIP_ISR_DISABLE();
   IINCHIP_CSoff();                              // CS=0, SPI start
   IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16);// Address byte 1
   IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8);// Address byte 2
   IINCHIP_SpiSendData( (addrbsb & 0x000000F8) + 4);    // Data write command and Write data length 1
   for(idx = 0; idx < len; idx++)                // Write data in loop
   {
     IINCHIP_SpiSendData(buf[idx]);
   }
   IINCHIP_CSon();                               // CS=1, SPI end
   IINCHIP_ISR_ENABLE();                         // Interrupt Service Routine Enable    

   return len;  
}

uint16 wiz_read_buf(uint32 addrbsb, uint8* buf,uint16 len)
{
  uint16 idx = 0;
  if(len == 0)
  {
    printf("Unexpected2 length 0\r\n");
  }

  IINCHIP_ISR_DISABLE();
  //SPI MODE I/F
  IINCHIP_CSoff();                                  // CS=0, SPI start
  IINCHIP_SpiSendData( (addrbsb & 0x00FF0000)>>16);// Address byte 1
  IINCHIP_SpiSendData( (addrbsb & 0x0000FF00)>> 8);// Address byte 2
  IINCHIP_SpiSendData( (addrbsb & 0x000000F8));    // Data write command and Write data length 1
  for(idx = 0; idx < len; idx++)                    // Write data in loop
  {
    buf[idx] = IINCHIP_SpiSendData(0x00);
  }
  IINCHIP_CSon();                                   // CS=1, SPI end
  IINCHIP_ISR_ENABLE();                             // Interrupt Service Routine Enable
  
  return len;
}
  1. http请求
void do_http(void)
{
  uint8 ch=SOCK_HTTP;
  uint16 len;

  st_http_request *http_request;
  memset(rx_buf,0x00,MAX_URI_SIZE);
  http_request = (st_http_request*)rx_buf;		// struct of http request  
  /* http service start */
  switch(getSn_SR(ch))
  {
    case SOCK_INIT:
      listen(ch);
      break;
    case SOCK_LISTEN:
      break;
    case SOCK_ESTABLISHED:
    //case SOCK_CLOSE_WAIT:
      if(getSn_IR(ch) & Sn_IR_CON)
      {
        setSn_IR(ch, Sn_IR_CON);
      }
      if ((len = getSn_RX_RSR(ch)) > 0)		
      {
        len = recv(ch, (uint8*)http_request, len); 
        *(((uint8*)http_request)+len) = 0;
        proc_http(ch, (uint8*)http_request); // request is processed
        disconnect(ch);
      }
      break;
    case SOCK_CLOSE_WAIT:   
      if ((len = getSn_RX_RSR(ch)) > 0)
      {
        //printf("close wait: %d\r\n",len);
        len = recv(ch, (uint8*)http_request, len);       
        *(((uint8*)http_request)+len) = 0;
        proc_http(ch, (uint8*)http_request); // request is processed
      }
      disconnect(ch);
      break;
    case SOCK_CLOSED:
      socket(ch, Sn_MR_TCP, 80, 0x00);    /* reinitialize the socket */
      break;
    default:
    break;
  }// end of switch
}


void JTXD_do_http(void)
{
  uint8 ch=SOCK_HTTP;
  uint16 len;

  st_http_request *http_request;
  memset(rx_buf,0x00,MAX_URI_SIZE);
  http_request = (st_http_request*)rx_buf;		// struct of http request
  
  /* http service start */
  switch(getSn_SR(ch))
  {
    case SOCK_INIT:
      listen(ch);
      break;
    case SOCK_LISTEN:

      break;
    case SOCK_ESTABLISHED:
    //case SOCK_CLOSE_WAIT:
      if(getSn_IR(ch) & Sn_IR_CON)
      {
        setSn_IR(ch, Sn_IR_CON);
      }
      if ((len = getSn_RX_RSR(ch)) > 0)		
      {
        len = recv(ch, (uint8*)http_request, len); 
        *(((uint8*)http_request)+len) = 0;
        JTXD_proc_http(ch, (uint8*)http_request); // request is processed
        disconnect(ch);
      }
      break;
    case SOCK_CLOSE_WAIT:   
      if ((len = getSn_RX_RSR(ch)) > 0)
      {
        //printf("close wait: %d\r\n",len);
        len = recv(ch, (uint8*)http_request, len);       
        *(((uint8*)http_request)+len) = 0;
        JTXD_proc_http(ch, (uint8*)http_request); // request is processed
      }
      disconnect(ch);
      break;
    case SOCK_CLOSED:                   
      socket(ch, Sn_MR_TCP, 80, 0x00);    /* reinitialize the socket */
      break;
    default:
    break;
  }// end of switch
}
  1. 修改默认参数地址
    • main.c函数
      在这里插入图片描述
    • 修改flash.c初始默认设置地址
      在这里插入图片描述
    • 同理修改device.c文件ip地址等 在这里插入图片描述
  2. 程序烧录运行
    • 构建生成hex文件,烧录 在这里插入图片描述
    • 断电,boot0置0后,启动串口调试程序,使串口函数可以正常使用
      在这里插入图片描述
    • 连接测试
      ping +设定ip地址在这里插入图片描述
  3. 启动网页服务器
    在这里插入图片描述

5. 总结

  1. 本次实验中在对w5500模块操作中,存在较多的问题,在进行DHCP获取ip与web服务器搭建过程中出现问题较多
    • 在使用校园网的局域网络时,因为网络端口限制问题,对于动态获取与ping连接等存在较多问题,同时会出现拒绝访问现象
      在这里插入图片描述
    • 实验中尽量避免使用端口局限性较多的网络,同时在进行网络数据发送中,需要关闭自虚拟网络端口,否则可能出现网络丢包现象
  2. 在进行网络端口访问中,同时应保证网络联通与电压稳定性,在进行DHCP获取ip过程中因线路问题导致电压不稳定,获取ip只能显示部分

参考

STM32F103基于W5500实现modbus简单TCP通信
通讯接口应用笔记3:使用W5500实现Modbus TCP服务器
基于w5500实现TCP/IP协议后应用层开发
基于STM32和W5500的Modbus TCP通讯
DHCP 原理以及IP获取过程
STM32 W5500 HTTP Server 微型web服务实现
通讯接口应用笔记3:使用W5500实现Modbus TCP服务器
Modbus协议最基础概念详细介绍
图文详解Modbus-RTU协议

版权声明:本文为CSDN博主「鹤引」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_47554309/article/details/122144707

生成海报
点赞 0

鹤引

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

STM32+W5500网络通信

1. W5500模块实现数据通信 引脚初始化,此连线方式用于三个模块功能的整体实现 PA3 -> W5500_RSTPA4 -> W5500_SCSPA5 -> W5500_SCKPA6 -> W5500_MISOPA7 ->

STM32驱动W5500连接腾讯云

STM32驱动W5500连接腾讯云 1.W5500简介 W5500 是一款全硬件 TCP/IP 嵌入式以太网控制器,为嵌入式系统提供了更加简易的互联网连接方案。 W5500 集成了 TCP/IP 协议栈, 10/1

stm32f1xx-freemodbus-RTU 移植

STM32F103芯片的 freemodbus RTU的移植和使用。1 示例代码 代码示例上传在 gitee上,仓库地址为freemodbus移植示例 2 freemodbus介绍 Freemodbus是一个奥地利人写的Modb