PL011 UART

Mini UART 使用 System Clock,PL011 UART (UART0) 則擁有自己的 Clock,可以透過 Mailbox 對其做設定。在設定完 Clock 後,其餘剩下的流程與 Mini UART 就非常相似了。

PL011 UART Registers

uart0.c

uart_init

流程:

  1. Configure the UART clock frequency by mailbox. (clock-id)
  2. Enable GPIO (almost same as mini UART).
  3. Set IBRD and FBRD to configure baud rate.
  4. Set LCRH to configure line control.
  5. Set CR to enable UART.

注意: PL011 UART 的 alternate function 需設定為 ALT0 (See UART Introduction)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
void uart_init() {
    *UART0_CR = 0;  // turn off UART0

    /* Configure UART0 Clock Frequency */
    unsigned int __attribute__((aligned(16))) mbox[9];
    mbox[0] = 9 * 4;
    mbox[1] = MBOX_CODE_BUF_REQ;
    // tags begin
    mbox[2] = MBOX_TAG_SET_CLOCK_RATE;
    mbox[3] = 12;
    mbox[4] = MBOX_CODE_TAG_REQ;
    mbox[5] = 2;        // UART clock
    mbox[6] = 4000000;  // 4MHz
    mbox[7] = 0;        // clear turbo
    mbox[8] = 0x0;      // end tag
    // tags end
    mbox_call(mbox, 8);

    /* Map UART to GPIO Pins */
    // 1. Change GPIO 14, 15 to alternate function
    register unsigned int r = *GPFSEL1;
    r &= ~((7 << 12) | (7 << 15));  // Reset GPIO 14, 15
    r |= (4 << 12) | (4 << 15);     // Set ALT0
    *GPFSEL1 = r;
    // 2. Disable GPIO pull up/down (Because these GPIO pins use alternate functions, not basic input-output)
    // Set control signal to disable
    *GPPUD = 0;
    // Wait 150 cycles
    r = 150;
    while (r--) {
        asm volatile("nop");
    }
    // Clock the control signal into the GPIO pads
    *GPPUDCLK0 = (1 << 14) | (1 << 15);
    // Wait 150 cycles
    r = 150;
    while (r--) {
        asm volatile("nop");
    }
    // Remove the clock
    *GPPUDCLK0 = 0;

    /* Configure UART0 */
    *UART0_IBRD = 0x2;        // Set 115200 Baud
    *UART0_FBRD = 0xB;        // Set 115200 Baud
    *UART0_LCRH = 0b11 << 5;  // Set word length to 8-bits
    *UART0_ICR = 0x7FF;       // Clear Interrupts

    /* Enable UART */
    *UART0_CR = 0x301;
}

uart_read

流程:

  1. Check FR
  2. Read from DR
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
char uart_read() {
    // Check data ready field
    do {
        asm volatile("nop");
    } while (*UART0_FR & 0x10);
    // Read
    char r = (char)(*UART0_DR);
    // Convert carrige return to newline
    return r == '\r' ? '\n' : r;
}

uart_write

流程:

  1. Check FR
  2. Write to DR
1
2
3
4
5
6
7
8
void uart_write(unsigned int c) {
    // Check transmitter idle field
    do {
        asm volatile("nop");
    } while (*UART0_FR & 0x20);
    // Write
    *UART0_DR = c;
}

Reference

PrimeCell® UART (PL011)

https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface

comments powered by Disqus