“传输缓冲区”是什么意思?和“接收缓冲器”在 UART 上下文中意味着什么?

发布于 2025-01-20 10:05:49 字数 3902 浏览 1 评论 0原文

我对发送/接收缓冲区的理解很大程度上与以太网系统有关,在以太网系统中,在传输整个数据之前,一些数据存储在缓冲区中。这与 UART 是否相同,其中一些数据存储在 UART 发送/接收缓冲区中,直到有 8 位(从而填满 UART 容量)或当系统被命令从缓冲区发送数据时?

我问这个问题的原因是因为我正在查看 MSP430FR5994 MCU涉及UART,我想完全理解代码。如果需要更多信息来回答我的问题,请告诉我。

如果有人感兴趣的话,有问题的代码。代码运行良好,我只想知道缓冲区在 UART 中的作用。

#include <msp430.h>

char RXbuffer[32];
const unsigned char maxRXbytes = sizeof(RXbuffer);
unsigned char RXbytes = 0;

const char message[] = "ok\n";
const unsigned char messageLength = sizeof(message);
unsigned char TXbytes = 0;

int main(void)
{
    WDTCTL = WDTPW | WDTHOLD;               // Stop Watchdog


    // Configure GPIO
    P2SEL0 &= ~(BIT0 | BIT1);
    P2SEL1 |= (BIT0 | BIT1);                // USCI_A0 UART operation (p93_s)

    // Disable the GPIO power-on default high-impedance mode to activate
    // previously configured port settings
    PM5CTL0 &= ~LOCKLPM5;

    // Startup clock system with max DCO setting ~8MHz
    CSCTL0_H = CSKEY_H;                       // Unlock CS registers
    CSCTL1 = DCOFSEL_3 | DCORSEL;             // Set DCO to 8MHz
    CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK;
    CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1;     // Set all dividers
    CSCTL0_H = 0;                             // Lock CS registers

    // Configure USCI_A0 for UART mode
    UCA0CTLW0 = UCSWRST;                    // Put eUSCI in reset (p788)
    UCA0CTLW0 |= UCSSEL__SMCLK;             // CLK = SMCLK
    // Baud Rate calculation for 19200
    // 8000000/(16*19200) = 26.042
    // Fractional portion = 0.042
    // User's Guide Table 21-4: UCBRSx = 0xD6
    // UCBRFx = int ( (52.083-52)*16) = 1
    UCA0BRW = 26;                           // 8000000/16/19200, p789
    UCA0MCTLW |= UCOS16 | UCBRF_1 | 0xD600; // UCOS16 = Oversampling enable, used when high frequency clk is used, probably divides everything by 16, UCBRF = fine turner when UCOS16 is active
                                            // 0xD600 is for first 8 bits,
    UCA0CTLW0 &= ~UCSWRST;                  // Initialize eUSCI
    UCA0IE |= UCRXIE;                       // Enable USCI_A0 RX interrupt

    __bis_SR_register(LPM3_bits | GIE);       // Enter LPM3, interrupts enabled
    __no_operation();                         // For debugger
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=EUSCI_A0_VECTOR
__interrupt void USCI_A0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(EUSCI_A0_VECTOR))) USCI_A0_ISR (void)
#else
#error Compiler not supported!
#endif
{
    switch(__even_in_range(UCA0IV,USCI_UART_UCTXCPTIFG))
    {
      case USCI_NONE: break;
      case USCI_UART_UCRXIFG:

          if(RXbytes < maxRXbytes)
          {
              // Get the byte
              RXbuffer[RXbytes] = UCA0RXBUF;

              // Check for either ASCII carriage return '\r', or linefeed '\n' character.
              // If true enable the TX interrupt to send response message
              if((RXbuffer[RXbytes] == '\r') || (RXbuffer[RXbytes] ==  '\n'))
              {
                  // Start message transmission
                  UCA0IE |= UCTXIE;

                  // Reset receive buffer index
                  RXbytes = 0;
              }
              else
                  RXbytes++;

          }
          break;

      case USCI_UART_UCTXIFG:

          // Transmit the byte
          UCA0TXBUF = message[TXbytes++];

          // If last byte sent, disable the interrupt
          if(TXbytes == messageLength)
          {
              UCA0IE &= ~UCTXIE;
              TXbytes = 0;
          }
          break;

      case USCI_UART_UCSTTIFG: break;
      case USCI_UART_UCTXCPTIFG: break;
      default: break;
    }
}

My understanding of what a transmit/receive buffer is largely related to ethernet systems, where some data is stored in the buffer before the whole data is transmitted. Is this the same with UART, where some data is stored in a UART transmit/receive buffer until there are 8 bits (thus filling up the UART capacity) or when the system is ordered to send the data from the buffer?

The reason I am asking this is because I am looking at some C code for the MSP430FR5994 MCU involving UART and I'd like to fully understand the code. Please let me know if more info is needed to answer my question.

The code in question, if anyone's interested. The code runs fine, I just want to know what the buffer does in UART.

#include <msp430.h>

char RXbuffer[32];
const unsigned char maxRXbytes = sizeof(RXbuffer);
unsigned char RXbytes = 0;

const char message[] = "ok\n";
const unsigned char messageLength = sizeof(message);
unsigned char TXbytes = 0;

int main(void)
{
    WDTCTL = WDTPW | WDTHOLD;               // Stop Watchdog


    // Configure GPIO
    P2SEL0 &= ~(BIT0 | BIT1);
    P2SEL1 |= (BIT0 | BIT1);                // USCI_A0 UART operation (p93_s)

    // Disable the GPIO power-on default high-impedance mode to activate
    // previously configured port settings
    PM5CTL0 &= ~LOCKLPM5;

    // Startup clock system with max DCO setting ~8MHz
    CSCTL0_H = CSKEY_H;                       // Unlock CS registers
    CSCTL1 = DCOFSEL_3 | DCORSEL;             // Set DCO to 8MHz
    CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK;
    CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1;     // Set all dividers
    CSCTL0_H = 0;                             // Lock CS registers

    // Configure USCI_A0 for UART mode
    UCA0CTLW0 = UCSWRST;                    // Put eUSCI in reset (p788)
    UCA0CTLW0 |= UCSSEL__SMCLK;             // CLK = SMCLK
    // Baud Rate calculation for 19200
    // 8000000/(16*19200) = 26.042
    // Fractional portion = 0.042
    // User's Guide Table 21-4: UCBRSx = 0xD6
    // UCBRFx = int ( (52.083-52)*16) = 1
    UCA0BRW = 26;                           // 8000000/16/19200, p789
    UCA0MCTLW |= UCOS16 | UCBRF_1 | 0xD600; // UCOS16 = Oversampling enable, used when high frequency clk is used, probably divides everything by 16, UCBRF = fine turner when UCOS16 is active
                                            // 0xD600 is for first 8 bits,
    UCA0CTLW0 &= ~UCSWRST;                  // Initialize eUSCI
    UCA0IE |= UCRXIE;                       // Enable USCI_A0 RX interrupt

    __bis_SR_register(LPM3_bits | GIE);       // Enter LPM3, interrupts enabled
    __no_operation();                         // For debugger
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=EUSCI_A0_VECTOR
__interrupt void USCI_A0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(EUSCI_A0_VECTOR))) USCI_A0_ISR (void)
#else
#error Compiler not supported!
#endif
{
    switch(__even_in_range(UCA0IV,USCI_UART_UCTXCPTIFG))
    {
      case USCI_NONE: break;
      case USCI_UART_UCRXIFG:

          if(RXbytes < maxRXbytes)
          {
              // Get the byte
              RXbuffer[RXbytes] = UCA0RXBUF;

              // Check for either ASCII carriage return '\r', or linefeed '\n' character.
              // If true enable the TX interrupt to send response message
              if((RXbuffer[RXbytes] == '\r') || (RXbuffer[RXbytes] ==  '\n'))
              {
                  // Start message transmission
                  UCA0IE |= UCTXIE;

                  // Reset receive buffer index
                  RXbytes = 0;
              }
              else
                  RXbytes++;

          }
          break;

      case USCI_UART_UCTXIFG:

          // Transmit the byte
          UCA0TXBUF = message[TXbytes++];

          // If last byte sent, disable the interrupt
          if(TXbytes == messageLength)
          {
              UCA0IE &= ~UCTXIE;
              TXbytes = 0;
          }
          break;

      case USCI_UART_UCSTTIFG: break;
      case USCI_UART_UCTXCPTIFG: break;
      default: break;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

夜声 2025-01-27 10:05:49

有软件缓冲区和硬件缓冲区。

UART 硬件外设具有硬件缓冲区、接收数据等待程序处理的接收缓冲区和数据等待 MCU 发送的发送缓冲区(将设置发送完成标志)。在某些 MCU 中,每个只有 1 字节大。其他的则遵循 FIFO 原则,具有更大的接收缓冲区。

您的示例中的 UCA0TXBUF 似乎是这个 tx 缓冲区/数据寄存器,显然它只有 1 个字节大。 USCI_UART_UCTXIFG 似乎是传输完成时设置的标志,并且设置为在完成时生成中断。

示例中的 RXbuffer 是 UART 驱动程序使用的软件缓冲区。


与您的问题无关,此代码有几个问题和潜在的错误正在等待爆发:

  • 使用char作为原始数据总是不正确的,因为char具有实现定义的签名如果将原始数据存储在 MSB 中,则可能会变成负数。它不是可移植类型,除了文本字符串之外不应该用于任何其他类型。请改用 unsigned charuint8_t

  • 代码中没有针对竞争条件的保护,因此,如果主程序在 ISR 写入时访问 RXbuffer,您将遇到各种奇怪的错误。

  • 无法防止错误的编译器优化。如果您的编译器没有意识到 ISR 从来不是由软件调用而是由硬件调用,则优化器可能会破坏代码。为了防止这种情况,所有共享变量都应该声明为易失性(如果您使用 DMA 缓冲区,情况也是如此)。

    查看:

  • 微控制器系统不会从 main() 返回,因此 int main (void) 总是错误的。您应该使用实现定义的形式 void main (void)(如果使用 gcc 编译,请使用 -ffreestand)并结束 main() 函数使用 for(;;) {} 循环。

  • 您可能想要处理 UART 帧错误 = 数据损坏或错误波特率,以及 UART 溢出错误 = 硬件缓冲区在 MCU 清空之前被覆盖。这些通常也可以通过中断标志来获得。

There are software buffers and hardware buffers.

The UART hardware peripheral has hardware buffers, a rx buffer where received data is waiting to get handled by the program, and a tx buffer where data is waiting for the MCU to transmit it (a tx complete flag will get set). In some MCUs these are just 1 byte large each. Others have a larger rx buffers following a FIFO principle.

UCA0TXBUF in your example appears to be this tx buffer/data register and apparently it is just 1 byte large. USCI_UART_UCTXIFG appears to be the flag set upon transmission complete and it's set to generate an interrupt when done.

The RXbuffer in your example is a software buffer used by the UART driver.


Unrelated to your question, this code has several problems and latent bugs waiting to explode:

  • Using char for raw data is always incorrect, since char has implementation-defined signedness and might turn into negative number if you store raw data in the MSB. It is not a portable type and shouldn't be used for anything but text strings. Use unsigned char or uint8_t instead.

  • There is no protection from race conditions in your code, so in case the main program is accessing RXbuffer while the ISR is writing to it, you will get all manner of weird bugs.

  • There is no protection against incorrect compiler optimizations. In case your compiler doesn't realize that an ISR is never called by software but by hardware, the optimizer might break the code. To prevent this all shared variables should be declared volatile (same goes in case you would use DMA buffers instead).

    Check out:

  • Microcontroller systems do not return from main() so int main (void) is always wrong. You should use the implementation-defined form void main (void) (use -ffreestanding if compiling with gcc) and end the main() function with a for(;;) {} loop.

  • You probably want to handle UART framing errors = data corruption or wrong baudrate, as well as UART overrun errors = hardware buffers were overwritten before MCU emptied them. These are typically available through interrupt flags too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文