【网络编程】Modbus协议

目录

1.分类

2.Modbus TCP协议

 3.Modbus RTU协议

 modbus库函数接口

编程流程


1.分类

(1)Modbus RTU:运行在串口上的协议,采用二进制表现形式及紧凑型数据结构,通信效率高,应用最为广泛。

(2)Modbus ASCII:也是运行在串口上的协议,采用ASCII码传输,利用特殊字符作为其字节开始和结束的标识,效率远远低于Modbus RTU协议,一般只有才通信数据量很小的情况下才考虑使用。

(3)Modbus TCP:运行在以太网上的协议。

2.Modbus TCP协议

Modbus TCP的特点:

(1)采用主从问答式通信

(2)Modbus TCP是应用层协议,基于传输层TCP协议实现

(3)Modbus TCP的端口号默认502

Modbus TCP协议格式:

协议包含三个部分:报文头、功能码、数据

协议最大数据帧长度为260字节,报文头7字节,功能码1字节,数据最大252字节。

(1)报文头

报文头包含7个字节,分别是

(2)寄存器

        包含四种:离散量输入、线圈、输入寄存器、保持寄存器

        ①离散量和线圈其实就是位寄存器(每个寄存器数据占1字节),工业上主要用于控制IO设备。

        线圈寄存器,类比为开关量,每一个bit都对应一个信号的开关状态。所以一个byte就可以同时控制8路的信号。比如控制外部8路io的高低。 线圈寄存器支持读也支持写,写在功能码里面又分为写单个线圈寄存器和写多个线圈寄存器。

        离散输入寄存器,离散输入寄存器就相当于线圈寄存器的只读模式,他也是每个bit表示一个开关量,而他的开关量只能读取输入的开关信号,是不能够写的。比如我读取外部按键的按下还是松开。

        ②输入和保持寄存器是字寄存器(每个寄存器数据占2个字节),工业上主要用于存储工业设备的值。

        保持寄存器,这个寄存器的单位不再是bit而是两个byte,也就是可以存放具体的数据量的,并且是可读写的。比如我我设置时间年月日,不但可以写也可以读出来现在的时间。写也分为单个写和多个写

        输入寄存器,这个和保持寄存器类似,但是也是只支持读而不能写。一个寄存器也是占据两个byte的空间。比如我通过读取输入寄存器获取现在的AD采集值。

(3)功能码

此处功能代码为十进制,15和16转换为十六进制分别为 0x0f 和 0x10 。

寄存器PLC地址和寄存器的对应关系:

线圈:           00001-09999

离散量输入:10001-19999

输入寄存器:30001-39999

保持寄存器:40001-49999

 具体协议分析可参考:实例分享 | ModbusTCP报文详解

 3.Modbus RTU协议

Modbus RTU与Modbus TCP的区别

        在一般工业场景使用modbus RTU的场景还是更多一些,modbus RTU基于串行协议进行收发数据,包括RS232/485等工业总线协议。与modbus TCP不同的是RTU没有报文头MBAP字段,但是在尾部增加了两个CRC检验字节(CRC16),因为网络协议中自带校验,所以在TCP协议中不需要使用CRC校验码。

        RTU和TCP的总体使用方法基本一致,只是在创建modbus对象时有所不同,TCP需要传入网络socket信息;而RTU需要传入串口相关信息。

Modbus RTU的特点

        ModbusRTU也是主从问答协议,由主机发起,一问一答

设置串口参数:

波特率:9600 / 115200

8位数据位

1位停止位

无流控

 Modbus RTU协议格式

ModbusRTU协议数据帧包含四部分:地址码、功能码、数据、校验码

从机ID:1-247

功能码:同Modbus TCP 协议

数据: 起始地址、数量、数据

校验码:2字节,对地址码、功能码、数据部分进行校验,调用函数生成

参考示例: 值得收藏 Modbus RTU 协议详解-CSDN博客

 modbus库函数接口

modbus_t * modbus_new_tcp ( const char * ip , int port )

功能:以TCP方式创建Modbus实例,并初始化

参数:

ip :ip地址

port:端口号

返回值:成功:Modbus实例

失败:NULL

int modbus_set_slave(modbus_t *ctx, int slave)

功能:设置从机ID

参数:

ctx :Modbus实例

slave:从机ID

返回值:成功:0

失败:-1

int modbus_connect(modbus_t *ctx)

功能:和从机(slave)建立连接

参数:

ctx:Modbus实例

返回值:成功:0

失败:-1

void modbus_free(modbus_t *ctx)

功能:释放Modbus实例

参数:ctx:Modbus实例

void modbus_close(modbus_t *ctx)

功能:关闭套接字

参数:ctx:Modbus实例

int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)

功能:读取线圈状态,可读取多个连续线圈的状态(对应功能码为0x01

参数:

ctx :Modbus实例

addr :寄存器起始地址

nb :寄存器个数

dest :得到的状态值

int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)

功能:读取输入状态,可读取多个连续输入的状态(对应功能码为0x02

参数:

ctx :Modbus实例

addr :寄存器起始地址

nb :寄存器个数

dest :得到的状态值

返回值:成功:返回nb的值

int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)

功能:读取保持寄存器的值,可读取多个连续保持寄存器的值(对应功能码为0x03

参数:

ctx :Modbus实例

addr :寄存器起始地址

nb :寄存器个数

dest :得到的寄存器的值

返回值:成功:读到寄存器的个数

失败:-1

int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)

功能:读输入寄存器的值,可读取多个连续输入寄存器的值(对应功能码为0x04

参数:

ctx :Modbus实例

addr :寄存器起始地址

nb :寄存器个数

dest :得到的寄存器的值

返回值:成功:读到寄存器的个数

失败:-1

int modbus_write_bit(modbus_t *ctx, int addr, int status);

功能:写入单个线圈的状态(对应功能码为0x05

参数:

ctx :Modbus实例

addr :线圈地址

status:线圈状态

返回值:成功:1

失败:-1

int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);

功能:写入多个连续线圈的状态(对应功能码为15

参数:

ctx :Modbus实例

addr :线圈地址

nb :线圈个数

src :多个线圈状态

返回值:成功:nb

失败:-1

int modbus_write_register(modbus_t *ctx, int addr, int value);

功能: 写入单个寄存器(对应功能码为0x06

参数:

ctx :Modbus实例

addr :寄存器地址

value :寄存器的值

返回值:成功:1

失败:-1

int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);

功能:写入多个连续寄存器(对应功能码为16

参数:

ctx :Modbus实例

addr :寄存器地址

nb :寄存器的个数

src :多个寄存器的值

返回值:成功:nb

失败:-1

编程流程

  1. 创建实例 modbus_new_tcp
  2. 设置从机ID modbus_set_slave
  3. 建立连接 modbus_connect
  4. 寄存器的操作(根据功能码自行选择)
  5. 关闭套接字 modbus_close
  6. 释放实例 modbus_free
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值