多亏了另一个用户的帮助,我实现了一个简单的循环缓冲区来读取一些UART数据,进行一些小处理,然后在另一个UART上传输.根据用户与外部设备的一些交互,我会收到10-20个6字节的数据包流.我有一个已知的SOF,它是两个值之一,四个数据字节,然后是一个EOF字节.前XX个分组始终具有第一个SOF值,而最后一个分组始终具有另一个已知SOF值.问题是,当我获得第二个SOF值时,IRQ不会再次命中.因此,我错过了状态机中的EOF字节,循环缓冲区指针不会递增,因此不会传输最后一个包.正确的最终包被加载到循环缓冲区中(我不存储EOF字节),但是因为我的头和尾索引相等(因为我不会在最后一个字节上进入CASE STATE_WAIT_EOF状态,这将增加头缓冲区),头索引不会递增,包也不会被处理/传输.

下面是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;  //Set flag indicating got full data buff of last pkt
                }
            }
        }
        break;

        case STATE_WAIT_EOF:
        {
            if(LO_Pkt == TRUE)
            {
                DataFull = TRUE;    //Set breakpoint here, never hits
            }
        
            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
                {
                    //No space; msg will be lost
                }
            }

            Pkt_State = STATE_WAIT_SOF;
        }
        break;

        default:
        break;

    }

    HAL_UART_IRQHandler(&huart2);                       //Let HAL clean up flags

    HAL_UART_Receive_IT( &huart2, RX_Touch_Data, 1 );   //Get next byte
}

当然,我还会在进入主While循环之前调用一次HAL_UART_RECEIVE_IT函数来启动该过程.

该设备发送的最后一个数据包没有任何"特殊"之处.如果我可以强制设备只发送一种类型的包,我会发现同样的情况:最终的包仍然不会被发送(不幸的是,我不能这样做).另外,请注意,如果我通过新的用户交互触发新的数据包突发,则第一个突发的最后一个数据包将被"强制"输出(这是有意义的,因为Head索引增加了).因此,我可以有10个用户交互与10个数据包突发,一切都会很好,除了最后一个,它将错过最后一个数据包.

有人看到我错过了什么吗?我希望我的解释是有道理的.

编辑:只是为了测试,我将循环缓冲区大小扩大到了10倍,这是我预期的10次猝发.只是为了消除任何环绕式索引问题的可能性.没有变化.

我暂时实现了一个"补丁",在该补丁中我不会在最后一个包中查找EOF字节.这是有效的,但充其量只是一种黑客攻击,我想知道为什么没有这个它就不能工作.

EDIT2:

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, 
uint8_t *pData, uint16_t Size)
{
   /* Check that a Rx process is not already ongoing */
   if (huart->RxState == HAL_UART_STATE_READY)
   {
       if ((pData == NULL) || (Size == 0U))
   {
       return HAL_ERROR;
   }

   /* Set Reception type to Standard reception */
   huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;

   if (!(IS_LPUART_INSTANCE(huart->Instance)))
  {
     /* Check that USART RTOEN bit is set */
     if (READ_BIT(huart->Instance->CR2, USART_CR2_RTOEN) != 0U)
     {
        /* Enable the UART Receiver Timeout Interrupt */
        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 )                                   
//There are available packets to send
{
    Result = Process_Touch_Data();
    Buff_Tail_Indx = ( Buff_Tail_Indx + 1) % NUM_MESSAGES;              
}

推荐答案

问题

您的数据处理都延迟了一个字节.以下是事件的顺序:

您可以调用HAL_UART_Receive_IT( &huart2, RX_Touch_Data, 1 );,这将使能UART上的接收中断.

在UART上接收到一个字节(比方说SOF).USART2_IRQHandler()号打来电话.您从*RX_Touch_Data中读取一个字节,该字节当前未设置为任何有意义的值,然后处理该字节.

然后调用HAL_UART_IRQHandler(),这将从UART中读取SOF字节并将其存储在*RX_Touch_Data中.

当接收到下一个字节(比方说0x53)时,状态机处理SOF,然后HAL将0x53存储在*RX_Touch_Data中.Ad-inifinitum.你总是落后一个字节.

在最后的EOF进入之后,它在*RX_Touch_Data中被缓冲,并且因为没有更多的字节出现,所以它不被状态机处理.

建议的解决方案

自己在中断处理程序中读取UART2中的字节,如下所示:

uint8_t Curr_Byte = UART2->DR & 0xff;

不要拨打HAL_UART_IRQHandler().

我还建议您自己启用接收中断.那你就不需要再打HAL_UART_Receive_IT()了.每次收到一个字节时,您都会得到一个中断.这可能就足够了:

/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);

C++相关问答推荐

新的memaligning函数有什么用?

从STdin读写超过4096个字节

球体—立方体重叠:无、部分或全部?

为什么在C中二维字符数组会有这样的行为?

struct 上的OpenMP缩减

GDB输出ARM助记符

我的C函数起作用了,但我不确定为什么

C代码在字符串中删除不区分大小写的子字符串的问题

Cairo STM32MP1 cairo_Surface_WRITE_TO_PNG始终返回CAROLIO_STATUS_WRITE_ERROR

判断系统命令返回值的正确方法

OSDev--双缓冲重启系统

GetText不适用于包含国际字符的帐户名称

从C中的函数返回静态字符串是不是一种糟糕的做法?

Valgrind用net_pton()抱怨

程序对大输入给出错误答案

Linux/C:带有子进程的进程在添加waitid后都挂起

函数的typedef是标准 C 语法吗?它与函数指针的typedef有何不同?

为什么 int32_t 和 int16_t 在 printf 输出中具有相同的位数?

std::malloc/calloc/realloc/free 与纯 C 的 malloc/calloc/realloc/free 有什么不同

GDB 用内容初始化数组