/** ****************************************************************************** * @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 /*=========================================================================== * 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 *===========================================================================*/ static void lup_debug_tx(const uint8_t *buf, uint16_t len) { uint8_t i; PRINT("LUP Tx:"); for (i = 0; i < len; i++) { PRINT(" %02X", buf[i]); } PRINT("\n"); } void lup_send_get_version(void) { uint8_t buf[LUP_MAX_PKG_LEN]; uint16_t len = lup_build_get_version(buf); lup_debug_tx(buf, len); UART2_SendString(buf, len); lup_cmd_begin(LUP_CMD_GET_VERSION, 200); } void lup_send_reset(void) { uint8_t buf[LUP_MAX_PKG_LEN]; uint16_t len = lup_build_reset(buf); lup_debug_tx(buf, len); 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); lup_debug_tx(buf, len); 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); lup_debug_tx(buf, len); 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); lup_debug_tx(buf, len); 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); lup_debug_tx(buf, len); 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); }