文章目录
一、modbus_mapping_new_start_address函数
modbus_mapping_new_start_address
是 libmodbus
库中的一个函数,用于创建一个新的 modbus_mapping_t
结构体,并分配内存以存储 Modbus 数据区的映射。这些数据区包括离散输出(bits)、离散输入(input bits)、保持寄存器(registers)和输入寄存器(input registers)。这个函数通过传入的参数来定义各个数据区的起始地址和数量,并为这些数据区分配内存。
函数原型
modbus_mapping_t *modbus_mapping_new_start_address(
unsigned int start_bits,
unsigned int nb_bits,
unsigned int start_input_bits,
unsigned int nb_input_bits,
unsigned int start_registers,
unsigned int nb_registers,
unsigned int start_input_registers,
unsigned int nb_input_registers
);
参数说明
start_bits
:离散输出(Coils)区的起始地址。nb_bits
:离散输出(Coils)的数量。start_input_bits
:离散输入(Discrete Inputs)区的起始地址。nb_input_bits
:离散输入(Discrete Inputs)的数量。start_registers
:保持寄存器(Holding Registers)区的起始地址。nb_registers
:保持寄存器(Holding Registers)的数量。start_input_registers
:输入寄存器(Input Registers)区的起始地址。nb_input_registers
:输入寄存器(Input Registers)的数量。
功能描述
modbus_mapping_new_start_address
的主要作用是为 modbus_mapping_t
结构体分配内存,并初始化指向这些数据区的指针。这些数据区的起始地址和数量由传入的参数决定。该函数在成功时返回一个指向 modbus_mapping_t
结构体的指针,在失败时返回 NULL
,并将 errno
设置为 ENOMEM
(内存不足)。
工作原理
-
内存分配:
- 根据每种数据区的数量(例如
nb_bits
,nb_input_bits
等),该函数为每个数据区分配足够的内存空间。 - 这些内存空间用于存储数据,例如离散输出的位、输入寄存器的值等。
- 根据每种数据区的数量(例如
-
初始化映射结构:
- 函数会将数据区的指针存储到返回的
modbus_mapping_t
结构体中,使得每个数据区能够通过指针直接访问。 - 每个数据区的起始地址由用户提供,并且数据区的起始地址与 Modbus 地址的约定相对应。
- 函数会将数据区的指针存储到返回的
-
返回值:
- 如果成功,返回一个指向
modbus_mapping_t
结构体的指针,用户可以通过该结构体访问和修改 Modbus 数据。 - 如果内存分配失败,返回
NULL
,并且errno
被设置为ENOMEM
,表示内存不足。
- 如果成功,返回一个指向
返回结构体:modbus_mapping_t
modbus_mapping_t
结构体是一个包含多个指向数据区的指针的结构体,通常包括以下内容:
nb_bits
:离散输出(Coils)的数量。nb_input_bits
:离散输入(Discrete Inputs)的数量。nb_registers
:保持寄存器(Holding Registers)的数量。nb_input_registers
:输入寄存器(Input Registers)的数量。tab_bits
:指向离散输出数据的指针。tab_input_bits
:指向离散输入数据的指针。tab_registers
:指向保持寄存器数据的指针。tab_input_registers
:指向输入寄存器数据的指针。
示例代码
#include <modbus.h>
#include <stdio.h>
int main() {
// 创建 Modbus 映射,指定起始地址和数据区数量
modbus_mapping_t *mb_mapping = modbus_mapping_new_start_address(
0, // 离散输出的起始地址
100, // 离散输出的数量
100, // 离散输入的起始地址
100, // 离散输入的数量
1, // 保持寄存器的起始地址
100, // 保持寄存器的数量
0, // 输入寄存器的起始地址
100 // 输入寄存器的数量
);
if (mb_mapping == NULL) {
fprintf(stderr, "Memory allocation failed\n");
return -1;
}
// 访问映射中的数据
mb_mapping->tab_bits[0] = 1; // 设置第一个离散输出为 1
mb_mapping->tab_registers[0] = 1234; // 设置第一个保持寄存器为 1234
// 打印一些数据
printf("First coil: %d\n", mb_mapping->tab_bits[0]);
printf("First register: %d\n", mb_mapping->tab_registers[0]);
// 释放映射内存
modbus_mapping_free(mb_mapping);
return 0;
}
错误处理
如果内存分配失败(如系统内存不足),modbus_mapping_new_start_address
函数会返回 NULL
,并将 errno
设置为 ENOMEM
,用户应当检查返回值并根据需要进行错误处理。
总结
modbus_mapping_new_start_address
用于创建一个新的 Modbus 数据映射结构,并为各个数据区分配内存。- 它接受多个参数来指定数据区的起始地址和数量,这些参数将用于初始化
modbus_mapping_t
结构体。 - 返回的结构体包含指向各个数据区的指针,用户可以用来读取和写入 Modbus 数据。
- 内存分配失败时,函数返回
NULL
,并设置errno
为ENOMEM
。
二、modbus_reply函数
modbus_reply
是 libmodbus
库中的一个重要函数,用于处理接收到的 Modbus 请求并发送相应的响应。该函数根据收到的请求数据构造一个响应,并通过 Modbus 协议将响应发送回客户端。若发生错误,函数将根据错误类型构造相应的错误响应。
函数原型
int modbus_reply(modbus_t *ctx,
const uint8_t *req,
int req_length,
modbus_mapping_t *mb_mapping);
参数说明
-
ctx
:一个指向modbus_t
类型的指针,表示 Modbus 上下文。该上下文包含与通信相关的所有设置(如端口、协议类型、从站地址等)。 -
req
:指向接收到的请求数据的指针,req
是一个字节数组,包含了接收到的 Modbus 请求报文。 -
req_length
:请求数据的长度,单位为字节。它告诉modbus_reply
函数请求数据的大小。 -
mb_mapping
:指向modbus_mapping_t
类型的指针。modbus_mapping_t
是 Modbus 映射结构,包含了寄存器和位的内存映射(例如,离散输出、输入寄存器等)。此参数用于执行实际的 Modbus 数据操作。
功能描述
modbus_reply
函数的主要功能是根据接收到的 Modbus 请求,分析请求内容,构造响应并将其发送回请求方。它通过以下步骤完成操作:
-
分析请求:
- 解析
req
中的请求数据,确定请求的功能码、请求的目标地址、请求的寄存器地址和长度等信息。 - 根据请求的内容,判断应该执行哪种操作(读取、写入寄存器,或是错误响应等)。
- 解析
-
执行操作:
- 根据请求的类型,执行实际的操作。比如:
- 读取数据:根据请求的数据类型(如离散输出、保持寄存器等),从
mb_mapping
中提取相应的数据。 - 写入数据:根据请求的目标地址和写入的值,修改
mb_mapping
中相应的数据区。
- 读取数据:根据请求的数据类型(如离散输出、保持寄存器等),从
- 根据请求的类型,执行实际的操作。比如:
-
构建响应:
- 如果请求处理成功,构建一个符合 Modbus 协议格式的响应报文。
- 如果发生错误(如非法操作、地址越界等),则根据错误类型构建相应的错误响应。
-
发送响应:
- 将构建好的响应数据通过
ctx
中配置的通信通道(如串口、TCP 等)发送回客户端。
- 将构建好的响应数据通过
错误处理
如果在请求分析或数据处理过程中发生错误,modbus_reply
会根据错误类型构造错误响应。Modbus 错误响应的格式通常为:
- 功能码保持不变。
- 错误标识符会在功能码的基础上加上 0x80(例如,如果原功能码是 0x03(读取保持寄存器),则错误响应的功能码将是 0x83)。
- 错误代码指示具体错误类型,比如地址越界、非法功能码等。
返回值
- 如果成功,返回发送的字节数,即响应的长度。
- 如果失败,返回 -1,表示出现了错误。
Modbus 响应格式
Modbus 响应包含以下几个部分:
- 功能码:与请求的功能码相同,若发生错误则将功能码加上 0x80。
- 字节数:响应中有效数据的字节数。
- 数据:响应的数据(根据请求的类型,可能是寄存器的值、离散输出的状态等)。
- 错误代码(如果发生错误):如非法功能码、非法数据等。
示例代码
#include <modbus.h>
#include <stdio.h>
int main() {
// 创建 Modbus TCP 上下文
modbus_t *ctx = modbus_new_tcp("127.0.0.1", 502);
if (ctx == NULL) {
fprintf(stderr, "Unable to create Modbus context\n");
return -1;
}
// 创建 Modbus 映射结构,设置初始数据区
modbus_mapping_t *mb_mapping = modbus_mapping_new_start_address(0, 100, 100, 100, 0, 100);
if (mb_mapping == NULL) {
fprintf(stderr, "Unable to create Modbus mapping\n");
modbus_free(ctx);
return -1;
}
// 假设我们从某种通信协议获取了请求数据
uint8_t req[256]; // 请求数据
int req_length = 0; // 假设请求的长度
// 调用 modbus_reply 处理请求并发送响应
int ret = modbus_reply(ctx, req, req_length, mb_mapping);
if (ret == -1) {
fprintf(stderr, "Error handling Modbus request\n");
}
// 释放资源
modbus_mapping_free(mb_mapping);
modbus_free(ctx);
return 0;
}
Modbus 错误响应的示例
如果请求包含错误,例如请求一个不存在的寄存器,modbus_reply
会构建一个错误响应。例如,如果请求读取保持寄存器,但请求的地址超出了有效范围,那么响应的功能码将是原功能码(例如 0x03)加上 0x80,错误代码则会表示“非法地址”。
错误响应格式示例:
// 请求功能码:0x03
// 错误响应功能码:0x83 // 0x03 + 0x80
// 错误代码:0x02 // Illegal Data Address (非法地址)
总结
modbus_reply
函数用于处理接收到的 Modbus 请求,并发送响应。- 根据请求类型,它会执行读取或写入操作,并根据需要构造错误响应。
- 如果操作成功,返回发送的字节数;如果发生错误,返回 -1。
- 在实际应用中,该函数处理 Modbus 协议的基本通信,包括正常的请求处理和错误处理。