Modbus学习记录(3)

本文只列举了常用的API,其他的API参考官方API文档:https://libmodbus.org/docs/v3.1.4/

初始化相关API

modbus_new_rtu()

modbus_t *modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit);

功能:
函数应该分配和初始化一个modbus_t结构,以便在串行线上以RTU模式通信。

参数:

  • device:参数指定操作系统处理的串口的名称,例如。“/ dev / ttyS0”或“/ dev /
    ttyUSB0”。
  • baud:指定通信的波特率,例如。9600、19200、57600、115200等
  • parity:奇偶校验参数可以有以下值之一: N没有 E为偶校验 O为奇校验
  • data_bits参数指定数据的位数,允许的值为5、6、7和8。
  • stop_bits参数指定stop的位,允许的值是1和2。

一旦modbus_t结构被初始化,你必须用modbus_set_slave(3)设置你的设备的从机,并用modbus_connect(3)连接到串行总线。

返回值:
如果成功,函数将返回一个指向modbus_t结构体的指针。否则,它将返回NULL并将errno设置为EINVAL(给出了一个无效参数)

例子:

modbus_t *ctx;

ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1);
if (ctx == NULL) {
    fprintf(stderr, "Unable to create the libmodbus context\n");
    return -1;
}

modbus_set_slave(ctx, YOUR_DEVICE_ID);

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

modbus_new_tcp()

modbus_t *modbus_new_tcp(const char *ip, int port);

功能:
modbus_new_tcp()函数应该分配和初始化一个modbus_t结构体来与Modbus TCP IPv4服务器通信。

参数:

  • ip指定客户端想要建立连接的服务器的ip地址。NULL值可以用来监听服务器模式下的任何地址。
  • port参数是要使用的TCP端口。将端口设置为MODBUS_TCP_DEFAULT_PORT,使用默认的502端口。使用大于或等于1024的端口号很方便,因为它不需要拥有管理员权限。

返回:
如果成功,函数将返回一个指向modbus_t结构体的指针。否则,它将返回NULL并将errno设置为EINVAL(An invalid IP address was given.)

例子:

modbus_t *ctx;

ctx = modbus_new_tcp("127.0.0.1", 1502);
if (ctx == NULL) {
    fprintf(stderr, "Unable to allocate libmodbus context\n");
    return -1;
}

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

modbus_new_tcp_pi()

modbus_t *modbus_new_tcp_pi(const char *node, const char *service);

功能:
modbus_new_tcp_pi()函数应该分配和初始化一个modbus_t结构体来与Modbus TCP IPv4或IPv6服务器通信。

参数:

  • node参数指定要连接的主机的主机名或IP地址,例如。192.168.0.5、::1或server.com。NULL值可以用来监听服务器模式下的任何地址。
  • service参数是要连接的服务名/端口号。如果使用默认的Modbus端口,请使用字符串“502”。在许多Unix系统上,使用大于或等于1024的端口号是很方便的,因为它不需要拥有管理员权限。

返回:
如果成功,函数将返回一个指向modbus_t结构体的指针。否则,它将返回NULL并将errno设置为EINVAL(节点字符串为空或已被截断。服务字符串为空或已被截断)。

例子:

modbus_t *ctx;

ctx = modbus_new_tcp_pi("::1", "1502");
if (ctx == NULL) {
    fprintf(stderr, "Unable to allocate libmodbus context\n");
    return -1;
}

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

连接相关API

建立和关闭与Modbus设备的连接提供以下功能:

modbus_connect()

int modbus_connect(modbus_t *ctx);

功能:
函数应该使用参数中给出的libmodbus上下文信息建立到Modbus服务器、网络或总线的连接。

参数:
ctx:指向modbus_t结构体的指针

返回:
如果成功,函数将返回0。否则,它将返回-1,并将errno设置为底层平台的系统调用定义的值之一。

例子:

modbus_t *ctx;

ctx = modbus_new_tcp("127.0.0.1", 502);
if (modbus_connect(ctx) == -1) {
    fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    return -1;
}

modbus_close()

void modbus_close(modbus_t *ctx);

功能:
函数应该关闭与上下文中设置的后端建立的连接。

参数:
ctx:指向modbus_t结构体的指针

无返回值

例子:

modbus_t *ctx;

ctx = modbus_new_tcp("127.0.0.1", 502);
if (modbus_connect(ctx) == -1) {
    fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    return -1;
}

modbus_close(ctx);
modbus_free(ctx);

modbus_flush()

int modbus_flush(modbus_t *ctx);

功能:
函数将丢弃接收到的数据,但不将数据读到套接字或与上下文ctx相关的文件描述符中。

参数:
ctx:指向modbus_t结构体的指针

返回:
如果成功,函数将返回0或刷新的字节数。否则返回-1并设置errno。

Client/Master中的相关API

modbus_read_bits()

int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);

功能:
modbus_read_bits()函数将从远端设备的地址addr开始读取数量为nb的bits(线圈)的状态。

参数:

  • ctx:指向modbus_t结构体的指针
  • addr:要读取的远端设备的起始地址
  • nb:读取的线圈数量
  • dest:读取的结果以无符号字节(8位)的形式存储在dest数组中

你必须注意分配足够的内存来存储结果在dest(至少nb * sizeof(uint8_t))。
该功能使用Modbus功能码0x01(读取线圈状态)。
返回:
如果成功,函数将返回读取的位数。否则返回-1并设置errno。
错误:EMBMDATA(请求的位太多)

例子:

addr = ADDRESS_START;
ret = modbus_write_bits(ctx, addr,nums, tab_rq_bits);
if (nums != ret)
{
	printf("Error modbus_write_bit: %d\n", ret);
	printf("Address: %d value: %d\n", addr, tab_rq_bits[0]);
}

modbus_read_input_bits()

int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);

功能:
modbus_read_bits()函数将从远端设备的地址addr开始读取数量为nb的bits(输入离散量)的状态。

参数:

  • ctx:指向modbus_t结构体的指针
  • addr:要读取的远端设备的起始地址
  • nb:读取的输入离散量数量
  • dest:读取的结果以无符号字节(8位)的形式存储在dest数组中

你必须注意分配足够的内存来存储结果在dest(至少nb * sizeof(uint8_t))。
该功能使用Modbus功能码0x02(读取输入离散量状态)。

返回:
如果成功,函数将返回读取的位数。否则返回-1并设置errno。
错误:EMBMDATA(请求的位太多)

modbus_read_registers()

int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);

功能:
函数将从远端设备的地址addr开始读取数量为nb的保持寄存器的内容。

参数:

  • ctx:指向modbus_t结构体的指针
  • addr:要读取的远端设备的起始地址
  • nb:读取的保持寄存器数量
  • dest:读取结果以字值(16位)的形式存储在dest数组中。

你必须注意分配足够的内存来存储结果在dest(至少nb * sizeof(uint16_t))。
该函数使用Modbus函数代码0x03(读取保持寄存器)。

返回:
如果成功,函数将返回读取的位数。否则返回-1并设置errno。
错误:EMBMDATA(请求的位太多)

例子:

modbus_t *ctx;
uint16_t tab_reg[64];
int rc;
int i;

ctx = modbus_new_tcp("127.0.0.1", 1502);
if (modbus_connect(ctx) == -1) {
    fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
    modbus_free(ctx);
    return -1;
}

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 < rc; i++) {
    printf("reg[%d]=%d (0x%X)\n", i, tab_reg[i], tab_reg[i]);
}

modbus_close(ctx);
modbus_free(ctx);

modbus_read_input_registers()

int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);

功能:
函数将从远端设备的地址addr开始读取数量为nb的输入寄存器的内容。

参数:

  • ctx:指向modbus_t结构体的指针
  • addr:要读取的远端设备的起始地址
  • nb:读取的输入寄存器数量
  • dest:读取结果以字值(16位)的形式存储在dest数组中。

你必须注意分配足够的内存来存储结果在dest(至少nb * sizeof(uint16_t))。
该函数使用Modbus函数代码0x04(读取输入寄存器)。持有寄存器和输入寄存器有不同的历史意义,但现在更常见的是只使用保持寄存器。

返回:
如果成功,函数将返回读取的位数。否则返回-1并设置errno。
错误:EMBMDATA(请求的位太多)

modbus_write_bit()

int modbus_write_bit(modbus_t *ctx, int addr, int status);

功能:
函数将单个线圈状态写入远端设备的地址addr。

参数:

  • ctx:指向modbus_t结构体的指针
  • addr:要读取的远端设备的起始地址
  • status:开关量 0或1

该函数使用Modbus函数代码0x05(强制单线圈)。

返回:
如果成功,函数将返回1。否则返回-1并设置errno。

modbus_write_register()

int modbus_write_register(modbus_t *ctx, int addr, int value);

功能:
函数的作用是:将保存值寄存器的值写入远端设备的地址addr。

参数:

  • ctx:指向modbus_t结构体的指针
  • addr:要读取的远端设备的起始地址
  • status:要写入的字数据(16位)

该函数使用Modbus函数代码0x06(预设单寄存器)。

返回:
如果成功,函数将返回1。否则返回-1并设置errno。

modbus_write_bits()

int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);

功能:
函数将从src写入远端设备地址addr的nb个bits(线圈)的状态。

参数:
src数组必须包含设置为TRUE或FALSE的字节。
该函数使用Modbus函数代码0x0F(写多个线圈)。

返回:
如果成功,函数将返回写入的位数。否则返回-1并设置errno。

modbus_write_registers()

int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);

功能:
函数的作用是:从远端设备的地址addr的数组src中写入nb持有寄存器的内容。

参数:

  • ctx:指向modbus_t结构体的指针
  • addr:要写入的远端设备的起始地址
  • nb:写入的保持寄存器数量
  • dest:写入的数据以字值(16位)的形式存储在dest数组中。

该函数使用Modbus函数代码0x10(写多个保持寄存器)。

返回:
如果成功,函数将返回写入的位数。否则返回-1并设置errno。

Server/Slave中的相关API

modbus_tcp_listen()

int modbus_tcp_listen(modbus_t *ctx, int nb_connection);

功能:函数应该创建一个套接字并监听指定IP地址上的最大nb_connection传入连接。

参数:

  • ctx:必须是已经被分配并使用modbus_new_tcp(3)初始化的才能设置监听的IP地址,如果IP地址设置为NULL,则任何地址都将被监听。
  • nb_connection:最大连接个数

返回:
如果成功,函数将返回一个新的套接字。否则返回-1并设置errno。

modbus_tcp_accept()

int modbus_tcp_accept(modbus_t *ctx, int *socketfd);

功能:
函数将提取挂起连接队列中的第一个连接,创建一个新的套接字并将其存储在参数中给出的libmodbus上下文中。如果可用,将调用带有SOCK_CLOEXEC的accept4(),而不是accept()。
参数:

  • ctx:已分配的modbus_t结构体。
  • socketfd:连接队列中的第一个套接字指针

返回:
如果成功,函数将返回一个新的套接字。否则返回-1并设置errno。

例子:

...

ctx = modbus_new_tcp("127.0.0.1", 502);
s = modbus_tcp_listen(ctx, 1);
modbus_tcp_accept(ctx, &s);

...

close(s)
modbus_free(ctx);

modbus_mapping_new()

modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, int nb_registers, int nb_input_registers);

功能:函数应该分配四个数组来存储位、输入位、寄存器和输入寄存器。指针存储在modbus_mapping_t结构中。数组中的所有值都初始化为0。
这个函数相当于调用modbus_mapping_new_start_address()函数,所有起始地址都为0。
如果不需要为特定类型的数据分配数组,则可以在参数中传递零值,相关指针将为NULL。
这个函数是方便处理请求在ModbusServer/Slave。

参数:
nb_bits:存放线圈寄存器需要的内存大小
nb_input_bits:存放离散输入寄存器需要的内存大小
nb_registers:存放保持寄存器需要的内存大小
nb_input_registers:存放输入寄存器需要的数据大小

返回:
如果成功,函数将返回新分配的结构体指针。否则返回NULL并设置errno。

modbus_mapping_free()

void modbus_mapping_free(modbus_mapping_t *mb_mapping);

功能:
该函数将释放mb_mapping_t结构的四个数组,最后释放mb_mapping_t指针。

参数:
mb_mapping:modbus_mapping_new()返回的结构体指针。

无返回值

modbus_receive()

int modbus_receive(modbus_t *ctx, uint8_t *req);

功能:
函数将从ctx的套接字接收一个指示请求。Modbus从机/服务器接收和分析主机/客户端发送的指示请求。

参数:

  • ctx:已经建立连接的modbus_t结构体指针
  • req:获取到的请求ADU队列

返回:
函数将指示请求存储在req中,如果成功则返回请求长度。如果指示请求被忽略,返回的请求长度可以为0。(例如在RTU模式下查询另一个slave)。否则返回-1并设置errno。

modbus_reply()

int modbus_reply(modbus_t *ctx, const uint8_t *req, int req_length, modbus_mapping_t *mb_mapping);

功能:
函数将对收到的请求发送响应。对参数中给出的请求req进行分析,然后通过modbus上下文ctx的信息构建并发送响应。
如果请求指示读或写一个值,操作将根据操作数据的类型在modbus映射mb_mapping中完成。
如果发生错误,将发送异常响应。
本功能是针对Modbus服务器设计的。

参数:

  • ctx:已经建立连接的modbus_t结构体指针
  • req:modbus_receive从ctx的套接字接收到的指示请求
  • req_length:req的长度,一般就是modbus_receive()的返回值
  • mb_mapping:modbus_mapping_new()分配的map结构体指针

返回:
如果成功,函数将返回响应的长度。否则返回-1并设置errno。

通用的相关API

modbus_free()

void modbus_free(modbus_t *ctx);

功能:
函数的作用是释放一个已分配的modbus_t结构体。

参数:
已分配的modbus_t结构体。

无返回值

modbus_set_slave()

int modbus_set_slave(modbus_t *ctx, int slave);

功能:
modbus_set_slave()函数将在libmodbus上下文中设置从机号。

参数:
这种行为取决于网络和设备的角色:
RTU
定义远端设备在主模式下通话的从机ID,或者在从模式下设置内部从机ID。根据协议,Modbus设备必须只接受持有其从机号或特殊广播号的消息。
TCP
只有当消息必须到达串行网络上的设备时,从机号才需要在TCP中。但没有从机值,故障的远端设备或软件将丢弃请求!特殊值MODBUS_TCP_SLAVE (0xFF)可以在TCP模式下使用,使用默认值。
广播地址为“MODBUS_BROADCAST_ADDRESS”。当您希望网络中的所有Modbus设备都接收到请求时,必须使用这个特殊值。

返回值:
如果成功,函数将返回0。否则,它将返回-1并将errno设置为EINVAL(设置了错误的从机号)

例子:

modbus_t *ctx;

ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1);
if (ctx == NULL) {
    fprintf(stderr, "Unable to create the libmodbus context\n");
    return -1;
}

rc = modbus_set_slave(ctx, YOUR_DEVICE_ID);
if (rc == -1) {
    fprintf(stderr, "Invalid slave ID\n");
    modbus_free(ctx);
    return -1;
}

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

modbus_set_debug()

int modbus_set_debug(modbus_t *ctx, int flag);

功能:
modbus_set_debug()函数应该通过使用参数标志来设置modbus_t上下文的调试标志。

参数:

  • ctx:指向modbus_t结构体的指针
  • flag:默认情况下,布尔标志设置为FALSE。当标志值设置为TRUE时,许多详细消息将显示在stdout和stderr上。例如,该标志用于显示Modbus消息的字节:
[00][14][00][00][00][06][12][03][00][6B][00][03]
Waiting for a confirmation…
<00><14><00><00><00><09><12><03><06><02><2B><00><00><00><00>

返回:
如果成功,函数将返回0。否则返回-1并设置errno。

  • 8
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
freemodbus 是一款开源的Modbus通信协议栈,用于实现Modbus通信协议的开发。通过学习freemodbus,我收获了许多知识和技能。 首先,在学习freemodbus时,我了解到了Modbus通信协议的基本原理和工作方式。Modbus是一种常用的串行通信协议,主要用于工业自动化领域中设备之间的通信。它使用简单的请求-响应机制进行数据传输,包括读取和写入寄存器等功能。通过freemodbus学习,我了解到了Modbus通信协议的组成结构、数据帧格式以及常用的功能码等。 其次,通过实践freemodbus的使用,我学会了如何在嵌入式系统中实现Modbus通信功能。freemodbus提供了一系列的接口函数,可以方便地实现Modbus主站和从站的功能,并支持多种串口通信方式,如RS485、TCP等。学习过程中,我熟悉了freemodbus的API函数的使用方法,掌握了创建、初始化和配置Modbus通信对象的技巧。 另外,通过使用freemodbus,我学到了一些调试和排错的技巧。在实际应用中,由于硬件、软件等多种因素的影响,可能会出现通信故障或数据错误的情况。通过freemodbus的调试工具和相关的日志输出,我学会了如何在开发过程中定位和解决各种问题,提高了软件开发的技能和经验。 总体来说,学习freemodbus是一次非常有益的经历。通过这个学习过程,我不仅掌握了Modbus通信协议的原理和应用,还提高了嵌入式软件开发的能力。我相信,在今后的工作中,我可以更好地应用freemodbus开发各种工业自动化设备和系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值