文章目录
- 一.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.3V | 3.3V电源输入引脚 | 5V | 5V电源输入引脚 |
MISO | SPI主机输入从机输出引脚 | GND | 电源地引脚 |
MOSI | SPI主机输出从机输入引脚 | RST | W5500硬件初始化引脚(低电平有效) |
SCS | SPI SLAVE选择引脚(低电平有效) | INT | W5500中断引脚(低电平有效) |
SCLK | SPI时钟引脚 | 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