首页 > 其他分享 >EPICS synApps modbus模块-Copy

EPICS synApps modbus模块-Copy

时间:2024-03-04 10:34:58浏览次数:25  
标签:Modbus 驱动程序 16 EPICS modbus A0 Koyo1 synApps

原文链接:EPICS synApps modbus模块_epics synapps win-CSDN博客

在EPICS下用于Modbus协议的驱动支持

Modbus概要

MODBUS是一个应用层消息协议,位于OSI模块的第7层,它在在不同总线类型或网络上连接的设备之间提供了客户端/服务器通信。它一般用于用I/O系统通信,包括可编程逻辑控制器(PLCs)。

 

Modbus通信链路

Modbus支持以下3种通信链路层:

Modbus通信链路

链路类型 描述
TCP 使用标准端口502的TCP/IP
RTU RTU通常运行在串行通信链路,即:RS-232, RS-433或RS-485。RTU使用一个附加的CRC用于包校验。协议直接以8个数据为传输每个字节,所以使用"二进制"而非ASCII编码。当使用串行链路时,消息帧的起始和结束时通过计时而非通过特定字符被探测到。RTU也可以在TCP上运行,虽然这比不使用RTU的标准Modbus TCP不常见。
Serial ASCII 串行协议,其通常运行在串行通信链路,即:RS-232, RS-433或RS-485。串行ASCII使用一个附加的LRC用于包校验。这个协议以2个ASCII字符编码每个字节。消息帧的起始和结束是通过特殊字符被探测(":"开始一条消息和CR/LR结束一条消息)。此协议效率低于RTU,但在某些环境中更加可靠。ASCII也可以运行在TCP上,虽然这比标准Modbus TCP不常见。

这个modbus包支持所有以上modbus通信链路层。

Modbus数据类型

Modbus提供对以下4中数据类型的访问:

modbus数据类型

主要表格 对象类型 访问 注释
离散输入 单bit 只读 可以由一个I/O系统提供这种数据类型。
线圈 单bit 读写 这种数据类型可以由应用程序更改。
输入寄存器 16位字 只读 可以由一个I/O系统提供这种数据类型。
保持寄存器 16位字 读写 这种数据类型可以由应用程序更改。

 

Modbus通信

Modbus通信由一条从Modbus客户端发送给Modbus服务器的请求消息组成。服务器用一条应答消息回答。Modbus请求消息包括:

1)一个8位Modbus功能码,其描述要被执行的数据传输的类型。

2) 一个16位Modbus地址,其描述要从其读取或者写数据到的服务器中的位置。

3) 对于写到操作,要被传输的数据。(???)

Modbus功能码

modbus支持以下9种Modbus功能码:

Modbus功能码

Modbus地址

通过一个16位整数地址指定Modbus地址。在16位地址空间中输入和输出的位置不是由Modbus协议定义,它是特定于厂家的。

注意:通过用一个400001(或300001)的偏移指定16位Modbus地址。此偏移未被这个modbus驱动使用,它只使用16位地址,不使用这个偏移。

 

Modbus数据长度限制

Modbus读取操作被限制于传输125个16位字或者2000比特。Modbus写操作被限制于传输123个16位字或者1968比特。

驱动架构

小心:modbus可以提供对PLC的所有I/O和内存的访问。实际上,甚至完全不需要在PLC中运行一个梯形逻辑程序。PLC可以用做一个"dumb" I/O子系统,所有逻辑驻留在EPICS IOC。但如果一个梯形逻辑正被运行在PLC中,则必须仔细地设置使用modbus的EPICS访问。例如,EPICS IOC可能被允许读取任何PLC I/O点(X输入,Y输出等),但写可能被限制于一个小范围的控制寄存器(例如:C200-C400)。梯形逻辑会监控这些控制寄存器,仅在这么做安全时,才认为来自EPICS的请求应该被响应。

modbus模块的架构从上自下由以下4层组成:

1) EPICS asyn device support。这是由asyn提供的通用设备支持。modbus不需要或不提供特殊的设备支持。

2) 一个EPICS asyn 端口驱动程序,用能作为一个Modbus客户端。modbus端口驱动程序使用标准的asyn接口(asynUInt32Digital, asynInt32等)与EPICS设备支持(第1层)进行通信。此驱动程序通过标准的asynOctet接口发送和接收设备无关的Modbus帧到"interpose interface(第三层)"。这些帧是独立于底层通信协议。在R3-0前,这个驱动程序用C编写。在R3-0中,它被编写成一个从asynPortDriver继承来的C++类。这使得它用一种方式导出其方法,这对其它驱动程序使用简单,尤其doModbusIO()方法。

3) 一个asyn "interpose interface"层处理底层通信层(TCP, RTU, ASCII)所需的其它数据。这层通过标准的asynOctet接口与上层Modbus层(第二层)和底层asyn硬件端口驱动程序(第4层)进行通信。

4) 一个asyn端口驱动程序处理底层通信(TCP/IP或串行)。这是asyn提供的标准端口驱动程序之一,即:drvAsynIPPort或drvAsynSerialPort。它们不是modbus模块的组成部分。

因为modbus充分使用了已有的asyn功能,并且值需要实现以上第2层和第3层,modbus中的代码量非常小(少于2500行)。

每个modbus端口驱动程序被分配单个Modbus功能码。通常一个驱动程序被被分配单个连续范围的Modbus内存,最多2000个位或125个字。一般位单个PLC创建若干modbus端口驱动程序,每个驱动程序读或写一个不同集合的离散输入、线圈,输入寄存器或保持寄存器。例如,可以创建一个端口驱动程序来读取离散输入X0-X37,创建第二个端口驱动程序读取控制寄存器C0-C377,和第三个端口驱动程序写控制寄存器C300-C377。

创建一个驱动程序,其被允许在16位Modbus地址空间中寻址任何位置,是可能的。每个读或写操作仍然被限制于125/123个字的限制。在这种情况中,被每个记录使用的asyn地址是绝对Modbus地址。在创建这个驱动程序时,通过传递-1作为modbusStartAddress,启用绝对寻址模式。

modbus端口驱动程序对单个Modbus功能的限制不应用到doModbusIO()方法。此访问可以用于使用任何功能码的任意Modbus IO。如果如上描述启用了绝对寻址,则doModbusIO()函数也可以寻址任意Modbus内存位置。

端口驱动程序的行为对读功能码(1,2,3,4),写功能码(5,6,15,16)以及读/写功能码(23)不同。

Modbus读功能

对于读功能码(当未使用绝对寻址时),这个驱动程序产生一个poller线程。这个poller线程在单个Modbus事务中读取分配给这个端口的整个Modbus内存块。值被存储在此驱动程序一个缓存中。在创建这个端口驱动时,设置了查询之间的延时,并且在之后运行时可以被更改。EPICS通过使用标准asyn接口(asynUInt32Digital, asynFloat64, asynInt32等)读取这些值。被读取的值是来自这个poller线程的最后被存储值。这表示EPICS读取操作是异步的,即:它们可以阻塞。这是因为虽然它们不直接导致Modbus I/O,它们需要等待一个指明这个poller线程完成的mutex。

对于读功能,设置EPICS记录为"I/O Intr"扫描是可能的。如果这已经完成了,则当有新数据对应那个输入时,这个端口驱动程序将回调设备支持。这提高了效率,因为这样的记录只在需要时才运行,不需要周期地扫描它们。

先前地段落描述了对应读取操作的正常配置,在此使用了相对的Modbus寻址。如果使用绝对寻址,则这个驱动不创建一个poller线程,因为它不知道Modbus地址空间的什么部分应该被查询。在这种情况中,进行读取的记录不能设置SCAN=I/O Intr。它们要么被周期地扫描,或者通过直接使记录运行被扫描,诸如写1到.PROC字段。这个记录每次运行时,它将产生一个单独地Modbus读操作。注意:这效率比用相对Modbus寻址一次读取很多寄存器低得多。出于此原因,通常应该避免用读功能的绝对Modbus寻址。

Modbus写功能

对于写功能,此驱动不自己创建一个单独的线程。此驱动程序而是进行Modbus I/O立即响应在标准asyn接口上的写操作。这表示EPICS写操作也是异步的,即:因为需要Modbus I/O,所有它们阻塞。当创建modbus驱动程序时,它告诉asynManager它可以阻塞,并且asynManager创建一个执行写操作的单独线程。

使用读/修改/写操作,完成使用asynUInt32Digital接口(掩码参数不是0x0或0xFFFF)的字写操作。这使得多个Modbus客户端可以读和写在相同Modbus内存块单个字。如果多个Modbus客户端(或PLC自身)能够修改单个字中位时,它不确保正确的操作。这是因为Modbus服务器不能以Modbus客户端级别一个原子操作执行读/修改/写。

对于写操作,指定单个读操作应该在端口驱动程序被创建时完成是可能的。这是通常被使用的,使得EPICS在IOC被初始化时获取一个输出设备的当前值。

Modbus RTU指定在向设备写之间一个至少3.5个字符的延时。modbusInterposeConfig函数允许你指定在每次写前一个写延时(msec为单位)。

Modbus写/读函数

Modbus功能码23允许写在单次操作中写一个寄存器集合并且读取一个寄存器集合。在写操作后执行读操作,并且要被读取的寄存器范围可以不同于要被写的寄存器范围。功能码23不被广泛使用,并且写/读操作不合适只读和只写驱动程序的modbus驱动程序模型。在modbus中实现23功能码有以下限制:

  • 一个使用modbus功能码23的驱动程序要么只读要么只写
  • 通过指定功能码123给drvModbusAsynConfigure命令创建的只读驱动程序。此驱动程序将使用对应Modbus协议的Modbus功能码23。它将只读取寄存器(如功能码3和4),它将不写任何数据到设备。
  • 通过指定功能码223给drvModbusAsynConfiure命令创建的只写驱动程序。此驱动程序将使用对应Modbus协议的Modbus功能码23。它将只写寄存器(如功能码16),它将不从设备读取任何数据。

平台无关

modbus应该运行在所有EPICS平台。已经在linux-x86, linux-x86_64, vxWorks, win32-x86,window-x6(带有Microsoft Visual Studio 2010 C++编译器的本地Windows),和cygwin-x86(带有gcc编译器和Cygwin库的Windows)上测试了它。

在modbus中可能与架构相关的唯一东西是在modbus.h中结构体包装。在gnu和Microsoft编译器上支持在那是使用的指令"#pragma pack(1)"。如果在某些感兴趣的编译器上不支持这个指令,则modbus.h将需要添加合适的架构相关代码。

创建一个modbus驱动程序

在modbus端口驱动程序被创建前,必须至少首先创建一个asyn TCP/IP或串口驱动程序来与硬件通信。所需命令取决于正在被使用的通信链路。

TCP/IP

对于TCP/IP,使用以下标准asyn命令:

drvAsynIPPortConfigure(portName, hostInfo, priority, noAutoConnect, noProcessEos)

以下示例在IP地址192.168.3.80的TCP端口502上创建了一个名为"Koyo1"的asyn IP端口驱动程序。使用默认优先级,并且noAutoConnect标志被设置为0,因而asynManager将进行正常的自动连接管理。设置noProcessEos为1,因为在TCP上的Modbus不需要字符串结尾处理。

drvAsynIPPortConfigure("Koyo1","192.168.3.80:502",0,0,1)

Serial RTU

对于串行RTU,使用以下标准asyn命令:

  1.   drvAsynSerialPortConfigure(portName, ttyName, priority, noAutoConnect, noProcessEos)
  2.   asynSetOption(portName, addr, key, value)

以下示例在/dev/ttyS1上创建了一个名为"Koyo1"的asyn本地串口驱动程序。使用默认优先级,并且noAutoConnect标志被设为0,使得asynManager将进行正常的自动连接管理。noProcessEos设置为0,因为在串行上的Modbus需要字符串结尾处理。串行端口参数被配置成38400波特,不检验,8数据位,1停止位。

  1.  drvAsynSerialPortConfigure("Koyo1", "/dev/ttyS1", 0,0,0)
  2.  asynSetOption("Koyo1",0,"baud","38400")
  3.  asynSetOption("Koyo1",0,"parity","none")
  4.  asynSetOption("Koyo1",0,"bits",8)
  5.  asynSetOption("Koyo1",0,"stop",1)

 

Serial ASCII

对于串行RTU,使用与用于串行RTU描述相同的命令。在asynSetOption命令之后,使用以下标准asyn命令:

  1.   asynOctetSetOutputEos(portName, addr, eos)
  2.   asynOctetSetInputEos(portName, addr, eos)

以下示例在/dev/ttyS1上创建了一个名为"Koyo1"的asyn本地串口驱动程序。使用默认优先级并且设置noAutoConnect标记为0,使得asynManager将进行正常的自动连接管理。noProcessEos标志设为0,因为在串行上的Modbus需要字符串结尾处理。串口参数被设置成38400,不校验,8数据位,1停止位。输入和输出的end-of-string被设置成CR/LF。

  1.  drvAsynSerialPortConfigure("Koyo1", "/dev/ttyS1", 0,0,0)
  2.  asynSetOption("Koyo1",0,"baud","38400")
  3.  asynSetOption("Koyo1",0,"parity","none")
  4.  asynSetOption("Koyo1",0,"bits",8)
  5.  asynSetOption("Koyo1",0,"stop",1)
  6.  asynOctetSetOutputEos("Koyo1",0,"\r\n")
  7.  asynOctetSetInputEos("Koyo1",0, "\r\n")

 

modbusInterposeConfig

在创建asynIPPort或者asynSerialPort驱动程序后,下一步是添加asyn “interpose interface”驱动程序。此驱动程序接收设备无关的Modbus帧并且位TCP,RTU或ASCII链路协议添加或移除通信链路专用信息。用以下命令创建这个interpose驱动程序:

modbusInterposeConfig(portName, linkType, timeoutMsec, writeDelayMsec)

modbusInterposeConfig命令

参数 数据类型 描述
portName string 先前创建的asynIPPort或asynSerialPort的名称
linkType int

Modbus链路层类型:

0=TCP/IP

1=RTU

2=ASCII

timeoutMsec int 对底层asynOcete驱动程序写和读操作的超时时间(毫秒为单位)。这个只被用于替代在EpICS设备支持中指定的超时时间。如果指定0,则使用要给默认2000毫秒的超时。
writeDelayMsec int 每次从EPICS写到设备前延时(毫秒为单位)。这一般只是Serial RTU设备需要。Modicon Modbus Protocol Reference Guide说着对于Serial RUT必须至少3.5个字符时间,即在9600波特时3.5毫秒。默认为0。

对于以上串行ASCII示例,在asynOctetSetInputEos命令后,将使用以下命令。着使用1秒超时时间,以及一个0ms的写超时。

modbusInterposeConfig("Koyo1",2,1000,0)

 

drvModbusAsynConfigure

一旦创建了asyn IP或串行端口驱动程序,并且已经配置了modbusInterpose驱动程序,用以下命令创建一个modbus端口驱动程序:

drvModbusAsynConfigure(portName, tcpPortName, slaveAddress, modbusFunction, modbusStartAddress, modbusLength, dataType, pollMsec, plcType)
drvModbusAsynConfigure命令


参数 数据类型 描述
portName string 要被创建的modbus端口的名称
tcpPortName string 先前创建的asyn IP或串口的名称
slaveAddress int Modbus slave的地址。这必须匹配对应RTU和ASCII的Modbus slave的配置。对于TCP,slave地址被用于"单元标识符",最后字段在MBAP头。"单元标识符"被大部分PLCs忽略,但某些PLC可能需要。
modbusFunction int modbus功能ma(1,2,3,4,5,6,15,16,123(对应23只读))或223(对于23只写)
modbusStartAddress int 要被访问的odbus数据段的起始地址。对于相对寻址,这必须在十进制范围0-65536,或者八进制范围0-0177777。对于绝对寻址,这必须设置为-1。
modbusLength int

要被访问的Modbus数据段的长度。

对于Modbus功能码1,2,5和15,以位指定这个。

对于Modbus功能码3,4,6,16,23,以16位字指定这个。

对于功能码1和2,长度限制是2000,对于功能5和15长度限制是1968.

对于3和4功能码,长度限制是125,对于功能码6,16和23,长度限制是123。

对于绝对寻址,这必须被设置成可能被使用的最大单次Modbus操作所需的尺寸。如果所有Modbus读和写是用于16位寄存器,这会是1,但如果64位浮点(4个16位寄存器)正在被使用,它将是4,例如,如果一个NELM=100的Int32 waveform记录 正在被读取或写,它将是200。

modbusDatatype int 这为这个端口设置默认数据类型。如果一个记录的drvUser是空或者它是MODBUS_DATA,这是使用的数据类型。支持的Modbus数据类型和相应的drvUser字段在以下表格中被描述。
pollMsec int 用于读取功能的查询线程的查询延时(毫秒为单位)。对于写功能,一个非0值表示在这个端口驱动程序首次被创建时Modbus数据应该被读取一次。
plcType string

PLC的类型(例如:Koyo, Modicon等)。

这个参数当前被用于在asynReport中打印信息。如果plcType字符串包含子串"Wago",它也用于特殊地对待Wago设备。


Modbus寄存器数据类型


modbus功能码3,4,6和16是用于访问16位寄存器。Modbus说明没有定义在这些寄存器中数据如何被解析,例如,以有符号或者无符号数值,二进制编码十进制(BCD)数值等。实际中,厂家组合多个16位寄存器去编码32位整数,32位或64位浮点数。以下表格列出了modbus支持地数据类型。用以上描述地modbusDataType参数定义了这个端口的默认数据类型。通过在链接中用drvUser字段指定一个不同的数据类型,对应特定记录的数据类型可以重写这个默认值。驱动程序使用这个信息在EPICS设备支持和Modbus之间转换数值。数据以epicsUInt32, epicsInt32和epicsFloat64数值与EPICS设备支持来回传输。注意:在此表格中描述的数据类型转换只用于使用asynInt32或asynFloat64接口的记录,在使用asynInt32Digtial接口时没有使用数据类型转换。asynUInt32Digital接口总是把寄存器当成无符号16位整数。


支持的Modbus数据类型



Modbus DataType

drvUser字段 描述
0 UNIT16 无符号16位二进制是整数
1 INT16SM 16位二进制整数,符号和幅度格式。在这种格式中,第15位是符号位,第0-14位是这个数值的幅度的绝对值。这是Koyo PLCs使用用于数值的其中一种格式,诸如ADC转换。
2 BCD_UNSIGNED 二进制编码BCD,无符号。这种数据类型是对应由4个4位半字节组成的一个16位数值,每个半字节编码一个从0到9的十进制数值。一个BCD
3 BCD_SIGNED 4个数字编码的十进制(BCD),有符号。这种数据类型是用于由3个4位半字节和一个3位比特组成的一个16位数值。第15位是一个符号位。有符号BCD数值可以保存从-7999到+7999的数值。这是由Koyo PLCs用于数值的其中一种格式,诸如ADC转换。
4 INT16 16位带符号的整数(2的补码)。当转换成epicsInt32时,这种数据类型扩展符号位。
5 INT32_LE 32位整数,小端(最低字在Modbus地址N,最高字在Modbus地址N+1)。
6 INT32_BE 32位整数。大端(最高字在Modbus地址N+1,最低字在Modbus地址N+1)
7 FLOAT32_LE 32位浮点,小端(最低字在Modbus地址N,最高字在Modbus地址N+1)
8 FLOAT32_BE 32位浮点,大端(最高字在Modbus地址N,最低字在Modbus地址N+1)
9 FLOAT64_LE 64位浮点,小端(最低字在Modbus地址N,最高字在Modbus地址N+3)
10 FLOAT64_BE 64位浮点,大端(最高字在Modbus地址N,最低字在Modbus地址N+3)
11 STRING_HIGH 字符串数据。在每个寄存器的高位字节中存储一个字符。
12 STRING_LOW 字符串数据。在每个寄存器的低位字节中存储一个字符
11 STRING_HIGH_LOW 字符串数据。在每个寄存器中存储两个字符,第一个字符在高字节,第二个字符存储低字节。
11 STRING_LOW_HIGH 字符串数据。在每个寄存器中存储两个字符,第一个存在低字节,第二个存在高字节。

注意:如果想要在asynInt32接口上不经转换的传输BCD数值给EPICS,则应该使用数据类型0,因为在这种情况中,不进行转换。


以下时一个使用32位浮点值的示例ai记录:


  1.  # 用于寄存器输入的ai记录模板
  2.  record(ai, "$(P)$(R)"){
  3.  field(DTYP, "asynFloat64")
  4.  field(INP, "@asyn($(PORT) $(OFFSET)FLOAT32_LE)")
  5.  field(HOPR, "$(HOPR)")
  6.  field(LOPR, "$(LOPR)")
  7.  field(PREC, "$(PREC)")
  8.  field(SCAN, "$(SCAN)")
  9.  }
 

Wago设备的注意


Wago设备的注意


通常在与写操作相同的地址进行这种初始读取操作。Wago设备不同于其它Modbus设备,因为要回读一个寄存器的地址与要写一个寄存器的地址不同。对于Wago设备,用于回读一个Modbus写功能的初始值的地址必须比用于写功能的地址大0x200。如果传给drvModbusAsynConfigure的plcType包含子串"Wago"(区分大小写),通过位回读地址添加0x200的偏移处理这个。注意:这不影响用于Wago读功能的地址。用户必须为读功能指定实际的Modbus地址。


用于TCP的drvAsynIPPort驱动程序的数目


每个drvAsynIPPort驱动程序创建一个单独的TCP/IP套接字连接PLC。使所有modbus端口驱动共享单个drvAsynIPPort驱动程序是可能的。在这种情况中,在单个套接字上以"串行"方式进行对这个PLC的所有I/O。一个modbus驱动程序的一个事务必须在另一个modbus驱动程序的事务开始前结束。创建多个drvAsynIPPort驱动程序(套接字)到单个PLC也是可能的,并且对每个modbus端口使用一个不同的drvAsynIPPort。在这种情况中,来自多个modbus驱动程序的I/O操作可以并行运行,而不是串行。这可以以在IOC和PLC上更多CPU负载以及多个网络流量为代价提高性能。


注意很多PLCs在几秒不活动后将超时是重要的。这对使用读功能码的modbus驱动程序不是一个问题,因为它们频繁进行查询。但使用写功能码的modbus驱动程序可能只进行偶尔的I/O,并且如果它们是通过一个drvAsynIPPort驱动程序进行通信的仅有驱动程序。因而,对于有写功能码的modbus驱动程序通常需要以至少一个有读功能码的驱动程序使用相同drvAsynIPPort驱动程序(套接字)来避免超时。


每个PLC使用多少drvAsynIPPort驱动程序的选择将根据外设性能以及资源 使用的考虑。一般从每个PLC一个drvAsynIPPort服务器开始(例如:由用于那个PLC的所有modbus驱动程序共享)并且看看此结果是否满足性能。

数值格式


用八进制而非十进制指定modbusStartAddress和modbusLength会是方便的,因为在大部分PLCs上这是方便的。在iocsh和vxWorks shell中,通过在数值前带一个0做这件事,例如:040400是一个八进制数。


EPICS设备支持


modbus实现了以下标准asyn接口:


  • asynUInt32Digital
  • asynInt32
  • asynInt32Array
  • asynFloat64
  • asynOctet
  • asynCommon
  • asynDrvUser

因为它实现了这些标准接口,完全用asyn自身提供的通用EPICS设备支持进行EPICS设备支持。没有提供作为modbus组成部分的特殊设备支持。


必须使用asyn R4-8或以上,因为对asyn做了某种次要增强来支持modbus所需的特性。


以下表格记录了EPICS设备支持使用的asyn接口。


由这个去哦的那个程序使用的drvUser参数确定了从设备支持发送什么命令。默认是MODBUS_DATA,因而其是设备支持链接说明中可选的。如果没有使用drvUser或者如果治党MODBUS_DATA,则用于使用asynInt32和asynFloat64接口的记录的数据类型是在drvModbusAsynConfigure命令中指定的默认数据类型。记录可以通过指定数据类型专用的drvUser字段重写默认的Modbus数据类型,例如:BCD_SIGNED, INT16,FLOAT32_LE等。


offset参数用于为一个记录指定相对于那个驱动程序的起始Modbus地址的数据位置。用位为使用功能1,2,5和15控制离散输入或线圈的驱动程序指定这个offset。例如,如果Modbus功能是2,并且Modbus起始地址是04000,则offset=2指向地址04002。对于Koyo PLC,X输入是位于对应功能2的Modbus起始地址,所以offset=2是输入X2。


如果使用了绝对寻址,则offset参数是一个绝对16位Modbus地址,而不i是相对于其是-1的起始Modbus地址。


用字为使用Modbus功能3,4,6和16寻址输入寄存器或保持寄存器的驱动程序指定offset。如果Modbus功能被设置成6并且Modbus地址是040600,则offset指向地址040602。对于Koyo PLC,在Modbus起始地址用功能6以16位字访问C控制继电器,offset=2将写到第三个16位字,其是线圈C40-C57。


对于32位或64位数据类型(INT32_LE, INT32_BE, FLOAT32_LE, FLOAT32_BE),offset指定第一个16位寄存器的位置,而第二个寄存器是在offset+1等。


对于字符串数据类型(STING_HIGH, STRING_LOW, STRING_HIGH_LOW, STRING_LOW_HIGH),offset指定第一个16位寄存器的位置,而第二个寄存器是在offset+1等。


asynUInt32Digital


用以下选择asynUInt32Digital设备支持


  1.   field(DTYP, "asynUInt32Digital")
  2.   field(INP, "@asynMask(portName, offset, mask timeout)drvUser")

asynUInt32Digial设备支持



Modbus

功能

偏移类型 数据类型 drvUser 支持的记录 描述
1,2 单bit MODUBS_DATA

bi,mbbi,

mbbiDirect

longin

value=(Modbus data & mask)

(通常mask=1)

3,4,23 16位字 16位字 MODUBS_DATA

bi,mbbi,

mbbiDirect

longin

value=(modbus data & mask)

(mask选择感兴趣的位)

5 单bit MODUBS_DATA

bo, mbbo,

mbboDirect

longout

modbus写(value&mask)

(通常mask=1)

6 16位字 16位字 MODUBS_DATA

bo, mbbo,

mbboDirect

longout

如果mask==0或mask=0xFFFF,进行modbus写(value),否则进行读/修改/写:对在value中置1和在mask中置1的位置1,在value中置0和在mask中置1的位,置0
any NA NA ENABLE_HISTOGRAM

bi,mbbi,

mbbiDirect,

longin

根据在驱动中禁用/启用I/O时间直方图,返回0/1
any NA NA ENABLE_HISTOGRAM

bo, mbbo,

mbboDirect

longout

根据value=0/1,则在驱动中禁用/启用I/O时间直方图

asynInt32


用以下选择asynInt32设备支持:


  1.   field(DTYP, "asynInt32")
  2.   field(INP, "@asyn(portName, offset, timeout)drvUser")

或者


field(INP, "@asynMask(portName, offset, nbits, timeout)drvUser")

 asynMask语法用于模拟I/O设备,为了指定在设备中的位数。对于Modbus需要这个,因为驱动程序只知道它返回了一个16位寄存器,单步知道在设备中实际的位数,并且因而不能用asynInt32->getBounds()返回有意义的数据。


nbits>0对应一个单极性设备。例如,nbits=12表示范围0-4095的单极性12位设备。nbits<-对应一个双极性设备。例如,nbits=-12表示范围-2048-2047的双极性12位设备。


注意:当写32位或64位值时,如果设备支持共功能码16,应该使用它。这种写将是"原子的"。如果使用功能码6,则将多条消息写这个数据,将有一段短时间,在此段时间内设备有不正确的数据。


asynInt32设备支持



Modbus功能 偏移类型 数据类型 drvUser 支持的记录 描述
1,2 单bit MODBUS_DATA

ai,bi,mbbi,

longin

value=(epicsUInt32)Modbus data
3,4,23 16位字 16,32或64位字 MODBUS_DATA(或数据类型专用值)

ai,mbbi,

longin

value=(epicsUInt32)Modbus data
5 单bit MODBUS_DATA

ao,bo,mbbo,

longout

modbus写值
6,16,23 16位字 16,32或64位字 MODBUS_DATA(或数据类型专用值)

ao,mbbo,

longout

modbus写值
any NA NA MODBUS_READ ao,bo,longout 用这个drvUser值写到一个Modbus输入驱动程序将强制这个poller线程立即运行一次,无论POLL_VALUE的值。
any NA NA READ_OK ai,longin 返回在这个asyn端口上成功读操作的数目。
any NA NA WRITE_OK ai,longin 返回在这个asyn端口上成功写操作的数目。
any NA NA IO_ERRORS ai,longin 返回在这个asyn端口上I/O错误的数目
any NA NA LAST_IO_TIME ai,longin 返回用于上次I/O操作的毫秒数
any NA NA MAX_IO_TIME ai,longin 返回用于I/O操作的最大毫秒数目
any NA NA HISTOGRAM_BIN_TIME ai,longin 用毫秒设置在统计直方图中每个bin的时间
 
 

asynFloat64

 用以下选择asynFloat64设备支持

  1.   field(DTYP, "asynFloat64")
  2.   field(INP, "@asyn(portName, offset, timeout)drvUser")

注意:当写32位或64位值时,如果设备支持支持功能码16,应该使用它。这种写将是"原子的"。如果使用功能码6,将用多条消息写这个数据,将有一段短时间,在这段时间内,设备将有不正确的数据。 

Modbus功能 偏移类型 数据类型 drvUser

支持的

记录

描述
1,2 单bit MODBUS_DATA ai value=(epicsFloat64)Modbus data
3,4,23 16位字 16,32或64位字 MODBUS_DATA(或数据类型专用值) ai value=(epicsFloat64)modbus data
5 单bit MODBUS_DATA ao modbus写(epicsUInt16)value
6,16,23 16位 16位字 MODBUS_DATA(或数据类型专用值) ao modbus写值
any NA NA POLL_DELAY ai,ao 用于读poller线程的查询之间读或写延时时间(秒为单位)。如果小于等于0,poller线程不周期地运行,只在其被一个epicsEvent信号唤醒时它才运行,这发生在驱动程序有一个用MODBUS_READ字符串的asynInt32写时。

asynInt32Array

  1.   field(DTYP, "asynInt32ArrayIn")
  2.   field(INP, "@asyn(portName, offset, timeout)drvUser")
  3.    
  4.   或
  5.   field(DTYP, "asynInt32ArrayOut")
  6.   field(INP, "@asyn(portName, offset, timeout)drvUser")

asynInt32Array设备支持用于读或写最多2000个线圈值或者对多125个16位寄存器的数组。当启用直方图时,它也用于读取I/O次数的直方数组。

asynInt32Array设备支持

modbus功能 偏移类型 数据类型 drvUser 支持的记录 描述
1,2 位的数组 MODBUS_DATA waveform(input) value=(epicsInt32)Modbus data[]
3,4,23 16位字 16,32,64位字的数组 MODBUS_DATA(或数据类型专用值) waveform(input) value=(epicsInt32)Modbus data[]
15 bit bits数组 MODBUS_DATA waveform(output) modbus写(epicsUInt16)value[]
16,23 16位字 16,32,64位字的数组 MODBUS_DATA(或数据类型专用值) waveform(output) modbus写value[]
any 32位字 NA READ_HISTOGRAM waveform(input) 返回从上次启用直方图以来一个I/O次数的直方数组(毫秒为单位)
any 32位字 NA HISTOGRAM_TIME_AXIS waveform(input) 返回直方图数据的时间轴。每个元素是HISTOGRAM_BIN_TIME毫秒

asynOctet

用以下选择asynOctet设备支持

  1.   field(DTYP, "asynOctetRead")
  2.   field(INP, "@asyn(portName, offset, timeout)drvUser")
  3.   或
  4.   field(DTYP, "asynOceteWrite")
  5.   field(INP,"@asyn(portName, offset, timeout)drvUser")

asynOctet设备支持用于读或写最多250个字符的字符串。

注意:在waveform记录或stringout记录中串末尾的0终止字节不被写到Modbus设备。

注意:从Modbus设备读取的输入字符数目是以下两种中的较小者:

1) 在记录中字符数目减去终结的0字节(对于stringin为39,对于waveform是NELM-1)或

2) 在寄存器中包含的字符数目定义了传递给drvModbusAsynConfigure的modbusLength参数(modbusLength或modbusLength*2取决于drvUser字段指定每个寄存器1个或2个字符)。

如果从Modbus读取的任意字符是0字节,这个字符串将被截短,但不保证Modbus寄存器中在字符串中最后一个字符后跟一个0字节。

asynOctet设备支持

modbus功能 偏移类型 数据类型 drvUser 支持的记录 描述
3,4,23 16位字 字符串

STRING_HIGH,STRING_LOW

STRING_HIGH_LOW

STRING_LOW_HIGH

waveform(输入)

stringin

value=modbus data[]
16,23 16位字 字符串

STRING_HIGH,STRING_LOW

STRING_HIGH_LOW

STRING_LOW_HIGH

waveform(输出)

stringout

modbus写value[]

模板文件

modbus在modbusApp/Db目录中提供了示例模板文件。这些包括:

模板文件

modbus在modbusApp/Db目录中提供了一个示例文件。这些包含:

文件 描述 宏参数
bi_bit.template asynUInt32Digital支持具有离散输入或线圈的bi记录。Mask=1

P,R,PORT,

OFFSET, ZNAM,

ONAM, ZSV,

OSV, SCAN

bi_word.template asynUInt32Digital支持具有寄存器输入的bi记录。

P, R, PORT,

OFFSET, MASK, 

ZNAM, ONAM,

ZSV, OSV

mbbiDirect.

template

asynUInt32Digital支持具有寄存器输入的mbbiDirect记录

P,R,PORT, MASK

OFFSET, SCAN

longin.template asynUInt32Digital支持具有寄存器输入的longin记录。Mask=0xFFFF。

P,R,PORT,OFFSET,

SCAN

longinInt32.

template

asynInt32支持具有寄存器输入的longin记录

P,R,PORT,OFFSET,

SCAN,DATA_TYPE

intarray_in.template asynInt32Array支持具有离散,线圈或寄存器输入的waveform记录

P,R,PORT,OFFSET,

NELM,SCAN

bo_bit.template asynUInt32Digital支持具有线圈输出的bo记录。Mask=1

P,R,PORT,OFFSET,

ZNAM,ONAM

bo_word.template asynUInt32Digital支持具有寄存器输出的bo记录。

P,R,PORT,OFFSET,

MASK, ZNAM

ONAM

mbboDirect.template asynUInt32Digital支持具有寄存器输出的mbboDirect记录

P,R,PORT,OFFSET,

MASK

longout.template asynUInt32Digital支持具有寄存器输出的longout记录。Mask=0xFFFF。 P,R,PORT,OFFSET
longoutInt32.template 对带有寄存器输出的longout记录的asynInt32支持

P,R,PORT,OFFSET,

DATA_TYPE

intarray_out.template 对带有离散,线圈或寄存器输出的waveform记录的支持

P,R,PORT,OFFSET,

NELM

ai.template 对带有LINEAR转换的ai记录的asynInt32支持

P,R,PORT,OFFSET,BITS

EGUL,EGUF,PREC,

SCAN

ai_average.template 对带有LINEAR转换的ai记录的asynInt32Average支持。每查询线程读取模拟输入,并且直到记录被运行对读取就平均,这个支持获取回调。

P,R,PORT,OFFSET,

BITS,EGUL,EGUF

PREC,SCAN

ao.template asynInt32支持带有LINEAR转换的ao记录

R,R,PORT,OFFSET

BITS,EGUL,EGUF

PREC

aoFloat64.template asynFloat64支持ao记录

P,R,PORT,OFFSET

LOPR,HOPR,PREC

DATA_TYPE

stingin.template 对string记录的asynOctet支持

P,R,PORT,OFFSET

DATA_TYPE,SCAN

stringout.template 对stringout的asynOctet支持

P,R,PORT,OFFSET

DATA_TYPE,NELM

SCAN

stringWaveformIn

.template

对waveform记录的asynOctet输入支持

P,R,PORT,OFFSET,

DATA_TYPE,NELM,

SCAN

stringWavefromOut

.template

对waveform记录的asynOctet输出支持

P,R,PORT,OFFSET,

DATA_TYPE,NELM

INITIAL_READBACK

asynRecord.template 对asyn记录的支持。对控制跟踪打印,和用于调试有用

P,R,PORT,ADDR,TMOD,

IFACE

poll_delay.template 对控制poller线程的延时时间的ao记录的支持 P,R,PORT
poll_trigger.template 对触发运行poller线程的bo记录的支持 P,R,PORT
statistics.template 对控制读取端口的I/O统计数据的bo,longin和waveform记录的支持

以下表格解释了在前一个表格中使用的宏参数。

宏参数

宏         描述
P 这个记录的前缀。完整的记录名是$(P)$(R)。
R 记录名。完整的记录名是$(P)$(R)
PORT 对modbus asyn端口的端口名
OFFSET 相对于这个端口的起始地址的Modbus数据的偏移。
MASK 用于位这个记录选取数据的位掩码。
ZNAM

用于对应bi/bo记录的0值的字符串。

ONAM 用于对应bi/bo记录的1值得字符串
OSV 对应bi/bo记录的0严重性
ZSV 对应bi/bo记录的1严重性
BITS 用于模拟I/O设备的位数。>0=单极性,<0=双极性
DATA_TYPE 指定Modbus数据类型的drvUser字段。如果这个字段位空或是MODBUS_DATA,则使用在drvModbusAsynConfigure命令指定的默认数据类型。在前面表格中列出了其它可用值(UINT16, INT16SM, BCD_SIGNED等)
EGUL 对应模拟设备低限的工程单位
EGUF 对应模拟设备高限的工程单位
LOPR 模拟设备的低显示限制
HOPR 模拟设备的高显示限制
PREC 用于ai/ao记录的精度数字位数
NELM 在waveform记录中的元素数目
ADDR 对应asyn记录的地址,与以上OFFSET相同。
TMOD 对应asyn记录的传输模式。
IFACE 用于asyn记录的asyn接口
SCAN 记录的扫描速率(例如:"1 second", "I/O Intr"等)

INITIAL_

READBACK

控制duistringout或string waveform输出记录是否从设备读取一个初始回读。

示例应用程序

modbus构建一个名为modbusApp的示例应用程序。可以运行这个应用程序去控制任意数目的Modbus PLCs。

在iocBoot/iocTest目录中,有用于EPICS IOCs的若干启动脚本。它们被用于在Koyo PLCs上测试大部分modbus驱动程序特性,诸如来自Automation Direct的DL系列。

1) Koyo1.cmd创建modbus端口驱动程序去读取X输入,写入Y输出,以及读写C控制寄存器。以线圈和以寄存器(V内存)都可以访问这些输入和输出集合中每个集合。装载bi/bo,mbbiDirect/mbboDirect和waveform记录使用这些驱动程序去读和写。

2) Koyo2.cmd创建一个modbus驱动程序去读取X输入,写到Y输出,并且读取C控制寄存器。仅使用线圈访问。这个示例也读取一个4通道13位双极A/D转换器。使用有符号BCD和符号和幅度二进制格式测试了这个驱动。注意:必须装载一个进行合适的A/D值转换成V内存的梯形逻辑程序。

3) st.cmd是在非vxWorks IOCs上运行的简单示例启动脚本。它只是装载Koyo1.cmd和Koyo2.cmd。使用以下像以下的命令调用它:

 ../../bin/linux-x86/modbusApp st.cmd

你也可以用以下分别装载Koyo1.cmd和Koyo2.cmd:

 ../../bin/linux-x86/modbusApp Koyo1.cmd

st.cmd.vxWorks是一个运行在vxWorks IOCs上的简单示例启动脚本。它只是装载Koyo1.cmd和Koyo2.cmd。

以下是Koyo1.cmd起始,在/dev/ttyS1上用slave地址1为串行RTU配置它。它也展示了如何配置TCP和串行ASCII连接。(但Koyo PLCs不支持ASCII)。

# Koyo1.cmd
 
dbLoadDatabase("../../dbd/modbus.dbd")
modbus_registerRecordDeviceDriver(pdbbase)
 
# 对TCP/IP使用以下命令
#drvAsynIPPortConfigure(const char *portName,   这个asyn端口的端口名,供后面使用
#                       const char *hostInfo,   这个端口的主机信息,IP地址:tcp端口号
#                       unsigned int priority,  优先级,0为默认优先级
#                       int noAutoConnect,      是否自动连接,0表示进行自动连接
#                       int noProcessEos);      是否进行字符末尾字符处理,1表示不处理
#
drvAsynIPPortConfigure("Koyo1","164.54.160.158:502",0,0,1)
#modbusInterposeConfig(const char *portName,    先前创建的asynIPPort或asynSerialPort的名称
#                      modbusLinkType linkType, 链路类型,0表示TCP/IP
#                      int timeoutMsec,         对底层asynOcete驱动程序写和读操作的超时时间
#                      int writeDelayMsec)      每次从EPICS写到设备前延时(毫秒为单位)。一般仅串行RTU需要使用。
modbusInterposeConfig("Koyo1",0,5000,0)         
 
# 对serial RTU或ASCII使用以下命令
#drvAsynSerialPortConfigure(const char *portName, 
#                           const char *ttyName,
#                           unsigned int priority, 
#                           int noAutoConnect,
#                           int noProcessEos);
#drvAsynSerialPortConfigure("Koyo1", "/dev/ttyS1", 0, 0, 0)
#asynSetOption("Koyo1",0,"baud","38400")
#asynSetOption("Koyo1",0,"parity","none")
#asynSetOption("Koyo1",0,"bits","8")
#asynSetOption("Koyo1",0,"stop","1")
 
# 对串行RTU使用以下命令
# 注意:可能需要非0写延时(末尾参数)
#modbusInterposeConfig("Koyo1",1,1000,0)
 
# 对串行ASCII使用以下命令
#asynOctetSetOutputEos("Koyo1",0,"\r\n")
#asynOctetSetInputEos("Koyo1",0,"\r\n")
# 注意:可能需要非0写延时(最后一个参数)
#modbusInterposeConfig("Koyo1",2,1000,0)
 
# drvModbusAsynConfigure(portName, tcpPortName, slaveAddress, modbusFunction, 
# modbusStartAddress, modbusLength, dataType, pollMsec, plcType)
 
# 注意:我们对起始地址和长度(起始0)使用八进制数值来保持与PLC命名法一致。
# 这是可选的,十进制数值(不以0开头)或十六进制数值也可以被使用。
# 在这些示例中,我们使用slave地址0(在"Koyo1"后面的数值)
# DL205在Modbus偏移4000(八进制)对Xn输入位访问
# 读32位(X0-X37)。功能码=2,读离散输入
# 起始地址04000,长度040,数据类型UINT16,读取的查询时间100毫秒
drvModbusAsynConfigure("K1_Xn_Bit",      "Koyo1", 0, 2,  04000, 040,    0,  100, "Koyo")
 
# DL205在Modbus偏移40400(八进制)处对Xn输入进行字访问
# 读取8个字(128位)。功能码3,读寄存器输入。
# 这个modbus驱动程序名为"K1_Xn_Word", asyn端口驱动程序"Koyo1"
# slave地址为0,功能码3,起始地址040400,长度010
drvModbusAsynConfigure("K1_Xn_Word",     "Koyo1", 0, 3, 040400, 010,    0,  100, "Koyo")
 
# DL205在Modbus偏移4000(八进制)对Yn输出进行位访问
# 读取32位(Y0-Y37)。功能码=1,读线圈。
drvModbusAsynConfigure("K1_Yn_In_Bit",   "Koyo1", 0, 1,  04000, 040,    0,  100, "Koyo")
 
# DL205在Modbus偏移4000(八进制)处对Yn输出进行位访问。
# 写32位(Y0-Y37)。功能码=5,写线圈。
drvModbusAsynConfigure("K1_Yn_Out_Bit",  "Koyo1", 0, 5,  04000, 040,    0,  1, "Koyo")
 
# DL205在Modbus偏移40500(八进制)处对Yn输出进行字访问。
# 读取8个字(128位)。功能码=3,读保持寄存器。
drvModbusAsynConfigure("K1_Yn_In_Word",  "Koyo1", 0, 3, 040500, 010,    0,  100, "Koyo")
 
# 写8个字(128位)。功能码=6,写寄存器。
drvModbusAsynConfigure("K1_Yn_Out_Word", "Koyo1", 0, 6, 040500, 010,    0,  100, "Koyo")
 
# The DL205 has bit access to the Cn bits at Modbus offset 6000 (octal)
# DL205在Modbus偏移6000(八进制)处对Cn位进行位访问。
# 访问256位(C0-C377)作为输入。读线圈。 功能码=1
drvModbusAsynConfigure("K1_Cn_In_Bit",   "Koyo1", 0, 1,  06000, 0400,   0,  100, "Koyo")
 
# 方位相同的256位作为输出。功能码=5,写线圈。
drvModbusAsynConfigure("K1_Cn_Out_Bit",  "Koyo1", 0, 5,  06000, 0400,   0,  1,  "Koyo")
 
 
# 访问相同的256位(C0-C377)作为数组输出。功能码=15,写多个线圈。
drvModbusAsynConfigure("K1_Cn_Out_Bit_Array",  "Koyo1", 0, 15,  06000, 0400,   0,   1, "Koyo")
 
# DL205在Modbus偏移40600(八进制)处对Cn进行字访问。
# 我们使用前16个字(C0-C377)作为输入(256位)。功能码=3,读保持寄存器。
drvModbusAsynConfigure("K1_Cn_In_Word",  "Koyo1", 0, 3, 040600, 020,    0,  100, "Koyo")
 
# 我们访问相同的16个字(C0-C377)作为输出(256位)。功能码=6,写寄存器。
drvModbusAsynConfigure("K1_Cn_Out_Word", "Koyo1", 0, 6, 040600, 020,    0,  1,  "Koyo")
 
# 我们访问相同的16个字(C0-C377)作为数组输出(256位)。功能码=16,写多个寄存器。
drvModbusAsynConfigure("K1_Cn_Out_Word_Array", "Koyo1", 0, 16, 040600, 020,    0,   1, "Koyo")
 
# 在八进制服务器上启用ASYN_TRACEIO_HEX
asynSetTraceIOMask("Koyo1",0,4)
# Enable ASYN_TRACE_ERROR and ASYN_TRACEIO_DRIVER on octet server
#asynSetTraceMask("Koyo1",0,9)
 
# Enable ASYN_TRACEIO_HEX on modbus server
asynSetTraceIOMask("K1_Yn_In_Bit",0,4)
# Enable all debugging on modbus server
#asynSetTraceMask("K1_Yn_In_Bit",0,255)
# Dump up to 512 bytes in asynTrace
asynSetTraceIOTruncateSize("K1_Yn_In_Bit",0,512)
 
dbLoadTemplate("Koyo1.substitutions")
 
iocInit
注意:设计这个示例用于测试和演示目的,不是通常如何使用modbus的真实示例。例如,它装载了6个分别使用功能码1(读线圈),3(读保持寄存器),5(写单个线圈),6(写单个保持寄存器),15(写多个线圈和16(写多个保持寄存器)的驱动程序访问C控制继电器。这允许测试所有功能码和记录类型,包括waveforms。实际中,你通常最多字装载2个驱动程序用于C控制继电器,例如功能码1(读线圈),和功能码5(写单个线圈)。

bi_bit.template

  1.   # 用于寄存器输入的bi记录
  2.   record(bi,"$(P)$(R)") {
  3.   field(DTYP,"asynUInt32Digital")
  4.   field(INP,"@asynMask($(PORT) $(OFFSET) 0x1)")
  5.   field(SCAN,"$(SCAN)")
  6.   field(ZNAM,"$(ZNAM)")
  7.   field(ONAM,"$(ONAM)")
  8.   field(ZSV,"$(ZSV)")
  9.   field(OSV,"$(OSV)")
  10.   }

 mbbiDirect.template

  1.   # 用于寄存器输入的mbbiDirect记录模板
  2.   record(mbbiDirect,"$(P)$(R)") {
  3.   field(DTYP,"asynUInt32Digital")
  4.   field(INP,"@asynMask($(PORT) $(OFFSET) $(MASK))")
  5.   field(SCAN,"$(SCAN)")
  6.   }

intarray_in.template

 

  1. record(waveform,"$(P)$(R)") {
  2.   field(DTYP,"asynInt32ArrayIn")
  3.   field(INP,"@asyn($(PORT) $(OFFSET=0))MODBUS_DATA")
  4.   field(SCAN,"$(SCAN)")
  5.   field(FTVL,"LONG")
  6.   field(NELM,"$(NELM)")
  7.   }

bo_bit.template

  1.   # 用于寄存器输出的bo记录
  2.   record(bo,"$(P)$(R)") {
  3.   field(DTYP,"asynUInt32Digital")
  4.   field(OUT,"@asynMask($(PORT) $(OFFSET) 0x1)")
  5.   field(ZNAM,"$(ZNAM)")
  6.   field(ONAM,"$(ONAM)")
  7.   }

 

 

在iocTest目录中有另一个示例应用程序,sim*.cmd和sim*.substitutions。这些示例用于测试不同的Modbus数据类型和其它特性。用Modbus Slave程序(modbus slave模拟器)测试了它们。

sim1.cmd

< envPaths

 

# simulator.cmd

 

dbLoadDatabase("../../dbd/modbus.dbd")

modbus_registerRecordDeviceDriver(pdbbase)

 

# 对TCP/IP使用以下命令

#drvAsynIPPortConfigure(const char *portName,       asyn端口驱动名

#                       const char *hostInfo,       主机信息:IP地址:TCP端口号

#                       unsigned int priority,      优先级,0是默认优先级

#                       int noAutoConnect,          是否自动连接,0自动连接

#                       int noProcessEos);          是否处理字符串末尾,1不处理字符串

drvAsynIPPortConfigure("sim1","camaro:502",0,0,1)

#asynSetOption("sim1",0, "disconnectOnReadTimeout", "Y")

#modbusInterposeConfig(const char *portName,        前面创建的asyn端口驱动程序的名称

#                      modbusLinkType linkType,     链接类型,0是TCP/IP

#                      int timeoutMsec,             对底层asynOcete驱动程序写和读操作的超时时间

#                      int writeDelayMsec)          每次从EPICS写到Modbus前的延时。

modbusInterposeConfig("sim1",0,2000,0)

 

# 对串行RTU或ASCII使用以下命令

#drvAsynSerialPortConfigure(const char *portName,

#                           const char *ttyName,

#                           unsigned int priority,

#                           int noAutoConnect,

#                           int noProcessEos);

#drvAsynSerialPortConfigure("sim1", "/dev/ttyS1", 0, 0, 0)

#asynSetOption("sim1",0,"baud","38400")

#asynSetOption("sim1",0,"parity","none")

#asynSetOption("sim1",0,"bits","8")

#asynSetOption("sim1",0,"stop","1")

 

# 对串行RTU使用以下命令 

# 注意:可能需要非0写延时(最后一个参数)

# 2000:对底层asynOcete驱动程序写和读操作的超时时间

#modbusInterposeConfig("sim1",1,2000,0)

 

# 对串行ASCII使用以下命令

#asynOctetSetOutputEos("sim1",0,"\r\n")

#asynOctetSetInputEos("sim1",0,"\r\n")

# 注意:可能需要非0写延时(最后一个参数)

# 2000:对底层asynOcete驱动程序写和读操作的超时时间

#modbusInterposeConfig("sim1",2,2000,0)

 

# 位访问Modbus地址0

# 访问128位

# 功能码=1,读取线圈

# 数据类型,默认MODBUS_DATA

# 100:毫秒,两次读取线圈之间的延时

# drvModbusAsynConfigure("portName", "tcpPortName", slaveAddress, modbusFunction, modbusStartAddress, modbusLength, dataType, pollMsec, "plcType")

drvModbusAsynConfigure("A0_In_Bits", "sim1", 0, 1,  0, 128, 0, 100, "Simulator")

 

# 位访问Modbus地址0

# 访问128比特作为输出。

# 功能码=5,写线圈。

# drvModbusAsynConfigure("portName", "tcpPortName", slaveAddress, modbusFunction, modbusStartAddress, modbusLength, dataType, pollMsec, "plcType")

drvModbusAsynConfigure("A0_Out_Bits", "sim1", 0, 5,  0, 128, 0, 100, "Simulator")

 

# Access 60 words as outputs.  访问60个字作为输出。

# 可以使用功能码6(单寄存器)或16(多寄存器),但16更好,因为当写大于16位的值时,它是原子的。

# 默认数据类型无符号整数。

# drvModbusAsynConfigure("portName", "tcpPortName", slaveAddress, modbusFunction, modbusStartAddress, modbusLength, dataType, pollMsec, "plcType")

drvModbusAsynConfigure("A0_Out_Word", "sim1", 0, 16, 100, 60, 0, 1, "Simulator")

 

# 0字访问Modbus地址100

# 访问60个字作为输入。

# 功能码=3

# 默认数据类型无符号。

# drvModbusAsynConfigure("portName", "tcpPortName", slaveAddress, modbusFunction, modbusStartAddress, modbusLength, dataType, pollMsec, "plcType")

drvModbusAsynConfigure("A0_In_Word", "sim1", 0, 3, 100, 60, 0, 100, "Simulator")

 

# Enable ASYN_TRACEIO_HEX on octet server

asynSetTraceIOMask("sim1",0,4)

# Enable ASYN_TRACE_ERROR and ASYN_TRACEIO_DRIVER on octet server

#asynSetTraceMask("sim1",0,9)

 

dbLoadTemplate("sim1.substitutions")

 

iocInit

sim1.substitutions

# 用于底层asyn octet端口的asyn记录

file "$(ASYN)/db/asynRecord.db" { pattern

{P,           R,       PORT,      ADDR,   IMAX,    OMAX}

{SIM1:    OctetAsyn,   sim1,        0,      80,      80}

}

 

# 这些是用位访问完成的A0输入

# drvModbusAsynConfigure("A0_In_Bits", "sim1", 0, 1,  0, 128, 0, 100, "Simulator")

# A0_In_Bits是modbus驱动程序的名称

# sim1是asyn端口驱动程序

# slave地址为0, 功能码=1,读线圈,从地址偏移0开始读取128位,数据类型默认MODBUS_DATA

file "../../db/bi_bit.template" { pattern

{P,        R,          PORT,         OFFSET,   ZNAM,   ONAM,  ZSV,       OSV,    SCAN}

{SIM1:,    BI0B,     A0_In_Bits,     0,        Low,    High,  NO_ALARM,  MAJOR,  "I/O Intr"}

{SIM1:,    BI1B,     A0_In_Bits,     1,        Low,    High,  NO_ALARM,  MAJOR,  "I/O Intr"}

{SIM1:,    BI2B,     A0_In_Bits,     2,        Low,    High,  NO_ALARM,  MAJOR,  "I/O Intr"}

{SIM1:,    BI3B,     A0_In_Bits,     3,        Low,    High,  NO_ALARM,  MAJOR,  "I/O Intr"}

{SIM1:,    BI4B,     A0_In_Bits,     4,        Low,    High,  NO_ALARM,  MAJOR,  "I/O Intr"}

{SIM1:,    BI5B,     A0_In_Bits,     5,        Low,    High,  NO_ALARM,  MAJOR,  "I/O Intr"}

{SIM1:,    BI6B,     A0_In_Bits,     6,        Low,    High,  NO_ALARM,  MAJOR,  "I/O Intr"}

{SIM1:,    BI7B,     A0_In_Bits,     7,        Low,    High,  NO_ALARM,  MAJOR,  "I/O Intr"}

}

# A0_In_Bits端口读出了128位,取了末尾16位

file "../../db/mbbiDirect.template" { pattern

{P,           R,       PORT,          OFFSET,  MASK,   SCAN}

{SIM1:,    MBBID0,   A0_In_Bits,      0,       0xFFFF, "I/O Intr"}

}

file "../../db/intarray_in.template" { pattern

{P,           R,        PORT,           NELM,   SCAN}

{SIM1:,    BIArray,    A0_In_Bits,      32,     "I/O Intr"}

}

file "../../db/asynRecord.template" { pattern

{P,           R,       PORT,         ADDR,     TMOD,  IFACE}

{SIM1:,    BIAsyn,   A0_In_Bits,     0,        Read,  asynUInt32Digital}

}

file "../../db/statistics.template" { pattern

{P,        R,      PORT,        SCAN}

{SIM1:,    BI,     A0_In_Bits,  "10 second"}

file "../../db/poll_delay.template" { pattern

{P,           R,            PORT}

{SIM1:,    BIPollDelay, A0_In_Bits}

}

 

# These are the A0 outputs done with bit access.

file "../../db/bo_bit.template" { pattern

{P,        R,             PORT,       OFFSET,   ZNAM,   ONAM}

{SIM1:,    BO0B,     A0_Out_Bits,     0,        Low,    High}

{SIM1:,    BO1B,     A0_Out_Bits,     1,        Low,    High}

{SIM1:,    BO2B,     A0_Out_Bits,     2,        Low,    High}

{SIM1:,    BO3B,     A0_Out_Bits,     3,        Low,    High}

{SIM1:,    BO4B,     A0_Out_Bits,     4,        Low,    High}

{SIM1:,    BO5B,     A0_Out_Bits,     5,        Low,    High}

{SIM1:,    BO6B,     A0_Out_Bits,     6,        Low,    High}

{SIM1:,    BO7B,     A0_Out_Bits,     7,        Low,    High}

}

file "../../db/mbboDirect.template" { pattern

{P,           R,         PORT,         OFFSET, MASK}

{SIM1:,    MBBOD0,     A0_Out_Bits,    0,      0xFFFF}

}

file "../../db/asynRecord.template" { pattern

{P,           R,       PORT,          ADDR,     TMOD,  IFACE}

{SIM1:,    BOAsyn,   A0_Out_Bits,     0,        Read,  asynUInt32Digital}

}

file "../../db/statistics.template" { pattern

{P,        R,       PORT,        SCAN}

{SIM1:,    BO,     A0_Out_Bits,  "10 second"}

file "../../db/poll_delay.template" { pattern

{P,           R,            PORT}

{SIM1:,    BOPollDelay, A0_Out_Bits}

}

 

 

 

# These are the A0 inputs done with word access

file "../../db/longinInt32.template" { pattern

{P,           R,                PORT,     OFFSET,   DATA_TYPE,      SCAN}

{SIM1:,    LI:UINT16,        A0_In_Word,     0,     UINT16,       "I/O Intr"}

{SIM1:,    LI:BCD_UNSIGNED,  A0_In_Word,     1,     BCD_UNSIGNED, "I/O Intr"}

{SIM1:,    LI:BCD_SIGNED,    A0_In_Word,     2,     BCD_SIGNED,   "I/O Intr"}

{SIM1:,    LI:INT16,         A0_In_Word,     3,     INT16,        "I/O Intr"}

{SIM1:,    LI:INT32_LE,      A0_In_Word,     4,     INT32_LE,     "I/O Intr"}

{SIM1:,    LI:INT32_BE,      A0_In_Word,     6,     INT32_BE,     "I/O Intr"}

{SIM1:,    LI:FLOAT32_LE,    A0_In_Word,     8,     FLOAT32_LE,   "I/O Intr"}

{SIM1:,    LI:FLOAT32_BE,    A0_In_Word,    10,     FLOAT32_BE,   "I/O Intr"}

{SIM1:,    LI:FLOAT64_LE,    A0_In_Word,    12,     FLOAT64_LE,   "I/O Intr"}

{SIM1:,    LI:FLOAT64_BE,    A0_In_Word,    16,     FLOAT64_BE,   "I/O Intr"}

{SIM1:,    LI:DEFAULT,       A0_In_Word,    20,     "",           "I/O Intr"}

}

 

# These are the A0 outputs done with word access.

file "../../db/longoutInt32.template" { pattern

{P,           R,               PORT,      OFFSET,   DATA_TYPE}

{SIM1:,    LO:UINT16,        A0_Out_Word,    0,     UINT16}

{SIM1:,    LO:BCD_UNSIGNED,  A0_Out_Word,    1,     BCD_UNSIGNED}

{SIM1:,    LO:BCD_SIGNED,    A0_Out_Word,    2,     BCD_SIGNED}

{SIM1:,    LO:INT16,         A0_Out_Word,    3,     INT16}

{SIM1:,    LO:INT32_LE,      A0_Out_Word,    4,     INT32_LE}

{SIM1:,    LO:INT32_BE,      A0_Out_Word,    6,     INT32_BE}

{SIM1:,    LO:FLOAT32_LE,    A0_Out_Word,    8,     FLOAT32_LE}

{SIM1:,    LO:FLOAT32_BE,    A0_Out_Word,   10,     FLOAT32_BE}

{SIM1:,    LO:FLOAT64_LE,    A0_Out_Word,   12,     FLOAT64_LE}

{SIM1:,    LO:FLOAT64_BE,    A0_Out_Word,   16,     FLOAT64_BE}

{SIM1:,    LO:DEFAULT,       A0_Out_Word,   20,     ""}

}

 

file "../../db/aiFloat64.template" { pattern

{P,           R,                PORT,     OFFSET,   DATA_TYPE,    LOPR, HOPR, PREC,    SCAN}

{SIM1:,    AI:UINT16,        A0_In_Word,    30,     UINT16,       -1e6,  1e6,    0,   "I/O Intr"}

{SIM1:,    AI:BCD_UNSIGNED,  A0_In_Word,    31,     BCD_UNSIGNED, -1e6,  1e6,    0,   "I/O Intr"}

{SIM1:,    AI:BCD_SIGNED,    A0_In_Word,    32,     BCD_SIGNED,   -1e6,  1e6,    0,   "I/O Intr"}

{SIM1:,    AI:INT16,         A0_In_Word,    33,     INT16,        -1e6,  1e6,    0,   "I/O Intr"}

{SIM1:,    AI:INT32_LE,      A0_In_Word,    34,     INT32_LE,     -1e6,  1e6,    0,   "I/O Intr"}

{SIM1:,    AI:INT32_BE,      A0_In_Word,    36,     INT32_BE,     -1e6,  1e6,    0,   "I/O Intr"}

{SIM1:,    AI:FLOAT32_LE,    A0_In_Word,    38,     FLOAT32_LE,   -1e6,  1e6,    3,   "I/O Intr"}

{SIM1:,    AI:FLOAT32_BE,    A0_In_Word,    40,     FLOAT32_BE,   -1e6,  1e6,    3,   "I/O Intr"}

{SIM1:,    AI:FLOAT64_LE,    A0_In_Word,    42,     FLOAT64_LE,   -1e6,  1e6,    3,   "I/O Intr"}

{SIM1:,    AI:FLOAT64_BE,    A0_In_Word,    46,     FLOAT64_BE,   -1e6,  1e6,    3,   "I/O Intr"}

{SIM1:,    AI:DEFAULT,       A0_In_Word,    50,     ""        ,   -1e6,  1e6,    3,   "I/O Intr"}

}

 

file "../../db/aoFloat64.template" { pattern

{P,           R,               PORT,      OFFSET,   DATA_TYPE,    LOPR, HOPR, PREC}

{SIM1:,    AO:UINT16,        A0_Out_Word,   30,     UINT16,       -1e6,  1e6,    0}

{SIM1:,    AO:BCD_UNSIGNED,  A0_Out_Word,   31,     BCD_UNSIGNED, -1e6,  1e6,    0}

{SIM1:,    AO:BCD_SIGNED,    A0_Out_Word,   32,     BCD_SIGNED,   -1e6,  1e6,    0}

{SIM1:,    AO:INT16,         A0_Out_Word,   33,     INT16,        -1e6,  1e6,    0}

{SIM1:,    AO:INT32_LE,      A0_Out_Word,   34,     INT32_LE,     -1e6,  1e6,    0}

{SIM1:,    AO:INT32_BE,      A0_Out_Word,   36,     INT32_BE,     -1e6,  1e6,    0}

{SIM1:,    AO:FLOAT32_LE,    A0_Out_Word,   38,     FLOAT32_LE,   -1e6,  1e6,    3}

{SIM1:,    AO:FLOAT32_BE,    A0_Out_Word,   40,     FLOAT32_BE,   -1e6,  1e6,    3}

{SIM1:,    AO:FLOAT64_LE,    A0_Out_Word,   42,     FLOAT64_LE,   -1e6,  1e6,    3}

{SIM1:,    AO:FLOAT64_BE,    A0_Out_Word,   46,     FLOAT64_BE,   -1e6,  1e6,    3}

{SIM1:,    AO:DEFAULT,       A0_Out_Word,   50,     "",           -1e6,  1e6,    3}

}

 

file "../../db/asynRecord.template" { pattern

{P,           R,         PORT,       ADDR,   TMOD,  IFACE}

{SIM1:,    A0:AsynIn,  A0_In_Word,     0,    Read, asynInt32}

}

 

file "../../db/asynRecord.template" { pattern

{P,           R,         PORT,       ADDR,   TMOD,  IFACE}

{SIM1:,    A0:AsynOut,  A0_Out_Word,     0,    Read, asynInt32}

}

 

file "../../db/statistics.template" { pattern

{P,           R,       PORT,       SCAN}

{SIM1:,    A0:,     A0_In_Word,  "10 second"}

 

file "../../db/poll_delay.template" { pattern

{P,           R,            PORT}

{SIM1:,    A0:PollDelay, A0_In_Word}

 

}


  1. # Koyo1.cmd
  2.    
  3.   dbLoadDatabase("../../dbd/modbus.dbd")
  4.   modbus_registerRecordDeviceDriver(pdbbase)
  5.    
  6.   # 对TCP/IP使用以下命令
  7.   #drvAsynIPPortConfigure(const char *portName, 这个asyn端口的端口名,供后面使用
  8.   # const char *hostInfo, 这个端口的主机信息,IP地址:tcp端口号
  9.   # unsigned int priority, 优先级,0为默认优先级
  10.   # int noAutoConnect, 是否自动连接,0表示进行自动连接
  11.   # int noProcessEos); 是否进行字符末尾字符处理,1表示不处理
  12.   #
  13.   drvAsynIPPortConfigure("Koyo1","164.54.160.158:502",0,0,1)
  14.   #modbusInterposeConfig(const char *portName, 先前创建的asynIPPort或asynSerialPort的名称
  15.   # modbusLinkType linkType, 链路类型,0表示TCP/IP
  16.   # int timeoutMsec, 对底层asynOcete驱动程序写和读操作的超时时间
  17.   # int writeDelayMsec) 每次从EPICS写到设备前延时(毫秒为单位)。一般仅串行RTU需要使用。
  18.   modbusInterposeConfig("Koyo1",0,5000,0)
  19.    
  20.   # 对serial RTU或ASCII使用以下命令
  21.   #drvAsynSerialPortConfigure(const char *portName,
  22.   # const char *ttyName,
  23.   # unsigned int priority,
  24.   # int noAutoConnect,
  25.   # int noProcessEos);
  26.   #drvAsynSerialPortConfigure("Koyo1", "/dev/ttyS1", 0, 0, 0)
  27.   #asynSetOption("Koyo1",0,"baud","38400")
  28.   #asynSetOption("Koyo1",0,"parity","none")
  29.   #asynSetOption("Koyo1",0,"bits","8")
  30.   #asynSetOption("Koyo1",0,"stop","1")
  31.    
  32.   # 对串行RTU使用以下命令
  33.   # 注意:可能需要非0写延时(末尾参数)
  34.   #modbusInterposeConfig("Koyo1",1,1000,0)
  35.    
  36.   # 对串行ASCII使用以下命令
  37.   #asynOctetSetOutputEos("Koyo1",0,"\r\n")
  38.   #asynOctetSetInputEos("Koyo1",0,"\r\n")
  39.   # 注意:可能需要非0写延时(最后一个参数)
  40.   #modbusInterposeConfig("Koyo1",2,1000,0)
  41.    
  42.   # drvModbusAsynConfigure(portName, tcpPortName, slaveAddress, modbusFunction,
  43.   # modbusStartAddress, modbusLength, dataType, pollMsec, plcType)
  44.    
  45.   # 注意:我们对起始地址和长度(起始0)使用八进制数值来保持与PLC命名法一致。
  46.   # 这是可选的,十进制数值(不以0开头)或十六进制数值也可以被使用。
  47.   # 在这些示例中,我们使用slave地址0(在"Koyo1"后面的数值)
  48.   # DL205在Modbus偏移4000(八进制)对Xn输入位访问
  49.   # 读32位(X0-X37)。功能码=2,读离散输入
  50.   # 起始地址04000,长度040,数据类型UINT16,读取的查询时间100毫秒
  51.   drvModbusAsynConfigure("K1_Xn_Bit", "Koyo1", 0, 2, 04000, 040, 0, 100, "Koyo")
  52.    
  53.   # DL205在Modbus偏移40400(八进制)处对Xn输入进行字访问
  54.   # 读取8个字(128位)。功能码3,读寄存器输入。
  55.   # 这个modbus驱动程序名为"K1_Xn_Word", asyn端口驱动程序"Koyo1"
  56.   # slave地址为0,功能码3,起始地址040400,长度010
  57.   drvModbusAsynConfigure("K1_Xn_Word", "Koyo1", 0, 3, 040400, 010, 0, 100, "Koyo")
  58.    
  59.   # DL205在Modbus偏移4000(八进制)对Yn输出进行位访问
  60.   # 读取32位(Y0-Y37)。功能码=1,读线圈。
  61.   drvModbusAsynConfigure("K1_Yn_In_Bit", "Koyo1", 0, 1, 04000, 040, 0, 100, "Koyo")
  62.    
  63.   # DL205在Modbus偏移4000(八进制)处对Yn输出进行位访问。
  64.   # 写32位(Y0-Y37)。功能码=5,写线圈。
  65.   drvModbusAsynConfigure("K1_Yn_Out_Bit", "Koyo1", 0, 5, 04000, 040, 0, 1, "Koyo")
  66.    
  67.   # DL205在Modbus偏移40500(八进制)处对Yn输出进行字访问。
  68.   # 读取8个字(128位)。功能码=3,读保持寄存器。
  69.   drvModbusAsynConfigure("K1_Yn_In_Word", "Koyo1", 0, 3, 040500, 010, 0, 100, "Koyo")
  70.    
  71.   # 写8个字(128位)。功能码=6,写寄存器。
  72.   drvModbusAsynConfigure("K1_Yn_Out_Word", "Koyo1", 0, 6, 040500, 010, 0, 100, "Koyo")
  73.    
  74.   # The DL205 has bit access to the Cn bits at Modbus offset 6000 (octal)
  75.   # DL205在Modbus偏移6000(八进制)处对Cn位进行位访问。
  76.   # 访问256位(C0-C377)作为输入。读线圈。 功能码=1
  77.   drvModbusAsynConfigure("K1_Cn_In_Bit", "Koyo1", 0, 1, 06000, 0400, 0, 100, "Koyo")
  78.    
  79.   # 方位相同的256位作为输出。功能码=5,写线圈。
  80.   drvModbusAsynConfigure("K1_Cn_Out_Bit", "Koyo1", 0, 5, 06000, 0400, 0, 1, "Koyo")
  81.    
  82.    
  83.   # 访问相同的256位(C0-C377)作为数组输出。功能码=15,写多个线圈。
  84.   drvModbusAsynConfigure("K1_Cn_Out_Bit_Array", "Koyo1", 0, 15, 06000, 0400, 0, 1, "Koyo")
  85.    
  86.   # DL205在Modbus偏移40600(八进制)处对Cn进行字访问。
  87.   # 我们使用前16个字(C0-C377)作为输入(256位)。功能码=3,读保持寄存器。
  88.   drvModbusAsynConfigure("K1_Cn_In_Word", "Koyo1", 0, 3, 040600, 020, 0, 100, "Koyo")
  89.    
  90.   # 我们访问相同的16个字(C0-C377)作为输出(256位)。功能码=6,写寄存器。
  91.   drvModbusAsynConfigure("K1_Cn_Out_Word", "Koyo1", 0, 6, 040600, 020, 0, 1, "Koyo")
  92.    
  93.   # 我们访问相同的16个字(C0-C377)作为数组输出(256位)。功能码=16,写多个寄存器。
  94.   drvModbusAsynConfigure("K1_Cn_Out_Word_Array", "Koyo1", 0, 16, 040600, 020, 0, 1, "Koyo")
  95.    
  96.   # 在八进制服务器上启用ASYN_TRACEIO_HEX
  97.   asynSetTraceIOMask("Koyo1",0,4)
  98.   # Enable ASYN_TRACE_ERROR and ASYN_TRACEIO_DRIVER on octet server
  99.   #asynSetTraceMask("Koyo1",0,9)
  100.    
  101.   # Enable ASYN_TRACEIO_HEX on modbus server
  102.   asynSetTraceIOMask("K1_Yn_In_Bit",0,4)
  103.   # Enable all debugging on modbus server
  104.   #asynSetTraceMask("K1_Yn_In_Bit",0,255)
  105.   # Dump up to 512 bytes in asynTrace
  106.   asynSetTraceIOTruncateSize("K1_Yn_In_Bit",0,512)
  107.    
  108.   dbLoadTemplate("Koyo1.substitutions")
  109.    
  110.  

标签:Modbus,驱动程序,16,EPICS,modbus,A0,Koyo1,synApps
From: https://www.cnblogs.com/helloword-2022/p/18051294

相关文章

  • modbusTCP协议和TCP协议
    TCP最主要的特点TCP是面向连接的运输层协议。应用程序在使用TCP协议之前,必须先建立TCP连接。在传送数据完毕后,必须释放已经建立的TCP连接每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点的(一对一)TCP提供可靠交付的服务。通过TCP连接传送的数据,无差错......
  • Modbus字节序说明-汇川PLC用littly endine byte swap【低位优先传输且反序】 解析寄存
    Modbus字节序说明-汇川PLC用littlyendinebyteswap解析寄存器最近做ModBusTCP方面的测试有点多,尽管对于ModBus协议算是比较了解了,也经常知道字节传输序列的不同对工程师带来了很多不必要的麻烦,这不是一个技术难题,仅仅只是过去各家各户开发遗留下来的标准统一问题,所以这里写下......
  • Modbus Slave 和 Modbus Poll 使用说明
    1软件说明ModbusPoll:是WitteSoftware公司开发的的Modbus主机仿真器,用于测试和调试Modbus从设备。软件支持ModbusRTU、ASCII、TCP/IP协议。支持多设备监控,可以同时监视多个从设备/数据域。ModbusSlave:Modbus从设备仿真器,主要用来模拟Modbus从站设备,接收主站的命令包,回送数据......
  • Modbus和Modbus/TCP协议
    一.概述Modbus是MODICON公司与1979年开发的一种通讯协议,是一种工业现场总线协议标准。1996年施耐德公司推出了基于以太网TCP/IP的Modbus协议------ModbusTCP。Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型,协议本身并没有定义物理层,只是定义了控制器......
  • Qt QModbus相关类实现ModbusTcpServer总结
    在疫情环境下催生出了很多的无人或者减少人员接触的项目,比如无人智慧餐厅项目中的无人送餐项目,主要是由送餐小车和一个中控屏和部分协助发餐的设备组成,由于餐厅一般的范围不会很大,考虑到Wi-Fi通信可能比较麻烦,我们前期组网协议使用的是zigbee,这样的话小车可以无网络运行且待......
  • Qt QModbusServer类
    1、概述QModbusServer类是用于接收和处理Modbus请求的接口。1Header:#include<QModbusServer>2qmake:QT+=serialbus3Since:Qt5.84Inherits:QModbusDevice5InheritedBy:QModbusRtuSerialSlaveandQModbusTcpServerModbus网络可以具有多个Modbus......
  • ModbusTCP从站建立
    背景根据网络安全策略要求,无法程序作为主站进行PLC的读取解决方案:PLC作为主站,程序作为从站进行被动的数据火获取,后续根据数据变化值进行建立解决方案使用NModbus4组件构建从站在同个程序中开启不同的端口区分不同的PLC信号写入从站开启监听代码IPEndPointiPEn......
  • WCH_ModBus网关模块使用说明
    目录1.Modbus网关模块介绍2.ModBus协议的特性3.模块简单调试使用3.1MODBUSCLIENT模式  3.2MODBUSSERVER模式相关文档工具下载连接1.模块优势●支持10/100M,全双工/半双工自适应以太网接口● 内置固件,无需用户编程开发,可通过上位机配置软件或者串口命令配......
  • Modbus RTU通过从站地址获取校验码的代码
    主要方法拆分高低位计算校验码完整通过从站地址获取校验码的代码usingSystem;classProgram{staticvoidMain(){Console.Write("请输入从站地址(十六进制):");stringslaveAddressInput=Console.ReadLine();bytesl......
  • Modbus Slave 软件使用详解
    软件介绍         ModbusSlave是一个模拟Modbus协议从机的上位机软件,主要用于模拟测试跟其他主机设备通信的过程。与之成套存在的另一个软件--ModbusPoll,则是模拟Modbus协议主机的上位机软件。该软件内部封装标准Modbus协议栈,通过图形化界面使得操作更为简便。目前软......