03. UART "Hello World" - First Output from Our OS¶
Now that our kernel boots, let's add UART (serial communication) to see "Hello World!" on the screen.
What is UART?¶
UART (Universal Asynchronous Receiver/Transmitter) is a serial communication protocol. On the Raspberry Pi 4B, we use the Mini UART (UART1) which is mapped to GPIO pins 14 (TX) and 15 (RX).
Hardware Setup¶
Connecting a USB-to-TTL Cable¶
| USB-TTL Cable | Raspberry Pi GPIO |
|---|---|
| GND (Black) | Pin 6 (GND) |
| RX (White) | Pin 8 (GPIO 14, TX) |
| TX (Green) | Pin 10 (GPIO 15, RX) |
Don't Connect 5V!
The Raspberry Pi GPIO operates at 3.3V. Connecting 5V can damage the board.
Opening a Serial Terminal¶
Use PuTTY or Tera Term: - Port: COM3 (or whatever shows up) - Baud: 115200
Understanding Memory-Mapped I/O¶
On bare-metal systems, we control hardware by reading/writing to specific memory addresses.
Raspberry Pi 4B Memory Map¶
| Device | Base Address |
|---|---|
| GPIO | 0xFE200000 |
| Mini UART | 0xFE215000 |
Legacy vs RPi 4
- Raspberry Pi 1-3:
0x3F000000 - Raspberry Pi 4:
0xFE000000
UART Register Map¶
Implementing the UART Driver¶
File: kernel/kernel.c
GPIO Configuration¶
First, we configure GPIO pins 14 and 15 to use "Alt Function 5" (UART mode):
Sending Characters¶
The Main Function¶
Building and Testing¶
1. Build the Kernel¶
Verify kernel8.img was created.
2. Prepare the SD Card¶
Copy these files to the SD card:
- bootcode.bin
- start4.elf
- fixup4.dat
- kernel8.img (your kernel)
3. Connect Serial Cable¶
Connect the USB-to-TTL cable as described above.
4. Power On¶
- Insert the SD card
- Open the serial terminal (
screen /dev/ttyUSB0 115200) - Power on the Raspberry Pi
You should see:
5. Test Echo¶
Type any key in the terminal. It should echo back!
Troubleshooting¶
Mini UART Baud Rate Calculation
The Mini UART (UART1) baud rate depends on the GPU core frequency, not a fixed clock. The formula is:
For Raspberry Pi 4 at 500MHz core frequency:
Therefore: AUX_MU_BAUD = 541
Critical Requirements:
- Your config.txt must include core_freq_min=500 to lock the GPU core frequency
- Without this, dynamic frequency scaling will cause garbled output even if your code is correct
- See Article 02 for complete config.txt configuration
If changing baud rate or core frequency:
- For 9600 baud @ 500MHz: AUX_MU_BAUD = 6509
- For 115200 @ 250MHz: AUX_MU_BAUD = 270
- Always recalculate using the formula above
| Problem | Solution |
|---|---|
| No output | Check wiring (TX/RX might be swapped) |
| Garbled characters | Verify config.txt has core_freq_min=500 and initial_turbo=0 (see warning above) |
| Wrong baud rate | Recalculate AUX_MU_BAUD using formula with actual core frequency |
| Garbled text | Wrong baud rate (should be 115200) |
| Raspberry Pi won't boot | Missing firmware files (start4.elf, etc.) |
Complete Source Code¶
The full implementation is available in the os-rasp repository:
kernel/boot.S- Assembly entry pointkernel/kernel.c- UART driver and main functionkernel/linker.ld- Linker script
What's Next?¶
Congratulations! You've successfully: - ✅ Set up a bare-metal development environment - ✅ Written ARM64 assembly boot code - ✅ Implemented a UART driver - ✅ Printed "Hello World" from your own OS!
Future topics: - Exception handling and interrupts - Timers - Memory management (MMU) - Multitasking
Stay tuned for more articles!