3.19. 程式碼中的 UART

machine.UART 封裝了一個硬體 UART 通道。以匯流排 id 與鮑率建構一個實例;其餘所有項目都有合理的預設值:

from machine import UART

uart = UART(3, baudrate=115200)

id 選擇要使用哪個硬體 UART;其值取決於板子(請參閱 OpenMV 開發板 以查看特定相機上可用的匯流排編號與接腳配置)。預設的框架格式為 8 個資料位元、無同位、一個停止位元——也就是大家所預期的「8N1」。

3.19.1. 寫入與讀取

UART 輸出透過 write() 進行:

uart.write("hello\n")
uart.write(b"\x01\x02\x03")

write 接受 str(以 UTF-8 編碼)或 bytes / bytearray。一旦位元組排入 TX 緩衝區,它就會立即回傳;硬體則在背景中完成將它們時脈輸出的工作。

讀取透過三種方法進行,視需求而定:

n = uart.any()              # bytes available to read right now
data = uart.read(8)         # up to 8 bytes, or None on timeout
line = uart.readline()      # bytes ending in '\n', or None on timeout

any() 在不阻塞的情況下檢查 RX 緩衝區。read() 讀取固定數量的位元組,若逾時(可透過建構子中的 timeout 引數設定)先到期,則回傳 Nonereadline() 讀取直到並包含下一個換行符為止,對於以行為基礎的協定很有用。

一個簡單的迴圈,回送它所接收到的任何資料:

uart = UART(3, baudrate=115200, timeout=100)

while True:
    if uart.any():
        data = uart.read()
        uart.write(data)

不帶長度引數的 read() 會持續讀取位元組,直到接收線路維持安靜達到所設定的 timeout 為止,然後回傳累積到的內容。在 timeout=100 的情況下,這裡每次呼叫都會回傳一 位元組——也就是發送端在位元組之間沒有出現 100 ms 間隔的情況下時脈輸出的所有內容。若沒有逾時,這個呼叫就沒有訊號可判斷發送端是否已結束,可能會無限期地卡住。

3.19.2. 使用 struct 處理二進位資料

在線路上傳送整數與浮點數正是 struct 模組的用途。它使用一個格式字串將固定寬度的值打包成一個 bytes 物件,該格式字串指定了位元組順序以及每個欄位的型別:

import struct

uart.write(struct.pack("<lhb", count, temperature_x100, status))

開頭的 "<" 選擇小端序(little-endian)位元組順序;"l" 是 32 位元有號整數,"h" 是 16 位元有號整數,"b" 是 8 位元有號整數。另一端則以 相同 的格式字串解包:

payload = uart.read(7)        # 4 + 2 + 1 = 7 bytes
count, temperature_x100, status = struct.unpack("<lhb", payload)

格式字串是兩端之間的約定。任何不相符——位元組順序錯誤、型別大小錯誤、欄位順序錯誤——都會產生無意義的值。

3.19.3. 緩衝與高速率讀取

一個呼叫 any()read() 的輪詢迴圈,只要主迴圈疊代得夠快,能在接收緩衝區填滿之前將其排空,就能靠自己跟上大部分的流量。當速率攀升或迴圈很忙碌時,有兩個建構子選項就變得重要。

rxbuf 設定軟體 RX 緩衝區的大小。預設值為數百個位元組;對於每陣發出數百個位元組的感測器,或當主迴圈在兩次輪詢之間執行耗時工作時,加大緩衝區能讓進來的位元組在迴圈忙於其他事務時不至於被丟棄:

uart = UART(3, baudrate=115200, timeout=100, rxbuf=4096)

當緩衝區已滿時抵達的任何資料都會遺失;請將 rxbuf 設定為足以涵蓋兩次排空之間最長的間隔。

對於持續性的高速率,readinto() 會讀入一個預先配置好的緩衝區,而不是每次呼叫都回傳一個新的 bytes 物件:

buf = bytearray(256)

while True:
    n = uart.readinto(buf)
    if n:
        process(buf, n)

讀取路徑上不會發生記憶體配置,這在堆積(heap)已碎片化時,或當配置延遲否則會超出位元組間的時序預算時,就顯得很重要。