# 在 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](https://www.raspberrypi.org/software/) 來進行下載與燒錄。 或是自行從 [Operating system images](https://www.raspberrypi.org/software/operating-systems/) 下載映像檔並透過 [rufus](https://rufus.ie) 或是 [UNetbootin](https://unetbootin.github.io) 進行燒錄。 ### 連接 UART 請參考 Raspberry Pi 的接腳對應圖([UART at Raspberry Pi GPIO Pinout](https://pinout.xyz/pinout/uart)) 將 TTL 的 `GND`, `RX` 與 `TX` 分別連接到以下的接腳: - 將 TTL 線的 `GND` 接到 `6` 號接腳 - 將 TTL 的 `RX` 接到 `8` 號 `TXD` 接腳 - 將 TTL 的 `TX` 接到 `10` 號 `RXD` 接腳 ### 安裝 TTL 線的驅動程式 為了可以在電腦上接收來自 TTL 線的訊號,我們首先需要將對應的驅動程式安裝在系統中,安裝的教學可以參考 [[基礎] 從序列埠登入到 Raspberry Pi](https://www.raspberrypi.com.tw/tag/usb-to-ttl/) 這篇。 :::warning 需要修改放在 `boot/` 底下的 `config.txt`:加入 `enable_uart=1` 與 `core_freq_min=500` 兩項設定 ::: ### 測試結果 完成先前的配置之後,我們就可以把燒錄好 raspbian 的 micro SD card 裝到 raspberry pi 上並接上電源開機。 如果連線成功的話,我們就會看到 kernel 開機的訊息: ```shell <...> [ 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 - [bztsrc / raspi3-tutorial](https://github.com/bztsrc/raspi3-tutorial) - [isometimes / rpi4-osdev](https://github.com/isometimes/rpi4-osdev) ### 硬體相關設定 因為 Raspberry Pi 使用 memory-mapped IO,所以我們首先需要定義相關的 register 的位址,詳細的描述可以在 [BCM2711 ARM Peripherials](https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf) 文件中找到 #### 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 | :::warning :question: 根據 [BCM2711B0 - Raspberry Pi Forums](https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=244479) 指出 Raspberry Pi 4B 的 Peripherial Base 應該設定為 `0xFE000000` 但是在 [BCM 2711 ARM Peripherials](https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf) 中卻說要設定為 `0x7E000000` ? ::: :::info :bulb: 在 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](https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf) 得知 baud rate 的計算公式: $$ baud\ rate = \frac{system\_clock\_freq}{8 \times (baudrate\_reg + 1)} $$ 因為我們想要使用的是 115200 baud 所以可以算得 $$ \begin{align} 115200 &= \frac{system\_clock\_freq}{8 \times (baudrate\_reg + 1)} \\\\ baudrate\_reg &= \frac{500000000}{8 \times 115200} - 1 \\\\ &= 541.53 \end{align} $$ 根據計算的結果我們將 `AUX_MU_BAUD_REG` 設定為 `541` :::warning 這邊的 $system\_clock\_freq = 500000000$ 是因為在上一階段我們在 `config.txt` 設定 `core_freq_min=500` ::: ### 開機測試 透過 cross compiler 我們可以將剛剛實作的 `uart.c` 與 `start.S`, `main.c`, `link.ld` 等等必要的檔案進行連結,並產生 `kernel8.img` 的映像檔。 因為我們產生的只是單純的 bare metal 程式,因此可以使用在前一個階段燒錄好的 SD card 來進行接下來的操作。我們只需要將對應的系統映像檔放入,bootloader 就會幫我們在開機的時候載入對應的程式。 所以我們需要進行以下的操作: - 將 boot 磁碟區中的 kernel* 檔案刪除,並將剛剛產生的 `kernel8.img` 寫入 - 接上 TTL 線,並開啟 putty 連接對應的裝置 - 將 SD Card 放入 raspberry pi 中,並連接電源 如果順利驅動的話就可以看到 putty 的畫面印出以下訊息並可以任意輸入字元: ```shell Hello World! ``` :::success :pencil: 實作詳細的修改可以參考 [`961e7af`](https://github.com/OscarShiang/raspi3-tutorial/commit/961e7af19f2d9e8feefdc8962c3f42c5bdc82d92) ::: ## 參考資料 - [[基礎] 從序列埠登入到 Raspberry Pi](https://www.raspberrypi.com.tw/tag/usb-to-ttl/) - [BCM 2711 ARM Peripherials](https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf) - [bztsrc / raspi3-tutorial](https://github.com/bztsrc/raspi3-tutorial) - [isometimes / rpi4-osdev](https://github.com/isometimes/rpi4-osdev) - [BCM2711B0 - Raspberry Pi Forums](https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=244479) - [UART configuration - Raspberry Pi Documentation](https://www.raspberrypi.org/documentation/configuration/uart.md) ###### tags: `raspi`