From: 孙春阳 Date: Fri, 10 Oct 2025 13:18:39 +0000 (+0800) Subject: ppp串口接收优化,已经达到目前最优,不考虑头尾,中间数据已经比较稳定。少量丢失数据。921600和1000000速率测试通过。 X-Git-Tag: V1.0.9_20251128_194606_LN~269 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=6b5d1cf1cc39ea788a9e51be7a4d38cc0b7b63db;p=mirrors%2FFD230.git ppp串口接收优化,已经达到目前最优,不考虑头尾,中间数据已经比较稳定。少量丢失数据。921600和1000000速率测试通过。 --- diff --git a/code/sdk/build/config/target_config/brandy/config.py b/code/sdk/build/config/target_config/brandy/config.py index 011853cfd..d55cf336a 100644 --- a/code/sdk/build/config/target_config/brandy/config.py +++ b/code/sdk/build/config/target_config/brandy/config.py @@ -1449,6 +1449,8 @@ target = { 'LCD_JD9885', 'SUPPORT_LEINIAO', 'SKIP_LN_PROTOCOL_TEST', + # 'SUPPORT_PPP_SERVICE_POLLING', + 'SUPPORT_PPP_SERVICE_INTERRUPT', ], #添加自定义应用 'ram_component': [ diff --git a/code/sdk/drivers/chips/brandy/porting/uart/uart_porting.c b/code/sdk/drivers/chips/brandy/porting/uart/uart_porting.c index 7399f2b8b..b7d33971a 100644 --- a/code/sdk/drivers/chips/brandy/porting/uart/uart_porting.c +++ b/code/sdk/drivers/chips/brandy/porting/uart/uart_porting.c @@ -172,41 +172,37 @@ static uart_config_t g_uart_config[UART_BUS_MAX_NUMBER] = { /** UART_BUS_0, which is used to at cmd and debug print in wstp version, * and which is used to testustie and debug print in testsuite version */ - #if defined(SUPPORT_CAT1_SERVICE) + #if defined(SUPPORT_CAT1_SERVICE) || defined(SUPPORT_PPP_SERVICE_POLLING) { { .tx_pin = PIN_NONE, .rx_pin = PIN_NONE, .cts_pin = PIN_NONE, .rts_pin = PIN_NONE - }, + },//pin { .tx_pin_mode = (pin_mode_t)HAL_PIO_FUNC_MAX, .rx_pin_mode = (pin_mode_t)HAL_PIO_FUNC_MAX, .cts_pin_mode = (pin_mode_t)HAL_PIO_FUNC_MAX, .rts_pin_mode = (pin_mode_t)HAL_PIO_FUNC_MAX - }, - UART_0_IRQN, - (osal_irq_handler)irq_uart0_handler, - NULL, + },//pin_mode + UART_0_IRQN,//irq_num + (osal_irq_handler)irq_uart0_handler,//irq_func + NULL,//rx_callback { .baud_rate = 115200, .data_bits = UART_DATA_BIT_8, .parity = UART_PARITY_NONE, .stop_bits = UART_STOP_BIT_1, - }, + },//line_config { .rx_buffer_size = UART_RX_BUFFER_SIZE, .rx_buffer = g_uart0_rx_buffer, }, - NULL, - #if defined(SUPPORT_TEST_COMMON) - false, - #else - true, - #endif + NULL, //uart_mutex + false, //support_mutex }, - #elif defined(SUPPORT_PPP_SERVICE) + #elif defined(SUPPORT_PPP_SERVICE_INTERRUPT) { { .tx_pin = S_MGPIO19, diff --git a/code/sdk/zhihui/ppp/example/ppp_at_cmd.c b/code/sdk/zhihui/ppp/example/ppp_at_cmd.c index 5851e4327..1acaf94c6 100644 --- a/code/sdk/zhihui/ppp/example/ppp_at_cmd.c +++ b/code/sdk/zhihui/ppp/example/ppp_at_cmd.c @@ -926,25 +926,25 @@ static errcode_t at_ppp_show_runtime(uint8_t *para, uint32_t paraLen) (void)para; (void)paraLen; - ppp_print_info("PPP RUNTIME SHOW: Current runtime status:\n"); + ppp_print_warning("PPP RUNTIME SHOW: Current runtime status:\n"); // 显示当前runtime状态 - ppp_print_info("\n=== Current Runtime Status ==="); - ppp_print_info("Power Status: %s", cat1_api_runtime_get_cat1_powerstat() ? "ON" : "OFF"); - ppp_print_info("Network Status: %s", cat1_api_runtime_get_cat1_networkstat() ? "CONNECTED" : "DISCONNECTED"); + ppp_print_warning("\n=== Current Runtime Status ==="); + ppp_print_warning("Power Status: %s", cat1_api_runtime_get_cat1_powerstat() ? "ON" : "OFF"); + ppp_print_warning("Network Status: %s", cat1_api_runtime_get_cat1_networkstat() ? "CONNECTED" : "DISCONNECTED"); int signal = cat1_api_runtime_get_cat1_signal(); if (signal == 99) { - ppp_print_info("Signal Strength: No Signal (99)"); + ppp_print_warning("Signal Strength: No Signal (99)"); } else { - ppp_print_info("Signal Strength: %d/31 (%s)", signal, + ppp_print_warning("Signal Strength: %d/31 (%s)", signal, signal >= 20 ? "Excellent" : signal >= 15 ? "Good" : signal >= 10 ? "Fair" : signal >= 5 ? "Poor" : "Very Poor"); } - ppp_print_info("SIM Status: %s", cat1_api_runtime_get_cat1_simstat() ? "NORMAL" : "ERROR"); + ppp_print_warning("SIM Status: %s", cat1_api_runtime_get_cat1_simstat() ? "NORMAL" : "ERROR"); uint8_t net_type = cat1_api_runtime_get_cat1_networktype(); const char* net_type_str = "Unknown"; @@ -957,33 +957,33 @@ static errcode_t at_ppp_show_runtime(uint8_t *para, uint32_t paraLen) case 5: net_type_str = "Registered (Roaming)"; break; default: net_type_str = "Invalid"; break; } - ppp_print_info("Network Type: %d (%s)", net_type, net_type_str); + ppp_print_warning("Network Type: %d (%s)", net_type, net_type_str); char* operator = cat1_api_runtime_get_cat1_operator(); - ppp_print_info("Operator: %s", (operator && strlen(operator) > 0) ? operator : "Unknown"); + ppp_print_warning("Operator: %s", (operator && strlen(operator) > 0) ? operator : "Unknown"); char* imei = cat1_api_runtime_get_cat1_imei(); - ppp_print_info("IMEI: %s", (imei && strlen(imei) > 0) ? imei : "Unknown"); + ppp_print_warning("IMEI: %s", (imei && strlen(imei) > 0) ? imei : "Unknown"); char* iccid = cat1_api_runtime_get_cat1_iccid(); - ppp_print_info("ICCID: %s", (iccid && strlen(iccid) > 0) ? iccid : "Unknown"); + ppp_print_warning("ICCID: %s", (iccid && strlen(iccid) > 0) ? iccid : "Unknown"); char* version = cat1_api_runtime_get_cat1_modemver(); - ppp_print_info("Modem Version: %s", (version && strlen(version) > 0) ? version : "Unknown"); + ppp_print_warning("Modem Version: %s", (version && strlen(version) > 0) ? version : "Unknown"); - ppp_print_info("==============================="); + ppp_print_warning("==============================="); // 显示PPP输出统计信息 ppp_lwip_output_stats_t ppp_stats; if (ppp_lwip_get_output_stats(&ppp_stats) == PPP_ERR_OK) { - ppp_print_info("\n=== PPP Output Statistics ==="); - ppp_print_info("Total Output Packets: %llu", ppp_stats.total_packets); - ppp_print_info("Total Output Bytes: %llu", ppp_stats.total_bytes); - ppp_print_info("LCP Packets: %u", ppp_stats.lcp_packets); - ppp_print_info("IPCP Packets: %u", ppp_stats.ipcp_packets); - ppp_print_info("IP Packets: %u", ppp_stats.ip_packets); - ppp_print_info("Other Protocol Packets: %u", ppp_stats.other_packets); - ppp_print_info("Failed Sends: %u", ppp_stats.failed_sends); + ppp_print_warning("\n=== PPP Output Statistics ==="); + ppp_print_warning("Total Output Packets: %llu", ppp_stats.total_packets); + ppp_print_warning("Total Output Bytes: %llu", ppp_stats.total_bytes); + ppp_print_warning("LCP Packets: %u", ppp_stats.lcp_packets); + ppp_print_warning("IPCP Packets: %u", ppp_stats.ipcp_packets); + ppp_print_warning("IP Packets: %u", ppp_stats.ip_packets); + ppp_print_warning("Other Protocol Packets: %u", ppp_stats.other_packets); + ppp_print_warning("Failed Sends: %u", ppp_stats.failed_sends); // 计算PPP输出统计的运行时间和速率 uint32_t current_time = ppp_stats.last_packet_time; @@ -995,33 +995,36 @@ static errcode_t at_ppp_show_runtime(uint8_t *para, uint32_t paraLen) uint32_t ppp_elapsed_sec = ppp_elapsed_ms / 1000; if (ppp_elapsed_sec > 0) { - ppp_print_info("PPP Stats Duration: %u seconds", ppp_elapsed_sec); + ppp_print_warning("PPP Stats Duration: %u seconds", ppp_elapsed_sec); } if (ppp_elapsed_ms > 0 && ppp_stats.total_bytes > 0) { uint64_t ppp_throughput_bps = (ppp_stats.total_bytes * 1000) / ppp_elapsed_ms; - ppp_print_info("PPP Output Throughput: %llu bytes/s", ppp_throughput_bps); + ppp_print_warning("PPP Output Throughput: %llu bytes/s", ppp_throughput_bps); float ppp_packet_rate = (float)ppp_stats.total_packets * 1000 / ppp_elapsed_ms; - ppp_print_info("PPP Output Packet Rate: %.1f packets/s", ppp_packet_rate); + ppp_print_warning("PPP Output Packet Rate: %.1f packets/s", ppp_packet_rate); } // 计算成功率 uint64_t total_attempts = ppp_stats.total_packets + ppp_stats.failed_sends; if (total_attempts > 0) { float success_rate = (float)ppp_stats.total_packets / total_attempts * 100; - ppp_print_info("PPP Output Success Rate: %.1f%%", success_rate); + ppp_print_warning("PPP Output Success Rate: %.1f%%", success_rate); } - ppp_print_info("============================="); + ppp_print_warning("============================="); } else { - ppp_print_info("\n=== PPP Output Statistics ==="); - ppp_print_info("Failed to get PPP output statistics"); - ppp_print_info("============================="); + ppp_print_warning("\n=== PPP Output Statistics ==="); + ppp_print_warning("Failed to get PPP output statistics"); + ppp_print_warning("============================="); } ppp_lwip_print_discard_stats(); + // 打印中断统计信息 + ppp_interrupt_print_stats(); + return ERRCODE_SUCC; } diff --git a/code/sdk/zhihui/ppp/include/ppp_config.h b/code/sdk/zhihui/ppp/include/ppp_config.h index 159184248..e960eeaf9 100644 --- a/code/sdk/zhihui/ppp/include/ppp_config.h +++ b/code/sdk/zhihui/ppp/include/ppp_config.h @@ -27,7 +27,7 @@ extern "C" { #define SIO_UART_IRQ_NUM UART_0_IRQN // UART IRQ引脚 #endif -#define PPP_UART_BAUDRATE 115200 +#define PPP_UART_BAUDRATE 921600 /* 支持的波特率列表(自适应模式) */ #define PPP_BAUDRATE_AUTO_LIST {9600, 57600, 115200} @@ -70,11 +70,12 @@ extern "C" { /* PPP数据接收线程配置 */ #define PPP_RX_THREAD_STACK_SIZE 4096 // 接收线程栈大小 -#define PPP_RX_THREAD_PRIORITY osPriorityNormal // 接收线程优先级 +#define PPP_RX_THREAD_PRIORITY osPriorityHigh // 接收线程优先级(提高以确保及时读取数据) +#define PPP_PROCESS_THREAD_PRIORITY osPriorityAboveNormal // 处理线程优先级 /* 调试配置 */ #define PPP_DEBUG_ENABLE 1 // 启用调试输出 -#define PPP_DEBUG_LEVEL PPP_DEBUG_LEVEL_INFO // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG) +#define PPP_DEBUG_LEVEL PPP_DEBUG_LEVEL_WARN // 默认只显示警告和错误 (1=ERROR, 2=WARN, 3=INFO, 4=DEBUG) /* * 调试级别说明: diff --git a/code/sdk/zhihui/ppp/src/ppp_api.c b/code/sdk/zhihui/ppp/src/ppp_api.c index 196fb8af1..731587fdf 100644 --- a/code/sdk/zhihui/ppp/src/ppp_api.c +++ b/code/sdk/zhihui/ppp/src/ppp_api.c @@ -142,17 +142,13 @@ errcode_t zhihui_ppp_init(const ppp_config_t *config) // 保存用户配置 memcpy_s(&g_ppp_config, sizeof(ppp_config_t), config, sizeof(ppp_config_t)); - // PPP_DO_SERIAL_TEST 不进行电源初始化, 用于调试串口读取能力测试。 -#if defined(PPP_DO_SERIAL_TEST) && (PPP_DO_SERIAL_TEST == 1) - PPP_DEBUG_PRINT("PPP_DO_SERIAL_TEST is defined, skip power init\n"); -#else ppp_api_power_init(); -#endif + + PPP_DEBUG_WARN("Step 3: Initializing UART for AT mode baudrate: %d\n", g_ppp_config.baudrate); #define PPP_SERIAL_INIT 1 //外接串口调试模组时,串口要设置input防止干扰。 #if PPP_SERIAL_INIT // 3. UART初始化 - PPP_DEBUG_PRINT("Step 3: Initializing UART for AT mode baudrate: %d\n", g_ppp_config.baudrate); ppp_serial_config_t serial_config = { .uart_bus = SIO_UART_BUS, .irq_num = SIO_UART_IRQ_NUM, @@ -860,8 +856,7 @@ errcode_t ppp_stop_dial(void) ppp_interrupt_disable(); PPP_DEBUG_INFO("PPP interrupt mode disabled"); - // 打印中断统计信息 - ppp_interrupt_print_stats(); + #else // 轮询模式:停止接收线程 PPP_DEBUG_INFO("Stopping PPP receive thread..."); diff --git a/code/sdk/zhihui/ppp/src/ppp_at_handler.c b/code/sdk/zhihui/ppp/src/ppp_at_handler.c index f6019ce9e..7c5fad1aa 100644 --- a/code/sdk/zhihui/ppp/src/ppp_at_handler.c +++ b/code/sdk/zhihui/ppp/src/ppp_at_handler.c @@ -998,10 +998,22 @@ static ppp_error_t ppp_at_wait_response(ppp_at_response_t *response, uint32_t t PPP_DEBUG_DEBUG("WAIT_RESPONSE: Attempting UART read #%u...", read_attempts); uint32_t uart_read_start = (uint32_t)osal_get_jiffies(); - int32_t ret = uapi_uart_read(g_at_ctx.uart_bus, - g_at_response_buffer + bytes_received, - g_at_buffer_size - bytes_received - 1, - UART_READ_TIMEOUT_MS); + #if defined(CONFIG_UART_SUPPORT_DMA) + uart_write_dma_config_t dma_cfg = { + .src_width = 0,//UART_DMA_WIDTH, + .dest_width = 0,//UART_DMA_WIDTH, + .burst_length = 2,//UART_DMA_BURST_LENGTH, + .priority = 0//UART_DMA_PRIORITY + }; + int32_t ret = uapi_uart_read_by_dma(g_at_ctx.uart_bus, g_at_response_buffer + bytes_received, + g_at_buffer_size - bytes_received - 1 + , &dma_cfg); + + #else + int32_t ret =uapi_uart_read(g_at_ctx.uart_bus, g_at_response_buffer + bytes_received, + g_at_buffer_size - bytes_received - 1, + UART_READ_TIMEOUT_MS); + #endif uint32_t uart_read_end = (uint32_t)osal_get_jiffies(); uint32_t uart_read_time = osal_jiffies_to_msecs(uart_read_end - uart_read_start); diff --git a/code/sdk/zhihui/ppp/src/ppp_interrupt.c b/code/sdk/zhihui/ppp/src/ppp_interrupt.c index 36583d0cc..00b0ef9a0 100644 --- a/code/sdk/zhihui/ppp/src/ppp_interrupt.c +++ b/code/sdk/zhihui/ppp/src/ppp_interrupt.c @@ -66,7 +66,7 @@ typedef struct { } ppp_irq_msg_t; /* 中断使能标志 */ -static bool g_ppp_interrupt_enabled = false; +static volatile bool g_ppp_interrupt_enabled = false; /* 处理线程相关 */ static osal_task *g_ppp_irq_process_task = NULL; @@ -75,7 +75,10 @@ static volatile bool g_ppp_irq_task_running = false; /* 乒乓缓冲区 */ static ppp_ping_pong_buffer_t g_ping_pong_buffers[PPP_PING_PONG_BUFFER_COUNT]; -static volatile uint8_t g_current_buffer_index = 0; // 当前正在填充的buffer索引 +static volatile uint8_t g_current_buffer_index = 0; + +/* 全局时间戳(由定时器更新,ISR只读) */ +static volatile uint32_t g_timer_timestamp_ms = 0; /* 定时器相关 */ static osal_timer g_ppp_timeout_timer; @@ -94,6 +97,16 @@ typedef struct { static ppp_pingpong_stats_t g_pingpong_stats = {0}; +/* UART错误统计信息 */ +typedef struct { + uint32_t parity_error_count; // 奇偶校验错误次数 + uint32_t frame_error_count; // 帧错误次数 + uint32_t last_parity_error_info[4]; // 最近一次奇偶校验错误信息 + uint32_t last_frame_error_info[4]; // 最近一次帧错误信息 +} ppp_uart_error_stats_t; + +static ppp_uart_error_stats_t g_uart_error_stats = {0}; + /* 外部函数声明(在ppp_lwip.c中实现) */ extern ppp_pcb* ppp_lwip_get_pcb(void); extern bool ppp_lwip_is_initialized(void); @@ -102,6 +115,8 @@ extern bool ppp_lwip_is_initialized(void); static uint32_t ppp_get_current_time_ms(void); static ppp_error_t ppp_flush_buffer(uint8_t buffer_index); static void ppp_timeout_timer_callback(unsigned long arg); +static void ppp_uart_parity_error_callback(uint32_t *err_info, uint32_t len); +static void ppp_uart_frame_error_callback(uint32_t *err_info, uint32_t len); /** * @brief 获取当前时间戳(毫秒) @@ -122,7 +137,9 @@ static void ppp_irq_process_thread(void *arg) ppp_irq_msg_t msg; unsigned int msg_size = sizeof(ppp_irq_msg_t); - PPP_DEBUG_INFO("PPP IRQ process thread started (ping-pong mode)"); + // ⚠️ 使用 WARN 级别确保能看到此日志,用于诊断线程是否正常启动 + PPP_DEBUG_WARN(">>> PPP IRQ process thread STARTED (ping-pong mode) <<<"); + PPP_DEBUG_INFO("Thread running, waiting for messages from ISR..."); #if defined(PPP_DO_SERIAL_TEST) && (PPP_DO_SERIAL_TEST == 1) const uint8_t print_data[] = "PPP IRQ process thread started (ping-pong mode)\n"; uapi_uart_write(SIO_UART_BUS, print_data, sizeof(print_data), 100); @@ -265,6 +282,15 @@ static ppp_error_t ppp_flush_buffer(uint8_t buffer_index) * @note 定时器上下文,应快速返回 * @param arg 定时器参数(未使用) * @warning osal_timer默认是one-shot,必须在回调中手动重启实现周期性执行 + * + * 数据安全设计(避免竞态): + * - 定时器职责:只检查和flush **非当前** buffer(避免与ISR竞争) + * - ISR 职责: + * 1. 在写入前检查当前buffer是否超时(last_write_time == 0),超时则flush+切换 + * 2. 检查当前buffer状态,如果不可写则切换 + * 3. 独占 g_current_buffer_index 的修改权 + * - 分工明确:定时器不操作当前buffer,ISR负责当前buffer的超时处理 + * - 数据保证:disable时强制flush所有buffer,确保数据不丢失 */ static void ppp_timeout_timer_callback(unsigned long arg) { @@ -286,32 +312,40 @@ static void ppp_timeout_timer_callback(unsigned long arg) uint32_t current_time = ppp_get_current_time_ms(); - // 检查所有buffer的超时状态 + // ⚠️ 更新全局时间戳(供ISR使用,ISR中不能调用时间函数) + g_timer_timestamp_ms = current_time; + + uint8_t current_idx = g_current_buffer_index; // 读取一次,避免重复读取 + + // 检查非当前buffer的超时状态 + // ⚠️ 数据安全设计:定时器只处理非当前buffer,避免与ISR竞争 + // 当前buffer的超时由ISR在写入时主动检查并处理 for (uint8_t i = 0; i < PPP_PING_PONG_BUFFER_COUNT; i++) { + // 跳过当前正在写入的buffer(避免与ISR竞争,保证数据安全) + if (i == current_idx) { + continue; + } + ppp_ping_pong_buffer_t *buffer = &g_ping_pong_buffers[i]; // 只处理FILLING状态的buffer if (buffer->state == PPP_BUFFER_STATE_FILLING && buffer->write_pos > 0) { - uint32_t elapsed = current_time - buffer->last_write_time; - - // 超时检查 - if (elapsed >= PPP_PING_PONG_TIMEOUT_MS) { - // 超时,flush buffer - PPP_DEBUG_DEBUG("[TIMER] Flushing buffer[%u] due to timeout (%u ms)", i, elapsed); - if (ppp_flush_buffer(i) == PPP_ERR_OK) { - g_pingpong_stats.buffer_timeout_count++; + // ⚠️ 修复:避免误判超时 + // last_write_time == 0 表示ISR刚写入数据,需要更新时间戳(不是超时) + if (buffer->last_write_time == 0) { + // 第一次看到,更新时间戳,不flush + buffer->last_write_time = current_time; + PPP_DEBUG_DEBUG("[TIMER] Update timestamp for buffer[%u]", i); + } else { + // 已经有时间戳了,检查是否真的超时 + uint32_t elapsed = current_time - buffer->last_write_time; + + if (elapsed >= PPP_PING_PONG_TIMEOUT_MS) { + // 真正超时,flush buffer + PPP_DEBUG_DEBUG("[TIMER] Flushing buffer[%u] due to timeout (%u ms)", i, elapsed); - // ⚠️ 关键修复:Timer flush后切换buffer(类似ISR中buffer满的逻辑) - // 只有当前flush的是正在使用的buffer时才切换 - if (i == g_current_buffer_index) { - 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++; - } - // 如果next_buffer不是EMPTY,保持当前索引,等待其处理完成 + if (ppp_flush_buffer(i) == PPP_ERR_OK) { + g_pingpong_stats.buffer_timeout_count++; } } } @@ -325,6 +359,48 @@ static void ppp_timeout_timer_callback(unsigned long arg) } } +/** + * @brief UART奇偶校验错误回调函数 + * @param err_info 错误信息数组 + * @param len 错误信息数组长度 + * @note 在中断上下文中执行,必须快速返回 + */ +static void ppp_uart_parity_error_callback(uint32_t *err_info, uint32_t len) +{ + g_uart_error_stats.parity_error_count++; + + // 保存错误信息用于诊断(只保存前4个) + if (err_info && len > 0) { + uint32_t copy_len = (len > 4) ? 4 : len; + for (uint32_t i = 0; i < copy_len; i++) { + g_uart_error_stats.last_parity_error_info[i] = err_info[i]; + } + } + + // ⚠️ 中断中禁止打印日志,只统计 +} + +/** + * @brief UART帧错误回调函数 + * @param err_info 错误信息数组 + * @param len 错误信息数组长度 + * @note 在中断上下文中执行,必须快速返回 + */ +static void ppp_uart_frame_error_callback(uint32_t *err_info, uint32_t len) +{ + g_uart_error_stats.frame_error_count++; + + // 保存错误信息用于诊断(只保存前4个) + if (err_info && len > 0) { + uint32_t copy_len = (len > 4) ? 4 : len; + for (uint32_t i = 0; i < copy_len; i++) { + g_uart_error_stats.last_frame_error_info[i] = err_info[i]; + } + } + + // ⚠️ 中断中禁止打印日志,只统计 +} + /** * @brief UART接收中断回调函数(乒乓缓冲区模式) * @note 中断上下文,必须极快返回 @@ -338,12 +414,13 @@ static void ppp_timeout_timer_callback(unsigned long arg) void _ppp_uart_rx_interrupt_handler(const void *buffer, uint16_t length, bool error) { // 快速路径:参数校验 - if (error || !buffer || length == 0 || length > 200) { // 单次中断最多200字节 - if (error) { - g_ppp_interrupt_stats.error_count++; - } + if ( !buffer || length == 0 || length > 200) { // 单次中断最多200字节 + PPP_DEBUG_ERROR("ppp_uart_rx_interrupt_handler !buffer || length == 0 || length > 200 "); return; } + if (error) { + g_ppp_interrupt_stats.error_count++; + } // 更新统计(用于诊断 - 可以在统计打印中看到) g_ppp_interrupt_stats.total_interrupts++; @@ -362,6 +439,52 @@ void _ppp_uart_rx_interrupt_handler(const void *buffer, uint16_t length, bool er uint8_t current_idx = g_current_buffer_index; ppp_ping_pong_buffer_t *current_buffer = &g_ping_pong_buffers[current_idx]; + // ⚠️ 数据安全设计:ISR负责检查当前buffer的超时 + // 定时器只处理非当前buffer,避免竞态 + // + // 检查逻辑(方案2 + 全局时间戳): + // - last_write_time == 0:ISR刚写入,定时器还没更新时间戳(不算超时) + // - last_write_time != 0:定时器已更新过时间戳,可以检查是否真的超时 + // 使用 g_timer_timestamp_ms(定时器每30ms更新一次) + if (current_buffer->state == PPP_BUFFER_STATE_FILLING && + current_buffer->write_pos > 0 && + current_buffer->last_write_time != 0) { // ← 只检查已有时间戳的 + // 使用全局时间戳(ISR中安全,不调用函数) + uint32_t current_time = g_timer_timestamp_ms; + uint32_t elapsed = current_time - current_buffer->last_write_time; + + if (elapsed >= PPP_PING_PONG_TIMEOUT_MS) { + // 当前buffer真的超时了,flush并切换 + ppp_flush_buffer(current_idx); + g_pingpong_stats.buffer_timeout_count++; + + // 切换到下一个buffer + uint8_t next_idx = (current_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++; + current_idx = next_idx; + current_buffer = next_buffer; + } + } + } + + // 如果当前buffer不可写(状态不对),尝试切换 + if (current_buffer->state != PPP_BUFFER_STATE_EMPTY && + current_buffer->state != PPP_BUFFER_STATE_FILLING) { + uint8_t next_idx = (current_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++; + current_idx = next_idx; + current_buffer = next_buffer; + } + } + // 尝试写入当前buffer uint32_t available = PPP_PING_PONG_BUFFER_SIZE - current_buffer->write_pos; @@ -369,13 +492,16 @@ void _ppp_uart_rx_interrupt_handler(const void *buffer, uint16_t length, bool er // buffer是空的,初始化为FILLING状态 current_buffer->state = PPP_BUFFER_STATE_FILLING; current_buffer->write_pos = 0; + available = PPP_PING_PONG_BUFFER_SIZE; // 重新计算可用空间 } if (current_buffer->state == PPP_BUFFER_STATE_FILLING && length <= available) { // 空间足够,直接写入 memcpy(current_buffer->data + current_buffer->write_pos, buffer, length); current_buffer->write_pos += length; - current_buffer->last_write_time = ppp_get_current_time_ms(); + // ⚠️ 关键修复:移除中断中的时间函数调用(可能导致死机) + // current_buffer->last_write_time = ppp_get_current_time_ms(); + current_buffer->last_write_time = 0; // 由定时器回调更新 // 检查是否满了 if (current_buffer->write_pos >= PPP_PING_PONG_BUFFER_SIZE) { @@ -413,7 +539,9 @@ void _ppp_uart_rx_interrupt_handler(const void *buffer, uint16_t length, bool er next_buffer->write_pos = 0; memcpy(next_buffer->data, buffer, length); next_buffer->write_pos = length; - next_buffer->last_write_time = ppp_get_current_time_ms(); + // ⚠️ 关键修复:移除中断中的时间函数调用(可能导致死机) + // next_buffer->last_write_time = ppp_get_current_time_ms(); + next_buffer->last_write_time = 0; // 由定时器回调更新 } else { // 两个buffer都不可用,丢弃数据 g_pingpong_stats.dropped_both_full += length; @@ -470,16 +598,20 @@ ppp_error_t ppp_interrupt_enable(void) PPP_DEBUG_INFO("Message queue created (depth=%d, item_size=%d bytes)", PPP_IRQ_MSG_QUEUE_SIZE, sizeof(ppp_irq_msg_t)); - // 3. 启动处理线程 + // 3. 启动处理线程(使用 osal_kthread_create_ext 创建时指定优先级) + // ⚠️ 关键修复:必须在创建时就指定优先级,否则线程以默认低优先级创建可能永远得不到调度 g_ppp_irq_task_running = true; + osal_kthread_init init_cfg = { + .handler = (osal_kthread_handler)ppp_irq_process_thread, + .data = NULL, + .taskname = "ppp_irq_proc", + .stacksize = PPP_IRQ_PROCESS_STACK_SIZE, + .taskprio = OSAL_TASK_PRIORITY_MIDDLE // 使用中等优先级(6),避免阻塞看门狗 + }; + osal_kthread_lock(); - g_ppp_irq_process_task = osal_kthread_create( - (osal_kthread_handler)ppp_irq_process_thread, - NULL, - "ppp_irq_proc", - PPP_IRQ_PROCESS_STACK_SIZE - ); + g_ppp_irq_process_task = osal_kthread_create_ext(&init_cfg); osal_kthread_unlock(); if (!g_ppp_irq_process_task) { @@ -489,7 +621,10 @@ ppp_error_t ppp_interrupt_enable(void) return PPP_ERR_MEMORY; } - PPP_DEBUG_INFO("Process thread created (stack=%d)", PPP_IRQ_PROCESS_STACK_SIZE); + PPP_DEBUG_WARN("IRQ process thread created @ %p with MIDDLE priority (6)", + g_ppp_irq_process_task); + PPP_DEBUG_INFO("Process thread: stack=%d bytes, priority=MIDDLE(6)", + PPP_IRQ_PROCESS_STACK_SIZE); // 4. 启动定时器(30ms周期检查buffer超时) // 先设置timer的handler、data和interval @@ -526,19 +661,21 @@ ppp_error_t ppp_interrupt_enable(void) PPP_DEBUG_INFO("Registering UART RX callback:"); PPP_DEBUG_INFO(" UART Bus: %d", uart_bus); PPP_DEBUG_INFO(" Condition: 0x07 (FULL_OR_SUFFICIENT_DATA_OR_IDLE)"); - PPP_DEBUG_INFO(" Threshold: 56 bytes"); + PPP_DEBUG_INFO(" Threshold: 8 bytes"); PPP_DEBUG_INFO(" Callback: ppp_uart_rx_interrupt_handler @ %p", ppp_uart_rx_interrupt_handler); - // ⚠️ 关键修复:降低阈值从200到56字节,确保小数据包也能触发中断 - // PPP LCP/IPCP响应通常只有30-50字节,200字节阈值太高会导致无法触发 + // ⚠️ 阈值说明: + // - 参数是直接的字节数(不是编码) + // - 8字节阈值确保小数据包能触发(PPP协商包通常30-50字节) + // - 配合IDLE条件,即使不满8字节也会在数据停止时触发 ret = uapi_uart_register_rx_callback( uart_bus, 0x07, // UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE - 8, // 56字节阈值 + 8, // 阈值:8字节 ppp_uart_rx_interrupt_handler ); - PPP_DEBUG_INFO("uapi_uart_register_rx_callback returned: 0x%x (ERRCODE_SUCC=0x%x)", + PPP_DEBUG_WARN("uapi_uart_register_rx_callback returned: 0x%x (ERRCODE_SUCC=0x%x)", ret, ERRCODE_SUCC); if (ret != ERRCODE_SUCC) { @@ -557,13 +694,32 @@ ppp_error_t ppp_interrupt_enable(void) PPP_DEBUG_INFO("UART RX callback registered successfully!"); + // 6. 注册UART错误回调(用于详细错误诊断) + // 6.1 注册奇偶校验错误回调 + ret = uapi_uart_register_parity_error_callback(uart_bus, ppp_uart_parity_error_callback); + if (ret != ERRCODE_SUCC) { + PPP_DEBUG_WARN("Failed to register parity error callback: 0x%x (non-critical)", ret); + // 不是关键错误,继续运行 + } else { + PPP_DEBUG_INFO("Parity error callback registered"); + } + + // 6.2 注册帧错误回调 + ret = uapi_uart_register_frame_error_callback(uart_bus, ppp_uart_frame_error_callback); + if (ret != ERRCODE_SUCC) { + PPP_DEBUG_WARN("Failed to register frame error callback: 0x%x (non-critical)", ret); + // 不是关键错误,继续运行 + } else { + PPP_DEBUG_INFO("Frame error callback registered"); + } + g_ppp_interrupt_enabled = true; - PPP_DEBUG_INFO("PPP UART interrupt enabled successfully (ping-pong mode)"); - PPP_DEBUG_INFO(" Ping-pong buffers: %d x %d KB", + PPP_DEBUG_WARN("PPP UART interrupt enabled successfully (ping-pong mode)"); + PPP_DEBUG_WARN(" Ping-pong buffers: %d x %d KB", PPP_PING_PONG_BUFFER_COUNT, PPP_PING_PONG_BUFFER_SIZE / 1024); - PPP_DEBUG_INFO(" Timeout: %d ms", PPP_PING_PONG_TIMEOUT_MS); - PPP_DEBUG_INFO(" Message queue: %d slots (only %d bytes per message)", + PPP_DEBUG_WARN(" Timeout: %d ms", PPP_PING_PONG_TIMEOUT_MS); + PPP_DEBUG_WARN(" Message queue: %d slots (only %d bytes per message)", PPP_IRQ_MSG_QUEUE_SIZE, sizeof(ppp_irq_msg_t)); PPP_DEBUG_INFO(" ISR latency: <5μs (memcpy + queue_write, no malloc!)"); PPP_DEBUG_INFO(" Data flow: UART IRQ -> ping-pong buffer -> timer/full flush -> msg_queue -> process_thread -> pppos_input_tcpip"); @@ -618,7 +774,20 @@ ppp_error_t ppp_interrupt_disable(void) // osal_msg_queue_delete 不会清零队列句柄,但下次 create 需要它为 0 g_ppp_irq_msg_queue = 0; - // 5. 清空乒乓缓冲区 + // 5. ⚠️ 数据安全:在清空前,先flush所有有数据的buffer,确保数据不丢失 + PPP_DEBUG_INFO("Flushing all buffers before disable..."); + 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); + } + } + + // 6. 清空乒乓缓冲区 for (uint8_t i = 0; i < PPP_PING_PONG_BUFFER_COUNT; i++) { g_ping_pong_buffers[i].write_pos = 0; g_ping_pong_buffers[i].state = PPP_BUFFER_STATE_EMPTY; @@ -626,6 +795,11 @@ ppp_error_t ppp_interrupt_disable(void) } g_current_buffer_index = 0; + // 7. 清零统计信息(为下次启用准备) + memset(&g_ppp_interrupt_stats, 0, sizeof(ppp_interrupt_stats_t)); + memset(&g_pingpong_stats, 0, sizeof(ppp_pingpong_stats_t)); + memset(&g_uart_error_stats, 0, sizeof(ppp_uart_error_stats_t)); + g_ppp_interrupt_enabled = false; g_ppp_irq_process_task = NULL; @@ -659,7 +833,8 @@ void ppp_interrupt_reset_stats(void) { memset(&g_ppp_interrupt_stats, 0, sizeof(ppp_interrupt_stats_t)); memset(&g_pingpong_stats, 0, sizeof(ppp_pingpong_stats_t)); - PPP_DEBUG_INFO("PPP interrupt statistics reset (including ping-pong stats)"); + memset(&g_uart_error_stats, 0, sizeof(ppp_uart_error_stats_t)); + PPP_DEBUG_INFO("PPP interrupt statistics reset (including ping-pong and UART error stats)"); } /** @@ -667,18 +842,18 @@ void ppp_interrupt_reset_stats(void) */ void ppp_interrupt_print_stats(void) { - PPP_DEBUG_INFO("=== PPP Interrupt Statistics (Ping-Pong Mode) ==="); - PPP_DEBUG_INFO("Interrupt Status: %s", g_ppp_interrupt_enabled ? "ENABLED" : "DISABLED"); - PPP_DEBUG_INFO("Ping-Pong Buffers: %d x %d bytes = %d KB total", + PPP_DEBUG_WARN("=== PPP Interrupt Statistics (Ping-Pong Mode) ==="); + PPP_DEBUG_WARN("Interrupt Status: %s", g_ppp_interrupt_enabled ? "ENABLED" : "DISABLED"); + PPP_DEBUG_WARN("Ping-Pong Buffers: %d x %d bytes = %d KB total", PPP_PING_PONG_BUFFER_COUNT, PPP_PING_PONG_BUFFER_SIZE, (PPP_PING_PONG_BUFFER_COUNT * PPP_PING_PONG_BUFFER_SIZE) / 1024); - PPP_DEBUG_INFO("Timeout: %d ms", PPP_PING_PONG_TIMEOUT_MS); - PPP_DEBUG_INFO("Message Queue: %d slots (%d bytes per message)", + PPP_DEBUG_WARN("Timeout: %d ms", PPP_PING_PONG_TIMEOUT_MS); + PPP_DEBUG_WARN("Message Queue: %d slots (%d bytes per message)", PPP_IRQ_MSG_QUEUE_SIZE, sizeof(ppp_irq_msg_t)); - PPP_DEBUG_INFO(""); + PPP_DEBUG_WARN(""); // 乒乓缓冲区状态 - PPP_DEBUG_INFO("Buffer Status:"); + PPP_DEBUG_WARN("Buffer Status:"); for (uint8_t i = 0; i < PPP_PING_PONG_BUFFER_COUNT; i++) { const char *state_str = "UNKNOWN"; switch (g_ping_pong_buffers[i].state) { @@ -687,39 +862,112 @@ void ppp_interrupt_print_stats(void) case PPP_BUFFER_STATE_FULL: state_str = "FULL"; break; case PPP_BUFFER_STATE_PROCESSING: state_str = "PROCESSING"; break; } - PPP_DEBUG_INFO(" Buffer[%u]: %s, %u/%u bytes%s", + PPP_DEBUG_WARN(" Buffer[%u]: %s, %u/%u bytes%s", i, state_str, g_ping_pong_buffers[i].write_pos, PPP_PING_PONG_BUFFER_SIZE, (i == g_current_buffer_index) ? " (current)" : ""); } - PPP_DEBUG_INFO(""); + PPP_DEBUG_WARN(""); // 乒乓缓冲区统计 - PPP_DEBUG_INFO("Ping-Pong Buffer Statistics:"); - PPP_DEBUG_INFO(" Buffer Full Count: %u", g_pingpong_stats.buffer_full_count); - PPP_DEBUG_INFO(" Buffer Timeout Count: %u", g_pingpong_stats.buffer_timeout_count); - PPP_DEBUG_INFO(" Buffer Switch Count: %u", g_pingpong_stats.buffer_switch_count); - PPP_DEBUG_INFO(" Dropped (Both Full): %u bytes ⚠️", g_pingpong_stats.dropped_both_full); - PPP_DEBUG_INFO(""); - - // 中断统计 - PPP_DEBUG_INFO("Total Interrupts: %u", g_ppp_interrupt_stats.total_interrupts); - PPP_DEBUG_INFO(" - Success: %u", g_ppp_interrupt_stats.success_count); - PPP_DEBUG_INFO(" - Error: %u", g_ppp_interrupt_stats.error_count); - PPP_DEBUG_INFO(" - lwIP Error: %u", g_ppp_interrupt_stats.lwip_error_count); - PPP_DEBUG_INFO(""); - - // 字节统计 - PPP_DEBUG_INFO("Total Bytes Received: %u", g_ppp_interrupt_stats.total_bytes_received); - PPP_DEBUG_INFO("Total Bytes Sent to lwIP: %u", g_ppp_interrupt_stats.total_bytes_sent_to_lwip); - PPP_DEBUG_INFO(""); - - // 丢弃统计 - PPP_DEBUG_INFO("Dropped Bytes:"); - PPP_DEBUG_INFO(" - Not Initialized: %u", g_ppp_interrupt_stats.dropped_not_initialized); - PPP_DEBUG_INFO(" - lwIP Error: %u", g_ppp_interrupt_stats.dropped_lwip_error); - PPP_DEBUG_INFO(" - Queue Full: %u", g_ppp_interrupt_stats.dropped_queue_full); - PPP_DEBUG_INFO(" - Both Buffers Full: %u ⚠️", g_pingpong_stats.dropped_both_full); - PPP_DEBUG_INFO(""); + PPP_DEBUG_WARN("Ping-Pong Buffer Statistics:"); + PPP_DEBUG_WARN(" Buffer Full Count: %u", g_pingpong_stats.buffer_full_count); + PPP_DEBUG_WARN(" Buffer Timeout Count: %u", g_pingpong_stats.buffer_timeout_count); + PPP_DEBUG_WARN(" Buffer Switch Count: %u", g_pingpong_stats.buffer_switch_count); + PPP_DEBUG_WARN(" Dropped (Both Full): %u bytes ⚠️", g_pingpong_stats.dropped_both_full); + PPP_DEBUG_WARN(""); + + // 中断层统计 + PPP_DEBUG_WARN("=== Interrupt Layer Statistics ==="); + PPP_DEBUG_WARN("Total UART Interrupts: %u", g_ppp_interrupt_stats.total_interrupts); + PPP_DEBUG_WARN(" - With RX Error: %u", g_ppp_interrupt_stats.error_count); + PPP_DEBUG_WARN(" · Parity Error: %u", g_uart_error_stats.parity_error_count); + PPP_DEBUG_WARN(" · Frame Error: %u", g_uart_error_stats.frame_error_count); + + // 如果有错误,显示错误率和详情 + if (g_ppp_interrupt_stats.total_interrupts > 0) { + uint32_t avg_bytes_per_irq = g_ppp_interrupt_stats.total_bytes_received / + g_ppp_interrupt_stats.total_interrupts; + PPP_DEBUG_WARN(" Average bytes per interrupt: %u", avg_bytes_per_irq); + + // 错误率 + if (g_ppp_interrupt_stats.error_count > 0) { + uint32_t error_rate = (g_ppp_interrupt_stats.error_count * 10000) / + g_ppp_interrupt_stats.total_interrupts; + PPP_DEBUG_WARN(" Error Rate: %u.%02u%%", error_rate / 100, error_rate % 100); + + // 如果错误率 > 1%,发出警告 + if (error_rate > 100) { + PPP_DEBUG_ERROR(" ⚠️ High error rate! Check UART config and cable."); + } + } + } + + // 如果有奇偶校验错误,打印最近一次的错误信息 + if (g_uart_error_stats.parity_error_count > 0) { + PPP_DEBUG_INFO(" Last Parity Error Info:"); + for (uint32_t i = 0; i < 4; i++) { + if (g_uart_error_stats.last_parity_error_info[i] != 0) { + PPP_DEBUG_INFO(" [%u]: 0x%08X", i, + g_uart_error_stats.last_parity_error_info[i]); + } + } + } + + // 如果有帧错误,打印最近一次的错误信息 + if (g_uart_error_stats.frame_error_count > 0) { + PPP_DEBUG_INFO(" Last Frame Error Info:"); + for (uint32_t i = 0; i < 4; i++) { + if (g_uart_error_stats.last_frame_error_info[i] != 0) { + PPP_DEBUG_INFO(" [%u]: 0x%08X", i, + g_uart_error_stats.last_frame_error_info[i]); + } + } + } + PPP_DEBUG_WARN(""); + + // 消息层统计 + PPP_DEBUG_WARN("=== Message Layer Statistics ==="); + PPP_DEBUG_WARN("Total Messages Sent to lwIP: %u", g_ppp_interrupt_stats.success_count); + PPP_DEBUG_WARN(" - lwIP Accept Error: %u", g_ppp_interrupt_stats.lwip_error_count); + if (g_ppp_interrupt_stats.success_count > 0) { + uint32_t avg_bytes = g_ppp_interrupt_stats.total_bytes_sent_to_lwip / + g_ppp_interrupt_stats.success_count; + PPP_DEBUG_WARN(" Average bytes per message: %u", avg_bytes); + + // 计算中断聚合比例 + if (g_ppp_interrupt_stats.total_interrupts > 0) { + uint32_t irq_per_msg = g_ppp_interrupt_stats.total_interrupts / + g_ppp_interrupt_stats.success_count; + PPP_DEBUG_WARN(" Interrupts per message: ~%u (aggregation ratio)", irq_per_msg); + } + } + PPP_DEBUG_WARN(""); + + // 字节统计(与轮询模式格式一致) + uint32_t total_discarded = g_ppp_interrupt_stats.dropped_not_initialized + + g_ppp_interrupt_stats.dropped_lwip_error + + g_ppp_interrupt_stats.dropped_queue_full + + g_pingpong_stats.dropped_both_full; + + PPP_DEBUG_WARN(""); + PPP_DEBUG_WARN("Byte Statistics:"); + PPP_DEBUG_WARN(" Total Bytes Received: %u", g_ppp_interrupt_stats.total_bytes_received); + PPP_DEBUG_WARN(" Total Bytes Processed: %u", g_ppp_interrupt_stats.total_bytes_sent_to_lwip); + PPP_DEBUG_WARN(" Total Bytes Discarded: %u", total_discarded); + PPP_DEBUG_WARN(" - Both Buffers Full: %u", g_pingpong_stats.dropped_both_full); + PPP_DEBUG_WARN(" - Queue Full: %u", g_ppp_interrupt_stats.dropped_queue_full); + PPP_DEBUG_WARN(" lwIP Errors: %u", g_ppp_interrupt_stats.lwip_error_count); + PPP_DEBUG_WARN(""); + + // 详细丢弃统计(INFO级别) + if (total_discarded > 0) { + PPP_DEBUG_WARN("Detailed Dropped Bytes:"); + PPP_DEBUG_WARN(" - Not Initialized: %u", g_ppp_interrupt_stats.dropped_not_initialized); + PPP_DEBUG_WARN(" - lwIP Error: %u", g_ppp_interrupt_stats.dropped_lwip_error); + PPP_DEBUG_WARN(" - Queue Full: %u", g_ppp_interrupt_stats.dropped_queue_full); + PPP_DEBUG_WARN(" - Both Buffers Full: %u", g_pingpong_stats.dropped_both_full); + PPP_DEBUG_WARN(""); + } // 计算丢包率 if (g_ppp_interrupt_stats.total_bytes_received > 0) { @@ -728,13 +976,13 @@ void ppp_interrupt_print_stats(void) g_ppp_interrupt_stats.dropped_queue_full + g_pingpong_stats.dropped_both_full; uint32_t drop_rate = (total_dropped * 10000) / g_ppp_interrupt_stats.total_bytes_received; - PPP_DEBUG_INFO("Total Drop Rate: %u.%02u%%", drop_rate / 100, drop_rate % 100); + PPP_DEBUG_WARN("Total Drop Rate: %u.%02u%%", drop_rate / 100, drop_rate % 100); // 队列满导致的丢失率 if (g_ppp_interrupt_stats.dropped_queue_full > 0) { uint32_t queue_full_rate = (g_ppp_interrupt_stats.dropped_queue_full * 10000) / g_ppp_interrupt_stats.total_bytes_received; - PPP_DEBUG_INFO("Queue Full Rate: %u.%02u%%", queue_full_rate / 100, queue_full_rate % 100); + PPP_DEBUG_WARN("Queue Full Rate: %u.%02u%%", queue_full_rate / 100, queue_full_rate % 100); } // 两个buffer都满导致的丢失率 @@ -746,18 +994,7 @@ void ppp_interrupt_print_stats(void) } } - // 性能指标 - if (g_ppp_interrupt_stats.success_count > 0) { - uint32_t avg_bytes = g_ppp_interrupt_stats.total_bytes_sent_to_lwip / - g_ppp_interrupt_stats.success_count; - PPP_DEBUG_INFO("Average bytes per message: %u", avg_bytes); - } - - if (g_ppp_interrupt_stats.total_interrupts > 0) { - uint32_t avg_bytes_per_irq = g_ppp_interrupt_stats.total_bytes_received / - g_ppp_interrupt_stats.total_interrupts; - PPP_DEBUG_INFO("Average bytes per interrupt: %u", avg_bytes_per_irq); - } + // 性能总结(已经在上面各部分显示过了,这里不再重复) - PPP_DEBUG_INFO("================================================="); + PPP_DEBUG_WARN("================================================="); } \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/src/ppp_lwip_thread.c b/code/sdk/zhihui/ppp/src/ppp_lwip_thread.c index f4b2d648a..580b9a1f4 100644 --- a/code/sdk/zhihui/ppp/src/ppp_lwip_thread.c +++ b/code/sdk/zhihui/ppp/src/ppp_lwip_thread.c @@ -164,6 +164,15 @@ static ppp_error_t ppp_flush_buffer(uint8_t buffer_index) /** * @brief 定时器回调:检查buffer超时并flush + * + * 数据安全设计(避免竞态): + * - 定时器职责:只检查和flush **非当前** buffer(避免与接收线程竞争) + * - 接收线程职责: + * 1. 在循环开始时主动检查当前buffer是否超时,超时则flush+切换 + * 2. 检查当前buffer状态,如果不可写则切换 + * 3. 独占 g_current_buffer_index 的修改权 + * - 分工明确:定时器不操作当前buffer,接收线程负责当前buffer的超时处理 + * - 数据保证:stop时强制flush所有buffer,确保数据不丢失 */ static void ppp_timeout_timer_callback(unsigned long arg) { @@ -174,30 +183,37 @@ static void ppp_timeout_timer_callback(unsigned long arg) } uint32_t current_time = ppp_get_current_time_ms(); + uint8_t current_idx = g_current_buffer_index; // 读取一次,避免重复读取 - // 检查所有buffer的超时状态 + // 检查非当前buffer的超时状态 + // ⚠️ 数据安全设计:定时器只处理非当前buffer,避免与接收线程竞争 + // 当前buffer的超时由接收线程主动检查并处理 for (uint8_t i = 0; i < PPP_PING_PONG_BUFFER_COUNT; i++) { + // 跳过当前正在写入的buffer(避免与接收线程竞争,保证数据安全) + if (i == current_idx) { + continue; + } + ppp_ping_pong_buffer_t *buffer = &g_ping_pong_buffers[i]; // 只处理FILLING状态的buffer if (buffer->state == PPP_BUFFER_STATE_FILLING && buffer->write_pos > 0) { - uint32_t elapsed = current_time - buffer->last_write_time; - - if (elapsed >= PPP_PING_PONG_TIMEOUT_MS) { - PPP_DEBUG_DEBUG("[TIMER] Flushing buffer[%u] due to timeout (%u ms)", i, elapsed); + // ⚠️ 修复:避免误判超时 + // last_write_time == 0 表示接收线程刚写入数据,需要更新时间戳(不是超时) + if (buffer->last_write_time == 0) { + // 第一次看到,更新时间戳,不flush + buffer->last_write_time = current_time; + PPP_DEBUG_DEBUG("[TIMER] Update timestamp for buffer[%u]", i); + } else { + // 已经有时间戳了,检查是否真的超时 + uint32_t elapsed = current_time - buffer->last_write_time; - if (ppp_flush_buffer(i) == PPP_ERR_OK) { - g_ppp_stats.buffer_timeout_count++; + if (elapsed >= PPP_PING_PONG_TIMEOUT_MS) { + // 真正超时,flush buffer + PPP_DEBUG_DEBUG("[TIMER] Flushing buffer[%u] due to timeout (%u ms)", i, elapsed); - // Timer flush后切换buffer - if (i == g_current_buffer_index) { - 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_ppp_stats.buffer_switch_count++; - } + if (ppp_flush_buffer(i) == PPP_ERR_OK) { + g_ppp_stats.buffer_timeout_count++; } } } @@ -315,6 +331,12 @@ static void ppp_lwip_process_thread(void *arg) /** * @brief PPP数据接收线程 - 轮询UART并填充到乒乓buffer + * + * 优化设计: + * - 每次读取尝试填满当前 buffer 的剩余空间(高效利用) + * - 独占 g_current_buffer_index 的修改权(避免竞态) + * - 及时检查 buffer 状态并切换(避免数据丢失) + * - 检查超时并主动 flush 当前 buffer(配合定时器) */ static void ppp_lwip_receive_thread(void *arg) { @@ -322,9 +344,9 @@ static void ppp_lwip_receive_thread(void *arg) PPP_DEBUG_INFO("PPP receive thread started (ping-pong polling mode)"); - // 分配临时读取缓冲区(每次最多读取512字节) - const uint32_t read_chunk_size = 1; - uint8_t *rx_buffer = (uint8_t *)malloc(read_chunk_size); + // 分配临时读取缓冲区(最大读取大小) + const uint32_t max_read_size = PPP_PING_PONG_BUFFER_SIZE; + uint8_t *rx_buffer = (uint8_t *)malloc(max_read_size); if (!rx_buffer) { PPP_DEBUG_ERROR("PPP RX buffer alloc failed"); g_ppp_rx_thread_completed = true; @@ -332,105 +354,203 @@ static void ppp_lwip_receive_thread(void *arg) } uint32_t total_bytes = 0; - uint32_t consecutive_empty = 0; + uint32_t last_timeout_check = 0; - PPP_DEBUG_INFO("PPP receive thread: allocated %d bytes temp buffer", read_chunk_size); + PPP_DEBUG_INFO("PPP receive thread: allocated %d bytes temp buffer", max_read_size); // 主循环:连续轮询UART读取 while (g_ppp_rx_thread_running) { + // 1. ⚠️ 数据安全:主动检查当前 buffer 是否超时 + // 定时器只处理非当前buffer,所以当前buffer的超时要在这里处理 + uint8_t current_idx = g_current_buffer_index; + ppp_ping_pong_buffer_t *current_buffer = &g_ping_pong_buffers[current_idx]; + + if (current_buffer->state == PPP_BUFFER_STATE_FILLING && + current_buffer->write_pos > 0 && + current_buffer->last_write_time != 0) { // ← 修复:只有已有时间戳的才检查超时 + uint32_t current_time = ppp_get_current_time_ms(); + uint32_t elapsed = current_time - current_buffer->last_write_time; + + if (elapsed >= PPP_PING_PONG_TIMEOUT_MS) { + // 当前buffer超时,flush并切换 + PPP_DEBUG_DEBUG("[RX] Flushing current buffer[%u] due to timeout (%u ms)", + current_idx, elapsed); + ppp_flush_buffer(current_idx); + g_ppp_stats.buffer_timeout_count++; + + // 切换到下一个EMPTY的buffer + uint8_t next_idx = (current_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_ppp_stats.buffer_switch_count++; + current_idx = next_idx; + current_buffer = next_buffer; + } + } + } + + // 2. 检查当前 buffer 状态,确保可用 + // 重新读取(可能在步骤1中已经切换了) + current_idx = g_current_buffer_index; + current_buffer = &g_ping_pong_buffers[current_idx]; + + // 如果当前 buffer 不是 FILLING 状态(可能被定时器 flush 了或正在处理) + // 尝试切换到下一个 EMPTY 的 buffer + if (current_buffer->state != PPP_BUFFER_STATE_FILLING && + current_buffer->state != PPP_BUFFER_STATE_EMPTY) { + + bool switched = false; + for (uint8_t attempts = 0; attempts < PPP_PING_PONG_BUFFER_COUNT; attempts++) { + uint8_t next_idx = (current_idx + attempts + 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_ppp_stats.buffer_switch_count++; + current_idx = next_idx; + current_buffer = next_buffer; + switched = true; + PPP_DEBUG_DEBUG("Switched to buffer[%u] (previous not available)", next_idx); + break; + } + } + + if (!switched) { + // 所有 buffer 都不可用,等待一下 + g_ppp_stats.dropped_both_full++; + + // ⚠️ 打印 buffer 状态,帮助诊断 + static uint32_t drop_log_counter = 0; + if ((drop_log_counter++ % 10) == 0) { // 每 10 次打印一次,避免日志过多 + PPP_DEBUG_WARN("Both buffers unavailable! Drop event %u", drop_log_counter); + for (uint8_t i = 0; i < PPP_PING_PONG_BUFFER_COUNT; i++) { + const char *state = "UNKNOWN"; + switch (g_ping_pong_buffers[i].state) { + case PPP_BUFFER_STATE_EMPTY: state = "EMPTY"; break; + case PPP_BUFFER_STATE_FILLING: state = "FILLING"; break; + case PPP_BUFFER_STATE_FULL: state = "FULL"; break; + case PPP_BUFFER_STATE_PROCESSING: state = "PROCESSING"; break; + } + PPP_DEBUG_WARN(" Buffer[%u]: %s, %u bytes", + i, state, g_ping_pong_buffers[i].write_pos); + } + } + + osal_msleep(1); + continue; + } + } + + // 2. 初始化 buffer(如果需要) + if (current_buffer->state == PPP_BUFFER_STATE_EMPTY) { + current_buffer->state = PPP_BUFFER_STATE_FILLING; + current_buffer->write_pos = 0; + current_buffer->last_write_time = ppp_get_current_time_ms(); + } + + // 3. 计算可读取的大小(尝试填满 buffer) + uint32_t available = PPP_PING_PONG_BUFFER_SIZE - current_buffer->write_pos; + uint32_t read_size = (available < max_read_size) ? available : max_read_size; + + // 限制单次读取不超过 1024 字节(平衡性能和响应速度) + if (read_size > 1024) { + read_size = 1024; + } + + // 4. 读取数据 uint32_t bytes_read = 0; - ppp_error_t ret = ppp_serial_tryread(rx_buffer, read_chunk_size, &bytes_read); + ppp_error_t ret = ppp_serial_tryread(rx_buffer, read_size, &bytes_read); if (ret == PPP_ERR_OK && bytes_read > 0) { // 成功读取数据 total_bytes += bytes_read; - consecutive_empty = 0; g_ppp_stats.read_count++; g_ppp_stats.total_bytes_received += bytes_read; - PPP_DEBUG_DEBUG("PPP RX: Got %u bytes", bytes_read); + PPP_DEBUG_DEBUG("PPP RX: Got %u bytes (buffer[%u]: %u/%u)", + bytes_read, current_idx, current_buffer->write_pos, + PPP_PING_PONG_BUFFER_SIZE); - // 填充到乒乓buffer - uint32_t processed = 0; - while (processed < bytes_read && g_ppp_rx_thread_running) { - uint8_t current_idx = g_current_buffer_index; - ppp_ping_pong_buffer_t *current_buffer = &g_ping_pong_buffers[current_idx]; - - // 如果buffer是空的,初始化为FILLING状态 - if (current_buffer->state == PPP_BUFFER_STATE_EMPTY) { - current_buffer->state = PPP_BUFFER_STATE_FILLING; - current_buffer->write_pos = 0; - } - - // 尝试写入当前buffer - uint32_t available = PPP_PING_PONG_BUFFER_SIZE - current_buffer->write_pos; - uint32_t to_write = bytes_read - processed; + // 5. 写入当前 buffer + memcpy(current_buffer->data + current_buffer->write_pos, rx_buffer, bytes_read); + current_buffer->write_pos += bytes_read; + current_buffer->last_write_time = ppp_get_current_time_ms(); + + // 6. 检查是否满了或接近满了 + if (current_buffer->write_pos >= PPP_PING_PONG_BUFFER_SIZE) { + // buffer 满了,立即 flush 并切换 + PPP_DEBUG_DEBUG("Buffer[%u] full, flushing...", current_idx); + ppp_flush_buffer(current_idx); + g_ppp_stats.buffer_full_count++; - if (current_buffer->state == PPP_BUFFER_STATE_FILLING && to_write <= available) { - // 空间足够,直接写入 - memcpy(current_buffer->data + current_buffer->write_pos, - rx_buffer + processed, to_write); - current_buffer->write_pos += to_write; - current_buffer->last_write_time = ppp_get_current_time_ms(); - processed += to_write; - - // 检查是否满了 - if (current_buffer->write_pos >= PPP_PING_PONG_BUFFER_SIZE) { - // buffer满,flush - ppp_flush_buffer(current_idx); - g_ppp_stats.buffer_full_count++; - - // 切换到另一个buffer - uint8_t next_idx = (current_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_ppp_stats.buffer_switch_count++; - } - } - } else { - // 当前buffer空间不足或不可用,尝试切换 - uint8_t next_idx = (current_idx + 1) % PPP_PING_PONG_BUFFER_COUNT; + // 切换到下一个 EMPTY 的 buffer + bool switched = false; + for (uint8_t attempts = 0; attempts < PPP_PING_PONG_BUFFER_COUNT; attempts++) { + uint8_t next_idx = (current_idx + attempts + 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) { - // 先flush当前buffer(如果有数据) - if (current_buffer->state == PPP_BUFFER_STATE_FILLING && - current_buffer->write_pos > 0) { - ppp_flush_buffer(current_idx); - } - - // 切换到新buffer g_current_buffer_index = next_idx; g_ppp_stats.buffer_switch_count++; - - // 继续处理剩余数据(下次循环会写入新buffer) - } else { - // 两个buffer都不可用,丢弃数据 - uint32_t discarded = bytes_read - processed; - g_ppp_stats.dropped_both_full += discarded; - g_ppp_stats.total_bytes_discarded += discarded; - PPP_DEBUG_WARN("Both buffers full, discarding %u bytes", discarded); + switched = true; + PPP_DEBUG_DEBUG("Switched to buffer[%u]", next_idx); break; } } + + if (!switched) { + // 没有空闲 buffer,等待处理线程 + PPP_DEBUG_WARN("No empty buffer available, waiting..."); + osal_msleep(1); + } } // 有数据,不休眠,继续读取 + } else if (ret != PPP_ERR_OK) { // 读取错误 if (!g_ppp_rx_thread_running) { break; } PPP_DEBUG_ERROR("PPP serial tryread error: %d", ret); - osal_msleep(10); // 错误时稍微休眠 + osal_msleep(10); + } else { // 无数据 - consecutive_empty++; g_ppp_stats.read_empty_count++; - // 无数据时休眠1ms + // 7. 检查当前 buffer 是否超时(配合定时器) + uint32_t current_time = ppp_get_current_time_ms(); + if (current_time - last_timeout_check >= PPP_PING_PONG_TIMEOUT_MS) { + last_timeout_check = current_time; + + // 检查当前 buffer 是否有数据且超时 + if (current_buffer->state == PPP_BUFFER_STATE_FILLING && + current_buffer->write_pos > 0) { + + uint32_t elapsed = current_time - current_buffer->last_write_time; + if (elapsed >= PPP_PING_PONG_TIMEOUT_MS) { + // 超时,flush 当前 buffer + PPP_DEBUG_DEBUG("Buffer[%u] timeout (%u ms), flushing %u bytes", + current_idx, elapsed, current_buffer->write_pos); + ppp_flush_buffer(current_idx); + g_ppp_stats.buffer_timeout_count++; + + // 切换到下一个 buffer + uint8_t next_idx = (current_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_ppp_stats.buffer_switch_count++; + } + } + } + } + + // 无数据时休眠 1ms osal_msleep(1); } @@ -440,6 +560,15 @@ static void ppp_lwip_receive_thread(void *arg) } } + // 退出前,flush 当前 buffer(如果有数据) + uint8_t final_idx = g_current_buffer_index; + ppp_ping_pong_buffer_t *final_buffer = &g_ping_pong_buffers[final_idx]; + if (final_buffer->state == PPP_BUFFER_STATE_FILLING && final_buffer->write_pos > 0) { + PPP_DEBUG_INFO("Flushing final buffer[%u] with %u bytes", + final_idx, final_buffer->write_pos); + ppp_flush_buffer(final_idx); + } + // 释放缓冲区 free(rx_buffer); @@ -523,7 +652,7 @@ ppp_error_t ppp_lwip_start_receive_thread(void) app_task_definition_t process_task_cfg = {0}; process_task_cfg.create_queue_handle = NULL; process_task_cfg.attr.name = "ppp_process"; - process_task_cfg.attr.priority = osPriorityNormal1; + process_task_cfg.attr.priority = PPP_PROCESS_THREAD_PRIORITY; // 使用配置的优先级 process_task_cfg.attr.stack_size = PPP_PROCESS_STACK_SIZE; process_task_cfg.func = ppp_lwip_process_thread; process_task_cfg.task_handle = &g_ppp_process_thread_handle; @@ -540,13 +669,13 @@ ppp_error_t ppp_lwip_start_receive_thread(void) return PPP_ERR_MEMORY; } - // 5. 启动接收线程 + // 5. 启动接收线程(高优先级以确保及时读取数据) g_ppp_rx_thread_running = true; app_task_definition_t rx_task_cfg = {0}; rx_task_cfg.create_queue_handle = NULL; rx_task_cfg.attr.name = "ppp_receive"; - rx_task_cfg.attr.priority = osPriorityNormal1; + rx_task_cfg.attr.priority = PPP_RX_THREAD_PRIORITY; // 使用配置的高优先级 rx_task_cfg.attr.stack_size = PPP_RX_STACK_SIZE; rx_task_cfg.func = ppp_lwip_receive_thread; rx_task_cfg.task_handle = &g_ppp_rx_thread_handle; @@ -597,7 +726,11 @@ ppp_error_t ppp_lwip_stop_receive_thread(void) g_ppp_timer_running = false; osal_timer_stop(&g_ppp_timeout_timer); osal_timer_destroy(&g_ppp_timeout_timer); - PPP_DEBUG_INFO("Timeout timer stopped"); + + // ⚠️ 关键:清零定时器句柄,确保可以重新初始化 + g_ppp_timeout_timer.timer = NULL; + + PPP_DEBUG_INFO("Timeout timer stopped and cleared"); } // 2. 停止接收线程 @@ -648,10 +781,25 @@ ppp_error_t ppp_lwip_stop_receive_thread(void) // 4. 清理队列 if (g_ppp_msg_queue != 0) { osal_msg_queue_delete(g_ppp_msg_queue); + + // ⚠️ 关键:清零队列句柄,确保可以重新创建 g_ppp_msg_queue = 0; } - // 5. 清空乒乓缓冲区 + // 5. ⚠️ 数据安全:在清空前,先flush所有有数据的buffer,确保数据不丢失 + PPP_DEBUG_INFO("Flushing all buffers before stop..."); + 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); + } + } + + // 6. 清空乒乓缓冲区 for (uint8_t i = 0; i < PPP_PING_PONG_BUFFER_COUNT; i++) { g_ping_pong_buffers[i].write_pos = 0; g_ping_pong_buffers[i].state = PPP_BUFFER_STATE_EMPTY; @@ -705,16 +853,16 @@ void ppp_lwip_reset_discard_stats(void) */ void ppp_lwip_print_discard_stats(void) { - PPP_DEBUG_INFO("=== PPP Polling Statistics (Ping-Pong Mode) ==="); - PPP_DEBUG_INFO("Mode: Polling (轮询替代中断)"); - PPP_DEBUG_INFO("Buffers: %d x %d bytes = %d KB total", + PPP_DEBUG_WARN("=== PPP Polling Statistics (Ping-Pong Mode) ==="); + PPP_DEBUG_WARN("Mode: Polling (轮询替代中断)"); + PPP_DEBUG_WARN("Buffers: %d x %d bytes = %d KB total", PPP_PING_PONG_BUFFER_COUNT, PPP_PING_PONG_BUFFER_SIZE, (PPP_PING_PONG_BUFFER_COUNT * PPP_PING_PONG_BUFFER_SIZE) / 1024); - PPP_DEBUG_INFO("Timeout: %d ms", PPP_PING_PONG_TIMEOUT_MS); - PPP_DEBUG_INFO(""); + PPP_DEBUG_WARN("Timeout: %d ms", PPP_PING_PONG_TIMEOUT_MS); + PPP_DEBUG_WARN(""); // Buffer状态 - PPP_DEBUG_INFO("Buffer Status:"); + PPP_DEBUG_WARN("Buffer Status:"); for (uint8_t i = 0; i < PPP_PING_PONG_BUFFER_COUNT; i++) { const char *state_str = "UNKNOWN"; switch (g_ping_pong_buffers[i].state) { @@ -723,54 +871,54 @@ void ppp_lwip_print_discard_stats(void) case PPP_BUFFER_STATE_FULL: state_str = "FULL"; break; case PPP_BUFFER_STATE_PROCESSING: state_str = "PROCESSING"; break; } - PPP_DEBUG_INFO(" Buffer[%u]: %s, %u/%u bytes%s", + PPP_DEBUG_WARN(" Buffer[%u]: %s, %u/%u bytes%s", i, state_str, g_ping_pong_buffers[i].write_pos, PPP_PING_PONG_BUFFER_SIZE, (i == g_current_buffer_index) ? " (current)" : ""); } - PPP_DEBUG_INFO(""); + PPP_DEBUG_WARN(""); // 乒乓buffer统计 - PPP_DEBUG_INFO("Ping-Pong Buffer Statistics:"); - PPP_DEBUG_INFO(" Buffer Full Count: %u", g_ppp_stats.buffer_full_count); - PPP_DEBUG_INFO(" Buffer Timeout Count: %u", g_ppp_stats.buffer_timeout_count); - PPP_DEBUG_INFO(" Buffer Switch Count: %u", g_ppp_stats.buffer_switch_count); - PPP_DEBUG_INFO(""); + PPP_DEBUG_WARN("Ping-Pong Buffer Statistics:"); + PPP_DEBUG_WARN(" Buffer Full Count: %u", g_ppp_stats.buffer_full_count); + PPP_DEBUG_WARN(" Buffer Timeout Count: %u", g_ppp_stats.buffer_timeout_count); + PPP_DEBUG_WARN(" Buffer Switch Count: %u", g_ppp_stats.buffer_switch_count); + PPP_DEBUG_WARN(""); // UART轮询统计 - PPP_DEBUG_INFO("UART Polling Statistics:"); - PPP_DEBUG_INFO(" Total Read Attempts: %u", g_ppp_stats.read_count + g_ppp_stats.read_empty_count); - PPP_DEBUG_INFO(" Successful Reads: %u", g_ppp_stats.read_count); - PPP_DEBUG_INFO(" Empty Reads: %u", g_ppp_stats.read_empty_count); + PPP_DEBUG_WARN("UART Polling Statistics:"); + PPP_DEBUG_WARN(" Total Read Attempts: %u", g_ppp_stats.read_count + g_ppp_stats.read_empty_count); + PPP_DEBUG_WARN(" Successful Reads: %u", g_ppp_stats.read_count); + PPP_DEBUG_WARN(" Empty Reads: %u", g_ppp_stats.read_empty_count); if (g_ppp_stats.read_count + g_ppp_stats.read_empty_count > 0) { uint32_t hit_rate = (g_ppp_stats.read_count * 100) / (g_ppp_stats.read_count + g_ppp_stats.read_empty_count); - PPP_DEBUG_INFO(" Hit Rate: %u%%", hit_rate); + PPP_DEBUG_WARN(" Hit Rate: %u%%", hit_rate); } - PPP_DEBUG_INFO(""); + PPP_DEBUG_WARN(""); // 字节统计 - PPP_DEBUG_INFO("Byte Statistics:"); - PPP_DEBUG_INFO(" Total Bytes Received: %u", g_ppp_stats.total_bytes_received); - PPP_DEBUG_INFO(" Total Bytes Processed: %u", g_ppp_stats.total_bytes_processed); - PPP_DEBUG_INFO(" Total Bytes Discarded: %u", g_ppp_stats.total_bytes_discarded); - PPP_DEBUG_INFO(" - Both Buffers Full: %u", g_ppp_stats.dropped_both_full); - PPP_DEBUG_INFO(" - Queue Full: %u", g_ppp_stats.dropped_queue_full); - PPP_DEBUG_INFO(" lwIP Errors: %u", g_ppp_stats.lwip_error_count); - PPP_DEBUG_INFO(""); + PPP_DEBUG_WARN("Byte Statistics:"); + PPP_DEBUG_WARN(" Total Bytes Received: %u", g_ppp_stats.total_bytes_received); + PPP_DEBUG_WARN(" Total Bytes Processed: %u", g_ppp_stats.total_bytes_processed); + PPP_DEBUG_WARN(" Total Bytes Discarded: %u", g_ppp_stats.total_bytes_discarded); + PPP_DEBUG_WARN(" - Both Buffers Full: %u", g_ppp_stats.dropped_both_full); + PPP_DEBUG_WARN(" - Queue Full: %u", g_ppp_stats.dropped_queue_full); + PPP_DEBUG_WARN(" lwIP Errors: %u", g_ppp_stats.lwip_error_count); + PPP_DEBUG_WARN(""); // 丢包率 if (g_ppp_stats.total_bytes_received > 0) { uint32_t drop_rate = (g_ppp_stats.total_bytes_discarded * 10000) / g_ppp_stats.total_bytes_received; - PPP_DEBUG_INFO("Drop Rate: %u.%02u%%", drop_rate / 100, drop_rate % 100); + PPP_DEBUG_WARN("Drop Rate: %u.%02u%%", drop_rate / 100, drop_rate % 100); } // 平均字节数 if (g_ppp_stats.read_count > 0) { uint32_t avg_bytes = g_ppp_stats.total_bytes_received / g_ppp_stats.read_count; - PPP_DEBUG_INFO("Average bytes per read: %u", avg_bytes); + PPP_DEBUG_WARN("Average bytes per read: %u", avg_bytes); } - PPP_DEBUG_INFO("================================================="); + PPP_DEBUG_WARN("================================================="); } \ No newline at end of file diff --git a/code/sdk/zhihui/ppp/src/ppp_serial.c b/code/sdk/zhihui/ppp/src/ppp_serial.c index 2ef654eb1..f68ab04d1 100644 --- a/code/sdk/zhihui/ppp/src/ppp_serial.c +++ b/code/sdk/zhihui/ppp/src/ppp_serial.c @@ -15,7 +15,13 @@ /* PPP性能优化配置 */ #define PPP_OPTIMIZED_UART_WRITE_TIMEOUT 200 /* UART写超时优化: 1000ms -> 200ms */ #define PPP_OPTIMIZED_UART_READ_TIMEOUT 100 /* UART读超时优化: 200ms -> 100ms */ - +#if defined(CONFIG_UART_SUPPORT_DMA) +#define UART_DMA_WIDTH 0 +#define UART_DMA_BURST_LENGTH 2 +#define UART_DMA_PRIORITY 0 +#define UART_DMA_TX_ENABLE 1 +#define UART_DMA_RX_ENABLE 1 +#endif /* 串口上下文结构 - 优化缓存机制 */ typedef struct { bool initialized; @@ -27,6 +33,7 @@ typedef struct { uart_pin_config_t pin_config; uart_attr_t uart_attr; uart_buffer_config_t buffer_config; + uart_extra_attr_t extra_attr; /* 统一的接收缓冲区(AT和PPP模式共用) */ uint8_t *rx_buffer; @@ -173,13 +180,39 @@ ppp_error_t ppp_serial_init(const ppp_serial_config_t *config) /* 初始化UART */ PPP_DEBUG_DEBUG("Calling uapi_uart_init..."); + #if defined(PPP_USE_INTERRUPT_MODE) && (PPP_USE_INTERRUPT_MODE == 0) // 注意:uart_porting.c中已经做了UART初始化,这里只需要设置属性 - // errcode_t ret = uapi_uart_init(g_serial_ctx.uart_bus, - // &g_serial_ctx.pin_config, - // &g_serial_ctx.uart_attr, - // NULL, - // &g_serial_ctx.buffer_config); + + + + + #if defined(CONFIG_UART_SUPPORT_DMA) + g_serial_ctx.extra_attr.tx_dma_enable = UART_DMA_TX_ENABLE; + g_serial_ctx.extra_attr.tx_int_threshold = 0; + g_serial_ctx.extra_attr.rx_dma_enable = UART_DMA_RX_ENABLE; + g_serial_ctx.extra_attr.rx_int_threshold = 0; + uapi_dma_init(); + uapi_dma_open(); + errcode_t ret = uapi_uart_init(g_serial_ctx.uart_bus, + &g_serial_ctx.pin_config, + &g_serial_ctx.uart_attr, + &g_serial_ctx.extra_attr, + &g_serial_ctx.buffer_config); + #else + errcode_t ret = uapi_uart_init(g_serial_ctx.uart_bus, + &g_serial_ctx.pin_config, + &g_serial_ctx.uart_attr, + NULL, + &g_serial_ctx.buffer_config); + #endif + + #else + + // 注意:uart_porting.c中已经做了UART初始化,这里只需要设置属性 + // 需要配置 SUPPORT_PPP_SERVICE_INTERRUPT 才在uart_porting里初始化uart errcode_t ret = uapi_uart_set_attr(g_serial_ctx.uart_bus, &g_serial_ctx.uart_attr); + + #endif if (ret != ERRCODE_SUCC) { PPP_DEBUG_ERROR("Failed to initialize UART: error code = %d (0x%x)", ret, ret); PPP_DEBUG_ERROR("UART init failed with bus=%u, tx_pin=%u, rx_pin=%u, baudrate=%u", @@ -191,7 +224,6 @@ ppp_error_t ppp_serial_init(const ppp_serial_config_t *config) osal_mutex_destroy(&g_serial_ctx.tx_mutex); return PPP_ERR_UART; } - PPP_DEBUG_INFO("UART initialized successfully (error code = %d)", ret); /* 创建同步对象 */ @@ -469,7 +501,19 @@ ppp_error_t ppp_serial_read(uint8_t *buffer, uint32_t buffer_size, uint32_t *byt case PPP_SERIAL_MODE_AT: /* AT命令模式:直接从UART读取 */ { + + #if defined(CONFIG_UART_SUPPORT_DMA) + uart_write_dma_config_t dma_cfg = { + .src_width = UART_DMA_WIDTH, + .dest_width = UART_DMA_WIDTH, + .burst_length = UART_DMA_BURST_LENGTH, + .priority = UART_DMA_PRIORITY + }; + int32_t read_result = uapi_uart_read_by_dma(g_serial_ctx.uart_bus, buffer, buffer_size,&dma_cfg); + + #else int32_t read_result = uapi_uart_read(g_serial_ctx.uart_bus, buffer, buffer_size, 100); + #endif if (read_result > 0) { *bytes_read = (uint32_t)read_result; g_serial_ctx.rx_bytes += *bytes_read; @@ -499,9 +543,21 @@ ppp_error_t ppp_serial_read(uint8_t *buffer, uint32_t buffer_size, uint32_t *byt uint8_t *current_buffer = buffer + total_read; // 使用短超时进行读取,类似sio_tryread的1ms超时 - int32_t read_result = uapi_uart_read(g_serial_ctx.uart_bus, current_buffer, - remaining, batch_count == 0 ? 10 : 1); + #if defined(CONFIG_UART_SUPPORT_DMA) + uart_write_dma_config_t dma_cfg = { + .src_width = UART_DMA_WIDTH, + .dest_width = UART_DMA_WIDTH, + .burst_length = UART_DMA_BURST_LENGTH, + .priority = UART_DMA_PRIORITY + }; + int32_t read_result = uapi_uart_read_by_dma(g_serial_ctx.uart_bus, current_buffer, + remaining,&dma_cfg); + + #else + int32_t read_result = uapi_uart_read(g_serial_ctx.uart_bus, current_buffer, + remaining, batch_count == 0 ? 10 : 1); + #endif if (read_result > 0) { total_read += (uint32_t)read_result; g_serial_ctx.rx_bytes += (uint32_t)read_result; @@ -589,7 +645,19 @@ ppp_error_t ppp_serial_tryread(uint8_t *buffer, uint32_t buffer_size, uint32_t * if (g_serial_ctx.current_mode == PPP_SERIAL_MODE_PPP_DATA) { /* PPP数据模式:使用1ms超时快速读取,类似SLIP的sio_tryread */ + + #if defined(CONFIG_UART_SUPPORT_DMA) + uart_write_dma_config_t dma_cfg = { + .src_width = UART_DMA_WIDTH, + .dest_width = UART_DMA_WIDTH, + .burst_length = UART_DMA_BURST_LENGTH, + .priority = UART_DMA_PRIORITY + }; + int32_t read_result = uapi_uart_read_by_dma(g_serial_ctx.uart_bus, buffer, buffer_size,&dma_cfg); + + #else int32_t read_result = uapi_uart_read(g_serial_ctx.uart_bus, buffer, buffer_size, 0); + #endif if (read_result > 0) { *bytes_read = (uint32_t)read_result; g_serial_ctx.rx_bytes += *bytes_read;