From f5edab43c2e585771e70fe4fdb2e8450932c1ccc Mon Sep 17 00:00:00 2001 From: wangfq Date: Tue, 28 Jul 2026 09:26:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(opt):=20=E5=85=89=E8=80=A6=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E6=A8=A1=E5=BC=8F=E5=8D=87=E7=BA=A7=20=E2=80=94=20DLD?= =?UTF-8?q?=5FOUTPUT=5FMODE=20=E6=9E=9A=E4=B8=BE=20+=20direction=5Fmode=20?= =?UTF-8?q?=E4=BD=8E=E5=9B=9B=E4=BD=8D=E5=8F=96=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 DLD_OUTPUT_MODE 枚举: 7种模式(复位/纯地感/纯雷达/或/与/人车混行/ETC) - g_fm_radar_input_disable → g_output_radar_mode: 从 function_mode bit1 改为 direction_mode & 0x0F - 仅模式0(复位)已实现, 1~6 暂为 no-op - BLE 回读字段改为 direction_mode 完整字节 - rs485_700bs.c: 注释块重排(无功能变更) - docs: 更新开发日志 --- .../APP/dbn_ble_srv.c | 4 +- .../APP/include/cmcng.h | 2 +- .../APP/include/storage.h | 11 + .../APP/peripheral_main.c | 12 +- .../APP/rs485_700bs.c | 635 +++++++++--------- docs/devlog.md | 52 ++ 6 files changed, 391 insertions(+), 325 deletions(-) diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/dbn_ble_srv.c b/BLE/DLD154Pro_Peripheral_28K/APP/dbn_ble_srv.c index 3580f71..21bc860 100644 --- a/BLE/DLD154Pro_Peripheral_28K/APP/dbn_ble_srv.c +++ b/BLE/DLD154Pro_Peripheral_28K/APP/dbn_ble_srv.c @@ -430,7 +430,7 @@ static void unpack_pkg_set_cjq_param(uint8_t *pkg, uint8_t len) g_function_mode = (g_loop_cng_unit.direction_mode >> 4) & 0x0F; g_fm_700bs_disable = g_function_mode & 0x01; - g_fm_radar_input_disable = (g_function_mode >> 1) & 0x01; + g_output_radar_mode = g_loop_cng_unit.direction_mode & 0x0F; g_hold_time = g_loop_cng_unit.exist_mode * 20 * 5; g_freq_sens = (g_loop_cng_unit.sensitvity > 0) ? (g_loop_cng_unit.sensitvity - 1) : 0; @@ -634,7 +634,7 @@ void manage_dbn_ble_default(uint8_t *pkg, uint8_t len) tmp_ble_buf[i++] = g_loop_sens_list.total << 4; tmp_ble_buf[i++] = (g_loop_power_up_state == 0) ? 1 : ((uint8_t)(g_loop_cut_amount)); tmp_ble_buf[i++] = (uint8_t)(g_loop_cut_amount >> 8); - tmp_ble_buf[i++] = g_function_mode; + tmp_ble_buf[i++] = g_loop_cng_unit.direction_mode; set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, i); // PRINT("\nGet_Cjq_param\n"); diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/include/cmcng.h b/BLE/DLD154Pro_Peripheral_28K/APP/include/cmcng.h index cda9c9c..a8c91f6 100644 --- a/BLE/DLD154Pro_Peripheral_28K/APP/include/cmcng.h +++ b/BLE/DLD154Pro_Peripheral_28K/APP/include/cmcng.h @@ -42,7 +42,7 @@ extern uint8_t g_loop_power_up_state; extern uint8_t g_function_mode; extern uint8_t g_fm_700bs_disable ; //0: 700bs protocol; 1 Modbus protocol -extern uint8_t g_fm_radar_input_disable; //0: radar input enable; 1 reset +extern uint8_t g_output_radar_mode; /* 新算法状�? (DLD154V4B 移植) */ extern uint8_t g_loop_stable; // 稳定期结束标�? (0=收敛�?, 1=稳定) diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/include/storage.h b/BLE/DLD154Pro_Peripheral_28K/APP/include/storage.h index f7f3ae4..5c9f68f 100644 --- a/BLE/DLD154Pro_Peripheral_28K/APP/include/storage.h +++ b/BLE/DLD154Pro_Peripheral_28K/APP/include/storage.h @@ -177,6 +177,17 @@ typedef struct _LOOP_ACS_INFO } Loop_ACS_Info; extern Loop_ACS_Info g_loop_acs_info; +typedef enum _DLD_OUTPUT_MODE +{ + OUTPUT_MODE_LOOP_X_RESET = 0, // 光耦输入为 设备 复位信号,仅地感检测 输出 + OUTPUT_MODE_LOOP_ONLY, // 仅地感输出 有效,光耦输入 无效 + OUTPUT_MODE_RADAR_ONLY, // 仅光耦输入有效,雷达的输出信号接光耦输入,雷达有输出,车检器的继电器输出;地感信号 无效。 + OUTPUT_MODE_LOOP_OR_RADAR, // 地感或雷达,雷达的输出信号接光耦输入,雷达或地感有信号,车检器的继电器输出。 + OUTPUT_MODE_LOOP_AND_RADAR, // 地感和雷达,雷达的输出信号接光耦输入,雷达 并且 地感都有信号,车检器继电器才输出。 + OUTPUT_MODE_HUMAN_VD_MIX, // 人车混行,雷达的输出信号接光耦输入,刚开始屏蔽雷达信号,当地感检测有车、车检器继电器输出后,车检器对地感 或雷达信号都有效(或的关系),车辆离开地感与雷达检测区域后,三秒内雷达检测有效,车检器继电器立即输出,起到防砸人的效果,连续进入持续防砸、人走落杆。 + OUTPUT_MODE_ETC, // ETC模式,雷达的输出信号接光耦输入,刚开始屏蔽雷达信号,当地感检测有车、车检器继电器输出后,车检器对地感 或雷达信号都有效(或的关系),雷达输出断开后会有500ms的防抖超时时间,超时时间结束后,车检器才确定释放继电器。 +}DLD_OUTPUT_MODE; + #pragma pack() diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/peripheral_main.c b/BLE/DLD154Pro_Peripheral_28K/APP/peripheral_main.c index 0239785..9cae0f0 100644 --- a/BLE/DLD154Pro_Peripheral_28K/APP/peripheral_main.c +++ b/BLE/DLD154Pro_Peripheral_28K/APP/peripheral_main.c @@ -35,7 +35,7 @@ uint8_t g_flag_bt_state = 0; // 0 蓝牙没有连接,1 有蓝牙连接 uint8_t g_function_mode = 0; uint8_t g_fm_700bs_disable = 0; //0: 700bs protocol; 1 Modbus protocol -uint8_t g_fm_radar_input_disable = 0; //0: radar input enable; 1 reset +uint8_t g_output_radar_mode = 0; uint32_t g_sys_freq = 0; uint8_t g_storage_uart_num = 0; uint32_t g_storage_uart_baud = 19200;//9600; @@ -340,7 +340,7 @@ void para_store_init(void) g_freq_level = g_loop_cng_unit.loopFreq_Level - 1; g_function_mode = g_loop_cng_unit.direction_mode >> 4; g_fm_700bs_disable = g_function_mode & 0x01; - g_fm_radar_input_disable = (g_function_mode >> 1) & 0x01; + g_output_radar_mode = g_loop_cng_unit.direction_mode & 0x0F; g_freq_sens = (g_loop_cng_unit.sensitvity > 0) ? (g_loop_cng_unit.sensitvity - 1) : 0; #ifdef DEBUG @@ -850,8 +850,8 @@ void cjq_srv(void){ /*=========================================================================== * opt_input_poll — 光耦输入处理 * - * g_fm_radar_input_disable == 0: 雷达输入模式 (暂未实现) - * g_fm_radar_input_disable == 1: 光耦复位模式 + * g_output_radar_mode == 0: 光耦复位模式 + 地感模式 + * g_output_radar_mode >= 1: 雷达输入模式 (暂未实现) * 外部干接点接通 → PB10=0 → 计时; 保持 ≥500ms 后断开 → 上升沿 → 芯片复位 *===========================================================================*/ static void opt_input_poll(void) @@ -859,7 +859,7 @@ static void opt_input_poll(void) static uint32_t _opt_low_since = 0; // PB10 变低的时刻 static uint8_t _opt_armed = 0; // 低电平已保持 ≥500ms, 等待上升沿 - if (g_fm_radar_input_disable == 0) { + if (g_output_radar_mode) { _opt_low_since = 0; _opt_armed = 0; return; @@ -868,7 +868,7 @@ static void opt_input_poll(void) if (OPT_IN1 == 0) { // 干接点接通 → 低电平 if (_opt_low_since == 0) { _opt_low_since = mstick(); - } else if (!_opt_armed && (mstick() - _opt_low_since >= 100)) { // 100 tick × 5ms = 500ms + } else if (!_opt_armed && (mstick() - _opt_low_since >= 80)) { // 100 tick × 5ms = 500ms _opt_armed = 1; // 满足 500ms, 等待上升沿 } } else { // 干接点断开 → 高电平 diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/rs485_700bs.c b/BLE/DLD154Pro_Peripheral_28K/APP/rs485_700bs.c index 540ff4f..144a92f 100644 --- a/BLE/DLD154Pro_Peripheral_28K/APP/rs485_700bs.c +++ b/BLE/DLD154Pro_Peripheral_28K/APP/rs485_700bs.c @@ -1,316 +1,319 @@ -/* - * rs485_700bs.c - * DLD154Pro RS485 700BS 从机协议实现 - * - * 物理层: RS485 半双工, 9600 8N1, PA14=TX PA15=RX PA13=RE - * 帧格式: 主机 3 字节 (STX ADDR XOR), 从机可变长 - * 心跳: 空闲 5s / 故障 1s / 事件立即 - * - * 2026-07-23 初始实现 - */ -#include "CONFIG.h" -#include "CH59x_common.h" -#include "cmcng.h" -#include "rs485_700bs.h" -#include "storage.h" -#include "dbn_ble_srv.h" -#include - -/*=========================================================================== - * UART0 中断接收缓冲 - *===========================================================================*/ -#define RS485_RX_BUF_SIZE 16 -static uint8_t _rx_buf[RS485_RX_BUF_SIZE]; -static uint8_t _rx_len = 0; -static uint32_t _rx_last_tick = 0; // 帧间隔计时 (3.5 字符 ≈ 4ms @9600) - -/*=========================================================================== - * 心跳 & 状态追踪 - *===========================================================================*/ -static uint32_t _hb_last_ms = 0; // 上次心跳时刻 -static uint8_t _hb_need_init = 1; // 需要发 0xA1 首次心跳 -static uint8_t _last_car_state = 0; // car_state 快照 (翻转沿检测) -static uint8_t _last_loop_state = 0; // loop_state 快照 - -/*=========================================================================== - * RS485 方向控制 (PA13) - *===========================================================================*/ -#define RS485_RE_TX() GPIOA_SetBits(GPIO_Pin_13) -#define RS485_RE_RX() GPIOA_ResetBits(GPIO_Pin_13) - -/*=========================================================================== - * UART0 初始化 — 9600 8N1, 中断接收 - *===========================================================================*/ -void rs485_init(void) -{ - /* PA14=TX(推挽), PA15=RX(上拉输入), PA13=RE(推挽, 默认接收) */ - GPIOA_SetBits(GPIO_Pin_13); // RE 先拉高, GPIO 模式 - GPIOA_ModeCfg(GPIO_Pin_14, GPIO_ModeOut_PP_5mA); - GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU); - GPIOA_ModeCfg(GPIO_Pin_13, GPIO_ModeOut_PP_5mA); - - /* UART0 外设: 9600 8N1 */ - UART0_BaudRateCfg(RS485_BAUD); - R8_UART0_LCR = 0x03; // 8 数据位, 1 停止位, 无校验 - R8_UART0_MCR = 0x04; // RE=0 进入接收模式 - - /* 中断: 接收可用 */ - UART0_CLR_RXFIFO(); - UART0_ByteTrigCfg(UART_1BYTE_TRIG); // 每收到 1 字节触发中断 - UART0_INTCfg(ENABLE, RB_IER_RECV_RDY | RB_IER_LINE_STAT); - PFIC_EnableIRQ(UART0_IRQn); - - RS485_RE_RX(); // 默认接收模式 - _hb_last_ms = mstick(); // 对齐心跳起始时刻,避免首次立即触发 - PRINT("RS485: init ok, addr=%d, baud=%d\n", g_rs485_id, RS485_BAUD); -} - -/*=========================================================================== - * 发送一帧 — 切 RE=TX, 发完等 1ms, 切回 RE=RX - *===========================================================================*/ -static void rs485_send(const uint8_t *data, uint8_t len) -{ - RS485_RE_TX(); - mDelayuS(100); // 总线稳定 - - for (uint8_t i = 0; i < len; i++) { - while (R8_UART0_TFC != 0); // 等 TX FIFO 空 (上一字节发完) - R8_UART0_THR = data[i]; - } - while (R8_UART0_TFC != 0); // 等最后一字节发完 - mDelayuS(500); // 等待停止位 + 总线释放 - UART0_CLR_TXFIFO(); - RS485_RE_RX(); -} - -/*=========================================================================== - * 命令处理 - *===========================================================================*/ - -/* 直接从原始测量值计算线圈频率 (Hz),不依赖 BLE 报告通道 */ -static uint32_t rs485_get_freq(void) -{ - extern uint32_t loop1_CAPVD; // vd1_task() 实时更新的 IIR 滤波值 - if (loop1_CAPVD == 0 || loop1_LPCNT == 0) return 0; - /* freq = FREQ_SYS / (CAPVD / LPCNT) + 360 (补偿值) */ - return (uint32_t)((uint64_t)FREQ_SYS * loop1_LPCNT / loop1_CAPVD) + 360; -} - -/* 0xFA — 查询有车状态 + 线圈好坏 */ -static void handle_query_status(uint8_t addr) -{ - extern uint8_t loop1_LOOP_OK; - uint8_t data = addr & 0x1F; // bit4-0 = 地址 - if (g_loop_acs_info.car_state) data |= 0x80; // bit7 = 有车 - if (!loop1_LOOP_OK) data |= 0x20; // bit5 = 线圈断开 (LOOP_OK=0) - uint8_t buf[3] = { RSP_STATUS, data, (uint8_t)(RSP_STATUS ^ data) }; - rs485_send(buf, 3); -} - -/* 0x1B — 查询线圈频率 */ -static void handle_query_freq(uint8_t addr) -{ - uint32_t freq = rs485_get_freq(); // Hz - uint8_t buf[6]; - buf[0] = RSP_FREQ; - buf[1] = (uint8_t)((freq >> 16) & 0xFF); // Fre_1 (MSB) - buf[2] = (uint8_t)((freq >> 8) & 0xFF); // Fre_2 - buf[3] = (uint8_t)(freq & 0xFF); // Fre_3 (LSB) - buf[4] = addr & 0x1F; - buf[5] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3] ^ buf[4]; - rs485_send(buf, 6); -} - -/* 0x2C — 复位初始化线圈 */ -static void handle_reset_init(uint8_t addr) -{ - (void)addr; - extern uint8_t g_loop_stable; - extern uint8_t loop1_LOOP_OK0; - extern uint8_t loop1_LC_Reset; - extern uint8_t loop1_INI_LOOP; - extern uint16_t loop1_stable_cnt; - extern uint16_t LC_Hold_CNT; - extern uint32_t loop1_ORG_CNT, loop1_ORG_SUM; - - loop1_LC_Reset = 1; - loop1_INI_LOOP = 1; - loop1_LOOP_OK0 = 0; - g_loop_stable = 0; - loop1_stable_cnt = 0; - LC_Hold_CNT = 0; - loop1_ORG_CNT = 0; - loop1_ORG_SUM = 0; - _hb_need_init = 1; - _last_car_state = (_last_car_state ^ 1); - PRINT("RS485: coil reset init\n"); -} - -/* 0x3D — 查询拨码 DIP1-5 (DLD154Pro 无物理拨码, 从 BLE 配置映射) */ -static void handle_query_dip(uint8_t addr) -{ - uint8_t data = 0; - uint8_t sens = g_loop_cng_unit.sensitvity & 0x0F; // 灵敏度 0-3 - data |= (sens & 0x01); // DIP1 = bit0 - data |= ((sens >> 1) & 0x01) << 1; // DIP2 = bit1 - if (g_loop_cng_unit.output_mode & 0x01) data |= 0x04; // DIP3 = bit2 (1=脉冲) - if (g_loop_cng_unit.exist_mode == 0) data |= 0x08; // DIP4 = bit3 (0=永久→ON=1) - if (g_loop_cng_unit.delay_time > 0) data |= 0x10; // DIP5 = bit4 (>0=启用延时) - uint8_t buf[4] = { RSP_DIP, data, (uint8_t)(addr & 0x1F), - (uint8_t)(RSP_DIP ^ data ^ (addr & 0x1F)) }; - rs485_send(buf, 4); -} - -/*=========================================================================== - * 心跳上报 (0xAA) — 空闲 5s / 故障 1s / 事件立即 - *===========================================================================*/ - -/* Fre_Err 计算: bit0=下限异常, bit4=上限异常 */ -static uint8_t calc_freq_err(uint32_t freq) -{ - (void)freq; - /* DLD154Pro 未实现频率越限检测, 始终返回 0 */ - return 0x00; -} - -static void rs485_send_heartbeat(void) -{ - extern uint8_t loop1_LOOP_OK; - uint32_t freq = rs485_get_freq(); - uint8_t st = g_rs485_id & 0x1F; - - if (g_loop_acs_info.car_state) st |= 0x80; // bit7=有车 - if (!loop1_LOOP_OK) st |= 0x20; // bit5=线圈断开 - - uint8_t buf[9]; - buf[0] = HB_PREAMBLE; // 0xAA - buf[1] = _hb_need_init ? HB_INIT_FIRST : HB_INIT_NORMAL; - _hb_need_init = 0; - buf[2] = st; // State - buf[3] = calc_freq_err(freq); // Fre_Err - buf[4] = (uint8_t)((freq >> 16) & 0xFF); // Fre_1 - buf[5] = (uint8_t)((freq >> 8) & 0xFF); // Fre_2 - buf[6] = (uint8_t)(freq & 0xFF); // Fre_3 - buf[7] = g_rs485_id & 0x1F; // ADDR - buf[8] = buf[0]; - for (uint8_t i = 1; i < 8; i++) buf[8] ^= buf[i]; - - rs485_send(buf, 9); -} - -/*=========================================================================== - * 帧解析 & 主循环 - *===========================================================================*/ - -/* UART0 接收中断 (每字节触发) */ -__attribute__((interrupt("WCH-Interrupt-fast"))) -void UART0_IRQHandler(void) -{ - switch (UART0_GetITFlag()) { - case UART_II_RECV_RDY: // 接收数据就绪 - if (_rx_len < RS485_RX_BUF_SIZE) { - _rx_buf[_rx_len++] = UART0_RecvByte(); - } else { - _rx_len = 0; // 溢出, 丢弃 - } - _rx_last_tick = mstick(); - break; - - case UART_II_LINE_STAT: // 线路状态 (帧错误/溢出等) - UART0_GetLinSTA(); // 读 LSR 清状态 - break; - - default: - break; - } -} - -/* 处理收到的完整帧 */ -static void rs485_process_frame(void) -{ - if (_rx_len < 3) return; // 最少 3 字节 - - /* XOR 校验: STX ^ ADDR/CMD */ - uint8_t stx = _rx_buf[0]; - uint8_t addr = _rx_buf[1]; - uint8_t xorr = _rx_buf[2]; - - if ((stx ^ addr) != xorr) { - PRINT("RS485: XOR fail %02X %02X %02X\n", stx, addr, xorr); - _rx_len = 0; - return; - } - - /* 地址匹配检查 (0x2C 复位命令全地址响应) */ - if (stx != CMD_RESET_INIT && (addr & 0x1F) != (g_rs485_id & 0x1F)) { - _rx_len = 0; - return; // 不是给我的 - } - - switch (stx) { - 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; - default: - PRINT("RS485: unknown cmd 0x%02X\n", stx); - break; - } - - _rx_len = 0; -} - -/* 主循环调用 — 帧超时检测 + 周期心跳 */ -void rs485_poll(void) -{ - /* 帧超时: 3.5 字符 ≈ 4ms @9600, 10ms 足够 */ - if (_rx_len > 0 && mstick() - _rx_last_tick > 10) { - rs485_process_frame(); - } - - /* 心跳上报 */ - extern uint8_t loop1_LOOP_OK; - uint32_t now = mstick(); - uint8_t car_state = g_loop_acs_info.car_state; - uint8_t loop_state = loop1_LOOP_OK; // 1=正常, 0=断开 - uint32_t interval; - - /* 事件触发: car_state 翻转沿 → 立即上报 */ - if (car_state != _last_car_state) { - _last_car_state = car_state; - rs485_send_heartbeat(); - _hb_last_ms = now; - return; - } - /* 线圈状态变化 → 立即上报 */ - if (loop_state != _last_loop_state) { - _last_loop_state = loop_state; - rs485_send_heartbeat(); - _hb_last_ms = now; - return; - } - - /* 周期心跳 */ - interval = (!loop_state) ? RS485_FAULT_MS : RS485_HEARTBEAT_MS; - if (now - _hb_last_ms >= interval) { - rs485_send_heartbeat(); - _hb_last_ms = now; - } -} - -/* 强制立即上报 (由外部事件触发) */ -void rs485_event_notify(void) -{ - _last_car_state = (_last_car_state ^ 1); // 强制触发 -} - -void rs485_loop_notify(void) -{ - _last_loop_state = (_last_loop_state ^ 1); // 强制触发 -} - -void rs485_reset_heartbeat(void) -{ - _hb_need_init = 1; - _hb_last_ms = 0; // 立即触发首次心跳 -} +/* + * rs485_700bs.c + * DLD154Pro RS485 700BS 从机协议实现 + * + * 物理层: RS485 半双工, 9600 8N1, PA14=TX PA15=RX PA13=RE + * 帧格式: 主机 3 字节 (STX ADDR XOR), 从机可变长 + * 心跳: 空闲 5s / 故障 1s / 事件立即 + * + * 2026-07-23 初始实现 + */ +#include "CONFIG.h" +#include "CH59x_common.h" +#include "cmcng.h" +#include "rs485_700bs.h" +#include "storage.h" +#include "dbn_ble_srv.h" +#include + +/*=========================================================================== + * UART0 中断接收缓冲 + *===========================================================================*/ +#define RS485_RX_BUF_SIZE 16 +static uint8_t _rx_buf[RS485_RX_BUF_SIZE]; +static uint8_t _rx_len = 0; +static uint32_t _rx_last_tick = 0; // 帧间隔计时 (3.5 字符 ≈ 4ms @9600) + +/*=========================================================================== + * 心跳 & 状态追踪 + *===========================================================================*/ +static uint32_t _hb_last_ms = 0; // 上次心跳时刻 +static uint8_t _hb_need_init = 1; // 需要发 0xA1 首次心跳 +static uint8_t _last_car_state = 0; // car_state 快照 (翻转沿检测) +static uint8_t _last_loop_state = 0; // loop_state 快照 + +/*=========================================================================== + * RS485 方向控制 (PA13) + *===========================================================================*/ +#define RS485_RE_TX() GPIOA_SetBits(GPIO_Pin_13) +#define RS485_RE_RX() GPIOA_ResetBits(GPIO_Pin_13) + +/*=========================================================================== + * UART0 初始化 — 9600 8N1, 中断接收 + *===========================================================================*/ +void rs485_init(void) +{ + /* PA14=TX(推挽), PA15=RX(上拉输入), PA13=RE(推挽, 默认接收) */ + GPIOA_SetBits(GPIO_Pin_13); // RE 先拉高, GPIO 模式 + GPIOA_ModeCfg(GPIO_Pin_14, GPIO_ModeOut_PP_5mA); + GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU); + GPIOA_ModeCfg(GPIO_Pin_13, GPIO_ModeOut_PP_5mA); + + /* UART0 外设: 9600 8N1 */ + UART0_BaudRateCfg(RS485_BAUD); + R8_UART0_LCR = 0x03; // 8 数据位, 1 停止位, 无校验 + R8_UART0_MCR = 0x04; // RE=0 进入接收模式 + + /* 中断: 接收可用 */ + UART0_CLR_RXFIFO(); + UART0_ByteTrigCfg(UART_1BYTE_TRIG); // 每收到 1 字节触发中断 + UART0_INTCfg(ENABLE, RB_IER_RECV_RDY | RB_IER_LINE_STAT); + PFIC_EnableIRQ(UART0_IRQn); + + RS485_RE_RX(); // 默认接收模式 + _hb_last_ms = mstick(); // 对齐心跳起始时刻,避免首次立即触发 + PRINT("RS485: init ok, addr=%d, baud=%d\n", g_rs485_id, RS485_BAUD); +} + +/*=========================================================================== + * 发送一帧 — 切 RE=TX, 发完等 1ms, 切回 RE=RX + *===========================================================================*/ +static void rs485_send(const uint8_t *data, uint8_t len) +{ + RS485_RE_TX(); + mDelayuS(100); // 总线稳定 + + for (uint8_t i = 0; i < len; i++) { + while (R8_UART0_TFC != 0); // 等 TX FIFO 空 (上一字节发完) + R8_UART0_THR = data[i]; + } + while (R8_UART0_TFC != 0); // 等最后一字节发完 + mDelayuS(500); // 等待停止位 + 总线释放 + UART0_CLR_TXFIFO(); + RS485_RE_RX(); +} + +/*=========================================================================== + * 命令处理 + *===========================================================================*/ + +/* 直接从原始测量值计算线圈频率 (Hz),不依赖 BLE 报告通道 */ +static uint32_t rs485_get_freq(void) +{ + extern uint32_t loop1_CAPVD; // vd1_task() 实时更新的 IIR 滤波值 + if (loop1_CAPVD == 0 || loop1_LPCNT == 0) return 0; + /* freq = FREQ_SYS / (CAPVD / LPCNT) + 360 (补偿值) */ + return (uint32_t)((uint64_t)FREQ_SYS * loop1_LPCNT / loop1_CAPVD) + 360; +} + +/* 0xFA — 查询有车状态 + 线圈好坏 */ +static void handle_query_status(uint8_t addr) +{ + extern uint8_t loop1_LOOP_OK; + uint8_t data = addr & 0x1F; // bit4-0 = 地址 + if (g_loop_acs_info.car_state) data |= 0x80; // bit7 = 有车 + if (!loop1_LOOP_OK) data |= 0x20; // bit5 = 线圈断开 (LOOP_OK=0) + uint8_t buf[3] = { RSP_STATUS, data, (uint8_t)(RSP_STATUS ^ data) }; + rs485_send(buf, 3); +} + +/* 0x1B — 查询线圈频率 */ +static void handle_query_freq(uint8_t addr) +{ + uint32_t freq = rs485_get_freq(); // Hz + uint8_t buf[6]; + buf[0] = RSP_FREQ; + buf[1] = (uint8_t)((freq >> 16) & 0xFF); // Fre_1 (MSB) + buf[2] = (uint8_t)((freq >> 8) & 0xFF); // Fre_2 + buf[3] = (uint8_t)(freq & 0xFF); // Fre_3 (LSB) + buf[4] = addr & 0x1F; + buf[5] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3] ^ buf[4]; + rs485_send(buf, 6); +} + +/* 0x2C — 复位初始化线圈 */ +static void handle_reset_init(uint8_t addr) +{ + (void)addr; + extern uint8_t g_loop_stable; + extern uint8_t loop1_LOOP_OK0; + extern uint8_t loop1_LC_Reset; + extern uint8_t loop1_INI_LOOP; + extern uint16_t loop1_stable_cnt; + extern uint16_t LC_Hold_CNT; + extern uint32_t loop1_ORG_CNT, loop1_ORG_SUM; + + loop1_LC_Reset = 1; + loop1_INI_LOOP = 1; + loop1_LOOP_OK0 = 0; + g_loop_stable = 0; + loop1_stable_cnt = 0; + LC_Hold_CNT = 0; + loop1_ORG_CNT = 0; + loop1_ORG_SUM = 0; + _hb_need_init = 1; + _last_car_state = (_last_car_state ^ 1); + PRINT("RS485: coil reset init\n"); +} + +/* 0x3D — 查询拨码 DIP1-5 (DLD154Pro 无物理拨码, 从 BLE 配置映射) */ +static void handle_query_dip(uint8_t addr) +{ + uint8_t data = 0; + uint8_t sens = g_loop_cng_unit.sensitvity & 0x0F; // 灵敏度 0-3 + data |= (sens & 0x01); // DIP1 = bit0 + data |= ((sens >> 1) & 0x01) << 1; // DIP2 = bit1 + if (g_loop_cng_unit.output_mode & 0x01) data |= 0x04; // DIP3 = bit2 (1=脉冲) + if (g_loop_cng_unit.exist_mode == 0) data |= 0x08; // DIP4 = bit3 (0=永久→ON=1) + if (g_loop_cng_unit.delay_time > 0) data |= 0x10; // DIP5 = bit4 (>0=启用延时) + uint8_t buf[4] = { RSP_DIP, data, (uint8_t)(addr & 0x1F), + (uint8_t)(RSP_DIP ^ data ^ (addr & 0x1F)) }; + rs485_send(buf, 4); +} + +/*=========================================================================== + * 心跳上报 (0xAA) — 空闲 5s / 故障 1s / 事件立即 + *===========================================================================*/ + +/* Fre_Err 计算: bit0=下限异常, bit4=上限异常 */ +static uint8_t calc_freq_err(uint32_t freq) +{ + (void)freq; + /* DLD154Pro 未实现频率越限检测, 始终返回 0 */ + return 0x00; +} + +static void rs485_send_heartbeat(void) +{ + extern uint8_t loop1_LOOP_OK; + uint32_t freq = rs485_get_freq(); + uint8_t st = g_rs485_id & 0x1F; + + if (g_loop_acs_info.car_state) st |= 0x80; // bit7=有车 + if (!loop1_LOOP_OK) st |= 0x20; // bit5=线圈断开 + + uint8_t buf[9]; + buf[0] = HB_PREAMBLE; // 0xAA + buf[1] = _hb_need_init ? HB_INIT_FIRST : HB_INIT_NORMAL; + _hb_need_init = 0; + buf[2] = st; // State + buf[3] = calc_freq_err(freq); // Fre_Err + buf[4] = (uint8_t)((freq >> 16) & 0xFF); // Fre_1 + buf[5] = (uint8_t)((freq >> 8) & 0xFF); // Fre_2 + buf[6] = (uint8_t)(freq & 0xFF); // Fre_3 + buf[7] = g_rs485_id & 0x1F; // ADDR + buf[8] = buf[0]; + for (uint8_t i = 1; i < 8; i++) buf[8] ^= buf[i]; + + rs485_send(buf, 9); +} +void tmp_send(void){ + rs485_send_heartbeat(); +} + +/*=========================================================================== + * 帧解析 & 主循环 + *===========================================================================*/ + +/* UART0 接收中断 (每字节触发) */ +__attribute__((interrupt("WCH-Interrupt-fast"))) +void UART0_IRQHandler(void) +{ + switch (UART0_GetITFlag()) { + case UART_II_RECV_RDY: // 接收数据就绪 + if (_rx_len < RS485_RX_BUF_SIZE) { + _rx_buf[_rx_len++] = UART0_RecvByte(); + } else { + _rx_len = 0; // 溢出, 丢弃 + } + _rx_last_tick = mstick(); + break; + + case UART_II_LINE_STAT: // 线路状态 (帧错误/溢出等) + UART0_GetLinSTA(); // 读 LSR 清状态 + break; + + default: + break; + } +} + +/* 处理收到的完整帧 */ +static void rs485_process_frame(void) +{ + if (_rx_len < 3) return; // 最少 3 字节 + + /* XOR 校验: STX ^ ADDR/CMD */ + uint8_t stx = _rx_buf[0]; + uint8_t addr = _rx_buf[1]; + uint8_t xorr = _rx_buf[2]; + + if ((stx ^ addr) != xorr) { + PRINT("RS485: XOR fail %02X %02X %02X\n", stx, addr, xorr); + _rx_len = 0; + return; + } + + /* 地址匹配检查 (0x2C 复位命令全地址响应) */ + if (stx != CMD_RESET_INIT && (addr & 0x1F) != (g_rs485_id & 0x1F)) { + _rx_len = 0; + return; // 不是给我的 + } + + switch (stx) { + 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; + default: + PRINT("RS485: unknown cmd 0x%02X\n", stx); + break; + } + + _rx_len = 0; +} + +/* 主循环调用 — 帧超时检测 + 周期心跳 */ +void rs485_poll(void) +{ + /* 帧超时: 3.5 字符 ≈ 4ms @9600, 10ms 足够 */ + if (_rx_len > 0 && mstick() - _rx_last_tick > 10) { + rs485_process_frame(); + } + + /* 心跳上报 */ + extern uint8_t loop1_LOOP_OK; + uint32_t now = mstick(); + uint8_t car_state = g_loop_acs_info.car_state; + uint8_t loop_state = loop1_LOOP_OK; // 1=正常, 0=断开 + uint32_t interval; + + /* 事件触发: car_state 翻转沿 → 立即上报 */ + if (car_state != _last_car_state) { + _last_car_state = car_state; + rs485_send_heartbeat(); + _hb_last_ms = now; + return; + } + /* 线圈状态变化 → 立即上报 */ + if (loop_state != _last_loop_state) { + _last_loop_state = loop_state; + rs485_send_heartbeat(); + _hb_last_ms = now; + return; + } + + /* 周期心跳 */ + interval = (!loop_state) ? RS485_FAULT_MS : RS485_HEARTBEAT_MS; + if (now - _hb_last_ms >= interval) { + rs485_send_heartbeat(); + _hb_last_ms = now; + } +} + +/* 强制立即上报 (由外部事件触发) */ +void rs485_event_notify(void) +{ + _last_car_state = (_last_car_state ^ 1); // 强制触发 +} + +void rs485_loop_notify(void) +{ + _last_loop_state = (_last_loop_state ^ 1); // 强制触发 +} + +void rs485_reset_heartbeat(void) +{ + _hb_need_init = 1; + _hb_last_ms = 0; // 立即触发首次心跳 +} diff --git a/docs/devlog.md b/docs/devlog.md index 60056f6..be86c63 100644 --- a/docs/devlog.md +++ b/docs/devlog.md @@ -4,6 +4,58 @@ --- +## 2026-07-28 — 光耦输出模式升级:`DLD_OUTPUT_MODE` 枚举 + `direction_mode` 低四位取值 + +### 背景 + +光耦输入功能从原来的「二值化开关」(使能/复位)升级为 **7 种输出模式**, +由 `direction_mode` 字节的低四位选择。`g_fm_radar_input_disable`(来自 `function_mode` bit1) +更名为 `g_output_radar_mode`,语义从布尔值变为枚举值。 + +### `DLD_OUTPUT_MODE` 枚举 + +```c +typedef enum _DLD_OUTPUT_MODE +{ + OUTPUT_MODE_LOOP_X_RESET = 0, // 光耦输入为复位信号,仅地感检测输出 + OUTPUT_MODE_LOOP_ONLY, // 仅地感,光耦输入无效 + OUTPUT_MODE_RADAR_ONLY, // 仅光耦(雷达输出接光耦输入),地感无效 + OUTPUT_MODE_LOOP_OR_RADAR, // 地感或雷达 + OUTPUT_MODE_LOOP_AND_RADAR, // 地感和雷达 + OUTPUT_MODE_HUMAN_VD_MIX, // 人车混行防砸:有车后光耦/地感"或",离场 3s 内光耦有效持续防砸 + OUTPUT_MODE_ETC, // ETC:有车后光耦/地感"或",光耦断开需 500ms 防抖才释放继电器 +}DLD_OUTPUT_MODE; +``` + +### 变量与取值变更 + +| 项目 | 旧 | 新 | +|------|-----|-----| +| 全局变量名 | `g_fm_radar_input_disable` | `g_output_radar_mode` | +| 符号类型 | bit (0/1) | `uint8_t` (0~6) | +| 取值来源 | `(function_mode >> 1) & 0x01` | `direction_mode & 0x0F` | +| 解析位置 | `peripheral_main.c:para_store_init()` + `dbn_ble_srv.c:unpack_pkg_set_cjq_param()` | 同上 | +| BLE 回读字段 | `g_function_mode` | `g_loop_cng_unit.direction_mode` (完整 8 位) | +| `opt_input_poll` 门控 | `if (g_fm_radar_input_disable == 0)` → 雷达模式 return | `if (g_output_radar_mode)` → 非 0 模式 return | + +### 实现状态 + +| 模式 | 状态 | 说明 | +|------|------|------| +| 0 — LOOP_X_RESET | ✅ 已实现 | PB10 低电平 ≥400ms(80 tick×5ms)后上升沿 `SYS_ResetExecute()` 芯片复位 | +| 1~6 | ❌ 未实现 | `opt_input_poll()` 中直接 return | + +### 文件变更 + +| 文件 | 变更 | +|------|------| +| `storage.h` | +`DLD_OUTPUT_MODE` 枚举 (7 种模式) | +| `cmcng.h` | `g_fm_radar_input_disable` → `g_output_radar_mode` | +| `peripheral_main.c` | 变量更名 + 解析路径改 `direction_mode & 0x0F` | +| `dbn_ble_srv.c` | 同上 + BLE 回读改用 `direction_mode` 完整字段 | + +--- + ## 2026-07-22 — BLE 新增 RS485 地址/波特率读写 + 空闲关蓝牙超时 + 功能码细分 ### 新增 BLE 命令