基于STM32和W5500的Modbus TCP通讯

时间:2021-10-05 01:19:36

在最近的一个项目中需要实现Modbus TCP通讯,而选用的硬件平台则是STM32F103和W5500,软件平台则选用IAR EWAR6.4来实现。

1、移植千的准备工作

为了实现Modbus TCP通讯首先需要下载W5500的驱动源码,可以到WIZnet的官网下载:

http://wizwiki.net/wiki/doku.php?id=products:w5500:driver

下载下来的压缩包,解压后如下图:

基于STM32和W5500的Modbus TCP通讯

需要将ethernet文件夹拷贝到我们的项目目录中:

基于STM32和W5500的Modbus TCP通讯

并在IAR的项目下添加相关的文件和路径,主要是socket.c、w5500.c、wizchip_.conf.c三个文件。这三个文件分别实现socket、硬件驱动及相关通讯配置功能,具体可以查看相应的源码级手册。

基于STM32和W5500的Modbus TCP通讯

并在如下图所示的项目选项设置中添加Ethernet和Ethernet\W5500目录。

基于STM32和W5500的Modbus TCP通讯基于STM32和W5500的Modbus TCP通讯

2、移植过程和代码编写

在完成以上工作后就可以开始真正地移植工作了。具体步骤如下:

  • 硬件配置及初始化。
  • 以太网通讯配置的初始化。
  • 实现具体的通讯过程。

2.1、硬件的配置及初始化

由于W5500通过SPI接口与STM32通讯,所以硬件配置和初始化是非常简单的,与W5500实际上没有关系,使一些通用的操作。事实上就是STM32F103的SPI接口初始化的过程,需要实现RCC、GPIO以及SPI的初始化就可以了。关于这部分可以查看ST的例程。

2.2、以太网通讯配置的初始化

以太网通讯配置的初始化主要有三个方面的内容:

  • 注册TCP通讯相关的回调函数  RegisterFunction();
  • 初始化芯片参数  ChipParametersConfiguration();
  • 初始化网络通讯参数  NetworkParameterConfiguration()

三个函数的具体实现内容如下:

//函数注册,首先,应由用户实现SPI注册回调函数来访问WIZCHIP
void RegisterFunction(void)
{
//临界区回调函数
reg_wizchip_cris_cbfunc(SPI_CrisEnter, SPI_CrisExit); //注册临界区函数
//片选回调函数
#if _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_VDM_
reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect);//注册SPI片选信号函数
#elif _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_FDM_
reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect); // CS必须为低电平.
#else
#if (_WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_SIP_) != _WIZCHIP_IO_MODE_SIP_
#error "Unknown _WIZCHIP_IO_MODE_"
#else
reg_wizchip_cs_cbfunc(wizchip_select, wizchip_deselect);
#endif
#endif
//SPI的读写回调函数
reg_wizchip_spi_cbfunc(SPI_ReadByte, SPI_WriteByte); //注册读写函数
}

注册函数实际上就是函数指针的调用,可参考C语言函数指针部分内容。对于以上注册的函数,SPI_WriteByte需要说明一下,无论是用可函数还是直接操作寄存器,在写完之后都需要再读一下(红色部分),否则就会在客户端出现连接TCPServer超时的报警,没明白什么原因。

//写1字节数据到SPI总线

void SPI_WriteByte(uint8_t TxData)

{                       

//  while((SPI2->SR&SPI_I2S_FLAG_TXE)==0);        //等待发送区空              

//  SPI2->DR=TxData;                              //发送一个byte

//  while((SPI2->SR&SPI_I2S_FLAG_RXNE)==0);       //等待接收完一个byte 

//  SPI2->DR;

  while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);        //等待发送区空

  SPI_I2S_SendData(SPI2,TxData);                                        //发送一个byte

  while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET);        //等待接收完一个byte

  SPI_I2S_ReceiveData(SPI2);                                            //返回接收的数据

}

初始化芯片参数:

//初始化芯片参数

void ChipParametersConfiguration(void)

{

  uint8_t tmp;

  uint8_t memsize[][] = {{,,,,,,,},{,,,,,,,}};

  //WIZCHIP SOCKET缓存区初始化

  if(ctlwizchip(CW_INIT_WIZCHIP,(void*)memsize) == -){

    //printf("WIZCHIP Initialized fail.\r\n");

  while();

  }

  //PHY物理层连接状态检查

  do{

    if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp) == -){

      //printf("Unknown PHY Link stauts.\r\n");

    }

  }while(tmp == PHY_LINK_OFF);

}

以上实现网络物理层的配置。

初始化WIZCHIP中的网络参数信息:

//初始化WIZCHIP中的网络参数信息

void NetworkParameterConfiguration(void)

{

  uint8_t tmpstr[];

  ctlnetwork(CN_SET_NETINFO, (void*)&gWIZNETINFO);

  ctlnetwork(CN_GET_NETINFO, (void*)&gWIZNETINFO);

  ctlwizchip(CW_GET_ID,(void*)tmpstr);

}

其中gWIZNETINFO是一个wiz_NetInfo类型的结构体变量,该结构体在wizchip_conf.h中定义,用于设置mac地址、IP地址等网络参数,具体如下:

typedef struct wiz_NetInfo_t

{

   uint8_t mac[];  ///< Source Mac Address

   uint8_t ip[];   ///< Source IP Address

   uint8_t sn[];   ///< Subnet Mask

   uint8_t gw[];   ///< Gateway IP Address

   uint8_t dns[];  ///< DNS server IP Address

   dhcp_mode dhcp;  ///< 1 - Static, 2 - DHCP

}wiz_NetInfo;

至此网络部分的初始化就已完成。

2.3、具体通讯过程的实现

经过前面的配置网络已经可以ping通了,下面可以实现具体的应用。对于我这个项目就是可是实现Modbus TCP的编写了。

编写TCP Server,这部分有很多资料,直接附代码:

//TCP服务器数据通讯

int32_t TCPServer(uint8_t sn, uint16_t port)

{

  int32_t ret;

  uint8_t socketStatus=getSn_SR(sn);

  switch(socketStatus)

  {

    case SOCK_ESTABLISHED :

      {

        if(getSn_IR(sn) & Sn_IR_CON)

        {

          setSn_IR(sn,Sn_IR_CON);

        }

        uint16_t size=;

        if((size = getSn_RX_RSR(sn)) > )

        {

          if(size > DATA_BUFFER_SIZE)

          {

            size = DATA_BUFFER_SIZE;

          }

          uint8_t rxBuffer[DATA_BUFFER_SIZE];

          ret = recv(sn,rxBuffer,size);

          if(ret <= )

          {

            return ret;

          }

          //添加数据解析及响应的函数

          uint8_t txBuffer[DATA_BUFFER_SIZE];

          uint16_t length=ReceivedDataParsing(rxBuffer,txBuffer);

          uint16_t sentsize=;

          while(length != sentsize)

          {

            ret = send(sn,txBuffer+sentsize,length-sentsize);

            if(ret < )

            {

              close(sn);

              return ret;

            }

            sentsize += ret; // 不用管SOCKERR_BUSY, 因为它是零.

          }

        }

        break;

      }

    case SOCK_CLOSE_WAIT :

      if((ret=disconnect(sn)) != SOCK_OK)

      {

        return ret;

      }

      break;

    case SOCK_INIT :

      if( (ret = listen(sn)) != SOCK_OK)

      {

        return ret;

      }

      break;

    case SOCK_CLOSED:

      if((ret=socket(sn,Sn_MR_TCP,port,0x00)) != sn)

      {

        return ret;

      }

      break;

    default:

      break;

  }

  return ;

}

其中ReceivedDataParsing(rxBuffer,txBuffer)实现具体的Modbus协议,根据具体的需求而定。

通过Modscan连接测试,结果正确。

基于STM32和W5500的Modbus TCP通讯

欢迎关注:

基于STM32和W5500的Modbus TCP通讯

基于STM32和W5500的Modbus TCP通讯的更多相关文章

  1. C&num; ModBus Tcp读写数据 与服务器进行通讯

    前言 本文将使用一个NuGet公开的组件技术来实现一个ModBus TCP的客户端,方便的对Modbus tcp的服务器进行读写,这个服务器可以是电脑端C#设计的,也可以是PLC实现的,也可以是其他任 ...

  2. Modbus tcp 格式说明 通讯机制 附C&num;测试工具用于学习,测试

    前言: 之前的博客介绍了如何用C#来读写modbus tcp服务器的数据,文章:http://www.cnblogs.com/dathlin/p/7885368.html 当然也有如何创建一个服务器文 ...

  3. &period;Net开发笔记(十五) 基于&OpenCurlyDoubleQuote;泵”的TCP通讯(接上篇)

    上一篇博客中说了基于“泵”的UDP通讯,附上了一个Demo,模拟飞鸽传书的功能,功能不太完善,主要是为了说明“泵”在编程中的应用.本篇文章我再附上一个关于TCP通讯的两个Demo,也都采用了“泵”模式 ...

  4. 基于&OpenCurlyDoubleQuote;泵”的TCP通讯(接上篇)

    基于“泵”的TCP通讯(接上篇) 上一篇博客中说了基于“泵”的UDP通讯,附上了一个Demo,模拟飞鸽传书的功能,功能不太完善,主要是为了说明“泵”在编程中的应用.本篇文章我再附上一个关于TCP通讯的 ...

  5. wp8使用Beetle&period;NetPackage实现基于TCP通讯的订单查询

    在新版本的Beetle.NetPackage中提供了对Protobuf和Controller的支持,所以在WP8下使用Beetle.NetPackage进行基于TCP的数据交互则一件非常简单事情.下面 ...

  6. 基于QTcpSocket和QTcpServer的Tcp通讯以及QDataStream序列化数据

    最近要在QT下开发Tcp通讯,发送序列化数据以便于接收. 这里涉及到几个问题: 1.QTcpSocket.QTcpServer的通讯 2.QDataStream序列化数据 多的不说,直接上干货!!! ...

  7. 开放型Modbus&sol;TCP 规范

    修订版 1.0,1999 年3 月29 日Andy SwalesSchneider 电气公司aswales@modicon.com目录目录............................... ...

  8. LwIP之socket应用--WebServer和Modbus TCP

    1. 引言 LwIP是嵌入式领域一个流行的以太网协议栈, LwIP开放源码,用C写成非常方便移植,并且支持socket接口,使用者可以集中精力处理应用功能. 本文就是LwIP socket使用的一个小 ...

  9. modbus tcp 入门详解

    Modbus tcp 格式说明 通讯机制 附C#测试工具用于学习,测试   前言: 之前的博客介绍了如何用C#来读写modbus tcp服务器的数据,文章:http://www.cnblogs.com ...

随机推荐

  1. px、dp和sp,这些单位有什么区别?

    DP 这个是最常用但也最难理解的尺寸单位.它与“像素密度”密切相关,所以 首先我们解释一下什么是像素密度.假设有一部手机,屏幕的物理尺寸为1.5英寸x2英寸,屏幕分辨率为240x320,则我们可以计算 ...

  2. poj 1816 &lpar;Trie &plus; dfs&rpar;

    题目链接:http://poj.org/problem?id=1816 思路:建好一颗Trie树,由于给定的模式串可能会重复,在原来定义的结构体中需要增加一个vector用来记录那些以该节点为结尾的字 ...

  3. iOS NSNotificationCenter(消息机制)

    转自:http://blog.csdn.net/liliangchw/article/details/8276803 对象之间进行通信最基本的方式就是消息传递,在Cocoa中提供Notificatio ...

  4. python进程池剖析(一)

    python中两个常用来处理进程的模块分别是subprocess和multiprocessing,其中subprocess通常用于执行外部程序,比如一些第三方应用程序,而不是Python程序.如果需要 ...

  5. java 常见的几种运行时异常RuntimeException

    常见的几种如下:   NullPointerException - 空指针引用异常ClassCastException - 类型强制转换异常.IllegalArgumentException - 传递 ...

  6. JVM之垃圾回收

    1.哪些内存需要回收?判断对象已死的方法(存活判定算法) 1.引用计数算法:难以解决对象之间相互循环引用的问题,不使用. 2.可达性分析算法:通过一系列“GC Root”对象作为起始点向下搜索,所走过 ...

  7. 使用pyenv安装多个Python版本

    pyenv是一个便于使用多版本Python环境的工具 pyenv使用shell脚本编写的,只需要下载就可以使用了,不需要root用户,这个我比较喜欢. 具体介绍看网址:https://github.c ...

  8. java&period;util&period;ConcurrentModificationException详解

    引用于http://blog.csdn.net/dabing69221/article/details/40065071 在使用set/map时,一个可爱的小bug:Java.util.Concurr ...

  9. FXML Stuffs &lpar;include and define&rpar;

    FXML Stuffs (include and define)Hello folks,Today I would like to blog about the FXML define and inc ...

  10. Java如何获取本地计算机的IP地址和主机名?

    在Java编程中,如何获取本地计算机的IP地址和主机名? 以下示例显示如何使用InetAddress类的getLocalAddress()方法获取系统的本地IP地址和主机名. package com. ...