环形缓冲区突发传输时丢失最后字节的问题排查
环形缓冲区+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
相关产品推荐
相关产品推荐

