You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

环形缓冲区突发传输时丢失最后字节的问题排查

环形缓冲区+UART状态机异常问题

问题背景

我在他人协助下实现了一个简单环形缓冲区,用于读取UART数据、处理后从另一个UART转发。外部设备交互时,会接收10-20个6字节数据包:每个包包含两种已知SOF值之一、4个数据字节和EOF字节,前N个包用第一种SOF,最后一个包用第二种SOF。

异常现象

接收到第二种SOF后,IRQ不再触发,状态机错过EOF字节,环形缓冲区的Head指针未递增,导致最后一个包无法被处理和发送。最终包的数据已经加载到缓冲区(不存储EOF),但因为头尾索引相等(未进入STATE_WAIT_EOF状态更新Head索引),包无法被读取处理。

IRQ处理函数中的状态机代码

void USART2_IRQHandler(void)
{
    uint8_t Curr_Byte = *RX_Touch_Data;

    switch(Pkt_State)
    {
        case STATE_WAIT_SOF:
        {
            Data_Indx = 0;

            if(( Curr_Byte == TD_PKT) || ( Curr_Byte == LO_PKT ))
            {
                Msg_Buff[Buff_Head_Indx].RX_Data[Data_Indx] = Curr_Byte;                
                ++Data_Indx;
                Pkt_State = STATE_RX_DATA;
            }
         }
         break;

        case STATE_RX_DATA:
        {
            Msg_Buff[Buff_Head_Indx].RX_Data[Data_Indx] = Curr_Byte;
            ++Data_Indx;

            if( Data_Indx == DATA_SIZE )
            {
                Pkt_State = STATE_WAIT_EOF;

                if (Msg_Buff[Buff_Head_Indx].RX_Data[0] == LO_PKT)
                {
                    LO_Pkt = TRUE;  //标记已接收最后一包的完整数据
                }
            }
        }
        break;

        case STATE_WAIT_EOF:
        {
            if(LO_Pkt == TRUE)
            {
                DataFull = TRUE;    //在此处设置断点,但从未触发
            }
        
            if( Curr_Byte == EOF_PKT )
            {
                uint8_t New_Buff_Head_Indx = ( Buff_Head_Indx + 1 ) % NUM_MESSAGES;     

                if( New_Buff_Head_Indx != Buff_Tail_Indx)
                {
                    Buff_Head_Indx = New_Buff_Head_Indx;
                }
                else
                {
                    //缓冲区满,数据丢失
                }
            }

            Pkt_State = STATE_WAIT_SOF;
        }
        break;

        default:
        break;

    }

    HAL_UART_IRQHandler(&huart2);                       //HAL清理中断标志

    HAL_UART_Receive_IT( &huart2, RX_Touch_Data, 1 );   //开启下一字节接收
}

前置条件

进入主while循环前,已调用一次HAL_UART_Receive_IT启动接收流程。

补充测试信息

  • 最后一个数据包无特殊格式,怀疑即使只发送一种SOF的包也会出现问题;若触发新的突发传输,上一轮的最后一个包会被强制发送。十轮传输中,仅最后一轮的最后一个包无法发送。
  • 已将环形缓冲区扩容10倍,排除索引回绕问题,现象无变化。
  • 临时跳过最后一个包的EOF检查可以正常工作,但这只是权宜之计,希望找到根本原因。

编辑补充:HAL_UART_Receive_IT源码及状态检查

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, 
uint8_t *pData, uint16_t Size)
{
   /* 检查接收流程是否未在运行 */
   if (huart->RxState == HAL_UART_STATE_READY)
   {
       if ((pData == NULL) || (Size == 0U))
       {
           return HAL_ERROR;
       }

       /* 设置接收类型为标准接收 */
       huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;

       if (!(IS_LPUART_INSTANCE(huart->Instance)))
       {
           /* 检查USART RTOEN位是否设置 */
           if (READ_BIT(huart->Instance->CR2, USART_CR2_RTOEN) != 0U)
           {
               /* 使能UART接收超时中断 */
               ATOMIC_SET_BIT(huart->Instance->CR1, USART_CR1_RTOIE);
           }
       }

       return (UART_Start_Receive_IT(huart, pData, Size));
   }
   else
   {
       return HAL_BUSY;
   }
}
  • 未触发过HAL_BUSY或HAL_ERROR标志。

数据包检查逻辑代码

if( Buff_Head_Indx != Buff_Tail_Indx )                                   
//有可发送的数据包
{
    Result = Process_Touch_Data();
    Buff_Tail_Indx = ( Buff_Tail_Indx + 1) % NUM_MESSAGES;              
}

内容的提问来源于stack exchange,提问作者nobby

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 16:40:32