本教程演示如何通过 Modbus TCP 协议实现 PLC 与上位机或其他设备的通信。

前置条件

  • KVPAC IDE 已安装并配置
  • RTE 运行环境(模拟器或 STM32 设备)
  • Modbus 调试工具(如 ModScan、Modbus Poll)

步骤 1:启用 Modbus

在目标平台配置文件中启用 Modbus 并设置参数:

{
  "communication": {
    "modbus_enabled": true,
    "modbus_port": 502,
    "modbus_slave_id": 1
  }
}

步骤 2:定义 Modbus 映射变量

在 ST 程序中使用 AT % 修饰符将变量映射到 Modbus 寄存器:

PROGRAM ModbusExample
VAR
    // 映射到 Modbus 输入寄存器 (30001-3xxxx)
    temperature AT %IW* : INT;
    pressure AT %IW* : INT;
    
    // 映射到 Modbus 保持寄存器 (40001-4xxxx)
    setpoint AT %QW* : INT := 500;
    speed AT %QW* : INT := 0;
    
    // 映射到 Modbus 线圈 (00001-0xxxx)
    start_btn AT %IX* : BOOL;
    motor_run AT %QX* : BOOL;
    
    // 内部变量
    running : BOOL := FALSE;
    counter : INT := 0;
END_VAR
地址范围 Modbus 区域 KVPAC 映射
0xxxx 线圈 (Coils) AT %IX* / AT %QX*
1xxxx 离散输入 AT %IX* (只读)
3xxxx 输入寄存器 AT %IW* (只读)
4xxxx 保持寄存器 AT %QW* (读写)

步骤 3:编写通信逻辑

PROGRAM ModbusControl
VAR
    // Modbus 映射
    cmd_register AT %QW* : INT;       // 命令寄存器
    status_register AT %QW* : INT;    // 状态寄存器
    data_in AT %IW* : INT;            // 数据输入
    
    // 状态变量
    system_state : INT := 0;
    cmd_buffer : INT := 0;
END_VAR

// 读取命令
cmd_buffer := cmd_register;

// 命令解析
CASE cmd_buffer OF
    1:  // 启动
        system_state := 1;
        status_register := 100;
        cmd_register := 0;  // 清除命令
        
    2:  // 停止
        system_state := 0;
        status_register := 200;
        cmd_register := 0;
        
    3:  // 读取状态
        status_register := system_state;
        cmd_register := 0;
        
    ELSE:
        // 无命令
END_CASE

步骤 4:使用 Modbus 调试

  1. 编译并下载程序到 RTE
  2. 启动 Modbus 调试工具(如 ModScan)
  3. 配置连接参数:
    • IP:RTE 设备 IP(模拟器使用 127.0.0.1)
    • 端口:502
    • 从站 ID:1
  4. 读取保持寄存器地址 0(对应 cmd_register
  5. 写入值 1 触发启动命令
  6. 观察状态寄存器值变为 100

步骤 5:编写 Modbus 通信状态机

对于复杂的通信场景,建议使用状态机管理:

FUNCTION_BLOCK ModbusComm
VAR_INPUT
    enable : BOOL;
    request_data : INT;
    slave_id : INT := 1;
END_VAR
VAR_OUTPUT
    response_data : INT;
    comm_ok : BOOL;
    error_code : INT;
END_VAR
VAR
    state : INT := 0;
    timeout : INT := 0;
    retry_count : INT := 0;
    MAX_RETRIES : INT := 3;
END_VAR

IF NOT enable THEN
    state := 0;
    comm_ok := FALSE;
    RETURN;
END_IF

CASE state OF
    0:  // 空闲
        IF request_data > 0 THEN
            state := 10;
            timeout := 0;
        END_IF
        
    10:  // 发送请求
        // 写入请求寄存器
        %QW10 := request_data;
        state := 20;
        timeout := 0;
        
    20:  // 等待响应
        timeout := timeout + 1;
        IF %IW10 > 0 THEN
            // 收到响应
            response_data := %IW10;
            comm_ok := TRUE;
            state := 0;
        ELSIF timeout >= 100 THEN
            // 超时处理
            retry_count := retry_count + 1;
            IF retry_count >= MAX_RETRIES THEN
                error_code := 1;  // 通信失败
                comm_ok := FALSE;
                state := 0;
                retry_count := 0;
            ELSE
                state := 10;  // 重试
            END_IF
        END_IF
        
END_CASE
END_FUNCTION_BLOCK

完整示例:温控系统

PROGRAM TempControl
VAR
    // Modbus IO 映射
    current_temp AT %IW* : INT;
    target_temp AT %QW* : INT := 250;  // 25.0°C
    heater_ctrl AT %QX* : BOOL;
    heater_status AT %IX* : BOOL;
    
    // 控制参数
    HYSTERESIS : INT := 10;  // ±1.0°C
    alarm_temp : INT := 500; // 50.0°C 报警
    
    // 状态
    alarm : BOOL := FALSE;
    runtime : INT := 0;
END_VAR

// 温度控制 (PID 简化版 - 滞回控制)
IF current_temp < (target_temp - HYSTERESIS) THEN
    heater_ctrl := TRUE;
ELSIF current_temp > (target_temp + HYSTERESIS) THEN
    heater_ctrl := FALSE;
END_IF

// 高温报警
IF current_temp >= alarm_temp THEN
    alarm := TRUE;
    heater_ctrl := FALSE;  // 紧急关断
END_IF

// 运行计时
IF heater_ctrl THEN
    runtime := runtime + 1;
END_IF

常见问题

Q: Modbus 通信超时? A: 检查网络连接、防火墙设置,确认 Modbus 端口未被占用。

Q: 读取寄存器返回 0? A: 确认寄存器地址正确,变量已映射到正确的地址范围,设备已连接并运行。

Q: 多台设备如何区分? A: 每个 Modbus 从站设备设置唯一的 slave_id(1-247),主站通过 slave_id 寻址。