Modbus协议栈应用实例之二:Modbus RTU从站应用

自从开源了我们自己开发的Modbus协议栈之后,有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例,这一篇中我们将使用协议栈实现一个Modbus RTU从站应用。

1、何为RTU从站

Modbus协议是一个主从协议,那肯定就有主站和从站之分。所谓从站就是被动动响应通讯的对象,所以从站总是响应通讯的一方。

对于RTU从站来说,它是数据的数据的生产者,从站通过响应主站数据请求的方式将数据发送给主站。这一过程如下图所示:

从上图我们不难看出,首先主站要主动发起数据请求,这也是它为什么被称之为主站的缘由。它首先告诉从站我需要哪些数据。然后从站按照主站的请求返回数据。主站得到响应后解析数据,这样就完成了主从站之间的一次数据通讯。所以主站就需要主动发起每一次数据通讯的对象。

2、如何实现RTU从站

我们已经了解的从站总是响应主站的数据请求来实现数据的传送。下面我们来看看使用协议栈如何实现一个从站。

我们知道从站是数据的生产者,对于Modbus协议来说有四类数据:线圈、状态、输入寄存器和保持寄存器。所以在从站中我们要为这四种数据定义相应的地址,以便主站能够对应的访问。所以设计一个从站我们先来设计它的数据地址,在我们的例子中我们规定如下:

我们规定了每类数据类型的数量为8,对于从站来说除了生成这些数据外,还需要根据主站的数据请求来返回相应的数据响应。在我们的协议栈中实现了0x01、0x02、0x03、0x04、0x05、0x06、0x0F以及0x10等功能码。也就是说主站对象会生成面向这些功能码的从站数据请求。从站收到请求后,解析请求并根据请求生成响应的数据响应。可以表示为下图所示:

从上图我们明白协议栈中已经实现了对收到的主站数据请求进行解析以及根据解析生成对应的响应的函数。我们使用协议栈时,主要需要做两个方面的事情:解析数据请求和生成数据响应。

在协议栈中定义了一个解析函数,该函数将收到的数据请求消息解析,并根据解析的结果生成返回的数据响应。该函数的原型如下:

uint16_t ParsingMasterAccessCommand(uint8_t *receivedMessage, uint8_t *respondBytes, uint16_t rxLength, uint8_t StationAddress)

这个函数有四个参数:uint8_t *receivedMessage是收到的数据请求消息; uint8_t *respondBytes是返回的数据响应消息,也是函数需要生成的;uint16_t rxLength是接收到的数据请求消息的长度;uint8_t StationAddress本站的地址。而函数的返回值则是生成的数据响应详细的长度。

在解析的过程中,该函数判断消息的完整性,并根据不同的功能码调用不同的回调函数来实现,包括设置本地数据和获取本地数据的相关回调函数,在后续将讨论它们的实现。

3RTU从站编码

我们已经详述了使用协议栈实现RTU从站的方法,接下来我们就来利用协议栈具体开发一个RTU从站的实例。

我们调用解析函数对接收到的数据请求进行解析,具体调用方式如下所示:

respondLength=ParsingMasterAccessCommand(hgudRxBuffer,respondBytes,hgudRxLength,StationAddress);

返回值会有3种情况,返回值为0则表示接收到的数据请求消息是错误的。返回值为65535则表示返回的消息尚未接收完整。返回的是一个合适的数值则表示解析成功,返回了数据响应的长度。

当然我们需要实现8个回调函数,分别是获取线圈量、获取状态量、获取输入寄存器和获取保持寄存器,以及预置单个线圈量、预置多个线圈量、预置单个保持寄存器和预置多个保持寄存器。函数原型定义如下:

/*获取想要读取的Coil量的值*/
__weak void GetCoilStatus(uint16_t startAddress,uint16_t quantity,bool *statusList)
{//如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}/*获取想要读取的InputStatus量的值*/
__weak void GetInputStatus(uint16_t startAddress,uint16_t quantity,bool *statusValue)
{//如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}/*获取想要读取的保持寄存器的值*/
__weak void GetHoldingRegister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue)
{//如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}/*获取想要读取的输入寄存器的值*/
__weak void GetInputRegister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue)
{//如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}/*设置单个线圈的值*/
__weak void SetSingleCoil(uint16_t coilAddress,bool coilValue)
{//如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}/*设置单个寄存器的值*/
__weak void SetSingleRegister(uint16_t registerAddress,uint16_t registerValue)
{//如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}/*设置多个线圈的值*/
__weak void SetMultipleCoil(uint16_t startAddress,uint16_t quantity,bool *statusValue)
{//如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}/*设置多个寄存器的值*/
__weak void SetMultipleRegister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue)
{//如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}

我们需要做的工作就是根据我们具体实例中4类数据量的地址分配来实现这8个回调函数。当然,如果从站没有某一类数据量操作,回调函数则不需要编写。在我们的实例中我们将这几个函数实现如下:

/*获取想要读取的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<count;i++){statusList[i]=dPara.coil[start+i];}
}/*获取想要读取的保持寄存器的值*/
void GetHoldingRegister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue)
{uint16_t start;uint16_t count;/*先判断地址是否处于合法范围*/start=(startAddress>HoldingResterStartAddress)?((startAddress<=HoldingResterEndAddress)?startAddress:HoldingResterEndAddress):HoldingResterStartAddress;count=((start+quantity-1)<=HoldingResterEndAddress)?quantity:(HoldingResterEndAddress-start);for(int i=0;i<count;i++){registerValue[i]=aPara.holdingRegister[start+i];}
}/*设置单个线圈的值*/
void SetSingleCoil(uint16_t coilAddress,bool coilValue)
{/*先判断地址是否处于合法范围*/if((4<=coilAddress)&&(coilAddress<=CoilEndAddress)){dPara.coil[coilAddress]=coilValue;}PresetSlaveCoilControll(coilAddress,coilAddress);
}/*设置多个线圈的值*/
void SetMultipleCoil(uint16_t startAddress,uint16_t quantity,bool *statusValue)
{uint16_t endAddress=startAddress+quantity-1;if((4<=startAddress)&&(startAddress<=CoilEndAddress)&&(4<=endAddress)&&(endAddress<=CoilEndAddress)){for(int i=0;i<quantity;i++){dPara.coil[i+startAddress]=statusValue[i];}}PresetSlaveCoilControll(startAddress,endAddress);
}/*设置单个寄存器的值*/
void SetSingleRegister(uint16_t registerAddress,uint16_t registerValue)
{bool noError=(bool)(((41<=registerAddress)&&(registerAddress<=42))||((44<=registerAddress)&&(registerAddress<=45))||((50<=registerAddress)&&(registerAddress<=51))||((54<=registerAddress)&&(registerAddress<=55))||((58<=registerAddress)&&(registerAddress<=59)));if(noError){aPara.holdingRegister[registerAddress]=registerValue;}WriteSlaveRegisterControll(registerAddress,registerAddress);
}/*设置多个寄存器的值*/
void SetMultipleRegister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue)
{uint16_t endAddress=startAddress+quantity-1;bool noError=(bool)(((8<=startAddress)&&(startAddress<=15)&&(8<=endAddress)&&(endAddress<=15))||((41<=startAddress)&&(startAddress<=42)&&(41<=endAddress)&&(endAddress<=42))||((44<=startAddress)&&(startAddress<=47)&&(44<=endAddress)&&(endAddress<=47))||((50<=startAddress)&&(startAddress<=51)&&(50<=endAddress)&&(endAddress<=51))||((54<=startAddress)&&(startAddress<=55)&&(54<=endAddress)&&(endAddress<=55))||((58<=startAddress)&&(startAddress<=59)&&(58<=endAddress)&&(endAddress<=59))||((62<=startAddress)&&(startAddress<=67)&&(62<=endAddress)&&(endAddress<=67))||((72<=startAddress)&&(startAddress<=77)&&(72<=endAddress)&&(endAddress<=77))||((82<=startAddress)&&(startAddress<=87)&&(82<=endAddress)&&(endAddress<=87))||((92<=startAddress)&&(startAddress<=97)&&(92<=endAddress)&&(endAddress<=97))||((100<=startAddress)&&(startAddress<=115)&&(100<=endAddress)&&(endAddress<=115)));if(noError){for(int i=0;i<quantity;i++){aPara.holdingRegister[startAddress+i]=registerValue[i];}}WriteSlaveRegisterControll(startAddress,endAddress);
}

到这里对从站的开发实际已经完成。对于这些回调函数并不是全部需要编写,而是要根据我们自己定义的从站各类参数的地址分配来实现。

4RTU从站小结

我们实现了一个简单的RTU从站实例,我们可以通过一些RTU主站软件来测试它。这样的软件有很多,常见的如Modscan、Modbus Poll等。这里我们使用Modbus Poll来测试一下,如下图所示:

RTU从站的实现相对较简单,因为在同一台设备上只需实现一个从站,哪怕是通过不同的端口来访问。这一点与主站是不一样的,原因是从站的数据是自己产生,而且只需被动响应主站请求,而且理论上同一条总线只会有一个主站。

接下来我们来总结一下使用协议栈实现RTU从站的工作流程,或者说实现的步骤。首先从站要解析从主站送来的数据请求。在协议栈中已经封装了数据请求的解析函数、所以我们实现从站时首先就是调用这一函数来解析接收到的数据请求消息。

然后将解析函数返回的数据响应消息发送到主站就可以了。也就是说使用协议栈,只需要调用一下这个函数从站功能就实现了。这是因为这个函数实现了整个从站的响应过程,大致分三个步骤:第一步,解析收到的主站数据请求消息;第二步,根据解析的结果预置数据或者获取数据,预置和获取数据由8个回调函数实现;第三步,生成从站数据响应消息。说到这里我们已经清楚,RTU从站必须实现这些回调函数,其它工作则全由协议栈完成。

源码下载:https://download.csdn.net/download/foxclever/12882021

欢迎关注:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499359.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

外设驱动库开发笔记11:SHT3x系列温湿度传感器驱动

在我们的产品中经常会遇到温湿度检测的需求。可以用于检测温湿度的传感器元件也有很多。我们经常使用的SHT各系列数字温湿度传感器来实现应用需求。在这里我们将设计并实现SHT3x系列温湿度传感器的驱动。 1、功能概述 SHT3x系列温湿度传感器是适用于各种应用的高品质湿度传感…

LwIP应用开发笔记之十:LwIP带操作系统基本移植

现在&#xff0c;TCP/IP协议的应用无处不在。随着物联网的火爆&#xff0c;嵌入式领域使用TCP/IP协议进行通讯也越来越广泛。在我们的相关产品中&#xff0c;也都有应用&#xff0c;所以我们结合应用实际对相关应用作相应的总结。 1、技术准备 我们采用的开发平台是STM32F407…

ThreadX应用开发笔记之一:移植ThreadX到STM32平台

现在一些小型系统中也往往有多任务处理的需求&#xff0c;这就为实时操作系统提供了用武之地。事实上国内外各种各样的RTOS有很多&#xff0c;而且基本都在走开源的路线&#xff0c;ThreadX也不例外&#xff0c;在这一篇中我们就来学习ThreadX初步应用并将其移植到STM32平台中。…

外设驱动库开发笔记12:TSEV01CL55红外温度传感器驱动

有时候我们需要检测一些无法直接接触的器件的温度。为了实现这一需求&#xff0c;我们通常会选择红外温度传感器来实现这一功能。考虑到复用的问题&#xff0c;我们一般会将操作元器件的代码抽象为驱动函数以备调用。这里我们就来设计并实现TSEV01CL55红外温度传感器的驱动。 …

FreeRTOS应用开发笔记之一:FreeRTOS在STM32的移植

FreeRTOS是如今在小型嵌入式领域应用比较广泛的一种实时操作系统。它是一种开源且免费的操作系统&#xff0c;而且移植和使用都非常的简单。在这里我们将学习并移植FreeRTOS。 1、必要的准备 工欲善其事&#xff0c;必先利其器&#xff0c;在开始学习和移植之前&#xff0c;相…

外设驱动库开发笔记13:MLX90614红外温度传感器驱动

红外温度传感器一般用于非接触式的温度检测。在我们的系统中经常会有这样的需求。所以我们将其设计为通用的驱动库以备复用。这一篇我们将讲述MLX90614红外温度传感器驱动的设计与实现。 1、功能概述 MLX90614是一种红外温度计&#xff0c;用于非接触式温度测量。红外测温是根…

Modbus协议栈应用实例之三:Modbus TCP客户端应用

自从开源了我们自己开发的Modbus协议栈之后&#xff0c;有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例&#xff0c;这一篇中我们将解说如何使用协议栈实现一个Modbus TCP客户端。 1、何为TCP客户端 Modbus…

Modbus协议栈应用实例之四:ModbusTCP服务器应用

自从开源了我们自己开发的Modbus协议栈之后&#xff0c;有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例&#xff0c;这一篇中我们来简述如何使用协议栈实现一个Modbus TCP服务器应用。 1、何为TCP服务器 Mo…

外设驱动库开发笔记14:DS18B20温度变送器驱动

在一时候我们需要相对简单的检测温度信号&#xff0c;而DS18B20就是一款功能和应用都相对简单的温度传感器&#xff0c;通过单线就可以实现检测温度信号的需求。这一篇我们就来实现操作DS18B20获取温度数据的驱动。 1、功能概述 DS18B20是常用的数字温度传感器&#xff0c;其…

Modbus协议栈应用实例之五:Modbus ASCII主站应用

自从开源了我们自己开发的Modbus协议栈之后&#xff0c;有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例&#xff0c;这一篇中我们来使用协议栈实现Modbus ASCII主站应用。 1、何为ASCII主站 我们知道Modbus…

Modbus协议栈应用实例之六:Modbus ASCII从站应用

自从开源了我们自己开发的Modbus协议栈之后&#xff0c;有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例&#xff0c;这一篇中我们来使用协议栈实现Modbus ASCII从站应用。 1、何为ASCII从站 我们知道Modbus…

外设驱动库开发笔记15:DHT11温湿度传感器驱动

与DS18B20一样DHT11也是采用单总线&#xff0c;但所不同的是DHT11可同时实现温度和湿度的检测。在我们的产品中经常使用它来检测环境的温湿度信息。这一篇我们将设计并封装DHT11的驱动程序&#xff0c;以方便重复使用。 1、功能概述 DHT11数字温湿度传感器是一款含有已校准数…

外设驱动库开发笔记16:MS5536C压力变送器驱动

压力检测也是经常会遇到的需求&#xff0c;比如环境压力或者低压气体等都会用到压力检测。这类检测压力都比较低&#xff0c;一般不会超过大气压&#xff0c;有时甚至是负压。这一篇我们要讨论的MS5536C就属于这类器件。接下来我们将设计并实现MS5536C的驱动。 1、功能概述 M…

外设驱动库开发笔记17:MS5803压力变送器驱动

很多时候我们需要检测被控对象的绝对压力&#xff0c;而且在我们的多款产品中也有这样的需求。当然检测绝对压力的传感器有很多&#xff0c;我们经常使用MS5803来实现压力检测。本篇中我们将设计并实现MS5803系列压力传感器的驱动。 1、功能概述 MS5803系列产品包含压阻传感器…

通讯接口应用笔记1:RS485通讯上下拉电阻的选择

RS485是一种常见的通讯接口方式&#xff0c;在我们的实际产品中也是多次使用。但我们平常并不会去过多考虑某一实现的细节问题&#xff0c;不过最近我们遇到了一个因如上下拉电阻的选择问题而造成的通讯故障&#xff0c;所以在这一片中我们来讨论一下RS485总线上下拉电阻的选择…

外设驱动库开发笔记18:MS5837压力变送器驱动

绝对压力的检测是常见的需求。在我们的系统中也常常会遇到。而MS5837压力传感器也是我们进场会采用的方案。在这篇里我们将讨论并实现MS5837压力传感器的驱动。 1、功能概述 MS5837压力传感器是一种可用于电路板上&#xff0c;适用于检测10-1200mbar压力范围的传感器&#xf…

外设驱动库开发笔记19:BMP280压力温度传感器驱动

压力和温度监测在嵌入式系统开发中是非常常见的需求&#xff0c;特别是对环境大气压力和温度的检测需求就更常见了。我们一般都会选择一些封装较小操作比较方便的压力传感器。BMP280就是满足这一要求的器件。在这一篇中我们将设计并实现BMP280的驱动。 1、功能概述 BMP280是一…

一个简单的空气质量数据监测站项目

大气质量数据监测站用于测试空气质量监测及数据采集&#xff0c;实现野外或者室内空气质量的检测。并通过网络将数据上传到OneNet​显示。​ 1、项目概述 本项目是一个定制项目&#xff0c;要求采集大气的压力、温度、湿度、PM25、位置等数据并上传到指定的后台服务器。但有时…

一个基于STM32实现的多组分气体分析仪项目

本篇将简要的总结一下一个基于STM32F412ZG实现的多组分气体分析仪的项目。简要描述该项目的软硬件设计及其验证。 一、项目概述 多组分气体分析仪是我公司近期研发的三个主要产品之一。采用模块化设计&#xff0c;可增减配置&#xff0c;可分析混合气体中的氧气、氢气、甲烷、…

外设驱动库开发笔记20:BME280压力湿度温度传感器驱动

嵌入式产品开发中&#xff0c;我们常常会有检测环境温度、压力、湿度的需求。如果有一个集成有这3个传感器的元件&#xff0c;无疑将是很方便的。博世的BME280就能实现这一要求。在这一篇中我们将讨论BME280的驱动设计与实现。 1、功能概述 BME280是一款专为移动应用而开发的…