上下位机通信

上下位机使用使用Modbus协议进行通信。在Qt开发中,可以采用第三方库libmodbus完成

modbus协议介绍

简介
  1. modbus是一种免费的应用层协议,采用请求响应模式进行设备间的通信,由主机请求从机。
  2. 应用Modbus协议进行通信的程序调试可以使用几个重要工具:
    1. Modbus Poll
    2. Modbus Slave
    3. VSPD:虚拟串口工具
    4. 串口调试助手
    5. 网络调试助手
Modbus中的存储区
  1. 线圈状态(输出线圈)(coil status):存储开关量,值为0或者1。值可读可写
  2. 输入状态(输入线圈)(input status):存储开关量,值为0或者1。值只读不可写
  3. 保持寄存器(输出寄存器)(Holding Register):存储一个字,值可读可写。
  4. 输入寄存器(Input Registers):存储一个字,值可读不可写
地址模型
  1. 长地址模型:六位地址模型。这种地址模型中,每个存储区能存储65536个线圈或者寄存器
    1. 输出线圈:地址从000001~065536
    2. 输入线圈:地址从100001~165536
    3. 输入寄存器:地址从300001~365536
    4. 输出寄存器:地址从400001~465536
  2. 短地址模型:五位地址模型
    1. 输出线圈:地址范围从00001~09999
    2. 输入线圈:地址从10001~19999
    3. 输入寄存器:地址从30001~39999
    4. 输出寄存器:地址从40001~49999
Modbus协议分类
  1. ModbusRTU通信协议
  2. ModbusRTUOverTCP通信协议
  3. ModbusRTUOverUDP通信协议
  4. ModbusASCII通信协议
  5. ModbusASCIIOverTCP通信协议
  6. ModbusASCIIOverUDP通信协议
  7. ModbusTCP通信协议
  8. ModbusUDP通信协议
功能码及异常码
  1. 功能码:告知从机设备需要执行的动作。这些功能码表征了Modbus协议具有的功能
    1. 读操作:
      1. 0x01:读线圈状态(输出线圈)。
      2. 0x02:读输入状态(输入线圈)。
      3. 0x03:读保持寄存器的值(输出寄存器)。
      4. 0x04:读输入寄存器的值(输入寄存器)。
    2. 写操作:
      1. 0x05:写单个线圈状态(输出线圈)。
      2. 0x06:写单个保持寄存器的值。
      3. 0x0F:写多个线圈状态。
      4. 0x10:写多个保持寄存器的值。
  2. 异常码:将对应功能码的最高位置为1,比如说0x01的异常码为0x81

Modbus通信协议

Modbus RTU通信协议

对于Modbus RTU通信协议,通用的发送报文格式为:从站地址(1个字节) + 功能码(1个字节) + 数据部分 + 校验码(两个字节)。数据部分,如果是读取,则数据部分为要读取的起始地址(2字节)和读取的长度(2字节);如果是写入,则数据部分为写入的地址(2字节)和数据(N个字节)

  1. 功能码01读取输出线圈:
    1. 发送报文为从站地址+功能码01+起始线圈地址+线圈数量+CRC校验码
    2. 接收报文为从站地址+功能码01+字节计数+具体数据+CRC校验码
  2. 功能码02读取输入线圈:
    1. 发送报文格式为从站地址+功能码02+起始线圈地址+线圈数量+CRC校验码
    2. 接受报文格式为从站地址+功能码02+字节计数+具体数据+CRC校验码
  3. 功能码03读取输出寄存器:
    1. 发送报文格式为:从站地址+功能码03+起始寄存器地址+寄存器数量+CRC校验码
    2. 接受报文格式为:从站地址+功能码03+字节计数+具体数据+CRC校验码
  4. 功能码04读取输入寄存器:
    1. 发送报文格式为:从站地址+功能码04+起始寄存器地址+寄存器数量+CRC校验码
    2. 接受报文格式为:从站地址+功能码04+字节计数+具体数据+CRC校验码
  5. 功能码05写入单个输出线圈:成功时,发送报文和接收报文一致
    1. 发送报文格式为:从站地址+功能码05+线圈地址+写入的值+CRC校验码,对于写入1时写入的值为FF00,对于写入0时写入的值为0000
    2. 接受报文格式为:从站地址+功能码05+线圈地址+写入的值+CRC校验码
  6. 功能码06写入单个输出寄存器:
    1. 发送报文格式为:从站地址+功能码06+寄存器地址+写入的值+CRC校验码
    2. 接受报文格式为:从站地址+功能码06+寄存器地址+写入的值+CRC校验码
  7. 功能码0F写入多个输出线圈:
    1. 发送报文格式为:从站地址+功能码0F+起始线圈地址+线圈数量+字节计数+ 具体写入的值+CRC校验码
    2. 接受报文格式为:从站地址+功能码0F+起始线圈地址+线圈数量+CRC校验码
  8. 功能码10写入多个输出寄存器:
    1. 发送报文格式为:从站地址+功能码10+起始寄存器地址+寄存器数量+字节计数+ 具体写入的值+CRC校验码
    2. 接受报文格式为:从站地址+功能码10+起始寄存器地址+寄存器数量+CRC校验码
Modbus ASCII

Modbus TCP协议报文格式

参考

上下位机Modbus通信

1.第三方库libmodbus的基本使用

环境 Windows,visual studio 2019,Qt5.15.2,libmodbus3.1.11

  1. 双击运行源码src\win32目录下的configure.js的文件,用于生成相应操作系统的配置。如果报错输入错误: 没有文件扩展“.js”的脚本引擎。则进行注册表的修改
    1. win + r =》regedit,进入注册表
    2. 查找HKEY_CLASSES_ROOT\.js,将默认值修改为JSFile
  2. 将八个头文件放置 include目录下。删除项目目录下的modbus-version.h文件,然后重新添加src/modbus-version.h头文件
  3. 使用 VS 打开解决方案文件modbus-9.sln,根据需要编译x64还是x86平台的。如果选择x64位的,需要在项目属性页中选择链接器=》输入=》附加依赖项,输入ws2_32.lib
  4. 将 配置类型更改为动态库
  5. 重新生成解决方案,编译生成modbus.dll和modbus.lib
  6. 将lib和dll也分别放置项目目录下
  7. CMAKE配置如下所示:
cmake_minimum_required(VERSION 3.20)
project(TEST VERSION 1.0 LANGUAGES CXX)

find_package(QT NAMES Qt5 REQUIRED COMPONENTS Core Gui Widgets PrintSupport )
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS  Core Gui Widgets PrintSupport)

# 设置源文件编码为UTF-8
if(MSVC)
    add_compile_options(/utf-8)
   
endif()

# 自动处理Qt的moc、uic、rcc
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

message(STATUS "CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")

# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

file(GLOB copyResources 
    "${CMAKE_SOURCE_DIR}/LICENSE"
    "${CMAKE_SOURCE_DIR}/assets/x64/release/modbus.dll"
)
file(COPY ${copyResources} DESTINATION ${CMAKE_BINARY_DIR})

# 源文件列表
file(GLOB PROJECT_SOURCES
    "${CMAKE_SOURCE_DIR}/src/*.cpp"
)

message("PROJECT_SOURCES:${PROJECT_SOURCES}")
# 设置 UI 文件搜索路径
set(CMAKE_AUTOUIC_SEARCH_PATHS
    "${CMAKE_SOURCE_DIR}/ui"
)
# UI文件
file(GLOB UI_FILES
    "${CMAKE_SOURCE_DIR}/ui/*.ui"
)

# 资源文件
file(GLOB RESOURCE_FILES
    "${CMAKE_SOURCE_DIR}/res/*.qrc"
)

# 创建可执行文件
file(GLOB HEADERS 
    "${CMAKE_SOURCE_DIR}/include/*.h"
)

IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
    # Debug模式显示用于调试的控制台
    ADD_EXECUTABLE(${PROJECT_NAME} 
        ${HEADERS}          #MOC处理使用了Qt元系统的头文件
        ${PROJECT_SOURCES}
        ${UI_FILES}
        ${RESOURCE_FILES})
ELSE()
    # Release模式不显示控制台
    ADD_EXECUTABLE(${PROJECT_NAME} WIN32
        ${HEADERS}          #MOC处理使用了Qt元系统的头文件
        ${PROJECT_SOURCES}
        ${UI_FILES}
        ${RESOURCE_FILES})
ENDIF()

# 设置 MOC选项和生成路径
set_target_properties(${PROJECT_NAME} PROPERTIES
    AUTOMOC_MOC_OPTIONS "-I${CMAKE_CURRENT_SOURCE_DIR}/include"
    AUTOGEN_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/autogen"
)

# 包含目录
target_include_directories(${PROJECT_NAME} PRIVATE
    "${CMAKE_SOURCE_DIR}/include"
    "${CMAKE_SOURCE_DIR}/third_party/libmodbus/include"   
    ${CMAKE_CURRENT_BINARY_DIR}/autogen/include #生成的ui_xxx文件的头文件目录
)


# 链接Qt库
target_link_libraries(${PROJECT_NAME} PRIVATE
    Qt${QT_VERSION_MAJOR}::Core
    Qt${QT_VERSION_MAJOR}::Gui
    Qt${QT_VERSION_MAJOR}::Widgets
    Qt${QT_VERSION_MAJOR}::PrintSupport
     "${CMAKE_SOURCE_DIR}/third_party/libmodbus/lib/x64/release/modbus.lib"
)

# 开启 MOC 调试输出
set(CMAKE_AUTOMOC_VERBOSE ON)
  1. 简单测试如下:
void modbus_rtu_test() {
    char* pcom = "COM5";                //COM口
    int    serverAddress = 1;           //站号    
    modbus_t * mb = modbus_new_rtu(pcom, 115200, 'N', 8, 1);
    modbus_set_slave(mb, serverAddress);
    
    if (modbus_connect(mb) != 0) {
        qDebug() << "connect error!";
    }
}

void modbus_tcp_test() {
    int    serverAddress = 1;           //站号   
    modbus_t* mb = modbus_new_tcp("127.0.0.1", 502);

    modbus_set_slave(mb, serverAddress);

    if (modbus_connect(mb) != 0) {
        qDebug() << "connect error!";
    }
}
2.使用Qt中的SerialBus模块

这是Qt官方提供的模块。参考