写入安全的 UART 中断缓冲区

Writing safe UART interrupt buffer

我知道易失性键盘,它不能确保同步安全。

以下代码用于中断例程,我大量使用了该函数

主循环中的 GetCharUART。

这样写代码安全稳定吗?或者我必须像互斥锁一样进行低级同步,如果这是真的,我将如何保护那个 rbuf?

 volatile char rbuf[5][UART_BUFSIZE];
vu16 rin[5] = { 0, 0, 0, 0, 0 };
vu16 rout[5] = { 0, 0, 0, 0, 0 };
    void USART_IRQHandler(char Channel, USART_TypeDef *USARTx)
    {
        volatile unsigned int IIR;
     
        IIR = USARTx->SR;
        if (IIR & USART_FLAG_RXNE)
        {                  // read interrupt
          USARTx->SR &= ~USART_FLAG_RXNE;             // clear interrupt
     
          rbuf[Channel][rin[Channel]] = USART_ReceiveData(USARTx);
          rin[Channel]++;
          if(rin[Channel]>=UART_BUFSIZE) rin[Channel]=0;
        }
     
        if (IIR & USART_FLAG_TXE)
        {
          USARTx->SR &= ~USART_FLAG_TXE;              // clear interrupt
        }
    }   
     
     
    int GetCharUART (char Channel)
    {
      int result;
     
      if (rin[Channel]==rout[Channel]) {
            return EMPTY;
        }
     
      result=rbuf[Channel][rout[Channel]];
      rout[Channel]++;
      if(rout[Channel]>=UART_BUFSIZE) 
            rout[Channel]=0;
     
      return result;
    }

修改后的代码:

void USART_IRQHandler(char Channel, USART_TypeDef *USARTx)
{
    volatile unsigned int IIR;

    IIR = USARTx->SR;
    if (IIR & USART_FLAG_RXNE)
    {                  // read interrupt
      USARTx->SR &= ~USART_FLAG_RXNE;             // clear interrupt

      rbuf[Channel][rin[Channel]% UART_BUFSIZE] = USART_ReceiveData(USARTx);
      rin[Channel]++;
    }

    if (IIR & USART_FLAG_TXE)
    {
      USARTx->SR &= ~USART_FLAG_TXE;              // clear interrupt
    }
}
/******************************************************************************/
int GetCharUART (char Channel)
{
  int result;

  if (rin[Channel]==rout[Channel]) {
        return EMPTY;
    }

  result=rbuf[Channel][rout[Channel]% UART_BUFSIZE];
  rout[Channel]++;

  return result;
}

您的代码存在功能错误。

在 ISR 中,您不检查“缓冲区已满”情况。代码只是递增 rin[Channel] 而没有查看 rout[Channel]。所以整个缓冲区的数据可能会丢失。

示例:如果 rout[Channel] 等于零且 rin[Channel] 等于 UART_BUFSIZE-1,则 ISR 会将 rin[Channel] 设置为零。换句话说,缓冲区现在将显示为空并且数据丢失。

所以第一步是修复代码。

与其尝试制作可以与中断同步工作的数据结构,不如使用实际上可以安全用于中断的不同数据结构。

circular/ring 缓冲区通常用于需要在常规代码和中断代码之间共享的数据结构。

https://en.wikipedia.org/wiki/Circular_buffer

一个循环缓冲区是用两个索引实现的,每一方只修改一个索引,有一种reader/writer风格。这允许它被并发执行推入和弹出。

由于双索引以及索引总是在一个方向递增的事实,可能发生的最坏情况是中断在缓冲区快满时丢弃一个字节,因为它没有在最近对另一个索引的更改,这没什么大不了的。

如果你自己实现,记得测试一下,因为它很容易搞砸。