在一般工业场景使用modbus RTU的场景还是更多一些,modbus RTU基于串行协议进行收发数据,包括RS232/485等工业总线协议。
与modbus TCP不同的是RTU没有报文头MBAP字段,但是在尾部增加了两个CRC检验字节(CRC16),因为网络协议中自带校验,所以在TCP协议中不需要使用CRC校验码。
RTU和TCP的总体使用方法基本一致,只是在创建modbus对象时有所不同,TCP需要传入网络socket信息;而RTU需要传入串口相关信息。
二、Modbus RTU特点
三、Modbus RTU协议格式
地址码:一个字节 从机ID
数据:起始地址 数量 数据
校验码:2个字节,对 地址码+功能码+数据进行校验,可以通过函数自动生成。
四、报文详解
03功能码为例
主机—》从机:
01 03 00 00 00 01 84 0a
01:地址码
03:功能码
00 00 :起始地址
00 01:读的数量
从机–》主机:
01 03 02 00 14 b8 44
01:地址码
03:功能码
00 14:数据
五、模拟器的使用
由于实际硬件产品成本较高,我们这里可以使用Modbus软件模拟器,进行数据模拟从而分析Modbus协议。
1. ModbusPoll(模拟主机)和ModbusSlave(模拟从机)
设置串口参数要求:波特率为9600 8位数据位 1位停止位 无流控 无校验
虚拟串口的使用:
2.安装完成后,找到安装目录,将Cracked下的文件复制到软件安装目录
或
- 将虚拟机在系统关机(必须是关机状态,挂起不行)状态下,点击虚拟机->设置->硬件->添加串行端口,添加COM1
- 添加完成后,第一次使用需要将电脑重启
- 重启之后,打开虚拟机,点击虚拟机->可移动设备->串行端口->连接
- 当连接上虚拟串口后,在终端输入dmesg | grep tty,可以查看到对应的设备文件,其中默认的会有ttyS0文件,剩下的就是虚拟串口对应的设备文件
1.Windows打开串口调试工具,选择好串口COM2->COM1,设置对应的波特率
2)设置设备文件,波特率,关闭流控,按如下图设置(文件改成自己的)
3)修改完成后,回车,保存修改,选择save setup as dfl,敲回车,再次选择exit回车
4)退出后就可以和windows下的串口调试工具进行通信测试
6)退出:ctrl+A、Z,在弹出的界面里输入X,即可退出。
虚拟机绑定COM1端口,slave连接COM2端口,虚拟机通过编程测试串口通信
五、可能会遇到的问题
虚拟串口完成主机与vmware下虚拟机进行串口通信_xcom2v2.0怎么用-CSDN博客
练习:代码完成通过串口读取slave端
六、Modbus库
tar –xvf libmodbus-3.1.7.tar.gz
3. 执行脚本configure,进行安装配置(指定安装目录)
./configure —prefix=$PWD/install
执行完成后会在install文件夹下生产对应的头文件、库文件件夹install,用于存放产生的头文件、库文件等
-
- 库的使用
sudo cp install/include/modbus/*.h /usr/include
sudo cp install/lib/* –r /lib -d
头文件默认搜索路径:/usr/include 、/usr/local/include
modbus_t* modbus_new_tcp(const char *ip, int port)
功能:以TCP方式创建Modbus实例,并初始化
参数:
ip :ip地址
port:端口号
返回值:成功:Modbus实例
失败:NULL
- 设置从机ID
int modbus_set_slave(modbus_t *ctx, int slave)
功能:设置从机ID
参数:
ctx :Modbus实例
slave:从机ID
返回值:成功:0
失败:-1
- 和从机(slave)建立连接
一个modbus实例只能链接一个从机:根据提供的引用内容,一个modbus实例只能连接一个从机ID。因为modbus通信协议是基于主从模式的,每个从机都有一个唯一的从机地址,主机通过从机地址来访问不同的从机。因此,一个modbus实例只能连接一个从机ID,如果需要连接多个从机,需要创建多个modbus实例。
int modbus_connect(modbus_t *ctx)
功能:和从机(slave)建立连接
参数:
ctx:Modbus实例
返回值:成功:0
失败:-1
- 释放Modbus实例
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 :得到的状态值
7)读取输入状态,可读取多个连续输入的状态(对应功能码为0x02)
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
- 读输入寄存器的值,可读取多个连续输入寄存器的值(对应功能码为0x04)
int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
功能:读输入寄存器的值,可读取多个连续输入寄存器的值(对应功能码为0x04)
参数:
ctx :Modbus实例
addr :寄存器起始地址
nb :寄存器个数
dest :得到的寄存器的值
返回值:成功:读到寄存器的个数
失败:-1
- 写入单个线圈的状态(对应功能码为0x05)
int modbus_write_bit(modbus_t *ctx, int addr, int status);
功能:写入单个线圈的状态(对应功能码为0x05)
参数:
ctx :Modbus实例
addr :线圈地址
status:线圈状态
返回值:成功:0
失败:-1
- 写入多个连续线圈的状态(对应功能码为15)
int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);
功能:写入多个连续线圈的状态(对应功能码为15)
参数:
ctx :Modbus实例
addr :线圈地址
nb :线圈个数
src :多个线圈状态
返回值:成功:0
失败:-1
- 写入单个寄存器(对应功能码为0x06)
int modbus_write_register(modbus_t *ctx, int addr, int value);
功能: 写入单个寄存器(对应功能码为0x06)
参数:
ctx :Modbus实例
addr :寄存器地址
value :寄存器的值
返回值:成功:0
失败:-1
12)写入多个连续寄存器(对应功能码为16)
int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);
功能:写入多个连续寄存器(对应功能码为16)
参数:
ctx :Modbus实例
addr :寄存器地址
nb :寄存器的个数
src :多个寄存器的值
返回值:成功:0
失败:-1
- 创建实例 modbus_new_tcp
- 设置从机id modbus_set_slave
- 建立连接 modbus_connect
- 寄存器操作
- 关闭套接字 modbus_close
- 释放实例 modbus_free
注意:编译不要忘了链接库、查看网络是否能用,查看slave端协议是否正确,查看slave端是否有对应的寄存器类型,查看slave id是否一致
gcc modbus.c -lmodbus
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <modbus.h>
int main(int argc, char const *argv[])
{
int ret;
uint16_t buf[32];
// 1.创建实例 modbus_new_tcp,端口号字符型转整型
modbus_t* md = modbus_new_tcp(argv[1],atoi(argv[2]));
// 2.设置从机id modbus_set_slave
ret = modbus_set_slave(md, 1);
if (ret < 0)
{
printf("set errn");
}
// 3.建立连接 modbus_connect
ret = modbus_connect(md);
if (ret < 0)
{
printf("connect err.n");
}
// 4.寄存器操作
//从0开始读十个寄存器值
ret = modbus_read_registers(md, 0, 10, buf);
for (int i = 0; i < 10; i++)
{
printf("%#x ", buf[i]);
}
// 5.关闭套接字 modbus_close,先关闭套接字,再释放实例
modbus_close(md);
// 6.释放实例 modbus_free
modbus_free(md);
return 0;
}
1.任务:编程实现采集传感器数据和控制硬件设备(传感器和硬件通过slave模拟)
要求:
0 1 :led灯打开
1 1:蜂鸣器开
1 0 : 蜂鸣器关
原文地址:https://blog.csdn.net/m0_74937538/article/details/134585177
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_18771.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!