Modbus协议学习第六篇之基于libmodbus库的示例程序(可以联合Modbus模拟仿真软件进行调试)

前置工作

        学了这么多Modbus的知识,如果不进行实际的操作,总感觉懂的不透彻。基于此, 本篇博文就带各位读者来了解下如何通过编写程序来模拟与Modbus Slave仿真软件的通讯。当然了,这里有两个前提,如下:

        1.请确保读者跟随我的第五篇博文进行了同等的操作,编译生成了modbus库。第五篇博文地址:libmodbus库的编译

        2.使用VSPD创建一对串口:COM3和COM4。可参考:Modbus poll & slave仿真软件初体验

具体步骤

代码侧具体步骤

        1.启动Visual Studio,创建一个新的工程项目:【File】→【New】→【Project】。在弹出的新建对话框左边中选择【Visual C++】→【Win32 Console Application】项,输入你自定义的应用程序名,设置完成后单击【确定】按钮。继续点击下一步后,具体项目配置勾选如下图:

Modbus协议学习第六篇之基于libmodbus库的示例程序(可以联合Modbus模拟仿真软件进行调试)_第1张图片

        2.工程创建完成后,找到第五篇博客中编译的libmodbus库文件,将之前生成的lib和dll文件以及几个必要的头文件都复制到本项目所在的目录,如图:

Modbus协议学习第六篇之基于libmodbus库的示例程序(可以联合Modbus模拟仿真软件进行调试)_第2张图片

        3.来到VS的主界面,在项目下的文件夹【源文件】上右键,选择 【添加】→【现有项】,在弹出的对话框中,选中modbus.h和modbus.lib文件(按住ctrl键多选),再点击【添加】按钮将这两个文件添加进项目中,如下图所示:

Modbus协议学习第六篇之基于libmodbus库的示例程序(可以联合Modbus模拟仿真软件进行调试)_第3张图片

        4.添加完之后,我们就可以使用libmodbus库中的各种接口函数了。接下来,我们在【源文件】文件夹上右键,【添加】→【新建项】,添加自己的Demo程序,暂且命名为SimRtuMaster.cpp吧,如下图:

Modbus协议学习第六篇之基于libmodbus库的示例程序(可以联合Modbus模拟仿真软件进行调试)_第4张图片

        5.现在就可以在SimRtuMaster.cpp中编写程序了,在此将代码分享如下:

#include 
#ifndef _MSC_VER
#include 
#endif

#include 
#include 
#include 

#include "modbus.h"

#define LOOP           1    //循环次数
#define SERVER_ID     17    //从端设备地址
#define ADDRESS_START  0    //测试寄存器起始地址
#define ADDRESS_END   99    //测试寄存器结束地址

int main(void) {
	modbus_t *ctx;
	int rc;
	int nb_fail;
	int nb_loop;
	int addr;
	int nb;
	uint8_t *tab_rq_bits;		// 用于保存发送或接收的数据
	uint8_t *tab_rp_bits;
	uint16_t *tab_rq_registers;
	uint16_t *tab_rp_registers;
	uint16_t *tab_rw_rq_registers;

	// RTU
	ctx = modbus_new_rtu("COM3", 19200, 'N', 8, 1);		// 创建一个RTU类型的容器
	modbus_set_slave(ctx, SERVER_ID);					// 设置从端地址

	modbus_set_debug(ctx, TRUE);							// 设置debug模式

	if (modbus_connect(ctx) == -1)		// 建立连接
	{
		fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
		modbus_free(ctx);
		return -1;
	}

	nb = ADDRESS_END - ADDRESS_START;					// 计算需要测试的寄存器个数

	// 以下申请内存块用来保存发送和接收各数据
	tab_rq_bits = (uint8_t *)malloc(nb * sizeof(uint8_t));
	memset(tab_rq_bits, 0, nb * sizeof(uint8_t));

	tab_rp_bits = (uint8_t *)malloc(nb * sizeof(uint8_t));
	memset(tab_rp_bits, 0, nb * sizeof(uint8_t));

	tab_rq_registers = (uint16_t *)malloc(nb * sizeof(uint16_t));
	memset(tab_rq_registers, 0, nb * sizeof(uint16_t));

	tab_rp_registers = (uint16_t *)malloc(nb * sizeof(uint16_t));
	memset(tab_rp_registers, 0, nb * sizeof(uint16_t));

	tab_rw_rq_registers = (uint16_t *)malloc(nb * sizeof(uint16_t));
	memset(tab_rw_rq_registers, 0, nb * sizeof(uint16_t));

	nb_loop = nb_fail = 0;
	while (nb_loop++ < LOOP)
	{
		// 从起始地址开始顺序测试
		for (addr = ADDRESS_START; addr < ADDRESS_END; addr++)
		{
			int i;

			// 生成随机数, 用于测试
			for (i = 0; i < nb; i++) {
				tab_rq_registers[i] = (uint16_t)(65535.0 * rand() / (RAND_MAX + 1.0));
				tab_rw_rq_registers[i] = ~tab_rq_registers[i];
				tab_rq_bits[i] = tab_rq_registers[i] % 2;
			}

			nb = ADDRESS_END - addr;

			// 测试线圈寄存器的单个读写
 			rc = modbus_write_bit(ctx, addr, tab_rq_bits[0]);			// 写线圈寄存器
			if (rc != 1)
			{
				printf("ERROR modbus_write_bit (%d) \n", rc);
				printf("Address = %d, value = %d\n", addr, tab_rq_bits[0]);
				nb_fail++;
			}
			else
			{
				// 写入之后,再读取并比较
				rc = modbus_read_bits(ctx, addr, 1, tab_rp_bits);
				if (rc != 1 || tab_rq_bits[0] != tab_rp_bits[0])
				{
					printf("ERROR modbus_read_bits single (%d) \n", rc);
					printf("address = %d\n", addr);
					nb_fail++;
				}
			}

			// 测试线圈寄存器的批量读写
			rc = modbus_write_bits(ctx, addr, nb, tab_rq_bits);
			if (rc != nb) {
				printf("ERROR modbus_write_bits (%d) \n", rc);
				printf("Address = %d, nb = %d\n", addr, nb);
				nb_fail++;
			}
			else {
				// 写入之后,再读取并比较
				rc = modbus_read_bits(ctx, addr, nb, tab_rp_bits);
				if (rc != nb) {
					printf("ERROR modbus_read_bits\n");
					printf("Address = %d, nb = %d\n", addr, nb);
					nb_fail++;
				}
				else {
					// 进行比较
					for (i = 0; i < nb; i++) {
						if (tab_rp_bits[i] != tab_rq_bits[i])
						{
							printf("ERROR modbus_read_bits\n");
							printf("Addr = %d, Val = %d (0x%X) != %d (0x%X\n", addr, tab_rq_bits[i], tab_rq_bits[i], tab_rp_bits[i], tab_rp_bits[i]);
							nb_fail++;
						}
					}
				}
			}

			// 测试保持寄存器的单个读写
			rc = modbus_write_register(ctx, addr, tab_rq_registers[0]);
			if (rc != 1) {
				printf("ERROR modbus_write_register (%d) \n", rc);
				printf("Addr = %d, Val = %d (0x%X) \n", addr, tab_rq_registers[0], tab_rq_registers[0]);
				nb_fail++;
			}
			else {
				// 写入后进行读取
				rc = modbus_read_registers(ctx, addr, 1, tab_rp_registers);
				if (rc != 1) {
					printf("ERROR modbus_write_registers (%d) \n", rc);
					printf("Address = %d\n", addr);
					nb_fail++;
				}
				else {
					// 读取后进行比较
					if (tab_rq_registers[0] != tab_rp_registers[0]) {
						printf("ERROR modbus_write_registers\n");
						printf("Addr = %d, Val = %d (0x%X) != %d (0x%X\n", addr, tab_rq_registers[i], tab_rq_registers[i], tab_rp_registers[i], tab_rp_registers[i]);
						nb_fail++;
					}
				}
			}

			// 测试线圈寄存器的批量读写
			rc = modbus_write_registers(ctx, addr, nb, tab_rq_registers);
			if (rc != nb) {
				printf("ERROR modbus_write_registers (%d) \n", rc);
				printf("Address = %d, nb = %d\n", addr, nb);
				nb_fail++;
			}
			else {
				// 进行读取测试
				rc = modbus_read_registers(ctx, addr, nb, tab_rp_registers);
				if (rc != nb) {
					printf("ERROR modbus_read_registers (%d) \n", rc);
					printf("Address = %d, nb = %d\n", addr, nb);
					nb_fail++;
				}
				else
				{
					for ( i = 0; i < nb; i++)
					{
						if (tab_rq_registers[i] != tab_rp_registers[i]) {
							printf("ERROR modbus_read_registers\n");
							printf("Addr = %d, Val = %d (0x%X) != %d (0x%X)\n", addr, tab_rq_registers[i], tab_rq_registers[i], tab_rp_registers[i], tab_rp_registers[i]);
							nb_fail++;
						}
					}
				}
			}

			// 功能码23(0x17)读写多个寄存器的测试
			rc = modbus_write_and_read_registers(ctx,
				addr, nb, tab_rw_rq_registers,
				addr, nb, tab_rp_registers
				);
			if (rc != nb) {
				printf("ERROR modbus_write_and_read_registers(%d)\n", rc);
				printf("Address = %d, nb = %d\n", addr, nb);
				nb_fail++;
			}
			else {
				for (i = 0; i < nb; i++) {
					if (tab_rp_registers[i] != tab_rw_rq_registers[i])
					{
						printf("ERROR modbus_read_and_write_registers READ\n");
						printf("Addr = %d, Val = %d (0x%X) != %d (0x%X)\n", addr, tab_rp_registers[i], tab_rw_rq_registers[i], tab_rp_registers[i], tab_rw_rq_registers[i]);
						nb_fail++;
					}
				}

				rc = modbus_read_registers(ctx, addr, nb, tab_rp_registers);
				if (rc != nb) {
					printf("ERROR modbus_read_registers(%d)\n", rc);
					printf("Address = %d, nb = %d\n", addr, nb);
					nb_fail++;
				}
				else {
					for (i = 0; i < nb; i++)
					{
						if (tab_rw_rq_registers[i] != tab_rp_registers[i]) {
							printf("ERROR modbus_read_and_write_registers READ\n");
							printf("Addr = %d, Val = %d (0x%X) != %d (0x%X)\n", addr, tab_rp_registers[i], tab_rw_rq_registers[i], tab_rp_registers[i], tab_rw_rq_registers[i]);
							nb_fail++;
						}
					}
				}
			}
		}

		printf("Test: ");
		if (nb_fail) {
			printf("%d Fails\n", nb_fail);
		}
		else
		{
			printf("Success\n");
		}
	}

	// Free the memory
	free(tab_rq_bits);
	free(tab_rq_bits);
	free(tab_rq_registers);
	free(tab_rp_registers);
	free(tab_rw_rq_registers);

	// Close the connection
	modbus_close(ctx);
	modbus_free(ctx);

	return 0;
}

模拟器侧具体步骤

        1.打开Modbus Slave仿真软件(上面的代码是用于模拟Master,所以不再需要打开Modbus poll),在导航栏点击【Connection】→【Connect】,进行连接配置,注意各项配置要与代码中一致,才能保证串口通信,具体配置如下图:

Modbus协议学习第六篇之基于libmodbus库的示例程序(可以联合Modbus模拟仿真软件进行调试)_第5张图片

        2.连接设置完成之后,再在Modbus Slave主窗口中选择【File】→【New】,在下方新建的子窗口的空白处右键,弹出菜单,选择【Slave Definition】,按照下图进行配置:

Modbus协议学习第六篇之基于libmodbus库的示例程序(可以联合Modbus模拟仿真软件进行调试)_第6张图片

        3.设置完成后,新建另一个窗口,使用相同的配置,只将Function改为03,现在便可以开始测试了。

        4.回到Visual Studio面板,在编译代码之前需要修改一个配置。在VS顶部导航栏选择【工具】→【选项】,然后在【调试】下找到【符号】,将Microsoft符号服务器的勾打上(需要联网),不然有可能会运行失败,如下图:

Modbus协议学习第六篇之基于libmodbus库的示例程序(可以联合Modbus模拟仿真软件进行调试)_第7张图片

        5.右键该项目,点击生成,进行编译,编译完成后,点击启动,然后查看Modbus Slave中是否有变化,当然,你也可以在VS中打断点进行调试,一步一步理解本篇文章的代码逻辑。

写在最后

        本篇文章介绍了具体的调试过程,如果帮到了您,我将万分荣幸,还请不要吝啬您小小的点赞和收藏。当然,如果需要的话,可以将整个项目文件下载下去(下载地址),支持一下用爱发电的博主。

你可能感兴趣的:(Modbus,学习,Modbus,PLC,仿真)