From 7eb30c1ab29a3b5a262133ff10c976f9d9924c41 Mon Sep 17 00:00:00 2001 From: wrh Date: Tue, 29 Sep 2026 16:54:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E5=8D=97=E5=90=91?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=8D=8F=E8=AE=AE=EF=BC=8C=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E6=B5=81=E7=A8=8B=E9=80=BB=E8=BE=91=20?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=8D=97=E5=90=91=E4=B8=BB=E5=8A=A8=E5=8F=91?= =?UTF-8?q?=E9=80=81C=EF=BC=8C=E4=B8=8D=E5=86=8D=E5=90=91=E5=8D=97?= =?UTF-8?q?=E5=90=91=E5=8F=91=E9=80=81=E8=BF=9B=E5=85=A5=E5=92=8C=E9=80=80?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1 升级进入/退出命令不再向南向发送控制帧,本地会话建立后立即响应成功 2 功率板Boot主动每500ms发送'C',协议板收到后转发给上位机 3 更新协议板版本寄存器为0x0143,版本号升级至V1.1.51 4 调整升级状态机,删除等待南向ACK的相关逻辑 5 更新相关文档,同步升级协议和状态机的修改内容 --- apps/chrg/applications/thread/chrg_comm.c | 9 +- apps/chrg/applications/thread/chrg_north.h | 1 - apps/chrg/applications/utils/chrg_def.h | 4 +- .../applications/utils/chrg_ymodem_proxy.c | 88 +++---------------- .../applications/utils/chrg_ymodem_proxy.h | 8 +- docs/修改日志.md | 17 ++++ docs/功率板升级_北向转发计划.md | 32 ++----- docs/功率板升级_南向应答计划.md | 47 ++-------- 8 files changed, 56 insertions(+), 150 deletions(-) diff --git a/apps/chrg/applications/thread/chrg_comm.c b/apps/chrg/applications/thread/chrg_comm.c index 65d949b..b0d2fe3 100644 --- a/apps/chrg/applications/thread/chrg_comm.c +++ b/apps/chrg/applications/thread/chrg_comm.c @@ -27,7 +27,7 @@ #include "stm32f4xx_hal.h" #define HOLD_REG_FIRMWARE_VERSION 0x0300U /* 协议板固件版本,只读 */ -#define CHRG_FIRMWARE_VERSION 0x0141U /* 当前协议板版本:V1.1.49 */ +#define CHRG_FIRMWARE_VERSION 0x0143U /* 当前协议板版本:V1.1.51 */ rt_uint8_t ucDiscInBuf[(TOTAL_DISC_REGS+BITS_UCHAR-1)/BITS_UCHAR]; @@ -1403,10 +1403,9 @@ eMBException funYmodemUpadata (rt_uint8_t *reqFrame, rt_uint16_t reqLen, return MB_EX_ILLEGAL_DATA_VALUE; } - /* 保留原0x20响应内容,外层统一补设备地址和Modbus CRC。 */ - resFrame[0] = reqFrame[0]; - resFrame[1] = ACK; - *resLen = 2; + /* 成功时回显地址、功能码、保留字节、通道和操作,外层统一补CRC。 */ + rt_memcpy(resFrame, reqFrame, 6); + *resLen = 6; return MB_EX_NONE; } diff --git a/apps/chrg/applications/thread/chrg_north.h b/apps/chrg/applications/thread/chrg_north.h index 05c56f3..1707e0a 100644 --- a/apps/chrg/applications/thread/chrg_north.h +++ b/apps/chrg/applications/thread/chrg_north.h @@ -48,7 +48,6 @@ #define SOUTH_FRAME_DEBUG 0x00 // 南向UART原始收发帧 #define SOUTH_COMMAND_DEBUG 0x01 // 南向寄存器写命令 #define SOUTH_YMODEM_DEBUG 0x01 // 南向YMODEM回码 -#define SOUTH_YMODEM_EXIT_APP 0x01 // 升级退出时发送南向跳转App帧 #define COMM_FRAME_DEBUG 0x00 // COM UART原始收发帧 #define COMM_DEBUG 0x00 // COM寄存器读写过程 #define COMM_TRIM_DEBUG 0x00 // COM校准参数解析 diff --git a/apps/chrg/applications/utils/chrg_def.h b/apps/chrg/applications/utils/chrg_def.h index 8fc103f..28c3a18 100644 --- a/apps/chrg/applications/utils/chrg_def.h +++ b/apps/chrg/applications/utils/chrg_def.h @@ -14,8 +14,8 @@ /* CHRG application version. Keep this value synchronized with the changelog. */ #define CHRG_VERSION_MAJOR 1U #define CHRG_VERSION_MINOR 1U -#define CHRG_VERSION_PATCH 49U -#define CHRG_VERSION_STRING "V1.1.49" +#define CHRG_VERSION_PATCH 51U +#define CHRG_VERSION_STRING "V1.1.51" /* * Channel polling mode: diff --git a/apps/chrg/applications/utils/chrg_ymodem_proxy.c b/apps/chrg/applications/utils/chrg_ymodem_proxy.c index 8b9ef17..5fb3d05 100644 --- a/apps/chrg/applications/utils/chrg_ymodem_proxy.c +++ b/apps/chrg/applications/utils/chrg_ymodem_proxy.c @@ -8,7 +8,6 @@ #include "chrg_def.h" #include "chrg_thread.h" -#include "chrg_north.h" #include "chrg_south.h" #include "chrg_roll_sou.h" #include "chrg_eload.h" @@ -19,9 +18,7 @@ #define YMODEM_RX_FRAME_TIMEOUT_MS 1000U // PC分段数据拼帧超时 #define YMODEM_SESSION_IDLE_MS 15000U // 整个升级会话无有效数据超时 -#define YMODEM_ENTER_TIMEOUT_MS 15000U // 功率板进入Boot及擦除等待时间 #define YMODEM_RESPONSE_TIMEOUT_MS 10000U // 南向单帧应答等待时间 -#define YMODEM_PREPARE_DELAY_MS 200U // 切换升级模式后等待功率板重启稳定 typedef struct { chrg_ymodem_proxy_state_t state; // 当前升级阶段 @@ -30,7 +27,6 @@ typedef struct { rt_uint8_t current_packet; // 当前已转发帧的包号,仅用于日志 rt_uint8_t meta_ack_seen; // 元数据ACK已收到,后续才接受'C' rt_uint8_t eot2_ack_seen; // 第二个EOT的ACK已收到 - rt_uint8_t deferred_c; // 进入Boot时与ACK同批到达的'C'延后转发 rt_uint16_t rx_len; // 北向当前已拼接字节数 rt_uint16_t expected_len; // 根据SOH/STX计算的完整帧长度 rt_tick_t rx_tick; // 当前半帧起始时间 @@ -46,13 +42,6 @@ static chrg_ymodem_proxy_t g_proxy = { .ch = 0xFFU }; -/* 功率板进入Boot和跳转App的固定Modbus控制帧。 */ -static const rt_uint8_t g_enter_frame[8] = - {ID_ELOAD, 0x06, 0x00, 0x0F, 0x00, 0xFF, 0xF8, 0x6B}; -#if SOUTH_YMODEM_EXIT_APP -static const rt_uint8_t g_exit_frame[8] = - {ID_ELOAD, 0x06, 0x00, 0x0F, 0x00, 0x0F, 0xF8, 0x2F}; -#endif static const rt_uint8_t g_cancel_frame[2] = {YM_CAN, YM_CAN}; static const char *chrg_ymodem_proxy_state_name(chrg_ymodem_proxy_state_t state) @@ -245,7 +234,10 @@ static int chrg_ymodem_proxy_prepare_channel(rt_uint8_t ch) roll->leng = 0U; roll->updata_result = 0; rt_mutex_release(thread->mutex); - rt_thread_mdelay(YMODEM_PREPARE_DELAY_MS); + if (chrg_south_switch((eIDX_SOU_CH)ch) != 0) { + chrg_ymodem_proxy_session_stop(); + return -3; + } chrg_eload_clear_ymodem_resp(); if (chrgsouth.rb != RT_NULL) rt_ringbuffer_reset(chrgsouth.rb); return 0; @@ -253,88 +245,38 @@ static int chrg_ymodem_proxy_prepare_channel(rt_uint8_t ch) /***************************************************************** 函数名称: chrg_ymodem_proxy_enter -函数描述: 进入指定功率板的Boot升级模式 +函数描述: 锁定指定功率板通道并等待Boot周期发送'C' 输入参数: ch:南向通道编号 -返回说明: 0:收到功率板ACK <0:进入失败 -其它说明: 仅在真实ACK到达后允许COM回复PC成功 +返回说明: 0:本地升级会话已建立 <0:进入失败 +其它说明: 不向南向发送进入指令,建立会话后立即允许COM回复PC成功 *****************************************************************/ int chrg_ymodem_proxy_enter(rt_uint8_t ch) { - rt_tick_t start; - rt_uint8_t response; - if (g_proxy.state != YMODEM_PROXY_IDLE) return -1; if (chrg_ymodem_proxy_prepare_channel(ch) != 0) return -2; chrg_ymodem_proxy_reset(); g_proxy.ch = ch; - g_proxy.state = YMODEM_PROXY_ENTERING; + g_proxy.state = YMODEM_PROXY_WAIT_C; g_proxy.activity_tick = rt_tick_get(); - if (chrg_send_sou_upadata_data(g_enter_frame, sizeof(g_enter_frame)) != 0) { - chrg_ymodem_proxy_session_stop(); - return -3; - } - start = rt_tick_get(); - while ((rt_tick_t)(rt_tick_get() - start) < - rt_tick_from_millisecond(YMODEM_ENTER_TIMEOUT_MS)) { - response = chrg_eload_get_ymodem_resp(); - if (response == ACK) { - g_proxy.state = YMODEM_PROXY_WAIT_C; - g_proxy.activity_tick = rt_tick_get(); - return 0; - } - if (response == ACK_C) g_proxy.deferred_c = 1U; - if ((response == NAK) || (response == YM_CAN)) break; - rt_thread_mdelay(1U); - } - chrg_ymodem_proxy_session_stop(); - return -4; + return 0; } /***************************************************************** 函数名称: chrg_ymodem_proxy_exit -函数描述: 跳转功率板App并恢复普通南向轮询 +函数描述: 立即结束本地升级会话并恢复普通南向轮询 输入参数: ch:当前升级通道 -返回说明: 0:收到跳转ACK <0:状态或通信失败 -其它说明: 升级会话任意阶段均可退出,退出后强制刷新版本 +返回说明: 0:退出成功 <0:通道错误 +其它说明: 不向南向发送退出指令,也不等待南向回码 *****************************************************************/ int chrg_ymodem_proxy_exit(rt_uint8_t ch) { -#if SOUTH_YMODEM_EXIT_APP - rt_tick_t start; - rt_uint8_t response; -#endif - if (ch >= TOTAL_SOU_CHS) return -1; - if (g_proxy.state == YMODEM_PROXY_IDLE) { - if (chrg_ymodem_proxy_prepare_channel(ch) != 0) return -2; - g_proxy.ch = ch; - } else if (ch != g_proxy.ch) { + if ((g_proxy.state != YMODEM_PROXY_IDLE) && (ch != g_proxy.ch)) { return -1; } -#if SOUTH_YMODEM_EXIT_APP - g_proxy.state = YMODEM_PROXY_EXITING; - chrg_eload_clear_ymodem_resp(); - if (chrg_send_sou_upadata_data(g_exit_frame, sizeof(g_exit_frame)) != 0) - return -3; - start = rt_tick_get(); - while ((rt_tick_t)(rt_tick_get() - start) < - rt_tick_from_millisecond(YMODEM_RESPONSE_TIMEOUT_MS)) { - response = chrg_eload_get_ymodem_resp(); - if (response == ACK) { - chrg_roll_sou_version_invalidate(ch); - chrg_ymodem_proxy_session_stop(); - return 0; - } - if ((response == NAK) || (response == YM_CAN)) break; - rt_thread_mdelay(1U); - } - chrg_ymodem_proxy_session_stop(); - return -4; -#else chrg_roll_sou_version_invalidate(ch); chrg_ymodem_proxy_session_stop(); return 0; -#endif } /* 根据当前阶段校验完整帧,校验成功才允许提交南向。 */ @@ -542,10 +484,6 @@ rt_uint16_t chrg_ymodem_proxy_poll(rt_uint8_t *output, chrg_ymodem_proxy_session_stop(); return 0U; } - if (g_proxy.deferred_c && (g_proxy.state == YMODEM_PROXY_WAIT_C)) { - g_proxy.deferred_c = 0U; - chrg_ymodem_proxy_handle_south(ACK_C); - } response = chrg_eload_get_ymodem_resp(); if (response != 0U) chrg_ymodem_proxy_handle_south(response); if ((g_proxy.expected_len != 0U) && (g_proxy.rx_tick != 0U) && diff --git a/apps/chrg/applications/utils/chrg_ymodem_proxy.h b/apps/chrg/applications/utils/chrg_ymodem_proxy.h index 8af7114..bc0ee0c 100644 --- a/apps/chrg/applications/utils/chrg_ymodem_proxy.h +++ b/apps/chrg/applications/utils/chrg_ymodem_proxy.h @@ -9,7 +9,7 @@ typedef enum { YMODEM_PROXY_IDLE = 0, // 未进入升级模式 - YMODEM_PROXY_ENTERING, // 已发送进入Boot控制帧,等待ACK + YMODEM_PROXY_ENTERING, // 保留状态 YMODEM_PROXY_WAIT_C, // 进入Boot成功,等待功率板'C' YMODEM_PROXY_WAIT_META, // 等待PC发送包号0元数据SOH YMODEM_PROXY_WAIT_META_RESP, // 元数据已转发,等待南向ACK和'C' @@ -21,13 +21,13 @@ typedef enum { YMODEM_PROXY_WAIT_END_SOH, // 等待包号0且数据全零的结束SOH YMODEM_PROXY_WAIT_END_ACK, // 结束SOH已转发,等待南向ACK YMODEM_PROXY_TRANSFER_DONE, // YMODEM传输完成,等待0x20退出命令 - YMODEM_PROXY_EXITING, // 已发送跳转App控制帧,等待ACK + YMODEM_PROXY_EXITING, // 保留状态 YMODEM_PROXY_ABORT // 异常取消,等待CAN发送完成 } chrg_ymodem_proxy_state_t; -/* 锁定目标通道并进入功率板Boot,收到真实ACK后才返回成功。 */ +/* 锁定目标通道并等待功率板Boot周期发送'C'。 */ extern int chrg_ymodem_proxy_enter(rt_uint8_t ch); -/* 仅传输完成后允许跳转App,收到ACK后恢复普通轮询。 */ +/* 立即结束本地升级会话并恢复普通轮询。 */ extern int chrg_ymodem_proxy_exit(rt_uint8_t ch); /* 累计PC分段数据,形成完整YMODEM帧后校验并提交南向。 */ extern int chrg_ymodem_proxy_feed_north(const rt_uint8_t *data, diff --git a/docs/修改日志.md b/docs/修改日志.md index f3ed5f0..7e765f8 100644 --- a/docs/修改日志.md +++ b/docs/修改日志.md @@ -1,5 +1,22 @@ # 修改日志 +## V1.1.51 - 2026-09-29 + +### 南向Boot主动握手 + +- `0x20`进入命令仅锁定目标通道并立即回码,不再向南向发送进入升级控制帧或等待ACK。 +- 南向Boot每500ms发送的 `C` 由协议板转发给上位机,收到首个 `C` 后进入YMODEM元数据阶段。 +- `0x20`退出命令立即结束本地升级会话并回码,不再发送南向跳转App帧或等待ACK。 +- 协议板版本寄存器 `0x0300` 更新为 `0x0143`。 + +## V1.1.50 - 2026-09-29 + +### 南向升级命令回码格式 + +- `0x20` 进入和退出升级成功后,回显请求前6字节并重新附加Modbus CRC。 +- 成功回码保留功能码、目标通道和进入/退出操作,不再使用4字节 `地址+ACK+CRC` 格式。 +- 协议板版本寄存器 `0x0300` 更新为 `0x0142`。 + ## V1.1.49 - 2026-09-29 ### 南向升级随时退出 diff --git a/docs/功率板升级_北向转发计划.md b/docs/功率板升级_北向转发计划.md index f03fbe4..3f36745 100644 --- a/docs/功率板升级_北向转发计划.md +++ b/docs/功率板升级_北向转发计划.md @@ -4,13 +4,13 @@ 已在 `chrg_ymodem_proxy.c/.h` 实现独立的非阻塞转发状态机,并接入COM北向入口和现有南向发送线程。当前实现包括: -- 指定通道进入/退出升级,以功率板真实ACK决定成功响应。 +- 指定通道进入/退出升级命令完成本地状态切换后立即成功响应。 - SOH/STX分段拼帧、包号反码和CRC-16/XMODEM校验。 - `.upk` 元数据、连续数据包号、包号1固定STX和结束空SOH校验。 - 两次EOT独立状态,ACK/NAK/C/CAN均来自南向功率板。 - NAK原样转发给PC,协议板不再自动重发数据帧或EOT。 - 半帧1秒超时、南向应答10秒超时和会话15秒空闲取消。 -- 跳转App确认后恢复普通轮询,并强制目标通道重新读取版本。 +- 退出命令恢复普通轮询,并强制目标通道重新读取版本。 ## 1. 范围与原则 @@ -42,24 +42,23 @@ Byte 6 CRC_L Byte 7 CRC_H ``` -协议板必须完成以下操作后才能回复成功: +协议板必须完成以下操作后立即回复成功: 1. 校验地址、长度、通道和 Modbus CRC。 2. 锁定目标通道并停止普通南向轮询。 -3. 南向发送进入升级控制帧。 -4. 收到功率板 Boot 的 ACK;若同时收到 `C`,必须保留并在北向响应完成后转发。 +3. 清理历史南向回码,等待功率板 Boot 每500ms主动发送 `C`。 成功响应: ```text -协议板地址 + 0x06 + CRC_L + CRC_H +协议板地址 + 0x20 + 0x00 + 0x00 + 功率板通道号 + 0xFF + CRC_L + CRC_H ``` 广播地址 `0x00` 不回复。失败使用现有 Modbus 异常响应,不得进入原始 YMODEM 模式。 -### 2.2 退出升级/跳转 App +### 2.2 退出升级 -在升级模式中,PC 发送同格式的 8 字节命令,Byte 5 为 `0x00`。协议板南向发送跳转 App 控制帧;功率板 Boot 必须先发送 `ACK` 并确认 UART 发送完成,再执行跳转。协议板收到该 ACK 后退出升级模式并恢复普通轮询。 +PC发送同格式的8字节命令,Byte 5为 `0x00`。协议板立即结束本地升级会话、恢复普通轮询并原样回显退出命令,不向南向发送控制帧,也不等待南向回码。 结束空 SOH 获得 ACK 只表示 YMODEM 传输结束;PC 仍须发送退出命令,并在恢复通信后读取功率板版本确认升级结果。 @@ -101,15 +100,8 @@ CRC 使用 CRC-16/XMODEM:`poly=0x1021`、`init=0x0000`、不反射、`xorout=0 ```text N_IDLE 收到合法 0x20/进入命令 - -> N_ENTERING - -N_ENTERING - 南向确认进入 Boot -> 回复 0x20 成功 -> N_WAIT_C - 失败/超时 - -> 回复异常 - -> N_IDLE N_WAIT_C 收到南向 C -> 原样转发 -> N_WAIT_META @@ -159,10 +151,7 @@ N_WAIT_END_ACK 南向 ACK -> 转发 ACK -> N_TRANSFER_DONE N_TRANSFER_DONE - 收到合法 0x20/退出命令 -> N_EXITING - -N_EXITING - 南向跳转确认 -> 回复命令成功 -> 恢复轮询 -> N_VERIFY + 收到合法 0x20/退出命令 -> 回复命令成功 -> 恢复轮询 -> N_VERIFY N_VERIFY PC通过恢复后的正常协议读取功率板版本 @@ -174,9 +163,8 @@ N_VERIFY ```text PC -> 协议板 : 0x20进入命令 -协议板 -> 功率板 : 进入升级控制帧 -功率板 -> 协议板 : ACK, C 协议板 -> PC : 0x20成功响应 +功率板 -> 协议板 : C(Boot每500ms主动发送) 协议板 -> PC : C PC -> 协议板 : SOH包0元数据 @@ -206,8 +194,6 @@ PC -> 协议板 : SOH包0,Data全0 协议板 -> PC : ACK PC -> 协议板 : 0x20退出命令 -协议板 -> 功率板 : 跳转App控制帧 -功率板 -> 协议板 : ACK(发送完成后才跳转App) 协议板 -> PC : 0x20成功响应 协议板 : 恢复轮询并刷新功率板版本 PC -> 协议板 : 通过正常协议查询功率板版本并确认升级结果 diff --git a/docs/功率板升级_南向应答计划.md b/docs/功率板升级_南向应答计划.md index 05c098d..2137e29 100644 --- a/docs/功率板升级_南向应答计划.md +++ b/docs/功率板升级_南向应答计划.md @@ -8,37 +8,17 @@ 功率板UPK采用与协议板Boot相同的“1024字节首块”结构:前96字节为UPK逻辑头,后928字节固定为 `0x00`,package body从文件偏移 `0x400` 开始。协议板只校验YMODEM帧长度、包号反码和CRC16;UPK头、大小、加密和CRC由功率板Boot解析。 -## 2. 串口和控制帧 +## 2. 串口和会话启动 南向串口参数:115200 bps、8N1、无流控。每次发送前必须确认目标通道已选通,并正确控制 RS485 收发方向。 -进入功率板 Boot: - -```text -03 06 00 0F 00 FF F8 6B -``` - -功率板正常应答: - -```text -06 43 -``` - -其中 `06` 表示进入命令执行成功,`43` 表示 Boot 已准备接收 CRC 模式 YMODEM。 - -跳转功率板 App: - -```text -03 06 00 0F 00 0F F8 2F -``` - -正常应答为 `06`;`15` 表示失败。Boot 必须在 UART 完整发送 `06` 后才关闭 DMA/中断并跳转 App,避免协议板因未收到应答而超时。 +协议板不再向南向发送进入Boot或跳转App控制帧。功率板Boot启动后每500ms主动发送 `43`,协议板收到后转发北向并开始YMODEM流程。北向退出命令仅结束协议板本地升级会话。 ## 3. 南向帧和应答映射 | 协议板发送 | 功率板 Boot 正常回复 | 协议板处理 | | --- | --- | --- | -| 进入升级控制帧 | `ACK + C` | ACK确认进入,C进入应答队列,按顺序交北向 | +| Boot主动发送 | `C` | 转发北向并进入元数据等待阶段 | | 元数据 SOH/包0 | `ACK + C` | 两字节均按顺序交北向 | | UPK头区 STX/包1 | `ACK`或`NAK` | Boot解析96字节头并检查928字节填充,不写APP区 | | UPK包体 STX/包2起 | `ACK`或`NAK` | 从APP首地址开始安装,仅接收`pkg_size`范围内的数据 | @@ -48,21 +28,15 @@ | 第二次 EOT | `ACK + C` | 两字节均按顺序交北向 | | 结束空 SOH/包0 | `ACK` | 交北向,标记传输完成 | | CAN | `ACK`或无回复 | 停止会话并清理缓存 | -| 跳转 App 控制帧 | `ACK` | 退出升级模式并恢复轮询 | ## 4. 南向状态机 ```text S_IDLE - 北向进入请求 -> 锁通道、停轮询 -> S_ENTER_TX + 北向进入请求 -> 锁通道、停轮询 -> S_WAIT_C -S_ENTER_TX - UART发送完成 -> S_ENTER_WAIT - -S_ENTER_WAIT - ACK -> 记录进入成功 - C -> 记录Boot就绪 -> S_WAIT_META - NAK/超时 -> S_ABORT +S_WAIT_C + Boot主动发送C -> 转交北向 -> S_WAIT_META S_WAIT_META 收到北向完整元数据帧 -> S_META_TX @@ -121,14 +95,7 @@ S_END_WAIT ACK -> 转交北向 -> S_TRANSFER_DONE S_TRANSFER_DONE - 收到退出请求 -> S_JUMP_TX - -S_JUMP_TX - UART发送完成 -> S_JUMP_WAIT - -S_JUMP_WAIT - ACK -> 确认已转交北向 -> 恢复轮询 -> S_IDLE - NAK/超时 -> S_ABORT + 收到退出请求 -> 恢复轮询 -> S_IDLE S_ABORT 清理应答队列和半帧,解除通道锁,恢复轮询 -> S_IDLE