3.19. UART у коді

machine.UART охоплює один апаратний канал UART. Створіть об’єкт із ідентифікатором шини та швидкістю передачі; всі інші параметри мають розумні значення за замовчуванням:

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() зчитує фіксовану кількість байтів, повертаючи None, якщо вичерпується тайм-аут (налаштовується через аргумент timeout у конструкторі). readline() зчитує до наступного символу нового рядка включно, що зручно для протоколів на основі рядків.

Простий цикл, що луною повторює все отримане:

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

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

read() без аргументу довжини продовжує зчитувати байти, доки лінія прийому не залишатиметься тихою протягом налаштованого timeout, після чого повертає все накопичене. При timeout=100 кожен виклик тут повертає один пакет байтів — все, що відправник тактував без 100 мс перерви між байтами. Без тайм-ауту виклик не мав би сигналу про те, що відправник завершив передачу, і міг би зависнути на невизначений час.

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)

На шляху читання не відбувається жодного виділення пам’яті, що важливо, коли купа є фрагментованою або коли затримка виділення пам’яті інакше перевищувала б бюджет часу між байтами.