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 引數設定)先到期,則回傳 None。readline() 讀取直到並包含下一個換行符為止,對於以行為基礎的協定很有用。
一個簡單的迴圈,回送它所接收到的任何資料:
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)已碎片化時,或當配置延遲否則會超出位元組間的時序預算時,就顯得很重要。