0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于W5500的Modbus TCP服务器设计

CHANBAEK 来源:木南创智 作者:尹家军 2022-12-14 16:03 次阅读

 前面我们设计实现了W5500的驱动程序,也讲解了驱动的使用方式。在最近一次的项目应用中,正好有一个使用W5500实现TCP通讯的需求,所以我们就使用该驱动程序轻松实现。这一篇中我们就来说一说基于我们W5500通讯驱动程序实现TCP通讯的过程。

1、应用需求

  在本次应用中,要求实现一个基于W5500的Modbus TCP服务器。这个需求的描述虽然只有一句话,但是这个需求的内容可不简单。我们首先来分析一下这个需求的具体内容。

  为了实现基于W5500的Modbus TCP服务器,我们必先须基于W5500实现一个TCP服务器。W5500本身是带硬件协议栈的,但却并不带TCP服务器。不过在我们前面的关于外设驱动库的系列文章中已经封装了W5500的驱动,其中就带有一个TCP服务器,我们可以直接采用就可以了。

  其次我们要在TCP服务器的基础上实现Modbus TCP协议。关于Modbus协议栈,我们以前的文章就讲述过Modbus通讯协议栈的开发问题。而且我们已经将我们开发的Modbus通讯协议栈开源。其中已经封装了Modbus TCP服务器对象,所以我们直接采用这一Modbus通讯协议栈就可以了。

  有了驱动和协议栈,我们还需要考虑应用层面的具体问题,而且也只需要考虑应用层面的具体问题。这里就看出我们前面封装外设驱动和Modbus通讯协议栈的价值所在了。关于应用层面的问题我们主要需要重点考虑几个问题:

  第一,数据的存储类型及地址范围。我们知道Modbus协议常见的数据类型有4种。我们需要考虑在系统中需要使用到的类型及地址,这将决定Modbus协议数据处理回调函数的实现。

  第二,网络配置问题,我们需要通过网络访问这台下位机就需要要为其配置网络。这存在静态配置,动态配置和系统自动分配的问题。作为服务器,我们一般不会希望让系统自动分配。所以我们需要考虑的是如何方便使用者为其分配地址的问题。

  第三,并发访问的问题。挂载在网络上的服务器肯定面临多个客户端来访问的问题。W5500可以实现8个Socket,而Modbus TCP通用的默认端口号是502,当然也可以使用其它端口,只要不冲突就好。所以我们可以考虑使用不同的Socket和不同的端口号来实现并发访问。

2、功能设计

  我们分析了基于W5500实现Modbus TCP服务器的需求。我们现在从硬件和软件两个方面来分析器功能的实现。

2.1、硬件功能设计

  我们知道W5500带有硬件协议栈,集成有以太网控制器和物理层,所以对外我们只需要实现以太网变压器和硬件接口就好了。但与控制器部分的连接则采用SPI接口,除此之外还需要提供中断输入和模式设定的相关接口。在这里我们设计器硬件连接如下:

  在上图中,我们将中断输入引入到MCU的GPIO端口,而模式设定PMODE0、PMODE1、PMODE2均通过电阻上拉到电源。对于W5500来说PMODE0、PMODE1、PMODE2均为高电平表示开启全部功能,所以我们直接拉高而不是引入到MCU引脚来控制。

2.2、软件功能设计

  从需求来说,软件的功能非常简单,就是实现一个Modbus TCP服务器。但实际上,如我们前面所描述的那样,软件需要考虑的问题还是比较多的。从功能实现上主要有3个方面需要考虑:

  第一,实现TCP服务器,这个服务器用于在系统中轮询处理,从W5500获取数据和发送数据给W5500都需要通过这部分来实现。

  第二,TCP服务器得到数据后,我们需要解析数据,并根据解析的上位数据决定进一步的动作,还需要生成返回信息。这部分对应功能就是Modbus TCP服务器的实现。

  第三,根据Modbus TCP服务器解析出的Modbus消息,需要决定下一步的动作,这个具体动作根据功能码的不同可能有不同需求,所以我们需要根据具体的要求实现不同功能码的动作。

  根据上述的设计,我们可以简单的将需要实现的软件功能图示如下:

  上图中,因为W5500的TCP服务器以及Modbus TCP协议栈的相关函数我们都做了封装,所以它们之间的调用都将以回调函数的方式实现。除了上述的软件实现外,还需要注意必要的初始化配置。

3、应用实现

  根据我们前面的设计,接下来我们考虑一下这一需求的具体实现过程。我们将这一过程分为4个部分来分别描述。

3.1、系统的初始化

  在实现具体的功能之前,我们需要对硬件以及软件环境做必要的初始化配置。具体到这里就是对W5500作必要的软硬件配置,包括接口、网络以及回调函数等。具体实例代码如下:

/* 以太网通讯配置 */
void McEthernetConfiguration(void)
{
  uint8_t mac[6]={0x01, 0x08, 0xdc,0x00, 0xab, 0xcd}; //本地Mac地址
  uint8_t ip[4]={192, 168, 1, 190};          //本地IP地址
  uint8_t sn[4]={255,255,255,0};           //子网掩码
  uint8_t gw[4]={192, 168, 1, 1};           //网关地址
  uint8_t dns[4]={0,0,0,0};              //DNS服务器地址
  
  /* 以太网使用GPIO初始化 */
  GPIO_Init_Configuration();
  
  /* SPI1端口初始化 */
  SPI1_Init_Configuration();
  
  /*W5500对象初始化函数*/
  W5500Initialization(&w5500,      //W5500对象
                      mac,        //本地Mac地址
                      ip,        //本地IP地址
                      sn,        //子网掩码
                      gw,        //网关地址
                      dns,        //DNS服务器地址
                      NETINFO_STATIC,  //DHCP类型
                      EnterCritical,   //进入临界区
                      ExitCritical,   //退出临界区
                      EnableChipSelect, //片选使能
                      DisableChipSelect, //片选失能
                      ReadByteBySPI,   //SPI读字节
                      WriteByteBySPI,  //SPI写字节
                      W5500DataParsing, //报文解析函数
                      NULL        //数据请求函数
                      );
}

  在这个实例中,我们对网络部分采用的是静态配置,就是说网络参数是固定不变的,而且我们的测试环境只限于局域网内。

3.2、数据处理函数

  数据处理函数是最灵活的,因为每个项目及每个人对数据处理的要求都是不一样的,只要能符合应用要求就没问题。需要说一下的是,这部分是Modbus协议栈对处理数据的要求,想要详细了解的话,可以看我们以前关于Modbus协议站的文章。对于这个实例,数据处理函数如下:

/*获取想要读取的Coil量的值*/
void GetCoilStatus(uint16_t startAddress,uint16_t quantity,bool *statusList)
{
 uint16_t start;
 uint16_t count;

 /*先判断地址是否处于合法范围*/
 start=(startAddress>CoilStartAddress)?((startAddress<=CoilEndAddress)?startAddress:CoilEndAddress):CoilStartAddress;
 count=((start+quantity-1)<=CoilEndAddress)?quantity:(CoilEndAddress-start);
 
 for(int i=0;i/*获取想要读取的保持寄存器的值*/
void GetHoldingRegister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue)
{
 uint16_t start;
 uint16_t count;

 /*先判断地址是否处于合法范围*/
 start=(startAddress>HoldingRegisterStartAddress)?((startAddress<=HoldingRegisterEndAddress)?startAddress:HoldingRegisterEndAddress):HoldingRegisterStartAddress;
 count=((start+quantity-1)<=HoldingRegisterEndAddress)?quantity:(HoldingRegisterEndAddress-start);
 
 for(int i=0;i/*设置单个线圈的值*/
void SetSingleCoil(uint16_t coilAddress,bool coilValue)
{
 /*先判断地址是否处于合法范围*/
 if(coilAddress<=12)
 {
  dPara.coil[coilAddress]=coilValue;
 }
}
 
/*设置多个线圈的值*/
void SetMultipleCoil(uint16_t startAddress,uint16_t quantity,bool *statusValue)
{
 uint16_t endAddress=startAddress+quantity-1;
 if((startAddress<=12)&&(endAddress<=12))
 {
  for(int i=0;i/*设置单个寄存器的值*/
void SetSingleRegister(uint16_t registerAddress,uint16_t registerValue)
{
 bool noError=(bool)(((50<=registerAddress)&&(registerAddress<=59))
                   ||((73<=registerAddress)&&(registerAddress<=74))
                   ||((90<=registerAddress)&&(registerAddress<=91)));

 if(noError)
 {
  aPara.holdingRegister[registerAddress]=registerValue;
 }
 
}
 
/*设置多个寄存器的值*/
void SetMultipleRegister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue)
{
 uint16_t endAddress=startAddress+quantity-1;
 
 bool noError=(bool)(((18<=startAddress)&&(startAddress<=28)&&(18<=endAddress)&&(endAddress<=28))
                   ||((50<=startAddress)&&(startAddress<=59)&&(50<=endAddress)&&(endAddress<=59))
                   ||((73<=startAddress)&&(startAddress<=74)&&(73<=endAddress)&&(endAddress<=74))
                   ||((90<=startAddress)&&(startAddress<=91)&&(90<=endAddress)&&(endAddress<=91)));
 if(noError)
 {
  for(int i=0;i

3.2、数据解析函数

  大家可能在前面的初始化函数中发现有一个名为W5500DataParsing的数据解析函数。这个函数是W5500驱动中,TCP服务器的要求,实现对数据的解析。因为具体的应用层协议解析多不胜数,所以设计成了回调函数,其函数原型如下:

/*解析接收到的数据*/
typedef uint16_t (*W5500DataParsingType)(uint8_t *rxBuffer,uint16_t rxSize,uint8_t *txBuffer);

  对于我们来说,我们需要根据具体的应用层协议来实现这一函数。不过我们采用的Modbus TCP协议,在我们的Modbus协议栈中已经实现了解析函数,所以我们调用如下:

/*报文解析函数*/
static uint16_t W5500DataParsing(uint8_t *rxBuffer,uint16_t rxSize,uint8_t *txBuffer)
{
  /*解析接收到的信息,返回响应命令的长度*/
  return ParsingClientAccessCommand(rxBuffer,txBuffer);
}

3.3、TCP服务器

  我们在前面已经说过了,需要对服务器进行轮询。所以我们需要在一个进程中轮询访问W5500的TCP服务器。同样我们也要考虑多客户端同时访问的问题,我们将轮询函数实现如下:

/* 以太网通讯处理 */
void McEthernetProcess(void)
{
  /*TCP服务器数据通讯*/
  W5500TCPServer(&w5500,Socket0,502);
  
  W5500TCPServer(&w5500,Socket1,503);
  
  W5500TCPServer(&w5500,Socket2,504);
  
  W5500TCPServer(&w5500,Socket3,505);
  
  W5500TCPServer(&w5500,Socket4,506);
  
  W5500TCPServer(&w5500,Socket5,507);
  
  W5500TCPServer(&w5500,Socket6,508);
  
  W5500TCPServer(&w5500,Socket7,509);
}

  事实上使用同一个Socket和不同的端口也是可以实现多客户端访问的,但既然有8个Socket,用起来自然更好一点。

4、应用验证

  我们已经根据需求实现了一个Modbus TCP服务器,究竟效果如何呢?我们还需要测试一下,以确认设计的正确性。

4.1、通讯测试

  我们将目标板连接到局域网中,使用著名的Modbus Poll软件来测试一下我们设计的程序是否符合要求。

  我们首先在一台机器上连接端口为504的Modbus TCP服务器,连接正常且数据获取也完全正确。具体如下图所示:

  同时,我们采用局域网内的另一台机器连接端口为502的Modbus TCP服务器,连接正常且数据获取也完全正确。具体如下图所示:

  经过上述测试,我们可以确定我们实现的Modbus TCP服务器是可行的,而且在多客户端并行访问下也可以正确工作。

4.2、小结

  这一篇中,我们实现了可以支持多客户端访问的Modbus TCP服务器,经测试运行也符合设计预期。这里我们将需要考虑的几个问题总结如下:

  关于初始化配置的问题,在这个例子中,我们对网络的配置是直接在软件上固定死的,这样做虽然简单直接但并不是一个好的选择。更好的办法是可以让使用者自己配置,方法有多种,可以根据自己的实际情况,在软件上进一步的考虑。

  关于数据处理的问题,具体的数据处理与实际的应用需求有关,也与应用层协议的要求有关,这个例子中实现的Modbus的数据处理函数并不是唯一的,但可参考其思路。

  关于数据解析的问题,在本例中实现的是Modbus TCP服务器的解析函数。对于不同的应用协议需要编写不同的解析函数,这部分是灵活性最大的,支持所有可运行于TCP应用层的通讯协议。

  关于多客户端访问的问题,W5500可以实现8个Socket,而Modbus TCP默认端口号是502,当然也可以使用其它端口。所以我们可以考虑使用不同的Socket和不同的端口号来实现并发访问。事实上,经过我们测试使用同一个Socket和不同的端口也是可以实现多客户端访问的,有兴趣的同仁可以试试。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • MODBUS
    +关注

    关注

    28

    文章

    1802

    浏览量

    76969
  • 服务器
    +关注

    关注

    12

    文章

    9129

    浏览量

    85346
  • TCP
    TCP
    +关注

    关注

    8

    文章

    1353

    浏览量

    79059
  • ModBus协议
    +关注

    关注

    3

    文章

    177

    浏览量

    33422
  • W5500
    +关注

    关注

    5

    文章

    45

    浏览量

    17582
收藏 人收藏

    评论

    相关推荐

    W5500 keep-alive的用途与用法

    本帖最后由 Katrina_WIZnet 于 2015-9-2 14:50 编辑 大家是否遇到过这样的问题,W5500作为服务器已经建立连接,突然网线掉了,然后再去连接W5500,就连
    发表于 08-04 09:44

    WIZnet W5500的特点和应用介绍

    `W5500是一款全硬件TCP/IP嵌入式以太网控制,为嵌入式系统提供了更加简易的互联网连接方案。W5500集成了TCP/IP协议栈,10
    发表于 05-05 13:28

    野火STM32+野火W5500实现服务器功能的问题

    `急!急!急!求大神相助!我用野火STM32+野火W5500实现服务器的功能,上位机通过IE输入W5500的IP地址,W5500返回给上位机一个点亮LED的页面,现在有“点亮LED”和
    发表于 11-18 21:47

    W5500无法连接服务器

    我的W5500能PING通,可是就是没办法连接服务器,究竟是什么原因呢?
    发表于 06-25 10:26

    W5500芯片通信问题

    w5500网络通信问题:电脑做服务器w5500做客户端,自发自收,500ms一次,刚开始正常,过一会电脑端收到的数据就不对了,如图:可能是哪儿的问题, 程序里面的接收buff吗
    发表于 12-11 21:23

    W5500接收巨型数据包有什么好用的办法

    W5500作为客户端利用TCP协议接收远大于自身缓存的数据包时,服务器一次性发送一个几十M为单位的文件,依靠TCP/IP协议栈维护数据收发的情况下,如何能保障接收的可靠性? 客户端有
    发表于 06-05 18:44

    W5500TCP Client模式下,断电重启之后无法立即连接到服务器

    `分析:这是由于客户端没有主动发送断开请求,造成服务器并不知道Socket已发生异常断开; 重新上电之后,芯片以相同的IP和端口连接服务器,而服务器还认为此Socket链接存在,所以拒绝芯片的立即
    发表于 05-04 16:54

    基于FPGA和W5500的以太网传输系统实现

    协议数据量,从而提高服务器性能。TOE方式不需进行软件协议栈移植,开发周期缩短,CPU负担降低,稳定性能提高。其处理方式框图如下图1所示。图 1 TOE方式进行TCP/IP协议栈处理2.2 W5500
    发表于 08-07 10:10

    STM32 W5500是如何提交数据到远程服务器

    STM32 W5500是如何提交数据到远程服务器的?如何去实现呢?
    发表于 11-26 07:43

    硬件SPI驱动W5500网口

    我有一块STM32F303板子,用它驱动SPI接口W5500网口,硬件SPI. 现在TCP服务器,UDP都好了,就差TCP客户端了。以前用其他单片机写这个程序
    发表于 06-12 20:21

    w5500原理图_w5500电路图

    W5500是WIZnet推出的高性能以太网接口芯片系列之一,内部集成全硬件TCP/IP协议栈+MAC+PHY。全硬件协议栈威廉希尔官方网站 采用硬件逻辑门电路实现复杂的TCP/IP协议簇,其应用具有简单快速
    发表于 10-23 19:14 5.5w次阅读
    <b class='flag-5'>w5500</b>原理图_<b class='flag-5'>w5500</b>电路图

    W5500数据手册免费下载

    W5500芯片是硬连线的TCP / IP嵌入式以太网控制,可提供与嵌入式系统的Internet连接更加轻松。 W5500使用户能够拥有只需使用其中的单个芯片即可在其应用程序中实现In
    发表于 05-18 11:26 28次下载

    RaspberryPi Pico Web服务器w5100S(W5500)

    电子发烧友网站提供《RaspberryPi Pico Web服务器w5100S(W5500).zip》资料免费下载
    发表于 11-10 14:48 1次下载
    RaspberryPi Pico Web<b class='flag-5'>服务器</b><b class='flag-5'>w</b>5100S(<b class='flag-5'>W5500</b>)

    W5500 数据手册中文资料

    W5500是一款全硬件 TCP/IP 嵌入式以太网控制,为嵌入式系统提供了更加简易的互联网连接方案。W5500集成了TCP/IP 协议栈,
    发表于 01-06 16:58 19次下载

    W5500浏览配置_Keil

    W5500浏览配置_Keil,浏览配置就是在电路板上搭载一个嵌入式的web服务器,所以功能一般的很有限。
    发表于 01-07 14:14 3次下载