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);
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++;
}
}
}
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};
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) {
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;
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
SUPPORT_AT_PPP
)
+# 配置 推荐范围 说明
+# lcp_echo_interval 10 ~ 120秒 太短频繁,太长不及时
+# lcp_echo_fails 2 ~ 10次 太少误判,太多延迟高
+# 实用最大值 120秒 × 10次 = 20分钟 合理的最大超时
+# 理论最大值 255秒 × 255次 = 18小时 不推荐
+
set(PRIVATE_DEFINES
)
// 设置新的波特率到配置模型
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;
}
*/
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字节丢弃统计接口 ========== */
/**
*/
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
ppp_get_network_signal_info();
+ ppp_enable_power_save_mode();
+
ppp_start_dial();
#endif
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;
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
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 模式
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模块挂起 - 用于系统休眠,暂停线程和定时器
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);
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);
}
}
}
// 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. 清空乒乓缓冲区
}
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
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,
} 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);
}
}