feat(vd960DBN): 实现 DLD960Loop 串口通信协议 (0x7F)
新增: - docs/DLD960Loop_串口通信协议.md — 协议文档 V1.02 - loop_uart_proto.h/c — 协议实现: checksum/组包/解析/帧状态机/命令状态机 修改: - usart_biz.c: 使用 lup_feed_byte() 帧解析器替代 timeout heuristic; 波特率修正为 115200 - tcp_json_srv.c/h: loop_param_set/query 真实实现(0x63/0x64), 0xC0 传感器推流, 延迟响应机制 - peripheral_main.c: 添加 tcp_json_push_sensor() 调用, 帧解析器超时保护 校验验证: 5个协议例程 XOR+SUM 全部通过
This commit is contained in:
@@ -0,0 +1,239 @@
|
|||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* @file loop_uart_proto.h
|
||||||
|
* @author wangfq
|
||||||
|
* @version V1.0
|
||||||
|
* @date 2026-07-02
|
||||||
|
* @brief DLD960Loop 串口通信协议 (7F) — CH32V208 ↔ DLD960Loop MCU
|
||||||
|
*
|
||||||
|
* 帧格式: 7F | Addr | LEN | CMD | Data... | XOR | SUM
|
||||||
|
* UART: USART2, 115200bps, PA2(Tx)/PA3(Rx)
|
||||||
|
*
|
||||||
|
* 该协议是通信MCU(CH32V208)与地感MCU(DLD960Loop/AT32F421)之间的接口。
|
||||||
|
* 网络接口(TCP JSON/5960)通过本模块向地感MCU下发命令并获取数据。
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
#ifndef __LOOP_UART_PROTO_H__
|
||||||
|
#define __LOOP_UART_PROTO_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Protocol Constants
|
||||||
|
*===========================================================================*/
|
||||||
|
#define LUP_MAGIC 0x7F // 配置/查询协议
|
||||||
|
#define LUP_MAGIC_OTA 0x9F // OTA
|
||||||
|
|
||||||
|
#define LUP_ADDR_DEFAULT 0x00 // 默认地址
|
||||||
|
|
||||||
|
#define LUP_MAX_VALUE_LEN 64 // Value 字段最大长度
|
||||||
|
#define LUP_MAX_PKG_LEN (1 + 3 + LUP_MAX_VALUE_LEN + 2) // 70 bytes 最大帧
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Command Bytes (CMD)
|
||||||
|
*===========================================================================*/
|
||||||
|
#define LUP_CMD_GET_VERSION 0x4A // 获取设备版本号
|
||||||
|
#define LUP_CMD_RESET 0x6D // 设备复位(无回复)
|
||||||
|
#define LUP_CMD_FACTORY_INIT 0x92 // 出厂初始化
|
||||||
|
#define LUP_CMD_SENSITIVITY 0x8A // 读写线圈灵敏度列表
|
||||||
|
#define LUP_CMD_SET_PARAM 0x63 // 设置多路参数
|
||||||
|
#define LUP_CMD_GET_PARAM 0x64 // 读取多路参数
|
||||||
|
#define LUP_CMD_SENSOR_REPORT 0xC0 // 设备主动上报传感信息
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Sensor Types (0xC0 SensType)
|
||||||
|
*===========================================================================*/
|
||||||
|
#define LUP_SENS_TYPE_MULTI_COIL 0x0C // 多路线圈传感信息(四路)
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* R/W byte for sensitivity (0x8A)
|
||||||
|
*===========================================================================*/
|
||||||
|
#define LUP_SENS_RW_READ 0x00
|
||||||
|
#define LUP_SENS_RW_WRITE 0x01
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* 线圈数
|
||||||
|
*===========================================================================*/
|
||||||
|
#define LUP_COIL_COUNT 4
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Data Structures
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
/* 线圈配置 (0x63 Set / 0x64 Get 中使用) */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t sensitivity; // 低4位灵敏度(0~9), 高4位频率等级[4]=高频,[5]=低频
|
||||||
|
uint8_t loop_delay; // 延时时间 0~200, 0.1s/级
|
||||||
|
uint8_t output_mode; // 低3位: 0=存在,1=进入脉冲,2=离开脉冲,3=方向
|
||||||
|
// 高5位: SafeMode 分钟数(0=关闭)
|
||||||
|
uint8_t exist_mode; // 低4位: 0=永久, 非0=有限分钟
|
||||||
|
uint8_t direction_mode; // 低3位: 0=触发, 1~6=方向模式
|
||||||
|
// 高4位: Function_Mode
|
||||||
|
} LUP_CoilParam;
|
||||||
|
|
||||||
|
/* 多路参数设置 (0x63) */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t auto_mode; // 0=不启用自动调频, 默认0
|
||||||
|
uint8_t amount; // 线圈数量
|
||||||
|
LUP_CoilParam params[LUP_COIL_COUNT];
|
||||||
|
} LUP_ParamSet;
|
||||||
|
|
||||||
|
/* 多路参数读取返回 (0x64 Response) */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t auto_mode;
|
||||||
|
uint8_t amount;
|
||||||
|
LUP_CoilParam params[LUP_COIL_COUNT];
|
||||||
|
uint32_t freq[LUP_COIL_COUNT][3]; // [ch][0]=freq1, [1]=freq2, [2]=freq3
|
||||||
|
} LUP_ParamGet;
|
||||||
|
|
||||||
|
/* 单路线圈传感数据单元 (0xC0/0x0C 中的每一路线圈) */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t freq_level : 2; // 00=高频33nF, 01=中高43nF, 10=中低66nF, 11=低频76nF
|
||||||
|
uint8_t direction : 1; // 0=触发, 1=方向判别
|
||||||
|
uint8_t freq_type : 1; // 0=初始频率, 1=实时频率
|
||||||
|
uint8_t sensitivity : 4; // 灵敏度等级
|
||||||
|
// 线圈评估条件
|
||||||
|
uint8_t condition : 4; // 环境状态评估值(0=正常, 值越大干扰越大)
|
||||||
|
uint8_t loop_state : 1; // 0=正常, 1=断开
|
||||||
|
uint8_t car_state : 1; // 0=无车, 1=有车
|
||||||
|
uint8_t misc_type : 2; // 0=时间量, 1=断开次数, 2=车流量
|
||||||
|
uint32_t freq; // 频率 (3 bytes, little-endian)
|
||||||
|
uint16_t variation; // 变化量 (2 bytes, little-endian)
|
||||||
|
union {
|
||||||
|
uint32_t passtime_ms5; // 通过时间/车间距, 5ms单位 (misc_type=0)
|
||||||
|
uint32_t cut_amount; // 线圈断开次数 (misc_type=1)
|
||||||
|
uint32_t flow_amount; // 车流量数 (misc_type=2)
|
||||||
|
} misc;
|
||||||
|
} LUP_CoilSensor;
|
||||||
|
|
||||||
|
/* 主动上报传感信息 (0xC0/0x0C) */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t sens_type; // 0x0C
|
||||||
|
uint8_t sub_amount; // 分包数量(高4位), 0=无分包
|
||||||
|
uint8_t sub_sequence; // 当前分包序号(低4位), 1-based
|
||||||
|
uint8_t coil_count; // 线圈数 (解析出的)
|
||||||
|
LUP_CoilSensor coils[LUP_COIL_COUNT];
|
||||||
|
} LUP_SensorReport;
|
||||||
|
|
||||||
|
/* 灵敏度读写 (0x8A) */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t rw; // 0=Read, 1=Write
|
||||||
|
uint8_t amount; // 灵敏度值数量
|
||||||
|
uint16_t sens_in[LUP_COIL_COUNT]; // 触发灵敏度
|
||||||
|
uint16_t sens_out[LUP_COIL_COUNT]; // 释放灵敏度
|
||||||
|
} LUP_Sensitivity;
|
||||||
|
|
||||||
|
/* 版本信息 (0x4A Response) */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t status; // Status byte
|
||||||
|
uint8_t hard_main;
|
||||||
|
uint8_t hard_sub;
|
||||||
|
uint8_t hard_ssub;
|
||||||
|
uint8_t soft_main;
|
||||||
|
uint8_t soft_sub;
|
||||||
|
uint8_t soft_ssub;
|
||||||
|
char version_str[32]; // 格式化字符串
|
||||||
|
} LUP_VersionInfo;
|
||||||
|
|
||||||
|
/* 命令状态机 */
|
||||||
|
typedef enum {
|
||||||
|
LUP_STATE_IDLE = 0, // 空闲
|
||||||
|
LUP_STATE_WAIT_RESPONSE, // 等待响应
|
||||||
|
LUP_STATE_RESPONSE_READY, // 响应已就绪
|
||||||
|
LUP_STATE_TIMEOUT // 超时
|
||||||
|
} LUP_CmdState;
|
||||||
|
|
||||||
|
/* 挂起的命令跟踪 */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t pending_cmd; // 当前等待响应的命令字节
|
||||||
|
uint32_t send_tick; // 发送时刻 (ms)
|
||||||
|
uint32_t timeout_ms; // 超时时间
|
||||||
|
LUP_CmdState state;
|
||||||
|
// 响应数据缓冲区
|
||||||
|
uint8_t resp_buf[LUP_MAX_PKG_LEN];
|
||||||
|
uint16_t resp_len;
|
||||||
|
} LUP_CmdTracker;
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Global State
|
||||||
|
*===========================================================================*/
|
||||||
|
extern LUP_CmdTracker g_lup_cmd;
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Public API
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
/* --- Checksum --- */
|
||||||
|
uint8_t lup_calc_xor(const uint8_t *data, uint16_t len);
|
||||||
|
uint8_t lup_calc_sum(const uint8_t *data, uint16_t len);
|
||||||
|
int lup_verify_checksum(const uint8_t *pkg, uint16_t len);
|
||||||
|
void lup_append_checksum(uint8_t *pkg);
|
||||||
|
|
||||||
|
/* --- Packet Builders (returns total packet length) --- */
|
||||||
|
uint16_t lup_build_get_version(uint8_t *buf);
|
||||||
|
uint16_t lup_build_reset(uint8_t *buf);
|
||||||
|
uint16_t lup_build_factory_init(uint8_t *buf);
|
||||||
|
uint16_t lup_build_sensitivity_read(uint8_t *buf);
|
||||||
|
uint16_t lup_build_sensitivity_write(uint8_t *buf, const LUP_Sensitivity *sens);
|
||||||
|
uint16_t lup_build_set_param(uint8_t *buf, const LUP_ParamSet *ps);
|
||||||
|
uint16_t lup_build_get_param(uint8_t *buf);
|
||||||
|
|
||||||
|
/* --- Packet Parsers (returns 0=success, <0=error) --- */
|
||||||
|
int lup_parse_version(const uint8_t *pkg, uint16_t len, LUP_VersionInfo *info);
|
||||||
|
int lup_parse_factory_init_resp(const uint8_t *pkg, uint16_t len, uint8_t *success);
|
||||||
|
int lup_parse_sensitivity_resp(const uint8_t *pkg, uint16_t len, LUP_Sensitivity *sens);
|
||||||
|
int lup_parse_param_get(const uint8_t *pkg, uint16_t len, LUP_ParamGet *pg);
|
||||||
|
int lup_parse_sensor_report(const uint8_t *pkg, uint16_t len, LUP_SensorReport *report);
|
||||||
|
int lup_parse_set_param_resp(const uint8_t *pkg, uint16_t len, uint8_t *success);
|
||||||
|
|
||||||
|
/* --- Command State Machine --- */
|
||||||
|
void lup_cmd_begin(uint8_t cmd, uint32_t timeout_ms);
|
||||||
|
void lup_cmd_done(void);
|
||||||
|
int lup_cmd_check_timeout(void);
|
||||||
|
void lup_cmd_on_response(const uint8_t *pkg, uint16_t len);
|
||||||
|
|
||||||
|
/* --- High-level Commands (send + wait handled by state machine) --- */
|
||||||
|
void lup_send_get_version(void);
|
||||||
|
void lup_send_reset(void);
|
||||||
|
void lup_send_factory_init(void);
|
||||||
|
void lup_send_sensitivity_read(void);
|
||||||
|
void lup_send_set_param(const LUP_ParamSet *ps);
|
||||||
|
void lup_send_get_param(void);
|
||||||
|
|
||||||
|
/* --- Mid-packet ACK for 0xC0 multi-packet --- */
|
||||||
|
uint16_t lup_build_sensor_ack(uint8_t *buf, uint8_t sens_type,
|
||||||
|
uint8_t seq, uint8_t sub_amount);
|
||||||
|
|
||||||
|
/* --- UART Frame Parser (called from USART2 ISR context) --- */
|
||||||
|
typedef enum {
|
||||||
|
LUP_FRAME_STATE_IDLE = 0, // 等待 0x7F
|
||||||
|
LUP_FRAME_STATE_HEADER, // 接收 Header (Addr, LEN, CMD)
|
||||||
|
LUP_FRAME_STATE_VALUE, // 接收 Value
|
||||||
|
LUP_FRAME_STATE_CHECK, // 接收 Checksum
|
||||||
|
LUP_FRAME_STATE_COMPLETE // 帧完成
|
||||||
|
} LUP_FrameState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
LUP_FrameState state;
|
||||||
|
uint8_t buf[LUP_MAX_PKG_LEN];
|
||||||
|
uint16_t idx; // 当前写入位置
|
||||||
|
uint16_t value_len; // 期望的 Value 长度 (从 LEN 字段解析)
|
||||||
|
uint16_t value_idx; // 当前已接收的 Value 字节数
|
||||||
|
} LUP_FrameParser;
|
||||||
|
|
||||||
|
extern LUP_FrameParser g_lup_parser;
|
||||||
|
|
||||||
|
/* 喂一个字节给帧解析器,返回 1 表示帧接收完成 */
|
||||||
|
int lup_feed_byte(uint8_t byte);
|
||||||
|
|
||||||
|
/* 取完整帧的指针和数据长度 */
|
||||||
|
const uint8_t *lup_frame_data(void);
|
||||||
|
uint16_t lup_frame_len(void);
|
||||||
|
|
||||||
|
/* 重置帧解析器 */
|
||||||
|
void lup_frame_reset(void);
|
||||||
|
|
||||||
|
/* --- Process a complete frame (call from main loop context) --- */
|
||||||
|
void lup_process_frame(const uint8_t *pkg, uint16_t len);
|
||||||
|
|
||||||
|
#endif /* __LOOP_UART_PROTO_H__ */
|
||||||
@@ -44,6 +44,17 @@ typedef enum {
|
|||||||
#define JSON_CODE_DATA_TOO_LONG 6
|
#define JSON_CODE_DATA_TOO_LONG 6
|
||||||
#define JSON_CODE_NOT_AUTHED 7
|
#define JSON_CODE_NOT_AUTHED 7
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Pending Loop MCU Command Response (deferred)
|
||||||
|
*===========================================================================*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t active; // 1 = pending response
|
||||||
|
uint8_t socket; // TCP socket to reply to
|
||||||
|
uint32_t msg_id; // Original msg_id
|
||||||
|
char cmd[32]; // Original command string
|
||||||
|
uint32_t deadline; // ms deadline for timeout
|
||||||
|
} TcpJsonPending;
|
||||||
|
|
||||||
/*===========================================================================
|
/*===========================================================================
|
||||||
* Externs
|
* Externs
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
@@ -51,6 +62,7 @@ extern uint8_t g_json_socket_listen; // TCP socket ID (listen + data)
|
|||||||
extern TcpJsonAuthState g_json_auth_state;
|
extern TcpJsonAuthState g_json_auth_state;
|
||||||
extern uint32_t g_json_auth_timer; // ms timer for auth timeout
|
extern uint32_t g_json_auth_timer; // ms timer for auth timeout
|
||||||
extern uint8_t g_json_pwd_retry; // password retry counter
|
extern uint8_t g_json_pwd_retry; // password retry counter
|
||||||
|
extern TcpJsonPending g_json_pending; // Pending Loop MCU command state
|
||||||
|
|
||||||
/*===========================================================================
|
/*===========================================================================
|
||||||
* API
|
* API
|
||||||
@@ -58,5 +70,6 @@ extern uint8_t g_json_pwd_retry; // password retry counter
|
|||||||
void tcp_json_srv_init(void); // Create listen socket on port 5960
|
void tcp_json_srv_init(void); // Create listen socket on port 5960
|
||||||
void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat); // Socket interrupt handler
|
void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat); // Socket interrupt handler
|
||||||
void tcp_json_poll(void); // Periodic poll (auth timeout, etc.)
|
void tcp_json_poll(void); // Periodic poll (auth timeout, etc.)
|
||||||
|
void tcp_json_push_sensor(void); // Push sensor data (0xC0) to TCP client
|
||||||
|
|
||||||
#endif /* __TCP_JSON_SRV_H__ */
|
#endif /* __TCP_JSON_SRV_H__ */
|
||||||
|
|||||||
679
vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c
Normal file
679
vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c
Normal file
@@ -0,0 +1,679 @@
|
|||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* @file loop_uart_proto.c
|
||||||
|
* @author wangfq
|
||||||
|
* @version V1.0
|
||||||
|
* @date 2026-07-02
|
||||||
|
* @brief DLD960Loop 串口通信协议实现
|
||||||
|
* - 校验计算 (XOR + SUM)
|
||||||
|
* - 命令组包 / 响应解析
|
||||||
|
* - 帧接收状态机 (替代 timeout heuristic)
|
||||||
|
* - 命令发送 + 超时跟踪
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
#include "CONFIG.h"
|
||||||
|
#include "loop_uart_proto.h"
|
||||||
|
#include "cmcng.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Global State
|
||||||
|
*===========================================================================*/
|
||||||
|
LUP_CmdTracker g_lup_cmd = {0, 0, 0, LUP_STATE_IDLE, {0}, 0};
|
||||||
|
|
||||||
|
/* --- Frame Parser --- */
|
||||||
|
LUP_FrameParser g_lup_parser = {
|
||||||
|
.state = LUP_FRAME_STATE_IDLE,
|
||||||
|
.idx = 0,
|
||||||
|
.value_len = 0,
|
||||||
|
.value_idx = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Checksum
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XOR 校验: 从 Addr 字节开始,到命令数据的最后一个字节结束
|
||||||
|
*/
|
||||||
|
uint8_t lup_calc_xor(const uint8_t *data, uint16_t len)
|
||||||
|
{
|
||||||
|
uint8_t x = 0;
|
||||||
|
uint16_t i;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
x ^= data[i];
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SUM 校验: 从 Addr 字节开始,到命令数据的最后一个字节结束
|
||||||
|
*/
|
||||||
|
uint8_t lup_calc_sum(const uint8_t *data, uint16_t len)
|
||||||
|
{
|
||||||
|
uint8_t s = 0;
|
||||||
|
uint16_t i;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
s += data[i];
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 校验整包: pkg = [7F] [Addr] [LEN] [CMD] [Data...] [XOR] [SUM]
|
||||||
|
* Checksum covers: Addr + LEN_field + Value (=CMD + Data) = 2 + LEN bytes
|
||||||
|
* starting at pkg[1]
|
||||||
|
*/
|
||||||
|
int lup_verify_checksum(const uint8_t *pkg, uint16_t len)
|
||||||
|
{
|
||||||
|
if (len < 6) return -1; // 最小: 7F + 3 header + 2 checksum = 6
|
||||||
|
|
||||||
|
uint8_t pkg_len = pkg[2]; // LEN field
|
||||||
|
uint16_t check_len = 2 + pkg_len; // Addr(1)+LEN_field(1)+Value(LEN)
|
||||||
|
|
||||||
|
if (len < check_len + 3) return -4; // Not enough data: Magic(1) + covered + Check(2)
|
||||||
|
|
||||||
|
uint8_t expect_xor = lup_calc_xor(pkg + 1, check_len);
|
||||||
|
uint8_t expect_sum = lup_calc_sum(pkg + 1, check_len);
|
||||||
|
|
||||||
|
uint8_t got_xor = pkg[1 + check_len];
|
||||||
|
uint8_t got_sum = pkg[2 + check_len];
|
||||||
|
|
||||||
|
if (expect_xor != got_xor) return -2;
|
||||||
|
if (expect_sum != got_sum) return -3;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 为已填充好 Data 的 buf 追加 checksum。
|
||||||
|
* buf 格式: [7F] [Addr] [LEN] [CMD] [Data...]
|
||||||
|
* Checksum 覆盖: Addr(1) + LEN(1) + Value(LEN) = 2 + LEN bytes
|
||||||
|
* Output: [7F][Addr][LEN][CMD][Data...][XOR][SUM]
|
||||||
|
*/
|
||||||
|
void lup_append_checksum(uint8_t *pkg)
|
||||||
|
{
|
||||||
|
// pkg[0]=7F, pkg[1]=Addr, pkg[2]=LEN, pkg[3]=CMD, ...
|
||||||
|
uint8_t total_len = 1 + 3 + pkg[2] + 2; // Magic(1) + Header(3) + Value(LEN) + Check(2)
|
||||||
|
uint16_t check_len = 2 + pkg[2]; // Addr(1) + LEN_field(1) + Value(LEN)
|
||||||
|
|
||||||
|
pkg[total_len - 2] = lup_calc_xor(pkg + 1, check_len);
|
||||||
|
pkg[total_len - 1] = lup_calc_sum(pkg + 1, check_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Packet Builders
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0x4A — 获取设备版本号
|
||||||
|
* Req: 7F 00 01 4A 4B 4B
|
||||||
|
*/
|
||||||
|
uint16_t lup_build_get_version(uint8_t *buf)
|
||||||
|
{
|
||||||
|
buf[0] = LUP_MAGIC; // 7F
|
||||||
|
buf[1] = LUP_ADDR_DEFAULT; // 0x00
|
||||||
|
buf[2] = 0x01; // LEN = 1 (CMD byte)
|
||||||
|
buf[3] = LUP_CMD_GET_VERSION; // 0x4A
|
||||||
|
lup_append_checksum(buf);
|
||||||
|
return 6; // Magic(1) + Header(3) + Check(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0x6D — 设备复位 (无回复)
|
||||||
|
* Req: 7F 00 01 6D 6C 6E
|
||||||
|
*/
|
||||||
|
uint16_t lup_build_reset(uint8_t *buf)
|
||||||
|
{
|
||||||
|
buf[0] = LUP_MAGIC;
|
||||||
|
buf[1] = LUP_ADDR_DEFAULT;
|
||||||
|
buf[2] = 0x01;
|
||||||
|
buf[3] = LUP_CMD_RESET;
|
||||||
|
lup_append_checksum(buf);
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0x92 — 出厂初始化
|
||||||
|
* Req: 7F 00 01 92 93 93
|
||||||
|
*/
|
||||||
|
uint16_t lup_build_factory_init(uint8_t *buf)
|
||||||
|
{
|
||||||
|
buf[0] = LUP_MAGIC;
|
||||||
|
buf[1] = LUP_ADDR_DEFAULT;
|
||||||
|
buf[2] = 0x01;
|
||||||
|
buf[3] = LUP_CMD_FACTORY_INIT;
|
||||||
|
lup_append_checksum(buf);
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0x8A — 读灵敏度列表
|
||||||
|
* Req: 7F 00 04 8A 00 00 ...
|
||||||
|
*/
|
||||||
|
uint16_t lup_build_sensitivity_read(uint8_t *buf)
|
||||||
|
{
|
||||||
|
buf[0] = LUP_MAGIC;
|
||||||
|
buf[1] = LUP_ADDR_DEFAULT;
|
||||||
|
buf[2] = 0x04; // LEN = 4 (CMD + R/W + Amount + reserved?)
|
||||||
|
buf[3] = LUP_CMD_SENSITIVITY; // 0x8A
|
||||||
|
// Value: R/W + Amount (2 bytes, all zeros for read)
|
||||||
|
// 协议说 R/W: 1 byte, Amount: 1 byte, 后面是 Amount * (SensityIn + SensityOut)
|
||||||
|
// 对于 Read: R/W=0, Amount 忽略
|
||||||
|
buf[4] = LUP_SENS_RW_READ;
|
||||||
|
buf[5] = 0x00; // Amount (ignored for read)
|
||||||
|
buf[6] = 0x00; // padding?
|
||||||
|
buf[7] = 0x00; // padding?
|
||||||
|
lup_append_checksum(buf);
|
||||||
|
return 10; // Magic(1)+Header(3)+Value(4)+Check(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0x8A — 写灵敏度列表
|
||||||
|
*/
|
||||||
|
uint16_t lup_build_sensitivity_write(uint8_t *buf, const LUP_Sensitivity *sens)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
uint16_t idx = 0;
|
||||||
|
|
||||||
|
buf[idx++] = LUP_MAGIC;
|
||||||
|
buf[idx++] = LUP_ADDR_DEFAULT;
|
||||||
|
// LEN field: 2 + 2*sens->amount
|
||||||
|
uint8_t val_len = 2 + 2 * sens->amount;
|
||||||
|
buf[idx++] = val_len;
|
||||||
|
buf[idx++] = LUP_CMD_SENSITIVITY;
|
||||||
|
|
||||||
|
buf[idx++] = sens->rw; // 0x01 (Write)
|
||||||
|
buf[idx++] = sens->amount;
|
||||||
|
|
||||||
|
for (i = 0; i < sens->amount; i++) {
|
||||||
|
buf[idx++] = (uint8_t)(sens->sens_in[i] & 0xFF);
|
||||||
|
buf[idx++] = (uint8_t)((sens->sens_in[i] >> 8) & 0xFF);
|
||||||
|
buf[idx++] = (uint8_t)(sens->sens_out[i] & 0xFF);
|
||||||
|
buf[idx++] = (uint8_t)((sens->sens_out[i] >> 8) & 0xFF);
|
||||||
|
}
|
||||||
|
lup_append_checksum(buf);
|
||||||
|
return idx + 2; // +2 for checksum bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0x63 — 设置多路参数
|
||||||
|
* Value: AutoMode(1) + Amount(1) + Amount*(Param[5])
|
||||||
|
*/
|
||||||
|
uint16_t lup_build_set_param(uint8_t *buf, const LUP_ParamSet *ps)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
uint16_t idx = 0;
|
||||||
|
|
||||||
|
buf[idx++] = LUP_MAGIC;
|
||||||
|
buf[idx++] = LUP_ADDR_DEFAULT;
|
||||||
|
// LEN = 2 + 5 * Amount
|
||||||
|
uint8_t val_len = 2 + 5 * ps->amount;
|
||||||
|
buf[idx++] = val_len;
|
||||||
|
buf[idx++] = LUP_CMD_SET_PARAM;
|
||||||
|
|
||||||
|
buf[idx++] = ps->auto_mode;
|
||||||
|
buf[idx++] = ps->amount;
|
||||||
|
|
||||||
|
for (i = 0; i < ps->amount; i++) {
|
||||||
|
buf[idx++] = ps->params[i].sensitivity;
|
||||||
|
buf[idx++] = ps->params[i].loop_delay;
|
||||||
|
buf[idx++] = ps->params[i].output_mode;
|
||||||
|
buf[idx++] = ps->params[i].exist_mode;
|
||||||
|
buf[idx++] = ps->params[i].direction_mode;
|
||||||
|
}
|
||||||
|
lup_append_checksum(buf);
|
||||||
|
return idx + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0x64 — 读取多路参数
|
||||||
|
* Req: 7F 00 01 64 ...
|
||||||
|
*/
|
||||||
|
uint16_t lup_build_get_param(uint8_t *buf)
|
||||||
|
{
|
||||||
|
buf[0] = LUP_MAGIC;
|
||||||
|
buf[1] = LUP_ADDR_DEFAULT;
|
||||||
|
buf[2] = 0x01; // LEN = 1
|
||||||
|
buf[3] = LUP_CMD_GET_PARAM; // 0x64
|
||||||
|
lup_append_checksum(buf);
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0xC0 mid-packet ACK
|
||||||
|
* Req: 7F | 00 | 07 | C0 | SensType | Seq | SubAmount | XOR | SUM
|
||||||
|
*/
|
||||||
|
uint16_t lup_build_sensor_ack(uint8_t *buf, uint8_t sens_type,
|
||||||
|
uint8_t seq, uint8_t sub_amount)
|
||||||
|
{
|
||||||
|
buf[0] = LUP_MAGIC;
|
||||||
|
buf[1] = LUP_ADDR_DEFAULT;
|
||||||
|
buf[2] = 0x07; // LEN = 7 (CMD + SensType + Seq + SubAmount)
|
||||||
|
buf[3] = LUP_CMD_SENSOR_REPORT;
|
||||||
|
buf[4] = sens_type;
|
||||||
|
buf[5] = seq;
|
||||||
|
buf[6] = sub_amount;
|
||||||
|
buf[7] = 0x00; // padding
|
||||||
|
buf[8] = 0x00;
|
||||||
|
buf[9] = 0x00;
|
||||||
|
buf[10] = 0x00;
|
||||||
|
lup_append_checksum(buf);
|
||||||
|
return 13; // Magic(1)+Header(3)+Value(7)+Check(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Packet Parsers
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse 0x4A Response:
|
||||||
|
* Header: 00 08 4A (Addr, LEN=8, CMD)
|
||||||
|
* Value: Status + Hard_Main + Hard_Sub + Hard_SSub + Soft_Main + Soft_Sub + Soft_SSub
|
||||||
|
*/
|
||||||
|
int lup_parse_version(const uint8_t *pkg, uint16_t len, LUP_VersionInfo *info)
|
||||||
|
{
|
||||||
|
if (len < 10) return -1; // 1+3+7+2 = 13 min
|
||||||
|
|
||||||
|
// pkg layout: [7F][Addr=0][LEN=8][CMD=0x4A][Status...7bytes][XOR][SUM]
|
||||||
|
if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_GET_VERSION) return -2;
|
||||||
|
if (pkg[2] < 7) return -3; // LEN should be >= 7
|
||||||
|
|
||||||
|
const uint8_t *val = pkg + 4; // skip magic + header(3)
|
||||||
|
info->status = val[0];
|
||||||
|
info->hard_main = val[1];
|
||||||
|
info->hard_sub = val[2];
|
||||||
|
info->hard_ssub = val[3];
|
||||||
|
info->soft_main = val[4];
|
||||||
|
info->soft_sub = val[5];
|
||||||
|
info->soft_ssub = val[6];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse 0x92 Response:
|
||||||
|
* Value: 00/01 (00=success)
|
||||||
|
*/
|
||||||
|
int lup_parse_factory_init_resp(const uint8_t *pkg, uint16_t len, uint8_t *success)
|
||||||
|
{
|
||||||
|
if (len < 7) return -1;
|
||||||
|
if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_FACTORY_INIT) return -2;
|
||||||
|
*success = (pkg[4] == 0x00) ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse 0x8A Response:
|
||||||
|
* Value: Amount (1byte) + Amount * SensityValue (2bytes each)
|
||||||
|
*/
|
||||||
|
int lup_parse_sensitivity_resp(const uint8_t *pkg, uint16_t len, LUP_Sensitivity *sens)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
if (len < 6) return -1;
|
||||||
|
if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_SENSITIVITY) return -2;
|
||||||
|
|
||||||
|
const uint8_t *val = pkg + 4;
|
||||||
|
uint8_t amount = val[0];
|
||||||
|
|
||||||
|
if (amount > LUP_COIL_COUNT) amount = LUP_COIL_COUNT;
|
||||||
|
sens->amount = amount;
|
||||||
|
sens->rw = 0; // response, not request
|
||||||
|
|
||||||
|
for (i = 0; i < amount; i++) {
|
||||||
|
uint16_t sv = val[1 + i * 2] | ((uint16_t)val[2 + i * 2] << 8);
|
||||||
|
// Response returns unified SensityValue (not split in/out)
|
||||||
|
sens->sens_in[i] = sv;
|
||||||
|
sens->sens_out[i] = sv;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse 0x64 Response:
|
||||||
|
* Value: AutoMode(1) + Amount(1) + Amount * [Sensitivity, Loop_Delay, Output_Mode,
|
||||||
|
* Exist_Mode, Direction_Mode, freq1(2), freq2(2), freq3(2)]
|
||||||
|
* Each coil = 8 bytes
|
||||||
|
*/
|
||||||
|
int lup_parse_param_get(const uint8_t *pkg, uint16_t len, LUP_ParamGet *pg)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
if (len < 6) return -1;
|
||||||
|
if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_GET_PARAM) return -2;
|
||||||
|
|
||||||
|
const uint8_t *val = pkg + 4;
|
||||||
|
pg->auto_mode = val[0];
|
||||||
|
pg->amount = val[1];
|
||||||
|
|
||||||
|
if (pg->amount > LUP_COIL_COUNT) pg->amount = LUP_COIL_COUNT;
|
||||||
|
|
||||||
|
for (i = 0; i < pg->amount; i++) {
|
||||||
|
const uint8_t *c = val + 2 + i * 8;
|
||||||
|
pg->params[i].sensitivity = c[0];
|
||||||
|
pg->params[i].loop_delay = c[1];
|
||||||
|
pg->params[i].output_mode = c[2];
|
||||||
|
pg->params[i].exist_mode = c[3];
|
||||||
|
pg->params[i].direction_mode = c[4];
|
||||||
|
pg->freq[i][0] = c[5] | ((uint32_t)c[6] << 8);
|
||||||
|
pg->freq[i][1] = c[7] | ((uint32_t)c[8] << 8);
|
||||||
|
pg->freq[i][2] = c[9] | ((uint32_t)c[10] << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse 0x63 Response (Set Param):
|
||||||
|
* Value: Status(1 byte, 0x00=success)
|
||||||
|
*/
|
||||||
|
int lup_parse_set_param_resp(const uint8_t *pkg, uint16_t len, uint8_t *success)
|
||||||
|
{
|
||||||
|
if (len < 7) return -1;
|
||||||
|
if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_SET_PARAM) return -2;
|
||||||
|
*success = (pkg[4] == 0x00) ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse 0xC0/0x0C — 多路线圈传感信息
|
||||||
|
*
|
||||||
|
* 每路线圈数据单元 = 11 bytes:
|
||||||
|
* [0] 配置1: freq_level(2) | Direction(1) | freq_type(1) | sens(4)
|
||||||
|
* [1] 评估条件: condition(4) | loop_state(1) | car_state(1) | misc_type(2)
|
||||||
|
* [2-4] 频率 (3 bytes, LE)
|
||||||
|
* [5-6] 变化量 (2 bytes, LE)
|
||||||
|
* [7-10] 杂项 (4 bytes)
|
||||||
|
*/
|
||||||
|
int lup_parse_sensor_report(const uint8_t *pkg, uint16_t len, LUP_SensorReport *report)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
if (len < 9) return -1; // 7F + 3 header + 2 sensType/SubPkgFlag + 2 checksum min
|
||||||
|
if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_SENSOR_REPORT) return -2;
|
||||||
|
|
||||||
|
uint8_t sens_type = pkg[4];
|
||||||
|
if (sens_type != LUP_SENS_TYPE_MULTI_COIL) return -3;
|
||||||
|
|
||||||
|
uint8_t sub_pkg = pkg[5];
|
||||||
|
report->sens_type = sens_type;
|
||||||
|
report->sub_amount = (sub_pkg >> 4) & 0x0F;
|
||||||
|
report->sub_sequence = sub_pkg & 0x0F;
|
||||||
|
|
||||||
|
// 计算线圈数: Value = sensType(1) + SubPkgFlag(1) + coil_count * 11
|
||||||
|
uint16_t val_len = pkg[2]; // LEN field
|
||||||
|
uint16_t data_offset = 6; // 跳过 7F + Addr + LEN + CMD + SensType + SubPkgFlag
|
||||||
|
uint16_t data_len = val_len - 2; // 减去 SensType + SubPkgFlag
|
||||||
|
|
||||||
|
uint8_t coil_count = data_len / 11;
|
||||||
|
if (coil_count > LUP_COIL_COUNT) coil_count = LUP_COIL_COUNT;
|
||||||
|
report->coil_count = coil_count;
|
||||||
|
|
||||||
|
for (i = 0; i < coil_count; i++) {
|
||||||
|
const uint8_t *c = pkg + data_offset + i * 11;
|
||||||
|
|
||||||
|
LUP_CoilSensor *cs = &report->coils[i];
|
||||||
|
cs->freq_level = (c[0] >> 6) & 0x03;
|
||||||
|
cs->direction = (c[0] >> 5) & 0x01;
|
||||||
|
cs->freq_type = (c[0] >> 4) & 0x01;
|
||||||
|
cs->sensitivity = c[0] & 0x0F;
|
||||||
|
|
||||||
|
cs->condition = (c[1] >> 4) & 0x0F;
|
||||||
|
cs->loop_state = (c[1] >> 3) & 0x01;
|
||||||
|
cs->car_state = (c[1] >> 2) & 0x01;
|
||||||
|
cs->misc_type = c[1] & 0x03;
|
||||||
|
|
||||||
|
cs->freq = c[2] | ((uint32_t)c[3] << 8) | ((uint32_t)c[4] << 16);
|
||||||
|
cs->variation = c[5] | ((uint16_t)c[6] << 8);
|
||||||
|
cs->misc.passtime_ms5 = c[7] | ((uint32_t)c[8] << 8)
|
||||||
|
| ((uint32_t)c[9] << 16) | ((uint32_t)c[10] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Command State Machine
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
void lup_cmd_begin(uint8_t cmd, uint32_t timeout_ms)
|
||||||
|
{
|
||||||
|
g_lup_cmd.pending_cmd = cmd;
|
||||||
|
g_lup_cmd.send_tick = mstick();
|
||||||
|
g_lup_cmd.timeout_ms = timeout_ms;
|
||||||
|
g_lup_cmd.state = LUP_STATE_WAIT_RESPONSE;
|
||||||
|
g_lup_cmd.resp_len = 0;
|
||||||
|
memset(g_lup_cmd.resp_buf, 0, sizeof(g_lup_cmd.resp_buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void lup_cmd_done(void)
|
||||||
|
{
|
||||||
|
g_lup_cmd.pending_cmd = 0;
|
||||||
|
g_lup_cmd.state = LUP_STATE_IDLE;
|
||||||
|
g_lup_cmd.resp_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lup_cmd_check_timeout(void)
|
||||||
|
{
|
||||||
|
if (g_lup_cmd.state != LUP_STATE_WAIT_RESPONSE) return 0;
|
||||||
|
if (mstick() - g_lup_cmd.send_tick > g_lup_cmd.timeout_ms) {
|
||||||
|
g_lup_cmd.state = LUP_STATE_TIMEOUT;
|
||||||
|
PRINT("LUP: cmd 0x%02X timeout\n", g_lup_cmd.pending_cmd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 收到响应时调用 — 如果当前有挂起命令且 CMD 匹配,则保存响应
|
||||||
|
*/
|
||||||
|
void lup_cmd_on_response(const uint8_t *pkg, uint16_t len)
|
||||||
|
{
|
||||||
|
if (g_lup_cmd.state != LUP_STATE_WAIT_RESPONSE) return;
|
||||||
|
if (len < 4) return;
|
||||||
|
|
||||||
|
uint8_t resp_cmd = pkg[3];
|
||||||
|
if (resp_cmd == g_lup_cmd.pending_cmd) {
|
||||||
|
// 保存响应数据
|
||||||
|
uint16_t copy_len = (len < sizeof(g_lup_cmd.resp_buf)) ? len : sizeof(g_lup_cmd.resp_buf);
|
||||||
|
memcpy(g_lup_cmd.resp_buf, pkg, copy_len);
|
||||||
|
g_lup_cmd.resp_len = copy_len;
|
||||||
|
g_lup_cmd.state = LUP_STATE_RESPONSE_READY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* High-level Send Commands
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
void lup_send_get_version(void)
|
||||||
|
{
|
||||||
|
uint8_t buf[LUP_MAX_PKG_LEN];
|
||||||
|
uint16_t len = lup_build_get_version(buf);
|
||||||
|
UART2_SendString(buf, len);
|
||||||
|
lup_cmd_begin(LUP_CMD_GET_VERSION, 200); // 200ms timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
void lup_send_reset(void)
|
||||||
|
{
|
||||||
|
uint8_t buf[LUP_MAX_PKG_LEN];
|
||||||
|
uint16_t len = lup_build_reset(buf);
|
||||||
|
UART2_SendString(buf, len);
|
||||||
|
// No response expected
|
||||||
|
}
|
||||||
|
|
||||||
|
void lup_send_factory_init(void)
|
||||||
|
{
|
||||||
|
uint8_t buf[LUP_MAX_PKG_LEN];
|
||||||
|
uint16_t len = lup_build_factory_init(buf);
|
||||||
|
UART2_SendString(buf, len);
|
||||||
|
lup_cmd_begin(LUP_CMD_FACTORY_INIT, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lup_send_sensitivity_read(void)
|
||||||
|
{
|
||||||
|
uint8_t buf[LUP_MAX_PKG_LEN];
|
||||||
|
uint16_t len = lup_build_sensitivity_read(buf);
|
||||||
|
UART2_SendString(buf, len);
|
||||||
|
lup_cmd_begin(LUP_CMD_SENSITIVITY, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lup_send_set_param(const LUP_ParamSet *ps)
|
||||||
|
{
|
||||||
|
uint8_t buf[LUP_MAX_PKG_LEN];
|
||||||
|
uint16_t len = lup_build_set_param(buf, ps);
|
||||||
|
UART2_SendString(buf, len);
|
||||||
|
lup_cmd_begin(LUP_CMD_SET_PARAM, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lup_send_get_param(void)
|
||||||
|
{
|
||||||
|
uint8_t buf[LUP_MAX_PKG_LEN];
|
||||||
|
uint16_t len = lup_build_get_param(buf);
|
||||||
|
UART2_SendString(buf, len);
|
||||||
|
lup_cmd_begin(LUP_CMD_GET_PARAM, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Frame Parser (called per-byte from USART2 ISR / timer context)
|
||||||
|
*
|
||||||
|
* 帧格式: [7F] [Addr] [LEN] [CMD] [Value: LEN bytes] [XOR] [SUM]
|
||||||
|
* LEN = Value 字段的长度,包括 CMD byte
|
||||||
|
* 所以: Value data bytes = LEN - 1 (减掉 CMD)
|
||||||
|
*
|
||||||
|
* 状态机:
|
||||||
|
* IDLE → 找 0x7F → HEADER
|
||||||
|
* HEADER → 收完 3 bytes Header → VALUE (若 LEN>1)
|
||||||
|
* VALUE → 收完 data_bytes → CHECK
|
||||||
|
* CHECK → 收完 2 bytes → COMPLETE
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
void lup_frame_reset(void)
|
||||||
|
{
|
||||||
|
g_lup_parser.state = LUP_FRAME_STATE_IDLE;
|
||||||
|
g_lup_parser.idx = 0;
|
||||||
|
g_lup_parser.value_len = 0;
|
||||||
|
g_lup_parser.value_idx = 0;
|
||||||
|
memset(g_lup_parser.buf, 0, sizeof(g_lup_parser.buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t *lup_frame_data(void)
|
||||||
|
{
|
||||||
|
return g_lup_parser.buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t lup_frame_len(void)
|
||||||
|
{
|
||||||
|
return g_lup_parser.idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 喂一个字节。返回 1 = 帧接收完成(在 buf 中)
|
||||||
|
*/
|
||||||
|
int lup_feed_byte(uint8_t byte)
|
||||||
|
{
|
||||||
|
LUP_FrameParser *p = &g_lup_parser;
|
||||||
|
|
||||||
|
switch (p->state) {
|
||||||
|
case LUP_FRAME_STATE_IDLE:
|
||||||
|
if (byte == LUP_MAGIC) {
|
||||||
|
p->buf[0] = byte;
|
||||||
|
p->idx = 1;
|
||||||
|
p->state = LUP_FRAME_STATE_HEADER;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_HEADER:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
if (p->idx == 4) {
|
||||||
|
// Header 收完: [7F][Addr][LEN][CMD]
|
||||||
|
// Value data bytes = LEN - 1 (CMD byte 已计入 LEN)
|
||||||
|
uint8_t len_field = p->buf[2];
|
||||||
|
uint16_t data_bytes; // Value 中除 CMD 之外的 data 字节数
|
||||||
|
if (len_field < 1) {
|
||||||
|
data_bytes = 0;
|
||||||
|
} else {
|
||||||
|
data_bytes = len_field - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_bytes > LUP_MAX_VALUE_LEN) {
|
||||||
|
// 非法长度,丢弃
|
||||||
|
lup_frame_reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_bytes > 0) {
|
||||||
|
p->value_len = data_bytes;
|
||||||
|
p->value_idx = 0;
|
||||||
|
p->state = LUP_FRAME_STATE_VALUE;
|
||||||
|
} else {
|
||||||
|
// 无 Data,直接等 Checksum
|
||||||
|
p->value_len = 0;
|
||||||
|
p->value_idx = 0;
|
||||||
|
p->state = LUP_FRAME_STATE_CHECK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_VALUE:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
p->value_idx++;
|
||||||
|
if (p->value_idx >= p->value_len) {
|
||||||
|
p->state = LUP_FRAME_STATE_CHECK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_CHECK:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
if (p->idx >= p->value_len + 1 + 3 + 2) { // Magic(1) + Header(3) + Value data + Check(2)
|
||||||
|
p->state = LUP_FRAME_STATE_COMPLETE;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_COMPLETE:
|
||||||
|
// 不应该到达这里,调用方应在 COMPLETE 后 reset
|
||||||
|
lup_frame_reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 收到完整帧后的处理:
|
||||||
|
* 1. 校验 checksum
|
||||||
|
* 2. 如果是当前命令的响应 → 通知状态机
|
||||||
|
* 3. 如果是主动上报 (0xC0) → 处理传感器数据
|
||||||
|
*/
|
||||||
|
void lup_process_frame(const uint8_t *pkg, uint16_t len)
|
||||||
|
{
|
||||||
|
int csr;
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
if (len < 6) return;
|
||||||
|
|
||||||
|
// Debug: print raw
|
||||||
|
PRINT("LUP Rx:");
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
PRINT(" %02X", pkg[i]);
|
||||||
|
}
|
||||||
|
PRINT("\n");
|
||||||
|
|
||||||
|
// --- Checksum ---
|
||||||
|
csr = lup_verify_checksum(pkg, len);
|
||||||
|
if (csr != 0) {
|
||||||
|
PRINT("LUP: checksum fail (%d)\n", csr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Dispatch by CMD ---
|
||||||
|
uint8_t cmd = pkg[3];
|
||||||
|
|
||||||
|
// Active reports (0xC0) are NOT responses to commands
|
||||||
|
if (cmd == LUP_CMD_SENSOR_REPORT) {
|
||||||
|
// Sensor report — will be handled in upper layer (uart_srv + TCP JSON)
|
||||||
|
// Don't consume as command response
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try matching with pending command
|
||||||
|
lup_cmd_on_response(pkg, len);
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "net_srv.h"
|
#include "net_srv.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "tcp_json_srv.h"
|
#include "tcp_json_srv.h"
|
||||||
|
#include "loop_uart_proto.h"
|
||||||
|
|
||||||
/*********************************************************************
|
/*********************************************************************
|
||||||
* GLOBAL TYPEDEFS
|
* GLOBAL TYPEDEFS
|
||||||
@@ -269,6 +270,8 @@ void Main_Circulation(void)
|
|||||||
|
|
||||||
poll_dbn_ble();
|
poll_dbn_ble();
|
||||||
|
|
||||||
|
tcp_json_push_sensor(); // Push 0xC0 sensor data to TCP JSON client
|
||||||
|
|
||||||
tcp_json_poll();
|
tcp_json_poll();
|
||||||
|
|
||||||
key_event_srv();
|
key_event_srv();
|
||||||
@@ -335,6 +338,13 @@ void TIM3_IRQHandler(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Safety: reset frame parser if stuck in non-IDLE state too long
|
||||||
|
if(g_lup_parser.state != LUP_FRAME_STATE_IDLE){
|
||||||
|
if(g_pkg_uart_2.tick > 20){
|
||||||
|
lup_frame_reset();
|
||||||
|
g_pkg_uart_2.tick = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0){//HAL_PUSH_BUTTON()){
|
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0){//HAL_PUSH_BUTTON()){
|
||||||
g_flag_counter_key.tick++;
|
g_flag_counter_key.tick++;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "wchnet.h"
|
#include "wchnet.h"
|
||||||
#include "net_srv.h"
|
#include "net_srv.h"
|
||||||
#include "cmcng.h"
|
#include "cmcng.h"
|
||||||
|
#include "loop_uart_proto.h"
|
||||||
#include "simple_json.h"
|
#include "simple_json.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -29,6 +30,7 @@ TcpJsonAuthState g_json_auth_state = JSON_STATE_WAIT_AUTH;
|
|||||||
uint32_t g_json_auth_timer = 0;
|
uint32_t g_json_auth_timer = 0;
|
||||||
uint8_t g_json_pwd_retry = 0;
|
uint8_t g_json_pwd_retry = 0;
|
||||||
uint32_t g_json_lockout_timer = 0;
|
uint32_t g_json_lockout_timer = 0;
|
||||||
|
TcpJsonPending g_json_pending = {0, 0, 0, "", 0};
|
||||||
|
|
||||||
/*===========================================================================
|
/*===========================================================================
|
||||||
* Frame Receive Buffer (line-delimited JSON)
|
* Frame Receive Buffer (line-delimited JSON)
|
||||||
@@ -464,46 +466,87 @@ static void handle_device_reset(uint8_t socket, uint32_t msg_id, const char *jso
|
|||||||
NVIC_SystemReset();
|
NVIC_SystemReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4.13 loop_param_set */
|
/* 4.13 loop_param_set — 设置地感线圈多路参数 (CMD 0x63) */
|
||||||
static void handle_loop_param_set(uint8_t socket, uint32_t msg_id, const char *json) {
|
static void handle_loop_param_set(uint8_t socket, uint32_t msg_id, const char *json) {
|
||||||
// Forward to Loop MCU via UART2
|
|
||||||
// Store params locally for query response
|
|
||||||
char *data = json_get_data_str(json);
|
char *data = json_get_data_str(json);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
json_send_error(socket, msg_id, "loop_param_set", JSON_CODE_PARAM_ERR, "missing data");
|
json_send_error(socket, msg_id, "loop_param_set", JSON_CODE_PARAM_ERR, "missing data");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LUP_ParamSet ps;
|
||||||
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
|
||||||
|
ps.auto_mode = (uint8_t)json_get_uint_field(data, "\"auto_mode\"");
|
||||||
|
|
||||||
|
// Parse channels array
|
||||||
|
// Use a simple approach: parse top-level fields for each known channel key
|
||||||
|
uint32_t amount = json_get_uint_field(data, "\"amount\"");
|
||||||
|
if (amount == 0 || amount > LUP_COIL_COUNT) {
|
||||||
|
amount = LUP_COIL_COUNT;
|
||||||
|
}
|
||||||
|
ps.amount = (uint8_t)amount;
|
||||||
|
|
||||||
|
// Parse per-channel data
|
||||||
|
char ch_key[16];
|
||||||
|
uint8_t ch;
|
||||||
|
for (ch = 0; ch < ps.amount; ch++) {
|
||||||
|
snprintf(ch_key, sizeof(ch_key), "\"ch%d\"", ch + 1);
|
||||||
|
|
||||||
|
char ch_data[512] = {0};
|
||||||
|
reset_tmp();
|
||||||
|
simple_parse_json(data, ch_key, g_tmp_value);
|
||||||
|
if (strlen(g_tmp_value) == 0) continue;
|
||||||
|
strncpy(ch_data, g_tmp_value, sizeof(ch_data) - 1);
|
||||||
|
|
||||||
|
LUP_CoilParam *cp = &ps.params[ch];
|
||||||
|
|
||||||
|
// sensitivity: 低4位=灵敏度, 高4位=freq_level
|
||||||
|
uint32_t sens_val = json_get_uint_field(ch_data, "\"sensitivity\"");
|
||||||
|
uint32_t freq_lev = json_get_uint_field(ch_data, "\"freq_level\""); // 0~3
|
||||||
|
cp->sensitivity = ((uint8_t)(freq_lev & 0x03) << 4) | (uint8_t)(sens_val & 0x0F);
|
||||||
|
|
||||||
|
cp->loop_delay = (uint8_t)json_get_uint_field(ch_data, "\"loop_delay\"");
|
||||||
|
|
||||||
|
// output_mode: 低3位=输出方式, 高5位=SafeMode
|
||||||
|
uint32_t out_mode = json_get_uint_field(ch_data, "\"output_mode\"");
|
||||||
|
uint32_t safe_mode = json_get_uint_field(ch_data, "\"safe_mode\"");
|
||||||
|
cp->output_mode = ((uint8_t)(safe_mode & 0x1F) << 3) | (uint8_t)(out_mode & 0x07);
|
||||||
|
|
||||||
|
cp->exist_mode = (uint8_t)json_get_uint_field(ch_data, "\"exist_mode\"");
|
||||||
|
|
||||||
|
// direction_mode: 低3位=方向模式, 高4位=Function_Mode
|
||||||
|
uint32_t dir_mode = json_get_uint_field(ch_data, "\"direction_mode\"");
|
||||||
|
uint32_t func_mode = json_get_uint_field(ch_data, "\"function_mode\"");
|
||||||
|
cp->direction_mode = ((uint8_t)(func_mode & 0x0F) << 4) | (uint8_t)(dir_mode & 0x07);
|
||||||
|
}
|
||||||
free(data);
|
free(data);
|
||||||
|
|
||||||
// Build binary packet for Loop MCU (protocol 0x63 = CMD_SET_MCJQ_PARAM)
|
// Send to Loop MCU and defer response
|
||||||
// For now, ack the command — full loop MCU integration deferred
|
lup_send_set_param(&ps);
|
||||||
json_send_ok(socket, msg_id, "loop_param_set", NULL);
|
|
||||||
PRINT("JSON: loop_param_set (Loop MCU relay pending)\n");
|
// Save pending state for async response
|
||||||
|
g_json_pending.active = 1;
|
||||||
|
g_json_pending.socket = socket;
|
||||||
|
g_json_pending.msg_id = msg_id;
|
||||||
|
strncpy(g_json_pending.cmd, "loop_param_set", sizeof(g_json_pending.cmd) - 1);
|
||||||
|
g_json_pending.deadline = mstick() + 500;
|
||||||
|
|
||||||
|
PRINT("JSON: loop_param_set sent (ch=%d)\n", ps.amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4.14 loop_param_query */
|
/* 4.14 loop_param_query — 读取地感线圈多路参数 (CMD 0x64) */
|
||||||
static void handle_loop_param_query(uint8_t socket, uint32_t msg_id, const char *json) {
|
static void handle_loop_param_query(uint8_t socket, uint32_t msg_id, const char *json) {
|
||||||
// Query Loop MCU via UART2 (protocol 0x64 = CMD_GET_MCJQ_PARAM)
|
// Send query to Loop MCU and defer response
|
||||||
// For now, return stub data — full loop MCU integration deferred
|
lup_send_get_param();
|
||||||
char data_json[600];
|
|
||||||
snprintf(data_json, sizeof(data_json),
|
|
||||||
"{\"auto_mode\":false,"
|
|
||||||
"\"channels\":["
|
|
||||||
"{\"ch\":1,\"sensitivity\":7,\"freq_level\":\"high\",\"loop_delay\":0,"
|
|
||||||
"\"output_mode\":\"exist\",\"exist_mode\":0,\"direction_mode\":0,"
|
|
||||||
"\"safe_mode\":0,\"function_mode\":0,\"freq_initial\":0,\"freq_current\":0,\"freq_diff\":0},"
|
|
||||||
"{\"ch\":2,\"sensitivity\":7,\"freq_level\":\"mid_high\",\"loop_delay\":5,"
|
|
||||||
"\"output_mode\":\"exist\",\"exist_mode\":0,\"direction_mode\":0,"
|
|
||||||
"\"safe_mode\":0,\"function_mode\":0,\"freq_initial\":0,\"freq_current\":0,\"freq_diff\":0},"
|
|
||||||
"{\"ch\":3,\"sensitivity\":5,\"freq_level\":\"mid_low\",\"loop_delay\":0,"
|
|
||||||
"\"output_mode\":\"exist\",\"exist_mode\":0,\"direction_mode\":0,"
|
|
||||||
"\"safe_mode\":0,\"function_mode\":0,\"freq_initial\":0,\"freq_current\":0,\"freq_diff\":0},"
|
|
||||||
"{\"ch\":4,\"sensitivity\":8,\"freq_level\":\"low\",\"loop_delay\":0,"
|
|
||||||
"\"output_mode\":\"exist\",\"exist_mode\":0,\"direction_mode\":0,"
|
|
||||||
"\"safe_mode\":0,\"function_mode\":0,\"freq_initial\":0,\"freq_current\":0,\"freq_diff\":0}]}");
|
|
||||||
|
|
||||||
json_send_ok(socket, msg_id, "loop_param_query", data_json);
|
g_json_pending.active = 1;
|
||||||
PRINT("JSON: loop_param_query (stub — Loop MCU relay pending)\n");
|
g_json_pending.socket = socket;
|
||||||
|
g_json_pending.msg_id = msg_id;
|
||||||
|
strncpy(g_json_pending.cmd, "loop_param_query", sizeof(g_json_pending.cmd) - 1);
|
||||||
|
g_json_pending.deadline = mstick() + 500;
|
||||||
|
|
||||||
|
PRINT("JSON: loop_param_query sent\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4.15 report_config */
|
/* 4.15 report_config */
|
||||||
@@ -679,6 +722,169 @@ void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Deferred Response Helpers
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Format coil param query response as JSON
|
||||||
|
*/
|
||||||
|
static int format_loop_param_json(char *buf, uint16_t buf_size, const LUP_ParamGet *pg) {
|
||||||
|
const char *freq_level_names[] = {"high", "mid_high", "mid_low", "low"};
|
||||||
|
char *p = buf;
|
||||||
|
int remaining = buf_size;
|
||||||
|
int written;
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
written = snprintf(p, remaining,
|
||||||
|
"{\"auto_mode\":%s,\"amount\":%d,\"channels\":[",
|
||||||
|
pg->auto_mode ? "true" : "false", pg->amount);
|
||||||
|
if (written < 0 || written >= remaining) return -1;
|
||||||
|
p += written; remaining -= written;
|
||||||
|
|
||||||
|
for (i = 0; i < pg->amount; i++) {
|
||||||
|
const LUP_CoilParam *cp = &pg->params[i];
|
||||||
|
uint8_t freq_lev = (cp->sensitivity >> 4) & 0x03;
|
||||||
|
uint8_t sens = cp->sensitivity & 0x0F;
|
||||||
|
uint8_t out_mode = cp->output_mode & 0x07;
|
||||||
|
uint8_t safe_mod = (cp->output_mode >> 3) & 0x1F;
|
||||||
|
uint8_t dir_mode = cp->direction_mode & 0x07;
|
||||||
|
uint8_t func_mod = (cp->direction_mode >> 4) & 0x0F;
|
||||||
|
|
||||||
|
written = snprintf(p, remaining,
|
||||||
|
"%s{\"ch\":%d,\"sensitivity\":%d,\"freq_level\":\"%s\","
|
||||||
|
"\"loop_delay\":%d,\"output_mode\":%d,\"safe_mode\":%d,"
|
||||||
|
"\"exist_mode\":%d,\"direction_mode\":%d,\"function_mode\":%d,"
|
||||||
|
"\"freq_initial\":%lu,\"freq_current\":%lu,\"freq_diff\":%lu}",
|
||||||
|
(i > 0) ? "," : "",
|
||||||
|
i + 1, sens, freq_level_names[freq_lev],
|
||||||
|
cp->loop_delay, out_mode, safe_mod,
|
||||||
|
cp->exist_mode, dir_mode, func_mod,
|
||||||
|
pg->freq[i][0], pg->freq[i][1], pg->freq[i][2]);
|
||||||
|
if (written < 0 || written >= remaining) return -1;
|
||||||
|
p += written; remaining -= written;
|
||||||
|
}
|
||||||
|
|
||||||
|
written = snprintf(p, remaining, "]}");
|
||||||
|
if (written < 0 || written >= remaining) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Format sensor report as JSON
|
||||||
|
*/
|
||||||
|
static int format_sensor_json(char *buf, uint16_t buf_size, const LUP_SensorReport *sr) {
|
||||||
|
const char *freq_level_names[] = {"high", "mid_high", "mid_low", "low"};
|
||||||
|
char *p = buf;
|
||||||
|
int remaining = buf_size;
|
||||||
|
int written;
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
written = snprintf(p, remaining,
|
||||||
|
"{\"sens_type\":\"multi_coil\",\"sub_amount\":%d,\"sub_seq\":%d,\"coils\":[",
|
||||||
|
sr->sub_amount, sr->sub_sequence);
|
||||||
|
if (written < 0 || written >= remaining) return -1;
|
||||||
|
p += written; remaining -= written;
|
||||||
|
|
||||||
|
for (i = 0; i < sr->coil_count; i++) {
|
||||||
|
const LUP_CoilSensor *cs = &sr->coils[i];
|
||||||
|
const char *misc_field;
|
||||||
|
uint32_t misc_val;
|
||||||
|
|
||||||
|
switch (cs->misc_type) {
|
||||||
|
case 0: misc_field = "\"passtime_ms5\""; misc_val = cs->misc.passtime_ms5; break;
|
||||||
|
case 1: misc_field = "\"cut_amount\""; misc_val = cs->misc.cut_amount; break;
|
||||||
|
case 2: misc_field = "\"flow_amount\""; misc_val = cs->misc.flow_amount; break;
|
||||||
|
default: misc_field = "\"misc\""; misc_val = 0; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
written = snprintf(p, remaining,
|
||||||
|
"%s{\"ch\":%d,\"freq_level\":\"%s\",\"direction\":%d,"
|
||||||
|
"\"freq_type\":\"%s\",\"sensitivity\":%d,"
|
||||||
|
"\"condition\":%d,\"loop_state\":\"%s\",\"car_state\":\"%s\","
|
||||||
|
"\"freq\":%lu,\"variation\":%d,%s:%lu}",
|
||||||
|
(i > 0) ? "," : "",
|
||||||
|
i + 1,
|
||||||
|
freq_level_names[cs->freq_level],
|
||||||
|
cs->direction,
|
||||||
|
cs->freq_type ? "current" : "initial",
|
||||||
|
cs->sensitivity,
|
||||||
|
cs->condition,
|
||||||
|
cs->loop_state ? "broken" : "normal",
|
||||||
|
cs->car_state ? "car" : "no_car",
|
||||||
|
cs->freq,
|
||||||
|
cs->variation,
|
||||||
|
misc_field, misc_val);
|
||||||
|
if (written < 0 || written >= remaining) return -1;
|
||||||
|
p += written; remaining -= written;
|
||||||
|
}
|
||||||
|
|
||||||
|
written = snprintf(p, remaining, "]}");
|
||||||
|
if (written < 0 || written >= remaining) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check for and handle pending Loop MCU deferred responses
|
||||||
|
*/
|
||||||
|
static void json_check_pending(void) {
|
||||||
|
if (!g_json_pending.active) return;
|
||||||
|
|
||||||
|
uint8_t cmd = g_lup_cmd.pending_cmd;
|
||||||
|
|
||||||
|
if (g_lup_cmd.state == LUP_STATE_RESPONSE_READY) {
|
||||||
|
// Response received!
|
||||||
|
if (strcmp(g_json_pending.cmd, "loop_param_set") == 0) {
|
||||||
|
uint8_t success = 0;
|
||||||
|
int ret = lup_parse_set_param_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &success);
|
||||||
|
if (ret == 0 && success) {
|
||||||
|
json_send_ok(g_json_pending.socket, g_json_pending.msg_id,
|
||||||
|
g_json_pending.cmd, NULL);
|
||||||
|
} else {
|
||||||
|
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
|
||||||
|
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
|
||||||
|
ret == 0 ? "Loop MCU returned failure" : "Parse error");
|
||||||
|
}
|
||||||
|
} else if (strcmp(g_json_pending.cmd, "loop_param_query") == 0) {
|
||||||
|
LUP_ParamGet pg;
|
||||||
|
memset(&pg, 0, sizeof(pg));
|
||||||
|
int ret = lup_parse_param_get(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &pg);
|
||||||
|
if (ret == 0) {
|
||||||
|
char data_json[2048];
|
||||||
|
if (format_loop_param_json(data_json, sizeof(data_json), &pg) == 0) {
|
||||||
|
json_send_ok(g_json_pending.socket, g_json_pending.msg_id,
|
||||||
|
g_json_pending.cmd, data_json);
|
||||||
|
} else {
|
||||||
|
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
|
||||||
|
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
|
||||||
|
"Response too large");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
|
||||||
|
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
|
||||||
|
"Failed to parse Loop MCU response");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lup_cmd_done();
|
||||||
|
g_json_pending.active = 0;
|
||||||
|
PRINT("JSON: deferred response sent for %s\n", g_json_pending.cmd);
|
||||||
|
}
|
||||||
|
else if (g_lup_cmd.state == LUP_STATE_TIMEOUT
|
||||||
|
|| mstick() > g_json_pending.deadline) {
|
||||||
|
// Timeout
|
||||||
|
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
|
||||||
|
g_json_pending.cmd, JSON_CODE_BUSY,
|
||||||
|
"Loop MCU no response");
|
||||||
|
lup_cmd_done();
|
||||||
|
g_json_pending.active = 0;
|
||||||
|
PRINT("JSON: deferred response timeout for %s\n", g_json_pending.cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* tcp_json_poll (updated)
|
||||||
|
*===========================================================================*/
|
||||||
void tcp_json_poll(void) {
|
void tcp_json_poll(void) {
|
||||||
// Only poll if we have an active connection (socket configured)
|
// Only poll if we have an active connection (socket configured)
|
||||||
if (g_json_socket_listen == 0xFF) return;
|
if (g_json_socket_listen == 0xFF) return;
|
||||||
@@ -702,4 +908,50 @@ void tcp_json_poll(void) {
|
|||||||
g_json_pwd_retry = 0;
|
g_json_pwd_retry = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for pending Loop MCU deferred responses
|
||||||
|
json_check_pending();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* tcp_json_push_sensor — Push sensor data (0xC0) to connected TCP client
|
||||||
|
*
|
||||||
|
* Called from main loop when uart_srv() has a sensor report frame in
|
||||||
|
* g_pkg_uart_2 that hasn't been consumed by BLE.
|
||||||
|
*===========================================================================*/
|
||||||
|
void tcp_json_push_sensor(void) {
|
||||||
|
// Check: socket active, authed, and data available
|
||||||
|
if (g_json_socket_listen == 0xFF) return;
|
||||||
|
if (g_json_auth_state != JSON_STATE_AUTHED) return;
|
||||||
|
|
||||||
|
// Only proceed if g_pkg_uart_2 has data and it's a 0xC0 sensor report
|
||||||
|
if (g_pkg_uart_2.flag == 0) return;
|
||||||
|
if (g_pkg_uart_2.pkg[0] != 0x7F) return;
|
||||||
|
if (g_pkg_uart_2.pkg[3] != 0xC0) return;
|
||||||
|
|
||||||
|
LUP_SensorReport sr;
|
||||||
|
memset(&sr, 0, sizeof(sr));
|
||||||
|
int ret = lup_parse_sensor_report(g_pkg_uart_2.pkg, g_pkg_uart_2.offset, &sr);
|
||||||
|
if (ret != 0) {
|
||||||
|
PRINT("JSON: sensor parse failed (%d)\n", ret);
|
||||||
|
InitPkgUart(&g_pkg_uart_2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char data_json[2048];
|
||||||
|
if (format_sensor_json(data_json, sizeof(data_json), &sr) == 0) {
|
||||||
|
char *out = (char *)malloc(TCP_JSON_MAX_FRAME);
|
||||||
|
if (out) {
|
||||||
|
snprintf(out, TCP_JSON_MAX_FRAME,
|
||||||
|
"{\"msg_id\":0,\"cmd\":\"sensor_report\",\"ts\":%lu,"
|
||||||
|
"\"code\":0,\"msg\":\"success\",\"data\":%s}\n",
|
||||||
|
(unsigned long)mstick(), data_json);
|
||||||
|
uint32_t slen = strlen(out);
|
||||||
|
WCHNET_SocketSend(g_json_socket_listen, (uint8_t *)out, &slen);
|
||||||
|
free(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear after processing
|
||||||
|
InitPkgUart(&g_pkg_uart_2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
*
|
*
|
||||||
* Created on: 2026-02-26
|
* Created on: 2026-02-26
|
||||||
* Author: wangfq
|
* Author: wangfq
|
||||||
|
* Updated: 2026-07-02 — 使用 loop_uart_proto 帧解析器替代 timeout heuristic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "cmcng.h"
|
#include "cmcng.h"
|
||||||
|
#include "loop_uart_proto.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "dbn_ble_srv.h"
|
#include "dbn_ble_srv.h"
|
||||||
|
|
||||||
@@ -34,7 +36,7 @@ void uart_init(void){
|
|||||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // Rx
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // Rx
|
||||||
GPIO_Init(GPIOA, &GPIO_InitStructure);
|
GPIO_Init(GPIOA, &GPIO_InitStructure);
|
||||||
|
|
||||||
USART_InitStructure.USART_BaudRate = 192000;//115200;
|
USART_InitStructure.USART_BaudRate = 115200; // 115200 (协议规定)
|
||||||
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
||||||
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
||||||
USART_InitStructure.USART_Parity = USART_Parity_No;
|
USART_InitStructure.USART_Parity = USART_Parity_No;
|
||||||
@@ -42,7 +44,7 @@ void uart_init(void){
|
|||||||
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
|
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
|
||||||
|
|
||||||
USART_Init(USART2, &USART_InitStructure);
|
USART_Init(USART2, &USART_InitStructure);
|
||||||
// USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); // 配合DMA,但测试不能接收,原因未知。
|
// USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
|
||||||
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
|
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
|
||||||
|
|
||||||
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
|
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
|
||||||
@@ -55,11 +57,13 @@ void uart_init(void){
|
|||||||
|
|
||||||
|
|
||||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
|
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
|
||||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//GPIO_Mode_IPU; // Key
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||||
GPIO_Init(GPIOA, &GPIO_InitStructure);
|
GPIO_Init(GPIOA, &GPIO_InitStructure);
|
||||||
|
|
||||||
}
|
// 初始化帧解析器
|
||||||
|
lup_frame_reset();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -76,11 +80,11 @@ void USART1_IRQHandler(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************
|
/*********************************************************************
|
||||||
* @fn USART2_IRQHandler
|
* @fn USART2_IRQHandler
|
||||||
*
|
*
|
||||||
* @brief This function handles USART2 global interrupt request.
|
* @brief USART2 RX — 使用 lup_feed_byte() 帧解析器
|
||||||
|
* 当解析出完整帧时,复制到 g_pkg_uart_2.pkg 并设置 flag
|
||||||
*
|
*
|
||||||
* @return none
|
* @return none
|
||||||
*/
|
*/
|
||||||
@@ -89,24 +93,23 @@ void USART2_IRQHandler(void)
|
|||||||
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
|
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
|
||||||
{
|
{
|
||||||
uint8_t _dat = USART_ReceiveData(USART2);
|
uint8_t _dat = USART_ReceiveData(USART2);
|
||||||
if(g_pkg_uart_2.offset == 0){
|
|
||||||
if(_dat != 0){
|
|
||||||
g_pkg_uart_2.pkg[g_pkg_uart_2.offset++] = _dat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
if(g_pkg_uart_2.offset < BUFF_STACK_SIZE){
|
|
||||||
g_pkg_uart_2.pkg[g_pkg_uart_2.offset++] = _dat;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
g_pkg_uart_2.flag = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(g_pkg_uart_2.offset){
|
// 喂给帧解析器
|
||||||
|
if (lup_feed_byte(_dat)) {
|
||||||
|
// 帧接收完成,复制到 g_pkg_uart_2
|
||||||
|
const uint8_t *frame = lup_frame_data();
|
||||||
|
uint16_t frame_len = lup_frame_len();
|
||||||
|
if (frame_len <= BUFF_STACK_SIZE) {
|
||||||
|
memcpy(g_pkg_uart_2.pkg, frame, frame_len);
|
||||||
|
g_pkg_uart_2.offset = frame_len;
|
||||||
|
g_pkg_uart_2.flag = 1;
|
||||||
|
g_pkg_uart_2.tick = 0;
|
||||||
|
}
|
||||||
|
lup_frame_reset();
|
||||||
|
} else if (g_lup_parser.state != LUP_FRAME_STATE_IDLE) {
|
||||||
|
// 正在接收帧中,tick 归零
|
||||||
g_pkg_uart_2.tick = 0;
|
g_pkg_uart_2.tick = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,44 +136,65 @@ void UART1_SendString(uint8_t *buf, uint16_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* uart_srv — 主循环中调用,处理已接收完整的 UART2 帧
|
||||||
|
*
|
||||||
|
* 处理流程:
|
||||||
|
* 1. 0x7F + 0xC0/0x0C → 传感器数据上报
|
||||||
|
* - 若无 BLE 连接 → 标记 `_report_flag`,在 TCP JSON 中处理
|
||||||
|
* - 若有 BLE 连接且 acs_enable → 转 0x8F 前缀发给 BLE
|
||||||
|
* 2. 0x7F + 其他 CMD → 响应帧,交给 lup_process_frame() 匹配挂起命令
|
||||||
|
* 3. 非 0x7F → 调试打印(可能是字符串等)
|
||||||
|
*/
|
||||||
void uart_srv(void)
|
void uart_srv(void)
|
||||||
{
|
{
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
uint8_t _report_flag = 0;
|
uint8_t _report_flag = 0;
|
||||||
|
|
||||||
|
// 检查命令超时
|
||||||
|
lup_cmd_check_timeout();
|
||||||
|
|
||||||
if(g_pkg_uart_2.flag){
|
if(g_pkg_uart_2.flag){
|
||||||
|
|
||||||
if(g_flag_counter_ota.flag == 0){
|
if(g_flag_counter_ota.flag == 0){
|
||||||
if(g_pkg_uart_2.pkg[0] == 0x7F){
|
if(g_pkg_uart_2.pkg[0] == 0x7F){
|
||||||
if(g_pkg_uart_2.pkg[3] == 0xC0 && g_pkg_uart_2.pkg[4] == 0x0C)
|
uint8_t cmd = g_pkg_uart_2.pkg[3];
|
||||||
|
|
||||||
|
// --- 传感器上报 (0xC0) ---
|
||||||
|
if(cmd == 0xC0 && g_pkg_uart_2.pkg[4] == 0x0C)
|
||||||
{
|
{
|
||||||
if(g_dbn_ble_state_acs_enable.flag == 0){
|
if(g_dbn_ble_state_acs_enable.flag == 0){
|
||||||
|
// 无 BLE ACS 连接 → 标记为 TCP JSON 上报
|
||||||
_report_flag = 1;
|
_report_flag = 1;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
// BLE ACS 已连接 → 改 Magic 为 0x8F 发给 BLE
|
||||||
g_pkg_uart_2.pkg[0] = 0x8F;
|
g_pkg_uart_2.pkg[0] = 0x8F;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// --- 命令响应 → 交给协议处理器 ---
|
||||||
|
lup_process_frame(g_pkg_uart_2.pkg, g_pkg_uart_2.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调试打印
|
||||||
for(i = 0; i < g_pkg_uart_2.offset; i++){
|
for(i = 0; i < g_pkg_uart_2.offset; i++){
|
||||||
PRINT(" %02X", g_pkg_uart_2.pkg[i]);
|
PRINT(" %02X", g_pkg_uart_2.pkg[i]);
|
||||||
}
|
}
|
||||||
PRINT("\n");
|
PRINT("\n");
|
||||||
|
|
||||||
if(_report_flag){
|
if(_report_flag){
|
||||||
InitPkgUart(&g_pkg_uart_2);
|
// 传感器帧保留在 pkg 中供上层 (tcp_json_srv) 处理
|
||||||
|
// 不 InitPkgUart — 由上层消费后再 InitPkgUart
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// 非 0x7F 魔法字节
|
||||||
PRINT("Rcv_len:%d,dat: %s\n", g_pkg_uart_2.offset, g_pkg_uart_2.pkg);
|
PRINT("Rcv_len:%d,dat: %s\n", g_pkg_uart_2.offset, g_pkg_uart_2.pkg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
// PRINT("From_Loop: ");
|
// OTA 模式 — 忽略 Loop MCU 数据
|
||||||
// for(i = 0; i < g_pkg_uart_2.offset; i++){
|
|
||||||
// PRINT(" %02X", g_pkg_uart_2.pkg[i]);
|
|
||||||
// }
|
|
||||||
// PRINT("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -180,7 +204,11 @@ void uart_srv(void)
|
|||||||
else{
|
else{
|
||||||
g_dbn_ble_state_acs_enable.flag = 0;
|
g_dbn_ble_state_acs_enable.flag = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 只有非 _report_flag 时才立即清空
|
||||||
|
// _report_flag 的帧由 tcp_json 消费后清理
|
||||||
|
if (!_report_flag) {
|
||||||
InitPkgUart(&g_pkg_uart_2);
|
InitPkgUart(&g_pkg_uart_2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
237
vd960DBN/docs/DLD960Loop_串口通信协议.md
Normal file
237
vd960DBN/docs/DLD960Loop_串口通信协议.md
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
# DLD960Loop 串口通信协议
|
||||||
|
|
||||||
|
# 1 通信说明
|
||||||
|
|
||||||
|
## 1.1 通信方式
|
||||||
|
|
||||||
|
串口通信,波特率:115200
|
||||||
|
|
||||||
|
# 2 协议格式
|
||||||
|
|
||||||
|
Package
|
||||||
|
|
||||||
|
| 1Byte | 3Bytes | 0~64Bytes | 2Bytes |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Magic byte | Header | Value | Check Bytes |
|
||||||
|
|
||||||
|
## 2.1 Magic Byte(1Byte)
|
||||||
|
|
||||||
|
标识协议类型,也表示数据包开始信息。
|
||||||
|
|
||||||
|
| Magic Byte | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| 0x7F | 屏、地感检测、探头的配置和查询协议,或者部分雷达的协议 |
|
||||||
|
| 0x8F | |
|
||||||
|
| 0x9F | OTA 用途 |
|
||||||
|
|
||||||
|
## 2.2 Header(3Bytes)
|
||||||
|
|
||||||
|
当Magic Byte为 0x7F时,Header字节定义如下:
|
||||||
|
|
||||||
|
| 1Byte | 1Byte | 1Byte |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Addr 地址 | LEN 数据长度 | CMD 命令字节 |
|
||||||
|
|
||||||
|
* Addr 默认0
|
||||||
|
|
||||||
|
* LEN数据长度,表示数据包的有效数据(Value)长度,包括命令(CMD Byte)和数据(Data Bytes);
|
||||||
|
|
||||||
|
* CMD 命令字节;
|
||||||
|
|
||||||
|
|
||||||
|
## 2.3 校验字节(Check Bytes:2Bytes)
|
||||||
|
|
||||||
|
当Magic Byte为0x7F时,校验字节实现如下:
|
||||||
|
|
||||||
|
| 1Byte | 1Byte |
|
||||||
|
| --- | --- |
|
||||||
|
| XOR 异或校验 | SUM 和校验 |
|
||||||
|
|
||||||
|
> XOR 异或校验,校验从Addr字节开始,到命令数据的最后一个字节结束;
|
||||||
|
|
||||||
|
> SUM 和校验,校验从Addr字节开始,到命令数据的最后一个字节结束。
|
||||||
|
|
||||||
|
# 3 Cmd详解
|
||||||
|
|
||||||
|
## 3.01 获取设备版本号(0x4A)
|
||||||
|
|
||||||
|
| | Header | Data |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Value(Hex)** | 00 01 4A | |
|
||||||
|
| **Length** | 3 Byte | 0 Byte |
|
||||||
|
|
||||||
|
> eg: `7F 00 01 4A 4B 4B`
|
||||||
|
|
||||||
|
返回:
|
||||||
|
|
||||||
|
| | Header | Status + Hard\_Main + Hard\_Sub + Hard\_SSub + Soft\_Main + Soft\_Sub + Soft\_SSub |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Value(Hex)** | 00 08 4A | xx |
|
||||||
|
| **Length** | 3 Byte | 7 Byte |
|
||||||
|
|
||||||
|
## 3.02 设备复位(0x6D)
|
||||||
|
|
||||||
|
| | Header | Data |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Value(Hex)** | 00 01 6D | |
|
||||||
|
| **Length** | 3 Byte | 0 Byte |
|
||||||
|
|
||||||
|
设备无回复。
|
||||||
|
|
||||||
|
> eg: `7F 00 01 6D 6C 6E`
|
||||||
|
|
||||||
|
## 3.03 出厂初始化(0x92)
|
||||||
|
|
||||||
|
| | Header | Data |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Value(Hex)** | 00 01 92 | |
|
||||||
|
| **Length** | 3 Byte | 0 Byte |
|
||||||
|
|
||||||
|
> eg: `7F 00 01 92 93 93`
|
||||||
|
|
||||||
|
返回:
|
||||||
|
|
||||||
|
| | Header | Data |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Value(Hex)** | 00 02 92 | 00/01(00 success) |
|
||||||
|
| **Length** | 3 Byte | 1 Byte |
|
||||||
|
|
||||||
|
> eg: `7F 00 02 92 00 90 94`
|
||||||
|
|
||||||
|
## 3.04 读写线圈灵敏度列表(0x8A)
|
||||||
|
|
||||||
|
| | Header | R/W,Amount, (Amount \* (SensityIn + SensityOut)) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Value(Hex)** | 00 04 8A | xx |
|
||||||
|
| **Length** | 3 Byte | (2 + 2\*x )Byte |
|
||||||
|
|
||||||
|
> R/W: 1字节,0 Read, 1 Write
|
||||||
|
|
||||||
|
> Amount:1字节,灵敏度值的数量, 当R/W为 Write时有效,最小3
|
||||||
|
|
||||||
|
> SensityIn:两字节,触发灵敏度值,当R/W为 Write时有效
|
||||||
|
|
||||||
|
> SensityOut:两字节,释放灵敏度值,当R/W为 Write时有效
|
||||||
|
|
||||||
|
返回
|
||||||
|
|
||||||
|
| | Header | Amount, (Amount \* SensityValue) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Value(Hex)** | ADDR 03 8A | xx |
|
||||||
|
| **Length** | 3 Byte | (1 + 2\*x) Byte |
|
||||||
|
|
||||||
|
## 3.05 设置车检器多路参数(0x63)
|
||||||
|
|
||||||
|
| | Header | AutoMode + Amount + Param\[Sensitivity, Loop_Delay, Output_Mode, Exist_Mode, Direction_Mode\] |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Value(Hex)** | 00 xx 63 | xx |
|
||||||
|
| **Length** | 3 Byte | (2 + 5 \* Amount) Byte |
|
||||||
|
|
||||||
|
* AutoMode: 是否处于自动模式(自动调频),0表示不启用,默认0。
|
||||||
|
|
||||||
|
* Sensitivity: 低四位表示灵敏度,0~9级,值越大,灵敏度越高,默认7;高四位表示高低频,其中,\[4,5\]表示线圈的高低频。
|
||||||
|
|
||||||
|
* Loop\_Delay: 地感延时时间,0~200, 0.1s为一级,延时时间范围:\[0, 20秒\];
|
||||||
|
|
||||||
|
* Output\_Mode: 低三位有效,地感输出方式,0 - 存在输出,1-进入脉冲,2-离开脉冲,3-方向判别;高5位表示SafeMode:是否开启安全模式, 0 关闭,非 0 开启安全模式,分钟为单位。
|
||||||
|
|
||||||
|
* Exist\_Mode:低四位表示地感存在方式,0-永久存在,非0-有限存在的分钟数;
|
||||||
|
|
||||||
|
* Direction\_Mode:
|
||||||
|
|
||||||
|
* 低三位表示方向判别模式,0 表示触发模式, 1-6 表示方向判别的输出模式,对应以前的 0-5, update 2023-02-24;
|
||||||
|
|
||||||
|
* 高四位表示Function\_Mode,功能模式,是否启用特殊的模式
|
||||||
|
|
||||||
|
|
||||||
|
| | Header | Status |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Value(Hex)** | 00 02 63 | 00/01 |
|
||||||
|
| **Length** | 3 Byte | 1 Byte |
|
||||||
|
|
||||||
|
## 3.06 读取车检器多路参数(0x64)
|
||||||
|
|
||||||
|
| | Header | Param |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Value(Hex)** | 00 01 64 | |
|
||||||
|
| **Length** | 3 Byte | |
|
||||||
|
|
||||||
|
返回:
|
||||||
|
|
||||||
|
| | Header | AutoMode+Amount + Data\[Sensitivity, Loop_Delay, Output_Mode, Exist_Mode, Direction_Mode, freq1, freq2, freq3\] |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Value(Hex)** | 00 xx 64 | XX |
|
||||||
|
| **Length** | 3 Byte | (2 + 8 \* Amount) Byte |
|
||||||
|
|
||||||
|
## 3.07 设备主动上报传感信息(0xC0)
|
||||||
|
|
||||||
|
设备主动上报传感信息指令,具体传感数据需要根据不同的类型进行解析。
|
||||||
|
|
||||||
|
| 7Fh | ADDR | Len | C0 | SensType(1Byte) | SubPkgFlag(1Byte) | SensData | XOR | SUM |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
|
SensType: 传感数据类型
|
||||||
|
|
||||||
|
SubPkgFlag: 分包标记 SubPkgFlag,高四位表示分包数量 SubAmount,低四位表示为当前分包中第几个分包 SubSequence,其中 SubSequence = \[1,SubAmount\]:
|
||||||
|
|
||||||
|
* 00 表示无分包,是一条数据完整的记录;
|
||||||
|
|
||||||
|
* 当SubAmount不为0时,SubSequence从1开始到SubAmount结束,整合为一条完整的数据包。
|
||||||
|
|
||||||
|
* 当SensType为0x05时,SubPkgFlag作为传感数据使用。
|
||||||
|
|
||||||
|
* 当SensType为0x06、0x07、0x09时,SubPkgFlag的Seq和SubAmount分开各用一个字节表示。
|
||||||
|
|
||||||
|
|
||||||
|
SensData:传感数据
|
||||||
|
|
||||||
|
| SensType | 类型说明 | SensData | 传感数据说明 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 0x0C | 多线圈传感信息 | | 四路线圈 |
|
||||||
|
| | | | |
|
||||||
|
|
||||||
|
上报线圈传感数据单元(多路线圈,每一路线圈单元的配置)
|
||||||
|
|
||||||
|
| 字段 | 字节数 | 内容 | 详情 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 配置 1 | 1 | freq\_level(2bit), Direction(1bit),freq\_type(1bit), sens(4bit) | 线圈 1 配置freq\_level:高低频,两个比特位,00 表示高频(33nF),01表示中高(43nF), 10表示中低(66nF), 11表示低频(76nF)。Direction: 0 表示触发,1 表示方向判别,freq\_type: 0 表示初始频率,1 表示当前实时频率。sens:低四位表示当前的灵敏度等级 |
|
||||||
|
| 线圈评估条件 | 1 | condition(4bit), loop\_state(1bit),<br>car\_state(1bit),<br>report\_msic(2bit) | loop\_condition 正常(0)有效,环境状态条件(环境状态评估值,值越大,干扰越大),高四位有效。低四位中 ,其中第3位表示线圈状态,0 表示正常,1 表示线圈断开;第 2 位表示有无车,0 表示无车,1 表示有车;低两位表示**杂项类型**,0b00表示时间量,0b01表示线圈断开次数;0b10表示车流量数 |
|
||||||
|
| 频率 1 | 3 | frequent | 低字节在前 |
|
||||||
|
| 变化量 1 | 2 | variation | 低字节在前 |
|
||||||
|
| 杂项 | 4 | in\_out\_passtime/cut\_amount/flow\_amount | **杂项类型**,可能是时间量,也可能是线圈断开次数、车流量数,说明:<br>1、通过时间或车间距,5ms为单位,最大计时()<br>2、线圈断开次数<br>3、车流量数 |
|
||||||
|
|
||||||
|
eg:
|
||||||
|
|
||||||
|
`7F 00 2F C0 0C 00 12 00 2A 0F 01 07 00 00 00 00 00 D2 00 75 B7 00 01 00 00 00 00 00 12 00 EC 0C 01 02 00 00 00 00 00 92 00 83 C3 00 02 00 00 00 00 00 E2 34`
|
||||||
|
|
||||||
|
指令解释
|
||||||
|
|
||||||
|
```python
|
||||||
|
7F Magic Byte
|
||||||
|
00 Addr,0 default
|
||||||
|
2F Len, 0x2F=47个字节(Cmd + Data)
|
||||||
|
C0 Cmd
|
||||||
|
0C SensType,传感数据类型,0x0C为 多线圈传感信息,960Loop为 四路线圈车检器,会有4个传感数据单元
|
||||||
|
00 SubPkgFlag,分包标记,00 表示无分包,是一条数据完整的记录。
|
||||||
|
12 00 2A 0F 01 07 00 00 00 00 00:线圈1传感数据单元。0b00 高频,0b0 触发模式,0b1实时频率,0b0010 表示2级灵敏度;0b0000环境正常,0b0线圈正常,0b0当前无车,0b00 表示当前杂项参数为时间量;0x010F2A,频率为69418Hz;0x0007,变化量为7;0x00000000,时间量为为0
|
||||||
|
D2 00 75 B7 00 01 00 00 00 00 00:线圈2传感数据单元。0b11 低频,0b0 触发模式,0b1实时频率,0b0010 表示2级灵敏度;0b0000环境正常,0b0线圈正常,0b0当前无车,0b00 表示当前杂项参数为时间量;0x00B775,频率为46965Hz;0x0001,变化量为1;0x00000000,时间量为为0
|
||||||
|
12 00 EC 0C 01 02 00 00 00 00 00:线圈3传感数据单元。0b00 高频,0b0 触发模式,0b1实时频率,0b0010 表示2级灵敏度;0b0000环境正常,0b0线圈正常,0b0当前无车,0b00 表示当前杂项参数为时间量;0x010CEC,频率为68844Hz;0x0002,变化量为2;0x00000000,时间量为为0
|
||||||
|
92 00 83 C3 00 02 00 00 00 00 00:线圈4传感数据单元。0b10 中低,0b0 触发模式,0b1实时频率,0b0010 表示2级灵敏度;0b0000环境正常,0b0线圈正常,0b0当前无车,0b00 表示当前杂项参数为时间量;0x00C383,频率为50051Hz;0x0002,变化量为2;0x00000000,时间量为为0
|
||||||
|
E2 34 校验字节
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意,如果有续包,最后一包上位机无返回**。中间续包上位机返回传感类型,当前收到包的序列和子包的数量:
|
||||||
|
|
||||||
|
| 7Fh | ADDR | 07 | C0 | SensType | Seq(1Byte) | SubAmount(1Byte) | XOR | SUM |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 修订记录
|
||||||
|
|
||||||
|
| 版本 | 修订时间 | 修订说明 | 修订人 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| | | | |
|
||||||
|
| V1.00 | 2026-02-11 | 创建协议初始版本 | wangfq |
|
||||||
|
| V1.01 | 2026-06-22 | 补充遗漏的接口说明 | wangfq |
|
||||||
|
| V1.02 | 2026-07-02 | 查缺补漏,补充例程说明。 | wangfq |
|
||||||
Reference in New Issue
Block a user