目录
一.理论概述
- 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 | 悬空 |
- Modbus简介
Modbus是Modicon(施耐德)公司于1979年开发的串行通信协议。它最初设计用于公司的可编程逻辑控制器(PLC)。Modbus是一种开放式协议,支持使用RS232/RS485/RS422协议的串行设备,同时还支持调制解调器。它的简单性以及制造商可以免费将其纳入其产品的事实使其成为连接工业电子设备的最流行的方法。
Modbus比其他通信协议使用的更广泛的主要原因有以下几点:
公开发表并且无著作权要求;
易于部署和维护;
对供应商来说,修改移动本地的比特或字节没有很多限制;
Modbus通过设备之间的串行线进行数据传输。最简单的设置是使用一根串行电缆连接两个设备(主设备和从设备)上的串行端口。
数据以称为比特的1和0的序列发送。每个位都作为电压发送。0被发送为正电压,1被发送为负电压。
- 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 数据值
二.TCP数据通信
链接:https://pan.baidu.com/s/1ninW1lpO94fl2GtDWx5csg
提取码:7yqt
调试工具使用该压缩包里面的TCPUDPDebug102_Setup,自行安装.
STM32与W5500接线方法
PC15->W5500_RST(源程序使用的是PC5,这里没有该引脚修改为PC15)
PC4->W5500_INT(使用寄存器查询方式的例程时,此引脚可以不接,其他例程可能涉及修改引脚)
PA4->W5500_SCS
PA5->W5500_SCK
PA6->W5500_MISO
PA7->W5500_MOSI
具体例程的调试过程请参考压缩包中NiRen_W5500模块用户手册(用Adobe Reader打开).pdf
文件。
- 根据自己的IP地址修改工程代码
UDP:
服务端:
客户端:
这里我调试了查询寄存器方式中的UDP模式,服务端模式和客户端模式例程,调试结果如下:
UDP:
客户端:
服务器:
三.STM32+W5500+modbus协议编程
- 初始化从机网络:
/* 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]=0;
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]=0;
array[3]=199;
Write_Bytes(SIPR, array, 4);//设置W5500的IP地址
}
简单响应函数:
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++;
}
- 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);
}
四.STM32+W5500的web服务
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);
// 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_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.0.199,通过对PB7的操作来实现缺省值和flash之间的切换,设置好的参数写入flash,w5500接的是SPI2,单片机是stm32f103c8t6。
第一个网页没有任何修改权限,只有输入登录密码(初始密码:123456
或者万能密码:765997)进入修改页面,可以修改参数,包括登录密码都可以修改。
浏览器用的是IE。
五.参考链接
STM32+W5500网络通信
STM32+W5500以太网模块
STM32+W5500+Freemodbus Modbus-TCP协议功能实现,工程文件下载
基于STM32_DM9000_UIP_FreeModbus的Modbus-TCP功能实现工程下载
STM32F103 W5500做的web服务