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
配置考虑
- 通信参数:波特率、数据位、停止位
- 映射配置:根据实际设备调整地址映射
- 超时处理:添加通信超时和重试机制
- 错误处理:完善的错误码和异常处理
