3.19. UART ในโค้ด¶
machine.UART ห่อหุ้มช่องสัญญาณ UART ฮาร์ดแวร์หนึ่งช่อง สร้างอ็อบเจกต์ด้วย bus 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 buffer แล้วฮาร์ดแวร์จะส่งข้อมูลออกในพื้นหลัง
การอ่านข้อมูลทำได้สามวิธีขึ้นอยู่กับสิ่งที่ต้องการ:
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 buffer โดยไม่บล็อก read() อ่านข้อมูลจำนวนไบต์ที่กำหนด และคืนค่า None หาก timeout (กำหนดได้ผ่านอาร์กิวเมนต์ timeout ใน constructor) หมดก่อน 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 ระหว่างไบต์ หากไม่มี timeout การเรียกจะไม่มีสัญญาณบอกว่าผู้ส่งเสร็จแล้ว และอาจค้างอยู่ตลอดไป
3.19.2. ข้อมูลไบนารีด้วย struct¶
การส่งจำนวนเต็มและจำนวนทศนิยมผ่านสายสัญญาณเป็นหน้าที่ของโมดูล struct ซึ่งบรรจุค่าความกว้างคงที่ลงใน bytes โดยใช้สตริงรูปแบบที่ระบุลำดับไบต์และชนิดของแต่ละฟิลด์:
import struct
uart.write(struct.pack("<lhb", count, temperature_x100, status))
"<" นำหน้าเลือก little-endian byte order; "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() สามารถตามทันการรับส่งข้อมูลส่วนใหญ่ได้เอง ตราบที่ลูปหลักวนซ้ำเร็วพอที่จะระบาย receive buffer ก่อนที่จะเต็ม ตัวเลือก constructor สองอย่างมีความสำคัญเมื่ออัตราเพิ่มขึ้นหรือลูปยุ่งอยู่
rxbuf กำหนดขนาดของ RX buffer ซอฟต์แวร์ ค่าดีฟอลต์อยู่ที่หลายร้อยไบต์ สำหรับเซนเซอร์ที่ส่งข้อมูลหลายร้อยไบต์ต่อชุด หรือเมื่อลูปหลักทำงานนานระหว่างการโพล การขยาย buffer จะช่วยป้องกันไม่ให้ไบต์ที่เข้ามาหายไปขณะที่ลูปยุ่งกับงานอื่น:
uart = UART(3, baudrate=115200, timeout=100, rxbuf=4096)
ข้อมูลที่มาถึงขณะที่ buffer เต็มจะสูญหาย กำหนดขนาด rxbuf ให้ครอบคลุมช่วงเวลานานที่สุดระหว่างการระบาย
สำหรับอัตราสูงต่อเนื่อง readinto() อ่านข้อมูลลงใน buffer ที่จัดสรรล่วงหน้าแทนที่จะคืนค่า bytes ใหม่ทุกครั้ง:
buf = bytearray(256)
while True:
n = uart.readinto(buf)
if n:
process(buf, n)
ไม่มีการจัดสรรหน่วยความจำเกิดขึ้นในเส้นทางการอ่าน ซึ่งสำคัญเมื่อ heap แตกกระจาย หรือเมื่อเวลาแฝงในการจัดสรรจะเกินงบประมาณเวลาระหว่างไบต์