From: 孙春阳 Date: Thu, 23 Oct 2025 08:36:26 +0000 (+0800) Subject: 优化ppp及sio的bufferflush机制,保证flsuh顺序。 X-Git-Tag: V1.0.9_20251128_194606_LN~180^2~4^2~2 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=bc671a6a7340d0030f73b809a60060734f4d47e7;p=mirrors%2FFD230.git 优化ppp及sio的bufferflush机制,保证flsuh顺序。 优化ppp的AT指令执行时的pause和resume逻辑,更顺畅,不报错。 --- diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/arch/sio.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/arch/sio.c index 6fb9a273d..b94f2a28b 100644 --- a/code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/arch/sio.c +++ b/code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/arch/sio.c @@ -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; diff --git a/code/sdk/zhihui/ppp/CMakeLists.txt b/code/sdk/zhihui/ppp/CMakeLists.txt index 80950319c..be8ccc17e 100644 --- a/code/sdk/zhihui/ppp/CMakeLists.txt +++ b/code/sdk/zhihui/ppp/CMakeLists.txt @@ -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 ) diff --git a/code/sdk/zhihui/ppp/example/ppp_at_cmd.c b/code/sdk/zhihui/ppp/example/ppp_at_cmd.c index 5e617eb8c..711e516c7 100644 --- a/code/sdk/zhihui/ppp/example/ppp_at_cmd.c +++ b/code/sdk/zhihui/ppp/example/ppp_at_cmd.c @@ -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; } diff --git a/code/sdk/zhihui/ppp/include/ppp_api.h b/code/sdk/zhihui/ppp/include/ppp_api.h index 7e5ba60ba..a9f3fc239 100644 --- a/code/sdk/zhihui/ppp/include/ppp_api.h +++ b/code/sdk/zhihui/ppp/include/ppp_api.h @@ -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字节丢弃统计接口 ========== */ /** diff --git a/code/sdk/zhihui/ppp/include/ppp_interrupt.h b/code/sdk/zhihui/ppp/include/ppp_interrupt.h index 9fddb4a4c..24452b175 100644 --- a/code/sdk/zhihui/ppp/include/ppp_interrupt.h +++ b/code/sdk/zhihui/ppp/include/ppp_interrupt.h @@ -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 diff --git a/code/sdk/zhihui/ppp/src/ppp_api.c b/code/sdk/zhihui/ppp/src/ppp_api.c index c660089de..7565a405d 100644 --- a/code/sdk/zhihui/ppp/src/ppp_api.c +++ b/code/sdk/zhihui/ppp/src/ppp_api.c @@ -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模块挂起 - 用于系统休眠,暂停线程和定时器 diff --git a/code/sdk/zhihui/ppp/src/ppp_interrupt.c b/code/sdk/zhihui/ppp/src/ppp_interrupt.c index 040f0f489..38916fd4b 100644 --- a/code/sdk/zhihui/ppp/src/ppp_interrupt.c +++ b/code/sdk/zhihui/ppp/src/ppp_interrupt.c @@ -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 diff --git a/code/sdk/zhihui/ppp/src/ppp_power_ctl.c b/code/sdk/zhihui/ppp/src/ppp_power_ctl.c index 74d86b08c..c9af10afc 100644 --- a/code/sdk/zhihui/ppp/src/ppp_power_ctl.c +++ b/code/sdk/zhihui/ppp/src/ppp_power_ctl.c @@ -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); } }