Search code examples
cembeddeduart

UARTs & Registers


So I am new to this and trying to learn about registers and UARTs and have been given the following code to study.

#include <stdint.h>

typedef volatile struct {
    uint32_t DR;
    uint32_t RSR_ECR;
    uint8_t reserved1[0x10];
    const uint32_t FR;
    uint8_t reserved2[0x4];
    uint32_t LPR;
    uint32_t IBRD;
    uint32_t FBRD;
    uint32_t LCR_H;
    uint32_t CR;
    uint32_t IFLS;
    uint32_t IMSC;
    const uint32_t RIS;
    const uint32_t MIS;
    uint32_t ICR;
    uint32_t DMACR;
} pl011_T;

enum {
    RXFE = 0x10,
    TXFF = 0x20,
};

pl011_T * const UART0 = (pl011_T *)0x101f1000;
pl011_T * const UART1 = (pl011_T *)0x101f2000;
pl011_T * const UART2 = (pl011_T *)0x101f3000;

static inline char upperchar(char c) {
    if((c >= 'a') && (c <= 'z')) {
        return c - 'a' + 'A';
    } else {
        return c;
    }
}

static void uart_echo(pl011_T *uart) {
    if ((uart->FR & RXFE) == 0) {
        while(uart->FR & TXFF);
        uart->DR = upperchar(uart->DR);
    }
}

void c_entry() {
    for(;;) {
        uart_echo(UART0);
        uart_echo(UART1);
        uart_echo(UART2);
    }
}

I am just wondering if someone could explain how the pl011 DR and FR registers transmit and receive data over the associated UART peripheral. Any help at all would be much appreciated.


Solution

  • There's some nice documentation on this UART here - http://infocenter.arm.com/help/topic/com.arm.doc.ddi0183g/DDI0183G_uart_pl011_r1p5_trm.pdf

    The way this program works is influenced by whether the UART is in FIFO mode or not. I have not read enough of the doco to know which is the default state. The operation of the Tx and Rx differ slightly depending on this mode. It looks like the code is working only on single words, so possibly it's not in FIFO mode (or it doesn't matter for this code).

    • FR is the UART Flag Register (AKA UARTFR). This contains a bunch of bits that can be queried to see what the state of the UART is. Two important ones for this question are:

      • TXFF is a bit in FR, it becomes 1 when the transmit buffer is full.
      • RXFE is a bit in FR, it becomes 1 when the receive buffer is empty
    • DR is the UART Data Register (AKA UARTDR). This holds the data to be transmitted, and data that has been received.

    So Looking at the main working part of the code ~

    static void uart_echo( pl011_T *uart ) 
    {
        if ( ( uart->FR & RXFE ) == 0 )      // While the receive buffer is NOT empty
        {
            while( uart->FR & TXFF );        // Do <nothing> while the Tx buffer is full
    
            // Read the content of the Data Register, converting it to uppercase(),
            // then make a write to the DR, which initiates a transmit
            uart->DR = upperchar(uart->DR);  
        }
    }  
    

    So this function is echoing back whatever it reads, but in uppercase. The program is calling this for each of the three UART's, in turn.