freemodbus移植、实例及其测试方法
Modbus简介
参考:Modbus协议深入讲解 https://www.ni.com/zh-cn/innovations/white-papers/14/the-modbus-protocol-in-depth.html
http://www.sohu.com/a/230628953_315598
官方文档:http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf
Modbus协议包括ASCII、RTU、TCP等,并没有规定物理层。此协议定义了控制器能够认识和使用的消息结构,而不管它们是经过何种网络进行通信的。标准的Modicon控制器使用RS232C实现串行的Modbus。Modbus的ASCII、RTU协议规定了消息、数据的结构、命令和就答的方式,数据通讯采用Maser/Slave方式,Master端发出数据请求消息,Slave端接收到正确消息后就可以发送数据到Master端以响应请求;Master端也可以直接发消息修改Slave端的数据,实现双向读写。
其通信协议理解也比较简单,其帧结构如下
地址码 + 功能码 + 数据区 + 错误校验
常用功能码
最常使用的读写功能码如下;
01 读取单个/多个线圈状态(类似DO:数字输出)
02 读取单个/多个离散输入(类似DI:数字输入)
03 读取单个/多个保存寄存器
04 读取单个/多个输入寄存器(类似AI:模拟输入)
05 写单个线圈状态
06 写单个保存寄存器
15 写多个线圈
16 写多个保存寄存器
FreeModbus简析
FreeMODBUS 提供了RTU/ASCII 传输模式及TCP协议支持。
FreeModbus协议对硬件的需求非常少——基本上任何具有串行接口,并且有一些能够容纳modbus数据帧的RAM的微控制器都足够了。
现支持如下功能码:
- Read Input Register (0x04)
- Read Holding Registers (0x03)
- Write Single Register (0x06)
- Write Multiple Registers (0x10)
- Read/Write Multiple Registers (0x17)
- Read Coils (0x01)
- Write Single Coil (0x05)
- Write Multiple Coils (0x0F)
- Read Discrete Inputs (0x02)
- Report Slave ID (0x11)
FreeModbus源码解析
参考:https://blog.csdn.net/u014748120/article/details/80313215
待续。。。
Modbus通信实现
本文测试主机为 arm-linux,可以使用 libmodbus 静态库实现modbus通信,为追求可移植性,本文主要使用 freemodbus 来实现。
1. libmodbus使用
ubuntu系统使用libmodbus可以使用以下命令安装
sudo apt-get install libmodbus-dev # 或者 sudo apt-get install libmodbus5
使用文档参考:https://libmodbus.org/documentation/
库参考手册:https://libmodbus.org/docs/v3.1.4/
移植到arm的话则需下载源码进行交叉编译 http://libmodbus.org/releases/libmodbus-3.1.4.tar.gz
解压安装
tar -xzvf libmodbus-3.1.4.tar.gz cd libmodbus-3.1.4 # 新建安装文件夹 mkdir -p install chmod 777 install ./configure --prefix=$(pwd)/install --host=arm-linux --enable-static ac_cv_func_malloc_0_nonnull=yes CC=arm-fsl-linux-gnueabi-gcc
make
make install
在我的应用程序工程里面新建一个 libmodbus 文件夹,将上面安装 install目录下的 include和lib文件夹拷贝过来
我的工程总体结构如下所示
在 mys_src 里面添加 modbus 主机测试程序 modbus_test.c
makefile编写如下所示:
#编译配置,使能为1 CONFIG_MODBUS_BUILD = 1 #当前路径 CUR_DIR := $(shell pwd) #libmodbus目录 LIBMODBUS_DIR := $(CUR_DIR)/../libmodbus # 头文件路径 INCLUDE := INCLUDE += -I$(CUR_DIR)/../include/ ifeq ($(CONFIG_MODBUS_BUILD), 1) INCLUDE += -I$(LIBMODBUS_DIR)/include/modbus/ endif #C编译器的选项 CFLAGS := CFLAGS += -g -Wall CFLAGS += -std=gnu99 CFLAGS += $(INCLUDE) #库文件参数 LDFLAGS := #libmodbus共享库链接 #LDFLAGS += -L$(LIBMODBUS_DIR)/lib #libmodbus静态库链接 ifeq ($(CONFIG_MODBUS_BUILD), 1) LDFLAGS += $(LIBMODBUS_DIR)/lib/libmodbus.a endif SRCS += modbus_test.c OBJS += modbus_test.o BINS += modbus_test all:$(OBJS) $(BINS) $(OBJS):%.o:%.c $(CC) -c $(CFLAGS) $^ -o $(OBJ_DIR)/$@ $(BINS):$(OBJS) $(CC) -o $(BIN_DIR)/$@ $(OBJ_DIR)/$^ $(LDFLAGS)
modbus_test.c
#include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <modbus.h> #define MODBUS_DEV_NAME "/dev/ttymxc3" ///< 串口设备 int main(void) { modbus_t *ctx =NULL; // 以串口的方式创建libmobus实例,并设置参数 ctx = modbus_new_rtu(MODBUS_DEV_NAME, 115200, 'N', 8, 1); if (ctx == NULL) //使用UART4,对应的设备描述符为ttymxc3 { fprintf(stderr, "Unable to allocate libmodbus contex\n"); return -1; } // 使用RS485时需考虑设置串口模式、RTS引脚等 // modbus_rtu_set_serial_mode(MODBUS_RTU_RS485); //设置串口模式 modbus_set_debug(ctx, 1); //设置1可看到调试信息 modbus_set_slave(ctx, 1); //设置slave ID if (modbus_connect(ctx) == -1) //等待连接设备 { fprintf(stderr, "Connection failed:%s\n", modbus_strerror(errno)); return -1; } int i,rc; uint16_t tab_reg[64] = {0}; //定义存放数据的数组 while (1) { printf("\n----------------\n"); //读取保持寄存器的值,可读取多个连续输入保持寄存器 rc = modbus_read_registers(ctx, 0, 10, tab_reg); if (rc == -1) { fprintf(stderr,"%s\n", modbus_strerror(errno)); return -1; } for (i=0; i<10; i++) { printf("reg[%d] = %d(0x%x)\n", i, tab_reg[i], tab_reg[i]); } usleep(5000000); } modbus_close(ctx); //关闭modbus连接 modbus_free(ctx); //释放modbus资源,使用完libmodbus需要释放掉 return 0; }
编译之后可通过nfs挂载进行测试
modbus从机模拟
Modbus slave测试工具可以用来做modbus从机设备,从而实现arm控制板通过串口与PC端模拟的modbus从机进行通信测试。
Modbus slave下载地址:https://www.modbustools.com/download/ModbusSlaveSetup64Bit.exe
然后设置 setup->slave definition 从机ID、设置为保存寄存器,10条
同时我们也给寄存器设置一些值
开发板运行测试程序后,成功读取modbus从机寄存器值
2.freemodbus在linux上的使用
待续。。。