Lab2: PL011 UART

Mini UART基於system clock,PL011有著自己的clock,兩著皆可藉由mailbox去設定clock,之後在基於clock去設定baud rate register。

Setup

  1. Disable UART0
  2. Configure the clock frequency via mailbox
  3. Map UART0 to GPIO pins
  4. Set IBRD and FBRD to configure baud rate
  5. Set LCRH to configure line control
  6. Enable UART0
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
52
53
54
void uart_init(unsigned int baudrate) {
/* 1. Turn off UART0 */
*UART0_CR = 0;

/* 2. Set up clock for consistent divisor values */
mbox[0] = 9 * 4;
mbox[1] = MBOX_REQUEST;
mbox[2] = MBOX_TAG_SETCLKRATE; // set clock rate
mbox[3] = 12; // Buffer's length
mbox[4] = MBOX_TAG_REQ;
mbox[5] = 2; // UART clock
mbox[6] = 4000000; // 4Mhz
mbox[7] = 0; // clear turbo
mbox[8] = MBOX_TAG_LAST;
mbox_call(MBOX_CH_PROP);

/* 3. Map UART0 to GPIO pins
*
* Goal: Set GPIO14, 15 to ALT0
* GPIO 0-9 => GPFSEL0
* GPIO 10-19 => GPFSEL1
* GPIO 20-29 => GPFSEL2
* ...
* GPIO14-15 use FSEL4 and FSEL5 field in GPFSEL1.
*/
register unsigned int r;
r = *GPFSEL1;
r &= ~((7 << 12) | (7 << 15));
r |= (4 << 12) | (4 << 15); // ALT0 = 100
*GPFSEL1 = r;

/* 4. Disable GPIO pull-up/down */
*GPPUD = 0;
// Wait 150 cycles (required set-up time for the control signal)
r = 150;
while (r--) {
asm volatile("nop");
}
*GPPUDCLK0 = (1 << 14) | (1 << 15);
// Wait 150 cycles (required hold time for the control signal)
r = 150;
while (r--) {
asm volatile("nop");
}
*GPPUDCLK0 = 0; // Flush GPIO setup

// 5. Enable Tx, Rx
float buddiv = 4000000 / baudrate / 16;
*UART0_ICR = 0x7FF; // clear interrupts
*UART0_IBRD = (unsigned int)buddiv; // 115200 baud, IBRD=0x2, FBRD=0xB
*UART0_FBRD = (unsigned int)((buddiv - *UART0_IBRD) * 64);
*UART0_LCRH = 0x7 << 4; // 8bits, enable FIFOs
*UART0_CR = 0x301; // enable Tx, Rx, UART
}

參考頻率FUARTCLK與Baud rate關係:

1
BAUDDIV = (FUARTCLK/(16 Baud rate))

BAUDDIV由整數IBRD及小數FBRD(6 bits)組成,若

  • FUARTCLK=4MHZ
  • Baud rate=115200

  • BAUDDIV=4M/(16*115200)=2.17
  • IBRD=2
  • FBRD=0.17*64=11

Read

  1. Check FR
  2. Read from DR
1
2
3
4
5
6
7
8
char uart_getc() {
/* wait until something is in the buffer */
do {
asm volatile("nop");
} while (*UART0_FR & 0x10);
char c = (char)(*UART0_DR);
return c == '\r' ? '\n' : c;
}

Write

  1. Check FR
  2. Write to DR
1
2
3
4
5
6
7
void uart_putc(unsigned int c) {
/* Wait until we can send */
do {
asm volatile("nop");
} while (*UART0_FR & 0x20);
*UART0_DR = c;
}

Reference