基于STM32和W5500的Modbus TCP通讯
在最近的一个项目中需要实现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()
三个函数的具体实现内容如下:
- //函数注册,首先,应由用户实现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通讯的更多相关文章
- C# ModBus Tcp读写数据 与服务器进行通讯
前言 本文将使用一个NuGet公开的组件技术来实现一个ModBus TCP的客户端,方便的对Modbus tcp的服务器进行读写,这个服务器可以是电脑端C#设计的,也可以是PLC实现的,也可以是其他任 ...
- Modbus tcp 格式说明 通讯机制 附C#测试工具用于学习,测试
前言: 之前的博客介绍了如何用C#来读写modbus tcp服务器的数据,文章:http://www.cnblogs.com/dathlin/p/7885368.html 当然也有如何创建一个服务器文 ...
- .Net开发笔记(十五) 基于“泵”的TCP通讯(接上篇)
上一篇博客中说了基于“泵”的UDP通讯,附上了一个Demo,模拟飞鸽传书的功能,功能不太完善,主要是为了说明“泵”在编程中的应用.本篇文章我再附上一个关于TCP通讯的两个Demo,也都采用了“泵”模式 ...
- 基于“泵”的TCP通讯(接上篇)
基于“泵”的TCP通讯(接上篇) 上一篇博客中说了基于“泵”的UDP通讯,附上了一个Demo,模拟飞鸽传书的功能,功能不太完善,主要是为了说明“泵”在编程中的应用.本篇文章我再附上一个关于TCP通讯的 ...
- wp8使用Beetle.NetPackage实现基于TCP通讯的订单查询
在新版本的Beetle.NetPackage中提供了对Protobuf和Controller的支持,所以在WP8下使用Beetle.NetPackage进行基于TCP的数据交互则一件非常简单事情.下面 ...
- 【.NET6+Modbus】Modbus TCP协议解析、仿真环境以及基于.NET实现基础通信
前言:随着工业化的发展,目前越来越多的开发,从互联网走向传统行业.其中,工业领域也是其中之一,包括各大厂也都在陆陆续续加入工业4.0的进程当中. 工业领域,最核心的基础设施,应该是与下位硬件设备或程序 ...
- 基于QTcpSocket和QTcpServer的Tcp通讯以及QDataStream序列化数据
最近要在QT下开发Tcp通讯,发送序列化数据以便于接收. 这里涉及到几个问题: 1.QTcpSocket.QTcpServer的通讯 2.QDataStream序列化数据 多的不说,直接上干货!!! ...
- 开放型Modbus/TCP 规范
修订版 1.0,1999 年3 月29 日Andy SwalesSchneider 电气公司aswales@modicon.com目录目录............................... ...
- LwIP之socket应用--WebServer和Modbus TCP
1. 引言 LwIP是嵌入式领域一个流行的以太网协议栈, LwIP开放源码,用C写成非常方便移植,并且支持socket接口,使用者可以集中精力处理应用功能. 本文就是LwIP socket使用的一个小 ...
随机推荐
- 【原】KMeans与深度学习模型结合提高聚类效果
这几天在做用户画像,特征是用户的消费商品的消费金额,原始数据(部分)是这样的: id goods_name goods_amount 男士手袋 1882.0 淑女装 2491.0 女士手袋 345.0 ...
- MVC之前的那点事儿系列(8):UrlRouting的理解
文章内容 根据对Http Runtime和Http Pipeline的分析,我们知道一个ASP.NET应用程序可以有多个HttpModuel,但是只能有一个HttpHandler,并且通过这个Http ...
- HTML5 移动浏览器支持
- iOS真机调试引入第三方库(如友盟等)编译时候,出现错误提示
用Xcode 7 beta 3在真机(iOS 8.3)上运行一下工程,结果发现工程编译不过.看了下问题,报的是以下错误: MARK:解决方法:在building Setting 中设置bitCode ...
- 线段树 poj 1436
题目大意:给出n条垂直于x轴的线段的数据y1,y2,x,求出有几个三条线段一组的三元组并且他们兩兩能相见的.思路:对y轴建树,将x排序,然后按顺序边询问边擦入,用mark[i][j]表示j往左可以看到 ...
- MFC的多线程操作
记得用MFC做了一个图像自动修复软件,当时没有多线程操作这一概念,由于图像修复算法比较复杂,因此,当执行图像修复时,程序就像卡死了似得而不能做其他操作.其实MFC对这种情况有一种很好地解决方案,那就是 ...
- SVM(支持向量机)的一点理解
最近有被问到SVM的问题,不懂装懂,羞愧不已.百度有很多深入浅出介绍SVM的文章,我就不赘述了,这里写一点自己肤浅的理解. SVM的核心思想是把求解低维空间上的高维分类器转化为求解高维函数空间上的线性 ...
- Css 知识点(不要删)
测试:火狐浏览器,谷歌浏览器,ie 6~9就足够了,现代浏览器从ie10及 以后兼容性好(适合html5) 1.结构用id,内容用class:fr\fl 用于结构上:能用margin-right就不要 ...
- socket编程基础
socket编程 什么是socket 定义 socket通常也称作套接字,用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过套接字向网络发出请求或者应答网络请求. socket起源于Unix ...
- ypzl药品质量不合格数据库-excel自动排版
原创: qq:231469242 import xlrdimport pandas,numpyimport matplotlib.pyplot as pltimport pandas as pd #参 ...