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

1、移植千的准备工作

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

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

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

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

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

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

2、移植过程和代码编写

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

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

2.1、硬件的配置及初始化

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

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

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

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

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

  1. //函数注册,首先,应由用户实现SPI注册回调函数来访问WIZCHIP
  2. void RegisterFunction(void)
  3. {
  4. //临界区回调函数
  5. reg_wizchip_cris_cbfunc(SPI_CrisEnter, SPI_CrisExit); //注册临界区函数
  6. //片选回调函数
  7. #if _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_VDM_
  8. reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect);//注册SPI片选信号函数
  9. #elif _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_FDM_
  10. reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect); // CS必须为低电平.
  11. #else
  12. #if (_WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_SIP_) != _WIZCHIP_IO_MODE_SIP_
  13. #error "Unknown _WIZCHIP_IO_MODE_"
  14. #else
  15. reg_wizchip_cs_cbfunc(wizchip_select, wizchip_deselect);
  16. #endif
  17. #endif
  18. //SPI的读写回调函数
  19. reg_wizchip_spi_cbfunc(SPI_ReadByte, SPI_WriteByte); //注册读写函数
  20. }

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

  1. //写1字节数据到SPI总线
  2.  
  3. void SPI_WriteByte(uint8_t TxData)
  4.  
  5. {
  6.  
  7. // while((SPI2->SR&SPI_I2S_FLAG_TXE)==0); //等待发送区空
  8.  
  9. // SPI2->DR=TxData; //发送一个byte
  10.  
  11. // while((SPI2->SR&SPI_I2S_FLAG_RXNE)==0); //等待接收完一个byte
  12.  
  13. // SPI2->DR;
  14.  
  15. while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET); //等待发送区空
  16.  
  17. SPI_I2S_SendData(SPI2,TxData); //发送一个byte
  18.  
  19. while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET); //等待接收完一个byte
  20.  
  21. SPI_I2S_ReceiveData(SPI2); //返回接收的数据
  22.  
  23. }

初始化芯片参数:

  1. //初始化芯片参数
  2.  
  3. void ChipParametersConfiguration(void)
  4.  
  5. {
  6.  
  7. uint8_t tmp;
  8.  
  9. uint8_t memsize[][] = {{,,,,,,,},{,,,,,,,}};
  10.  
  11. //WIZCHIP SOCKET缓存区初始化
  12.  
  13. if(ctlwizchip(CW_INIT_WIZCHIP,(void*)memsize) == -){
  14.  
  15. //printf("WIZCHIP Initialized fail.\r\n");
  16.  
  17. while();
  18.  
  19. }
  20.  
  21. //PHY物理层连接状态检查
  22.  
  23. do{
  24.  
  25. if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp) == -){
  26.  
  27. //printf("Unknown PHY Link stauts.\r\n");
  28.  
  29. }
  30.  
  31. }while(tmp == PHY_LINK_OFF);
  32.  
  33. }

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

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

  1. //初始化WIZCHIP中的网络参数信息
  2.  
  3. void NetworkParameterConfiguration(void)
  4.  
  5. {
  6.  
  7. uint8_t tmpstr[];
  8.  
  9. ctlnetwork(CN_SET_NETINFO, (void*)&gWIZNETINFO);
  10.  
  11. ctlnetwork(CN_GET_NETINFO, (void*)&gWIZNETINFO);
  12.  
  13. ctlwizchip(CW_GET_ID,(void*)tmpstr);
  14.  
  15. }

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

  1. typedef struct wiz_NetInfo_t
  2.  
  3. {
  4.  
  5. uint8_t mac[]; ///< Source Mac Address
  6.  
  7. uint8_t ip[]; ///< Source IP Address
  8.  
  9. uint8_t sn[]; ///< Subnet Mask
  10.  
  11. uint8_t gw[]; ///< Gateway IP Address
  12.  
  13. uint8_t dns[]; ///< DNS server IP Address
  14.  
  15. dhcp_mode dhcp; ///< 1 - Static, 2 - DHCP
  16.  
  17. }wiz_NetInfo;

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

2.3、具体通讯过程的实现

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

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

  1. //TCP服务器数据通讯
  2.  
  3. int32_t TCPServer(uint8_t sn, uint16_t port)
  4.  
  5. {
  6.  
  7. int32_t ret;
  8.  
  9. uint8_t socketStatus=getSn_SR(sn);
  10.  
  11. switch(socketStatus)
  12.  
  13. {
  14.  
  15. case SOCK_ESTABLISHED :
  16.  
  17. {
  18.  
  19. if(getSn_IR(sn) & Sn_IR_CON)
  20.  
  21. {
  22.  
  23. setSn_IR(sn,Sn_IR_CON);
  24.  
  25. }
  26.  
  27. uint16_t size=;
  28.  
  29. if((size = getSn_RX_RSR(sn)) > )
  30.  
  31. {
  32.  
  33. if(size > DATA_BUFFER_SIZE)
  34.  
  35. {
  36.  
  37. size = DATA_BUFFER_SIZE;
  38.  
  39. }
  40.  
  41. uint8_t rxBuffer[DATA_BUFFER_SIZE];
  42.  
  43. ret = recv(sn,rxBuffer,size);
  44.  
  45. if(ret <= )
  46.  
  47. {
  48.  
  49. return ret;
  50.  
  51. }
  52.  
  53. //添加数据解析及响应的函数
  54.  
  55. uint8_t txBuffer[DATA_BUFFER_SIZE];
  56.  
  57. uint16_t length=ReceivedDataParsing(rxBuffer,txBuffer);
  58.  
  59. uint16_t sentsize=;
  60.  
  61. while(length != sentsize)
  62.  
  63. {
  64.  
  65. ret = send(sn,txBuffer+sentsize,length-sentsize);
  66.  
  67. if(ret < )
  68.  
  69. {
  70.  
  71. close(sn);
  72.  
  73. return ret;
  74.  
  75. }
  76.  
  77. sentsize += ret; // 不用管SOCKERR_BUSY, 因为它是零.
  78.  
  79. }
  80.  
  81. }
  82.  
  83. break;
  84.  
  85. }
  86.  
  87. case SOCK_CLOSE_WAIT :
  88.  
  89. if((ret=disconnect(sn)) != SOCK_OK)
  90.  
  91. {
  92.  
  93. return ret;
  94.  
  95. }
  96.  
  97. break;
  98.  
  99. case SOCK_INIT :
  100.  
  101. if( (ret = listen(sn)) != SOCK_OK)
  102.  
  103. {
  104.  
  105. return ret;
  106.  
  107. }
  108.  
  109. break;
  110.  
  111. case SOCK_CLOSED:
  112.  
  113. if((ret=socket(sn,Sn_MR_TCP,port,0x00)) != sn)
  114.  
  115. {
  116.  
  117. return ret;
  118.  
  119. }
  120.  
  121. break;
  122.  
  123. default:
  124.  
  125. break;
  126.  
  127. }
  128.  
  129. return ;
  130.  
  131. }

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

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

欢迎关注:

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

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

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

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

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

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

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

  4. 基于“泵”的TCP通讯(接上篇)

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

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

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

  6. 【.NET6+Modbus】Modbus TCP协议解析、仿真环境以及基于.NET实现基础通信

    前言:随着工业化的发展,目前越来越多的开发,从互联网走向传统行业.其中,工业领域也是其中之一,包括各大厂也都在陆陆续续加入工业4.0的进程当中. 工业领域,最核心的基础设施,应该是与下位硬件设备或程序 ...

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

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

  8. 开放型Modbus/TCP 规范

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

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

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

随机推荐

  1. 如何用PowerShell列出你机器上的.NET Framework的版本号和SP服务补丁

    代码下载:本文提到的脚本,可以从微软的代码库下载, How to determine versions & service pack levels of .NET Framework by P ...

  2. C#如何防止程序多次运行的技巧

    一.使用互斥量Mutex弄懂了主要的实现思路之后,下面看代码实现就完全不是问题了,使用互斥量的实现就是第四点的思路的体现,我们用为该程序进程创建一个互斥量Mutex对象变量,当运行该程序时,该程序进程 ...

  3. Windows7SP1补丁包(Win7补丁汇总) 32位/64位版 更新截至2016年11月

    Windows7SP1(64位)补丁包(Win7补丁汇总)更新到本月最新.包含Windows7SP1中文版所有重要补丁,可离线安装,适用于Windows 7 SP1 64位 简体中文系统.包含Inte ...

  4. linux/ubuntu查看内核版本命令

    打开终端,输入: uname -a

  5. java高新技术-操作javaBean

    1. 对javaBean的简单内省操作 public class IntroSpectorTest { public static void main(String[] args) throws Ex ...

  6. 移动端webUI框架(HTML5手机框架)

    淘宝SUI Mobile框架 官网地址:http://m.sui.taobao.org/ SUI Mobile 是一套基于 Framework7 开发的UI库.它非常轻量.精美,只需要引入我们的CDN ...

  7. sql 知识点系统汇总

    提供性能: .服务器往往具有强大的计算能力和速度..避免把大量的数据下载到客户端,减少网络上的传输量. 第一章 T-SQL 语句 1.1数据类型 文本型 -- CHAR 定长型 <=8000字节 ...

  8. 学习笔记——git

    恩没错,又是个新东西 使用Git提交文件到版本库有两步: 第一步:是使用 git add 把文件添加进去,实际上就是把文件添加到暂存区. 第二步:使用git commit提交更改,实际上就是把暂存区的 ...

  9. MySQL索引结构--由 B-/B+树看

    B-树 B-树,这里的 B 表示 balance( 平衡的意思),B-树是一种多路自平衡的搜索树它类似普通的平衡二叉树,不同的一点是B-树允许每个节点有更多的子节点.下图是 B-树的简化图. B-树有 ...

  10. HDU1086You can Solve a Geometry Problem too(判断线段相交)

    You can Solve a Geometry Problem too Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/3 ...