Try   HackMD

在 Raspberry Pi 4B 上設定 UART 連線

contributed by < OscarShiang >

硬體準備

  • Raspberry Pi 4B
  • TTL to USB (P2303HXDL)
  • micro SD card
  • USB type A to type C

檢查 UART 是否可以正常運作

為了方便後續使用 bare metal 設定時可以先排除 UART 連接錯誤的問題,我們可以先使用 Raspberry Pi 官方提供的 Raspbian 來進行測試

燒錄系統

最簡單的方法就是使用 Raspberry Pi 官方所提供的 Raspberry Pi Imager 來進行下載與燒錄。

或是自行從 Operating system images 下載映像檔並透過 rufus 或是 UNetbootin 進行燒錄。

連接 UART

請參考 Raspberry Pi 的接腳對應圖(UART at Raspberry Pi GPIO Pinout

將 TTL 的 GND, RXTX 分別連接到以下的接腳:

  • 將 TTL 線的 GND 接到 6 號接腳
  • 將 TTL 的 RX 接到 8TXD 接腳
  • 將 TTL 的 TX 接到 10RXD 接腳

安裝 TTL 線的驅動程式

為了可以在電腦上接收來自 TTL 線的訊號,我們首先需要將對應的驅動程式安裝在系統中,安裝的教學可以參考 [基礎] 從序列埠登入到 Raspberry Pi 這篇。

需要修改放在 boot/ 底下的 config.txt:加入 enable_uart=1core_freq_min=500 兩項設定

測試結果

完成先前的配置之後,我們就可以把燒錄好 raspbian 的 micro SD card 裝到 raspberry pi 上並接上電源開機。

如果連線成功的話,我們就會看到 kernel 開機的訊息:

<...>

[    3.176335] Segment Routing with IPv6
[    3.214711] systemd[1]: systemd 241 running in system mode. (+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD -IDN2 +IDN -PCRE2 default-hierarchy=hybrid)
[    3.237254] systemd[1]: Detected architecture arm64.
[    3.267411] systemd[1]: Set hostname to <raspberrypi>.
[    3.828087] random: systemd: uninitialized urandom read (16 bytes read)
[    3.846401] random: systemd: uninitialized urandom read (16 bytes read)
[    3.854305] systemd[1]: Listening on Journal Socket.
[    3.860399] random: systemd: uninitialized urandom read (16 bytes read)
[    3.872492] systemd[1]: Mounting Kernel Debug File System...
[    3.883067] systemd[1]: Starting Create list of required static device nodes for the current kernel...
[    3.894427] systemd[1]: Created slice User and Session Slice.
[    3.901770] systemd[1]: Listening on fsck to fsckd communication Socket.
[    3.909961] systemd[1]: Listening on Syslog Socket.
[    3.920163] systemd[1]: Starting Set the console keyboard layout...

Raspbian GNU/Linux 10 raspberrypi ttyS0

raspberrypi login:

使用 bare metal 程式測試 UART 功能

這邊的實作主要是參考以下兩個 Repo

硬體相關設定

因為 Raspberry Pi 使用 memory-mapped IO,所以我們首先需要定義相關的 register 的位址,詳細的描述可以在 BCM2711 ARM Peripherials 文件中找到

UART

我們在這次所使用到的是 raspberry pi 的 mini UART,需要使用的 register 如下所列:

Offset Name Description
0x00 AUX_IRQ Auxiliary Interrupt status
0x04 AUX_ENABLES Auxiliary enables
0x40 AUX_MU_IO_REG Mini UART I/O Data
0x44 AUX_MU_IER_REG Mini UART Interrupt Enable
0x48 AUX_MU_IIR_REG Mini UART Interrupt Identify
0x4c AUX_MU_LCR_REG Mini UART Line Control
0x50 AUX_MU_MCR_REG Mini UART Modem Control
0x54 AUX_MU_LSR_REG Mini UART Line Status
0x58 AUX_MU_MSR_REG Mini UART Modem Status
0x5c AUX_MU_SCRATCH Mini UART Scratch
0x60 AUX_MU_CNTL_REG Mini UART Extra Control
0x64 AUX_MU_STAT_REG Mini UART Extra Status
0x68 AUX_MU_BAUD_REG Mini UART Baudrate

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
根據 BCM2711B0 - Raspberry Pi Forums 指出 Raspberry Pi 4B 的 Peripherial Base 應該設定為 0xFE000000 但是在 BCM 2711 ARM Peripherials 中卻說要設定為 0x7E000000

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
在 BCM2711 當中,預設使用 Legacy master addresses 來表示;但在 bare metal 的環境下,VPU 預設啟動 Low Peripheral Mode,所以實際使用的 Peripheral Base 應為 0xFE000000

設定好相關數值之後我們就可以來實作對應的函式:

  • uart_init(): 初始化 mini UART 的硬體設定 (baud rate 設定為 115200)
  • uart_send(): 使用 serial 傳送一個字元
  • uart_getc(): 接收一個字元
  • uart_puts(): 輸出一段字串

baud rate 計算

我們可以從 BCM 2711 ARM Peripherials 得知 baud rate 的計算公式:

baud rate=system_clock_freq8×(baudrate_reg+1)

因為我們想要使用的是 115200 baud 所以可以算得

115200=system_clock_freq8×(baudrate_reg+1)baudrate_reg=5000000008×1152001=541.53

根據計算的結果我們將 AUX_MU_BAUD_REG 設定為 541

這邊的

system_clock_freq=500000000 是因為在上一階段我們在 config.txt 設定 core_freq_min=500

開機測試

透過 cross compiler 我們可以將剛剛實作的 uart.cstart.S, main.c, link.ld 等等必要的檔案進行連結,並產生 kernel8.img 的映像檔。

因為我們產生的只是單純的 bare metal 程式,因此可以使用在前一個階段燒錄好的 SD card 來進行接下來的操作。我們只需要將對應的系統映像檔放入,bootloader 就會幫我們在開機的時候載入對應的程式。

所以我們需要進行以下的操作:

  • 將 boot 磁碟區中的 kernel* 檔案刪除,並將剛剛產生的 kernel8.img 寫入
  • 接上 TTL 線,並開啟 putty 連接對應的裝置
  • 將 SD Card 放入 raspberry pi 中,並連接電源

如果順利驅動的話就可以看到 putty 的畫面印出以下訊息並可以任意輸入字元:

Hello World!

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
實作詳細的修改可以參考 961e7af

參考資料

tags: raspi