/* * 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; /*=========================================================================== * 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; /* 继电器吸合累计次数 */ /*=========================================================================== * 主动上报 — 全数据块组装 & 发送 *===========================================================================*/ /* 计算线圈频率 (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; } } 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 > 125) { send_exception(0x04, 0x03); return; } uint8_t data_buf[250]; 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[5 + 250]; /* addr + fc + bc + data + crc */ 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; 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 > 125) { send_exception(0x03, 0x03); return; } uint8_t data_buf[250]; 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[5 + 250]; 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; 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; 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_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) { /*=== 帧接收 ===*/\n 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; /* 清空缓冲 */ } } /*=== 主动上报调度 ===*/\n modbus_report_schedule(); }