diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/include/rs485_700bs.h b/BLE/DLD154Pro_Peripheral_28K/APP/include/rs485_700bs.h new file mode 100644 index 0000000..5a213b6 --- /dev/null +++ b/BLE/DLD154Pro_Peripheral_28K/APP/include/rs485_700bs.h @@ -0,0 +1,53 @@ +/* + * rs485_700bs.h + * DLD154Pro RS485 700BS 协议 — 车检器从机实现 + * 协议版本: V1.15 | 2022-08-19 + * 波特率: 9600 8N1, 半双工 RS485, XOR 校验 + */ +#ifndef __RS485_700BS_H__ +#define __RS485_700BS_H__ + +#include + +/* 协议常量 */ +#define RS485_BAUD 9600 +#define RS485_HEARTBEAT_MS 5000 // 空闲心跳 5s +#define RS485_FAULT_MS 1000 // 故障心跳 1s +#define RS485_MAX_FRAME 10 // 最大帧长 + +/* 命令码 (主机→从机) */ +#define CMD_QUERY_STATUS 0xFA // 查询有车状态 + 线圈好坏 +#define CMD_QUERY_FREQ 0x1B // 查询线圈频率 +#define CMD_RESET_INIT 0x2C // 复位初始化线圈 +#define CMD_QUERY_DIP 0x3D // 查询拨码 DIP1-5 + +/* 回复码 (从机→主机) */ +#define RSP_STATUS 0xF5 // 状态回复 +#define RSP_FREQ 0x14 // 频率回复 +#define RSP_DIP 0x32 // 拨码回复 + +/* 心跳 (从机主动) */ +#define HB_PREAMBLE 0xAA +#define HB_INIT_FIRST 0xA1 // 设备复位后首次 +#define HB_INIT_NORMAL 0xA0 // 正常心跳 + +/*=========================================================================== + * API + *===========================================================================*/ + +/* 初始化 RS485 通信 (UART0 9600 8N1 + 中断接收) */ +void rs485_init(void); + +/* 主循环轮询 — 处理接收的 3 字节命令帧, 驱动心跳上报 */ +void rs485_poll(void); + +/* 有车/无车事件触发立即上报 (由检测算法调用) */ +void rs485_event_notify(void); + +/* 线圈状态变化触发 (连接/断开, 由 cjq_srv 调用) */ +void rs485_loop_notify(void); + +/* 重置心跳计时 (复位后调用, 触发首次 A1 上报) */ +void rs485_reset_heartbeat(void); + +#endif /* __RS485_700BS_H__ */ diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/peripheral_main.c b/BLE/DLD154Pro_Peripheral_28K/APP/peripheral_main.c index f7788a2..a5933fd 100644 --- a/BLE/DLD154Pro_Peripheral_28K/APP/peripheral_main.c +++ b/BLE/DLD154Pro_Peripheral_28K/APP/peripheral_main.c @@ -29,6 +29,7 @@ #include "cmcng.h" #include "dbn_ble_srv.h" #include "CH59x_pwm.h" +#include "rs485_700bs.h" uint8_t g_flag_bt_state = 0; // 0 蓝牙没有连接,1 有蓝牙连接 @@ -51,9 +52,9 @@ uint8_t g_rs485_id = 0; * PB22 : LEDA 绿色指示灯 (有车亮/无车灭, 低电平有效) * PB23 : LEDB 红色指示灯 (呼吸灯, 低电平有效) * PA12 : FLEVEL 线圈调频 (一级, 0=33nF, 1=0nF) - * PA14 : UART0_Tx RS485发送 (协议待定) - * PA15 : UART0_Rx RS485接收 (协议待定) - * PA13 : UART0_RE RS485方向控制 (协议待定) + * PA14 : UART0_Tx RS485发送 (700BS 协议) + * PA15 : UART0_Rx RS485接收 (700BS 协议) + * PA13 : UART0_RE RS485方向控制 (700BS 协议) * PB10 : IN_1 光耦输入 (逻辑待定) *===========================================================================*/ #define RLY1_ON (R32_PA_OUT |= BV(8)) @@ -69,7 +70,7 @@ uint8_t g_rs485_id = 0; #define FLEVEL_LOW (R32_PA_OUT &= ~BV(12)) #define FLEVEL_HIGH (R32_PA_OUT |= BV(12)) -/* RS485 预留 (协议待定) */ +/* RS485 700BS 协议 (2026-07-23) */ #define RS485_TX_PIN GPIO_Pin_14 #define RS485_RX_PIN GPIO_Pin_15 #define RS485_RE_PIN GPIO_Pin_13 @@ -391,12 +392,7 @@ void tm_init(void){ void gpio_uart_init(void) { /*--- RS485 通信 (PA14=TX, PA15=RX, PA13=RE) ---*/ - /* 协议待定, 仅初始化引脚, 暂不配置 UART0 */ - GPIOA_SetBits(GPIO_Pin_14); - GPIOA_ModeCfg(GPIO_Pin_14, GPIO_ModeOut_PP_5mA); // RS485 TX - GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU); // RS485 RX - GPIOA_SetBits(GPIO_Pin_13); - GPIOA_ModeCfg(GPIO_Pin_13, GPIO_ModeOut_PP_5mA); // RS485 RE (默认接收模式) + /* 由 rs485_init() 统一初始化 UART0 + GPIO + 中断 */ /*--- 光耦输入 (PB10=IN_1) ---*/ /* 逻辑待定, 仅初始化引脚为输入上拉 */ @@ -873,6 +869,8 @@ void Main_Circulation() cjq_srv(); + rs485_poll(); // RS485 700BS 协议: 命令处理 + 心跳上报 + if(g_flag_bt_state == 0){ // 如果蓝牙断开,主动上报需要禁用 if(g_dbn_ble_state_acs_enable.enable){ diff --git a/BLE/DLD154Pro_Peripheral_28K/APP/rs485_700bs.c b/BLE/DLD154Pro_Peripheral_28K/APP/rs485_700bs.c new file mode 100644 index 0000000..66cb9d6 --- /dev/null +++ b/BLE/DLD154Pro_Peripheral_28K/APP/rs485_700bs.c @@ -0,0 +1,291 @@ +/* + * 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 + +/*=========================================================================== + * 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_ResetRTXFIFO(); + UART0_ByteTrigCfg(UART0_Res_1Byte); // 每收到 1 字节触发中断 + UART0_INTCfg(ENABLE, RB_IER_RECV_RDY | RB_IER_LINE_STAT); + PFIC_EnableIRQ(UART0_IRQn); + + RS485_RE_RX(); // 默认接收模式 + 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_ResetRTXFIFO(); + RS485_RE_RX(); +} + +/*=========================================================================== + * 命令处理 + *===========================================================================*/ + +/* 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 = g_loop_acs_info.frequent; // 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 无物理拨码, 返回虚拟值=0) */ +static void handle_query_dip(uint8_t addr) +{ + uint8_t data = 0; // DIP1-5 全部 OFF (DLD154Pro 无 DIP) + 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 = g_loop_acs_info.frequent; + 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) +{ + if (UART0_GetITFlag(UART0_II_RECV_RDY)) { + UART0_ClearITFlag(UART0_II_RECV_RDY); + if (_rx_len < RS485_RX_BUF_SIZE) { + _rx_buf[_rx_len++] = R8_UART0_RBR; + } else { + _rx_len = 0; // 溢出, 丢弃 + } + _rx_last_tick = mstick(); + } +} + +/* 处理收到的完整帧 */ +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; // 立即触发首次心跳 +}