421 lines
19 KiB
C
421 lines
19 KiB
C
#include "proto_modbus_tcp_master.h"
|
||
#include "bsp_W5500_1.h"
|
||
#include "string.h"
|
||
|
||
/* 响应超时时间(单位:毫秒) */
|
||
#define RESP_TIMEOUT_MS 500
|
||
|
||
/* 静态函数声明 */
|
||
static void modbus_tcp_send_read(u16 start, u16 num);
|
||
static void modbus_tcp_parse_response(bsp_W5500_Class_t *pClass, u8 *data, u16 len);
|
||
static void modbus_tcp_init_local(void);
|
||
static void modbus_tcp_task(void);
|
||
static void modbus_tcp_write_control(u16 bit_mask);
|
||
|
||
char *proto_modbus_master_alarm[24] = {
|
||
"Alarm", /* 40002.00 一级报警 */
|
||
"HH Inlet1 Pressure", /* 40002.01 进口压力1高高报警 */
|
||
"HH Inlet2 Pressure", /* 40002.02 进口压力2高高报警 */
|
||
"HH Inlet3 Pressure", /* 40002.03 进口压力3高高报警 */
|
||
"HH Inlet4 Pressure", /* 40002.04 备口压力1高高报警 */
|
||
"HH Inlet5 Pressure", /* 40002.05 备口压力2高高报警 */
|
||
"HH Inlet6 Pressure", /* 40002.06 备口压力3高高报警 */
|
||
"HH Reaction Chamber Temp.", /* 40002.07 反应腔温度高高报警 */
|
||
"Drain Pump Alarm", /* 40003.00 排水泵异常报警 */
|
||
"LL PCW Flow", /* 40003.01 PCW流量低低报警 */
|
||
"LL Liquid Level", /* 40003.02 低低液位报警 */
|
||
"HH Liquid Level", /* 40003.03 高高液位报警 */
|
||
"EMO", /* 40003.04 EMO */
|
||
"HH Exhuast Temp.", /* 40003.05 排气温度高高报警 */
|
||
"L General Exhaust Pressure", /* 40003.06 一般排压力低报警 */
|
||
"O3 HH", /* 40003.07 O3浓度高高报警 */
|
||
"H Reaction Chamber Temp. Delay", /* 40003.08 反应腔温度高延时报警 */
|
||
"Reaction Chamber Temp.Out", /* 40003.09 反应腔顶盖超温报警 */
|
||
"HH Water Tank Temp.", /* 40003.10 水箱温度高高报警 */
|
||
"Water Leakage", /* 40003.11 水箱漏液报警 */
|
||
"Containment Leakage", /* 40003.12 围堰漏液报警 */
|
||
"Smoke", /* 40003.13 烟雾报警 */
|
||
"Heat Source Failure", /* 40003.14 热源失效报警 */
|
||
"Circulation Pump Alarm" /* 40003.15 循环水泵异常报警 */
|
||
};
|
||
|
||
char *proto_modbus_master_warning[64] = {
|
||
"Inlet1 Pressure Broken", /* 40004.00 进口压力1断线警告 */
|
||
"Inlet2 Pressure Broken", /* 40004.01 进口压力2断线警告 */
|
||
"Inlet3 Pressure Broken", /* 40004.02 进口压力3断线警告 */
|
||
"Inlet4 Pressure Broken", /* 40004.03 备口压力1断线警告 */
|
||
"Inlet5 Pressure Broken", /* 40004.04 备口压力2断线警告 */
|
||
"Inlet6 Pressure Broken", /* 40004.05 备口压力3断线警告 */
|
||
"Outlet Pressure Broken", /* 40004.06 出口压力断线警告 */
|
||
"Inlet Valve_1 Open Limit Warning", /* 40004.07 特气进口阀1开限位警告 */
|
||
"Warning", /* 40004.08 二级警告 */
|
||
"H Inlet1 Pressure", /* 40004.09 进口压力1高警告 */
|
||
"H Inlet2 Pressure", /* 40004.10 进口压力2高警告 */
|
||
"H Inlet3 Pressure", /* 40004.11 进口压力3高警告 */
|
||
"H Inlet4 Pressure", /* 40004.12 备口压力1高警告 */
|
||
"H Inlet5 Pressure", /* 40004.13 备口压力2高警告 */
|
||
"H Inlet6 Pressure", /* 40004.14 备口压力3高警告 */
|
||
"H Outlet Pressure", /* 40004.15 出口压力高警告 */
|
||
"Inlet Valve_5 Close Limit Warning", /* 40005.00 特气进口阀4关限位警告 */
|
||
"Inlet Valve_6 Open Limit Warning", /* 40005.01 特气进口阀5开限位警告 */
|
||
"Inlet Valve_6 Close Limit Warning", /* 40005.02 特气进口阀6关限位警告 */
|
||
"H water tank temp.", /* 40005.03 水箱温度高警告 */
|
||
"H exhuast temp.", /* 40005.04 排气温度高警告 */
|
||
"H reaction chamber temp.", /* 40005.05 反应腔温度高警告 */
|
||
"L reaction chamber temp.", /* 40005.06 反应腔温度低警告 */
|
||
"Water tank temp. Broken", /* 40005.07 水箱温度断线警告 */
|
||
"Inlet Valve_1 Close Limit Warning", /* 40005.08 特气进口阀2开限位警告 */
|
||
"Inlet Valve_2 Open Limit Warning", /* 40005.09 特气进口阀3开限位警告 */
|
||
"Inlet Valve_2 Close Limit Warning", /* 40005.10 特气进口阀4开限位警告 */
|
||
"Inlet Valve_3 Open Limit Warning", /* 40005.11 特气进口阀5开限位警告 */
|
||
"Inlet Valve_3 Close Limit Warning", /* 40005.12 特气进口阀6开限位警告 */
|
||
"Inlet Valve_4 Open Limit Warning", /* 40005.13 特气进口阀1关限位警告 */
|
||
"Inlet Valve_4 Close Limit Warning", /* 40005.14 特气进口阀2关限位警告 */
|
||
"Inlet Valve_5 Open Limit Warning", /* 40005.15 特气进口阀3关限位警告 */
|
||
"PCW valve closed with flow", /* 40006.00 PCW阀关有流量警告 */
|
||
"LSW valve closed with flow", /* 40006.01 CW阀关有流量警告 */
|
||
"L torch Current", /* 40006.02 等离子电流低警告 */
|
||
"H torch Current", /* 40006.03 等离子电流高警告 */
|
||
"L torch Voltage", /* 40006.04 等离子电压低警告 */
|
||
"H torch Voltage", /* 40006.05 等离子电压高警告 */
|
||
"Torch current sensor Broken", /* 40006.06 等离子电流断线警告 */
|
||
"Torch voltage sensor Broken", /* 40006.07 等离子电压断线警告 */
|
||
"Exhuast temp. Broken", /* 40006.08 排气温度断线警告 */
|
||
"Reaction chamber temp. Broken", /* 40006.09 反应腔温度断线警告 */
|
||
"L CDA pressure", /* 40006.10 CDA压力低警告 */
|
||
"L N2 pressure", /* 40006.11 N2压力低警告 */
|
||
"L LSW flow", /* 40006.12 CW流量低警告 */
|
||
"L PCW flow", /* 40006.13 PCW流量低警告 */
|
||
"L cathode nitrogen flow", /* 40006.14 等离子N2流量低警告 */
|
||
"H cathode nitrogen flow", /* 40006.15 等离子N2流量高警告 */
|
||
"Inlet_5 H belt jacket temp.", /* 40007.00 备口2加热带温度高警告 */
|
||
"Inlet_6 H belt jacket temp.", /* 40007.01 备口3加热带温度高警告 */
|
||
"",
|
||
"",
|
||
"",
|
||
"",
|
||
"",
|
||
"",
|
||
"H Cabinet exhuast Temp.", /* 40007.08 一般排温度高警告 */
|
||
"Cabinet exhuast Temp. Broken", /* 40007.09 一般排温度断线警告 */
|
||
"Cabinet exhuast pressure Broken", /* 40007.10 一般排压力断线警告 */
|
||
"Cabinet door open", /* 40007.11 机箱门打开警告 */
|
||
"Inlet_1 H belt jacket temp.", /* 40007.12 入口1加热带温度高警告 */
|
||
"Inlet_2 H belt jacket temp.", /* 40007.13 入口2加热带温度高警告 */
|
||
"Inlet_3 H belt jacket temp.", /* 40007.14 入口3加热带温度高警告 */
|
||
"Inlet_4 H belt jacket temp." /* 40007.15 备口1加热带温度高警告 */
|
||
};
|
||
|
||
/* 全局实例对象 */
|
||
proto_modbus_t modbus_tcp = {
|
||
.req_state = 0,
|
||
.last_req_time = 0,
|
||
.retry_cnt = 0,
|
||
.transaction_id = 0,
|
||
.no_response_count = 0,
|
||
.data = {0},
|
||
.init = modbus_tcp_init_local,
|
||
.task = modbus_tcp_task,
|
||
.write = modbus_tcp_write_control,
|
||
};
|
||
|
||
/* 内部指针简化访问 */
|
||
static proto_modbus_t *pModbusTcp = &modbus_tcp;
|
||
|
||
/*******************************************************************************
|
||
* 函数名 : modbus_tcp_init_local
|
||
* 描述 : 初始化 Modbus TCP 主机:绑定接收回调,重置状态变量
|
||
* 输入 : 无
|
||
* 输出 : 无
|
||
* 返回值 : 无
|
||
* 说明 : 该函数通过 modbus_tcp.init 指针调用
|
||
*******************************************************************************/
|
||
static void modbus_tcp_init_local(void) {
|
||
/* 将 W5500 第0个Socket的接收回调绑定到本模块的解析函数 */
|
||
W5500_1.W5500_Class[0].Rx_DataAnalysis = modbus_tcp_parse_response;
|
||
|
||
pModbusTcp->transaction_id = 0;
|
||
pModbusTcp->req_state = 0;
|
||
pModbusTcp->last_req_time = 0;
|
||
pModbusTcp->retry_cnt = 0;
|
||
pModbusTcp->write_pending = 0;
|
||
memset(&pModbusTcp->data, 0, sizeof(pModbusTcp->data));/*清除设备数据*/
|
||
memset(&pModbusTcp->alarm_ctx, 0, sizeof(pModbusTcp->alarm_ctx));/*清除时间*/
|
||
memset(&pModbusTcp->status_ctx,0,sizeof(pModbusTcp->status_ctx));
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* 函数名 : modbus_tcp_send_read
|
||
* 描述 : 发送 Modbus TCP 读保持寄存器请求(功能码0x03)
|
||
* 输入 : start : 起始寄存器地址(16位)
|
||
* num : 要读取的寄存器数量
|
||
* 输出 : 无
|
||
* 返回值 : 无
|
||
* 说明 : 自动递增事务ID,通过 W5500 Socket 发送
|
||
*******************************************************************************/
|
||
static void modbus_tcp_send_read(u16 start, u16 num) {
|
||
u8 req[12];
|
||
|
||
pModbusTcp->transaction_id++;
|
||
req[0] = (pModbusTcp->transaction_id >> 8) & 0xFF; /* 事务ID高字节 */
|
||
req[1] = pModbusTcp->transaction_id & 0xFF; /* 事务ID低字节 */
|
||
req[2] = 0x00; req[3] = 0x00; /* 协议标识符,固定0x0000 */
|
||
req[4] = 0x00; req[5] = 0x06; /* 后续字节数(固定6) */
|
||
req[6] = MODBUS_TCP_DEFAULT_UNIT_ID; /* 单元标识符 */
|
||
req[7] = 0x03; /* 功能码:读保持寄存器 */
|
||
req[8] = (start >> 8) & 0xFF; /* 起始地址高字节 */
|
||
req[9] = start & 0xFF; /* 起始地址低字节 */
|
||
req[10] = (num >> 8) & 0xFF; /* 寄存器数量高字节 */
|
||
req[11] = num & 0xFF; /* 寄存器数量低字节 */
|
||
|
||
/* 通过 W5500 的第0个Socket发送 */
|
||
W5500_1.Socket_Send(&W5500_1.W5500_Class[0], req, 12);
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* 函数名 : modbus_tcp_send_write
|
||
* 描述 : 发送 Modbus TCP 写寄存器请求
|
||
* 输入 : start : 寄存器地址(16位)
|
||
* n
|
||
* 输出 : 无
|
||
* 返回值 : 无
|
||
* 说明 : 自动递增事务ID,通过 W5500 Socket 发送
|
||
*******************************************************************************/
|
||
static void modbus_tcp_send_write(u16 reg_addr, u16 value) {
|
||
u8 req[12];
|
||
|
||
pModbusTcp->transaction_id++;
|
||
pModbusTcp->write_tid = pModbusTcp->transaction_id;
|
||
pModbusTcp->write_pending = 2;
|
||
pModbusTcp->write_result = 0;
|
||
|
||
req[0] = (pModbusTcp->transaction_id >> 8) & 0xFF;
|
||
req[1] = pModbusTcp->transaction_id & 0xFF;
|
||
req[2] = 0x00; req[3] = 0x00; /* 协议标识符 */
|
||
req[4] = 0x00; req[5] = 0x06; /* 后续字节数(固定6) */
|
||
req[6] = MODBUS_TCP_DEFAULT_UNIT_ID;
|
||
req[7] = 0x06; /* 功能码:写单个寄存器 */
|
||
req[8] = (reg_addr >> 8) & 0xFF;
|
||
req[9] = reg_addr & 0xFF;
|
||
req[10] = (value >> 8) & 0xFF;
|
||
req[11] = value & 0xFF;
|
||
|
||
W5500_1.Socket_Send(&W5500_1.W5500_Class[0], req, 12);
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* 函数名 : modbus_tcp_parse_response
|
||
* 描述 : 解析 W5500 收到的 Modbus TCP 响应,根据当前请求状态存储数据
|
||
* 输入 : pClass - W5500 端口类对象指针(未使用,匹配回调原型)
|
||
* data - 接收到的数据缓冲区指针
|
||
* len - 数据长度
|
||
* 输出 : 无
|
||
* 返回值 : 无
|
||
* 说明 : 该函数由 W5500 的 Rx_DataAnalysis 回调调用
|
||
*******************************************************************************/
|
||
static void modbus_tcp_parse_response(bsp_W5500_Class_t *pClass, u8 *data, u16 len) {
|
||
(void)pClass; /* 避免未使用参数警告 */
|
||
|
||
/* 最小长度检查:MBAP头(7) + 功能码(1) + 字节数(1) = 9 */
|
||
if (len < 9) return;
|
||
|
||
u16 tid = (data[0] << 8) | data[1];
|
||
if (tid != pModbusTcp->transaction_id) return; /* 事务ID不匹配则丢弃 */
|
||
|
||
if (data[2] != 0 || data[3] != 0) return; /* 协议标识符必须为0 */
|
||
if (data[6] != MODBUS_TCP_DEFAULT_UNIT_ID) return;
|
||
|
||
if (data[7] == 0x83) { /* 异常响应(功能码+0x80) */
|
||
pModbusTcp->req_state = 0; /* 放弃当前轮次 */
|
||
return;
|
||
}
|
||
|
||
/* ---- 处理写单个寄存器响应 (0x06) ---- */
|
||
if (data[7] == 0x06) {
|
||
if (tid == pModbusTcp->write_tid) {
|
||
pModbusTcp->write_pending = 1;
|
||
pModbusTcp->write_result = 1; /* 成功 */
|
||
}
|
||
return;
|
||
}
|
||
|
||
/* 异常响应(写操作异常) */
|
||
if (data[7] == 0x86) { /* 0x06 + 0x80 */
|
||
if (tid == pModbusTcp->write_tid) {
|
||
pModbusTcp->write_pending = 3;
|
||
pModbusTcp->write_result = 0; /* 失败 */
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (data[7] != 0x03) return; /* 非预期功能码 */
|
||
|
||
u8 byte_count = data[8];
|
||
if (len < (9 + byte_count)) return;
|
||
|
||
/* 根据当前请求状态解析数据 */
|
||
switch (pModbusTcp->req_state) {
|
||
case 1: { /* 设备状态寄存器(40000~40007) */
|
||
if (byte_count == 16) { /* 8个寄存器 × 2字节 */
|
||
u8 *p = &data[9];
|
||
for (int i = 0; i < 8; i++) {
|
||
pModbusTcp->data.reg_status[i] = (p[0] << 8) | p[1];
|
||
p += 2;
|
||
}
|
||
pModbusTcp->req_state = 2; /* 进入下一分组 */
|
||
pModbusTcp->last_req_time = 0; /* 立即发送下一请求 */
|
||
pModbusTcp->retry_cnt = 0;
|
||
pModbusTcp->no_response_count = 0;
|
||
pModbusTcp->alarm_ctx.offline = 0;
|
||
}
|
||
} break;
|
||
|
||
case 2: { /* 浮点仪表寄存器(40050~40100) */
|
||
if (byte_count == 104) { /* 26个浮点 × 4字节 = 104字节 */
|
||
u8 *p = &data[9];
|
||
for (int i = 0; i < 26; i++) {
|
||
u32 tmp = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
|
||
pModbusTcp->data.float_values[i] = *((float*)&tmp);
|
||
p += 4;
|
||
}
|
||
pModbusTcp->req_state = 3;
|
||
pModbusTcp->last_req_time = 0;
|
||
pModbusTcp->retry_cnt = 0;
|
||
pModbusTcp->no_response_count = 0;
|
||
pModbusTcp->alarm_ctx.offline = 0;
|
||
}
|
||
} break;
|
||
|
||
case 3: { /* 整数仪表寄存器(40250~40252) */
|
||
if (byte_count == 6) { /* 3个寄存器 × 2字节 */
|
||
u8 *p = &data[9];
|
||
for (int i = 0; i < 3; i++) {
|
||
pModbusTcp->data.int_values[i] = (p[0] << 8) | p[1];
|
||
p += 2;
|
||
}
|
||
pModbusTcp->req_state = 4;
|
||
pModbusTcp->last_req_time = HAL_GetTick(); /* 记录完成时间*/
|
||
pModbusTcp->retry_cnt = 0;
|
||
pModbusTcp->no_response_count = 0;
|
||
pModbusTcp->alarm_ctx.offline = 0;
|
||
}
|
||
} break;
|
||
|
||
case 4: /*20253~20260代表sn*/
|
||
{
|
||
if(byte_count == 20) /*10个寄存器,20个字节*/
|
||
{
|
||
u8 *p = &data[9];
|
||
u8 len = p[0];
|
||
if (len > 20) len = 20;
|
||
if (len > 0) {
|
||
memcpy(pModbusTcp->data.sn_str, &p[1], len);
|
||
pModbusTcp->data.sn_str[len] = '\0';
|
||
} else {
|
||
pModbusTcp->data.sn_str[0] = '\0';
|
||
}
|
||
// 存储原始寄存器值
|
||
for (int i = 0; i < 10; i++) {
|
||
pModbusTcp->data.sn[i] = (p[1 + i*2] << 8) | p[2 + i*2];
|
||
}
|
||
pModbusTcp->req_state = 0; /* 全部完成,回到空闲 */
|
||
pModbusTcp->last_req_time = HAL_GetTick();
|
||
pModbusTcp->retry_cnt = 0;
|
||
pModbusTcp->no_response_count = 0;
|
||
pModbusTcp->alarm_ctx.offline = 0;
|
||
}
|
||
}
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
/*******************************************************************************
|
||
* 函数名 : modbus_tcp_task
|
||
* 描述 : Modbus TCP 主机周期任务:由外部调度器每500ms调用一次。
|
||
* 任务内部检查连接状态,空闲时立即发起第一个请求;
|
||
* 等待响应时检查超时并重试或跳转至下一分组。
|
||
* 输入 : 无
|
||
* 输出 : 无
|
||
* 返回值 : 无
|
||
* 说明 : 该函数由 modbus_tcp.task 指针调用,外部调度周期决定轮询间隔
|
||
*******************************************************************************/
|
||
static void modbus_tcp_task(void) {
|
||
/* 检查 W5500 第0个Socket的连接状态 */
|
||
if (!(W5500_1.W5500_Class[0].Run_State & BSP_W5500_PORT_RUN_STATE_CONN)) {
|
||
pModbusTcp->req_state = 0; /* 未连接则复位状态 */
|
||
pModbusTcp->alarm_ctx.offline = 1;
|
||
return;
|
||
}
|
||
|
||
u32 now = HAL_GetTick();
|
||
|
||
/* 空闲状态:直接发起第一个请求(设备状态) */
|
||
if (pModbusTcp->req_state == 0) {
|
||
modbus_tcp_send_read(MODBUS_REG_DEVICE_STATUS_START,
|
||
MODBUS_REG_DEVICE_STATUS_NUM);
|
||
pModbusTcp->req_state = 1;
|
||
pModbusTcp->last_req_time = now;
|
||
pModbusTcp->retry_cnt = 0;
|
||
return;
|
||
}
|
||
|
||
/* 等待响应状态:若超时则重试或跳过当前分组 */
|
||
if (now - pModbusTcp->last_req_time >= RESP_TIMEOUT_MS) {
|
||
pModbusTcp->retry_cnt++;
|
||
if (pModbusTcp->retry_cnt < 4) {
|
||
/* 重发当前分组的请求 */
|
||
u16 start, num;
|
||
switch (pModbusTcp->req_state) {
|
||
case 1: start = MODBUS_REG_DEVICE_STATUS_START; num = MODBUS_REG_DEVICE_STATUS_NUM; break;
|
||
case 2: start = MODBUS_REG_INSTRUMENT_FLOAT_START; num = MODBUS_REG_INSTRUMENT_FLOAT_NUM; break;
|
||
case 3: start = MODBUS_REG_INSTRUMENT_INT_START; num = MODBUS_REG_INSTRUMENT_INT_NUM; break;
|
||
case 4: start = MODBUS_REG_INSTRUMENT_SN_START; num = MODBUS_REG_INSTRUMENT_SN_NUM; break;
|
||
default: return;
|
||
}
|
||
modbus_tcp_send_read(start, num);
|
||
pModbusTcp->last_req_time = now;
|
||
} else {
|
||
/* 重试次数耗尽,强制进入下一分组或回到空闲 */
|
||
pModbusTcp->retry_cnt = 0;
|
||
pModbusTcp->no_response_count++;
|
||
if (pModbusTcp->no_response_count >= 3) {
|
||
pModbusTcp->alarm_ctx.offline = 1;
|
||
}else
|
||
{
|
||
pModbusTcp->alarm_ctx.offline = 0;
|
||
// pModbusTcp->no_response_count = 0;
|
||
}
|
||
if (pModbusTcp->req_state < 4) {
|
||
pModbusTcp->req_state++; /* 跳至下一分组 */
|
||
} else {
|
||
pModbusTcp->req_state = 0; /* 所有分组完成,回到空闲 */
|
||
}
|
||
/* 将 last_req_time 置0,使得下一次调用时会立即进入超时分支,发送新分组 */
|
||
pModbusTcp->last_req_time = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*写入单个寄存器*/
|
||
static void modbus_tcp_write_control(u16 bit_mask) {
|
||
if (!(W5500_1.W5500_Class[0].Run_State & BSP_W5500_PORT_RUN_STATE_CONN)) {
|
||
return; /* 未连接不操作 */
|
||
}
|
||
|
||
// 读取当前寄存器值
|
||
u16 current_val = pModbusTcp->data.reg_status[7];
|
||
|
||
// 清除所有控制位 (bit3~bit6)
|
||
u16 clear_mask = CONTROL_BIT_START | CONTROL_BIT_STOP |
|
||
CONTROL_BIT_IDLE_OPEN | CONTROL_BIT_IDLE_CLOSE;
|
||
u16 new_val = current_val & ~clear_mask;
|
||
|
||
// 设置目标位
|
||
new_val |= bit_mask;
|
||
/* 发送写请求 */
|
||
modbus_tcp_send_write(MODBUS_REG_CONTROL_ADDR, new_val);
|
||
}
|