基于开源库进行 Modbus 编程,核心在于理解协议栈的层次结构以及如何通过回调函数或接口函数将协议与你的硬件/业务逻辑挂钩。
以下是基于开源库编程的通用步骤和代码逻辑架构:
无论使用哪个库,编程思路通常分为三层:
libmodbus 的编程示例 (适用于 Linux/C++)libmodbus 是最流行的开源库之一,其 API 非常直观。
主要逻辑是“连接 -> 请求 -> 处理结果”。
C
#include <modbus.h>
int main() {
modbus_t *ctx;
uint16_t tab_reg[32];
// 1. 创建上下文 (RTU 模式)
ctx = modbus_new_rtu("/dev/ttyUSB0", 9600, 'N', 8, 1);
modbus_set_slave(ctx, 1); // 设置目标从站 ID
// 2. 建立连接
if (modbus_connect(ctx) == -1) {
modbus_free(ctx);
return -1;
}
// 3. 读取寄存器 (从地址 0 开始读 10 个)
int rc = modbus_read_registers(ctx, 0, 10, tab_reg);
if (rc > 0) {
printf("寄存器0的值: %d\n", tab_reg[0]);
}
// 4. 关闭
modbus_close(ctx);
modbus_free(ctx);
}
FreeModbus 的编程示例 (适用于单片机/嵌入式)FreeModbus 采用事件驱动和回调机制,代码通常集成在中断服务中。
你不需要手动解析报文,只需告诉协议栈:当别人来读“保持寄存器”时,该给什么数据。
C
/* 保持寄存器回调函数示例 */
eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode)
{
int iRegIndex = (int)(usAddress - 1); // 协议地址通常从1开始,映射到数组索引0
if (eMode == MB_REG_READ) {
while (usNRegs > 0) {
// 将本地数据推入协议栈缓存(注意大端转换)
*pucRegBuffer++ = (UCHAR)(MyLocalData[iRegIndex] >> 8);
*pucRegBuffer++ = (UCHAR)(MyLocalData[iRegIndex] & 0xFF);
iRegIndex++;
usNRegs--;
}
}
return MB_ENOERR;
}
Modbus 标准寄存器是 16 位的,但实际业务中常有 float(32位)或 int32。
union(联合体)将 2 个 16 位寄存器拼成一个 32 位浮点数。libmodbus 时,建议将 Modbus 通信放在独立线程,避免阻塞 UI 或主逻辑。eMBPoll() 函数在 while(1) 中高频调用,否则响应会变慢。编程时最好开着仿真软件: