libmodbus学习(一)之Modbus RTU-master

一、Modbus 协议

Modbus簇主要有三种协议:Modbus-RTU、Modbus-ASCII、Modbus-TCP
工控行业通常的设备组网看,三者应用的范围如下:

image.png

三者模型区别如下:
image.png

如上图所示,串行传输的物理层是RS-485或RS-232,数据链路层是Modbus的串行传输协议;Modbus TCP传输的1、2、3、4层实现和日常所见的以太网、因特网一样,Modbus默认采用的TCP端口号是502。

三者报文格式的区别如下:


image.png

RTU相比较ASCII具备更紧凑的报文流,传输效率更高,目前MODBUS-ASCII已经应用较少。

Modbus的操作对象有4种:线圈、离散输入、保持寄存器、输入寄存器

image.png

常用功能码如下:
image.png

二、安装移植libmodbus

libmodbus安装编译见:
https://www.cnblogs.com/bliss-/p/12376424.html

三、libmodbus API

1 .初始化
/* 
  以TCP的方式创建libmobus实例
  char *ip:连接的IP地址
  int port: 连接的IP端口
*/
modbus_t *modbus_new_tcp(const char *ip, int port);

/* 
  以串口的方式创建libmobus实例
  onst char *device:连接的串口号,类似是这样'\\\\.\\COM10'
  int baud: 波特率
  char parity:奇偶校验
  int data_bit:数据位
  int stop_bit:停止位
*/
modbus_t *modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit);

/* 
  释放libmodbus实例,使用完libmodbus需要释放掉
  modbus_t *ctx:libmodbus实例
*/
void modbus_free(modbus_t *ctx);
2.读取
/* 
  读取线圈状态,可读取多个连续线圈的状态
  modbus_t *ctx:Modbus实例
  int addr: 线圈地址
  int nb:读取线圈的个数
  uint8_t *dest: 传出的状态值
*/
int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);

/* 
  读取输入状态,可读取多个连续输入的状态
  modbus_t *ctx:Modbus实例
  int addr:输入地址
  int nb:读取输入的个数
  uint8_t *dest:传出的状态值
*/
int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);

/* 
  读取输入寄存器的值,可读取多个连续输入输入寄存器
  modbus_t *ctx:Modbus实例
  int addr:输入地址
  int nb:读取输入寄存器的个数
  uint8_t *dest:传出的寄存器值
*/
int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);

/* 
  读取保持寄存器的值,可读取多个连续输入保持寄存器
  modbus_t *ctx:Modbus实例
  int addr:输入地址
  int nb:读取保持寄存器的个数
  uint8_t *dest:传出的寄存器值
*/
int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
3.写入
/*
  写入单个线圈的状态
  modbus_t *ctx:Modbus实例
  int addr:线圈地址
  int status:线圈状态
*/
int modbus_write_bit(modbus_t *ctx, int addr, int status);

/*
  写入多个连续线圈的状态
  modbus_t *ctx:Modbus实例
  int addr:线圈地址
  int nb:线圈个数
  const uint8_t *src:多个线圈状态
*/
int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);

/*
  写入单个寄存器
  modbus_t *ctx:Modbus实例
  int addr:寄存器地址
  int value:寄存器的值 
*/
int modbus_write_register(modbus_t *ctx, int addr, int value);

/*
  写入多个连续寄存器
  int addr:寄存器地址
  int nb:寄存器的个数
  const uint16_t *src:多个寄存器的值 
*/
int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);

四、主从站通信流程

image.png

五、RTU 测试程序

modbus_rtu.c

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "modbus/modbus.h"

int main()
{
    modbus_t *mb;
    uint8_t tab_coil[10] = {0};
    uint8_t tab_input_coil[10] = {0};
    uint16_t tab_reg[10] = {0};
    uint16_t tab_input_reg[10] = {0};

    int i;
    int coils,input_coils,regs,input_regs;

    //打开端口
    mb = modbus_new_rtu("/dev/ttymxc2",9600,'N',8,1);
    //设置从地址 为1
    modbus_set_slave(mb,1);
    //建立链接
    modbus_connect(mb);

    int num = 0;//读取次数
    while (1)
    {
        memset(tab_coil,0,10*1);
        memset(tab_input_coil,0,10*1);
        memset(tab_reg,0,10*2);
        memset(tab_input_reg,0,10*2);
        
        //读取线圈状态  //寄存器地址、数量、数据缓存
        coils = modbus_read_bits(mb,100,1,tab_coil);
        usleep(100);    
        //读取输入线圈状态  //寄存器地址、数量、数据缓存
        input_coils = modbus_read_input_bits(mb,200,1,tab_input_coil);
        usleep(100);
        //读取保持寄存器    //寄存器地址、数量、数据缓存
        regs = modbus_read_registers(mb,100,5,tab_reg);
        usleep(100);
        //读取输入寄存器    //寄存器地址、数量、数据缓存
        input_regs = modbus_read_input_registers(mb,100,6,tab_input_reg); 
        
        //写线圈状态 1
        modbus_write_bit(mb,100,1);
        printf("----------------------------------------------------\n");
        /*线圈数据*/
        printf("[%4d][read coils num = %d]",num,coils);
        for(i=0;i<1;i++)
        {
            printf("<%#x>",tab_coil[i]);
        }
        printf("\n");

        /*输入线圈数据*/
        printf("[%4d][read input coils num = %d]",num,input_coils);
        for(i=0;i<1;i++)
        {
            printf("<%#x>",tab_input_coil[i]);
        }
        printf("\n");

        /*保持寄存器数据*/
        printf("[%4d][read registers num = %d]",num,regs);
        for(i=0;i<5;i++)
        {
            printf("<%#x>",tab_reg[i]);
        }
        printf("\n");

        /*输入寄存器数据*/
        printf("[%4d][read input registers num = %d]",num,input_regs);
        num++;
        for(i=0;i<6;i++)
        {
            printf("<%#x>",tab_input_reg[i]);
        }
        printf("\n");

        printf("----------------------------------------------------\n");
        
        sleep(1);
        //写线圈状态 0
        modbus_write_bit(mb,100,0);
    }

    //关闭modbus端口
    modbus_close(mb);

    //释放modbus资源
    modbus_free(mb);

    return 0;
}

程序流程图


image.png

六、报文分析

1.读多个保持寄存器(功能码:03)

主站请求:01 03 00 64 00 05 c4 16
01从地址
03功能码:读多个保持寄存器
00 64寄存器起始地址:100
00 05 寄存器个数:5 (16bit,若为32bit则算2个)
c4 16 CRC校验码
从站响应:01 03 0a 00 00 00 01 04 9f 00 00 2a f8 bf 0c
01从地址
03功能码:读多个寄存器
0a数据大小:10byte
00 00
00 01
04 9f
00 00 2a f8 b
bf 0c CRC校验码
对应测试数据

image.png

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 电力系统是一个综合化的系统,作为一个熟练的电工,对于通信有着一定的认识。否则很多问题,我们将无从下手。首先我们从广...
    洪城小电工阅读 118,332评论 8 34
  • 一、背景 什么是ModbusModbus是在1970年末为可编程逻辑控制器通信开发的,Modbus是一种串行通信协...
    卜俊文阅读 29,340评论 10 12
  • 一、Modbus 协议简介 Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议,控制器相互之间、控制...
    lx_jian阅读 46,146评论 2 14
  • 一、MODBUS 工业上常用的一种串口通讯协议,协议包括RTU、TCP、ASCII;其中MODBUS RTU协议最...
    葬歌倾城阅读 1,780评论 0 2
  • 一、Modbus 协议简介 ModBus网络是一个工业通信系统,由带智能终端的可编程序控制器和计算机通过公用线路或...
    漠漠彡阅读 93,972评论 2 21