C语言中实现Modbus转IEC 60870-5-103协议转换器

2025-12-16

C语言中实现Modbus转IEC 60870-5-103协议转换器

协议概述

协议特性 Modbus IEC 60870-5-103
应用领域 工业自动化 电力系统保护设备
数据模型 寄存器、线圈 信息对象、ASDU
通信方式 请求-响应 主动上报+主站查询

转换器架构设计

核心数据结构

#include <stdint.h>
#include <string.h>

// Modbus PDU结构
typedef struct {
    uint8_t function_code;
    uint8_t data[252];
    uint16_t data_length;
} modbus_pdu_t;

// IEC 103 ASDU结构
typedef struct {
    uint8_t type_id;
    uint8_t vsq; // 可变结构限定词
    uint8_t cot; // 传输原因
    uint16_t common_address;
    uint8_t info_objects[250];
    uint16_t info_length;
} iec103_asdu_t;

// 协议映射配置
typedef struct {
    uint16_t modbus_address;
    uint8_t iec103_type;
    uint16_t iec103_address;
    uint8_t data_type; // 0:状态量, 1:测量值, 2:保护事件
} protocol_mapping_t;

核心转换函数

// Modbus到IEC 103的转换函数
int modbus_to_iec103(const modbus_pdu_t *modbus_pdu, 
                     iec103_asdu_t *iec103_asdu,
                     const protocol_mapping_t *mapping_table,
                     int mapping_count) {
    
    if (modbus_pdu == NULL || iec103_asdu == NULL) {
        return -1;
    }
    
    // 根据功能码处理不同请求
    switch (modbus_pdu->function_code) {
        case 0x01: // 读线圈
        case 0x02: // 读离散输入
            return convert_digital_inputs(modbus_pdu, iec103_asdu, mapping_table, mapping_count);
            
        case 0x03: // 读保持寄存器
        case 0x04: // 读输入寄存器
            return convert_analog_values(modbus_pdu, iec103_asdu, mapping_table, mapping_count);
            
        case 0x05: // 写单个线圈
        case 0x06: // 写单个寄存器
            return convert_control_commands(modbus_pdu, iec103_asdu, mapping_table, mapping_count);
            
        default:
            return -2; // 不支持的功能码
    }
}

具体转换实现

// 数字量输入转换
static int convert_digital_inputs(const modbus_pdu_t *modbus_pdu,
                                  iec103_asdu_t *iec103_asdu,
                                  const protocol_mapping_t *mapping_table,
                                  int mapping_count) {
    
    // 设置ASDU类型 - 单点信息
    iec103_asdu->type_id = 0x01; // M_SP_NA_1
    iec103_asdu->cot = 0x03; // 突发/周期
    iec103_asdu->vsq = 0x01; // 单个信息对象
    
    uint8_t coil_data = modbus_pdu->data[0]; // 假设第一个字节包含线圈状态
    
    // 构建信息对象
    iec103_asdu->info_objects[0] = 0x01; // 信息对象地址
    iec103_asdu->info_objects[1] = 0x00;
    iec103_asdu->info_objects[2] = coil_data & 0x01; // SIQ格式
    
    iec103_asdu->info_length = 3;
    return 0;
}

// 模拟量转换
static int convert_analog_values(const modbus_pdu_t *modbus_pdu,
                                 iec103_asdu_t *iec103_asdu,
                                 const protocol_mapping_t *mapping_table,
                                 int mapping_count) {
    
    iec103_asdu->type_id = 0x09; // M_ME_NA_1 - 测量值,规一化值
    iec103_asdu->cot = 0x03; // 突发/周期
    
    // 假设Modbus数据包含寄存器值
    uint16_t register_value = (modbus_pdu->data[0] << 8) | modbus_pdu->data[1];
    
    // 转换为IEC 103规一化值 (-1.0 to 1.0)
    int16_t normalized_value = (int16_t)((register_value / 65535.0) * 32767);
    
    iec103_asdu->info_objects[0] = 0x01; // 信息对象地址
    iec103_asdu->info_objects[1] = 0x00;
    iec103_asdu->info_objects[2] = (normalized_value >> 8) & 0xFF; // 高字节
    iec103_asdu->info_objects[3] = normalized_value & 0xFF;        // 低字节
    iec103_asdu->info_objects[4] = 0x00; // QDS - 品质描述词
    
    iec103_asdu->info_length = 5;
    return 0;
}

协议映射表配置

// 协议映射配置表
static protocol_mapping_t default_mapping[] = {
    // Modbus地址, IEC103类型, IEC103地址, 数据类型
    {0x0000, 0x01, 0x0001, 0}, // 线圈0 -> 单点信息1
    {0x0001, 0x01, 0x0002, 0}, // 线圈1 -> 单点信息2
    {0x3000, 0x09, 0x0001, 1}, // 输入寄存器3000 -> 测量值1
    {0x3001, 0x09, 0x0002, 1}, // 输入寄存器3001 -> 测量值2
};

#define MAPPING_COUNT (sizeof(default_mapping) / sizeof(protocol_mapping_t))

主处理循环

// 协议转换器主函数
void protocol_converter_main(void) {
    modbus_pdu_t modbus_frame;
    iec103_asdu_t iec103_frame;
    
    while (1) {
        // 接收Modbus数据
        if (receive_modbus_data(&modbus_frame) > 0) {
            
            // 进行协议转换
            if (modbus_to_iec103(&modbus_frame, &iec103_frame, 
                               default_mapping, MAPPING_COUNT) == 0) {
                
                // 发送IEC 103数据
                send_iec103_data(&iec103_frame);
            }
        }
        
        // 处理IEC 103到Modbus的转换(如需要)
        // handle_iec103_to_modbus();
    }
}

// Modbus数据接收函数(需要根据实际通信方式实现)
int receive_modbus_data(modbus_pdu_t *pdu) {
    // 实现串口、TCP等通信方式的数据接收
    // 返回接收到的数据长度
    return 0;
}

// IEC 103数据发送函数
int send_iec103_data(const iec103_asdu_t *asdu) {
    // 实现IEC 103帧的组帧和发送
    return 0;
}

参考代码 协议转换器,modbus转103协议 www.3dddown.com/cna/72685.html

编译和配置要点

编译指令

gcc -o protocol_converter protocol_converter.c -std=c99 -Wall

配置考虑

  1. 通信参数:波特率、数据位、停止位
  2. 映射配置:根据实际设备调整地址映射
  3. 超时处理:添加通信超时和重试机制
  4. 错误处理:完善的错误码和异常处理