feat(opt): 光耦输出模式升级 — DLD_OUTPUT_MODE 枚举 + direction_mode 低四位取值

- 新增 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: 更新开发日志
This commit is contained in:
wangfq
2026-07-28 09:26:09 +08:00
parent ffaa5cd733
commit f5edab43c2
6 changed files with 391 additions and 325 deletions
@@ -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_function_mode = (g_loop_cng_unit.direction_mode >> 4) & 0x0F;
g_fm_700bs_disable = g_function_mode & 0x01; 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_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; 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_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++] = (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++] = (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); set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, i);
// PRINT("\nGet_Cjq_param\n"); // PRINT("\nGet_Cjq_param\n");
@@ -42,7 +42,7 @@ extern uint8_t g_loop_power_up_state;
extern uint8_t g_function_mode; extern uint8_t g_function_mode;
extern uint8_t g_fm_700bs_disable ; //0: 700bs protocol; 1 Modbus protocol 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 移植) */ /* 新算法状? (DLD154V4B 移植) */
extern uint8_t g_loop_stable; // 稳定期结束标? (0=收敛?, 1=稳定) extern uint8_t g_loop_stable; // 稳定期结束标? (0=收敛?, 1=稳定)
@@ -177,6 +177,17 @@ typedef struct _LOOP_ACS_INFO
} Loop_ACS_Info; } Loop_ACS_Info;
extern Loop_ACS_Info g_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() #pragma pack()
@@ -35,7 +35,7 @@
uint8_t g_flag_bt_state = 0; // 0 蓝牙没有连接,1 有蓝牙连接 uint8_t g_flag_bt_state = 0; // 0 蓝牙没有连接,1 有蓝牙连接
uint8_t g_function_mode = 0; uint8_t g_function_mode = 0;
uint8_t g_fm_700bs_disable = 0; //0: 700bs protocol; 1 Modbus protocol 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; uint32_t g_sys_freq = 0;
uint8_t g_storage_uart_num = 0; uint8_t g_storage_uart_num = 0;
uint32_t g_storage_uart_baud = 19200;//9600; 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_freq_level = g_loop_cng_unit.loopFreq_Level - 1;
g_function_mode = g_loop_cng_unit.direction_mode >> 4; g_function_mode = g_loop_cng_unit.direction_mode >> 4;
g_fm_700bs_disable = g_function_mode & 0x01; 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; g_freq_sens = (g_loop_cng_unit.sensitvity > 0) ? (g_loop_cng_unit.sensitvity - 1) : 0;
#ifdef DEBUG #ifdef DEBUG
@@ -850,8 +850,8 @@ void cjq_srv(void){
/*=========================================================================== /*===========================================================================
* opt_input_poll — 光耦输入处理 * opt_input_poll — 光耦输入处理
* *
* g_fm_radar_input_disable == 0: 雷达输入模式 (暂未实现) * g_output_radar_mode == 0: 光耦复位模式 + 地感模式
* g_fm_radar_input_disable == 1: 光耦复位模式 * g_output_radar_mode >= 1: 雷达输入模式 (暂未实现)
* 外部干接点接通 → PB10=0 → 计时; 保持 ≥500ms 后断开 → 上升沿 → 芯片复位 * 外部干接点接通 → PB10=0 → 计时; 保持 ≥500ms 后断开 → 上升沿 → 芯片复位
*===========================================================================*/ *===========================================================================*/
static void opt_input_poll(void) 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 uint32_t _opt_low_since = 0; // PB10 变低的时刻
static uint8_t _opt_armed = 0; // 低电平已保持 ≥500ms, 等待上升沿 static uint8_t _opt_armed = 0; // 低电平已保持 ≥500ms, 等待上升沿
if (g_fm_radar_input_disable == 0) { if (g_output_radar_mode) {
_opt_low_since = 0; _opt_low_since = 0;
_opt_armed = 0; _opt_armed = 0;
return; return;
@@ -868,7 +868,7 @@ static void opt_input_poll(void)
if (OPT_IN1 == 0) { // 干接点接通 → 低电平 if (OPT_IN1 == 0) { // 干接点接通 → 低电平
if (_opt_low_since == 0) { if (_opt_low_since == 0) {
_opt_low_since = mstick(); _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, 等待上升沿 _opt_armed = 1; // 满足 500ms, 等待上升沿
} }
} else { // 干接点断开 → 高电平 } else { // 干接点断开 → 高电平
+319 -316
View File
@@ -1,316 +1,319 @@
/* /*
* rs485_700bs.c * rs485_700bs.c
* DLD154Pro RS485 700BS 从机协议实现 * DLD154Pro RS485 700BS 从机协议实现
* *
* 物理层: RS485 半双工, 9600 8N1, PA14=TX PA15=RX PA13=RE * 物理层: RS485 半双工, 9600 8N1, PA14=TX PA15=RX PA13=RE
* 帧格式: 主机 3 字节 (STX ADDR XOR), 从机可变长 * 帧格式: 主机 3 字节 (STX ADDR XOR), 从机可变长
* 心跳: 空闲 5s / 故障 1s / 事件立即 * 心跳: 空闲 5s / 故障 1s / 事件立即
* *
* 2026-07-23 初始实现 * 2026-07-23 初始实现
*/ */
#include "CONFIG.h" #include "CONFIG.h"
#include "CH59x_common.h" #include "CH59x_common.h"
#include "cmcng.h" #include "cmcng.h"
#include "rs485_700bs.h" #include "rs485_700bs.h"
#include "storage.h" #include "storage.h"
#include "dbn_ble_srv.h" #include "dbn_ble_srv.h"
#include <string.h> #include <string.h>
/*=========================================================================== /*===========================================================================
* UART0 中断接收缓冲 * UART0 中断接收缓冲
*===========================================================================*/ *===========================================================================*/
#define RS485_RX_BUF_SIZE 16 #define RS485_RX_BUF_SIZE 16
static uint8_t _rx_buf[RS485_RX_BUF_SIZE]; static uint8_t _rx_buf[RS485_RX_BUF_SIZE];
static uint8_t _rx_len = 0; static uint8_t _rx_len = 0;
static uint32_t _rx_last_tick = 0; // 帧间隔计时 (3.5 字符 ≈ 4ms @9600) static uint32_t _rx_last_tick = 0; // 帧间隔计时 (3.5 字符 ≈ 4ms @9600)
/*=========================================================================== /*===========================================================================
* 心跳 & 状态追踪 * 心跳 & 状态追踪
*===========================================================================*/ *===========================================================================*/
static uint32_t _hb_last_ms = 0; // 上次心跳时刻 static uint32_t _hb_last_ms = 0; // 上次心跳时刻
static uint8_t _hb_need_init = 1; // 需要发 0xA1 首次心跳 static uint8_t _hb_need_init = 1; // 需要发 0xA1 首次心跳
static uint8_t _last_car_state = 0; // car_state 快照 (翻转沿检测) static uint8_t _last_car_state = 0; // car_state 快照 (翻转沿检测)
static uint8_t _last_loop_state = 0; // loop_state 快照 static uint8_t _last_loop_state = 0; // loop_state 快照
/*=========================================================================== /*===========================================================================
* RS485 方向控制 (PA13) * RS485 方向控制 (PA13)
*===========================================================================*/ *===========================================================================*/
#define RS485_RE_TX() GPIOA_SetBits(GPIO_Pin_13) #define RS485_RE_TX() GPIOA_SetBits(GPIO_Pin_13)
#define RS485_RE_RX() GPIOA_ResetBits(GPIO_Pin_13) #define RS485_RE_RX() GPIOA_ResetBits(GPIO_Pin_13)
/*=========================================================================== /*===========================================================================
* UART0 初始化 — 9600 8N1, 中断接收 * UART0 初始化 — 9600 8N1, 中断接收
*===========================================================================*/ *===========================================================================*/
void rs485_init(void) void rs485_init(void)
{ {
/* PA14=TX(推挽), PA15=RX(上拉输入), PA13=RE(推挽, 默认接收) */ /* PA14=TX(推挽), PA15=RX(上拉输入), PA13=RE(推挽, 默认接收) */
GPIOA_SetBits(GPIO_Pin_13); // RE 先拉高, GPIO 模式 GPIOA_SetBits(GPIO_Pin_13); // RE 先拉高, GPIO 模式
GPIOA_ModeCfg(GPIO_Pin_14, GPIO_ModeOut_PP_5mA); GPIOA_ModeCfg(GPIO_Pin_14, GPIO_ModeOut_PP_5mA);
GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU); GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU);
GPIOA_ModeCfg(GPIO_Pin_13, GPIO_ModeOut_PP_5mA); GPIOA_ModeCfg(GPIO_Pin_13, GPIO_ModeOut_PP_5mA);
/* UART0 外设: 9600 8N1 */ /* UART0 外设: 9600 8N1 */
UART0_BaudRateCfg(RS485_BAUD); UART0_BaudRateCfg(RS485_BAUD);
R8_UART0_LCR = 0x03; // 8 数据位, 1 停止位, 无校验 R8_UART0_LCR = 0x03; // 8 数据位, 1 停止位, 无校验
R8_UART0_MCR = 0x04; // RE=0 进入接收模式 R8_UART0_MCR = 0x04; // RE=0 进入接收模式
/* 中断: 接收可用 */ /* 中断: 接收可用 */
UART0_CLR_RXFIFO(); UART0_CLR_RXFIFO();
UART0_ByteTrigCfg(UART_1BYTE_TRIG); // 每收到 1 字节触发中断 UART0_ByteTrigCfg(UART_1BYTE_TRIG); // 每收到 1 字节触发中断
UART0_INTCfg(ENABLE, RB_IER_RECV_RDY | RB_IER_LINE_STAT); UART0_INTCfg(ENABLE, RB_IER_RECV_RDY | RB_IER_LINE_STAT);
PFIC_EnableIRQ(UART0_IRQn); PFIC_EnableIRQ(UART0_IRQn);
RS485_RE_RX(); // 默认接收模式 RS485_RE_RX(); // 默认接收模式
_hb_last_ms = mstick(); // 对齐心跳起始时刻,避免首次立即触发 _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);
} }
/*=========================================================================== /*===========================================================================
* 发送一帧 — 切 RE=TX, 发完等 1ms, 切回 RE=RX * 发送一帧 — 切 RE=TX, 发完等 1ms, 切回 RE=RX
*===========================================================================*/ *===========================================================================*/
static void rs485_send(const uint8_t *data, uint8_t len) static void rs485_send(const uint8_t *data, uint8_t len)
{ {
RS485_RE_TX(); RS485_RE_TX();
mDelayuS(100); // 总线稳定 mDelayuS(100); // 总线稳定
for (uint8_t i = 0; i < len; i++) { for (uint8_t i = 0; i < len; i++) {
while (R8_UART0_TFC != 0); // 等 TX FIFO 空 (上一字节发完) while (R8_UART0_TFC != 0); // 等 TX FIFO 空 (上一字节发完)
R8_UART0_THR = data[i]; R8_UART0_THR = data[i];
} }
while (R8_UART0_TFC != 0); // 等最后一字节发完 while (R8_UART0_TFC != 0); // 等最后一字节发完
mDelayuS(500); // 等待停止位 + 总线释放 mDelayuS(500); // 等待停止位 + 总线释放
UART0_CLR_TXFIFO(); UART0_CLR_TXFIFO();
RS485_RE_RX(); RS485_RE_RX();
} }
/*=========================================================================== /*===========================================================================
* 命令处理 * 命令处理
*===========================================================================*/ *===========================================================================*/
/* 直接从原始测量值计算线圈频率 (Hz),不依赖 BLE 报告通道 */ /* 直接从原始测量值计算线圈频率 (Hz),不依赖 BLE 报告通道 */
static uint32_t rs485_get_freq(void) static uint32_t rs485_get_freq(void)
{ {
extern uint32_t loop1_CAPVD; // vd1_task() 实时更新的 IIR 滤波值 extern uint32_t loop1_CAPVD; // vd1_task() 实时更新的 IIR 滤波值
if (loop1_CAPVD == 0 || loop1_LPCNT == 0) return 0; if (loop1_CAPVD == 0 || loop1_LPCNT == 0) return 0;
/* freq = FREQ_SYS / (CAPVD / LPCNT) + 360 (补偿值) */ /* freq = FREQ_SYS / (CAPVD / LPCNT) + 360 (补偿值) */
return (uint32_t)((uint64_t)FREQ_SYS * loop1_LPCNT / loop1_CAPVD) + 360; return (uint32_t)((uint64_t)FREQ_SYS * loop1_LPCNT / loop1_CAPVD) + 360;
} }
/* 0xFA — 查询有车状态 + 线圈好坏 */ /* 0xFA — 查询有车状态 + 线圈好坏 */
static void handle_query_status(uint8_t addr) static void handle_query_status(uint8_t addr)
{ {
extern uint8_t loop1_LOOP_OK; extern uint8_t loop1_LOOP_OK;
uint8_t data = addr & 0x1F; // bit4-0 = 地址 uint8_t data = addr & 0x1F; // bit4-0 = 地址
if (g_loop_acs_info.car_state) data |= 0x80; // bit7 = 有车 if (g_loop_acs_info.car_state) data |= 0x80; // bit7 = 有车
if (!loop1_LOOP_OK) data |= 0x20; // bit5 = 线圈断开 (LOOP_OK=0) if (!loop1_LOOP_OK) data |= 0x20; // bit5 = 线圈断开 (LOOP_OK=0)
uint8_t buf[3] = { RSP_STATUS, data, (uint8_t)(RSP_STATUS ^ data) }; uint8_t buf[3] = { RSP_STATUS, data, (uint8_t)(RSP_STATUS ^ data) };
rs485_send(buf, 3); rs485_send(buf, 3);
} }
/* 0x1B — 查询线圈频率 */ /* 0x1B — 查询线圈频率 */
static void handle_query_freq(uint8_t addr) static void handle_query_freq(uint8_t addr)
{ {
uint32_t freq = rs485_get_freq(); // Hz uint32_t freq = rs485_get_freq(); // Hz
uint8_t buf[6]; uint8_t buf[6];
buf[0] = RSP_FREQ; buf[0] = RSP_FREQ;
buf[1] = (uint8_t)((freq >> 16) & 0xFF); // Fre_1 (MSB) buf[1] = (uint8_t)((freq >> 16) & 0xFF); // Fre_1 (MSB)
buf[2] = (uint8_t)((freq >> 8) & 0xFF); // Fre_2 buf[2] = (uint8_t)((freq >> 8) & 0xFF); // Fre_2
buf[3] = (uint8_t)(freq & 0xFF); // Fre_3 (LSB) buf[3] = (uint8_t)(freq & 0xFF); // Fre_3 (LSB)
buf[4] = addr & 0x1F; buf[4] = addr & 0x1F;
buf[5] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3] ^ buf[4]; buf[5] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3] ^ buf[4];
rs485_send(buf, 6); rs485_send(buf, 6);
} }
/* 0x2C — 复位初始化线圈 */ /* 0x2C — 复位初始化线圈 */
static void handle_reset_init(uint8_t addr) static void handle_reset_init(uint8_t addr)
{ {
(void)addr; (void)addr;
extern uint8_t g_loop_stable; extern uint8_t g_loop_stable;
extern uint8_t loop1_LOOP_OK0; extern uint8_t loop1_LOOP_OK0;
extern uint8_t loop1_LC_Reset; extern uint8_t loop1_LC_Reset;
extern uint8_t loop1_INI_LOOP; extern uint8_t loop1_INI_LOOP;
extern uint16_t loop1_stable_cnt; extern uint16_t loop1_stable_cnt;
extern uint16_t LC_Hold_CNT; extern uint16_t LC_Hold_CNT;
extern uint32_t loop1_ORG_CNT, loop1_ORG_SUM; extern uint32_t loop1_ORG_CNT, loop1_ORG_SUM;
loop1_LC_Reset = 1; loop1_LC_Reset = 1;
loop1_INI_LOOP = 1; loop1_INI_LOOP = 1;
loop1_LOOP_OK0 = 0; loop1_LOOP_OK0 = 0;
g_loop_stable = 0; g_loop_stable = 0;
loop1_stable_cnt = 0; loop1_stable_cnt = 0;
LC_Hold_CNT = 0; LC_Hold_CNT = 0;
loop1_ORG_CNT = 0; loop1_ORG_CNT = 0;
loop1_ORG_SUM = 0; loop1_ORG_SUM = 0;
_hb_need_init = 1; _hb_need_init = 1;
_last_car_state = (_last_car_state ^ 1); _last_car_state = (_last_car_state ^ 1);
PRINT("RS485: coil reset init\n"); PRINT("RS485: coil reset init\n");
} }
/* 0x3D — 查询拨码 DIP1-5 (DLD154Pro 无物理拨码, 从 BLE 配置映射) */ /* 0x3D — 查询拨码 DIP1-5 (DLD154Pro 无物理拨码, 从 BLE 配置映射) */
static void handle_query_dip(uint8_t addr) static void handle_query_dip(uint8_t addr)
{ {
uint8_t data = 0; uint8_t data = 0;
uint8_t sens = g_loop_cng_unit.sensitvity & 0x0F; // 灵敏度 0-3 uint8_t sens = g_loop_cng_unit.sensitvity & 0x0F; // 灵敏度 0-3
data |= (sens & 0x01); // DIP1 = bit0 data |= (sens & 0x01); // DIP1 = bit0
data |= ((sens >> 1) & 0x01) << 1; // DIP2 = bit1 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.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.exist_mode == 0) data |= 0x08; // DIP4 = bit3 (0=永久→ON=1)
if (g_loop_cng_unit.delay_time > 0) data |= 0x10; // DIP5 = bit4 (>0=启用延时) 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 buf[4] = { RSP_DIP, data, (uint8_t)(addr & 0x1F),
(uint8_t)(RSP_DIP ^ data ^ (addr & 0x1F)) }; (uint8_t)(RSP_DIP ^ data ^ (addr & 0x1F)) };
rs485_send(buf, 4); rs485_send(buf, 4);
} }
/*=========================================================================== /*===========================================================================
* 心跳上报 (0xAA) — 空闲 5s / 故障 1s / 事件立即 * 心跳上报 (0xAA) — 空闲 5s / 故障 1s / 事件立即
*===========================================================================*/ *===========================================================================*/
/* Fre_Err 计算: bit0=下限异常, bit4=上限异常 */ /* Fre_Err 计算: bit0=下限异常, bit4=上限异常 */
static uint8_t calc_freq_err(uint32_t freq) static uint8_t calc_freq_err(uint32_t freq)
{ {
(void)freq; (void)freq;
/* DLD154Pro 未实现频率越限检测, 始终返回 0 */ /* DLD154Pro 未实现频率越限检测, 始终返回 0 */
return 0x00; return 0x00;
} }
static void rs485_send_heartbeat(void) static void rs485_send_heartbeat(void)
{ {
extern uint8_t loop1_LOOP_OK; extern uint8_t loop1_LOOP_OK;
uint32_t freq = rs485_get_freq(); uint32_t freq = rs485_get_freq();
uint8_t st = g_rs485_id & 0x1F; uint8_t st = g_rs485_id & 0x1F;
if (g_loop_acs_info.car_state) st |= 0x80; // bit7=有车 if (g_loop_acs_info.car_state) st |= 0x80; // bit7=有车
if (!loop1_LOOP_OK) st |= 0x20; // bit5=线圈断开 if (!loop1_LOOP_OK) st |= 0x20; // bit5=线圈断开
uint8_t buf[9]; uint8_t buf[9];
buf[0] = HB_PREAMBLE; // 0xAA buf[0] = HB_PREAMBLE; // 0xAA
buf[1] = _hb_need_init ? HB_INIT_FIRST : HB_INIT_NORMAL; buf[1] = _hb_need_init ? HB_INIT_FIRST : HB_INIT_NORMAL;
_hb_need_init = 0; _hb_need_init = 0;
buf[2] = st; // State buf[2] = st; // State
buf[3] = calc_freq_err(freq); // Fre_Err buf[3] = calc_freq_err(freq); // Fre_Err
buf[4] = (uint8_t)((freq >> 16) & 0xFF); // Fre_1 buf[4] = (uint8_t)((freq >> 16) & 0xFF); // Fre_1
buf[5] = (uint8_t)((freq >> 8) & 0xFF); // Fre_2 buf[5] = (uint8_t)((freq >> 8) & 0xFF); // Fre_2
buf[6] = (uint8_t)(freq & 0xFF); // Fre_3 buf[6] = (uint8_t)(freq & 0xFF); // Fre_3
buf[7] = g_rs485_id & 0x1F; // ADDR buf[7] = g_rs485_id & 0x1F; // ADDR
buf[8] = buf[0]; buf[8] = buf[0];
for (uint8_t i = 1; i < 8; i++) buf[8] ^= buf[i]; for (uint8_t i = 1; i < 8; i++) buf[8] ^= buf[i];
rs485_send(buf, 9); rs485_send(buf, 9);
} }
void tmp_send(void){
/*=========================================================================== rs485_send_heartbeat();
* 帧解析 & 主循环 }
*===========================================================================*/
/*===========================================================================
/* UART0 接收中断 (每字节触发) */ * 帧解析 & 主循环
__attribute__((interrupt("WCH-Interrupt-fast"))) *===========================================================================*/
void UART0_IRQHandler(void)
{ /* UART0 接收中断 (每字节触发) */
switch (UART0_GetITFlag()) { __attribute__((interrupt("WCH-Interrupt-fast")))
case UART_II_RECV_RDY: // 接收数据就绪 void UART0_IRQHandler(void)
if (_rx_len < RS485_RX_BUF_SIZE) { {
_rx_buf[_rx_len++] = UART0_RecvByte(); switch (UART0_GetITFlag()) {
} else { case UART_II_RECV_RDY: // 接收数据就绪
_rx_len = 0; // 溢出, 丢弃 if (_rx_len < RS485_RX_BUF_SIZE) {
} _rx_buf[_rx_len++] = UART0_RecvByte();
_rx_last_tick = mstick(); } else {
break; _rx_len = 0; // 溢出, 丢弃
}
case UART_II_LINE_STAT: // 线路状态 (帧错误/溢出等) _rx_last_tick = mstick();
UART0_GetLinSTA(); // 读 LSR 清状态 break;
break;
case UART_II_LINE_STAT: // 线路状态 (帧错误/溢出等)
default: UART0_GetLinSTA(); // 读 LSR 清状态
break; break;
}
} default:
break;
/* 处理收到的完整帧 */ }
static void rs485_process_frame(void) }
{
if (_rx_len < 3) return; // 最少 3 字节 /* 处理收到的完整帧 */
static void rs485_process_frame(void)
/* XOR 校验: STX ^ ADDR/CMD */ {
uint8_t stx = _rx_buf[0]; if (_rx_len < 3) return; // 最少 3 字节
uint8_t addr = _rx_buf[1];
uint8_t xorr = _rx_buf[2]; /* XOR 校验: STX ^ ADDR/CMD */
uint8_t stx = _rx_buf[0];
if ((stx ^ addr) != xorr) { uint8_t addr = _rx_buf[1];
PRINT("RS485: XOR fail %02X %02X %02X\n", stx, addr, xorr); uint8_t xorr = _rx_buf[2];
_rx_len = 0;
return; if ((stx ^ addr) != xorr) {
} PRINT("RS485: XOR fail %02X %02X %02X\n", stx, addr, xorr);
_rx_len = 0;
/* 地址匹配检查 (0x2C 复位命令全地址响应) */ return;
if (stx != CMD_RESET_INIT && (addr & 0x1F) != (g_rs485_id & 0x1F)) { }
_rx_len = 0;
return; // 不是给我的 /* 地址匹配检查 (0x2C 复位命令全地址响应) */
} if (stx != CMD_RESET_INIT && (addr & 0x1F) != (g_rs485_id & 0x1F)) {
_rx_len = 0;
switch (stx) { return; // 不是给我的
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; switch (stx) {
case CMD_QUERY_DIP: handle_query_dip(addr); break; case CMD_QUERY_STATUS: handle_query_status(addr); break;
default: case CMD_QUERY_FREQ: handle_query_freq(addr); break;
PRINT("RS485: unknown cmd 0x%02X\n", stx); case CMD_RESET_INIT: handle_reset_init(addr); break;
break; case CMD_QUERY_DIP: handle_query_dip(addr); break;
} default:
PRINT("RS485: unknown cmd 0x%02X\n", stx);
_rx_len = 0; break;
} }
/* 主循环调用 — 帧超时检测 + 周期心跳 */ _rx_len = 0;
void rs485_poll(void) }
{
/* 帧超时: 3.5 字符 ≈ 4ms @9600, 10ms 足够 */ /* 主循环调用 — 帧超时检测 + 周期心跳 */
if (_rx_len > 0 && mstick() - _rx_last_tick > 10) { void rs485_poll(void)
rs485_process_frame(); {
} /* 帧超时: 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=断开 extern uint8_t loop1_LOOP_OK;
uint32_t interval; uint32_t now = mstick();
uint8_t car_state = g_loop_acs_info.car_state;
/* 事件触发: car_state 翻转沿 → 立即上报 */ uint8_t loop_state = loop1_LOOP_OK; // 1=正常, 0=断开
if (car_state != _last_car_state) { uint32_t interval;
_last_car_state = car_state;
rs485_send_heartbeat(); /* 事件触发: car_state 翻转沿 → 立即上报 */
_hb_last_ms = now; if (car_state != _last_car_state) {
return; _last_car_state = car_state;
} rs485_send_heartbeat();
/* 线圈状态变化 → 立即上报 */ _hb_last_ms = now;
if (loop_state != _last_loop_state) { return;
_last_loop_state = loop_state; }
rs485_send_heartbeat(); /* 线圈状态变化 → 立即上报 */
_hb_last_ms = now; if (loop_state != _last_loop_state) {
return; _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; 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_event_notify(void)
{
void rs485_loop_notify(void) _last_car_state = (_last_car_state ^ 1); // 强制触发
{ }
_last_loop_state = (_last_loop_state ^ 1); // 强制触发
} void rs485_loop_notify(void)
{
void rs485_reset_heartbeat(void) _last_loop_state = (_last_loop_state ^ 1); // 强制触发
{ }
_hb_need_init = 1;
_hb_last_ms = 0; // 立即触发首次心跳 void rs485_reset_heartbeat(void)
} {
_hb_need_init = 1;
_hb_last_ms = 0; // 立即触发首次心跳
}
+52
View File
@@ -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 低电平 ≥400ms80 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 地址/波特率读写 + 空闲关蓝牙超时 + 功能码细分 ## 2026-07-22 — BLE 新增 RS485 地址/波特率读写 + 空闲关蓝牙超时 + 功能码细分
### 新增 BLE 命令 ### 新增 BLE 命令