#define PPP_PING_PONG_BUFFER_SIZE 2048 // 每个buffer大小(2KB)
#endif
-#define PPP_PING_PONG_BUFFER_COUNT 2 // 乒乓buffer数量(固定2个)
+#define PPP_PING_PONG_BUFFER_COUNT 3 // 三缓冲模式(改为3个)
#define PPP_PING_PONG_TIMEOUT_MS 30 // buffer超时时间(30ms)
/* PPP中断消息队列配置 */
volatile uint32_t write_pos; // 当前写入位置
volatile ppp_buffer_state_t state; // 缓冲区状态
volatile uint32_t last_write_time; // 最后写入时间戳(ms)
+ volatile uint32_t first_write_time; // 首次写入时间戳(用于统计填充时间)
} ppp_ping_pong_buffer_t;
/* PPP中断消息结构 - 通过消息队列传递buffer索引 */
typedef struct {
- uint8_t buffer_index; // 缓冲区索引(0或1)
+ uint8_t buffer_index; // 缓冲区索引(0~PPP_PING_PONG_BUFFER_COUNT-1)
uint16_t length; // 数据长度
} ppp_irq_msg_t;
/* 乒乓缓冲区 */
static ppp_ping_pong_buffer_t g_ping_pong_buffers[PPP_PING_PONG_BUFFER_COUNT];
-static volatile uint8_t g_current_buffer_index = 0;
+static volatile uint8_t g_current_buffer_index = 0; // ⚠️ volatile不保证原子性,但单字节写入在ARM上通常是原子的
/* 全局时间戳(由定时器更新,ISR只读) */
static volatile uint32_t g_timer_timestamp_ms = 0;
/* 扩展统计信息:乒乓缓冲区相关 */
typedef struct {
- uint32_t buffer_full_count; // buffer满次数
- uint32_t buffer_timeout_count; // buffer超时flush次数
- uint32_t buffer_switch_count; // buffer切换次数
- uint32_t dropped_both_full; // 两个buffer都满导致丢弃的字节数
+ uint32_t buffer_full_count; // buffer满次数
+ uint32_t buffer_timeout_count; // buffer超时flush次数(未满但超时)
+ uint32_t buffer_switch_count; // buffer切换次数
+ uint32_t dropped_both_full; // 两个buffer都满导致丢弃的字节数
+ uint32_t switch_failed_count; // buffer切换失败次数(安全性指标)
+ uint32_t race_condition_detected; // 检测到的竞态条件次数(诊断)
+ uint32_t cross_buffer_count; // 跨buffer写入次数(数据跨两个buffer)
+ uint32_t timeout_unfilled_count; // 超时时未满的flush次数
+ uint32_t fill_time_min_ms; // buffer填充最短时间(ms)
+ uint32_t fill_time_max_ms; // buffer填充最长时间(ms)
+ uint32_t fill_time_total_ms; // buffer填充总时间(用于计算平均值)
+ uint32_t fill_time_count; // buffer填充次数统计
} ppp_pingpong_stats_t;
static ppp_pingpong_stats_t g_pingpong_stats = {0};
// 释放buffer(标记为空闲)
buffer->state = PPP_BUFFER_STATE_EMPTY;
buffer->write_pos = 0;
+ buffer->first_write_time = 0; // 重置首次写入时间
}
PPP_DEBUG_INFO("PPP IRQ process thread exited");
/**
* @brief 刷新指定buffer到消息队列
* @param buffer_index 缓冲区索引
+ * @param is_timeout 是否因超时触发(用于统计)
* @return PPP_ERR_OK 成功,其他值失败
*/
-static ppp_error_t ppp_flush_buffer(uint8_t buffer_index)
+static ppp_error_t ppp_flush_buffer_internal(uint8_t buffer_index, bool is_timeout)
{
if (buffer_index >= PPP_PING_PONG_BUFFER_COUNT) {
return PPP_ERR_INVALID_PARAM;
return PPP_ERR_OK; // 没有数据需要flush
}
+ // 统计填充时间(从首次写入到flush)
+ if (buffer->first_write_time > 0) {
+ uint32_t current_time = ppp_get_current_time_ms();
+ uint32_t fill_time = current_time - buffer->first_write_time;
+
+ // 更新最小/最大填充时间
+ if (g_pingpong_stats.fill_time_count == 0) {
+ g_pingpong_stats.fill_time_min_ms = fill_time;
+ g_pingpong_stats.fill_time_max_ms = fill_time;
+ } else {
+ if (fill_time < g_pingpong_stats.fill_time_min_ms) {
+ g_pingpong_stats.fill_time_min_ms = fill_time;
+ }
+ if (fill_time > g_pingpong_stats.fill_time_max_ms) {
+ g_pingpong_stats.fill_time_max_ms = fill_time;
+ }
+ }
+ g_pingpong_stats.fill_time_total_ms += fill_time;
+ g_pingpong_stats.fill_time_count++;
+ }
+
+ // 统计超时且未满的flush
+ if (is_timeout && buffer->write_pos < PPP_PING_PONG_BUFFER_SIZE) {
+ g_pingpong_stats.timeout_unfilled_count++;
+ }
+
// 标记为FULL状态
buffer->state = PPP_BUFFER_STATE_FULL;
return PPP_ERR_OK;
}
+// 便利函数:非超时flush
+static ppp_error_t ppp_flush_buffer(uint8_t buffer_index)
+{
+ return ppp_flush_buffer_internal(buffer_index, false);
+}
+
/**
* @brief 定时器回调:检查buffer超时并flush
* @note 定时器上下文,应快速返回
timer_call_count, g_ppp_interrupt_enabled, g_ppp_timer_running);
}
+ // 每333次(约10秒)打印一次PPP丢失统计
+ // 30ms * 333 ≈ 10秒
+ if ((timer_call_count % 333) == 0) {
+ ppp_interrupt_print_stats();
+ }
+
if (!g_ppp_interrupt_enabled || !g_ppp_timer_running) {
return;
}
uint8_t current_idx = g_current_buffer_index; // 读取一次,避免重复读取
- // 检查非当前buffer的超时状态
- // ⚠️ 数据安全设计:定时器只处理非当前buffer,避免与ISR竞争
- // 当前buffer的超时由ISR在写入时主动检查并处理
+ // ⚠️ 新增:检查所有buffer的超时状态(包括当前buffer)
+ // 需求4:超时时如果当前buffer未满也要flush,避免数据滞留
for (uint8_t i = 0; i < PPP_PING_PONG_BUFFER_COUNT; i++) {
- // 跳过当前正在写入的buffer(避免与ISR竞争,保证数据安全)
- if (i == current_idx) {
- continue;
+ // ⚠️ 竞态检测:在处理过程中再次检查current_idx是否变化
+ // 如果这个buffer变成current了,需要小心处理
+ uint8_t current_check = g_current_buffer_index;
+ bool is_current = (i == current_check);
+
+ // 检测到current_idx在循环中变化(竞态)
+ if (!is_current && i == current_idx) {
+ g_pingpong_stats.race_condition_detected++;
+ continue; // 跳过,避免竞态
}
ppp_ping_pong_buffer_t *buffer = &g_ping_pong_buffers[i];
- // 只处理FILLING状态的buffer
+ // 只处理FILLING状态且有数据的buffer
if (buffer->state == PPP_BUFFER_STATE_FILLING && buffer->write_pos > 0) {
- // ⚠️ 修复:避免误判超时
- // last_write_time == 0 表示ISR刚写入数据,需要更新时间戳(不是超时)
+ // 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);
+ // ⚠️ 超时了!即使未满也要flush(需求4)
+ PPP_DEBUG_DEBUG("[TIMER] Flushing buffer[%u] due to timeout (%u ms), %s, %u bytes",
+ i, elapsed,
+ is_current ? "CURRENT" : "non-current",
+ buffer->write_pos);
- if (ppp_flush_buffer(i) == PPP_ERR_OK) {
+ // 使用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);
+ }
+ }
}
}
}
* 1. 写入当前buffer(memcpy,~60字节 < 1μs)
* 2. buffer满了直接flush并切换(~2μs)
* 3. 无malloc/free,减少内存碎片和延迟
- * 4. 总耗时目标:< 5μs(比之前的7μs更快)
+ * 4. ⚠️ 新增:跨buffer写入支持(需求1)
+ * 5. 总耗时目标:< 5μs
*/
void _ppp_uart_rx_interrupt_handler(const void *buffer, uint16_t length, bool error)
{
- // 快速路径:参数校验
+ // ⚠️ 中断中禁止打印!必须极快返回
+
+ // 快速路径:参数校验(异常情况也不打印,只统计)
if ( !buffer || length == 0 || length > 200) { // 单次中断最多200字节
- PPP_DEBUG_ERROR("ppp_uart_rx_interrupt_handler !buffer || length == 0 || length > 200 ");
+ // 不打印!在中断中打印会阻塞系统,只记录统计
+ g_ppp_interrupt_stats.error_count++;
return;
}
if (error) {
g_ppp_interrupt_stats.error_count++;
}
- // 更新统计(用于诊断 - 可以在统计打印中看到)
+ // 更新统计
g_ppp_interrupt_stats.total_interrupts++;
g_ppp_interrupt_stats.total_bytes_received += length;
- // 诊断:每10次中断增加一个标记(避免频繁打印)
- static volatile uint32_t irq_count_marker = 0;
- if ((g_ppp_interrupt_stats.total_interrupts % 10) == 1) {
- irq_count_marker = g_ppp_interrupt_stats.total_interrupts;
- // 不能在中断中打印,但可以更新这个全局变量,在统计中查看
- }
-
- // ⚠️ 中断中禁止打印!
-
// 获取当前buffer
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更新一次)
+ // ⚠️ 检查当前buffer的超时
if (current_buffer->state == PPP_BUFFER_STATE_FILLING &&
current_buffer->write_pos > 0 &&
- current_buffer->last_write_time != 0) { // ← 只检查已有时间戳的
- // 使用全局时间戳(ISR中安全,不调用函数)
+ current_buffer->last_write_time != 0) {
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并切换
+ // 超时,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) {
+ if (g_ping_pong_buffers[next_idx].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;
+ current_buffer = &g_ping_pong_buffers[current_idx];
}
}
}
- // å¦\82æ\9e\9cå½\93å\89\8dbufferä¸\8då\8f¯å\86\99ï¼\88ç\8a¶æ\80\81ä¸\8d对ï¼\89ï¼\8cå°\9dè¯\95å\88\87æ\8d¢
+ // 如果当前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) {
+ if (g_ping_pong_buffers[next_idx].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;
+ current_buffer = &g_ping_pong_buffers[current_idx];
}
}
- // 尝试写入当前buffer
- uint32_t available = PPP_PING_PONG_BUFFER_SIZE - current_buffer->write_pos;
-
+ // 初始化空buffer
if (current_buffer->state == PPP_BUFFER_STATE_EMPTY) {
- // buffer是空的,初始化为FILLING状态
current_buffer->state = PPP_BUFFER_STATE_FILLING;
current_buffer->write_pos = 0;
- available = PPP_PING_PONG_BUFFER_SIZE; // 重新计算可用空间
+ current_buffer->first_write_time = g_timer_timestamp_ms; // ⚠️ 需求3:记录首次写入时间
}
- 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 = 0; // 由定时器回调更新
-
- // 检查是否满了
- if (current_buffer->write_pos >= PPP_PING_PONG_BUFFER_SIZE) {
- // buffer满,flush
+ // 计算当前buffer剩余空间
+ uint32_t available = PPP_PING_PONG_BUFFER_SIZE - current_buffer->write_pos;
+ uint16_t remaining_data = length;
+ const uint8_t *data_ptr = (const uint8_t *)buffer;
+
+ // ⚠️ 需求1:跨buffer写入处理
+ if (current_buffer->state == PPP_BUFFER_STATE_FILLING) {
+ if (remaining_data <= available) {
+ // 情况1:数据完全放得下,直接写入
+ memcpy(current_buffer->data + current_buffer->write_pos, data_ptr, remaining_data);
+ current_buffer->write_pos += remaining_data;
+ current_buffer->last_write_time = 0; // 由定时器更新
+
+ // 检查是否正好填满
+ if (current_buffer->write_pos >= PPP_PING_PONG_BUFFER_SIZE) {
+ ppp_flush_buffer(current_idx);
+ g_pingpong_stats.buffer_full_count++;
+
+ // 切换buffer
+ uint8_t next_idx = (current_idx + 1) % PPP_PING_PONG_BUFFER_COUNT;
+ if (g_ping_pong_buffers[next_idx].state == PPP_BUFFER_STATE_EMPTY) {
+ g_current_buffer_index = next_idx;
+ g_pingpong_stats.buffer_switch_count++;
+ } else {
+ g_pingpong_stats.switch_failed_count++;
+ }
+ }
+ } else {
+ // 情况2:数据超过当前buffer剩余空间,需要跨buffer写入
+ g_pingpong_stats.cross_buffer_count++; // ⚠️ 统计跨buffer次数
+
+ // 第1步:先填满当前buffer
+ if (available > 0) {
+ memcpy(current_buffer->data + current_buffer->write_pos, data_ptr, available);
+ current_buffer->write_pos += available;
+ data_ptr += available;
+ remaining_data -= available;
+ }
+
+ // 第2步:flush当前buffer
ppp_flush_buffer(current_idx);
g_pingpong_stats.buffer_full_count++;
- // 切换到另一个buffer
+ // 第3步:切换到下一个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) {
+ if (next_buffer->state == PPP_BUFFER_STATE_EMPTY &&
+ remaining_data <= PPP_PING_PONG_BUFFER_SIZE) {
+ // 第4步:把剩余数据写入新buffer
g_current_buffer_index = next_idx;
g_pingpong_stats.buffer_switch_count++;
+
+ next_buffer->state = PPP_BUFFER_STATE_FILLING;
+ next_buffer->write_pos = 0;
+ next_buffer->first_write_time = g_timer_timestamp_ms; // 新buffer的首次写入时间
+
+ memcpy(next_buffer->data, data_ptr, remaining_data);
+ next_buffer->write_pos = remaining_data;
+ next_buffer->last_write_time = 0; // 由定时器更新
+ } else {
+ // 下一个buffer不可用,剩余数据丢弃
+ g_pingpong_stats.dropped_both_full += remaining_data;
+ g_pingpong_stats.switch_failed_count++;
}
- // 如果next_buffer不是EMPTY,保持当前索引,下次数据会被丢弃
}
} else {
- // 当前buffer空间不足或不可用,尝试切换到另一个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 && length <= PPP_PING_PONG_BUFFER_SIZE) {
- // 先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_pingpong_stats.buffer_switch_count++;
-
- // 写入新buffer
- next_buffer->state = PPP_BUFFER_STATE_FILLING;
- 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 = 0; // 由定时器回调更新
- } else {
- // 两个buffer都不可用,丢弃数据
- g_pingpong_stats.dropped_both_full += length;
- }
+ // 当前buffer不可用,数据丢弃
+ g_pingpong_stats.dropped_both_full += length;
}
}
g_ping_pong_buffers[i].write_pos = 0;
g_ping_pong_buffers[i].state = PPP_BUFFER_STATE_EMPTY;
g_ping_pong_buffers[i].last_write_time = 0;
+ g_ping_pong_buffers[i].first_write_time = 0;
memset(g_ping_pong_buffers[i].data, 0, PPP_PING_PONG_BUFFER_SIZE);
}
g_current_buffer_index = 0;
// 乒乓缓冲区统计
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(" Buffer Timeout Count: %u (total)", g_pingpong_stats.buffer_timeout_count);
+ PPP_DEBUG_WARN(" - Timeout Unfilled: %u ⚠️ (未满即超时)", g_pingpong_stats.timeout_unfilled_count);
+ PPP_DEBUG_WARN(" Buffer Switch Count: %u (success)", g_pingpong_stats.buffer_switch_count);
+ PPP_DEBUG_WARN(" Buffer Switch Failed: %u ⚠️", g_pingpong_stats.switch_failed_count);
+ PPP_DEBUG_WARN(" Cross-Buffer Writes: %u 📌 (跨buffer写入)", g_pingpong_stats.cross_buffer_count);
PPP_DEBUG_WARN(" Dropped (Both Full): %u bytes ⚠️", g_pingpong_stats.dropped_both_full);
+
+ // 填充时间统计
+ if (g_pingpong_stats.fill_time_count > 0) {
+ uint32_t avg_fill_time = g_pingpong_stats.fill_time_total_ms / g_pingpong_stats.fill_time_count;
+ PPP_DEBUG_WARN(" Buffer Fill Time (从首字节到flush):");
+ PPP_DEBUG_WARN(" - Min: %u ms ⏱️", g_pingpong_stats.fill_time_min_ms);
+ PPP_DEBUG_WARN(" - Max: %u ms ⏱️", g_pingpong_stats.fill_time_max_ms);
+ PPP_DEBUG_WARN(" - Avg: %u ms ⏱️", avg_fill_time);
+ PPP_DEBUG_WARN(" - Sample Count: %u", g_pingpong_stats.fill_time_count);
+ } else {
+ PPP_DEBUG_WARN(" Buffer Fill Time: No data yet");
+ }
+
+ if (g_pingpong_stats.race_condition_detected > 0) {
+ PPP_DEBUG_ERROR(" Race Conditions Detected: %u 🔴", g_pingpong_stats.race_condition_detected);
+ }
PPP_DEBUG_WARN("");
// 中断层统计