What is UART Link to heading
UART is a way to asynchronously transmit serial data between 2 devices.
serial means data gets sent 1 bit at a time
Each device has a Transmitter(Tx)
and a Receiver(Rx)
and are connected in a cross so each Tx
is connected to the Rx
of the other device.
Recourses Link to heading
So far we didn’t require information about the specifics of the on board chip. This chapter we will be talking a lot about register addresses and for this we need a details about the peripherals of the PI.
Gladly Raspberry Pi has documented this on their website with all relevant links to the PDFs.
The raspberry pi 3 B+ uses the BCM2837B0
Broadcom chip. The documentation says here that we can refer to the BCM2837
.
The BCM2837
mentions: “The underlying architecture of the BCM2837
is identical to the BCM2836
”
Okay so lets check BCM2836
: “The underlying architecture in BCM2836
is identical to BCM2835
”
Finally we reached the end of this chain and the BCM2835
has the peripheral specification.
Download this file and keep it somewhere safe, because we’ll need it.
UART PL011 vs Mini UART Link to heading
The Broadcom chip comes with two UARTS.
They differ in a few areas but the most important part is that PL011 is tied to its own clock, while Mini UART uses the system clock. This means that Mini UART is more prone to data loss making it less reliable so we’ll focus on PL011 first.
Configuring UART Link to heading
Baud rate Link to heading
The baud rate is a measurement of how many symbols changes can occur per second. It’s important that both devices use the same rate or else the data can become scrambled.
The Baud rate is usually just referred as single number and can range from 110 bps to 1_000_000 bps but the most common one is 115_200
that gives use 115 kbps of data transfer, which should be enough for our use case.
To configure the baud rate we need to set the Integer Baud rate divisor (IBRD
) and the Fractional Baud rate divisor (FBRD
).
In the peripherals PDF(page 177) we can find the UART Address Map that tells us the base address and the offset.
0x7E20100
according to the documentation BUT thats NOT correct in our environment. The 0x7E...
address is for peripherals. We are developing for the CPU so we have to use the physical address that start at 0x3F...
Our base address for the UART registers is 0x3E20100
.
Lets calculate IBRD/FBRD Link to heading
Yes Yes you are seeing math in this section, ill try to be quick and get back to coding.
BAUDDIV = (FUARTCLK/(16 Baud rate))
. The integer divisor (floor(BAUDDIV)
) would be the IBRD
and the fractional devisor (round((BAUDDIV - IBRD) × 64)
) would be the FBRD
.
For this guide we’ll use a different formula:
\[ \text{Baud Rate} = \frac{FUARTCLK}{16 \times \left(\text{IBRD} + \frac{\text{FBRD}}{64} \right)} \]Because our goal is to get the IBRD
and FBRD
we can do a switcheroo and get:
Now times both sides by 4:
\[ \text{ScaledBaudDivisor} = \\ 64 \times \text{IBRD} + \text{FBRD} = \frac{FUARTCLK \times 4}{\text{Baud Rate}} \]And at last:
\[ IBRD = ScaledBaudDivisor/64; \]\[ FBRD = ScaledBaudDivisor\%64; \]
Why do all this math though? Cant we just use the one mentioned by Broadcom. And yes we can but with this version we can save us some floating point math. The .floor()
function is also from the std
library so we’d have to implement it ourself.
Now in code Link to heading
const BAUD: u32 = 115200;
const UART_CLK: u32 = 48_000_000;
pub fn configure_uart() {
let scaled_baud_rate = (UART_CLK * 4) / BAUD;
let ibrd = scaled_baud_rate / 64;
let fbrd = scaled_baud_rate % 64;
//...
For now we’ll use a constant for UART_CLK
but in the future we can read it from a register.
Summary Link to heading
In this chapter we mainly focused on the functionality of UART and how we can calculate the baud rate. Next chapter we will configure everything and hopefully have our first REAL “Hello World!”.