Modbus RTU、Modbus 库函数

Modbus RTU

与 Modbus TCP 的区别

一般在工业场景中,使用 Modbus RTU 的场景更多一些,Modbus RTU 基于串行协议进行收发数据,包括 RS232/485 等工业总线协议。采用主从问答式(master / slave)通信。
与 Modbus TCP 不同的是,RTU 没有报文头 MBAP 字段,但是在尾部增加了两个 CRC 检验字节(CRC16),因为网络协议中自带校验,所以在 TCP 协议中不需要使用 CRC 校验码。
RTU 和 TCP 的总体使用方法基本一致,只是在创建 Modbus 对象时有所不同。TCP 需要传入网络socket 信息;而 RTU 需要传入串口相关信息。

特点

通信

采用主从问答式(master / slave)通信,由主机发起,一问一答。

设置串口参数

波特率:9600
数据位:8
停止位:1
无流控

协议格式(地址码 + 功能码 + 数据 + 校验码)

Modbus RTU 数据帧包含:地址码、功能码、数据、校验码。
地址码: 从机 ID
功能码: 同 Modbus TCP
数据: 起始地址、数量、数据
CRC 校验码: 两个字节,对 地址码、功能码、数据 进行校验,可以通过函数自动生成

报文详解

(👆 链接至另一博主,放心跳转)

以 03 功能码为例:

主机 ——> 从机:

在这里插入图片描述

从机 ——> 主机:

在这里插入图片描述

模拟器的安装、配置、使用

实际硬件产品成本较高,可以使用一系列 Modbus 软件模拟器,进行数据模拟,从而分析 Modbus RTU 协议。

所用工具

Modbus Slave、vspd 虚拟串口、UartAssist 串口调试工具、虚拟机

安装与配置

一)vspd 虚拟串口的安装

1)将压缩包解压后,双击 vspd.exe 文件进行安装;
在这里插入图片描述

2)打开软件,添加 COM1 和 COM2 端口(用完之后记得删除端口);
在这里插入图片描述

3)打开设备管理器,出现如下图所示即可;
在这里插入图片描述

4)可以汉化,将 Cracked 下的文件复制到软件安装目录即可。
在这里插入图片描述

二)虚拟机绑定端口

1)VMware 虚拟机(注意不是 ubuntu)在系统关机(必须是关机状态,挂起不行)状态下,
点击:虚拟机 ——> 设置 ——> 硬件 ——> 添加串行端口,添加 COM1;
在这里插入图片描述

2)添加完成后,第一次使用需要将电脑重启;
3)重启之后,打开虚拟机,点击虚拟机 ——> 可移动设备 ——> 串行端口 ——> 连接;
在这里插入图片描述

4)在终端输入dmesg|grep tty,查看对应的设备文件,其中默认的会有 ttyS0 文件,
其余一个(ttyS1 或 ttyS2)就是虚拟串口对应的设备文件。
在这里插入图片描述

三)测试通信

1)Windows 下打开串口调试工具,选择好串口 COM2 ——> COM1,设置对应的波特率;
在这里插入图片描述

2)以下步骤在虚拟机下完成,在虚拟机安装 minicom 软件;sudo apt-get install minicom
在这里插入图片描述

3)在终端执行 sudo minicom -s ,选择 Serial port setup;
在这里插入图片描述

4)设置设备文件,波特率,关闭流控;(按 Ctrl + 相应字母)
在这里插入图片描述

5)回车,保存修改,选择 Save setup as dfl;
在这里插入图片描述

6)可以在以下界面输入字符,查看串口助手的显示情况;
在这里插入图片描述

7)测试通信(终端输入不可见);
在这里插入图片描述

8)退出:Ctrl + A,然后按 Z,在弹出的界面里输入X,即可退出。
在这里插入图片描述

四)将 Modbus Slave 模拟器作为 RTU 设备的从机

虚拟机绑定 COM1 端口,Modbus Slave 连接 COM2 端口,虚拟机通过编程测试串口通信;
在这里插入图片描述

五)可能遇到的问题

虚拟串口完成主机与 vmware 下虚拟机进行串口通信
VSPD 虚拟串口工具 —— 从此告别硬件串口调试
vmware 虚拟机检测不到 vspd 虚拟串口问题
(👆 链接至其他博主,放心跳转)

Modbus 库

库的安装

安装与配置

1)在 linux 中解压压缩包,tar -xvf libmodbus-3.1.7.tar.gz
2)进入源码目录,创建文件夹(存放头文件、库文件);

	cd libmodbus-3.1.7 
	mkdir install 

3)执行脚本 configure,进行安装配置(指定安装目录);

	./configure--prefix=$PWD/install 

在这里插入图片描述

4)执行 make 和 make install

	make 					// 编译
  	make install 			   // 安装

在这里插入图片描述
在这里插入图片描述

5)执行完成后会在 install 文件夹下产生对应的头文件、库文件。

使用

1、一般操作:

gcc xxx.c -I ./install/include/modbus -L ./install/lib -lmodbus
./a.out

-I : 后需要指定出头文件的路径(大写的i)
-L : 后需要指定库的路径
-l : 后需要指定库名(小写的L)

2、要想编译方便,可以将头文件和库文件放到系统路径下:

sudo  cp install/include/modbus/*.h  /usr/include 
sudo  cp install/lib/*  -r /lib -d 

在这里插入图片描述

后期编译时,就可以直接 gcc xxx.c -lmodbus,
头文件默认搜索路径:/usr/include、/usr/local/include
库文件默认搜索路径:/lib、/usr/lib

函数接口

0x01(modbus_read_bits)

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

功能:读取线圈状态,可读取多个连续线圈的状态(对应功能码为0x01)
参数:
    ctx   :	Modbus实例
    addr  :	寄存器起始地址
    nb    :	寄存器个数
    dest  :	得到的状态值

0x02(modbus_read_input_bits)

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

功能:读取输入状态,可读取多个连续输入的状态(对应功能码为0x02)
参数:
    ctx  :	Modbus 实例
    addr :	寄存器起始地址
    nb   :	寄存器个数
    dest :	得到的状态值
返回值:成功:返回nb的值

0x03(modbus_read_registers)

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

功能:读取保持寄存器的值,可读取多个连续保持寄存器的值(对应功能码为0x03)
参数:
    ctx  :	Modbus 实例
    addr :	寄存器起始地址
    nb   :	寄存器个数
    dest :	得到的寄存器的值
返回值:成功:读到寄存器的个数
       失败:-1

0x04(modbus_read_input_registers)

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

功能:读输入寄存器的值,可读取多个连续输入寄存器的值(对应功能码为0x04)
参数:
    ctx   :	Modbus 实例
    addr  :	寄存器起始地址
    nb    :	寄存器个数
    dest  :	得到的寄存器的值
返回值:成功:读到寄存器的个数
       失败:-1

0x05(modbus_write_bit)

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

功能:写入单个线圈的状态(对应功能码为0x05)
参数:
    ctx   :	Modbus 实例
    addr  :	线圈地址
    status:	线圈状态
返回值:成功:0
       失败:-1

0x06(modbus_write_register)

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

功能:写入单个寄存器(对应功能码为0x06)
参数: 
    ctx   :	Modbus 实例
    addr  :	寄存器地址
    value :	寄存器的值 
返回值:成功:0
       失败:-1

0x0F(modbus_write_bits)

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

功能:写入多个连续线圈的状态(对应功能码为15)
参数:
    ctx   :	Modbus 实例
    addr  :	线圈地址
    nb    :	线圈个数
    src   :	多个线圈状态
返回值:成功:0
       失败:-1

0x10(modbus_write_registers)

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

功能:写入多个连续寄存器(对应功能码为16)
参数:
    ctx   :	Modbus 实例
    addr  :	寄存器地址
    nb    :	寄存器的个数
    src   :	多个寄存器的值 
返回值:成功:0
       失败:-1

编程流程

1)创建实例(modbus_new_tcp / modbus_new_rtu)

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

功能:以 TCP 方式创建 Modbus 实例,并初始化
参数:
    ip  :	ip 地址
    port:	端口号
返回值:成功:Modbus 实例
       失败:NULL
modbus_t *modbus_new_rtu(const char *device, int baud, 
                         		char parity, int data_bit, int stop_bit);

功能:用于创建一个用于 Modbus RTU 通信的 modbus_t 结构体实例
参数:
	device:	要打开的串口设备的路径(例如:"/dev/ttyUSB0")
	baud:		波特率(如 9600、19200 等)
	parity:	校验位(可选值:'N' - 无校验、'E' - 偶校验、'O' - 奇校验)
	data_bit:	数据位(常用值为 8)
	stop_bit:	停止位(常用值为 1)
 返回值:成功:Modbus 实例
        失败:NULL

2)设置从机地址(modbus_set_slave)

int  modbus_set_slave(modbus_t *ctx, int slave); 
功能:设置从机ID
参数:
    ctx  :		Modbus 实例
    slave:		从机 ID
返回值:成功:0
       失败:-1

3)建立连接(modbus_connect)

int  modbus_connect(modbus_t *ctx); 
功能:和从机(slave)建立连接
参数:
    ctx:		Modbus 实例
返回值:成功:0
       失败:-1

4)各种操作(见函数接口)

5)关闭套接字(modbus_close)

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

6)释放实例(modbus_free)

void   modbus_free(modbus_t *ctx); 
功能:释放 Modbus 实例
参数:ctx:Modbus 实例

练习:

// 和 Slave 通信,读保持寄存器的三个值

#include <stdio.h>
#include <modbus.h>
#include <stdlib.h>
#include <string.h>
#include <modbus-rtu.h>

int main(int argc, char const *argv[])
{   
    if (argc != 3){
        printf("Please input %s <ip> <port>. \n", argv[0]);
        return -1;
    }

    modbus_t *ctx;
    ctx = modbus_new_tcp(argv[1], atoi(argv[2]));
    // ctx = modbus_new_rtu("/dev/ttyS1", 9600, N, 8, 1);
    
    if (ctx == NULL){
        perror("Failed to modbus_new_tcp");			// "Failed to modbus_new_rtu"
        return -1;
    }

    if (modbus_set_slave(ctx, 1) < 0){
        perror("Failed to modbus_set_slave");
        return -1;
    }

    if (modbus_connect(ctx) < 0){
        perror("Failed to modbus_connect");
        return -1;
    }

    uint16_t dest[32] = {};

    if (modbus_read_registers(ctx, 0, 3, dest) < 0){
        perror("Failed to modbus_read_registers");
        return -1;
    }

    for (int i = 0; i < 3; i++)
        printf("%#x ", dest[i]);
    putchar(10);
    for (int i = 0; i < 3; i++)
        printf("%d ", dest[i]);
    putchar(10);

    modbus_close(ctx);
    modbus_free(ctx);
    return 0;
}

运行结果如下:
在这里插入图片描述

注意:

1、使用 Modbus TCP 协议时,将 slave 的 connect 设置为“Modbus TCP/IP”。
2、使用 Modbus RTU 协议时,将 slave 的 connect 设置为“Serial Port”。
在这里插入图片描述

小目标:

编程实现采集传感器数据和控制硬件设备(传感器和硬件通过 slave 模拟)。
传感器:2个,光线传感器、加速度传感器(x \ y \ z);
硬件设备:2个,LED灯、蜂鸣器。
要求:
1、多任务编程:多线程、多进程
2、循环 1s 采集一次数据,并将数据打印至终端
3、同时从终端输入指令控制硬件设备
0 1:LED 灯开
0 0:LED 灯关
1 1:蜂鸣器开
1 0:蜂鸣器关

// 同步实现

#include <stdio.h>
#include <modbus.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>

modbus_t *ctx;
sem_t sem1, sem2;

void *collector(void *arg){

    uint16_t *dest = (uint16_t *)arg;
    while (1){
        sleep(5);
        sem_wait(&sem1);

        if (modbus_read_registers(ctx, 0, 4, dest) < 0){
            perror("Failed to modbus_read_registers");
            return NULL;
        }
        for (int i = 0; i < 4; i++)
            printf("%d ", dest[i]);
        putchar(10);

        sem_post(&sem2);
    }
    pthread_exit(0);
}

void *control(void *arg){
    
    uint8_t writer[2];
    while (1){
        sem_wait(&sem2);

        printf("Please set status of LED or BUZZER: ");
        for (int i = 0; i < 2; i++)
            scanf("%hhu", &writer[i]);
        
        modbus_write_bit(ctx, writer[0], writer[1]);

        sem_post(&sem1);
    }
    pthread_exit(0);
}

int main(int argc, char const *argv[])
{   
    if (argc != 3){
        printf("Please input %s <ip> <port>. \n", argv[0]);
        return -1;
    }

    ctx = modbus_new_tcp(argv[1], atoi(argv[2]));
    if (ctx == NULL){
        perror("Failed to modbus_new_tcp");
        return -1;
    }

    if (modbus_set_slave(ctx, 1) < 0){
        perror("Failed to modbus_set_slave");
        return -1;
    }

    if (modbus_connect(ctx) < 0){
        perror("Failed to modbus_connect");
        return -1;
    }

    uint16_t dest[32] = {};

    pthread_t tid1, tid2;
    sem_init(&sem1, 0, 1);
    sem_init(&sem2, 0, 0);

    if (pthread_create(&tid1, NULL, collector, dest)){
        perror("Failed to create a thread named collector");
        return -1;
    }
    pthread_detach(tid1);

    if (pthread_create(&tid2, NULL, control, NULL)){
        perror("Failed to create a thread named input");
        return -1;
    }
    pthread_detach(tid2);

    while (1);
    

    modbus_close(ctx);
    modbus_free(ctx);
    return 0;
}

实现效果如下:
在这里插入图片描述

  • 12
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Modbus RTU是一种串行通信协议,常用于工业自动化领域。C语言是一种通用的编程语言,可以用来实现Modbus RTU通信功能。 实现Modbus RTU通信的关键是通过串口与设备进行交互。在C语言中,可以使用串口库函数来控制串口通信。首先,需要设置串口的通信参数,如波特率、数据位、奇偶校验位和停止位。 然后,使用C语言编写Modbus RTU通信的相关函数。例如,可以编写一个函数来发送Modbus RTU命令,另一个函数来接收并解析Modbus RTU响应。在函数中,需要按照Modbus RTU协议的规定,将命令封装成帧,并通过串口发送给设备。 接收到设备的响应后,需要对响应进行解析和处理。可以编写一个函数来解析Modbus RTU帧,获取其中的数据内容。根据需求,可以使用C语言的数据类型和逻辑操作符,对解析得到的数据进行处理和判断。 除了发送和接收Modbus RTU命令之外,还可以编写一些辅助函数来处理Modbus RTU通信过程中可能出现的异常情况,如超时、校验错误等。这些函数可以提高通信的稳定性和可靠性。 最后,可以在C语言的主程序中调用以上编写的函数,实现Modbus RTU通信功能。可以根据具体的应用需求,编写相应的逻辑和算法,对接收到的数据进行处理和应用。 总之,使用C语言可以实现Modbus RTU通信功能,通过编写相关的函数和主程序,可以灵活地控制和管理Modbus RTU设备。 ### 回答2: Modbus是一种通信协议,广泛应用于工业控制系统中。Modbus RTU是其中的一种实现方式,它使用C语言编写的通信驱动程序进行数据传输。 C语言是一种强大的编程语言,用于开发各种软件和硬件应用。在Modbus RTU通信中,使用C语言可以方便地实现数据的读写和通信的控制。 首先,需要使用串口库来进行串口通信的配置和操作。通过配置串口的波特率、数据位、停止位等参数,可以建立C语言Modbus RTU设备之间的物理连接。 接下来,需要根据Modbus RTU通信协议规定的数据格式进行数据的读写。在C语言中,可以使用相应的数据类型来表示Modbus RTU协议的帧格式,如设备地址、功能码、寄存器地址、数据等。 通过发送读写请求,使用C语言的串口通信库将请求发送给Modbus RTU设备,并等待设备的响应。在接收到响应后,可以解析响应数据,根据需求进行数据处理和存储。 在编写C语言Modbus RTU通信程序时,需要考虑异常处理、数据校验和错误检测等方面。例如,可以使用CRC校验来检测通信数据的完整性,以确保数据的准确传输。 总之,使用C语言编写Modbus RTU通信程序可以实现与Modbus RTU设备之间的稳定、高效的数据通信。通过适当的配置和操作,可以实现数据的读写和设备的控制,为工业控制系统提供强大的功能支持。 ### 回答3: Modbus RTU是一种串行通信协议,被广泛应用于工业自动化领域。C语言是一种通用的编程语言,可以用于编写Modbus RTU通信的程序。 在使用C语言编写Modbus RTU程序时,需要使用串口通信库来实现与Modbus设备之间的通信。例如,可以使用标准的串口库或者第三方库来打开串口、设置波特率、发送和接收数据。 在发送数据时,需要按照Modbus RTU协议的规范构造数据帧,并通过串口发送给Modbus设备。通常情况下,需要设置从设备地址、功能码、寄存器地址、写入或读取的数据等信息来构造数据帧。 在接收数据时,需要通过串口接收数据,并根据Modbus RTU协议的规范解析数据帧。从接收到的数据帧中可以获取到设备返回的数据,然后可以对数据进行处理和解析。 在编写程序时,还需要考虑错误处理和异常情况,例如数据的完整性和正确性校验、超时处理等。可以使用适当的错误处理机制来处理这些情况,确保程序的稳定性和可靠性。 总之,使用C语言编写Modbus RTU程序需要了解Modbus RTU协议的规范,使用串口通信库来实现与Modbus设备的通信,构造和解析数据帧,并处理错误和异常情况。通过合理的编码和调试,可以实现可靠的Modbus通信功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值