文章目录

  • 为什么要使用驱动库?
  • libmodbus简介
  • libmodbus常用函数
  • Windows平台libmodbus 使用
    • 1.获取源代码
    • 2.生成config.h配置文件
    • 3.编写测试代码
    • 4.编译测试代码
  • Linux平台下libmodbus使用
  • ARM平台下libmodbus使用
  • libmodbus 从机地址限制的问题

这篇文章是接上一篇Modbus协议简介,介绍Modbus实际项目应用,断断续续写了近两周时间。

为什么要使用驱动库?

上一篇文章,我们介绍了Modbus协议物理层和协议层,我们知道了Modbus是一种总线协议,它可以基于串口或网口,以基于串口的Modbus-RTU为例,我们需要在Windows或Linux下实现一个上位机,上位机的功能是读写Modbus接口传感器设备的数据,或者是和单片机等从设备进行交互。

当需要向某个从机寄存器写入某个值时,如向01地址的设备,0x0105保持寄存器写入1个数据:0x0190为例,那么需要构建这样一个数据帧:

主机发送: 01 06 01 05 01 90 99 CB

01表示从机地址,06功能码表示写单个保持寄存器,0105表示寄存器地址,0190表示写入寄存器的数值,99 CB为CRC校验值。

如果从机正确的收到了数据,会回复一个数据帧:

从机回复: 01 06 01 05 01 90 99 CB

所以作为主机,写数据的流程是:

  1. 构建一个Modbus-RTU数据帧

  2. 等待从机响应的数据

  3. 如果响应数据正确,说明写入成功,否则写入失败。

读数据也是同样的流程,我们可以基于串口发送、串口接收函数、定时器等,自己写一个Modbus驱动库,来实现对从设备的读写。当然,也可以直接使用别人写好的Modbus驱动库,比如libmodbus,本文将介绍如何使用libmodbus驱动库,实现Modbus主机和从机。

libmodbus简介

libmodbus,是一个基于C语言实现的Modbus驱动库,作者是Stephane,支持Linux, Mac OS X, FreeBSD, QNX and Win32操作系统,主要应用在PC上,用来开发上位机,也可以对源代码进行交叉编译,以适配更多的平台,比如ARM Linux。源代码开源,遵循 LGPL-2.1 许可。目前最新版本是3.1.6,Github仓库最新提交时间是2021年5月21日。

官方网站:https://libmodbus.org/

开源地址:https://github.com/stephane/libmodbus/

libmodbus支持如下功能:

  • 支持Modbus-RTU和Modbus-TCP
  • 支持常用功能码,如01/02/03/04/05/06/07/0F/10/11/16/17
  • 支持线圈类型读写、寄存器读写、离散量读取等
  • 支持广播地址0,从机地址1-247
  • 支持浮点数和整形数据转换,大端小端等多种模式
  • 参数根据Modbus_Application_Protocol_V1_1b.pdf官方标准文档设计,比如最大读写线圈个数,最大读写寄存器个数等。
  • 源代码基于C编写,方便在各平台移植,只有11个文件。

libmodbus常用函数

libmodbus库函数非常简洁,读写操作函数对于RTU和TCP完全通用,RTU和TCP切换只需要修改一行代码就可以实现无缝切换。


modbus_t *mb;
int ret;
//创建一个modbus-rtu对象,指定串口号,波特率,校验位,数据位,停止位
//成功返回指针,否则返回NULL, 会调用malloc申请内存
mb = modbus_new_rtu("/dev/ttySP1", 115200, 'N', 8, 1); //linux
mb = modbus_new_rtu("COM1", 115200, 'N', 8, 1);        //windows
//创建modbus-tcp对象,指定IP地址和端口号
mb = modbus_new_tcp("127.0.0.1", 502);   //TCP/IP//设置从机地址,成功返回0, 否则返回-1
ret = modbus_set_slave(mb, slave);//连接Modbus主机,成功返回0, 否则返回-1
ret = modbus_connect(mb);//设置响应超时时间1s,200ms
ret = modbus_set_response_timeout(mb, 1, 200000);//读取寄存器数据,起始地址2, 数量5, 保存到table数组中
//成功返回5, 否则返回-1
uint16_t *table;
ret = modbus_read_registers(mb, 2, 5, table);//modbus设备关闭和释放内存
modbus_close(mb);
modbus_free(mb);//写单个寄存器, 地址2写入56, 成功返回1,否则返回-1
ret = modbus_write_register(mb, 2, 56);//写多个寄存器, 地址12起始,写入5个数据,成功返回5,否则返回-1
uint16_t table[5] = {11, 22, 33, 44, 55};
ret = modbus_write_registers(mb, 12, 5, table);//写单个线圈,线圈地址写入TRUE,成功返回1,否则返回-1
ret = modbus_write_bit(mb, 11, TRUE);//查看错误信息
char *err_str;
err_str = modbus_strerror(errno);

Windows平台libmodbus 使用

以Windows下使用libmodbus实现从机和主机为例,Linux下类似。

1.获取源代码

使用Git工具下载GitHub代码仓库源代码到本地,这样可以获取到最新的libmodbus代码,但是也会有一些Bug。

git clone https://github.com/stephane/libmodbus/

如果下载速度缓慢,可以到我的Gitee仓库下载:

git clone https://gitee.com/whik/libmodbus

或者到官方仓库下载最新稳定发布版本v3.1.6:

https://libmodbus.org/releases/libmodbus-3.1.6.tar.gz

下载完成之后,解压到本地,Linux系统可以使用tar -zxvf libmodbus-3.0.6.tar.gz命令行解压:

我们重点关注以下3个文件夹:doc,src,tests。

  • doc,doc文件夹包含库的使用文档,文件名就是函数名,介绍每个函数的使用方法,参数定制,返回值说明,示例代码等。

  • src,src文件夹是libmodbus库源文件和头文件,我们只需要把这些文件添加到工程中,然后包含头文件就可以直接使用了。

  • tests,tests文件夹包含libmodbus使用示例,

包括Modbus-RTU/TCP客户端和服务器单元测试,随机测试,效率测试,读写10万个线圈状态,10万个寄存器,记录消耗时间。

//部分代码
nb_points = MODBUS_MAX_READ_BITS;
start = gettime_ms();
for (i=0; i<n_loop; i++) {rc = modbus_read_bits(ctx, 0, nb_points, tab_bit);if (rc == -1) {fprintf(stderr, "%s\n", modbus_strerror(errno));return -1;}
}
end = gettime_ms();
elapsed = end - start;

官方提供的测试代码太繁琐,后面我们会写两个简单的示例程序,来演示主机和从机的使用。

2.生成config.h配置文件

无论是Windows还是Linux,在使用libmodbus库之前,我们需要先调用configure工具来生成config.h文件和Makefile。configure工具会根据当前系统环境,生成适用于当前平台的config.h文件。

在libmodbus库文件夹下执行./configure命令。


whik@windows_7 MINGW64 /d/libmodbus-3.1.6
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for strings.h... yes.......
checking for inttypes.h... yes
config.status: creating tests/unit-test.h
config.status: executing libtool commandslibmodbus 3.1.6===============prefix:                 /usr/localsysconfdir:             ${prefix}/etclibdir:                 ${exec_prefix}/libincludedir:             ${prefix}/includecompiler:               gcccflags:                 -g -O2ldflags:documentation:          notests:                  yes

整个过程需要1分钟左右的时间,等待运行完成之后,会发现在当前目录下多了一些文件,主要是config.hMakefile

如果想使用libmodbus官方提供的测试代码,可以直接在根目录执行make命令,就可以直接编译tests目录下的测试代码,Linux系统可以使用make install命令进行和安装。

3.编写测试代码

新建一个文件夹my_test,把libmodbus/src文件夹中的.c和.h文件,config.h复制到my_test。

学习了libmodbus常用函数之后,我们就可以写一个简单的测试代码了。

Modbus-RTU主机测试:test_rtu_master.c,实现对地址为1的从机设备,读取地址15/16/17的保持寄存器数据,进行+1操作后,再写入。

#include "stdio.h"
#include "stdlib.h"
#include "string.h"#include "modbus.h"#define PORT_NAME "COM1"int main(int argc, char *argv[])
{int ret;uint16_t table[3];modbus_t *mb;char port[20];printf("argc = %d, argv[1] = %s\n", argc, argv[1]);if(argc == 2)strcpy(port, argv[1]);else strcpy(port, PORT_NAME);printf("libmodbus modbu-rtu master demo: %s, 115200, N, 8, 1\n", port);mb = modbus_new_rtu(port, 115200, 'N', 8, 1);if (mb == NULL){modbus_free(mb);printf("new rtu failed: %s\n", modbus_strerror(errno));return 0;}modbus_set_slave(mb, 1);ret = modbus_connect(mb);if(ret == -1){modbus_close(mb);modbus_free(mb);printf("connect failed: %s\n", modbus_strerror(errno));return 0;}while(1){ret = modbus_read_registers(mb, 0x0F, 3, table);if(ret == 3)printf("read success : 0x%02x 0x%02x 0x%02x \n", table[0], table[1], table[2]);else{printf("read error: %s\n", modbus_strerror(errno));break;}for(int i = 0; i < 3; i++)table[i] += 1;ret = modbus_write_registers(mb, 0x0F, 3, table);if(ret == 3)printf("write success: 0x%02x 0x%02x 0x%02x \n", table[0], table[1], table[2]);else{printf("write error: %s\n", modbus_strerror(errno));break;}Sleep(1000);}modbus_close(mb);modbus_free(mb);system("pause");return 0;
}

Modbus-RTU从机测试:test_rtu_slave.c,创建从机设备,地址为1,初始化了3个保持寄存器,地址分别为15/16/17,数据分别为0x1001/0x1002/0x1003。

#include "stdio.h"
#include "stdlib.h"
#include "string.h"#include "modbus.h"#define PORT_NAME "COM2"int main(int argc, char *argv[])
{int ret = 0;uint8_t device = 1;uint8_t *query;modbus_t *mb;modbus_mapping_t *mb_mapping;char port[20];printf("argc = %d, argv[1] = %s\n", argc, argv[1]);if(argc == 2)strcpy(port, argv[1]);else strcpy(port, PORT_NAME);printf("libmodbus modbu-rtu slave demo: %s, 115200, N, 8, 1\n", port);mb = modbus_new_rtu(port, 115200, 'N', 8, 1);if (mb == NULL){modbus_free(mb);printf("new rtu failed: %s\n", modbus_strerror(errno));return 0;}//register: 15/16/17mb_mapping = modbus_mapping_new_start_address(0, 0, 0, 0, 15, 3, 0, 0);if(mb_mapping == NULL){modbus_free(mb);printf("new mapping failed: %s\n", modbus_strerror(errno));return 0;}//保持寄存器数据mb_mapping->tab_registers[0] = 0x1001;mb_mapping->tab_registers[1] = 0x1002;mb_mapping->tab_registers[2] = 0x1003;modbus_set_slave(mb, device);ret = modbus_connect(mb);if(ret == -1){modbus_free(mb);printf("connect failed: %s\n", modbus_strerror(errno));return 0;}printf("create modbus slave success\n");while(1){do {ret = modbus_receive(mb, query);    //轮询串口数据,} while (ret == 0);if(ret > 0) //接收到的报文长度{printf("len=%02d: ", ret);for(int idx = 0; idx < ret; idx++){printf(" %02x", query[idx]);}printf("\n");modbus_reply(mb, query, ret, mb_mapping);}else{printf("quit the loop: %s", modbus_strerror(errno));modbus_mapping_free(mb_mapping);break;}}modbus_close(mb);modbus_free(mb);return 0;
}

4.编译测试代码

现学了Makefile语法,凑合用。需要注意的是,windows下libmodbus依赖于ws2_32.dll库,需要添加编译参数-lws2_32:

.PHONY: allall: test_rtu_master test_rtu_slave
test_rtu_master : test_rtu_master.o modbus.o modbus-tcp.o modbus-rtu.o modbus-data.o gcc test_rtu_master.o modbus.o modbus-tcp.o modbus-rtu.o modbus-data.o -o test_rtu_master -lws2_32
test_rtu_slave : test_rtu_slave.o modbus.o modbus-tcp.o modbus-rtu.o modbus-data.o gcc test_rtu_slave.o modbus.o modbus-tcp.o modbus-rtu.o modbus-data.o -o test_rtu_slave -lws2_32test_rtu_slave.o : test_rtu_slave.cgcc test_rtu_slave.c -c -I.
test_rtu_master.o : test_rtu_master.cgcc test_rtu_master.c -c -I.
modbus.o : modbus.cgcc modbus.c -c -I.
modbus-rtu.o : modbus-rtu.cgcc modbus-rtu.c -c -I.
modbus-tcp.o : modbus-tcp.cgcc modbus-tcp.c -c -I.
modbus-data.o : modbus-data.cgcc modbus-data.c -c -I.clean:rm -rf *.o *.exe

最终的文件目录:

Windows下Make工具我使用的是Qt自带的mingw32-make.exe工具,位于\Qt5.7.0\Tools\mingw530_32\bin目录下,执行mingw32-make命令进行,会对两个测试文件进行编译:

whik@Windows_7 MINGW64 /d/my_test
$ mingw32-make.exe
gcc test_rtu_master.c -c -I.
gcc modbus.c -c -I.
gcc modbus-tcp.c -c -I.
gcc modbus-rtu.c -c -I.
gcc modbus-data.c -c -I.
In file included from modbus-data.c:24:0:
./config.h:171:0: warning: "WINVER" redefined#define WINVER 0x0501^
In file included from D:/Program/Qt5.7.0/Tools/mingw530_32/i686-w64-mingw32/include/windows.h:10:0,from D:/Program/Qt5.7.0/Tools/mingw530_32/i686-w64-mingw32/include/winsock2.h:23,from modbus-data.c:19:
D:/Program/Qt5.7.0/Tools/mingw530_32/i686-w64-mingw32/include/sdkddkver.h:162:0: note: this is the location of the previous definition#define WINVER  _WIN32_WINNT^
gcc test_rtu_master.o modbus.o modbus-tcp.o modbus-rtu.o modbus-data.o -o test_rtu_master -lws2_32
gcc test_rtu_slave.c -c -I.
gcc test_rtu_slave.o modbus.o modbus-tcp.o modbus-rtu.o modbus-data.o -o test_rtu_slave -lws2_32

会在当前目录下生成目标文件:test_rtu_master.exetest_rtu_slave.exe

这里,我的电脑本机虚拟了两个串口COM1和COM2,两个串口直接进行连接。

先启动从机设备,配置为COM1:

$ ./test_rtu_slave.exe "COM1"

再启动主机设备,配置为COM2:

$ ./test_rtu_master.exe "COM2"

可以看到,从机可以正确的对接收的数据帧进行相应,主机可以正确的进行读取和写入。

如果需要测试Modbus-TCP,只需要修改modbus设备创建函数:

//modbus-rtu
mb = modbus_new_rtu(port, 115200, 'N', 8, 1);//modbus-tcp
mb = modbus_new_tcp("127.0.0.120", 502); //指定IP地址

其他无需任何改动!

Linux平台下libmodbus使用

Ubuntu下使用libmodbus和Windows几乎一样:

//1.解压
tar -zxvf libmodbus-3.0.6.tar.gz//2.配置
./configure//3.编译
make//4.安装
make install

测试文件和Windows几乎一样,不过不需要ws2_32库的支持了。

(来自:blog.csdn.net/qq_30650153/article/details/83385626

ARM平台下libmodbus使用

ARM开发板下使用libmodbus,需要使用交叉编译器进行交叉编译,生成so库文件。

1.解压:

tar -zxvf libmodbus-3.0.6.tar.gz

2.创建安装目录:

mkdir install

3.配置编译选项:

./configure --host=arm-fsl-linux-gnueabi --enable-static --prefix=[安装路径]/install/

4.编译:make

5.安装:make install

在install目录会生成3个文件夹:include lib share

进入install/lib目录,执行file libmodbus*,出现如下打印信息,信息中有“ARM”说明libmodbus库移植成功。

libmodbus.a:        current ar archive
libmodbus.la:       libtool library file,
libmodbus.so:       symbolic link to `libmodbus.so.5.0.5'
libmodbus.so.5:     symbolic link to `libmodbus.so.5.0.5'
libmodbus.so.5.0.5: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, not stripped

libmodbus.so、libmodbus.so.5、libmodbus.so.5.0.5复制到ARM开发板中的/usr/lib目录下

执行cp libmodbus.so* /usr/lib

如果出现无法创建的问题(cannot create ‘/usr/lib/libmodbus.so*’: Read-only file system)。

可以执行 wr cp libmodbus* /usr/lib

测试与使用,和Windows一样,对测试文件使用ARM交叉编译器进行编译。

(来自:www.cnblogs.com/happybirthdaytoyou/p/11301612.html)

libmodbus 从机地址限制的问题

libmodbus支持1-247从机地址,0为广播地址,但是有些非标准的Modbus传感器,并不是采用0作为广播地址,而是0xfe作为广播地址:

所以使用libmodbus会出现报错终止运行的问题,这是因为libmodbus源代码中限制了从机地址1-247,我们只需要修改源代码即可。

modbus-rtu.c文件95行:

modbus-tcp.c文件80行:

只需要修改这两个数值就可以取消从机地址限制的问题。

详细的从站最大地址限制问题排查记录,可以查看:

https://blog.csdn.net/qingzhuyuxian/article/details/80391553

其实这个问题,早在2011年,就有人在官方GitHub仓库提Issues了:

https://github.com/stephane/libmodbus/issues/38

对此问题,作者的答复是,为了遵循Modbus官方标准,所以一直以来都没有进行修改。

Modbus驱动库—libmodbus驱动库的使用相关推荐

  1. MODBUS通讯:libmodbus库使用方法

    MODBUS通讯:libmodbus库使用方法 libmodbus是一个快速.跨平台的Modbus库,目前支持Linux, Mac OS X, FreeBSD, QNX和Windows系统.libmo ...

  2. python硬件驱动_从零开始:手把手教你安装深度学习操作系统、驱动和各种python库!...

    原标题:从零开始:手把手教你安装深度学习操作系统.驱动和各种python库! 为了研究强化学习,最近购置了一台基于 Ubuntu 和英伟达 GPU 的深度学习机器.尽管目前在网络中能找到一些环境部署指 ...

  3. ESP8266-Arduino编程实例-ILI9341-TFT LCD驱动(基于TFT_eSPI库)

    ILI9341-TFT LCD驱动(基于TFT_eSPI库) 液晶显示器 (LCD) 是一种平板显示器或其他电子调制光学设备,它利用液晶与偏振器的光调制特性.液晶不直接发光,而是使用背光或反射器来产生 ...

  4. 【STM32学习】(29)STM32实现595驱动三个数码管(标准库和HAL库实现)

    我选用的单片机型号为:STM32F103系列 74LS595是一个串转并行输出的芯片,它能为单片机节省很多的IO口,应用场景广泛. 现要求三个数码管动态显示,常规设计都是并行实现,需要8个IO数据口, ...

  5. 【开源项目介绍】STC32基于u8g2库DMA驱动IIC or SPI OLED屏幕显示

    [开源项目介绍]STC32基于u8g2库DMA驱动IIC or SPI OLED屏幕显示

  6. mysql 向下兼容_前言本文主要介绍的是关于Mysql8.0驱动getTables返回所有库的表的相关内容,MySQL Connector/J 8.0版本驱动向下兼容之前的5.5...

    前言 本文主要介绍的是关于Mysql8.0驱动getTables返回所有库的表的相关内容,MySQL Connector/J 8.0版本驱动向下兼容之前的5.5+版本MySQL,如果你使用的是5.5+ ...

  7. 乾坤合一~Linux设备驱动之终端设备驱动

    多想拥你在我的怀里 却无法超越那距离 美好回忆渐渐地远去 盼望今生出现奇迹 无尽的想念 荒了容颜 无助的爱恋 从未改变 这是今天的旋律,,,,今生今世,遥不可及~ 1 终端设备 终端是一种字符型设备, ...

  8. python第三方库排行-140种Python标准库、第三方库和外部工具

    导读:Python数据工具箱涵盖从数据源到数据可视化的完整流程中涉及到的常用库.函数和外部工具.其中既有Python内置函数和标准库,又有第三方库和工具. 这些库可用于文件读写.网络抓取和解析.数据连 ...

  9. python第三方库大全win-Python标准库、第三方库和外部工具汇总

    导读:Python数据工具箱涵盖从数据源到数据可视化的完整流程中涉及到的常用库.函数和外部工具.其中既有Python内置函数和标准库,又有第三方库和工具. 这些库可用于文件读写.网络抓取和解析.数据连 ...

  10. Windows网络驱动、NDIS驱动(微端口驱动、中间层驱动、协议驱动)、TDI驱动(网络传输层过滤)、WFP(Windows Filtering Platfrom))

    catalog 0.引言 1.Windows 2000网络结构和OSI模型 2.NDIS驱动 3.NDIS微端口驱动编程实例 4.NDIS中间层驱动编程实例 5.TDI驱动 6.TDI驱动 7.TDI ...

最新文章

  1. sleep() wait() notify/notifyAll() 的区别
  2. API读取写入 ini文件内容的方法函数详解
  3. ob_clean与gzip的bug
  4. Guru of the Week 条款01: 变量的初始化
  5. AndroidManifest.xml清单文件要点
  6. 计算机组成原理刘红玲,计算机组成原理/21世纪高等学校规划教材
  7. Spark-shell 脚本批量执行命令,命令行批量执行命令
  8. 用于指纹验证的C#框架
  9. SQL语句如何判断某字段是以字母开头而不是汉字开头
  10. 【2011-2012 ACM-ICPC Northeastern European Regional Contest (NEERC 11) G】GCD Guessing Game【数论思维题】
  11. 【项目管理一点通】(13) 如何写周报和日报
  12. PPT排版细节,写给大家看的设计书,完美总结
  13. 深度学习面试题2018
  14. 小米笔记本pro充电测试软件,小米笔记本 Pro 评测:高端已成,性价比不变
  15. 黄仁勋口述:英伟达的发展之道和星辰大海
  16. 广东省高中计算机学校图片,广东省排名前十的高中,快来看有你的母校吗?
  17. UOS 操作蓝牙、wifi开关
  18. 千人规模互联网公司研发效能成功之路
  19. Django练习——基于Mysql的登录注册功能界面(django模型、模板、表单简单应用)
  20. 基于ssm的田园管理系统

热门文章

  1. forge是用java装吗_我的世界forge怎么安装 forge使用方法
  2. Juniper交换机配置命令_学习笔记
  3. 幼儿园连锁管理系统源码
  4. 二元序列游程编码c语言,基于游程编码数据压缩算法设计与实现.doc
  5. SECS Message解析说明
  6. 单语种语料库 平行语料库 多语种语料库 可比语料库
  7. kindle电子书转换成pdf azw转pdf
  8. 设计模式之简单工厂模式和抽象工厂模式
  9. java判断字符是否为0_Java判断字符串是否为空
  10. Centos7安装masscan