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

Modbus_RTU和Modbus库

目录

一.Modbus_RTU

1.  与Modbus TCP的区别

2.  Modbus RTU特点

     3.  Modbus RTU协议格式

4.  报文详解

5.  代码实现RTU通信

1.  打开模拟的RTU从机

2.  linux端使用代码实现和串口连接

2.1.  框架搭建

          2.2 代码

二.Modbus库

1.库函数


一.Modbus_RTU

1.  与Modbus TCP的区别

在一般工业场景使用modbus RTU的场景还是更多一些,modbus RTU基于串行协议进行收发数据,包括RS232/485等工业总线协议。

与modbus TCP不同的是RTU没有报文头MBAP字段,保留从机地址,在尾部增加了两个CRC检验字节(CRC16),因为网络协议中自带校验,所以在 ModbusTCP协议中不需要使用CRC校验码。

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

2.  Modbus RTU特点

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

     3.  Modbus RTU协议格式

ModbusRTU数据帧包含:从站地址 功能码 数据 CRC校验码

地址码:从机ID

功能码:同TCP

数据:起始地址 数量 数据

校验码:2个字节,对 地址码 功能码 数据进行校验,可以通过函数自动生成

4.  报文详解

03功能码为例

主机--》从机

01 03 00 00 00 01 84 0A

01 : 从机地址

03:功能码,读取保持寄存器

00 00 :起始地址

00 01:读取的个数,1个。

84 0A:CRC校验码

从机---》主机

01 03 02 00 14 B4 44

01:从机地址

03:功能码

02:字节计数

00 14:数据

B4 44 :CRC校验码

5.  代码实现RTU通信

1.  打开模拟RTU从机

2.  linux使用代码实现串口连接

2.1.  框架搭建

1.  打开COM1口

打开串口文件( /dev/ttyS1)

2.  串口初始化

使用专用函数即可

3.  创建要发送的03功能数组

4.  根据所要实现的功能,拼接好数组

5.  加上CRC校验码

6.  串口发送----向串口文件写数据

7.  串口接收----读取串口文件

8.  打印收到的数据

          2.2 代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include "Crc_Calc.h"
#include <unistd.h>

int main(int argc, char const *argv[])
{
// 1.打开COM1口
// 打开串口文件( /dev/ttyS1)
    int fd=open("/dev/ttyS1",O_RDWR);
    if (fd < 0)
    {
        perror("open err");
        return -1;
    }
// 2.串口初始化
    uart_init(fd);// 使用专用函数即可
// 3.创建要发送的03功能数组
// 4.根据所要实现的功能,拼接好数组
    //RTU 01 03 00 00 00 01
    uint8_t req[12]={0x01,0x03,0x00,0x00,0x00,0x01};
    uint8_t buf[32]={0};
// 5.加上CRC校验码
    uint16_t Crc;
    Crc=GetCRC16(req,6);//CRC=5;
    req[6]=Crc >> 8;  // y=CRC+1; 
    req[7]=Crc; 
// 6.串口发送----向串口文件写数据
    write(fd,req,8);
// 7.串口接收----读取串口文件
    int ret=read(fd,buf,sizeof(buf));
// 8.打印收到的数据
    for (int i = 0; i < ret; i++)
    {
        printf("%02x ",buf[i]);
    }
    putchar(10);
    return 0;
}

二.Modbus库

1.库函数

modbus_t* ctx=  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
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:线圈状态
返回值:成功:0
      失败:-1
int  modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);
功能:写入多个连续线圈的状态(对应功能码为15)
参数:
    ctx     :Modbus实例
    addr  :线圈地址
    nb     :线圈个数
    src    :多个线圈状态 
返回值:成功:0
      失败:-1
int  modbus_write_register(modbus_t *ctx, int addr, int value);
功能:  写入单个寄存器(对应功能码为0x06)
参数: 
    ctx    :Modbus实例
    addr  :寄存器地址
    value :寄存器的值 
返回值:成功:0
       失败:-1
int  modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);
功能:写入多个连续寄存器(对应功能码为16)
参数:
    ctx    :Modbus实例
    addr  :寄存器地址
    nb     :寄存器的个数
    src    :多个寄存器的值 
返回值:成功:0
      失败:-1

void   modbus_free(modbus_t *ctx)
功能:释放Modbus实例
参数:ctx:Modbus实例
void   modbus_close(modbus_t *ctx)
功能:关闭套接字
参数:ctx:Modbus实例

练习:使用库函数实现03功能码

#include<modbus.h>
#include<modbus-tcp.h>
#include<stdio.h>

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("usage:<ip>\n");
        return -1;
    }
    //1.创建实例
    modbus_t * ctx=modbus_new_tcp(argv[1],502);
    //2.设置从机ID
    modbus_set_slave(ctx,1);
    //3.创建连接
    modbus_connect(ctx);
    //4.03功能码函数
    uint16_t dest[8]={0};
    modbus_read_registers(ctx, 0, 2, dest);
    //5.打印
    for (int i = 0; i < 2; i++)
    {
        printf("%d ",dest[i]);
    }
    putchar(10);
    return 0;
}


http://www.kler.cn/news/310881.html

相关文章:

  • 1.Seata 1.5.2 seata-server搭建
  • 线程池的类型和状态
  • sqli-labs靶场自动化利用工具——第11关
  • 【深度学习】(2)--PyTorch框架认识
  • 设计模式(Design Patterns)
  • springBoot整合mybatisplus
  • 学习风格的类型
  • 内核是如何接收网络包的
  • FLUX屠榜了小红书,平台这会也真假难辨
  • PMP 报考条件是有哪些?
  • 【mysql】mysql中窗口函数lag()用法
  • HarmonyOS开发实战( Beta5.0)蓝牙实现服务端和客户端通讯详解
  • 面向对象设计的五大原则(SOLID 原则)
  • Jsp学习笔记(详解)
  • 【2025】儿童疫苗接种预约小程序(源码+文档+解答)
  • python 实现collatz sequence考拉兹序列算法
  • 如何使用下拉字段创建WordPress表单(简单方法)
  • 1.熟悉接口测试(Postman工具)
  • JavaWeb笔记整理——Redis
  • 程序员装新机
  • 【架构设计】多级缓存:应用案例与问题解决策略
  • Linux入门学习:Linux权限理解
  • PyQGIS开发 1 环境配置
  • web渗透—RCE
  • 单片机中为什么要使用5v转3.3v,不直接使用3.3V电压
  • 【Bug解决】Nacos启动成功,但却无法访问(提示:无法访问此网站,192.168.10.88的响应时间过长)
  • 【算法题】46. 全排列-力扣(LeetCode)
  • Flink 实现无界流
  • 十七,Spring Boot 整合 MyBatis 的详细步骤(两种方式)
  • 四、JVM原理-4.2、内存管理