#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); }