【问题标题】:Writing safe UART interrupt buffer写入安全的 UART 中断缓冲区
【发布时间】:2021-11-21 07:36:58
【问题描述】:

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

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

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;
}

【问题讨论】:

  • volatile unsigned int IIR 波动的目的是什么?
  • @tstanisl 它指的是寄存器 SR
  • 不,它是SR register 在某个时间点拥有的内容的副本
  • 当用户端代码在操作 shared 变量时,它可以暂时禁用/屏蔽中断。 (互斥锁是个坏主意,因为它可能导致 ISR 无限期地旋转)
  • @tstanisl 另一个问题的好点。原来的问题怎么样>

标签: c firmware


【解决方案1】:

您的代码存在功能错误。

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

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

所以第一步是修复代码。

【讨论】:

  • 你能修复那个代码吗?请问同步问题?加 1
  • @AhmedSaleh 如果 UART_BUFSIZE 是 2 的幂,我会让 rinrout 在达到 MAX 时换行。换句话说,我会避免这样的代码:if(rin[Channel]>=UART_BUFSIZE) rin[Channel]=0;
  • 不,我不会那样做。只是不断增加。当它达到 65535 时,它将在下一次增量时自动回零。然后在访问缓冲区时使用% 运算符。
  • 点赞:result=rbuf[Channel][rout[Channel] % UART_BUFSIZE];
  • 你也需要它rbuf[Channel][rin[Channel]]
【解决方案2】:

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 2021-06-16
    • 1970-01-01
    • 2022-01-03
    • 2014-10-30
    • 2014-03-26
    • 2017-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多