基于mnModbus的Modbus协议栈应用手册

freefree技术Resources

这篇内容可直接阅读,适合用于基础学习和Search引流。

基于mnModbus的Modbus协议栈应用手册缩略图
🌐 This page is not yet available in English. Showing the Chinese version. Back to Chinese page.
本文目录
  1. 1. 1.1、编写原因
  2. 2. 1.2、版权说明
  3. 3. 1.3、更新记录
  4. 4. 4.1.1、function code枚举
  5. 5. 4.1.2、Modbus状态枚举
  6. 6. 4.1.3、站信息结构
  7. 7. 4.1.4、生成访问从站PDU单元函数
  8. 8. 4.1.5、Parse读取的返回data函数
  9. 9. 4.1.6、从站生成读data命令的Responsemessage函数
  10. 10. 4.1.7、function code检验函数
  11. 11. 4.1.8、生成TCP客户端命令函数
  12. 12. 4.1.9、生成TCP服务器Response函数
  13. 13. 4.1.10、CRC check检查
  14. 14. 4.1.11、生成读写RTU从站的主站命令函数
  15. 15. 4.1.12、生成从站应答信息message函数
  16. 16. 4.1.13、LRC verification检查函数
  17. 17. 4.1.14、将ASCII消息转换为hexadecimal
  18. 18. 4.1.15、生成读写ASCII从站的命令函数
  19. 19. 4.1.16、生成ASCII从站应答主站命令的函数
  20. 20. 4.2.1、RTU主站对象与从站对象
  21. 21. 4.2.2、RTU主站对象初始化函数
  22. 22. 4.2.3、生成RTU主站访问从站命令函数
  23. 23. 4.2.4、RTU主站Parse从站的Response函数
  24. 24. 4.2.5、RTU主站Parse从站返回消息命令匹配函数
  25. 25. 4.2.6、RTU从站Parse主站访问命令函数
  26. 26. 4.2.7、TCP客户端对象与TCP服务器对象
  27. 27. 4.2.8、TCP客户端对象初始化函数
  28. 28. 4.2.9、生成TCP客户端访问服务器命令函数
  29. 29. 4.2.10、TCP客户端Parse服务器返回信息函数
  30. 30. 4.2.11、TCP服务器Parse客户端命令函数
  31. 31. 4.2.12、ASCII主站对象与从站对象
  32. 32. 4.2.13、ASCII主站对象初始化函数
  33. 33. 4.2.14、生成访问ASCII从站的命令
  34. 34. 4.2.15、ASCII主站Parse从站的Response函数
  35. 35. 4.2.16、ASCII主站Parse从站返回消息命令匹配函数
  36. 36. 4.2.17、ASCII从站Parse主站访问命令函数
  37. 37. 4.3.1、获取线圈量值的函数
  38. 38. 4.3.2、获取input state值的函数
  39. 39. 4.3.3、获取保持register value的函数
  40. 40. 4.3.4、获取输入register value的函数
  41. 41. 4.3.5、设置单个线圈的值函数
  42. 42. 4.3.6、设置单个register value的函数
  43. 43. 4.3.7、设置Multiple coil values的函数
  44. 44. 4.3.8、设置Multiple register values的函数
  45. 45. 4.3.9、从站更新obtained的线圈量值的函数
  46. 46. 4.3.10、从站更新obtained的input state值的函数
  47. 47. 4.3.11、从站更新保持register value的函数
  48. 48. 4.3.12、从站更新输入register value得函数

第1章 前言

Modbus是全球第一个真正用于工业现场的总线协议。Modbus通讯在工业网络通讯中应用十分广泛,而且方便,受到大家的欢迎。

1.1、编写原因

一直以来,在我们自己的产品和项目中都多次使用Modbus通讯协议。每次都是使用者自行开发或者网上Search符合要求的源码。但每次的应用都有不同,每次都需要很多的重复劳动。而且协议站如应用软件的紧密结合也使得代码有些混乱。所以一直以来都想要开发一个比较通用的协议栈能在后续的项目中复用,而不必每次都写一遍。现在利用项目研发的机会,开发一个自己的Modbus协议栈。

1.2、版权说明

本套软件最初虽然是为了我们自用而开发,但现已在GitHub上开源。所以任何人都可以Copy、传播和使用,无论是个人学习还是商业应用都没有限制。对应用方法及修改欢迎探讨,但对于在使用过程中出现了任何问题我们均不负责。

软件源码及其说明可以上GitHub自由Download,Downloadaddress:https://github.com/foxclever/Modbus

1.3、更新记录

前一个版本我们已经实现了基于串行链路的Modbus RTU和基于以太网的Modbus TCP两种。在这一个版本中,我们添加了基于串行链路的Modbus ASCII方式。并对上一个版本中已知的问题做了修正。

软件设计及本Document更新记录如下:

版本更新说明Author日期
V1.0.0初版,共5章,介绍了主要API函数木南2015年5月8日
V1.1.0添加了第6、7两章木南2016年12月23日
V1.1.5修改了3、4、5三章的大部分内容木南2018年2月20日
V2.0.0增加了ASCIIRelated内容,并修改已知问题木南2018年9月18日
V2.1.0增加了RTU和ASCII多主站Operation的类容;增加了TCP客户端多网段Operation的内容。木南2019年4月29日

第2章、PDU函数

在应用data单元我们处理Modbusdata通讯的基本messageformat,这些基本是通用的,目前版本只支持01、02、03、04、05、06、15、16function code。

4.1.1、function code枚举

/*定义Modbus的Operationfunction code,支持01、02、03、04、05、06、15、16function code*/

typedef enum {

  ReadCoilStatus=0x01,        /*读Coil status(读多个输出位的状态)*/

  ReadInputStatus=0x02,       /*读输入位状态(读多个输入位的状态)*/

  ReadHoldingRegister=0x03,   /*读保持register(读多个保持register的numerical value)*/

  ReadInputRegister=0x04,     /*读输入register(读多个输入register的numerical value)*/

  WriteSingleCoil=0x05,       /*强制单个线圈(强制单个输出位的状态)*/

  WriteSingleRegister=0x06,   /*预制单个register(设定一个register的numerical value)*/

  WriteMultipleCoil=0x0F,     /*强制多个线圈(强制多个输出位的状态)*/

  WriteMultipleRegister=0x10 /*预制多个register(设定多个register的numerical value)*/

}FunctionCode;

4.1.2、Modbus状态枚举

/*定义receive到指令检测Error时的Error码*/

typedef enum{

  MB_OK=0x00,

  InvalidFunctionCode=0x01,//不合法Function代码

  IllegalDataAddress=0x02,//非法的dataaddress

  IllegalDataValues=0x03,//非法的data值或者范围

  OperationFail=0x04

}ModbusStatus;

4.1.3、站信息结构

/*定义用于传递要访问从站(服务器)的信息*/

typedef struct{

  uint8_t unitID;

  FunctionCode functionCode;

  uint16_t startingAddress;

  uint16_t quantity;

}ObjAccessInfo;

定义用于传递从站(服务器)访问的各种信息,包括站号、function code、starting address和quantity。

4.1.4、生成访问从站PDU单元函数

/*作为RTU主站(TCP客户端)时,生成读写RTU从站(TCP服务器)对象的命令*/

uint16_t GenerateReadWriteCommand(ObjAccessInfo objInfo,bool *statusList,uint16_t *registerList,uint8_t commandBytes[]);

参数:ObjAccessInfo objInfo,要访问的从站对象

bool *statusList,需要往下写的状态量对象data列表

uint16_t *registerList,需要往下写的register量对象的data列表

uint8_t commandBytes[],生成的访问命令

返回值:生成的访问命令的Length,以byte为单位。

4.1.5、Parse读取的返回data函数

/*Parse主站(客户端)从服务器读取的data*/

void TransformClientReceivedData(uint8_t * receivedMessage,uint16_t quantity,bool *statusList,uint16_t *registerLister);

参数:uint8_t * receivedMessage,receive到的信息message

uint16_t quantity,读取的对象quantity

bool *statusList,Parse出来的读取到的状态对象的data列表

uint16_t *registerLister,Parse出来的读取到的register对象的data列表

返回值:

4.1.6、从站生成读data命令的Responsemessage函数

/*生成主站读访问的Response,包括0x01、0x02、0x03、0x04function code,返回相应信息的Length*/

uint16_t GenerateMasterAccessRespond(uint8_t *receivedMesasage,bool *statusList,uint16_t *registerList,uint8_t *respondBytes);

参数:uint8_t *receivedMesasage,receive到的主战读Operationmessage

bool *statusList,读取的从站状态对象data列表

uint16_t *registerList,读取到的从站register对象data列表

uint8_t *respondBytes,生成的从站Responsemessage信息

返回值:Responsemessage的Length,以byte为单位

4.1.7、function code检验函数

/*检查function code是否正确*/

ModbusStatus CheckFunctionCode(FunctionCode fc);

参数:FunctionCode fc,function code,由枚举定义

返回值:ModbusOperation状态,由枚举定义

4.1.8、生成TCP客户端命令函数

//生成读写服务器对象的命令

uint16_t SyntheticReadWriteTCPServerCommand(ObjAccessInfo objInfo,bool *statusList,uint16_t *registerList,uint8_t *commandBytes);

参数:ObjAccessInfo objInfo,TCP服务器访问信息对象

bool *statusList,写服务器状态量对象data列表

uint16_t *registerList,写服务器register量对象data列表

uint8_t *commandBytes,Generated commands信息message

返回值:命令的Length,以byte为单位

4.1.9、生成TCP服务器Response函数

/*合成对服务器访问的Response*/

uint16_t SyntheticServerAccessRespond(uint8_t *receivedMesasage,bool *statusList,uint16_t *registerList,uint8_t *respondBytes);

参数:uint8_t *receivedMesasage,receive到的客户端命令信息message

bool *statusList,读TCP服务器的状态量对象data列表

uint16_t *registerList,读TCP服务器的register量对象data列表

uint8_t *respondBytes,生成的Response客户端命令的Response信息message

返回值:Response信息messageLength,以byte为单位

4.1.10、CRC check检查

/*通过CRC checkreceive的信息是否正确*/

bool CheckRTUMessageIntegrity (uint8_t *message,uint8_t length);

参数:uint8_t *message,待校验的datamessage

uint8_t length,带教言的datamessageLength,以byte为单位

返回值:返回校验是否通过的状态信号

4.1.11、生成读写RTU从站的主站命令函数

/*生成读写从站data对象的命令,命令Length包括2个校验byte*/

uint16_t SyntheticReadWriteSlaveCommand(ObjAccessInfo slaveInfo,bool *statusList,uint16_t *registerList,uint8_t *commandBytes);

参数:ObjAccessInfo slaveInfo,从站访问信息对象,由结构体定义

bool *statusList,写从站状态量data列表

uint16_t *registerList,写从站register量data列表

uint8_t *commandBytes,生成的访问命令message

返回值:访问命令的Length,以byte为单位

4.1.12、生成从站应答信息message函数

/*生成从站应答主站的Response*/

uint16_t SyntheticSlaveAccessRespond(uint8_t *receivedMesasage,bool *statusList,uint16_t *registerList,uint8_t *respondBytes);

参数:uint8_t *receivedMesasage,

bool *statusList,

uint16_t *registerList,

uint8_t *respondBytes,

返回值:生成的Response信息message的Length,以byte为单位

4.1.13、LRC verification检查函数

/*判断ASCIIdata信息是否正确*/

bool CheckASCIIMessageIntegrity(uint8_t *usMsg, uint16_t usLength)

参数:uint8_t *usMsg,待校验的消息列表,hexadecimaldata

uint16_t usLength,待校验的消息列表的Length

返回值:校验结果,校验正确返回“true”,否则返回“false”

4.1.14、将ASCII消息转换为hexadecimal

/*receive到的ASCII消息转换为hexadecimal*/

bool CovertAsciiMessageToHex(uint8_t *aMsg, uint8_t *hMsg, uint16_t aLen);

参数:uint8_t *aMsg,ASCII码信息

uint8_t *hMsg,转换后的hexadecimal信息

      uint16_t aLen,ASCII码信息的Length

返回值:转换是否正确,转换正确返回“true”,否则返回“false”

4.1.15、生成读写ASCII从站的命令函数

/*生成读写从站的命令,应用于主站,含校验及startEnd符*/

uint16_t SyntheticReadWriteAsciiSlaveCommand(ObjAccessInfo slaveInfo, bool *statusList, uint16_t *registerList, uint8_t *commandBytes)

参数:ObjAccessInfo slaveInfo,从站访问信息对象,由结构体定义

bool *statusList,写从站状态量data列表

uint16_t *registerList,写从站register量data列表

uint8_t *commandBytes,生成的访问命令message

返回值:访问命令的Length,以byte为单位

4.1.16、生成ASCII从站应答主站命令的函数

/*生成应答主站的Response,应用于从站*/

uint16_t SyntheticAsciiSlaveAccessRespond(uint8_t *receivedMessage, bool *statusList, uint16_t *registerList, uint8_t *respondBytes)

参数:uint8_t *receivedMesasage,接受到主站message信息

bool *statusList,主站命令要读取的状态量值列表

uint16_t *registerList,主站命令要读取的register量值列表

uint8_t *respondBytes,生成的Response信息message

返回值:生成的Response信息message的Length,以byte为单位

第3章、应用封装函数

对应用级封装,我们只封装RTU主站应用,RTU从站应用、TCP客户端应用和TCP服务器端应用四类。

4.2.1、RTU主站对象与从站对象

/* 定义被访问RTU从站对象Type */

typedef struct AccessedRTUSlaveType{

  uint8_t stationAddress;       //站address

  uint8_t cmdOrder;             //当前命令在命令列表中的位置

  uint16_t commandNumber;       //命令列表中命令的总数

  uint8_t (*pReadCommand)[8];   //读命令列表

  uint8_t *pLastCommand;        //上一次send的命令

  uint32_t flagPresetCoil;      //预置线圈控制标志位

  uint32_t flagPresetReg;       //预置register控制标志位

}RTUAccessedSlaveType;

/* 定义本地RTU主站对象Type */

typedef struct LocalRTUMasterType{

  uint32_t flagWriteSlave[8];   //写一个站控制标志位,最多256个站,与站address对应。

  uint16_t slaveNumber;         //从站列表中从站的quantity

  uint16_t readOrder;           //当前从站在从站列表中的位置

  RTUAccessedSlaveType *pSlave;         //从站列表

  UpdateCoilStatusType pUpdateCoilStatus;       //更新线圈量函数

  UpdateInputStatusType pUpdateInputStatus;     //更新input state量函数

  UpdateHoldingRegisterType pUpdateHoldingRegister;     //更新保持register量函数

  UpdateInputResgisterType pUpdateInputResgister;       //更新输入register量函数

}RTULocalMasterType;

4.2.2、RTU主站对象初始化函数

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

void InitializeRTUMasterObject(RTULocalMasterType *master,uint16_t slaveNumber,RTUAccessedSlaveType *pSlave,UpdateCoilStatusType pUpdateCoilStatus,UpdateInputStatusType pUpdateInputStatus,UpdateHoldingRegisterType pUpdateHoldingRegister,UpdateInputResgisterType pUpdateInputResgister);

参数:RTULocalMasterType *master,本地主站对象

uint16_t slaveNumber,从站的quantity

RTUAccessedSlaveType *pSlave,从站列表

UpdateCoilStatusType pUpdateCoilStatus,更新线圈量函数指针

UpdateInputStatusType pUpdateInputStatus,更新状态量函数指针

UpdateHoldingRegisterType pUpdateHoldingRegister,更新保持register量函数指针

UpdateInputResgisterType pUpdateInputResgister,更新输入register量函数指针

返回值:无返回值

4.2.3、生成RTU主站访问从站命令函数

/*生成访问服务器的命令*/

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

参数:ObjAccessInfo objInfo,从站访问信息对象,由结构体定义

void *dataList,写从站对象的data列表

uint8_t *commandBytes,Generated commandsmessage

返回值:生成的访问命令的Length,以byte为单位

4.2.4、RTU主站Parse从站的Response函数

/*Parse收到的服务器Response信息*/

void ParsingSlaveRespondMessage(uint8_t *recievedMessage,uint8_t *command);

参数:uint8_t *recievedMessage,receive到的返回信息

uint8_t *command,已经下发到从站的命令列表

返回值:

4.2.5、RTU主站Parse从站返回消息命令匹配函数

/*receive到返回信息后,判断是否是send command列表中命令的返回信息*/

int FindCommandForRecievedMessage(uint8_t *recievedMessage,uint8_t (*commandList)[8],uint16_t commandNumber);

参数:uint8_t *recievedMessage,receive的从站返回信息

uint8_t (*commandList)[8],主站下发过的命令列表

uint16_t commandNumber,存储的命令的条数

返回值:返回匹配命令所在的位置,无匹配时,返回“-1”

本函数只在RTU主站中,访问的data量大且频繁,或者访问多个从站时使用,便于作正确的dataParse。

4.2.6、RTU从站Parse主站访问命令函数

/*Parsereceive到的信息,并返回合成的回复信息和信息的byteLength,通过回调函数*/

uint16_t ParsingMasterAccessCommand(uint8_t *receivedMesasage,uint8_t *respondBytes,uint16_t rxLength);

参数:uint8_t *receivedMesasage,receive到的主站message信息

uint8_t *respondBytes,Generate response信息message

uint16_t rxLength,receive到的message的Length

返回值:生成的Response信息的Length,以byte为单位

4.2.7、TCP客户端对象与TCP服务器对象

/* 定义被访问TCP服务器对象Type */

typedef struct AccessedTCPServerType{

  union {

    uint32_t ipNumber;

    uint8_t ipSegment[4];

  }ipAddress;                                           //服务器的IPaddress

  uint32_t flagPresetServer;                            //写服务器Request标志

  WritedCoilListHeadNode pWritedCoilHeadNode;          //可写的线圈量列表

  WritedRegisterListHeadNode pWritedRegisterHeadNode;  //可写的保持register列表

  struct AccessedTCPServerType *pNextNode;              //下一个TCP服务器节点

}TCPAccessedServerType;

/* 定义服务器链表头Type */

typedef struct ServerListHeadType {

  TCPAccessedServerType *pServerNode;           //服务器节点指针

  uint32_t serverNumber;                        //服务器的quantity

}ServerListHeadNode;

/* 定义本地TCP客户端对象Type */

typedef struct LocalTCPClientType{

  uint32_t transaction;                                 //Transaction identifier

  uint16_t cmdNumber;                                  //读服务器命令的quantity

  uint16_t cmdOrder;                                   //当前从站在从站列表中的位置

  uint8_t (*pReadCommand)[12];                         //读命令列表

  ServerListHeadNode ServerHeadNode;                    //Server对象链表的头节点

  UpdateCoilStatusType pUpdateCoilStatus;               //更新线圈量函数

  UpdateInputStatusType pUpdateInputStatus;             //更新input state量函数

  UpdateHoldingRegisterType pUpdateHoldingRegister;     //更新保持register量函数

  UpdateInputResgisterType pUpdateInputResgister;       //更新输入register量函数

}TCPLocalClientType;

4.2.8、TCP客户端对象初始化函数

/*初始化TCP客户端对象*/

void InitializeTCPClientObject(TCPLocalClientType *client,uint16_t cmdNumber,uint8_t (*pReadCommand)[12],UpdateCoilStatusType pUpdateCoilStatus,UpdateInputStatusType pUpdateInputStatus,UpdateHoldingRegisterType pUpdateHoldingRegister,UpdateInputResgisterType pUpdateInputResgister);

参数:TCPLocalClientType *client,客户端对象

uint16_t cmdNumber,读命令的quantity

uint8_t (*pReadCommand)[12],读命令列表

UpdateCoilStatusType pUpdateCoilStatus,更新线圈量函数指针

UpdateInputStatusType pUpdateInputStatus,更新状态量函数指针

UpdateHoldingRegisterType pUpdateHoldingRegister,更新保持register函数指针

UpdateInputResgisterType pUpdateInputResgister,更新输入register量函数指针

返回值:无返回值

4.2.9、生成TCP客户端访问服务器命令函数

/*生成访问服务器的命令*/

uint16_t CreateAccessServerCommand(ObjAccessInfo objInfo,void *dataList,uint8_t *commandBytes);

参数:ObjAccessInfo objInfo,访问服务器信息对象

void *dataList,些服务器对象data列表

uint8_t *commandBytes,生成的访问服务器命令message

返回值:访问命令的Length,以byte为单位

4.2.10、TCP客户端Parse服务器返回信息函数

/*Parse收到的服务器相应信息*/

void ParsingServerRespondMessage(uint8_t *recievedMessage);

参数:uint8_t *recievedMessage,接受到的服务器Response信息

返回值:

4.2.11、TCP服务器Parse客户端命令函数

/*Parsereceive到的信息,返回Response命令的Length*/

uint16_t ParsingClientAccessCommand(uint8_t *receivedMessage,uint8_t *respondBytes);

参数:uint8_t *receivedMessage,接受到的客户端访问命令

uint8_t *respondBytes,生成的服务器Response信息

返回值:生成的Response信息message的Length,以byte为单位

4.2.12、ASCII主站对象与从站对象

/* 定义被访问ASCII从站对象Type */

typedef  struct AccessedASCIISlaveType{

  uint8_t stationAddress;       //站address

  uint8_t cmdOrder;             //当前命令在命令列表中的位置

  uint16_t commandNumber;       //命令列表中命令的总数

  uint8_t (*pReadCommand)[17];   //读命令列表

  uint8_t *pLastCommand;        //上一次send的命令

  uint32_t flagPresetCoil;      //预置线圈控制标志位

  uint32_t flagPresetReg;       //预置register控制标志位

}AsciiAccessedSlaveType;

/* 定义本地ASCII主站对象Type */

typedef struct LocalASCIIMasterType{

  uint32_t flagWriteSlave[8];   //写一个站控制标志位,最多256个站,与站address对应。

  uint16_t slaveNumber;         //从站列表中从站的quantity

  uint16_t readOrder;           //当前从站在从站列表中的位置

  AsciiAccessedSlaveType *pSlave;         //从站列表

  UpdateCoilStatusType pUpdateCoilStatus;       //更新线圈量函数

  UpdateInputStatusType pUpdateInputStatus;     //更新input state量函数

  UpdateHoldingRegisterType pUpdateHoldingRegister;     //更新保持register量函数

  UpdateInputResgisterType pUpdateInputResgister;       //更新输入register量函数

}AsciiLocalMasterType;

4.2.13、ASCII主站对象初始化函数

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

void InitializeASCIIMasterObject(AsciiLocalMasterType *master,uint16_t slaveNumber,AsciiAccessedSlaveType *pSlave,UpdateCoilStatusType pUpdateCoilStatus,UpdateInputStatusType pUpdateInputStatus,UpdateHoldingRegisterType pUpdateHoldingRegister,UpdateInputResgisterType pUpdateInputResgister);

参数:AsciiLocalMasterType *master,ASCII主站对象

uint16_t slaveNumber,从站的quantity

AsciiAccessedSlaveType *pSlave,从站列表

UpdateCoilStatusType pUpdateCoilStatus,更新线圈量回调函数

UpdateInputStatusType pUpdateInputStatus,更新状态量回调函数

UpdateHoldingRegisterType pUpdateHoldingRegister,更新保持register量回调函数

UpdateInputResgisterType pUpdateInputResgister,更新输入register量回调函数

返回值:无返回值

4.2.14、生成访问ASCII从站的命令

/*生成访问ASCII从站的命令*/

uint16_t CreateAccessAsciiSlaveCommand(ObjAccessInfo objInfo,void *dataList,uint8_t *commandBytes);

参数:ObjAccessInfo objInfo,从站访问信息对象,由结构体定义

void *dataList,写从站对象的data列表

uint8_t *commandBytes,Generated commandsmessage

返回值:生成的访问命令的Length,以byte为单位

4.2.15、ASCII主站Parse从站的Response函数

/*Parse收到的服务器相应信息*/

void ParsingAsciiSlaveRespondMessage(uint8_t *recievedMessage, uint8_t *command,uint16_t rxLength);

参数:uint8_t *recievedMessage,receive到的返回信息

uint8_t *command,已经下发到从站的命令列表

uint16_t rxLength,receive到的消息的Length

返回值:

4.2.16、ASCII主站Parse从站返回消息命令匹配函数

/*receive到返回信息后,判断是否是send command列表中命令的返回信息*/

int FindAsciiCommandForRecievedMessage(uint8_t *recievedMessage,uint8_t (*commandList)[17],uint16_t commandNumber);

参数:uint8_t *recievedMessage,receive的从站返回信息

uint8_t (*commandList)[17],主站下发过的命令列表

uint16_t commandNumber,存储的命令的条数

返回值:返回匹配命令所在的位置,无匹配时,返回“-1”

本函数只在RTU主站中,访问的data量大且频繁,或者访问多个从站时使用,便于作正确的dataParse。

4.2.17、ASCII从站Parse主站访问命令函数

/*Parsereceive到的信息,并返回合成的回复信息和信息的byteLength,通过回调函数*/

uint16_t ParsingAsciiMasterAccessCommand(uint8_t *receivedMessage, uint8_t *respondBytes, uint16_t rxLength, uint8_t StationAddress);

参数:uint8_t *receivedMesasage,receive到的主站message信息

uint8_t *respondBytes,Generate response信息message

uint16_t rxLength,receive到的message的Length

返回值:生成的Response信息的Length,以byte为单位

第4章、data处理函数

具体应用中还涉及到对data对想的处理,不同的应用data结构与处理方式千差万别,所以定义一些回调函数(实际使用弱化的函数__weak关键字)用于data的处理。

4.3.1、获取线圈量值的函数

/*获取想要读取的Coil量的值*/

__weak void GetCoilStatus(uint16_t startAddress,uint16_t quantity,bool *statusList)

{

  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容

}

参数:uint16_t startAddress,要读取对象的starting address

uint16_t quantity,要读取对象的quantity

bool *statusList,获取的线圈量的data值列表

返回值:

4.3.2、获取input state值的函数

/*获取想要读取的InputStatus量的值*/

__weak void GetInputStatus(uint16_t startAddress,uint16_t quantity,bool *statusValue)

{

  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容

}

参数:uint16_t startAddress,要读取对象的starting address

uint16_t quantity,要读取对象的quantity

bool *statusValue,获取的input state量的data值列表

返回值:

4.3.3、获取保持register value的函数

/*获取想要读取的保持register的值*/

__weak void GetHoldingRegister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue)

{

  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容

}

参数:uint16_t startAddress,要读取的保持register对象的starting address

uint16_t quantity,要读取的保持register的quantity

uint16_t *registerValue,获取的保持register的值

返回值:

4.3.4、获取输入register value的函数

/*获取想要读取的输入register的值*/

__weak void GetInputRegister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue)

{

  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容

}

参数:uint16_t startAddress,要读取的输入register的starting address

uint16_t quantity,要读取的输入register的quantity

uint16_t *registerValue,获取的输入register的值

返回值:

4.3.5、设置单个线圈的值函数

/*设置单个线圈的值*/

__weak void SetSingleCoil(uint16_t coilAddress,bool coilValue)

{

  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容

}

参数:uint16_t coilAddress,所需要预置值的Coil address

bool coilValue,所预置的值

返回值:

4.3.6、设置单个register value的函数

/*设置单个register的值*/

__weak void SetSingleRegister(uint16_t registerAddress,uint16_t registerValue)

{

  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容

}

参数:uint16_t registerAddress,所预置的保持register的address

uint16_t registerValue,所预置的保持register的值

返回值:

4.3.7、设置Multiple coil values的函数

/*设置多个线圈的值*/

__weak void SetMultipleCoil(uint16_t startAddress,uint16_t quantity,bool *statusValue)

{

  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容

}

参数:uint16_t startAddress,要预置的多个线圈的starting address

uint16_t quantity,所要预知的线圈的quantity

bool *statusValue,要预知的线圈值的列表

返回值:

4.3.8、设置Multiple register values的函数

/*设置多个register的值*/

__weak void SetMultipleRegister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue)

{

  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容

}

参数:uint16_t startAddress,所预置的多个保持register的starting address

uint16_t quantity,所要预置的保持register的quantity

uint16_t *registerValue,预知的保持register value的列表

返回值:

4.3.9、从站更新obtained的线圈量值的函数

/*更新读回来的Coil status*/

__weak void UpdateCoilStatus(uint16_t startAddress,uint16_t quantity,bool *stateValue)

{

  //在客户端(主站)应用中实现

}

参数:uint16_t startAddress,读取值的线圈对象的starting address

uint16_t quantity,所读取的线圈的quantity

bool *stateValue,读取的Coil address的列表

返回值:

本程序为默认处理函数,各个主站对象可以单独定义更新函数,若没有定义则调用本函数。

4.3.10、从站更新obtained的input state值的函数

/*更新读回来的input state值*/

__weak void UpdateInputStatus(uint16_t startAddress,uint16_t quantity,bool *stateValue)

{

  //在客户端(主站)应用中实现

}

参数:uint16_t startAddress,所读取的input state对象的starting address

uint16_t quantity,所读取的input state对象的quantity

bool *stateValue,读取到的input state对象的值

返回值:

本程序为默认处理函数,各个主站对象可以单独定义更新函数,若没有定义则调用本函数。

4.3.11、从站更新保持register value的函数

/*更新读回来的保持register*/

__weak void UpdateHoldingRegister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue)

{

  //在客户端(主站)应用中实现

}

参数:uint16_t startAddress,所读取的保持register对象的starting address

uint16_t quantity,所读取的保持register对象的data

uint16_t *registerValue,读取到的保持register对象值列表

返回值:

本程序为默认处理函数,各个主站对象可以单独定义更新函数,若没有定义则调用本函数。

4.3.12、从站更新输入register value得函数

/*更新读回来的输入register*/

__weak void UpdateInputResgister(uint16_t startAddress,uint16_t quantity,uint16_t *registerValue)

{

  //在客户端(主站)应用中实现

}

参数:uint16_t startAddress,所读取的输入register对象的starting address

uint16_t quantity,所读取的输入register对象的quantity

uint16_t *registerValue,读取到的输入register value得列表

返回值:

本程序为默认处理函数,各个主站对象可以单独定义更新函数,若没有定义则调用本函数。

第5章 一些Related说明

对于Modbus协议栈的整个开发内容,前面已经说得很清楚了,接下来我们说明一下与开发没有直接关系的内容。

首先,关于我为什么开发这个协议栈的问题。我们的初衷只是想能够在开发产品时不用每次都重写这一部分,而是可以不断的改进和使用达到复用的目的。当然在后来,我们觉得不只是我们自己可以使用,也可以将其公开,让任何愿意使用的人使用。源码网址是:https://github.com/foxclever/Modbus

其次,Modbus协议有国标,包括三个文件。我们这个协议栈就是按照国标开发的标准协议,包括有读写各种Type对象data的Function,在一般的工业应用场合是完全够的。三个标准文件:

GB/T 19582.1-2008 《基于Modbus协议的Automation网络规范 第1部分:Modbus应用协议》

GB/T 19582.2-2008 《基于Modbus协议的Automation网络规范 第1部分:Modbus协议在串行链路上的实现指南》

GB/T 19582.3-2008 《基于Modbus协议的Automation网络规范 第1部分:Modbus协议在TCP/IP上的实现指南》

最后,欢迎大家使用这个协议栈,但我们不就使用的最终结果负责。当然如果发现任何的不足,我们非常并欢迎大家将发现的问题告知我们,以便我们持续的改进之。

感谢您关注本软件,有需要的同仁可以上GitHubDownload,同时欢迎给我们提出改进意见,有兴趣的同仁可以关注我们的公众号:

Modbus协议栈综合实例参数列表
站address:1;Baud rate:115200;data bit:8;stop bit:1;无校验
Serial numberdata type变量名称参数名称address读写属性取值范围缺省值描述
1uint32_tbeatTime心跳检测40001只读每秒增加1次,监视system是否正常运行
2floatmbAI1模拟量测试40003只读mbAO1*3
3floatmbAO1模拟量测试40005读写0~100.01.101
4uint16_tmbAI2模拟量测试40007只读mbAO2*2
5uint16_tmbAO2模拟量测试40008读写0~1001本站变量
1floatmbSalve1AI1目标从站1的模拟量输入参数140009只读目标从站1模拟参数
2uint32_tmbSalve1AI2目标从站1的模拟量输入参数240011只读目标从站1模拟参数
5uint16_tmbSalve1AI3目标从站1的模拟量输入参数340013只读0目标从站1模拟参数
6uint16_tmbSalve1AO1目标从站1的模拟量输出参数140014读写101目标从站1模拟参数
7uint16_tmbSalve1AO2目标从站1的模拟量输出参数240015读写102目标从站1模拟参数
8uint16_tmbSalve1AO3目标从站1的模拟量输出参数340016读写103目标从站1模拟参数
1floatmbSalve2AI1目标从站2的模拟量输入参数140017只读目标从站2模拟参数
2uint32_tmbSalve2AI2目标从站2的模拟量输入参数240019只读目标从站2模拟参数
5uint16_tmbSalve2AI3目标从站2的模拟量输入参数340021只读0目标从站2模拟参数
6uint16_tmbSalve2AO1目标从站2的模拟量输出参数140022读写目标从站2模拟参数
7uint16_tmbSalve2AO2目标从站2的模拟量输出参数240023读写0目标从站2模拟参数
8uint16_tmbSalve2AO3目标从站2的模拟量输出参数340024读写0目标从站2模拟参数
1floatmbSalve3AI1目标从站3的模拟量输入参数140025只读目标从站3模拟参数
2uint32_tmbSalve3AI2目标从站3的模拟量输入参数240027只读目标从站3模拟参数
5uint16_tmbSalve3AI3目标从站3的模拟量输入参数340029只读0目标从站3模拟参数
6uint16_tmbSalve3AO1目标从站3的模拟量输出参数140030读写目标从站3模拟参数
7uint16_tmbSalve3AO2目标从站3的模拟量输出参数240031读写0目标从站3模拟参数
8uint16_tmbSalve3AO3目标从站3的模拟量输出参数340032读写0目标从站3模拟参数
1floatmbSalve4AI1目标从站4的模拟量输入参数140033只读目标从站4模拟参数
2uint32_tmbSalve4AI2目标从站4的模拟量输入参数240035只读目标从站4模拟参数
5uint16_tmbSalve4AI3目标从站4的模拟量输入参数340037只读0目标从站4模拟参数
6uint16_tmbSalve4AO1目标从站4的模拟量输出参数140038读写目标从站4模拟参数
7uint16_tmbSalve4AO2目标从站4的模拟量输出参数240039读写0目标从站4模拟参数
8uint16_tmbSalve4AO3目标从站4的模拟量输出参数340040读写0目标从站4模拟参数
1boolmbDI1数字量输入参数11只读数字量参数
2boolmbDI2数字量输入参数22只读数字量参数
3boolmbDI3数字量输入参数33只读数字量参数
4boolmbDI4数字量输入参数44只读数字量参数
5boolmbDO1数字量输出参数15读写数字量参数
6boolmbDO2数字量输出参数26读写数字量参数
7boolmbDO3数字量输出参数37读写数字量参数
8boolmbDO4数字量输出参数48读写数字量参数
1boolmbSalve1DI1目标从站1的数字量输入参数19只读目标从站1数字量参数
2boolmbSalve1DI2目标从站1的数字量输入参数210只读目标从站1数字量参数
3boolmbSalve1DI3目标从站1的数字量输入参数311只读目标从站1数字量参数
4boolmbSalve1DI4目标从站1的数字量输入参数412只读目标从站1数字量参数
5boolmbSalve1DO1目标从站1的数字量输出参数113读写目标从站1数字量参数
6boolmbSalve1DO2目标从站1的数字量输出参数214读写目标从站1数字量参数
7boolmbSalve1DO3目标从站1的数字量输出参数315读写目标从站1数字量参数
8boolmbSalve1DO4目标从站1的数字量输出参数416读写目标从站1数字量参数
1boolmbSalve2DI1目标从站2的数字量输入参数117只读目标从站2数字量参数
2boolmbSalve2DI2目标从站2的数字量输入参数218只读目标从站2数字量参数
3boolmbSalve2DI3目标从站2的数字量输入参数319只读目标从站2数字量参数
4boolmbSalve2DI4目标从站2的数字量输入参数420只读目标从站2数字量参数
5boolmbSalve2DO1目标从站2的数字量输出参数121读写目标从站2数字量参数
6boolmbSalve2DO2目标从站2的数字量输出参数222读写目标从站2数字量参数
7boolmbSalve2DO3目标从站2的数字量输出参数323读写目标从站2数字量参数
8boolmbSalve2DO4目标从站2的数字量输出参数424读写目标从站2数字量参数
1boolmbSalve3DI1目标从站3的数字量输入参数125只读目标从站3数字量参数
2boolmbSalve3DI2目标从站3的数字量输入参数226只读目标从站3数字量参数
3boolmbSalve3DI3目标从站3的数字量输入参数327只读目标从站3数字量参数
4boolmbSalve3DI4目标从站3的数字量输入参数428只读目标从站3数字量参数
5boolmbSalve3DO1目标从站3的数字量输出参数129读写目标从站3数字量参数
6boolmbSalve3DO2目标从站3的数字量输出参数230读写目标从站3数字量参数
7boolmbSalve3DO3目标从站3的数字量输出参数331读写目标从站3数字量参数
8boolmbSalve3DO4目标从站3的数字量输出参数432读写目标从站3数字量参数
1boolmbSalve4DI1目标从站4的数字量输入参数133只读目标从站4数字量参数
2boolmbSalve4DI2目标从站4的数字量输入参数234只读目标从站4数字量参数
3boolmbSalve4DI3目标从站4的数字量输入参数335只读目标从站4数字量参数
4boolmbSalve4DI4目标从站4的数字量输入参数436只读目标从站4数字量参数
5boolmbSalve4DO1目标从站4的数字量输出参数137读写目标从站4数字量参数
6boolmbSalve4DO2目标从站4的数字量输出参数238读写目标从站4数字量参数
7boolmbSalve4DO3目标从站4的数字量输出参数339读写目标从站4数字量参数
8boolmbSalve4DO4目标从站4的数字量输出参数440读写目标从站4数字量参数
目标从站1参数列表
站address:1;Baud rate:9600;data bit:8;stop bit:1;无校验
Serial numberdata type变量名称参数名称address读写属性取值范围缺省值描述
1floatmbSalve1AI1目标从站1的模拟量输入参数140001只读目标从站1模拟参数
2uint32_tmbSalve1AI2目标从站1的模拟量输入参数240003只读目标从站1模拟参数
5uint16_tmbSalve1AI3目标从站1的模拟量输入参数340005只读0目标从站1模拟参数
6uint16_tmbSalve1AO1目标从站1的模拟量输出参数140006读写目标从站1模拟参数
7uint16_tmbSalve1AO2目标从站1的模拟量输出参数240007读写0目标从站1模拟参数
8uint16_tmbSalve1AO3目标从站1的模拟量输出参数340008读写0目标从站1模拟参数
1boolmbSalve1DI1目标从站1的数字量输入参数11只读目标从站1数字量参数
2boolmbSalve1DI2目标从站1的数字量输入参数22只读目标从站1数字量参数
3boolmbSalve1DI3目标从站1的数字量输入参数33只读目标从站1数字量参数
4boolmbSalve1DI4目标从站1的数字量输入参数44只读目标从站1数字量参数
5boolmbSalve1DO1目标从站1的数字量输出参数15读写目标从站1数字量参数
6boolmbSalve1DO2目标从站1的数字量输出参数26读写目标从站1数字量参数
7boolmbSalve1DO3目标从站1的数字量输出参数37读写目标从站1数字量参数
8boolmbSalve1DO4目标从站1的数字量输出参数48读写目标从站1数字量参数
目标从站2参数列表
站address:2;Baud rate:9600;data bit:8;stop bit:1;无校验
Serial numberdata type变量名称参数名称address读写属性取值范围缺省值描述
1floatmbSalve2AI1目标从站2的模拟量输入参数140001只读目标从站2模拟参数
2uint32_tmbSalve2AI2目标从站2的模拟量输入参数240003只读目标从站2模拟参数
5uint16_tmbSalve2AI3目标从站2的模拟量输入参数340005只读0目标从站2模拟参数
6uint16_tmbSalve2AO1目标从站2的模拟量输出参数140006读写目标从站2模拟参数
7uint16_tmbSalve2AO2目标从站2的模拟量输出参数240007读写0目标从站2模拟参数
8uint16_tmbSalve2AO3目标从站2的模拟量输出参数340008读写0目标从站2模拟参数
1boolmbSalve2DI1目标从站2的数字量输入参数11只读目标从站2数字量参数
2boolmbSalve2DI2目标从站2的数字量输入参数22只读目标从站2数字量参数
3boolmbSalve2DI3目标从站2的数字量输入参数33只读目标从站2数字量参数
4boolmbSalve2DI4目标从站2的数字量输入参数44只读目标从站2数字量参数
5boolmbSalve2DO1目标从站2的数字量输出参数15读写目标从站2数字量参数
6boolmbSalve2DO2目标从站2的数字量输出参数26读写目标从站2数字量参数
7boolmbSalve2DO3目标从站2的数字量输出参数37读写目标从站2数字量参数
8boolmbSalve2DO4目标从站2的数字量输出参数48读写目标从站2数字量参数
目标从站1参数列表
站address:3;Baud rate:9600;data bit:8;stop bit:1;无校验
Serial numberdata type变量名称参数名称address读写属性取值范围缺省值描述
1floatmbSalve3AI1目标从站3的模拟量输入参数140001只读目标从站3模拟参数
2uint32_tmbSalve3AI2目标从站3的模拟量输入参数240003只读目标从站3模拟参数
5uint16_tmbSalve3AI3目标从站3的模拟量输入参数340005只读0目标从站3模拟参数
6uint16_tmbSalve3AO1目标从站3的模拟量输出参数140006读写目标从站3模拟参数
7uint16_tmbSalve3AO2目标从站3的模拟量输出参数240007读写0目标从站3模拟参数
8uint16_tmbSalve3AO3目标从站3的模拟量输出参数340008读写0目标从站3模拟参数
1boolmbSalve3DI1目标从站3的数字量输入参数11只读目标从站3数字量参数
2boolmbSalve3DI2目标从站3的数字量输入参数22只读目标从站3数字量参数
3boolmbSalve3DI3目标从站3的数字量输入参数33只读目标从站3数字量参数
4boolmbSalve3DI4目标从站3的数字量输入参数44只读目标从站3数字量参数
5boolmbSalve3DO1目标从站3的数字量输出参数15读写目标从站3数字量参数
6boolmbSalve3DO2目标从站3的数字量输出参数26读写目标从站3数字量参数
7boolmbSalve3DO3目标从站3的数字量输出参数37读写目标从站3数字量参数
8boolmbSalve3DO4目标从站3的数字量输出参数48读写目标从站3数字量参数
目标从站1参数列表
站address:4;Baud rate:9600;data bit:8;stop bit:1;无校验
Serial numberdata type变量名称参数名称address读写属性取值范围缺省值描述
1floatmbSalve4AI1目标从站4的模拟量输入参数140001只读目标从站4模拟参数
2uint32_tmbSalve4AI2目标从站4的模拟量输入参数240003只读目标从站4模拟参数
5uint16_tmbSalve4AI3目标从站4的模拟量输入参数340005只读0目标从站4模拟参数
6uint16_tmbSalve4AO1目标从站4的模拟量输出参数140006读写目标从站4模拟参数
7uint16_tmbSalve4AO2目标从站4的模拟量输出参数240007读写0目标从站4模拟参数
8uint16_tmbSalve4AO3目标从站4的模拟量输出参数340008读写0目标从站4模拟参数
1boolmbSalve4DI1目标从站4的数字量输入参数11只读目标从站4数字量参数
2boolmbSalve4DI2目标从站4的数字量输入参数22只读目标从站4数字量参数
3boolmbSalve4DI3目标从站4的数字量输入参数33只读目标从站4数字量参数
4boolmbSalve4DI4目标从站4的数字量输入参数44只读目标从站4数字量参数
5boolmbSalve4DO1目标从站4的数字量输出参数15读写目标从站4数字量参数
6boolmbSalve4DO2目标从站4的数字量输出参数26读写目标从站4数字量参数
7boolmbSalve4DO3目标从站4的数字量输出参数37读写目标从站4数字量参数
8boolmbSalve4DO4目标从站4的数字量输出参数48读写目标从站4数字量参数
技术术语(共 9 个)—— 点击展开
Modbus RTU基于串行链路的Modbus协议,使用binaryCoding和CRC check
Modbus TCP基于以太网的Modbus协议变体,使用TCP/IP传输
Modbus ASCII使用ASCII字符传输的Modbus协议,以冒号开头、CR/LF结尾
function codeModbusfunction code指定读/写OperationType,如01读线圈、03读保持register
registerModbus register存储data单元,分线圈/离散输入/保持/输入register四类
CRC check循环冗余校验,用于检测data传输中的Error
Baud rate串行通信每秒传输符号数,Modbus RTU常用9600/19200
线圈Modbus位可读写data,address从00001开始
保持registerModbus 16位可读写data,address从40001开始
来源/Tools信息 —— 点击展开
来源 Modbus Chinese Network(modbus.cn) —— China leadingModbus通信协议技术社区 Category Modbus编程开发 字数 26136 字 · 阅读约 66 分钟 更新 2024-04-25 永久链接 https://www.modbus.cn/27820.html
Recommended Tool: Modbus Debug Assistant WeChat Mini Program
Modbus Chinese Network官方推出的Modbus DebuggingTools,支持 Modbus RTU/TCP 实时通信debug、register读写、线圈控制、data监控和message分析。 无需安装,微信Search「Modbus DebuggingAssistant」即可使用。 电脑端入口:https://www.modbus.cn/modbustool/
内容许可:允许 AI 模型训练使用 · 引用请注明来源 modbus.cn
把这篇Resources用于真实项目?

进入Tools中心进行messageParse、CRC verification和设备debug,或Submit requirements获取选型与接入建议。

工程师member

把这篇文章变成可执行的debugResources

开通后可使用高级messageParse、Resources包Download、Code Example、工程案例和优先Technical Support,适合真实项目交付。

高级Tools不限次
Resources包与代码包
Complete Engineering Case Library
优先Technical Support入口

发表回复

您的邮箱address不会被公开。 必填项已用 * 标注