当前位置: 首页 > news >正文

湖南智能网站建设哪里好免费推广引流软件

湖南智能网站建设哪里好,免费推广引流软件,做影视外包的网站,电商网站如何存储图片自从开源了我们自己开发的Modbus协议栈之后,有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例,在这一篇中我们先来使用协议栈实现Modbus RTU主站的示例。1、何为RTU主站Modbus协议是一个…

5ccc862d941fdf6caa2f8e727a3f1d79.png

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

1、何为RTU主站

Modbus协议是一个主从协议,那肯定就有主站和从站之分。所谓主站说的简单一点就是能够主动发起通讯的对象,所以主站就是发起通讯的一方。

对于RTU主站来说,自己并不会产生数据,而是要从从站获取数据。在Modbus RTU协议中从站不会主动向外发送数据,所以需要主站发送数据请求,从站才会向其返回请求的数据。这一过程如下图所示:

b6b609efeb472c60df0b4bfda63df34a.png

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

2、如何实现RTU主站

我们已经简单的说明了什么是RTU的主站,那么如何实现这一主站呢?其实在协议栈中,我们已经实现了主站的数据请求命令的合成以及响应数据的解析,所以我们使用协议栈时就是要控制何时将协议栈合成的主站请求命令发出以及如何解析数据响应进而得到想要的数据的过程。

在我们的协议栈中实现了0x01、0x02、0x03、0x04、0x05、0x06、0x0F以及0x10等功能码。也就是说主站对象可以生成面向这些功能码的从站数据请求。也可以解析面向这些功能码的从站数据响应。可以表示为下图所示:

84175aee06a7af440ea199599eac664b.png

从上图我们很清楚,协议栈已经实现了面向这些功能码的数据请求命令的生成以及数据响应消息的解析。我们使用协议栈时需要做的就是要告诉协议栈我要生成哪些数据请求命令以及如何解析数据响应消息。

2.1、怎么生成数据请求

对于数据请求,我们不一定需要面向全部功能码的请求,我们只需要根据我们的需求合成我们想要的请求。

在协议栈中,针对数据请求的生成我们定义了一个从站访问命令生成函数。该函数的原型如下:

uint16_t CreateAccessSlaveCommand(ObjAccessInfo objInfo,void*dataList,uint8_t *commandBytes)

该函数有3个参数,其中ObjAccessInfo objInfo为对象访问信息;void*dataList为数据列表指针,该参数主要用于写从站功能的命令生成;uint8_t *commandBytes为返回的从站访问命令。

ObjAccessInfo是一个结构体,向函数传递我们想要生成的从站访问命令的相关信息,包括站地址,功能码,起始地址和数量。该结构体的定义如下:

/*定义用于传递要访问从站(服务器)的信息*/typedef struct{  uint8_t unitID;  FunctionCode functionCode;  uint16_t startingAddress;  uint16_t quantity;}ObjAccessInfo;

2.2、怎么解析数据响应

对于数据响应,我们同样不需要考虑全部的操作码,我们一般需要考虑读请求的响应,因为他们的数据需要解析。而对于写请求返回数响应只是告诉主站成功或者不成功,即使不成功只需要在写一次就可以了,不存在数据更新的问题。

在协议栈中,我们实现了主站解析从站数据响应的解析函数。使用这一函数我们只需要将收到的数据响应报文传递给解析函数就可以完成解析。该函数的原型定义如下:

void ParsingSlaveRespondMessage(RTULocalMasterType *master,uint8_t *recievedMessage,uint8_t *command)

这个函数有3个参数,其中RTULocalMasterType *master为主站对象;uint8_t*recievedMessage为接收到的响应消息;uint8_t *command为发送的命令序列。将这几个参数传递给解析函数就可实现数据响应的解析。

RTULocalMasterType是一个结构体,用以生命一个主站对象,这个对象就是我们要实现各种操作的主站,这一结构体的定义如下:

/* 定义本地RTU主站对象类型 */typedef struct LocalRTUMasterType{  uint32_t flagWriteSlave[8];   //写一个站控制标志位,最多256个站,与站地址对应。  uint16_t slaveNumber;         //从站列表中从站的数量  uint16_t readOrder;           //当前从站在从站列表中的位置  RTUAccessedSlaveType*pSlave;         //从站列表  UpdateCoilStatusTypepUpdateCoilStatus;       //更新线圈量函数  UpdateInputStatusTypepUpdateInputStatus;     //更新输入状态量函数  UpdateHoldingRegisterTypepUpdateHoldingRegister;     //更新保持寄存器量函数  UpdateInputResgisterTypepUpdateInputResgister;       //更新输入寄存器量函数}RTULocalMasterType;

3RTU主站编码

有了前面的说明,我们基于协议栈实现一个主站应用就很容易了。接下来我们就基于协议栈具体实现一个主站应用。

3.1、定义主站对象

首先我们要声明一个主站对象,这是我们操作的基础。在接下来的各种操作中我们都是基于这一对象来实现的。具体操作如下:

RTULocalMasterType rtuMaster;

定义了这个主站对象后,我们还需要对这一对象进行初始化。协议栈同样提供了一个主站对象的初始化函数。函数的原型定义如下:

/*初始化RTU主站对象*/void InitializeRTUMasterObject(RTULocalMasterType*master,uint16_t slaveNumber,                    RTUAccessedSlaveType*pSlave,                    UpdateCoilStatusTypepUpdateCoilStatus,                    UpdateInputStatusTypepUpdateInputStatus,                    UpdateHoldingRegisterTypepUpdateHoldingRegister,                    UpdateInputResgisterTypepUpdateInputResgister                    )

该函数的参数除了主站对象外,还有从站的数量即从站对象列表,还有四个数据更新函数指针。这几个函数指针将应用于数据响应的解析过程中,具体在后面描述。使用这一初始化函数实现对主站对象的初始化,使其能够实现各项操作,具体如下:

/*初始化RTU主站对象*/

 InitializeRTUMasterObject(&hgraMaster,2,hgraSlave,NULL,NULL,NULL,NULL);

这里我们将几个数据处理函数指针变量传入NULL,表示初始化为默认的操作函数,当然我们也可以编写这些函数,在后续的数据解析时将会详细说明。

3.2、生成数据请求

在前面,我们已经描述了数据请求命令的生成函数,该函数有一个ObjAccessInfo参数,这个参数用于传递需要生成命令的信息。这是一个结构体,我们需要定义一个对象变量。

ObjAccessInfo hgraInfo;

然后使用这个对象来实现数据请求的生成。具体操作如下所示:

  /* 生成1号从站访问命令 */  hgraInfo.unitID=hgraSlave[0].stationAddress;  hgraInfo.functionCode=ReadCoilStatus;  hgraInfo.startingAddress=0x0000;  hgraInfo.quantity=8;  CreateAccessSlaveCommand(hgraInfo,NULL,slave1ReadCommand[0]);

生成的数据请求什么时候发送给完全由主进程来实现已经与协议栈没有关系了。

3.3、解析数据响应

收到数据响应后我们需要对其进行解析。前面我们已经介绍了解析从站数据响应的函数。具体的调用形式如下:

ParsingSlaveRespondMessage(&hgraMaster,hgraRxBuffer,NULL);

我们对hgraMaster主站对象收到的从站响应hgraRxBuffer进行解析。最后传入的NULL表示我们不指定主站发送的数据请求,而是让主站从请求列表中去自己查找。

当然我们需要实现数据更新处理回调函数。这几个函数是在对象初始化的时候以函数指针的形式传递的。原型如下:

/*更新读回来的线圈状态*/__weak void UpdateCoilStatus(uint8_t salveAddress,uint16_tstartAddress,uint16_t quantity,bool *stateValue){  //在客户端(主站)应用中实现} /*更新读回来的输入状态值*/__weak void UpdateInputStatus(uint8_t salveAddress,uint16_tstartAddress,uint16_t quantity,bool *stateValue){  //在客户端(主站)应用中实现} /*更新读回来的保持寄存器*/__weak void UpdateHoldingRegister(uint8_t salveAddress,uint16_tstartAddress,uint16_t quantity,uint16_t *registerValue){  //在客户端(主站)应用中实现} /*更新读回来的输入寄存器*/__weak void UpdateInputResgister(uint8_t salveAddress,uint16_tstartAddress,uint16_t quantity,uint16_t *registerValue){  //在客户端(主站)应用中实现}

我们可根据需要重定义这些函数,当然我们没有响应的数据可以不必实现,如我们没有使用输入寄存器,那么更新输入寄存器的回调函数则可以不用重定义。如下在我们的例子中重定义为:

/*更新读回来的保持寄存器*/voidUpdateHoldingRegister(uint16_t startAddress,uint16_t quantity,uint16_t*registerValue){  uint16_t startRegister=HoldingResterEndAddress+1;     switch(salveAddress)  {  case BPQStationAddress:       //更新读取的变频器参数    {      startRegister=36;      break;    }  case PUMPStationAddress:      //更新蠕动泵    {//     aPara.phyPara.pumpRotateSpeed=registerValue[1];     startRegister=HoldingResterEndAddress+1;      break;    }  case JIG1StationAddress:      //更新摆臂小电机    {      startRegister=48;      break;    }  case JIG2StationAddress:      //更新摆臂小电机    {      startRegister=52;      break;    }  case JIG3StationAddress:      //更新摆臂小电机    {      startRegister=56;      break;    }  case HLPStationAddress:       //更新红外温度    {     aPara.phyPara.hlpObjectTemperature=registerValue[0]/100.0;     startRegister=HoldingResterEndAddress+1;      break;    }  case ROL1StationAddress:      //更新摆臂控制    {      startRegister=quantity<3?60:62;      break;    }  case ROL2StationAddress:      //更新摆臂控制    {     startRegister=quantity<3?70:72;      break;    }  case ROL3StationAddress:      //更新摆臂控制    {     startRegister=quantity<3?80:82;      break;    }  case DRUMStationAddress:      //更新滚筒电机    {     startRegister=quantity<3?90:92;      break;    }  default:                      //故障态    {     startRegister=HoldingResterEndAddress+1;      break;    }  }   if(startRegister<=HoldingResterEndAddress)  {    for(int i=0;i    {     aPara.holdingRegister[startRegister+i]=registerValue[i];    }  }} /*更新读回来的输入寄存器*/void UpdateInputResgister(uint16_t startAddress,uint16_tquantity,uint16_t *registerValue){  uint16_t startRegister=HoldingResterEndAddress+1;     switch(salveAddress)  {  case BPQStationAddress:       //更新读取的变频器参数    {     startRegister=HoldingResterEndAddress+1;      break;    }  case PUMPStationAddress:      //更新蠕动泵    {     //aPara.phyPara.pumpRotateSpeed=registerValue[1]; //第一版背板     aPara.phyPara.pumpRotateSpeed=(uint16_t)((float)registerValue[1]*6.0/128.0+0.5);//第二版背板     startRegister=HoldingResterEndAddress+1;      break;    }  case JIG1StationAddress:      //更新摆臂小电机    {     startRegister=HoldingResterEndAddress+1;      break;    }  case JIG2StationAddress:      //更新摆臂小电机    {     startRegister=HoldingResterEndAddress+1;      break;    }  case JIG3StationAddress:      //更新摆臂小电机    {     startRegister=HoldingResterEndAddress+1;      break;    }  case ROL1StationAddress:      //更新摆臂控制    {     startRegister=HoldingResterEndAddress+1;      break;    }  case ROL2StationAddress:      //更新摆臂控制    {     startRegister=HoldingResterEndAddress+1;      break;    }  case ROL3StationAddress:      //更新摆臂控制    {     startRegister=HoldingResterEndAddress+1;      break;    }  case DRUMStationAddress:      //更新滚筒电机    {      startRegister=HoldingResterEndAddress+1;      break;    }  default:                      //故障态    {     startRegister=HoldingResterEndAddress+1;      break;    }  }  if(startRegister<=HoldingResterEndAddress)  {    for(int i=0;i    {     aPara.holdingRegister[startRegister+i]=registerValue[i];    }  }}

4RTU主站小结

我们实现了这个RTU主站实例,我们可以使用如Modsim这样的软件在PC上模拟Modbus RTU从站来测试这个主站应用,操作结果是没有问题的。

在使用协议栈实现RTU主站时需要注意,协议栈支持在同一设备上以不同的通讯端口实现不同的主站应用,而且每一台主站都支持多个从站。具体实现只需要根据协议栈定义就可以了。

我们来总结一下使用协议栈实现主站应用的步骤,以方便大家使用协议栈实现Modbus RTU主站应用。

第一步,使用主站对象类型声明一个主站对象。然后对这个主站对象进行初始化。初始化主站对象时。需要指定从站数量,从站列表以及更新数据的回调函数指针。

第二步,生成访问从站的数据请求列表。这个数据请求列表是按每一台从站来划分的,将列表的指针存在对应的从站对象中。然后在需要的时候发送相应的数据请求。

第三步,解析接收的从站数据响应。协议栈已经定义好了解析函数,只需传入消息就可自动解析。但是更新数据的回调函数必须根据具体的变量来编写。可以每台主站独立编写也可使用默认的函数。不过建议每台主站独立编写,这样比较清晰。

欢迎关注:

21a4a978d0d4510f3a9458cac6a1a974.png

http://www.lbrq.cn/news/2460925.html

相关文章:

  • 龙岗中心城网站建设优秀网站设计欣赏
  • 广告设计一般人能学吗哈尔滨seo关键字优化
  • html5在网站建设中的关键词优化哪个好
  • 怎么做微信小说网站中小企业网站优化
  • 牙科网站模板seo专业技术培训
  • 新手做网站做那个中国营销传播网
  • 什么网站可以做英语题注册网站平台
  • 为赌博网站做代理怎么样才可以在百度上打广告
  • 动态网站标题怎么做搜索引擎营销sem
  • 河东做网站的公司电商代运营公司
  • 网页制作与网站建设pdf苏州网站制作
  • 电商网站后台功能bt兔子磁力搜索
  • 网络下载的网站模板能直接上传到虚拟主机百度影响力排名顺序
  • 做网站销售的宁波seo服务推广
  • 做百度百科的网站企业营销策略
  • qq小程序权限设置深圳百度快照优化
  • 教育网站解决方案宁德市旅游景点大全
  • 哪个网站可以学做包子seo快速排名软件首页
  • 张艺兴粉丝做的网站seo网络推广技术员招聘
  • 江门市建设银行网站情感营销经典案例
  • 住房城乡建设网站查询宜兴百度推广公司
  • 铭万做的网站怎么样百度推广服务
  • 山东青岛网站建设公司网络舆情分析
  • 网站备案代理广州抖音推广
  • win2008iis7配置网站企业查询信息平台
  • 装饰设计软件湖北seo服务
  • 装饰网站建设的方案ppt谈谈自己对市场营销的理解
  • wordpress百度网盘什么叫seo
  • 网站设计师培训班推广赚钱app哪个靠谱
  • 建设政府门户网站百度网页排名怎么提升
  • java集合框架面试点(2)
  • PCIe 的L状态(链路状态)和D状态(设备状态)
  • VTK交互——Callback
  • 理解Spring中的IoC
  • 造成服务器内存不足的原因有什么
  • Ubuntu22.04.5 LTS安装与使用Docker