STM32+W5500以太网模块

本文介绍了如何使用STM32单片机配合W5500以太网模块进行网络通信,涉及到模块介绍、排针功能、调试方法以及Modbus协议的编程。通过实例详细讲解了Modbus/TCP协议的查询与响应,并提供了初始化从机网络和创建简单响应函数的代码示例。此外,还分享了STM32实现的web服务,包括工程代码和IP配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

      • 一.W5500以太网模块介绍
      • 二.模块排针功能表
      • 三.调试方法
      • 四.学习STM32+W5500+modbus协议编程
        • 1.modbus简介
        • 2.modbus/TCP协议
        • 3.从机代码
          • 3.1初始化从机网络
          • 3.2简单响应函数
          • 3.3main函数循环等待连接
          • 3.4 运行
      • 五.学习STM32+W5500的web服务
      • 六.参考链接

一.W5500以太网模块介绍

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

二.模块排针功能表

排针标识功能说明排针标识功能说明
3.3V3.3V电源输入引脚5V5V电源输入引脚
MISOSPI主机输入从机输出引脚GND电源地引脚
MOSISPI主机输出从机输入引脚RSTW5500硬件初始化引脚(低电平有效)
SCSSPI SLAVE选择引脚(低电平有效)INTW5500中断引脚(低电平有效)
SCLKSPI时钟引脚NC悬空

三.调试方法

资源分享:

链接:https://pan.baidu.com/s/1fiWWfmWQT9CNh4EimU-Igw
提取码:1234

调试工具使用该压缩包里面的TCPUDPDebug102_Setup,自行安装.

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文件。

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

四.学习STM32+W5500+modbus协议编程

1.modbus简介

Modbus是Modicon(施耐德)公司于1979年开发的串行通信协议。它最初设计用于公司的可编程逻辑控制器(PLC)。 Modbus是一种开放式协议,支持使用RS232/RS485/RS422协议的串行设备,同时还支持调制解调器。 它的简单性以及制造商可以免费将其纳入其产品的事实使其成为连接工业电子设备的最流行的方法。
Modbus比其他通信协议使用的更广泛的主要原因有以下几点:

公开发表并且无著作权要求;
易于部署和维护;
对供应商来说,修改移动本地的比特或字节没有很多限制;
Modbus通过设备之间的串行线进行数据传输。最简单的设置是使用一根串行电缆连接两个设备(主设备和从设备)上的串行端口。
数据以称为比特的1和0的序列发送。每个位都作为电压发送。0被发送为正电压,1被发送为负电压。如下图所示;
在这里插入图片描述
主从模式
Modbus解决了通过串行线路在电子设备之间发送信息的问题。 该协议在遵循该协议的体系结构中实现主/从模型。
Modbus主站(Master)负责从其他设备(Slave)请求信息。 标准Modbus网络中有一个Modbus主站。具体如下图所示;
在这里插入图片描述
主设备向从设备请求信息,最多大约可达到240个 . 每个从设备都有自己唯一的从设备地址标识(Slave Address)。
除了从从设备请求信息之外,主设备还可以写入从设备的内部寄存器。
协议的分类
Modbus的应用十分广泛,特别是在工业控制领域,具体如下图所示;
在这里插入图片描述
我们可以看到最常用Modbus协议总共有以下四种:Modbus ASCII, Modbus RTU, Modbus Plus,Modbus TCP;

具体如下图所示;

在这里插入图片描述
Modbus ASCII

当设备设置为使用ASCII(美国信息交换标准代码)模式在MODBUS串行线上进行通信时,消息中的每个8位字节将作为两个ASCII
4位字符发送。当物理通信链路或设备的功能不允许符合RTU计时器管理要求时,使用此模式。
所以此模式的效率不如RTU,因为每个字节需要两个字符。示例:字节0x7D编码为两个字符:0x35和0x42(在ASCII表中为0x37
=‘7’,而0x44 =‘D’)。

Modbus RTU

Modbus RTU是一种紧凑的,采用二进制表示数据的方式;因为使用二进制编码和CRC错误检查的结合使得Modbus
RTU适用于工业应用,因为它比ASCII字符的替代方案更有效地传输。 在Modbus
RTU与ASCII之间进行选择时,如果考虑性能,则RTU是首选。

Modbus TCP

Modbus TCP 是在TCP/IP网络上运行的Modbus的实现,旨在允许Modbus ASCII / RTU协议在基于TCP /
IP的网络上传输。Modbus / TCP将Modbus消息嵌入TCP /
IP帧内。尽管实现起来非常简单,但是与网络相关的特性增加了一些挑战。例如,由于Modbus主机期望并要求在一定时间范围内对其轮询做出响应,因此必须考虑TCP
/ IP网络的不确定性(和其他方面)。 Modbus ASCII和Modbus TCP之间的主要区别在于,Modbus
ASCII所需的LRC错误检查由IP层执行。

对于以上TCP/RTU/ASCII的这三种通信协议在数据模型和功能调用上都是相同的,只有封装方式是不同的。

Modbus Plus

Modbus有一个扩展版本Modbus Plus(Modbus+或者MB+),不过此协议是Modicon专有的,和Modbus不同。
它需要一个专门的协处理器来处理类似HDLC的高速令牌旋转。它使用1Mbit/s的双绞线,并且每个节点都有转换隔离设备,是一种采用转换/边缘触发而不是电压/水平触发的设备。连接Modbus
Plus到计算机需要特别的接口,通常是支持ISA(SA85),PCI或者PCMCIA总线的板卡。

2.modbus/TCP协议
功能码作用
01读取线圈状态
02读取输入状态
03读取保持寄存器
04读取输入寄存器
05强置单线圈
06预置单寄存器
07读取异常状态
08回送诊断校验
09编程(只用于484)
0A控询
0B读取事件计数
0C读取通信事件记录
0D编程(184/384/484/584等)
0E探寻
0F强置多线圈
10预置多线圈
11报告多寄存器
12可使主机模拟编程功能
13重置通信链路
14读取通用参数
15写入通用参数
16屏蔽写寄存器
17读/写多个寄存器

查询报文

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

响应报文

00 6D 00 00 00 05 01 03 02 00 17
00 6D 查询编号
00 00 协议
00 05 数据包长度
01 设备地址
03 功能码
02 数据长度
00 17 数据值
3.从机代码
3.1初始化从机网络
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]=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]=0;
	IP_Addr[3]=199;

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

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


3.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.3main函数循环等待连接
 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);

  }


3.4 运行

使用modbus poll建立连接
配置如下:

在这里插入图片描述

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

五.学习STM32+W5500的web服务

工程代码分享:
链接:https://pan.baidu.com/s/1g7mO-mAxbyfSrx0VKafWBg
提取码:1234
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();//W5500相关引脚配置
	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();//开启http服务
		else
		  	JTXD_do_http();
		if(reboot_flag)
			NVIC_SystemReset();//发起系统复位请求复位单片机
//        reboot();
        
	}
}

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);
}

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
}

说明:

初始IP地址是192.168.1.111,通过对PB7的操作来实现缺省值和flash之间的切换,设置好的参数写入flash,w5500接的是SPI2,单片机是stm32f103c8t6。
第一个网页没有任何修改权限,只有输入登录密码(初始密码:123456
或者万能密码:765997)进入修改页面,可以修改参数,包括登录密码都可以修改。
浏览器用的是IE。

六.参考链接

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

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

http://www.coder100.com/index/index/content/id/953774

https://blog.csdn.net/weixin_46129506/article/details/122149700

智能网联汽车的安全员高级考试涉及多个方面的专业知识,包括但不限于自动驾驶技术原理、车辆传感器融合、网络安全防护以及法律法规等内容。以下是针对该主题的一些核心知识点解析: ### 关于智能网联车安全员高级考试的核心内容 #### 1. 自动驾驶分级标准 国际自动机工程师学会(SAE International)定义了六个级别的自动驾驶等级,从L0到L5[^1]。其中,L3及以上级别需要安全员具备更高的应急处理能力。 #### 2. 车辆感知系统的组成与功能 智能网联车通常配备多种传感器,如激光雷达、毫米波雷达、摄像头和超声波传感器等。这些设备协同工作以实现环境感知、障碍物检测等功能[^2]。 #### 3. 数据通信与网络安全 智能网联车依赖V2X(Vehicle-to-Everything)技术进行数据交换,在此过程中需防范潜在的网络攻击风险,例如中间人攻击或恶意软件入侵[^3]。 #### 4. 法律法规要求 不同国家和地区对于无人驾驶测试及运营有着严格的规定,考生应熟悉当地交通法典中有关自动化驾驶部分的具体条款[^4]。 ```python # 示例代码:模拟简单决策逻辑 def decide_action(sensor_data): if sensor_data['obstacle'] and not sensor_data['emergency']: return 'slow_down' elif sensor_data['pedestrian_crossing']: return 'stop_and_yield' else: return 'continue_driving' example_input = {'obstacle': True, 'emergency': False, 'pedestrian_crossing': False} action = decide_action(example_input) print(f"Action to take: {action}") ``` 需要注意的是,“橙点同学”作为特定平台上的学习资源名称,并不提供官方认证的标准答案集;建议通过正规渠道获取教材并参加培训课程来准备此类资格认证考试。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值