最近学习了Modbus协议,通过UART驱动来实现控制板与上位机之间的通讯。这里把Modbus里须注意的点总结一下。
前面六点主要是对Modbus协议知识点的总结与归纳,后面记录了在开发过程中遇到的一些问题及相应的解决办法,如RTU模式下1.5/3.5字符超时时间如何处理等。

1 Modbus协议

1.1 协议概要

Modbus协议是一种单主/多从的协议,说白了,同一时间,总线上只能有一个主机,从机可以有多个(最多247个)。Modbus通讯的建立只能由主机发起,从机没收到来自主机的请求时,不会主动发送数据。从机之间不能通信,主机只能同时启动一个Modbus访问请求。见下图。

主机可以通过广播/单播模式发送Modbus请求报文。

单播模式 广播模式
主机寻址单个从机 主机寻址多个从机
报文可读可写 报文只能写
从机地址任意(1至247) 从机地址只能为0
从机必须应答 从机无须应答

1.2 请求

主机发送的的请求报文包括从机地址、功能码、数据及CRC校验。
功能码 告知被选中的从机要执行什么功能;
数据段 里包含了从机要执行的功能的附加信息,这些信息必须包含要告诉从机的内容:从哪个寄存器开始读/写及要读的寄存器的数量;
CRC校验 验证数据是否正确。

1.3 应答

从机响应的应答报文包括从机地址、功能码、数据及CRC校验。
功能码 肯定报文中与请求报文中功能码一致;
数据段 包含了从机反馈的信息,如寄存器的状态或值。
CRC校验验证数据是否正确。

1.4 报文格式

Modbus里定义了数据传输的格式——PDU,PDU再加上另外的功能域构成ADU,如下图所示。

PDU:功能码+数据
ADU:地址+功能码+数据+CRC校验
受硬件限制(RS485/RS232的ADU最大为256字节),PDU最大内存空间为:256 - 从机地址(1字节)- CRC校验(2字节)= 253字节
所以完整的Modbus报文的构成就是:地址+功能码+数据+CRC校验,举例来讲:x向y发了一帧报文,报文内容为A+B+C+D,可以理解为x想从y的A地址里通过B命令获取C的值,D负责对A/B/C进行校验。

2 Modbus寄存器

2.1 寄存器种类

Modbus中的寄存器是一个宽泛的概念,并没有指MCU中某一特定的寄存器,可能是某一块内存区域。寄存器地址由开发者自己决定(实际开发过程中会有负责需求的同事整理好寄存器地址给到开发者,无须自己担心)。寄存器分类如下表。

寄存器种类 说明 与PLC类比 举例
线圈状态 输出端口,可读可写 DO(数字量输出) 电磁阀输出、MOSFET输出、LED显示等
离散输入状态 输入端口,可读不可写 DI(数字量输入) 拨码开关、接近开关等
保持寄存器 输出参数或保持参数,控制器运行时被设定的参数,可读可写 AO(模拟量输出) 模拟量输出设定值,PID运行参数,传感器报警值上、下限
输入寄存器 输入参数,可读不可写 AI(模拟量输入) 模拟量输入

2.2 寄存器地址

Modbus寄存器地址分配如下表所示,仍然参照了PLC寄存器地址的分配方式。

寄存器种类 寄存器PLC地址 寄存器Modbus协议地址 简称 读写状态
线圈状态 00001~09999 0000H~FFFFH 0x 可读可写
离散输入状态 10001~19999 0000H~FFFFH 1x 可读
保持寄存器 40001~49999 0000H~FFFFH 2x 可读可写
输入寄存器 30001~39999 0000H~FFFFH 3x 可读

该表中的PLC地址可以理解为Modbus地址的变种,在触摸屏和PLC编程中应用较广泛。寄存器协议地址指的是通信时使用的寄存器寻址地址,例如PLC地址40001对应寻址地址0x0000,40002对应寻址地址0x0001。寄存器协议地址一般用16进制表示。另外,虽然PLC寄存器地址00001和10001对应的寄存器协议地址都是0000H,但是因为不同寄存器的功能码不同,需要使用不同的功能码进行访问,所以实际使用起来不会发生冲突。

3 Modbus串行消息帧格式

3.1 ASCII消息格式

当控制器以ASCII格式通信时,消息中每个8位的字节都将以两个ASCII字符发送。这种方式的优点是字符发送的时间间隔可达到1s而不产生错误。
ASCII模式下,报文以(:)字符(ASCII码:0x3A)开始,以回车换行符结束(ASCII码:0x0A,0x0D)。报文里其他段的内容以十六进制的字符来表征。那么从机只要不断检测总线上是否冒号发过来且确认地址是否为发给自己的。报文中字符发送时间间隔不能超过1s,否则从机将会认为这帧报文无效。

3.2 RTU消息格式

主从机在收发报文的时候,接收对象需要知道在报文的起始处开始接收,同时也要知道报文何时结束。另外当接收到不完整的报文时,能够清晰的设置错误标志。
RTU消息格式中,报文的接收和发送以至少3.5个字符时间的停顿间隔为标志。实际使用中,从机不断侦测总线,计算字符间的停顿时间,判断报文的起始点。当接收到报文时,从机开始解析该报文是否发给自己的。在最后一个字符传输结束后,一个至少3.5字符的停顿标志了该帧报文的结束。另外,报文的发送必须保证连续性,在发送过程中,若两个字符之间停顿的时长超过1.5个字符时间,则从机认为该帧无效,将丢弃该帧。
如下图所示,Modbus通信时规定主机发送完一组报文必须间隔3.5个字符再发一组报文,这3.5个字符主要用于告诉从机该帧报文已经发送结束。

3.5个字符时间间隔采用如下计算方式:
串行通信中,1个字符包含1个起始位、8个数据位、1个校验位(如有)及1个停止位。那么一个字符就包含了11个位,3.5个字符就有3.5×11=38.5个位。
按照9600bps的波特率来算,38.5×(1000/9600)= 4.01041ms,也就是说上图中两个Modbus报文帧之间的间隔只要超过4.01041ms即可。
每个消息帧的格式如下图所示。

3.3 地址域

地址域,指的是Modbus通讯帧中的地址字段,内容为从设备地址。Modbus消息帧中的地址域包含两个两个字符(ASCII模式)或一个字节(RTU模式)。
报文中可能的从设备地址为0至247,其中,单个设备的实际地址是1~247,0用作广播地址。

3.4 功能码域

功能码域,用于表示当前报文的功能。由一个字节构成,取值范围由1~255。
常用的功能码有01、03、04、06、16等,其中03的作用为读保持寄存器的值,04的作用为读输入寄存器的值,06的作用为写单个保持寄存器,16的作用为写多个保持寄存器。
从设备根据执行情况,若执行成功,返回的功能码与接收的相同;若执行失败,返回的否定应答中功能码中最高位(MSB)须置1。
此外,对于主机发出的功能码,从机根据自身配置决定是否支持此功能码,若不支持,反馈异常响应。

3.5 数据域

数据域与功能码紧密相连,作为功能码域的补充,存放功能码域需要操作的具体细节。

4 差错校验

在Modbus通信中,根据传输模式不同(ASCII或者RTU),差错校验采用不同的方法。
ASCII模式
该模式下,报文包括一个错误校验字段。该字段由两个字符构成,具体的值基于对全部报文内容执行的纵向冗余校验(LRC)计算的结果而来,计算对象不包含起始的冒号和最后的回车换行符。
RTU模式
该模式下,报文同样包含一个错误校验字段。该字段由两个字节构成,具体的值基于对全部报文内容执行的循环冗余校验(CRC)计算的结果而来,计算对象包含差错校验域之前所有的报文。

4.1 LRC校验

4.2 CRC校验

在Modbus RTU通讯模式下,通讯报文包括了一个基于循环冗余校验方法的差错校验字段。
CRC全称是循环冗余校验,特点是检错能力极强,开销小,易于用编码器及检测电路实现。CRC校验包含了多个版本,常用的CRC校验有CRC-8、CRC-16、CRC-32、CRC-64 。
Modbus协议中,采用的是CRC-16的标准校验方法。在RTU模式下,CRC自身由两个字节构成,即CRC是一个16位的值。CRC字段校验针对整个报文的内容,无论报文中的单个字节采用何种奇偶校验方式,整个报文均采用CRC-16校验算法。
从机在接收报文时,会通过CRC算法重新计算接收到的报文的CRC值,并把计算得到的值与CRC字段中接收的实际值进行比较。若两者不同,则产生一个错误,并返回一个异常响应报文告知主机。
需要注意的是,CRC-16校验值由两个字节构成,所以涉及到哪个字节在前、哪个字节在后的问题,即大小端的问题。

5 大小端

大端存储模式:数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
小端存储模式:数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中;
我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

6 功能码

6.1(0x01)01读取线圈输出值

6.2(0x02)02读取离散输入值

6.3(0x03)03读取保持寄存器值

6.3.1 功能说明

该功能码用于读取保持寄存器的值,不支持广播模式。报文中指定了读取的保持寄存器的起始地址和数量

6.3.2 查询报文

主机发送的查询报文中,必须指定查询寄存器的数量和起始地址。如:需要从设备地址为07的从机中读取寄存器地址从0x00C8开始的3个寄存器中的值。见下表:

字段 例(RTU模式)
地址域 0x07
功能码域 0x03
数据域(寄存器地址高位) 0x00
数据域(寄存器地址低位) 0xC8
数据域(寄存器数量高位) 0x00
数据域(寄存器数量低位) 0x03
差错校验 CRC

此例中,寄存器地址由2个字节构成,取值范围为0x0000-0xFFFF;寄存器数量由2个字节构成,取值范围为0x0001-0x007D,即最多可以连续读125个寄存器的值。

6.3.3 响应报文

响应报文的构成和意义见下表。由于Modbus中保持寄存器和输入寄存器的值都是两个byte,因此上述例子中读取3个寄存器的值将返回6个byte。数据域里的值分别是6个字节数及3个寄存器里的数值。

字段 例(RTU模式)
地址域 0x07
功能码域 0x03
数据域(字节数) 0x06
数据域(数据1高位) 0x03
数据域(数据1低位) 0x53
数据域(数据2高位) 0x01
数据域(数据2低位) 0xF3
数据域(数据3高位) 0x01
数据域(数据3低位) 0x05
差错校验 CRC

6.4(0x04)04读取输入寄存器

6.5(0x05)05写单个线圈/离散输出状态

6.5.1 功能说明

此功能码用于将单个线圈寄存器设置为ON或OFF。此功能支持广播模式,在广播模式下,总线上所有从机的同一寄存器地址的值会被统一修改。查询报文中的ON/OFF状态由报文数据字段的常数指定,0xFF00表示ON状态,0x00FF表示OFF状态,其他值均为非法值。

6.5.2 查询报文

查询报文需要指定从设备地址以及需要变更的线圈地址和设定的状态值。需要注意,查询报文中,线圈地址从0开始计数。如,要设置线圈地址为00150为ON状态,则查询报文中线圈地址设置为0x95(00149),见下表。

字段 例(RTU模式)
地址域 0x03
功能码域 0x05
数据域(寄存器地址高位) 0x00
数据域(寄存器地址低位) 0x95
数据域(寄存器数量高位) 0xFF
数据域(寄存器数量低位) 0x00
差错校验 CRC

6.5.3 响应报文

响应报文的各项构成和意义见下表。对于从机而言,在线圈或离散输出寄存器正常变更的情况下,则返回与查询报文一样的响应报文。若修改失败,则反馈异常响应。

字段 例(RTU模式)
地址域 0x03
功能码域 0x05
数据域(寄存器地址高位) 0x00
数据域(寄存器地址低位) 0x95
数据域(寄存器数量高位) 0xFF
数据域(寄存器数量低位) 0x00
差错校验 CRC

6.6(0x06)06写单个保持寄存器

6.6.1 功能说明

此功能码用于修改单个保持寄存器的值,支持广播模式。在广播模式下,所有从机相同寄存器地址的寄存器的值都会被统一修改。

6.6.2 查询报文

此类型报文需要指定要修改的寄存器地址及设定的值。需要注意,查询报文中,寄存器地址从0开始计数。如,要设置寄存器地址为40150的寄存器的值为1200(0x4B0),则查询报文中地址字段设置为0x95(149),见下表。

字段 例(RTU模式)
地址域 0x04
功能码域 0x06
数据域(寄存器地址高位) 0x00
数据域(寄存器地址低位) 0x95
数据域(寄存器数值高位) 0x04
数据域(寄存器数值低位) 0xB0
差错校验 CRC

本功能码中,起始地址由2个字节构成,取值范围为0x0000-0xFFFF;变更目标数据由2个字节构成,取值范围为0x0000-0xFFFF。

6.6.3 响应报文

响应报文的各项构成和意义见下表。对于从机而言,在保持寄存器正常变更时,返回与查询报文完全相同的响应报文。若修改失败,则返回一个异常响应。

字段 例(RTU模式)
地址域 0x04
功能码域 0x06
数据域(寄存器地址高位) 0x00
数据域(寄存器地址低位) 0x95
数据域(寄存器数值高位) 0x04
数据域(寄存器数值低位) 0xB0
差错校验 CRC

6.7(0x08)08诊断功能

6.8(0x0F)15写多个线圈/离散输出状态

6.8.1 功能说明

此功能码用于将连续的多个线圈或离散输出设置为ON/OFF状态,支持广播模式,在广播模式下,所有从站设备的同一地址的值将会被统一修改。本功能码中,起始地址字段由2个字段构成,取值范围为0x0000~0xFFFF;而寄存器数量字段由2个字节构成,取值范围为0x0001至0x07B0。

6.9(0x10)16写多个保持寄存器

6.9.1 功能说明

此功能码用于修改多个连续的寄存器的值。数据域中须指定寄存器起始地址及数量、须设定的字节数及值。本功能码中,起始地址字段由2个字段构成,取值范围为0x0000~0xFFFF;而寄存器数量字段由2个字节构成,取值范围为0x0001至0x007B。

6.9.2 查询报文

查询报文中包含了请求数据字段。数据字段保存须设定的数值,各数据按每个寄存器2个字节存放。如,从机设备地址为1,需要将保持寄存器地址为40020~40022的寄存器设置0x0155、0x0156、0x0157的值。
由于值是双字节,须注意MCU的大小端布置。同时需要注意,在查询报文中,Modbus协议起始地址为19(0x13),即比寄存器起始地址20少1,见下表。

字段 例(RTU模式)
地址域 0x05
功能码域 0x10
数据域(寄存器地址高位) 0x00
数据域(寄存器地址低位) 0x13
数据域(寄存器数量高位) 0x00
数据域(寄存器数量低位) 0x03
数据域(写入数据字节数) 0x06
数据域(变更数据1高位) 0x01
数据域(变更数据1低位) 0x55
数据域(变更数据2高位) 0x01
数据域(变更数据2低位) 0x56
数据域(变更数据3高位) 0x01
数据域(变更数据3低位) 0x57
差错校验 CRC

6.9.3 响应报文

对于从机而言,在正常情况下响应报文包括功能码、起始地址及写入的寄存器数量,见下表。

字段 例(RTU模式)
地址域 0x05
功能码域 0x10
数据域(寄存器地址高位) 0x00
数据域(寄存器地址低位) 0x13
数据域(寄存器数量高位) 0x00
数据域(寄存器数量低位) 0x03
差错校验 CRC

在实际开发过程中,功能码16(0x10)写多个寄存器”常常用于方便用户写入多字节类型的数据。

6.10 Modbus异常响应

03、06、16等功能码是比较常用的功能码,在通常情况下,从机设备将返回一个正常响应报文,但是在某些特殊情况下,从机将返回异常响应报文。
对于查询报文,存在以下4种处理反馈:
i) 正常接收,正常处理,返回正常响应报文;
ii)因为通信错误等原因,造成从机没有接收到查询报文,主机将按照超时处理;
iii)从机接收到的查询报文存在通信错误(如LRC、CRC错误等),此时从机将丢弃该帧报文,主机将按照超时处理;
iv)从机接收到正确的报文,但是超过处理范围(如不存在的功能码或寄存器),此时从机将返回包含异常码的响应报文。
异常响应报文由从站地址、功能码以及异常码构成。其中,功能码与正常响应报文不同,在异常响应报文中,功能码最高位(MSB)被设置为1。因为Modbus协议中功能码占用一个字节,所以异常功能码 = 正常功能码 + 0x80。
举例说明,如现在主机想要查询起始地址为0x012C的寄存器的值。若从机中不存在地址为0x012C的寄存器,则从机将返回一个异常响应报文。见下表
查询码

字段 例(RTU模式)
地址域 0x07
功能码域 0x04
数据域(寄存器地址高位) 0x01
数据域(寄存器地址低位) 0x2C
数据域(寄存器数量高位) 0x00
数据域(寄存器数量低位) 0x03
差错校验 CRC

响应码

字段 例(RTU模式)
地址域 0x07
功能码域 0x84
数据域(异常码) 0x02
差错校验 CRC

常见的异常码见下表。

异常码 名称 说明
01 非法功能码 从机不支持此功能码
02 非法数据地址 指定的数据地址在从站设备中不存在
03 非法数据值 指定的数据超过范围或者不允许使用
04 从机设备故障 从机处理响应的过程中,出现未知错误

7 开发过程中遇到的问题

7.1 1.5字符/3.5字符超时时间如何处理(附代码)

3.2节中提到,Modbus协议中规定了连续两个Modbus报文之间的时间间隔必须超过3.5个UART报文的发送时间(T3.5),连续两个UART报文之间时间间隔不能超过1.5个UART报文的发送时间(T1.5)。
其实Modbus的开发过程中尤其是在我在做从机开发时,这个T3.5和T1.5是不需要管的。
T1.5:从主机发的报文来看,实际在一个帧内部,两个UART报文是紧密相连的,不会存在有两个报文分开的情况,上一个UART报文的停止位和下一个UART报文的起始位之间的δ时间几乎是0;
T3.5:同样看主机发的报文,两帧Modbus报文之间的时间间隔都是30ms以上,远远超过了规定的0.29362ms。
但是T3.5可以用作判断报文是否接收完整的依据,作为从机来讲,不知道UART报文什么时候接收完成,所以可以利用这个T3.5去做文章。当我用中断去进行接收UART报文时,每一次从机接收一个BTYE的UART报文,那么就启动定时器,如果接下来还有报文进来,那么就清除前面累计的定时器计数,反过来,如果超过T3.5的时间过去了,都没有UART报文进来,那么就可以认为当前UART报文已经接收完成,也就是Modbus报文接收完成,那么我就可以对这段报文进行解析了。
附上代码:

 //Code for UART receive interruptif(UART_GetRxDataAvailableIntStatus(UART))             //RX interrupt is triggered{TIMER_Stop(TIMER1);                                 //stop timerTIMER_ClearInt(TIMER1);                             //clear timer interrupt flag        if(uart_message.rxindex < MODBUS_MAX_ADU_LENGTH) //buffer is not full{//take data out of fifouart_message.ptrRx[uart_message.rxindex] = UART_ReadByte(UART);uart_message.rxindex++;uart_message.uart_rx_state = UART_RX_BUSY;        //uart receive state is busy TIMER_Run(TIMER1);}else{uart_message.rxindex = 0;uart_message.uart_rx_state = UART_STATE_ERROR;}}
 //Code for timer interruptif(Timer_3Byte5_cnt++ > TimerPeridCount)             //3.5 uart message time reaches{uart_message.uart_rx_state = UART_RX_FINISHED;     //uart receive state finisheduart_message.rxlength = uart_message.rxindex;     Timer_3Byte5_cnt = 0;}

在定时器中断函数里,当定时器计数达到3.5个UART报文发送时间时,认为此时Modbus报文已经接收完成,UART_State从BUSY变为FINISHED,同时把UART报文的length设置为rxindex,接下来就可以对接收到的报文进行解析了。

7.2 Modbus/UART模块化(附代码)

Modbus是应用层协议,Uart是底层的收发驱动,在具体实现时,需要把两个模块的内容分隔开。即Modbus协议相关内容的实现不要涉及UART。
代码里,对于Modbus和UART,都用了一个状态机去描述各自的状态。

typedef enum
{UART_TX_IDLE = 0,UART_TX_BUSY,UART_TX_FINISHED,UART_RX_IDLE,UART_RX_BUSY,UART_RX_FINISHED,UART_STATE_ERROR,
}uart_state_t;
//State machine of UART
typedef struct
{   uart_state_t uart_tx_state;uint8_t *ptrTx;uint8_t txlength;uint8_t txindex;uart_state_t uart_rx_state;uint8_t *ptrRx;uint8_t rxlength;uint8_t rxindex;
}uart_message_t;```c
typedef enum
{MODBUS_TX_IDLE = 0,MODBUS_TX_BUSY,MODBUS_TX_FINISHED,MODBUS_RX_IDLE,MODBUS_RX_BUSY,MODBUS_RX_FINISHED,MODBUS_STATE_ERROE,
}modbus_state_t;
//State machine of Modbus
typedef struct
{uint8_t* ptrTx;uint8_t txlength;uint8_t txindex;modbus_state_t modbus_tx_state;uint8_t* ptrRx;uint8_t rxlength;uint8_t rxindex;modbus_state_t modbus_rx_state;
}modbus_message_t;

uart_state有IDLE、BUSY、FINISHED这几个状态,分别用于表征UART总线的状态。
对于UART的收发,都有完整的函数去实现。在完成UART报文的接收后,就要把UART的报文移交给Modbus,移交之后UART本身的状态进行初始化。

uint8_t Modbus_Receive()
{uint8_t length;if(uart_message.uart_rx_state == UART_RX_FINISHED)        //uart message receive finished{modbus_message.rxlength = uart_message.rxlength;   modbus_message.modbus_rx_state = MODBUS_RX_FINISHED;DataCopy(uart_message.ptrRx, modbus_message.ptrRx, modbus_message.rxlength);       Uart_RxMsg_Init();                              //uart message clear, ready to receive next messagelength = Modbus_Check_Integrity(uart_message.ptrRx, modbus_message.rxlength);}else if((uart_message.uart_rx_state == UART_RX_BUSY) || (uart_message.uart_rx_state == UART_RX_IDLE))return 0;return length;
}

Modbus_Receive()函数中进行了Uart message buffer与Modbus message buffer内容的交接,当从机把通过Uart收到的报文给到Modbus后,Uart模块本身会进行初始化,等待总线上下一帧报文的到来。

uint8_t Modbus_Send(uint8_t *req, uint8_t req_length)            //Message doesn't include crc
{uint8_t Uart_Message_Length;if(req[0] == MODBUS_BROADCAST_ADDRESS){return 0;}else{Uart_Message_Length = Modbus_Send_Msg_Pre(req, req_length);if(Uart_Message_Length > 0){if(modbus_message.modbus_tx_state == MODBUS_TX_IDLE){modbus_message.modbus_tx_state = MODBUS_TX_BUSY;if(Uart_Send(req, Uart_Message_Length)){modbus_message.modbus_tx_state = MODBUS_TX_FINISHED;modbus_message.modbus_tx_state = MODBUS_TX_IDLE;                  }}elsereturn 0;}}return 1;
}

完成了两个模块的分割之后,这样Modbus相关的代码就可以轻松移植到其他项目中去,需要做的就是根据不同的芯片完成UART的收发代码。

MODBUS通讯协议学习总结相关推荐

  1. 485之modbus通讯协议学习笔记

    485之modbus通讯协议学习笔记 这里主要探讨两个寄存器 03 读保持寄存器 04读输入寄存器 输入寄存器其中的温度值和湿度值分别各占两个字节,一个字节八位使用十六进制00 01表示两个字节的寄存 ...

  2. Modbus通讯协议学习文档

    1 什么是Modbus通讯协议 Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表.Mo ...

  3. Modbus通讯协议的原文和详解

    这个协议很早以前就被公开发表了,现在Modbus已经成为工业领域通信协议的业界标准,有网站专门进行协议的整理和管理. 这里必须要吐槽一下,我就想查查协议发布时的原始文档,或者这个协议的英文原文和官方版 ...

  4. 基于modbus协议的工业自动化网络规范_工控学堂:解读Modbus通讯协议「宜收藏」...

    作为工控人,Modbus通讯协议想必都不陌生,Modbus通讯协议可以说是工业自动化领域应用最为广泛的通讯协议,因为他的开放性.可扩充性和标准化使他成为通用工业标准. 1979年施耐德电气制定了一个用 ...

  5. 一文搞懂物联网Modbus通讯协议

    简介: 一般来说,常见的物联网通讯协议众多,如蓝牙.Zigbee.WiFi.ModBus.PROFINET.EtherCAT.蜂窝等.而在众多的物联网通讯协议中,Modbus是当前非常流行的一种通讯协 ...

  6. 协议:Modbus通讯协议详细

    1.Modbus通讯协议详细解释 https://blog.csdn.net/rxiang12/article/details/79125813 2.Modbus通信协议详解 https://blog ...

  7. modbus通讯协议详解

    1.Modbus协议简介 Modbus协议是一种广泛应用于当今工业控制领域的通用通信协议.通过此协议,控制器相互之间.或者控制器经由网路(如以太网)可以和其他设备之间进行通信.Modbus协议使用的是 ...

  8. 工程监测多通道振弦模拟信号采集仪VTN的MODBUS 通讯协议

    工程监测多通道振弦模拟信号采集仪VTN的MODBUS 通讯协议 在 MODBUS 协议下,所有寄存器被定义为"保持寄存器" (详见 MODBUS 通讯协议标准说明), 设备支持基于 ...

  9. Modbus通讯协议

    https://baike.baidu.com/item/Modbus%E9%80%9A%E8%AE%AF%E5%8D%8F%E8%AE%AE/5972462?fromtitle=ModBus& ...

最新文章

  1. php yield 递归,递归运行所有yield请求h的废弃输出文件
  2. 云漫圈 | 如何给女朋友解释什么是HTTP
  3. Java开发者薪资最低?程序员只能干到30岁?国外真的没有996?Intellij真的比Eclipse受欢迎?
  4. 公众号向特定用户主动推送消息_公众号助手——消息不仅可以群发,还不限制次数!...
  5. butter滤波器是iir吗_如何快速设计应用一个IIR滤波器
  6. 解决64位进程调用32位库文件报错问题
  7. c++ string
  8. 双十二大前端工程师读书清单
  9. 静态多层Map缓存清除
  10. 秩和检验的概念及python实现
  11. SSD接口类型小知识
  12. 吴恩达深度学习作业(week2)-(1)
  13. Microsoft Visual C++ 14.0 网盘下载
  14. RFSoC应用笔记 - RF数据转换器 -10- RFSoC关键配置之其他功能(一)
  15. 子进程及时知道父进程已经退出的最简单方案
  16. 说说Linux的用途
  17. EMOTIV Epoc X 无线便携式脑电仪
  18. [SCOI2009]粉刷匠 dp
  19. 栅格数据去除黑边并无缝拼接(envi,arcgis)
  20. 一张照片引发的“美国总统泄密”事件

热门文章

  1. c#为什么用的人很少
  2. 最近的错误整理(LMY)
  3. 函数u=(x,y,z)在点P处延方向向量n的方向导数的计算
  4. 希望我们不要忘记喜欢过的,喜欢着的
  5. ctf之crypto练习二
  6. 贺泓胜:2.24黄金今日走势分析操作建议,黄金原油解套指导
  7. Axure设计网页时,需要注意的尺寸设置。
  8. 手机edge浏览器无法打开三方应用
  9. 关于“元宇宙”,讲点你能听懂的
  10. OSF Jonathan Bryce:Open Infrastructure开启开放协作新时代