From d1fde88dd1c5b049ccf797c8203629ee11aa49ce Mon Sep 17 00:00:00 2001 From: wangfq Date: Thu, 27 Aug 2026 08:55:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(DLD154Pro):=20RS485=20=E7=9C=81=20Flash=20?= =?UTF-8?q?=E5=87=8F=E9=85=8D=20+=20=E5=9B=BA=E4=BB=B6=20V3.05?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为节省 Flash 资源,注释掉 RS485 侧一批低频/非核心协议分支与调试打印: - Modbus: FC 0x03(读保持寄存器)/0x10(批量写)/0x11(Report Slave ID)分发禁用; IR 0x0006/0x0007(Variation)/0x000A/0x000B/0x000C 读禁用(Variation 走全数据块 0x010A/0x010B);HR 0x0000–0x0004/0x0009/0x000A 写禁用(FC 0x06 仅剩 0x0010/0x0011/0x0012 有效) - 700BS: 0x3D DIP 查询禁用 + 4 处调试 PRINT 移除 - MB_MAX_READ_REGS 125→40(40×2=80B 与 MB_MAX_DATA_BYTES 匹配,原 60 会越界 40B) - 呼吸灯光耦加速步长 32→64;OTA 跳转前 mDelaymS(5) 移除 - cmcng.h 版本 V3.04 → V3.05(字符串 + SUB 宏同步) 协议文档已同步标注未实现(rs485-modbus V1.8 / rs485-700bs),见 docs commit。 --- .../APP/include/cmcng.h | 4 +- .../APP/include/rs485_modbus.h | 113 +- BLE/DLD154Pro_Peripheral_28K/APP/peripheral.c | 2 +- .../APP/peripheral_main.c | 2 +- .../APP/rs485_700bs.c | 10 +- .../APP/rs485_modbus.c | 1708 ++++++++--------- 6 files changed, 920 insertions(+), 919 deletions(-) diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/include/cmcng.h b/BLE/DLD154Pro_Peripheral_28K/APP/include/cmcng.h index c3556da..4f0faef 100644 --- a/BLE/DLD154Pro_Peripheral_28K/APP/include/cmcng.h +++ b/BLE/DLD154Pro_Peripheral_28K/APP/include/cmcng.h @@ -17,10 +17,10 @@ */ #define PRODUCT_MODEL "DLD154Pro" -#define FIRMWARE_VER "3.04" +#define FIRMWARE_VER "3.05" #define HARDWARE_VER "3.01" #define FIRMWARE_VER_MAIN 3 -#define FIRMWARE_VER_SUB 4 +#define FIRMWARE_VER_SUB 5 /* 归一化基数: variation 换算 dL/L 时除以该值 (2^19=524288, 必须与 peripheral_main.c MEASUREMENT_BASE 同步) */ #define LOOP_MEASURE_BASE 524288L diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/include/rs485_modbus.h b/BLE/DLD154Pro_Peripheral_28K/APP/include/rs485_modbus.h index bfe01d4..d103887 100644 --- a/BLE/DLD154Pro_Peripheral_28K/APP/include/rs485_modbus.h +++ b/BLE/DLD154Pro_Peripheral_28K/APP/include/rs485_modbus.h @@ -1,56 +1,57 @@ -/* - * rs485_modbus.h - * DLD154Pro RS485 Modbus RTU 从机协议 — V1.6 私有扩展 - * - * 物理层: RS485 半双工 (共享 UART0, 与 700BS 互斥) - * 帧格式: ADDR(1) + FC(1) + DATA(N) + CRC16(2) - * 主动上报: 3 级速率 (事件立即 / 活动 150ms / 空闲 800ms), FC 0x04 响应格式 - * - * 2026-07-30 初始实现 - */ -#ifndef __RS485_MODBUS_H__ -#define __RS485_MODBUS_H__ - -#include - -/*=========================================================================== - * 协议常量 - *===========================================================================*/ - -/* 寄存器地址范围 */ -#define MB_IR_FULL_BLOCK_START 0x0100 /* 全数据块起始地址 */ -#define MB_IR_FULL_BLOCK_COUNT 19 /* 全数据块寄存器数 (0x0100–0x0112) */ - -#define MB_HR_AUTO_RPT_ENABLE 0x0010 /* 主动上报使能 (0=关, 1=开) */ -#define MB_HR_ACTIVE_THRESHOLD 0x0011 /* 活动状态阈值 (|Variation|≥N) */ -#define MB_HR_SYSTEM_CMD 0x0012 /* 系统指令: 写 0x2C2C 复位 MCU */ -#define MB_CMD_RESET_MCU 0x2C2C /* 复位 MCU 指令值 */ - -/* 主动上报间隔 (mstick() 5ms/tick) */ -#define MB_RPT_IDLE_MS 160 /* 空闲: 800ms / 5ms = 160 tick */ -#define MB_RPT_ACTIVE_MS 30 /* 活动: 150ms / 5ms = 30 tick */ -#define MB_RPT_ACTIVE_HYST 2 /* 降速迟滞: 连续 N 次低于阈值 */ - -/* 帧超时: t3.5 = 3.5 × 11 / 9600 ≈ 4.0ms, 取 2 tick (10ms) */ -#define MB_FRAME_TIMEOUT_TICK 2 - -/* 全数据块 CRC 后的帧总长: - * addr(1) + fc(1) + byte_cnt(1) + data(38) + crc(2) = 43 */ -#define MB_FULL_DATA_FRAME_LEN 43 - -/* FC 0x03/0x04 读寄存器缓冲 (Modbus 标准: max qty = 0x007D = 125) */ -#define MB_MAX_READ_REGS 125 /* 单次读取最大寄存器数 */ -#define MB_MAX_DATA_BYTES 250 /* 125 × 2 字节 */ -#define MB_RESP_BUF_SIZE (5 + MB_MAX_DATA_BYTES) /* addr+fc+bc+data+crc */ - -/*=========================================================================== - * API - *===========================================================================*/ - -/* 初始化 Modbus 协议 (UART0 已在 rs485_init 中配置, 此处仅做协议状态初始化) */ -void modbus_init(void); - -/* 主循环轮询 — 帧接收 & 主动上报调度 (替代 rs485_poll) */ -void modbus_poll(void); - -#endif /* __RS485_MODBUS_H__ */ +/* + * rs485_modbus.h + * DLD154Pro RS485 Modbus RTU 从机协议 — V1.6 私有扩展 + * + * 物理层: RS485 半双工 (共享 UART0, 与 700BS 互斥) + * 帧格式: ADDR(1) + FC(1) + DATA(N) + CRC16(2) + * 主动上报: 3 级速率 (事件立即 / 活动 150ms / 空闲 800ms), FC 0x04 响应格式 + * + * 2026-07-30 初始实现 + */ +#ifndef __RS485_MODBUS_H__ +#define __RS485_MODBUS_H__ + +#include + +/*=========================================================================== + * 协议常量 + *===========================================================================*/ + +/* 寄存器地址范围 */ +#define MB_IR_FULL_BLOCK_START 0x0100 /* 全数据块起始地址 */ +#define MB_IR_FULL_BLOCK_COUNT 19 /* 全数据块寄存器数 (0x0100–0x0112) */ + +#define MB_HR_AUTO_RPT_ENABLE 0x0010 /* 主动上报使能 (0=关, 1=开) */ +#define MB_HR_ACTIVE_THRESHOLD 0x0011 /* 活动状态阈值 (|Variation|≥N) */ +#define MB_HR_SYSTEM_CMD 0x0012 /* 系统指令: 写 0x2C2C 复位 MCU */ +#define MB_CMD_RESET_MCU 0x2C2C /* 复位 MCU 指令值 */ + +/* 主动上报间隔 (mstick() 5ms/tick) */ +#define MB_RPT_IDLE_MS 160 /* 空闲: 800ms / 5ms = 160 tick */ +#define MB_RPT_ACTIVE_MS 30 /* 活动: 150ms / 5ms = 30 tick */ +#define MB_RPT_ACTIVE_HYST 2 /* 降速迟滞: 连续 N 次低于阈值 */ + +/* 帧超时: t3.5 = 3.5 × 11 / 9600 ≈ 4.0ms, 取 2 tick (10ms) */ +#define MB_FRAME_TIMEOUT_TICK 2 + +/* 全数据块 CRC 后的帧总长: + * addr(1) + fc(1) + byte_cnt(1) + data(38) + crc(2) = 43 */ +#define MB_FULL_DATA_FRAME_LEN 43 + +/* FC 0x03/0x04 读寄存器缓冲 (Modbus 标准: max qty = 0x007D = 125) */ +/* ⚠ 40/80 匹配: 40 寄存器 × 2B = 80B 缓冲 (2026-08-27 省 Flash 减配, 原 60 会越界 40B) */ +#define MB_MAX_READ_REGS 40 //125 /* 单次读取最大寄存器数 */ +#define MB_MAX_DATA_BYTES 80 //250 /* 40 × 2 字节 */ +#define MB_RESP_BUF_SIZE (5 + MB_MAX_DATA_BYTES) /* addr+fc+bc+data+crc */ + +/*=========================================================================== + * API + *===========================================================================*/ + +/* 初始化 Modbus 协议 (UART0 已在 rs485_init 中配置, 此处仅做协议状态初始化) */ +void modbus_init(void); + +/* 主循环轮询 — 帧接收 & 主动上报调度 (替代 rs485_poll) */ +void modbus_poll(void); + +#endif /* __RS485_MODBUS_H__ */ diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/peripheral.c b/BLE/DLD154Pro_Peripheral_28K/APP/peripheral.c index c344918..e0d95ed 100644 --- a/BLE/DLD154Pro_Peripheral_28K/APP/peripheral.c +++ b/BLE/DLD154Pro_Peripheral_28K/APP/peripheral.c @@ -730,7 +730,7 @@ static void simpleProfileChangeCB(uint8_t paramID, uint8_t *pValue, uint16_t len // 0x4F, 0x54, 0x41 // PRINT("jump OTA \n"); WWDG_ResetCfg(DISABLE); - mDelaymS(5); + // mDelaymS(5); Jump_OTA(); } diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/peripheral_main.c b/BLE/DLD154Pro_Peripheral_28K/APP/peripheral_main.c index 4c6cf40..40ba1c0 100644 --- a/BLE/DLD154Pro_Peripheral_28K/APP/peripheral_main.c +++ b/BLE/DLD154Pro_Peripheral_28K/APP/peripheral_main.c @@ -101,7 +101,7 @@ uint8_t g_rs485_id = 0; #define BREATH_HOLD_LOW 3 /* 光耦有信号时加速参数 (周期 ~1s) */ -#define BREATH_OPTO_DUTY_STEP 32 // 步长 4x → 渐亮/渐灭各 4 步 (~0.5s 周期) +#define BREATH_OPTO_DUTY_STEP 64 // 步长 4x → 渐亮/渐灭各 4 步 (~0.5s 周期) #define BREATH_OPTO_HOLD_TICKS 1 // 保持 50ms /* 看门狗超时: Fsys=60MHz, WDOG_CLK=Fsys/131072≈458Hz, 0xFF≈558ms */ diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/rs485_700bs.c b/BLE/DLD154Pro_Peripheral_28K/APP/rs485_700bs.c index ca7340a..c0e0398 100644 --- a/BLE/DLD154Pro_Peripheral_28K/APP/rs485_700bs.c +++ b/BLE/DLD154Pro_Peripheral_28K/APP/rs485_700bs.c @@ -62,7 +62,7 @@ void rs485_init(void) RS485_RE_RX(); // 默认接收模式 _hb_last_ms = mstick(); // 对齐心跳起始时刻,避免首次立即触发 - PRINT("RS485: init ok, addr=%d, baud=%d\n", g_rs485_id, RS485_BAUD); + // PRINT("RS485: init ok, addr=%d, baud=%d\n", g_rs485_id, RS485_BAUD); } /*=========================================================================== @@ -126,7 +126,7 @@ static void handle_query_freq(uint8_t addr) static void handle_reset_init(uint8_t addr) { SYS_ResetExecute(); - PRINT("RS485: coil reset init\n"); + // PRINT("RS485: coil reset init\n"); } /* 0x3D — 查询拨码 DIP1-5 (DLD154Pro 无物理拨码, 从 BLE 配置映射) */ @@ -222,7 +222,7 @@ static void rs485_process_frame(void) uint8_t xorr = _rx_buf[2]; if ((stx ^ addr) != xorr) { - PRINT("RS485: XOR fail %02X %02X %02X\n", stx, addr, xorr); + // PRINT("RS485: XOR fail %02X %02X %02X\n", stx, addr, xorr); _rx_len = 0; return; } @@ -237,9 +237,9 @@ static void rs485_process_frame(void) case CMD_QUERY_STATUS: handle_query_status(addr); break; case CMD_QUERY_FREQ: handle_query_freq(addr); break; case CMD_RESET_INIT: handle_reset_init(addr); break; - case CMD_QUERY_DIP: handle_query_dip(addr); break; + // case CMD_QUERY_DIP: handle_query_dip(addr); break; default: - PRINT("RS485: unknown cmd 0x%02X\n", stx); + // PRINT("RS485: unknown cmd 0x%02X\n", stx); break; } diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/rs485_modbus.c b/BLE/DLD154Pro_Peripheral_28K/APP/rs485_modbus.c index cda5e3b..d81befd 100644 --- a/BLE/DLD154Pro_Peripheral_28K/APP/rs485_modbus.c +++ b/BLE/DLD154Pro_Peripheral_28K/APP/rs485_modbus.c @@ -1,854 +1,854 @@ -/* - * rs485_modbus.c - * DLD154Pro RS485 Modbus RTU 从机协议 — V1.6 私有扩展 - * - * 实现功能: - * FC 0x02 — 读离散输入 (车辆状态/故障/光耦/继电器) - * FC 0x03 — 读保持寄存器 (配置参数 + 主动上报控制) - * FC 0x04 — 读输入寄存器 (传感器数据 + 全数据块 0x0100–0x0112) - * FC 0x06 — 写单寄存器 - * FC 0x10 — 批量写寄存器 - * FC 0x11 — Report Slave ID - * 主动上报 — 3 级速率调度, 帧格式复用 FC 0x04 响应 - * - * 2026-07-30 初始实现 - */ -#include "CONFIG.h" -#include "CH59x_common.h" -#include "cmcng.h" -#include "rs485_modbus.h" -#include "rs485_700bs.h" /* rs485_send(), UART ISR 共享缓冲 */ -#include "storage.h" -#include "dbn_ble_srv.h" -#include - -/*=========================================================================== - * CRC16 查表 — poly=0xA001, init=0xFFFF - *===========================================================================*/ -static const uint16_t _crc16_table[256] = { - 0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241, - 0xC601,0x06C0,0x0780,0xC741,0x0500,0xC5C1,0xC481,0x0440, - 0xCC01,0x0CC0,0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40, - 0x0A00,0xCAC1,0xCB81,0x0B40,0xC901,0x09C0,0x0880,0xC841, - 0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81,0x1A40, - 0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41, - 0x1400,0xD4C1,0xD581,0x1540,0xD701,0x17C0,0x1680,0xD641, - 0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040, - 0xF001,0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240, - 0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0,0x3480,0xF441, - 0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41, - 0xFA01,0x3AC0,0x3B80,0xFB41,0x3900,0xF9C1,0xF881,0x3840, - 0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41, - 0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40, - 0xE401,0x24C0,0x2580,0xE541,0x2700,0xE7C1,0xE681,0x2640, - 0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041, - 0xA001,0x60C0,0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240, - 0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480,0xA441, - 0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41, - 0xAA01,0x6AC0,0x6B80,0xAB41,0x6900,0xA9C1,0xA881,0x6840, - 0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41, - 0xBE01,0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40, - 0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1,0xB681,0x7640, - 0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041, - 0x5000,0x90C1,0x9181,0x5140,0x9301,0x53C0,0x5280,0x9241, - 0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440, - 0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40, - 0x5A00,0x9AC1,0x9B81,0x5B40,0x9901,0x59C0,0x5880,0x9841, - 0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40, - 0x4E00,0x8EC1,0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41, - 0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680,0x8641, - 0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040, -}; - -static uint16_t crc16(const uint8_t *data, uint8_t len) -{ - uint16_t crc = 0xFFFF; - for (uint8_t i = 0; i < len; i++) { - crc = (crc >> 8) ^ _crc16_table[(crc ^ data[i]) & 0xFF]; - } - return crc; -} - -/*=========================================================================== - * 访问共享的 700BS ISR 接收缓冲 - * - * rs485_700bs.c 的 UART0_IRQHandler 负责填充这 3 个变量, - * Modbus 轮询时从同一缓冲读取。 - *===========================================================================*/ -extern uint8_t _rx_buf[16]; -extern uint8_t _rx_len; -extern uint32_t _rx_last_tick; -extern uint8_t loop1_LOOP_OK; /* 线圈连接状态 (1=正常, 0=断开) */ - -/*=========================================================================== - * Modbus 协议状态 - *===========================================================================*/ -static uint8_t _auto_rpt_enable = 0; /* HR 0x0010 影子 */ -static uint16_t _active_threshold = 10; /* HR 0x0011 影子, 默认 10 */ - -/* 主动上报调度 */ -static uint32_t _rpt_last_ms = 0; /* 上次上报时刻 (mstick) */ -static uint8_t _rpt_active = 0; /* 当前上报速率: 0=空闲, 1=活动 */ -static uint8_t _rpt_hyst_cnt = 0; /* 降速迟滞计数器 */ - -/* 车间距 / 通过时间 (10ms 单位) */ -static uint32_t _last_relay_off_10ms = 0; /* 上次继电器释放时刻 (mstick/2) */ -static uint32_t _last_relay_on_10ms = 0; /* 上次继电器吸合时刻 (mstick/2) */ -static uint8_t _last_relay_state = 0; /* 继电器状态快照 (边沿检测) */ -static uint16_t _misc_time_type = 0xFFFF; /* 0=间隙, 1=通过, 0xFFFF=无效 */ -static uint32_t _misc_time_value = 0; /* 时间值 (10ms 单位) */ - -/* 计数器 (上电清零) */ -static uint32_t _relay_engage_cnt = 0; /* 继电器吸合累计次数 */ - -/* 线圈状态追踪 (断开/重连 事件触发) */ -static uint8_t _last_loop_ok = 0; /* 线圈连接状态快照 (边沿检测) */ - -/*=========================================================================== - * 主动上报 — 全数据块组装 & 发送 - *===========================================================================*/ - -/* 计算线圈频率 (Hz) */ -static uint32_t calc_freq(void) -{ - extern uint32_t loop1_CAPVD; - extern uint16_t loop1_LPCNT; - if (loop1_CAPVD == 0 || loop1_LPCNT == 0) return 0; - return (uint32_t)((uint64_t)FREQ_SYS * loop1_LPCNT / loop1_CAPVD) + 360; -} - -/* 计算 Variation = CAPVD − Origin */ -static int32_t calc_variation(void) -{ - extern uint32_t loop1_CAPVD; - extern uint32_t loop1_Origin; - return (int32_t)(loop1_CAPVD - loop1_Origin); -} - -/* 构建全数据块 (19 寄存器 = 38 字节, big-endian) */ -static void build_full_data(uint8_t *buf) -{ - extern uint32_t loop1_CAPVD; - extern uint32_t loop1_Origin; - extern uint8_t loop1_LOOP_OK; - extern uint32_t g_loop_cut_amount; - int32_t var; - uint32_t freq, capvd, origin, val32; - - /* 线圈侧: 只读快照 */ - var = calc_variation(); - freq = calc_freq(); - capvd = loop1_CAPVD; - origin = loop1_Origin; - - /* 0x0100 Sensitivity */ - buf[0] = 0x00; - buf[1] = g_loop_cng_unit.sensitvity & 0x0F; - - /* 0x0101 Freq Level */ - buf[2] = 0x00; - buf[3] = g_freq_level & 0x01; - - /* 0x0102 Fusion Mode */ - buf[4] = 0x00; - buf[5] = g_output_radar_mode & 0x0F; - - /* 0x0103 Car State */ - buf[6] = 0x00; - buf[7] = g_loop_acs_info.car_state ? 0x01 : 0x00; - - /* 0x0104 Loop Status: bit0=断开, bit1=稳定 */ - { - uint8_t st = 0; - if (!loop1_LOOP_OK) st |= 0x01; - if (g_loop_stable) st |= 0x02; - buf[8] = 0x00; - buf[9] = st; - } - - /* 0x0105 Opto Status (光耦输入低电平有效) */ - { - uint8_t opto = (GPIOB_ReadPortPin(GPIO_Pin_10) == 0) ? 1 : 0; - buf[10] = 0x00; - buf[11] = opto; - } - - /* 0x0106–0x0107 Loop Freq (Hz, 32-bit big-endian) */ - buf[12] = (uint8_t)(freq >> 24); - buf[13] = (uint8_t)(freq >> 16); - buf[14] = (uint8_t)(freq >> 8); - buf[15] = (uint8_t)(freq); - - /* 0x0108–0x0109 CAPVD (32-bit big-endian) */ - buf[16] = (uint8_t)(capvd >> 24); - buf[17] = (uint8_t)(capvd >> 16); - buf[18] = (uint8_t)(capvd >> 8); - buf[19] = (uint8_t)(capvd); - - /* 0x010A–0x010B Variation (32-bit signed, big-endian) */ - val32 = (uint32_t)var; - buf[20] = (uint8_t)(val32 >> 24); - buf[21] = (uint8_t)(val32 >> 16); - buf[22] = (uint8_t)(val32 >> 8); - buf[23] = (uint8_t)(val32); - - /* 0x010C–0x010D Cut Cnt (线圈断开累计, 32-bit big-endian) */ - val32 = g_loop_cut_amount; - buf[24] = (uint8_t)(val32 >> 24); - buf[25] = (uint8_t)(val32 >> 16); - buf[26] = (uint8_t)(val32 >> 8); - buf[27] = (uint8_t)(val32); - - /* 0x010E–0x010F Relay Cnt (继电器吸合累计, 32-bit big-endian) */ - buf[28] = (uint8_t)(_relay_engage_cnt >> 24); - buf[29] = (uint8_t)(_relay_engage_cnt >> 16); - buf[30] = (uint8_t)(_relay_engage_cnt >> 8); - buf[31] = (uint8_t)(_relay_engage_cnt); - - /* 0x0110 Time Type */ - buf[32] = (uint8_t)(_misc_time_type >> 8); - buf[33] = (uint8_t)(_misc_time_type); - - /* 0x0111–0x0112 Misc Time (10ms 单位, 32-bit big-endian) */ - buf[34] = (uint8_t)(_misc_time_value >> 24); - buf[35] = (uint8_t)(_misc_time_value >> 16); - buf[36] = (uint8_t)(_misc_time_value >> 8); - buf[37] = (uint8_t)(_misc_time_value); -} - -/* 发送全数据块 (FC 0x04 响应格式) */ -static void send_full_data(void) -{ - uint8_t buf[MB_FULL_DATA_FRAME_LEN]; - uint16_t crc; - - buf[0] = g_rs485_id & 0xFF; /* 从机地址 */ - buf[1] = 0x04; /* FC 0x04 */ - buf[2] = 38; /* 字节计数 = 19 × 2 */ - build_full_data(&buf[3]); - crc = crc16(buf, 41); - buf[41] = (uint8_t)(crc); - buf[42] = (uint8_t)(crc >> 8); - - rs485_send(buf, MB_FULL_DATA_FRAME_LEN); -} - -/* 更新车间距 / 通过时间 (每次 relay state 翻转时调用) */ -static void update_misc_time(void) -{ - uint32_t now_10ms = mstick() / 2; /* 5ms/tick → 10ms 单位 */ - uint8_t relay = g_loop_acs_info.relay1_state; - - if (relay == _last_relay_state) return; - - if (relay) { - /* 0→1 吸合沿: 车间距 = now − 上次释放时刻 */ - _misc_time_value = now_10ms - _last_relay_off_10ms; - _misc_time_type = 0; /* 间隙时间 */ - _last_relay_on_10ms = now_10ms; - _relay_engage_cnt++; - } else { - /* 1→0 释放沿: 通过时间 = now − 上次吸合时刻 */ - _misc_time_value = now_10ms - _last_relay_on_10ms; - _misc_time_type = 1; /* 通过时间 */ - _last_relay_off_10ms = now_10ms; - } - _last_relay_state = relay; -} - -/*=========================================================================== - * 主动上报速率调度 - *===========================================================================*/ - -/* 判断是否处于活动状态 (|Variation| ≥ 阈值) */ -static uint8_t is_active(void) -{ - int32_t var = calc_variation(); - if (var < 0) var = -var; - return ((uint32_t)var >= _active_threshold) ? 1 : 0; -} - -/* 主动上报调度 — 每 tick 调用一次 */ -static void modbus_report_schedule(void) -{ - if (!_auto_rpt_enable) return; - - /* 继电器翻转 → 立即上报 */ - { - uint8_t relay = g_loop_acs_info.relay1_state; - if (relay != _last_relay_state) { - update_misc_time(); /* 先更新时间值 */ - send_full_data(); /* 立即发送 */ - _rpt_last_ms = mstick(); - _rpt_hyst_cnt = 0; - _rpt_active = is_active(); - return; - } - } - - /* 线圈断开/重连 → 立即上报 (对齐 700BS 行为) */ - { - uint8_t loop_ok = loop1_LOOP_OK; - if (loop_ok != _last_loop_ok) { - _last_loop_ok = loop_ok; - update_misc_time(); - send_full_data(); - _rpt_last_ms = mstick(); - _rpt_hyst_cnt = 0; - _rpt_active = is_active(); - return; - } - } - - uint32_t now = mstick(); - uint32_t elapsed = now - _rpt_last_ms; - - if (is_active()) { - /* 活动态: 150ms 间隔 */ - if (elapsed >= MB_RPT_ACTIVE_MS) { - update_misc_time(); - send_full_data(); - _rpt_last_ms = now; - _rpt_active = 1; - _rpt_hyst_cnt = 0; - } - } else { - /* 空闲态: 需要先确认降速 (连续 2 次低于阈值) */ - if (_rpt_active) { - _rpt_hyst_cnt++; - if (_rpt_hyst_cnt < MB_RPT_ACTIVE_HYST) { - /* 仍在迟滞期, 保持活动间隔 */ - if (elapsed >= MB_RPT_ACTIVE_MS) { - update_misc_time(); - send_full_data(); - _rpt_last_ms = now; - } - return; - } - /* 迟滞期满 → 降速 */ - _rpt_active = 0; - _rpt_hyst_cnt = 0; - _rpt_last_ms = now; /* 重置计时, 不立即发 */ - } - - /* 空闲态: 800ms 间隔 */ - if (elapsed >= MB_RPT_IDLE_MS) { - update_misc_time(); - send_full_data(); - _rpt_last_ms = now; - } - } -} - -/*=========================================================================== - * 异常响应 - *===========================================================================*/ - -static void send_exception(uint8_t fc, uint8_t excode) -{ - uint8_t buf[5]; - uint16_t crc; - - buf[0] = g_rs485_id & 0xFF; - buf[1] = fc | 0x80; /* 功能码最高位置 1 */ - buf[2] = excode; - crc = crc16(buf, 3); - buf[3] = (uint8_t)(crc); - buf[4] = (uint8_t)(crc >> 8); - - rs485_send(buf, 5); -} - -/*=========================================================================== - * FC 0x02 — 读离散输入 - *===========================================================================*/ - -static void handle_read_discrete_inputs(const uint8_t *rx, uint8_t rx_len) -{ - (void)rx_len; /* 固定 8 字节请求 */ - extern uint8_t loop1_LOOP_OK; - - uint16_t start_addr = ((uint16_t)rx[2] << 8) | rx[3]; - uint16_t quantity = ((uint16_t)rx[4] << 8) | rx[5]; - - if (quantity == 0 || quantity > 16) { - send_exception(0x02, 0x03); /* Illegal Data Value */ - return; - } - - /* 构建 9-bit 位图 (9 个离散输入: 0x0000–0x0008) */ - uint16_t bits = 0; - if (g_loop_acs_info.car_state) bits |= (1 << 0); /* 0x0000 */ - if (!loop1_LOOP_OK) bits |= (1 << 1); /* 0x0001 */ - if (g_loop_stable) bits |= (1 << 2); /* 0x0002 */ - if (GPIOB_ReadPortPin(GPIO_Pin_10) == 0) bits |= (1 << 3); /* 0x0003 光耦有效=低电平 */ - if (!g_loop_power_up_state) bits |= (1 << 4); /* 0x0004 上电初始化 */ - if (g_loop_acs_info.relay1_state) bits |= (1 << 5); /* 0x0005 */ - /* 0x0006 Freq Low Fault — DLD154Pro 未实现, 恒为 0 */ - /* 0x0007 Freq High Fault — DLD154Pro 未实现, 恒为 0 */ - /* 0x0008 Init Flag — DLD154Pro 未实现, 恒为 0 */ - - if (start_addr >= 9) { - send_exception(0x02, 0x02); /* Illegal Data Address */ - return; - } - - /* 读取指定位段 */ - uint16_t mask = ((1 << quantity) - 1) << start_addr; - uint8_t val = (uint8_t)((bits & mask) >> start_addr); - - uint8_t buf[6]; - uint16_t crc; - buf[0] = g_rs485_id & 0xFF; - buf[1] = 0x02; - buf[2] = 1; /* 1 字节数据 */ - buf[3] = val; - crc = crc16(buf, 4); - buf[4] = (uint8_t)(crc); - buf[5] = (uint8_t)(crc >> 8); - - rs485_send(buf, 6); -} - -/*=========================================================================== - * FC 0x04 — 读输入寄存器 (含全数据块 0x0100–0x0112) - *===========================================================================*/ - -static uint8_t read_ir_register(uint16_t addr, uint8_t *hi, uint8_t *lo) -{ - extern uint32_t loop1_CAPVD; - extern uint32_t loop1_Origin; - extern uint8_t loop1_LOOP_OK; - extern uint32_t g_loop_cut_amount; - int32_t var; - uint32_t freq, capvd, val32; - - switch (addr) { - /* 基础 IR 0x0000–0x000C (保持向后兼容) */ - case 0x0000: /* Freq Hi */ - freq = calc_freq(); - *hi = (uint8_t)(freq >> 24); *lo = (uint8_t)(freq >> 16); return 1; - case 0x0001: /* Freq Lo */ - freq = calc_freq(); - *hi = (uint8_t)(freq >> 8); *lo = (uint8_t)(freq); return 1; - case 0x0002: /* CAPVD Hi */ - *hi = (uint8_t)(loop1_CAPVD >> 24); *lo = (uint8_t)(loop1_CAPVD >> 16); return 1; - case 0x0003: /* CAPVD Lo */ - *hi = (uint8_t)(loop1_CAPVD >> 8); *lo = (uint8_t)(loop1_CAPVD); return 1; - case 0x0004: /* Origin Hi */ - *hi = (uint8_t)(loop1_Origin >> 24); *lo = (uint8_t)(loop1_Origin >> 16); return 1; - case 0x0005: /* Origin Lo */ - *hi = (uint8_t)(loop1_Origin >> 8); *lo = (uint8_t)(loop1_Origin); return 1; - case 0x0006: /* Variation Hi */ - var = (int32_t)(loop1_CAPVD - loop1_Origin); - val32 = (uint32_t)var; - *hi = (uint8_t)(val32 >> 24); *lo = (uint8_t)(val32 >> 16); return 1; - case 0x0007: /* Variation Lo */ - var = (int32_t)(loop1_CAPVD - loop1_Origin); - val32 = (uint32_t)var; - *hi = (uint8_t)(val32 >> 8); *lo = (uint8_t)(val32); return 1; - case 0x0008: /* FW Version (BCD: v3.01 → 0x0301) */ - *hi = FIRMWARE_VER_MAIN; *lo = FIRMWARE_VER_SUB; return 1; - case 0x0009: /* HW Version */ - *hi = HARDWARE_VER_MAIN; *lo = HARDWARE_VER_SUB; return 1; - case 0x000A: /* Freq Sens */ - *hi = 0; *lo = g_loop_cng_unit.sensitvity & 0x0F; return 1; - case 0x000B: /* Condition */ - *hi = 0; *lo = g_loop_acs_info.condition; return 1; - case 0x000C: /* DIP Virtual: bit[7:4]=function_mode, bit[3:0]=fusion_mode */ - *hi = 0; *lo = (g_function_mode << 4) | (g_output_radar_mode & 0x0F); return 1; - - /* 全数据块 0x0100–0x0112 — 热路径, 直接计算 */ - default: - if (addr >= 0x0100 && addr <= 0x0112) { - /* build_full_data 的偏移量映射 */ - uint8_t off = (uint8_t)((addr - 0x0100) * 2); - uint8_t tmp[38]; - build_full_data(tmp); - if (off < 38) { - *hi = tmp[off]; - *lo = tmp[off + 1]; - return 1; - } - } - return 0; /* 非法地址 */ - } -} - -static void handle_read_input_regs(const uint8_t *rx, uint8_t rx_len) -{ - (void)rx_len; - uint16_t start_addr = ((uint16_t)rx[2] << 8) | rx[3]; - uint16_t quantity = ((uint16_t)rx[4] << 8) | rx[5]; - - if (quantity == 0 || quantity > MB_MAX_READ_REGS) { - send_exception(0x04, 0x03); - return; - } - - uint8_t data_buf[MB_MAX_DATA_BYTES]; - uint16_t reg_addr; - - for (uint16_t i = 0; i < quantity; i++) { - reg_addr = start_addr + i; - uint8_t hi, lo; - if (!read_ir_register(reg_addr, &hi, &lo)) { - send_exception(0x04, 0x02); - return; - } - data_buf[i * 2] = hi; - data_buf[i * 2 + 1] = lo; - } - - uint8_t byte_cnt = (uint8_t)(quantity * 2); - uint8_t buf[MB_RESP_BUF_SIZE]; - uint16_t crc; - - buf[0] = g_rs485_id & 0xFF; - buf[1] = 0x04; - buf[2] = byte_cnt; - memcpy(&buf[3], data_buf, byte_cnt); - crc = crc16(buf, 3 + byte_cnt); - buf[3 + byte_cnt] = (uint8_t)(crc); - buf[3 + byte_cnt + 1] = (uint8_t)(crc >> 8); - - rs485_send(buf, 3 + byte_cnt + 2); -} - -/*=========================================================================== - * FC 0x03 — 读保持寄存器 - *===========================================================================*/ - -static uint8_t read_hr_register(uint16_t addr, uint8_t *hi, uint8_t *lo) -{ - switch (addr) { - case 0x0000: /* Sensitivity */ - *hi = 0; *lo = g_loop_cng_unit.sensitvity & 0x0F; return 1; - case 0x0001: /* Fusion Mode */ - *hi = 0; *lo = g_output_radar_mode & 0x0F; return 1; - case 0x0002: /* Hold Time */ - *hi = 0; *lo = g_loop_cng_unit.exist_mode; return 1; - case 0x0003: /* Output Delay */ - *hi = 0; *lo = g_loop_cng_unit.delay_time; return 1; - case 0x0004: /* Freq Level */ - *hi = 0; *lo = g_freq_level & 0x01; return 1; - case 0x0005: /* RS485 Baud Hi (只读) */ - *hi = (uint8_t)(g_storage_uart_baud >> 24); *lo = (uint8_t)(g_storage_uart_baud >> 16); return 1; - case 0x0006: /* RS485 Baud Lo (只读) */ - *hi = (uint8_t)(g_storage_uart_baud >> 8); *lo = (uint8_t)(g_storage_uart_baud); return 1; - case 0x0007: /* RS485 Addr (只读) */ - *hi = 0; *lo = g_rs485_id; return 1; - case 0x0008: /* Protocol (只读) */ - *hi = 0; *lo = g_fm_700bs_disable; return 1; - case 0x0009: /* BT Timeout */ - *hi = 0; *lo = g_max_counter_bt_min; return 1; - case 0x000A: /* Safe Timeout */ - *hi = 0; *lo = g_loop_cng_unit.loopSafe_Timeout; return 1; - case 0x000B: /* Opto Func (只读) */ - *hi = 0; *lo = (g_function_mode >> 1) & 0x01; return 1; - case 0x0010: /* Auto Rpt Enable */ - *hi = 0; *lo = _auto_rpt_enable; return 1; - case 0x0011: /* Active Threshold */ - *hi = (uint8_t)(_active_threshold >> 8); *lo = (uint8_t)(_active_threshold); return 1; - case 0x0012: /* System Cmd (只读, 恒为 0) */ - *hi = 0; *lo = 0; return 1; - default: - return 0; - } -} - -static void handle_read_holding_regs(const uint8_t *rx, uint8_t rx_len) -{ - (void)rx_len; - uint16_t start_addr = ((uint16_t)rx[2] << 8) | rx[3]; - uint16_t quantity = ((uint16_t)rx[4] << 8) | rx[5]; - - if (quantity == 0 || quantity > MB_MAX_READ_REGS) { - send_exception(0x03, 0x03); - return; - } - - uint8_t data_buf[MB_MAX_DATA_BYTES]; - - for (uint16_t i = 0; i < quantity; i++) { - uint16_t reg_addr = start_addr + i; - uint8_t hi, lo; - if (!read_hr_register(reg_addr, &hi, &lo)) { - send_exception(0x03, 0x02); - return; - } - data_buf[i * 2] = hi; - data_buf[i * 2 + 1] = lo; - } - - uint8_t byte_cnt = (uint8_t)(quantity * 2); - uint8_t buf[MB_RESP_BUF_SIZE]; - uint16_t crc; - - buf[0] = g_rs485_id & 0xFF; - buf[1] = 0x03; - buf[2] = byte_cnt; - memcpy(&buf[3], data_buf, byte_cnt); - crc = crc16(buf, 3 + byte_cnt); - buf[3 + byte_cnt] = (uint8_t)(crc); - buf[3 + byte_cnt + 1] = (uint8_t)(crc >> 8); - - rs485_send(buf, 3 + byte_cnt + 2); -} - -/*=========================================================================== - * FC 0x06 — 写单寄存器 - *===========================================================================*/ - -static void handle_write_single_reg(const uint8_t *rx, uint8_t rx_len) -{ - (void)rx_len; - uint16_t reg_addr = ((uint16_t)rx[2] << 8) | rx[3]; - uint16_t reg_val = ((uint16_t)rx[4] << 8) | rx[5]; - - /* 只读寄存器拦截 */ - switch (reg_addr) { - case 0x0005: case 0x0006: case 0x0007: case 0x0008: case 0x000B: - send_exception(0x06, 0x02); /* Illegal Data Address (只读) */ - return; - } - - /* 值校验 & 写入 */ - switch (reg_addr) { - case 0x0000: /* Sensitivity: 0-3 */ - if (reg_val > 3) { send_exception(0x06, 0x03); return; } - g_loop_cng_unit.sensitvity = (uint8_t)reg_val; - break; - case 0x0001: /* Fusion Mode: 0-6 */ - if (reg_val > 6) { send_exception(0x06, 0x03); return; } - g_output_radar_mode = (uint8_t)reg_val; - g_loop_cng_unit.direction_mode = (g_function_mode << 4) | (g_output_radar_mode & 0x0F); - break; - case 0x0002: /* Hold Time: 0-255 */ - g_loop_cng_unit.exist_mode = (uint8_t)reg_val; - g_hold_time = (uint16_t)reg_val * 20 * 5; - break; - case 0x0003: /* Output Delay: 0-255 */ - g_loop_cng_unit.delay_time = (uint8_t)reg_val; - break; - case 0x0004: /* Freq Level: 0-1 */ - if (reg_val > 1) { send_exception(0x06, 0x03); return; } - g_freq_level = (uint8_t)reg_val; - g_loop_cng_unit.loopFreq_Level = g_freq_level + 1; - break; - case 0x0009: /* BT Timeout: 0-255 */ - g_max_counter_bt_min = (uint8_t)reg_val; - g_max_counter_bt_timeout = (uint32_t)reg_val * 60 * 1000; - break; - case 0x000A: /* Safe Timeout: 0-255 */ - g_loop_cng_unit.loopSafe_Timeout = (uint8_t)reg_val; - break; - case 0x0010: /* Auto Rpt Enable: 0-1 */ - if (reg_val > 1) { send_exception(0x06, 0x03); return; } - _auto_rpt_enable = (uint8_t)reg_val; - if (_auto_rpt_enable) { - _rpt_last_ms = mstick(); /* 对齐计时起点 */ - _rpt_active = 0; - _rpt_hyst_cnt = 0; - } - break; - case 0x0011: /* Active Threshold: 0-65535 */ - _active_threshold = reg_val; - break; - case 0x0012: /* System Cmd: 写 0x2C2C 复位 MCU */ - if (reg_val == MB_CMD_RESET_MCU) { - SYS_ResetExecute(); - } - /* 非法值: 忽略, 不响应异常 (对齐 700BS 0x2C 行为) */ - return; - default: - send_exception(0x06, 0x02); /* Illegal Data Address */ - return; - } - - /* 回显 (Modbus 标准: 写成功的响应 = 请求原样返回) */ - { - uint8_t buf[8]; - uint16_t crc; - buf[0] = g_rs485_id & 0xFF; - buf[1] = 0x06; - buf[2] = rx[2]; buf[3] = rx[3]; /* 地址 */ - buf[4] = rx[4]; buf[5] = rx[5]; /* 值 */ - crc = crc16(buf, 6); - buf[6] = (uint8_t)(crc); - buf[7] = (uint8_t)(crc >> 8); - rs485_send(buf, 8); - } -} - -/*=========================================================================== - * FC 0x10 — 批量写寄存器 - * - * 简化实现: 逐寄存器调用 FC 0x06 的校验+写入逻辑 - *===========================================================================*/ - -static void handle_write_multiple_regs(const uint8_t *rx, uint8_t rx_len) -{ - (void)rx_len; - uint16_t start_addr = ((uint16_t)rx[2] << 8) | rx[3]; - uint16_t quantity = ((uint16_t)rx[4] << 8) | rx[5]; - uint8_t byte_cnt = rx[6]; - - if (quantity == 0 || quantity > 123 || byte_cnt != quantity * 2) { - send_exception(0x10, 0x03); - return; - } - - for (uint16_t i = 0; i < quantity; i++) { - uint16_t reg_addr = start_addr + i; - uint16_t reg_val = ((uint16_t)rx[7 + i * 2] << 8) | rx[8 + i * 2]; - - /* 只读拦截 */ - if (reg_addr == 0x0005 || reg_addr == 0x0006 || reg_addr == 0x0007 || - reg_addr == 0x0008 || reg_addr == 0x000B) { - send_exception(0x10, 0x02); - return; - } - - /* 值校验 — 同样的逻辑 */ - switch (reg_addr) { - case 0x0000: if (reg_val > 3) { send_exception(0x10, 0x03); return; } - g_loop_cng_unit.sensitvity = (uint8_t)reg_val; break; - case 0x0001: if (reg_val > 6) { send_exception(0x10, 0x03); return; } - g_output_radar_mode = (uint8_t)reg_val; - g_loop_cng_unit.direction_mode = (g_function_mode << 4) | (g_output_radar_mode & 0x0F); break; - case 0x0002: g_loop_cng_unit.exist_mode = (uint8_t)reg_val; - g_hold_time = (uint16_t)reg_val * 20 * 5; break; - case 0x0003: g_loop_cng_unit.delay_time = (uint8_t)reg_val; break; - case 0x0004: if (reg_val > 1) { send_exception(0x10, 0x03); return; } - g_freq_level = (uint8_t)reg_val; - g_loop_cng_unit.loopFreq_Level = g_freq_level + 1; break; - case 0x0009: g_max_counter_bt_min = (uint8_t)reg_val; - g_max_counter_bt_timeout = (uint32_t)reg_val * 60 * 1000; break; - case 0x000A: g_loop_cng_unit.loopSafe_Timeout = (uint8_t)reg_val; break; - case 0x0010: if (reg_val > 1) { send_exception(0x10, 0x03); return; } - _auto_rpt_enable = (uint8_t)reg_val; - if (_auto_rpt_enable) { _rpt_last_ms = mstick(); _rpt_active = 0; _rpt_hyst_cnt = 0; } - break; - case 0x0011: _active_threshold = reg_val; break; - case 0x0012: /* System Cmd: 写 0x2C2C 复位 MCU */ - if (reg_val == MB_CMD_RESET_MCU) { - SYS_ResetExecute(); - return; /* 不复位则忽略非法值 */ - } - /* 非法值: 忽略, 继续后续寄存器 */ - break; - default: send_exception(0x10, 0x02); return; - } - } - - /* 响应: 回显 addr + fc + start + qty */ - { - uint8_t buf[8]; - uint16_t crc; - buf[0] = g_rs485_id & 0xFF; - buf[1] = 0x10; - buf[2] = rx[2]; buf[3] = rx[3]; - buf[4] = rx[4]; buf[5] = rx[5]; - crc = crc16(buf, 6); - buf[6] = (uint8_t)(crc); - buf[7] = (uint8_t)(crc >> 8); - rs485_send(buf, 8); - } -} - -/*=========================================================================== - * FC 0x11 — Report Slave ID - *===========================================================================*/ - -static void handle_report_slave_id(const uint8_t *rx, uint8_t rx_len) -{ - (void)rx; (void)rx_len; - extern uint8_t loop1_LOOP_OK; - - /* 返回: addr + fc + byte_cnt + model(11) + status + crc */ - const char *model = PRODUCT_MODEL; /* "DLD154Pro" = 10 chars + '\0' = 11 */ - uint8_t model_len = (uint8_t)strlen(model) + 1; /* 含 '\0' */ - - uint8_t buf[5 + 12 + 2]; /* addr+fc+bc + model(max12) + status + crc */ - uint16_t crc; - uint8_t idx = 0; - - buf[idx++] = g_rs485_id & 0xFF; - buf[idx++] = 0x11; - buf[idx++] = model_len + 1; /* byte_cnt = model + status */ - memcpy(&buf[idx], model, model_len); - idx += model_len; - - /* Status: 0x00=RUN, 0xFF=STOP */ - buf[idx++] = loop1_LOOP_OK ? 0x00 : 0xFF; - - crc = crc16(buf, idx); - buf[idx++] = (uint8_t)(crc); - buf[idx++] = (uint8_t)(crc >> 8); - - rs485_send(buf, idx); -} - -/*=========================================================================== - * 初始化 & 轮询 - *===========================================================================*/ - -void modbus_init(void) -{ - /* 协议状态复位 */ - _auto_rpt_enable = 0; - _active_threshold = 10; - _rpt_last_ms = mstick(); - _rpt_active = 0; - _rpt_hyst_cnt = 0; - _last_relay_state = g_loop_acs_info.relay1_state; - _last_loop_ok = loop1_LOOP_OK; - _last_relay_off_10ms = 0; - _last_relay_on_10ms = 0; - _misc_time_type = 0xFFFF; - _misc_time_value = 0; - _relay_engage_cnt = 0; -} - -void modbus_poll(void) -{ - /*=== 帧接收 ===*/ - if (_rx_len > 0) { - uint32_t now = mstick(); - if (now - _rx_last_tick >= MB_FRAME_TIMEOUT_TICK) { - /* 帧超时 → 处理完整帧 */ - uint8_t *rx = _rx_buf; - uint8_t len = _rx_len; - - /* 最小帧: addr(1) + fc(1) + crc(2) = 4 */ - if (len >= 4) { - uint8_t addr = rx[0]; - uint8_t fc = rx[1]; - uint16_t crc_rx = ((uint16_t)rx[len - 1] << 8) | rx[len - 2]; /* 低字节在前 */ - uint16_t crc_calc = crc16(rx, len - 2); - - /* 地址匹配 (0=广播, 本从机不响应) */ - if (addr != 0 && addr == (g_rs485_id & 0xFF)) { - if (crc_rx == crc_calc) { - switch (fc) { - case 0x02: handle_read_discrete_inputs(rx, len); break; - case 0x03: handle_read_holding_regs(rx, len); break; - case 0x04: handle_read_input_regs(rx, len); break; - case 0x06: handle_write_single_reg(rx, len); break; - case 0x10: handle_write_multiple_regs(rx, len); break; - case 0x11: handle_report_slave_id(rx, len); break; - default: send_exception(fc, 0x01); break; - } - } - /* CRC 错误 → 静默 (Modbus 标准行为) */ - } - /* 地址不匹配 → 静默 */ - } - _rx_len = 0; /* 清空缓冲 */ - } - } - - /*=== 主动上报调度 ===*/ - modbus_report_schedule(); -} +/* + * rs485_modbus.c + * DLD154Pro RS485 Modbus RTU 从机协议 — V1.6 私有扩展 + * + * 实现功能: + * FC 0x02 — 读离散输入 (车辆状态/故障/光耦/继电器) + * FC 0x03 — 读保持寄存器 (配置参数 + 主动上报控制) + * FC 0x04 — 读输入寄存器 (传感器数据 + 全数据块 0x0100–0x0112) + * FC 0x06 — 写单寄存器 + * FC 0x10 — 批量写寄存器 + * FC 0x11 — Report Slave ID + * 主动上报 — 3 级速率调度, 帧格式复用 FC 0x04 响应 + * + * 2026-07-30 初始实现 + */ +#include "CONFIG.h" +#include "CH59x_common.h" +#include "cmcng.h" +#include "rs485_modbus.h" +#include "rs485_700bs.h" /* rs485_send(), UART ISR 共享缓冲 */ +#include "storage.h" +#include "dbn_ble_srv.h" +#include + +/*=========================================================================== + * CRC16 查表 — poly=0xA001, init=0xFFFF + *===========================================================================*/ +static const uint16_t _crc16_table[256] = { + 0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241, + 0xC601,0x06C0,0x0780,0xC741,0x0500,0xC5C1,0xC481,0x0440, + 0xCC01,0x0CC0,0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40, + 0x0A00,0xCAC1,0xCB81,0x0B40,0xC901,0x09C0,0x0880,0xC841, + 0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81,0x1A40, + 0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41, + 0x1400,0xD4C1,0xD581,0x1540,0xD701,0x17C0,0x1680,0xD641, + 0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040, + 0xF001,0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240, + 0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0,0x3480,0xF441, + 0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41, + 0xFA01,0x3AC0,0x3B80,0xFB41,0x3900,0xF9C1,0xF881,0x3840, + 0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41, + 0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40, + 0xE401,0x24C0,0x2580,0xE541,0x2700,0xE7C1,0xE681,0x2640, + 0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041, + 0xA001,0x60C0,0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240, + 0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480,0xA441, + 0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41, + 0xAA01,0x6AC0,0x6B80,0xAB41,0x6900,0xA9C1,0xA881,0x6840, + 0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41, + 0xBE01,0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40, + 0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1,0xB681,0x7640, + 0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041, + 0x5000,0x90C1,0x9181,0x5140,0x9301,0x53C0,0x5280,0x9241, + 0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440, + 0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40, + 0x5A00,0x9AC1,0x9B81,0x5B40,0x9901,0x59C0,0x5880,0x9841, + 0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40, + 0x4E00,0x8EC1,0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41, + 0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680,0x8641, + 0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040, +}; + +static uint16_t crc16(const uint8_t *data, uint8_t len) +{ + uint16_t crc = 0xFFFF; + for (uint8_t i = 0; i < len; i++) { + crc = (crc >> 8) ^ _crc16_table[(crc ^ data[i]) & 0xFF]; + } + return crc; +} + +/*=========================================================================== + * 访问共享的 700BS ISR 接收缓冲 + * + * rs485_700bs.c 的 UART0_IRQHandler 负责填充这 3 个变量, + * Modbus 轮询时从同一缓冲读取。 + *===========================================================================*/ +extern uint8_t _rx_buf[16]; +extern uint8_t _rx_len; +extern uint32_t _rx_last_tick; +extern uint8_t loop1_LOOP_OK; /* 线圈连接状态 (1=正常, 0=断开) */ + +/*=========================================================================== + * Modbus 协议状态 + *===========================================================================*/ +static uint8_t _auto_rpt_enable = 0; /* HR 0x0010 影子 */ +static uint16_t _active_threshold = 10; /* HR 0x0011 影子, 默认 10 */ + +/* 主动上报调度 */ +static uint32_t _rpt_last_ms = 0; /* 上次上报时刻 (mstick) */ +static uint8_t _rpt_active = 0; /* 当前上报速率: 0=空闲, 1=活动 */ +static uint8_t _rpt_hyst_cnt = 0; /* 降速迟滞计数器 */ + +/* 车间距 / 通过时间 (10ms 单位) */ +static uint32_t _last_relay_off_10ms = 0; /* 上次继电器释放时刻 (mstick/2) */ +static uint32_t _last_relay_on_10ms = 0; /* 上次继电器吸合时刻 (mstick/2) */ +static uint8_t _last_relay_state = 0; /* 继电器状态快照 (边沿检测) */ +static uint16_t _misc_time_type = 0xFFFF; /* 0=间隙, 1=通过, 0xFFFF=无效 */ +static uint32_t _misc_time_value = 0; /* 时间值 (10ms 单位) */ + +/* 计数器 (上电清零) */ +static uint32_t _relay_engage_cnt = 0; /* 继电器吸合累计次数 */ + +/* 线圈状态追踪 (断开/重连 事件触发) */ +static uint8_t _last_loop_ok = 0; /* 线圈连接状态快照 (边沿检测) */ + +/*=========================================================================== + * 主动上报 — 全数据块组装 & 发送 + *===========================================================================*/ + +/* 计算线圈频率 (Hz) */ +static uint32_t calc_freq(void) +{ + extern uint32_t loop1_CAPVD; + extern uint16_t loop1_LPCNT; + if (loop1_CAPVD == 0 || loop1_LPCNT == 0) return 0; + return (uint32_t)((uint64_t)FREQ_SYS * loop1_LPCNT / loop1_CAPVD) + 360; +} + +/* 计算 Variation = CAPVD − Origin */ +static int32_t calc_variation(void) +{ + extern uint32_t loop1_CAPVD; + extern uint32_t loop1_Origin; + return (int32_t)(loop1_CAPVD - loop1_Origin); +} + +/* 构建全数据块 (19 寄存器 = 38 字节, big-endian) */ +static void build_full_data(uint8_t *buf) +{ + extern uint32_t loop1_CAPVD; + extern uint32_t loop1_Origin; + extern uint8_t loop1_LOOP_OK; + extern uint32_t g_loop_cut_amount; + int32_t var; + uint32_t freq, capvd, origin, val32; + + /* 线圈侧: 只读快照 */ + var = calc_variation(); + freq = calc_freq(); + capvd = loop1_CAPVD; + origin = loop1_Origin; + + /* 0x0100 Sensitivity */ + buf[0] = 0x00; + buf[1] = g_loop_cng_unit.sensitvity & 0x0F; + + /* 0x0101 Freq Level */ + buf[2] = 0x00; + buf[3] = g_freq_level & 0x01; + + /* 0x0102 Fusion Mode */ + buf[4] = 0x00; + buf[5] = g_output_radar_mode & 0x0F; + + /* 0x0103 Car State */ + buf[6] = 0x00; + buf[7] = g_loop_acs_info.car_state ? 0x01 : 0x00; + + /* 0x0104 Loop Status: bit0=断开, bit1=稳定 */ + { + uint8_t st = 0; + if (!loop1_LOOP_OK) st |= 0x01; + if (g_loop_stable) st |= 0x02; + buf[8] = 0x00; + buf[9] = st; + } + + /* 0x0105 Opto Status (光耦输入低电平有效) */ + { + uint8_t opto = (GPIOB_ReadPortPin(GPIO_Pin_10) == 0) ? 1 : 0; + buf[10] = 0x00; + buf[11] = opto; + } + + /* 0x0106–0x0107 Loop Freq (Hz, 32-bit big-endian) */ + buf[12] = (uint8_t)(freq >> 24); + buf[13] = (uint8_t)(freq >> 16); + buf[14] = (uint8_t)(freq >> 8); + buf[15] = (uint8_t)(freq); + + /* 0x0108–0x0109 CAPVD (32-bit big-endian) */ + buf[16] = (uint8_t)(capvd >> 24); + buf[17] = (uint8_t)(capvd >> 16); + buf[18] = (uint8_t)(capvd >> 8); + buf[19] = (uint8_t)(capvd); + + /* 0x010A–0x010B Variation (32-bit signed, big-endian) */ + val32 = (uint32_t)var; + buf[20] = (uint8_t)(val32 >> 24); + buf[21] = (uint8_t)(val32 >> 16); + buf[22] = (uint8_t)(val32 >> 8); + buf[23] = (uint8_t)(val32); + + /* 0x010C–0x010D Cut Cnt (线圈断开累计, 32-bit big-endian) */ + val32 = g_loop_cut_amount; + buf[24] = (uint8_t)(val32 >> 24); + buf[25] = (uint8_t)(val32 >> 16); + buf[26] = (uint8_t)(val32 >> 8); + buf[27] = (uint8_t)(val32); + + /* 0x010E–0x010F Relay Cnt (继电器吸合累计, 32-bit big-endian) */ + buf[28] = (uint8_t)(_relay_engage_cnt >> 24); + buf[29] = (uint8_t)(_relay_engage_cnt >> 16); + buf[30] = (uint8_t)(_relay_engage_cnt >> 8); + buf[31] = (uint8_t)(_relay_engage_cnt); + + /* 0x0110 Time Type */ + buf[32] = (uint8_t)(_misc_time_type >> 8); + buf[33] = (uint8_t)(_misc_time_type); + + /* 0x0111–0x0112 Misc Time (10ms 单位, 32-bit big-endian) */ + buf[34] = (uint8_t)(_misc_time_value >> 24); + buf[35] = (uint8_t)(_misc_time_value >> 16); + buf[36] = (uint8_t)(_misc_time_value >> 8); + buf[37] = (uint8_t)(_misc_time_value); +} + +/* 发送全数据块 (FC 0x04 响应格式) */ +static void send_full_data(void) +{ + uint8_t buf[MB_FULL_DATA_FRAME_LEN]; + uint16_t crc; + + buf[0] = g_rs485_id & 0xFF; /* 从机地址 */ + buf[1] = 0x04; /* FC 0x04 */ + buf[2] = 38; /* 字节计数 = 19 × 2 */ + build_full_data(&buf[3]); + crc = crc16(buf, 41); + buf[41] = (uint8_t)(crc); + buf[42] = (uint8_t)(crc >> 8); + + rs485_send(buf, MB_FULL_DATA_FRAME_LEN); +} + +/* 更新车间距 / 通过时间 (每次 relay state 翻转时调用) */ +static void update_misc_time(void) +{ + uint32_t now_10ms = mstick() / 2; /* 5ms/tick → 10ms 单位 */ + uint8_t relay = g_loop_acs_info.relay1_state; + + if (relay == _last_relay_state) return; + + if (relay) { + /* 0→1 吸合沿: 车间距 = now − 上次释放时刻 */ + _misc_time_value = now_10ms - _last_relay_off_10ms; + _misc_time_type = 0; /* 间隙时间 */ + _last_relay_on_10ms = now_10ms; + _relay_engage_cnt++; + } else { + /* 1→0 释放沿: 通过时间 = now − 上次吸合时刻 */ + _misc_time_value = now_10ms - _last_relay_on_10ms; + _misc_time_type = 1; /* 通过时间 */ + _last_relay_off_10ms = now_10ms; + } + _last_relay_state = relay; +} + +/*=========================================================================== + * 主动上报速率调度 + *===========================================================================*/ + +/* 判断是否处于活动状态 (|Variation| ≥ 阈值) */ +static uint8_t is_active(void) +{ + int32_t var = calc_variation(); + if (var < 0) var = -var; + return ((uint32_t)var >= _active_threshold) ? 1 : 0; +} + +/* 主动上报调度 — 每 tick 调用一次 */ +static void modbus_report_schedule(void) +{ + if (!_auto_rpt_enable) return; + + /* 继电器翻转 → 立即上报 */ + { + uint8_t relay = g_loop_acs_info.relay1_state; + if (relay != _last_relay_state) { + update_misc_time(); /* 先更新时间值 */ + send_full_data(); /* 立即发送 */ + _rpt_last_ms = mstick(); + _rpt_hyst_cnt = 0; + _rpt_active = is_active(); + return; + } + } + + /* 线圈断开/重连 → 立即上报 (对齐 700BS 行为) */ + { + uint8_t loop_ok = loop1_LOOP_OK; + if (loop_ok != _last_loop_ok) { + _last_loop_ok = loop_ok; + update_misc_time(); + send_full_data(); + _rpt_last_ms = mstick(); + _rpt_hyst_cnt = 0; + _rpt_active = is_active(); + return; + } + } + + uint32_t now = mstick(); + uint32_t elapsed = now - _rpt_last_ms; + + if (is_active()) { + /* 活动态: 150ms 间隔 */ + if (elapsed >= MB_RPT_ACTIVE_MS) { + update_misc_time(); + send_full_data(); + _rpt_last_ms = now; + _rpt_active = 1; + _rpt_hyst_cnt = 0; + } + } else { + /* 空闲态: 需要先确认降速 (连续 2 次低于阈值) */ + if (_rpt_active) { + _rpt_hyst_cnt++; + if (_rpt_hyst_cnt < MB_RPT_ACTIVE_HYST) { + /* 仍在迟滞期, 保持活动间隔 */ + if (elapsed >= MB_RPT_ACTIVE_MS) { + update_misc_time(); + send_full_data(); + _rpt_last_ms = now; + } + return; + } + /* 迟滞期满 → 降速 */ + _rpt_active = 0; + _rpt_hyst_cnt = 0; + _rpt_last_ms = now; /* 重置计时, 不立即发 */ + } + + /* 空闲态: 800ms 间隔 */ + if (elapsed >= MB_RPT_IDLE_MS) { + update_misc_time(); + send_full_data(); + _rpt_last_ms = now; + } + } +} + +/*=========================================================================== + * 异常响应 + *===========================================================================*/ + +static void send_exception(uint8_t fc, uint8_t excode) +{ + uint8_t buf[5]; + uint16_t crc; + + buf[0] = g_rs485_id & 0xFF; + buf[1] = fc | 0x80; /* 功能码最高位置 1 */ + buf[2] = excode; + crc = crc16(buf, 3); + buf[3] = (uint8_t)(crc); + buf[4] = (uint8_t)(crc >> 8); + + rs485_send(buf, 5); +} + +/*=========================================================================== + * FC 0x02 — 读离散输入 + *===========================================================================*/ + +static void handle_read_discrete_inputs(const uint8_t *rx, uint8_t rx_len) +{ + (void)rx_len; /* 固定 8 字节请求 */ + extern uint8_t loop1_LOOP_OK; + + uint16_t start_addr = ((uint16_t)rx[2] << 8) | rx[3]; + uint16_t quantity = ((uint16_t)rx[4] << 8) | rx[5]; + + if (quantity == 0 || quantity > 16) { + send_exception(0x02, 0x03); /* Illegal Data Value */ + return; + } + + /* 构建 9-bit 位图 (9 个离散输入: 0x0000–0x0008) */ + uint16_t bits = 0; + if (g_loop_acs_info.car_state) bits |= (1 << 0); /* 0x0000 */ + if (!loop1_LOOP_OK) bits |= (1 << 1); /* 0x0001 */ + if (g_loop_stable) bits |= (1 << 2); /* 0x0002 */ + if (GPIOB_ReadPortPin(GPIO_Pin_10) == 0) bits |= (1 << 3); /* 0x0003 光耦有效=低电平 */ + if (!g_loop_power_up_state) bits |= (1 << 4); /* 0x0004 上电初始化 */ + if (g_loop_acs_info.relay1_state) bits |= (1 << 5); /* 0x0005 */ + /* 0x0006 Freq Low Fault — DLD154Pro 未实现, 恒为 0 */ + /* 0x0007 Freq High Fault — DLD154Pro 未实现, 恒为 0 */ + /* 0x0008 Init Flag — DLD154Pro 未实现, 恒为 0 */ + + if (start_addr >= 9) { + send_exception(0x02, 0x02); /* Illegal Data Address */ + return; + } + + /* 读取指定位段 */ + uint16_t mask = ((1 << quantity) - 1) << start_addr; + uint8_t val = (uint8_t)((bits & mask) >> start_addr); + + uint8_t buf[6]; + uint16_t crc; + buf[0] = g_rs485_id & 0xFF; + buf[1] = 0x02; + buf[2] = 1; /* 1 字节数据 */ + buf[3] = val; + crc = crc16(buf, 4); + buf[4] = (uint8_t)(crc); + buf[5] = (uint8_t)(crc >> 8); + + rs485_send(buf, 6); +} + +/*=========================================================================== + * FC 0x04 — 读输入寄存器 (含全数据块 0x0100–0x0112) + *===========================================================================*/ + +static uint8_t read_ir_register(uint16_t addr, uint8_t *hi, uint8_t *lo) +{ + extern uint32_t loop1_CAPVD; + extern uint32_t loop1_Origin; + extern uint8_t loop1_LOOP_OK; + extern uint32_t g_loop_cut_amount; + int32_t var; + uint32_t freq, capvd, val32; + + switch (addr) { + /* 基础 IR 0x0000–0x000C (保持向后兼容) */ + case 0x0000: /* Freq Hi */ + freq = calc_freq(); + *hi = (uint8_t)(freq >> 24); *lo = (uint8_t)(freq >> 16); return 1; + case 0x0001: /* Freq Lo */ + freq = calc_freq(); + *hi = (uint8_t)(freq >> 8); *lo = (uint8_t)(freq); return 1; + case 0x0002: /* CAPVD Hi */ + *hi = (uint8_t)(loop1_CAPVD >> 24); *lo = (uint8_t)(loop1_CAPVD >> 16); return 1; + case 0x0003: /* CAPVD Lo */ + *hi = (uint8_t)(loop1_CAPVD >> 8); *lo = (uint8_t)(loop1_CAPVD); return 1; + case 0x0004: /* Origin Hi */ + *hi = (uint8_t)(loop1_Origin >> 24); *lo = (uint8_t)(loop1_Origin >> 16); return 1; + case 0x0005: /* Origin Lo */ + *hi = (uint8_t)(loop1_Origin >> 8); *lo = (uint8_t)(loop1_Origin); return 1; + // case 0x0006: /* Variation Hi */ + // var = (int32_t)(loop1_CAPVD - loop1_Origin); + // val32 = (uint32_t)var; + // *hi = (uint8_t)(val32 >> 24); *lo = (uint8_t)(val32 >> 16); return 1; + // case 0x0007: /* Variation Lo */ + // var = (int32_t)(loop1_CAPVD - loop1_Origin); + // val32 = (uint32_t)var; + // *hi = (uint8_t)(val32 >> 8); *lo = (uint8_t)(val32); return 1; + case 0x0008: /* FW Version (BCD: v3.01 → 0x0301) */ + *hi = FIRMWARE_VER_MAIN; *lo = FIRMWARE_VER_SUB; return 1; + case 0x0009: /* HW Version */ + *hi = HARDWARE_VER_MAIN; *lo = HARDWARE_VER_SUB; return 1; + // case 0x000A: /* Freq Sens */ + // *hi = 0; *lo = g_loop_cng_unit.sensitvity & 0x0F; return 1; + // case 0x000B: /* Condition */ + // *hi = 0; *lo = g_loop_acs_info.condition; return 1; + // case 0x000C: /* DIP Virtual: bit[7:4]=function_mode, bit[3:0]=fusion_mode */ + // *hi = 0; *lo = (g_function_mode << 4) | (g_output_radar_mode & 0x0F); return 1; + + /* 全数据块 0x0100–0x0112 — 热路径, 直接计算 */ + default: + if (addr >= 0x0100 && addr <= 0x0112) { + /* build_full_data 的偏移量映射 */ + uint8_t off = (uint8_t)((addr - 0x0100) * 2); + uint8_t tmp[38]; + build_full_data(tmp); + if (off < 38) { + *hi = tmp[off]; + *lo = tmp[off + 1]; + return 1; + } + } + return 0; /* 非法地址 */ + } +} + +static void handle_read_input_regs(const uint8_t *rx, uint8_t rx_len) +{ + (void)rx_len; + uint16_t start_addr = ((uint16_t)rx[2] << 8) | rx[3]; + uint16_t quantity = ((uint16_t)rx[4] << 8) | rx[5]; + + if (quantity == 0 || quantity > MB_MAX_READ_REGS) { + send_exception(0x04, 0x03); + return; + } + + uint8_t data_buf[MB_MAX_DATA_BYTES]; + uint16_t reg_addr; + + for (uint16_t i = 0; i < quantity; i++) { + reg_addr = start_addr + i; + uint8_t hi, lo; + if (!read_ir_register(reg_addr, &hi, &lo)) { + send_exception(0x04, 0x02); + return; + } + data_buf[i * 2] = hi; + data_buf[i * 2 + 1] = lo; + } + + uint8_t byte_cnt = (uint8_t)(quantity * 2); + uint8_t buf[MB_RESP_BUF_SIZE]; + uint16_t crc; + + buf[0] = g_rs485_id & 0xFF; + buf[1] = 0x04; + buf[2] = byte_cnt; + memcpy(&buf[3], data_buf, byte_cnt); + crc = crc16(buf, 3 + byte_cnt); + buf[3 + byte_cnt] = (uint8_t)(crc); + buf[3 + byte_cnt + 1] = (uint8_t)(crc >> 8); + + rs485_send(buf, 3 + byte_cnt + 2); +} + +/*=========================================================================== + * FC 0x03 — 读保持寄存器 + *===========================================================================*/ + +static uint8_t read_hr_register(uint16_t addr, uint8_t *hi, uint8_t *lo) +{ + switch (addr) { + case 0x0000: /* Sensitivity */ + *hi = 0; *lo = g_loop_cng_unit.sensitvity & 0x0F; return 1; + case 0x0001: /* Fusion Mode */ + *hi = 0; *lo = g_output_radar_mode & 0x0F; return 1; + case 0x0002: /* Hold Time */ + *hi = 0; *lo = g_loop_cng_unit.exist_mode; return 1; + case 0x0003: /* Output Delay */ + *hi = 0; *lo = g_loop_cng_unit.delay_time; return 1; + case 0x0004: /* Freq Level */ + *hi = 0; *lo = g_freq_level & 0x01; return 1; + case 0x0005: /* RS485 Baud Hi (只读) */ + *hi = (uint8_t)(g_storage_uart_baud >> 24); *lo = (uint8_t)(g_storage_uart_baud >> 16); return 1; + case 0x0006: /* RS485 Baud Lo (只读) */ + *hi = (uint8_t)(g_storage_uart_baud >> 8); *lo = (uint8_t)(g_storage_uart_baud); return 1; + case 0x0007: /* RS485 Addr (只读) */ + *hi = 0; *lo = g_rs485_id; return 1; + case 0x0008: /* Protocol (只读) */ + *hi = 0; *lo = g_fm_700bs_disable; return 1; + case 0x0009: /* BT Timeout */ + *hi = 0; *lo = g_max_counter_bt_min; return 1; + case 0x000A: /* Safe Timeout */ + *hi = 0; *lo = g_loop_cng_unit.loopSafe_Timeout; return 1; + case 0x000B: /* Opto Func (只读) */ + *hi = 0; *lo = (g_function_mode >> 1) & 0x01; return 1; + case 0x0010: /* Auto Rpt Enable */ + *hi = 0; *lo = _auto_rpt_enable; return 1; + case 0x0011: /* Active Threshold */ + *hi = (uint8_t)(_active_threshold >> 8); *lo = (uint8_t)(_active_threshold); return 1; + case 0x0012: /* System Cmd (只读, 恒为 0) */ + *hi = 0; *lo = 0; return 1; + default: + return 0; + } +} + +static void handle_read_holding_regs(const uint8_t *rx, uint8_t rx_len) +{ + (void)rx_len; + uint16_t start_addr = ((uint16_t)rx[2] << 8) | rx[3]; + uint16_t quantity = ((uint16_t)rx[4] << 8) | rx[5]; + + if (quantity == 0 || quantity > MB_MAX_READ_REGS) { + send_exception(0x03, 0x03); + return; + } + + uint8_t data_buf[MB_MAX_DATA_BYTES]; + + for (uint16_t i = 0; i < quantity; i++) { + uint16_t reg_addr = start_addr + i; + uint8_t hi, lo; + if (!read_hr_register(reg_addr, &hi, &lo)) { + send_exception(0x03, 0x02); + return; + } + data_buf[i * 2] = hi; + data_buf[i * 2 + 1] = lo; + } + + uint8_t byte_cnt = (uint8_t)(quantity * 2); + uint8_t buf[MB_RESP_BUF_SIZE]; + uint16_t crc; + + buf[0] = g_rs485_id & 0xFF; + buf[1] = 0x03; + buf[2] = byte_cnt; + memcpy(&buf[3], data_buf, byte_cnt); + crc = crc16(buf, 3 + byte_cnt); + buf[3 + byte_cnt] = (uint8_t)(crc); + buf[3 + byte_cnt + 1] = (uint8_t)(crc >> 8); + + rs485_send(buf, 3 + byte_cnt + 2); +} + +/*=========================================================================== + * FC 0x06 — 写单寄存器 + *===========================================================================*/ + +static void handle_write_single_reg(const uint8_t *rx, uint8_t rx_len) +{ + (void)rx_len; + uint16_t reg_addr = ((uint16_t)rx[2] << 8) | rx[3]; + uint16_t reg_val = ((uint16_t)rx[4] << 8) | rx[5]; + + /* 只读寄存器拦截 */ + switch (reg_addr) { + case 0x0005: case 0x0006: case 0x0007: case 0x0008: case 0x000B: + send_exception(0x06, 0x02); /* Illegal Data Address (只读) */ + return; + } + + /* 值校验 & 写入 */ + switch (reg_addr) { + // case 0x0000: /* Sensitivity: 0-3 */ + // if (reg_val > 3) { send_exception(0x06, 0x03); return; } + // g_loop_cng_unit.sensitvity = (uint8_t)reg_val; + // break; + // case 0x0001: /* Fusion Mode: 0-6 */ + // if (reg_val > 6) { send_exception(0x06, 0x03); return; } + // g_output_radar_mode = (uint8_t)reg_val; + // g_loop_cng_unit.direction_mode = (g_function_mode << 4) | (g_output_radar_mode & 0x0F); + // break; + // case 0x0002: /* Hold Time: 0-255 */ + // g_loop_cng_unit.exist_mode = (uint8_t)reg_val; + // g_hold_time = (uint16_t)reg_val * 20 * 5; + // break; + // case 0x0003: /* Output Delay: 0-255 */ + // g_loop_cng_unit.delay_time = (uint8_t)reg_val; + // break; + // case 0x0004: /* Freq Level: 0-1 */ + // if (reg_val > 1) { send_exception(0x06, 0x03); return; } + // g_freq_level = (uint8_t)reg_val; + // g_loop_cng_unit.loopFreq_Level = g_freq_level + 1; + // break; + // case 0x0009: /* BT Timeout: 0-255 */ + // g_max_counter_bt_min = (uint8_t)reg_val; + // g_max_counter_bt_timeout = (uint32_t)reg_val * 60 * 1000; + // break; + // case 0x000A: /* Safe Timeout: 0-255 */ + // g_loop_cng_unit.loopSafe_Timeout = (uint8_t)reg_val; + // break; + case 0x0010: /* Auto Rpt Enable: 0-1 */ + if (reg_val > 1) { send_exception(0x06, 0x03); return; } + _auto_rpt_enable = (uint8_t)reg_val; + if (_auto_rpt_enable) { + _rpt_last_ms = mstick(); /* 对齐计时起点 */ + _rpt_active = 0; + _rpt_hyst_cnt = 0; + } + break; + case 0x0011: /* Active Threshold: 0-65535 */ + _active_threshold = reg_val; + break; + case 0x0012: /* System Cmd: 写 0x2C2C 复位 MCU */ + if (reg_val == MB_CMD_RESET_MCU) { + SYS_ResetExecute(); + } + /* 非法值: 忽略, 不响应异常 (对齐 700BS 0x2C 行为) */ + return; + default: + send_exception(0x06, 0x02); /* Illegal Data Address */ + return; + } + + /* 回显 (Modbus 标准: 写成功的响应 = 请求原样返回) */ + { + uint8_t buf[8]; + uint16_t crc; + buf[0] = g_rs485_id & 0xFF; + buf[1] = 0x06; + buf[2] = rx[2]; buf[3] = rx[3]; /* 地址 */ + buf[4] = rx[4]; buf[5] = rx[5]; /* 值 */ + crc = crc16(buf, 6); + buf[6] = (uint8_t)(crc); + buf[7] = (uint8_t)(crc >> 8); + rs485_send(buf, 8); + } +} + +/*=========================================================================== + * FC 0x10 — 批量写寄存器 + * + * 简化实现: 逐寄存器调用 FC 0x06 的校验+写入逻辑 + *===========================================================================*/ + +static void handle_write_multiple_regs(const uint8_t *rx, uint8_t rx_len) +{ + (void)rx_len; + uint16_t start_addr = ((uint16_t)rx[2] << 8) | rx[3]; + uint16_t quantity = ((uint16_t)rx[4] << 8) | rx[5]; + uint8_t byte_cnt = rx[6]; + + if (quantity == 0 || quantity > 123 || byte_cnt != quantity * 2) { + send_exception(0x10, 0x03); + return; + } + + for (uint16_t i = 0; i < quantity; i++) { + uint16_t reg_addr = start_addr + i; + uint16_t reg_val = ((uint16_t)rx[7 + i * 2] << 8) | rx[8 + i * 2]; + + /* 只读拦截 */ + if (reg_addr == 0x0005 || reg_addr == 0x0006 || reg_addr == 0x0007 || + reg_addr == 0x0008 || reg_addr == 0x000B) { + send_exception(0x10, 0x02); + return; + } + + /* 值校验 — 同样的逻辑 */ + switch (reg_addr) { + case 0x0000: if (reg_val > 3) { send_exception(0x10, 0x03); return; } + g_loop_cng_unit.sensitvity = (uint8_t)reg_val; break; + case 0x0001: if (reg_val > 6) { send_exception(0x10, 0x03); return; } + g_output_radar_mode = (uint8_t)reg_val; + g_loop_cng_unit.direction_mode = (g_function_mode << 4) | (g_output_radar_mode & 0x0F); break; + case 0x0002: g_loop_cng_unit.exist_mode = (uint8_t)reg_val; + g_hold_time = (uint16_t)reg_val * 20 * 5; break; + case 0x0003: g_loop_cng_unit.delay_time = (uint8_t)reg_val; break; + case 0x0004: if (reg_val > 1) { send_exception(0x10, 0x03); return; } + g_freq_level = (uint8_t)reg_val; + g_loop_cng_unit.loopFreq_Level = g_freq_level + 1; break; + case 0x0009: g_max_counter_bt_min = (uint8_t)reg_val; + g_max_counter_bt_timeout = (uint32_t)reg_val * 60 * 1000; break; + case 0x000A: g_loop_cng_unit.loopSafe_Timeout = (uint8_t)reg_val; break; + case 0x0010: if (reg_val > 1) { send_exception(0x10, 0x03); return; } + _auto_rpt_enable = (uint8_t)reg_val; + if (_auto_rpt_enable) { _rpt_last_ms = mstick(); _rpt_active = 0; _rpt_hyst_cnt = 0; } + break; + case 0x0011: _active_threshold = reg_val; break; + case 0x0012: /* System Cmd: 写 0x2C2C 复位 MCU */ + if (reg_val == MB_CMD_RESET_MCU) { + SYS_ResetExecute(); + return; /* 不复位则忽略非法值 */ + } + /* 非法值: 忽略, 继续后续寄存器 */ + break; + default: send_exception(0x10, 0x02); return; + } + } + + /* 响应: 回显 addr + fc + start + qty */ + { + uint8_t buf[8]; + uint16_t crc; + buf[0] = g_rs485_id & 0xFF; + buf[1] = 0x10; + buf[2] = rx[2]; buf[3] = rx[3]; + buf[4] = rx[4]; buf[5] = rx[5]; + crc = crc16(buf, 6); + buf[6] = (uint8_t)(crc); + buf[7] = (uint8_t)(crc >> 8); + rs485_send(buf, 8); + } +} + +/*=========================================================================== + * FC 0x11 — Report Slave ID + *===========================================================================*/ + +static void handle_report_slave_id(const uint8_t *rx, uint8_t rx_len) +{ + (void)rx; (void)rx_len; + extern uint8_t loop1_LOOP_OK; + + /* 返回: addr + fc + byte_cnt + model(11) + status + crc */ + const char *model = PRODUCT_MODEL; /* "DLD154Pro" = 10 chars + '\0' = 11 */ + uint8_t model_len = (uint8_t)strlen(model) + 1; /* 含 '\0' */ + + uint8_t buf[5 + 12 + 2]; /* addr+fc+bc + model(max12) + status + crc */ + uint16_t crc; + uint8_t idx = 0; + + buf[idx++] = g_rs485_id & 0xFF; + buf[idx++] = 0x11; + buf[idx++] = model_len + 1; /* byte_cnt = model + status */ + memcpy(&buf[idx], model, model_len); + idx += model_len; + + /* Status: 0x00=RUN, 0xFF=STOP */ + buf[idx++] = loop1_LOOP_OK ? 0x00 : 0xFF; + + crc = crc16(buf, idx); + buf[idx++] = (uint8_t)(crc); + buf[idx++] = (uint8_t)(crc >> 8); + + rs485_send(buf, idx); +} + +/*=========================================================================== + * 初始化 & 轮询 + *===========================================================================*/ + +void modbus_init(void) +{ + /* 协议状态复位 */ + _auto_rpt_enable = 0; + _active_threshold = 10; + _rpt_last_ms = mstick(); + _rpt_active = 0; + _rpt_hyst_cnt = 0; + _last_relay_state = g_loop_acs_info.relay1_state; + _last_loop_ok = loop1_LOOP_OK; + _last_relay_off_10ms = 0; + _last_relay_on_10ms = 0; + _misc_time_type = 0xFFFF; + _misc_time_value = 0; + _relay_engage_cnt = 0; +} + +void modbus_poll(void) +{ + /*=== 帧接收 ===*/ + if (_rx_len > 0) { + uint32_t now = mstick(); + if (now - _rx_last_tick >= MB_FRAME_TIMEOUT_TICK) { + /* 帧超时 → 处理完整帧 */ + uint8_t *rx = _rx_buf; + uint8_t len = _rx_len; + + /* 最小帧: addr(1) + fc(1) + crc(2) = 4 */ + if (len >= 4) { + uint8_t addr = rx[0]; + uint8_t fc = rx[1]; + uint16_t crc_rx = ((uint16_t)rx[len - 1] << 8) | rx[len - 2]; /* 低字节在前 */ + uint16_t crc_calc = crc16(rx, len - 2); + + /* 地址匹配 (0=广播, 本从机不响应) */ + if (addr != 0 && addr == (g_rs485_id & 0xFF)) { + if (crc_rx == crc_calc) { + switch (fc) { + case 0x02: handle_read_discrete_inputs(rx, len); break; + // case 0x03: handle_read_holding_regs(rx, len); break; + case 0x04: handle_read_input_regs(rx, len); break; + case 0x06: handle_write_single_reg(rx, len); break; + // case 0x10: handle_write_multiple_regs(rx, len); break; + // case 0x11: handle_report_slave_id(rx, len); break; + default: send_exception(fc, 0x01); break; + } + } + /* CRC 错误 → 静默 (Modbus 标准行为) */ + } + /* 地址不匹配 → 静默 */ + } + _rx_len = 0; /* 清空缓冲 */ + } + } + + /*=== 主动上报调度 ===*/ + modbus_report_schedule(); +}