优化ppp及sio的bufferflush机制,保证flsuh顺序。 2592/1 2592/head
author孙春阳 <snickersun@163.com>
Thu, 23 Oct 2025 08:36:26 +0000 (16:36 +0800)
committer孙春阳 <snickersun@163.com>
Thu, 23 Oct 2025 08:36:26 +0000 (16:36 +0800)
优化ppp的AT指令执行时的pause和resume逻辑,更顺畅,不报错。

code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/arch/sio.c
code/sdk/zhihui/ppp/CMakeLists.txt
code/sdk/zhihui/ppp/example/ppp_at_cmd.c
code/sdk/zhihui/ppp/include/ppp_api.h
code/sdk/zhihui/ppp/include/ppp_interrupt.h
code/sdk/zhihui/ppp/src/ppp_api.c
code/sdk/zhihui/ppp/src/ppp_interrupt.c
code/sdk/zhihui/ppp/src/ppp_power_ctl.c

index 6fb9a27..b94f2a2 100644 (file)
@@ -347,8 +347,19 @@ static void sio_timeout_timer_callback(unsigned long arg)
     
     uint8_t current_idx = g_sio_current_buffer_index;
     
-    // ⚠️ 检查所有buffer的超时状态
-    // 需求:超时时如果当前buffer未满也要flush,避免数据滞留
+    // ⚠️ 检查所有buffer的超时状态并按写入顺序 flush
+    // ⚠️ 关键修复:必须按 first_write_time 排序,避免数据乱序
+    
+    // 第1步:收集所有需要 flush 的超时 buffer
+    typedef struct {
+        uint8_t index;
+        uint32_t write_time;
+        bool is_current;
+    } buffer_order_t;
+    
+    buffer_order_t buffers_to_flush[SIO_PING_PONG_BUFFER_COUNT];
+    uint8_t flush_count = 0;
+    
     for (uint8_t i = 0; i < SIO_PING_PONG_BUFFER_COUNT; i++) {
         uint8_t current_check = g_sio_current_buffer_index;
         bool is_current = (i == current_check);
@@ -362,17 +373,38 @@ static void sio_timeout_timer_callback(unsigned long arg)
                 uint32_t elapsed = current_time - buffer->last_write_time;
                 
                 if (elapsed >= SIO_PING_PONG_TIMEOUT_MS) {
-                    if (sio_flush_buffer(i) == ERRCODE_SUCC) {
-                        g_sio_stats.buffer_timeout_count++;
-                        
-                        if (is_current) {
-                            uint8_t next_idx = (i + 1) % SIO_PING_PONG_BUFFER_COUNT;
-                            if (g_sio_ping_pong_buffers[next_idx].state == SIO_BUFFER_STATE_EMPTY) {
-                                g_sio_current_buffer_index = next_idx;
-                                g_sio_stats.buffer_switch_count++;
-                            }
-                        }
-                    }
+                    // 记录需要 flush 的 buffer
+                    buffers_to_flush[flush_count].index = i;
+                    buffers_to_flush[flush_count].write_time = buffer->first_write_time;
+                    buffers_to_flush[flush_count].is_current = is_current;
+                    flush_count++;
+                }
+            }
+        }
+    }
+    
+    // 第2步:按 first_write_time 排序
+    for (uint8_t i = 0; i < flush_count; i++) {
+        for (uint8_t j = i + 1; j < flush_count; j++) {
+            if (buffers_to_flush[j].write_time < buffers_to_flush[i].write_time) {
+                buffer_order_t temp = buffers_to_flush[i];
+                buffers_to_flush[i] = buffers_to_flush[j];
+                buffers_to_flush[j] = temp;
+            }
+        }
+    }
+    
+    // 第3步:按排序后的顺序 flush
+    for (uint8_t i = 0; i < flush_count; i++) {
+        uint8_t idx = buffers_to_flush[i].index;
+        if (sio_flush_buffer(idx) == ERRCODE_SUCC) {
+            g_sio_stats.buffer_timeout_count++;
+            
+            if (buffers_to_flush[i].is_current) {
+                uint8_t next_idx = (idx + 1) % SIO_PING_PONG_BUFFER_COUNT;
+                if (g_sio_ping_pong_buffers[next_idx].state == SIO_BUFFER_STATE_EMPTY) {
+                    g_sio_current_buffer_index = next_idx;
+                    g_sio_stats.buffer_switch_count++;
                 }
             }
         }
@@ -734,7 +766,55 @@ errcode_t sio_interrupt_disable(void)
         printf("[SIO] Timer stopped\n");
     }
     
-    // 2. 停止处理线程
+    // 2. ⚠️ 数据安全:在停止前,按写入顺序 flush 所有有数据的 buffer
+    if (g_sio_ping_pong_buffers != NULL) {
+        printf("[SIO] Flushing all buffers in write order before disable...\n");
+        
+        // 收集并排序(与 timer 回调相同的逻辑)
+        typedef struct {
+            uint8_t index;
+            uint32_t write_time;
+        } buffer_order_t;
+        
+        buffer_order_t buffers_to_flush[SIO_PING_PONG_BUFFER_COUNT];
+        uint8_t flush_count = 0;
+        
+        for (uint8_t i = 0; i < SIO_PING_PONG_BUFFER_COUNT; i++) {
+            if (g_sio_ping_pong_buffers[i].state == SIO_BUFFER_STATE_FILLING && 
+                g_sio_ping_pong_buffers[i].write_pos > 0) {
+                buffers_to_flush[flush_count].index = i;
+                buffers_to_flush[flush_count].write_time = g_sio_ping_pong_buffers[i].first_write_time;
+                flush_count++;
+            }
+        }
+        
+        // 按 first_write_time 排序
+        for (uint8_t i = 0; i < flush_count; i++) {
+            for (uint8_t j = i + 1; j < flush_count; j++) {
+                if (buffers_to_flush[j].write_time < buffers_to_flush[i].write_time) {
+                    buffer_order_t temp = buffers_to_flush[i];
+                    buffers_to_flush[i] = buffers_to_flush[j];
+                    buffers_to_flush[j] = temp;
+                }
+            }
+        }
+        
+        // 按正确顺序 flush
+        for (uint8_t i = 0; i < flush_count; i++) {
+            uint8_t idx = buffers_to_flush[i].index;
+            printf("[SIO]   Flushing buffer[%u] (write_order=%u): %u bytes\n", 
+                   idx, i, g_sio_ping_pong_buffers[idx].write_pos);
+            sio_flush_buffer(idx);
+            // 给处理线程一点时间处理最后的数据
+            osal_msleep(50);
+        }
+        
+        if (flush_count > 0) {
+            printf("[SIO] Flushed %u buffers in correct write order\n", flush_count);
+        }
+    }
+    
+    // 3. 停止处理线程
     g_sio_irq_task_running = false;
     
     sio_irq_msg_t empty_msg = {0};
@@ -743,11 +823,11 @@ errcode_t sio_interrupt_disable(void)
     
     osal_msleep(200);
     
-    // 3. 删除消息队列
+    // 4. 删除消息队列
     osal_msg_queue_delete(g_sio_irq_msg_queue);
     g_sio_irq_msg_queue = 0;
     
-    // 4. 释放内存
+    // 5. 释放内存
     if (g_sio_ping_pong_buffers != NULL) {
         for (uint8_t i = 0; i < SIO_PING_PONG_BUFFER_COUNT; i++) {
             if (g_sio_ping_pong_buffers[i].data != NULL) {
@@ -759,11 +839,11 @@ errcode_t sio_interrupt_disable(void)
         printf("[SIO] Memory freed\n");
     }
     
-    // 5. 清零统计
+    // 6. 清零统计
     memset(&g_sio_stats, 0, sizeof(sio_stats_t));
     
 #ifdef SUPPORT_PPP_SERVICE_INTERRUPT
-    // 6. 清理 CAT1 vote(如果之前添加过)
+    // 7. 清理 CAT1 vote(如果之前添加过)
     if (g_sio_cat1_vote_added) {
         cat1_api_remove_cat1_vote();
         g_sio_cat1_vote_added = false;
index 8095031..be8ccc1 100644 (file)
@@ -76,8 +76,8 @@ set(PUBLIC_DEFINES
     PPP_IPV4_SUPPORT=1
     # 启用LCP相关功能
     LCP_SUPPORT=1
-    LCP_ECHOINTERVAL=30
-    LCP_MAXECHOFAILS=3
+    LCP_ECHOINTERVAL=120
+    LCP_MAXECHOFAILS=4
     # 启用网络接口状态回调
     LWIP_NETIF_STATUS_CALLBACK=1
     # PPP_OPTIONS=1 
@@ -85,6 +85,12 @@ set(PUBLIC_DEFINES
     SUPPORT_AT_PPP
 )
 
+# 配置       推荐范围    说明
+# lcp_echo_interval    10 ~ 120秒     太短频繁,太长不及时
+# lcp_echo_fails       2 ~ 10次       太少误判,太多延迟高
+# 实用最大值      120秒 × 10次 = 20分钟      合理的最大超时
+# 理论最大值      255秒 × 255次 = 18小时     不推荐
+
 set(PRIVATE_DEFINES
 )
 
index 5e617eb..711e516 100644 (file)
@@ -2458,11 +2458,24 @@ static errcode_t at_ppp_set_config_baudrate(uint8_t *para, uint32_t paraLen, cha
     // 设置新的波特率到配置模型
     set_ppp_baudrate_from_config_model(baudrate);
     
-    ppp_print_info("PPP SET BAUDRATE: Successfully set PPP baudrate from %d to %d in config model\n", 
-                  current_baudrate, baudrate);
-    ppp_print_info("PPP SET BAUDRATE: Note: This change will take effect after PPP module restart\n");
-    errcode_t rett = ppp_api_set_baudrate_in_all_mode(baudrate); 
-    ppp_print_info("PPP SET BAUDRATE: Successfully set PPP baudrate to %d, ret: %d\n ", baudrate,rett);
+    ppp_print_info("PPP SET BAUDRATE: Config model updated: %d -> %d\n", current_baudrate, baudrate);
+    
+    // 尝试动态更新 Modem 波特率(如果 PPP 在线会先暂停再恢复)
+    ppp_print_info("PPP SET BAUDRATE: Attempting to set Modem baudrate to %d...\n", baudrate);
+    errcode_t rett = ppp_api_set_baudrate_in_all_mode(baudrate);
+    
+    if (rett == ERRCODE_SUCC) {
+        ppp_print_info("PPP SET BAUDRATE: Successfully set Modem baudrate to %d\n", baudrate);
+        ppp_print_info("PPP SET BAUDRATE: Baudrate change completed successfully\n");
+    } else {
+        ppp_print_err("PPP SET BAUDRATE: Failed to set Modem baudrate (ret: %d)\n", ret);
+        ppp_print_err("PPP SET BAUDRATE: Config model updated, but Modem baudrate NOT changed\n");
+        ppp_print_err("PPP SET BAUDRATE: Please restart PPP module to apply the new baudrate\n");
+        // 虽然 Modem 设置失败,但配置已更新,下次重启会生效
+        // 根据需求可以选择返回 ERRCODE_SUCC 或 ERRCODE_FAIL
+        return ERRCODE_FAIL;  // 返回失败,让用户知道当前操作未完全成功
+    }
+    
     return ERRCODE_SUCC;
 }
 
index 7e5ba60..a9f3fc2 100644 (file)
@@ -295,6 +295,17 @@ bool ppp_lwip_is_receive_thread_running(void);
  */
 errcode_t ppp_get_network_signal_info(void);
 
+/**
+ * @brief 启用4G模块电源省电模式
+ * @return ERRCODE_SUCC-成功,其他-失败
+ * 
+ * 说明:
+ * - 执行 AT+NWENPWRSAVE=2 命令
+ * - 配置为:低电平唤醒,高电平进入休眠
+ * - 必须在AT模式下调用
+ */
+errcode_t ppp_enable_power_save_mode(void);
+
 /* ========== PPP字节丢弃统计接口 ========== */
 
 /**
index 9fddb4a..24452b1 100644 (file)
@@ -104,6 +104,48 @@ bool ppp_interrupt_wait_for_safe_switch(uint32_t timeout_ms);
  */
 void ppp_interrupt_print_data_state(void);
 
+/**
+ * @brief 暂停PPP中断处理(用于临时切换到AT模式)
+ * @return PPP_ERR_OK 成功,其他值失败
+ * 
+ * 极简实现(只需停止 UART 中断):
+ * 1. 注销UART中断回调 → 无新数据
+ * 2. Timer 继续运行 → 自动 flush 残留数据
+ * 3. 线程自然阻塞 → 使用 OSAL_MSGQ_WAIT_FOREVER
+ * 
+ * 优势:
+ * - 代码极简(只有一行操作)
+ * - 自动处理(Timer 自动 flush,线程自动阻塞)
+ * - 资源保留(所有资源保持,包括 Timer)
+ * 
+ * 注意:
+ * - 与 ppp_interrupt_disable() 不同,本函数保留所有资源
+ * - 必须与 ppp_interrupt_resume() 配对使用
+ * - Timer 持续运行保证数据按顺序 flush
+ */
+ppp_error_t ppp_interrupt_pause(void);
+
+/**
+ * @brief 恢复PPP中断处理(从AT模式返回)
+ * @return PPP_ERR_OK 成功,其他值失败
+ * 
+ * 极简实现(只需重新注册 UART 中断):
+ * 1. 注册UART中断回调 → 开始接收新数据
+ * 2. 线程自动唤醒 → 从阻塞队列读取中返回
+ * 3. Timer 一直在运行 → 无需重启
+ * 
+ * 优势:
+ * - 代码极简(只有一行操作)
+ * - 自动唤醒(线程自动响应新数据)
+ * - 无需清理(Timer 已经处理过了)
+ * 
+ * 注意:
+ * - 与 ppp_interrupt_pause() 配对使用
+ * - 不重新创建任何资源,只恢复运行状态
+ * - Buffer 已经被 Timer 自动 flush 干净
+ */
+ppp_error_t ppp_interrupt_resume(void);
+
 #ifdef __cplusplus
 }
 #endif
index c660089..7565a40 100644 (file)
@@ -245,6 +245,8 @@ errcode_t zhihui_ppp_init(const ppp_config_t *config)
     
     ppp_get_network_signal_info();
 
+    ppp_enable_power_save_mode();
+
     ppp_start_dial();
 #endif
 
@@ -660,24 +662,39 @@ errcode_t ppp_ppp_mode_pause(void)
     PPP_DEBUG_INFO("Stopping PPP connection monitor...");
     ppp_lwip_stop_connection_monitor();
     
-    // 4. 注销中断回调/停止接收线程
+    // 4. 暂停中断处理/停止接收线程
+    //    使用暂停而不是禁用,保留所有资源(队列、缓冲区、统计信息)
 #if PPP_USE_INTERRUPT_MODE
-    PPP_DEBUG_INFO("Disabling PPP interrupt...");
-    ppp_interrupt_disable();
+    PPP_DEBUG_INFO("Pausing PPP interrupt (preserving resources)...");
+    ret = ppp_interrupt_pause();  // 使用 pause 而不是 disable
+    if (ret != PPP_ERR_OK) {
+        PPP_DEBUG_ERROR("Failed to pause PPP interrupt: %d", ret);
+        return ERRCODE_FAIL;
+    }
 #else
     PPP_DEBUG_INFO("Stopping PPP receive thread...");
     ppp_lwip_stop_receive_thread();
 #endif
     
     // 5. 切换串口模式为AT模式
+    //    重要:切换模式时不会清除 PPP 状态数据(如缓冲区)
+    //    因为第4步已经确保所有缓冲区都已处理完毕
     PPP_DEBUG_INFO("Switching serial mode to AT...");
     ret = ppp_serial_switch_to_at_mode();
     if (ret != PPP_ERR_OK) {
         PPP_DEBUG_ERROR("Failed to switch to AT mode: %d", ret);
+        
+        // 恢复:重新启动中断
+#if PPP_USE_INTERRUPT_MODE
+        ppp_interrupt_resume();
+#else
+        ppp_lwip_start_receive_thread();
+#endif
         return ERRCODE_FAIL;
     }
     
     PPP_DEBUG_CRITICAL("=== PPP Paused - Now in AT Mode ===");
+    PPP_DEBUG_INFO("Resources preserved: Buffers, Queue, Stats");
     PPP_DEBUG_INFO("Use ppp_ppp_mode_resume() to return to PPP data mode");
     
     return ERRCODE_SUCC;
@@ -755,12 +772,15 @@ errcode_t ppp_ppp_mode_resume(void)
         return ERRCODE_FAIL;
     }
     
-    // 5. 注册中断回调/启动接收线程
+    // 5. 恢复中断处理/启动接收线程
+    //    使用恢复而不是重新启用,不重新创建任何资源
 #if PPP_USE_INTERRUPT_MODE
-    PPP_DEBUG_INFO("Re-enabling PPP interrupt...");
-    ret = ppp_interrupt_enable();
+    PPP_DEBUG_INFO("Resuming PPP interrupt (restoring state)...");
+    ret = ppp_interrupt_resume();  // 使用 resume 而不是 enable
     if (ret != PPP_ERR_OK) {
-        PPP_DEBUG_ERROR("Failed to enable PPP interrupt: %d", ret);
+        PPP_DEBUG_ERROR("Failed to resume PPP interrupt: %d", ret);
+        // 如果恢复失败,尝试切换回 AT 模式
+        ppp_serial_switch_to_at_mode();
         return ERRCODE_FAIL;
     }
 #else
@@ -1951,17 +1971,30 @@ errcode_t ppp_api_set_baudrate_in_all_mode(uint32_t baudrate)
         need_deinit_at = 0;
     }
     
-    // 3. 执行 AT 命令更新信息
-    PPP_DEBUG_CRITICAL("Starting baudrate set... %u",baudrate);
-    if(ppp_at_set_baudrate(baudrate) != PPP_ERR_OK) {
-        if (ret != PPP_ERR_OK) {
-            ret = ppp_at_set_baudrate(baudrate);
-            if (ret != PPP_ERR_OK) {
-                PPP_DEBUG_ERROR("Failed to set baudrate %u", baudrate);
-                ret = ERRCODE_FAIL;
+    // 3. 执行 AT 命令设置波特率
+    PPP_DEBUG_CRITICAL("Starting baudrate set... %u", baudrate);
+    ppp_error_t at_ret = ppp_at_set_baudrate(baudrate);
+    if (at_ret != PPP_ERR_OK) {
+        PPP_DEBUG_WARN("First attempt to set baudrate failed, retrying...");
+        // 重试一次
+        at_ret = ppp_at_set_baudrate(baudrate);
+        if (at_ret != PPP_ERR_OK) {
+            PPP_DEBUG_ERROR("Failed to set baudrate %u after retry (error: %d)", baudrate, at_ret);
+            ret = ERRCODE_FAIL;
+            
+            // 如果设置失败,尝试恢复原状态
+            if (need_resume) {
+                if (need_deinit_at) {
+                    ppp_at_handler_deinit();
+                }
+                ppp_ppp_mode_resume();
             }
+            return ret;
+        } else {
+            PPP_DEBUG_INFO("Baudrate set successfully on retry");
         }
-        
+    } else {
+        PPP_DEBUG_INFO("Baudrate set successfully: %u", baudrate);
     }
 
     // 4. 如果需要,去初始化 AT handler 并恢复 PPP 模式
@@ -3131,6 +3164,56 @@ errcode_t ppp_get_network_signal_info(void)
     return ERRCODE_SUCC;
 }
 
+/**
+ * @brief 启用4G模块电源省电模式
+ * @return ERRCODE_SUCC-成功,其他-失败
+ * 
+ * 功能说明:
+ * - 执行 AT+NWENPWRSAVE=2 命令
+ * - 配置为:低电平唤醒,高电平进入休眠
+ * - 必须在AT模式下调用
+ */
+errcode_t ppp_enable_power_save_mode(void)
+{
+    // 检查是否在AT模式
+    if (ppp_serial_get_mode() != PPP_SERIAL_MODE_AT || !get_at_communication_and_baudrate_flag()) {
+        PPP_DEBUG_WARN("Cannot enable power save mode: not in AT mode (current mode: %d)", ppp_serial_get_mode());
+        return ERRCODE_FAIL;
+    }
+    
+    PPP_DEBUG_INFO("Enabling power save mode (AT+NWENPWRSAVE=1)...");
+    
+    // 分配响应缓冲区
+    char *response = (char *)malloc(512);
+    if (response == NULL) {
+        PPP_DEBUG_ERROR("Failed to allocate memory for AT response");
+        return ERRCODE_FAIL;
+    }
+    
+    // 发送AT+NWENPWRSAVE=2命令
+    uint32_t response_len;
+    errcode_t ret = ppp_execute_at_command("AT+NWENPWRSAVE=1", response, 512, &response_len, 5000);
+    if (ret != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to send AT+NWENPWRSAVE command");
+        free(response);
+        return ret;
+    }
+    
+    // 检查响应是否包含OK
+    if (strstr(response, "OK") == NULL) {
+        PPP_DEBUG_ERROR("AT+NWENPWRSAVE command failed, response: %s", response);
+        free(response);
+        return ERRCODE_FAIL;
+    }
+    
+    free(response);
+    
+    PPP_DEBUG_CRITICAL("Power save mode enabled successfully");
+    PPP_DEBUG_INFO("  Mode: High level wakes up, Low level enters sleep");
+    
+    return ERRCODE_SUCC;
+}
+
 
 /**
  * @brief PPP模块挂起 - 用于系统休眠,暂停线程和定时器
index 040f0f4..38916fd 100644 (file)
@@ -439,11 +439,21 @@ static void ppp_timeout_timer_callback(unsigned long arg)
         return;
     }
     
-    // ⚠️ 新增:检查所有buffer的超时状态(包括当前buffer)
-    // 需求4:超时时如果当前buffer未满也要flush,避免数据滞留
+    // ⚠️ 检查所有buffer的超时状态并按写入顺序 flush
+    // ⚠️ 关键修复:必须按 first_write_time 排序,避免数据乱序
+    
+    // 第1步:更新时间戳并收集超时的 buffer
+    typedef struct {
+        uint8_t index;
+        uint32_t write_time;
+        bool is_current;
+    } buffer_order_t;
+    
+    buffer_order_t buffers_to_flush[PPP_PING_PONG_BUFFER_COUNT];
+    uint8_t flush_count = 0;
+    
     for (uint8_t i = 0; i < PPP_PING_PONG_BUFFER_COUNT; i++) {
         // ⚠️ 竞态检测:在处理过程中再次检查current_idx是否变化
-        // 如果这个buffer变成current了,需要小心处理
         uint8_t current_check = g_current_buffer_index;
         bool is_current = (i == current_check);
         
@@ -466,28 +476,51 @@ static void ppp_timeout_timer_callback(unsigned long arg)
                 uint32_t elapsed = current_time - buffer->last_write_time;
                 
                 if (elapsed >= PPP_PING_PONG_TIMEOUT_MS) {
-                    // ⚠️ 超时了!即使未满也要flush(需求4)
-                    PPP_DEBUG_DEBUG("[TIMER] Flushing buffer[%u] due to timeout (%u ms), %s, %u bytes", 
+                    // 记录需要 flush 的 buffer
+                    buffers_to_flush[flush_count].index = i;
+                    buffers_to_flush[flush_count].write_time = buffer->first_write_time;
+                    buffers_to_flush[flush_count].is_current = is_current;
+                    flush_count++;
+                    
+                    PPP_DEBUG_DEBUG("[TIMER] Buffer[%u] timeout (%u ms), %s, %u bytes", 
                                    i, elapsed, 
                                    is_current ? "CURRENT" : "non-current",
                                    buffer->write_pos);
-                    
-                    // 使用internal函数,标记为超时flush
-                    if (ppp_flush_buffer_internal(i, true) == PPP_ERR_OK) {
-                        g_pingpong_stats.buffer_timeout_count++;
-                        
-                        // 如果是当前buffer超时被flush,需要切换到下一个
-                        if (is_current) {
-                            uint8_t next_idx = (i + 1) % PPP_PING_PONG_BUFFER_COUNT;
-                            ppp_ping_pong_buffer_t *next_buffer = &g_ping_pong_buffers[next_idx];
-                            
-                            if (next_buffer->state == PPP_BUFFER_STATE_EMPTY) {
-                                g_current_buffer_index = next_idx;
-                                g_pingpong_stats.buffer_switch_count++;
-                                PPP_DEBUG_INFO("[TIMER] Switched from buffer[%u] to buffer[%u] after timeout", i, next_idx);
-                            }
-                        }
-                    }
+                }
+            }
+        }
+    }
+    
+    // 第2步:按 first_write_time 排序
+    for (uint8_t i = 0; i < flush_count; i++) {
+        for (uint8_t j = i + 1; j < flush_count; j++) {
+            if (buffers_to_flush[j].write_time < buffers_to_flush[i].write_time) {
+                buffer_order_t temp = buffers_to_flush[i];
+                buffers_to_flush[i] = buffers_to_flush[j];
+                buffers_to_flush[j] = temp;
+            }
+        }
+    }
+    
+    // 第3步:按排序后的顺序 flush
+    for (uint8_t i = 0; i < flush_count; i++) {
+        uint8_t idx = buffers_to_flush[i].index;
+        
+        PPP_DEBUG_DEBUG("[TIMER] Flushing buffer[%u] (write_order=%u) due to timeout", idx, i);
+        
+        // 使用internal函数,标记为超时flush
+        if (ppp_flush_buffer_internal(idx, true) == PPP_ERR_OK) {
+            g_pingpong_stats.buffer_timeout_count++;
+            
+            // 如果是当前buffer超时被flush,需要切换到下一个
+            if (buffers_to_flush[i].is_current) {
+                uint8_t next_idx = (idx + 1) % PPP_PING_PONG_BUFFER_COUNT;
+                ppp_ping_pong_buffer_t *next_buffer = &g_ping_pong_buffers[next_idx];
+                
+                if (next_buffer->state == PPP_BUFFER_STATE_EMPTY) {
+                    g_current_buffer_index = next_idx;
+                    g_pingpong_stats.buffer_switch_count++;
+                    PPP_DEBUG_INFO("[TIMER] Switched from buffer[%u] to buffer[%u] after timeout", idx, next_idx);
                 }
             }
         }
@@ -961,19 +994,48 @@ ppp_error_t ppp_interrupt_disable(void)
     // osal_msg_queue_delete 不会清零队列句柄,但下次 create 需要它为 0
     g_ppp_irq_msg_queue = 0;
     
-    // 5. ⚠️ 数据安全:在清空前,先flush所有有数据的buffer,确保数据不丢失
+    // 5. ⚠️ 数据安全:在清空前,先按写入顺序flush所有有数据的buffer,确保数据不丢失
     if (g_ping_pong_buffers != NULL) {
-        PPP_DEBUG_INFO("Flushing all buffers before disable...");
+        PPP_DEBUG_INFO("Flushing all buffers in write order before disable...");
+        
+        // 收集并排序(与 pause 相同的逻辑,确保顺序正确)
+        typedef struct {
+            uint8_t index;
+            uint32_t write_time;
+        } buffer_order_t;
+        
+        buffer_order_t buffers_to_flush[PPP_PING_PONG_BUFFER_COUNT];
+        uint8_t flush_count = 0;
+        
         for (uint8_t i = 0; i < PPP_PING_PONG_BUFFER_COUNT; i++) {
             if (g_ping_pong_buffers[i].state == PPP_BUFFER_STATE_FILLING && 
                 g_ping_pong_buffers[i].write_pos > 0) {
-                PPP_DEBUG_INFO("  Flushing buffer[%u]: %u bytes", 
-                              i, g_ping_pong_buffers[i].write_pos);
-                ppp_flush_buffer(i);
-                // 给处理线程一点时间处理最后的数据
-                osal_msleep(50);
+                buffers_to_flush[flush_count].index = i;
+                buffers_to_flush[flush_count].write_time = g_ping_pong_buffers[i].first_write_time;
+                flush_count++;
             }
         }
+        
+        // 按 first_write_time 排序
+        for (uint8_t i = 0; i < flush_count; i++) {
+            for (uint8_t j = i + 1; j < flush_count; j++) {
+                if (buffers_to_flush[j].write_time < buffers_to_flush[i].write_time) {
+                    buffer_order_t temp = buffers_to_flush[i];
+                    buffers_to_flush[i] = buffers_to_flush[j];
+                    buffers_to_flush[j] = temp;
+                }
+            }
+        }
+        
+        // 按正确顺序 flush
+        for (uint8_t i = 0; i < flush_count; i++) {
+            uint8_t idx = buffers_to_flush[i].index;
+            PPP_DEBUG_INFO("  Flushing buffer[%u] (write_order=%u): %u bytes", 
+                          idx, i, g_ping_pong_buffers[idx].write_pos);
+            ppp_flush_buffer(idx);
+            // 给处理线程一点时间处理最后的数据
+            osal_msleep(50);
+        }
     }
     
     // 6. 清空乒乓缓冲区
@@ -1366,4 +1428,132 @@ void ppp_interrupt_print_data_state(void)
     }
     
     PPP_DEBUG_WARN("╚═══════════════════════════════════════╝");
+}
+
+/**
+ * @brief 暂停PPP中断处理(用于临时切换到AT模式)
+ * @return PPP_ERR_OK 成功,其他值失败
+ * 
+ * 极简设计(只需停止 UART 中断):
+ * 
+ * 关键机制:
+ * 1. UART 中断停止 → 无新数据写入 buffer
+ * 2. Timer 继续运行 → 自动 flush buffer 中的残留数据
+ * 3. 线程继续运行 → 自动处理完队列后阻塞(使用 OSAL_MSGQ_WAIT_FOREVER)
+ * 
+ * 不需要:
+ * - ✗ 不停止 timer(让它自动 flush)
+ * - ✗ 不手动 flush buffer(timer 会处理)
+ * - ✗ 不等待队列排空(线程会自动阻塞)
+ * - ✗ 不暂停线程(阻塞队列读取是天然的暂停)
+ * 
+ * 资源保留:
+ * - ✓ 消息队列
+ * - ✓ 乒乓缓冲区
+ * - ✓ 统计信息
+ * - ✓ 定时器(继续运行)
+ * - ✓ 线程(自动阻塞)
+ */
+ppp_error_t ppp_interrupt_pause(void)
+{
+    if (!g_ppp_interrupt_enabled) {
+        PPP_DEBUG_WARN("PPP interrupt not enabled, nothing to pause");
+        return PPP_ERR_OK;
+    }
+    
+    PPP_DEBUG_CRITICAL("=== PPP Interrupt Pause (Minimal) ===");
+    
+    // ========== 唯一操作: 注销UART中断回调 ==========
+    uint8_t uart_bus = ppp_serial_get_uart_bus();
+    uapi_uart_unregister_rx_callback(uart_bus);
+    PPP_DEBUG_INFO("UART RX callback unregistered");
+    
+    // ========== 自动处理机制(无需手动操作)==========
+    // ✓ Timer 继续运行(g_ppp_timer_running 保持 true)
+    //   → 定期检查 buffer(30ms 周期)
+    //   → 发现有数据就自动 flush 到队列
+    //   → 最终所有 buffer 都会被 flush 干净
+    //   → buffer 按 first_write_time 排序 flush,保证顺序正确
+    //
+    // ✓ 线程继续运行(使用 OSAL_MSGQ_WAIT_FOREVER)
+    //   → 从队列读取并处理所有消息
+    //   → 队列为空时自然阻塞在 osal_msg_queue_read_copy()
+    //   → 无需 suspend 线程,阻塞读取就是天然的暂停
+    //
+    // ✓ 资源完全保留
+    //   → 消息队列保持
+    //   → 乒乓缓冲区保持
+    //   → 统计信息保持
+    //   → 定时器保持运行
+    //   → 线程保持(阻塞状态)
+    
+    PPP_DEBUG_INFO("Timer auto-flushes residual data, thread auto-blocks on empty queue");
+    PPP_DEBUG_CRITICAL("=== PPP Interrupt Paused ===");
+    PPP_DEBUG_INFO("All resources preserved, ready for quick resume");
+    
+    return PPP_ERR_OK;
+}
+
+/**
+ * @brief 恢复PPP中断处理(从AT模式返回)
+ * @return PPP_ERR_OK 成功,其他值失败
+ * 
+ * 极简设计(只需重新注册 UART 中断):
+ * 
+ * 关键机制:
+ * 1. Timer 一直在运行 → 已自动 flush 所有 buffer
+ * 2. 线程自动唤醒 → 当新数据到达队列时自动从阻塞中返回
+ * 3. Buffer 已经干净 → Timer 已经 flush 过了
+ * 
+ * 不需要:
+ * - ✗ 不验证资源(pause 时保留了所有资源)
+ * - ✗ 不清理 buffer(timer 已经 flush 过了)
+ * - ✗ 不重启 timer(一直在运行)
+ * - ✗ 不恢复线程(会自动唤醒)
+ */
+ppp_error_t ppp_interrupt_resume(void)
+{
+    if (!g_ppp_interrupt_enabled) {
+        PPP_DEBUG_ERROR("PPP interrupt not enabled, cannot resume");
+        return PPP_ERR_INVALID_STATE;
+    }
+    
+    PPP_DEBUG_CRITICAL("=== PPP Interrupt Resume (Minimal) ===");
+    
+    // ========== 唯一操作: 重新注册UART中断回调 ==========
+    uint8_t uart_bus = ppp_serial_get_uart_bus();
+    
+    errcode_t ret = uapi_uart_register_rx_callback(
+        uart_bus,
+        UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE,
+        8,  // 阈值:8 字节
+        ppp_uart_rx_interrupt_handler
+    );
+    
+    if (ret != ERRCODE_SUCC) {
+        PPP_DEBUG_ERROR("Failed to register UART RX callback: 0x%x", ret);
+        return PPP_ERR_UART;
+    }
+    
+    PPP_DEBUG_INFO("UART RX callback registered");
+    
+    // ========== 自动唤醒机制(无需手动操作)==========
+    // ✓ Timer 一直在运行
+    //   → 已经自动 flush 了所有残留数据
+    //   → Buffer 已经是干净的
+    //
+    // ✓ 线程自动唤醒
+    //   → 当新数据到达时,UART 中断写入队列
+    //   → 线程自动从 osal_msg_queue_read_copy() 返回
+    //   → 继续处理新数据
+    //
+    // ✓ 状态自然恢复
+    //   → 无需手动清理或验证
+    //   → 系统自动进入正常工作状态
+    
+    PPP_DEBUG_INFO("Timer kept running, thread auto-wakes on new data");
+    PPP_DEBUG_CRITICAL("=== PPP Interrupt Resumed ===");
+    PPP_DEBUG_INFO("Ready to receive PPP data");
+    
+    return PPP_ERR_OK;
 } 
\ No newline at end of file
index 74d86b0..c9af10a 100644 (file)
@@ -55,10 +55,10 @@ static ppp_ctl_pin_4g_t g_ppp_cat1_ctl_pins = {
     1,
     S_AGPIO_L24,//reset_pin
     1, //电路中 控制gpio拉高,reset才拉低
-    S_MGPIO21,//ring_pin
+    S_MGPIO21,//ring_pin GPIO_16
     2,
-    S_MGPIO22,//wakemodem_pin
-    0,
+    S_MGPIO22,//wakemodem_pin GPIO_17 Main_DTR
+    1,
     #else
     PIN_NONE, //bat_pin
     1,
@@ -360,7 +360,7 @@ void ppp_api_wakeup_cat1(uint8_t wakeup)
         } else {
             uapi_gpio_set_val(wake_pin, GPIO_LEVEL_HIGH);
         }
-        PPP_DEBUG_PRINT("PPP: Release wakeup Cat1 pin %d\n", wake_pin);
+        PPP_DEBUG_PRINT("PPP: Release Wakeup Cat1 pin %d\n", wake_pin);
     }
 }