Home > front end >  What does "transmit buffer" and "receive buffer" mean in the UART context?
What does "transmit buffer" and "receive buffer" mean in the UART context?

Time:04-10

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

CodePudding user response:

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.

  • Related