Интерфейс UART
UART — асинхронный последовательный интерфейс: данные идут по линиям TX и RX без тактового сигнала, а приёмник подстраивается по старт-биту каждого байта. Здесь — формат кадра, допустимое расхождение скоростей, подключение и согласование уровней 3,3 и 5 В, переходники USB–UART и примеры для AVR, STM32 и Arduino.
Обновлено Редакция GAW
Содержание
- UART, USART и RS-232: в чём разница
- Кадр UART: старт-бит, данные, чётность и стоп-биты
- Скорость UART: стандартный ряд и 115 200 бит/с
- Как приёмник находит середину бита
- Допустимое расхождение скоростей
- Расчёт делителя скорости для AVR и STM32
- Внутренний RC-генератор: почему UART «не работает»
- Ошибки приёма: кадр, чётность, переполнение и break
- Управление потоком: RTS/CTS и XON/XOFF
- Подключение UART: TX к RX и общий провод
- Переходники USB–UART: CH340, CP2102N, FT232R и PL2303
- Полудуплекс по одному проводу, LIN и RS-485
- UART в AVR: регистры и приём по прерыванию
- UART в STM32 и Arduino
- Отладка UART
- Типичные ошибки
UART (Universal Asynchronous Receiver/Transmitter, универсальный асинхронный приёмопередатчик) — узел микросхемы, который превращает байт в последовательность бит на выводе TX и собирает принятые биты в байт на выводе RX. Тактового сигнала нет: обе стороны заранее настроены на одну скорость, а приёмник синхронизируется заново в начале каждого кадра — по спаду старт-бита. Поэтому для связи двух устройств хватает трёх проводов: TX, RX и общего.
UART есть почти в каждом микроконтроллере. Через него работают отладочная консоль и загрузчики прошивки, к нему подключают GPS-приёмники, модемы, модули Bluetooth и Wi-Fi, а компьютер видит его через переходник USB–UART как COM-порт. Сам UART задаёт только формат кадра и скорость. Электрические уровни определяет то, чем кадр передаётся: логические 0 и 3,3 или 5 В на плате, RS-232 или RS-485 на кабеле.
Коротко
- Линии: TX — выход передатчика, RX — вход приёмника, плюс общий провод. Соединяют накрест: TX одного устройства — на RX другого.
- Кадр: старт-бит (0), от 5 до 9 бит данных младшим битом вперёд, необязательный бит чётности, 1 или 2 стоп-бита (1). В покое линия в единице. Самый частый формат — 8N1, 10 бит на байт.
- Скорость одинакова на обеих сторонах, чаще всего 9600 или 115 200 бит/с. Приёмник делает 16 выборок на бит и решает по трём средним.
- Допуск: частоты сторон могут расходиться в сумме на 2–3,3 % (Maxim, AN2141). Для AVR Microchip рекомендует не больше ±2 % на каждую сторону при кадре 8N1.
- Уровни на выводах — логические, 0 и 3,3 или 5 В. COM-порт RS-232 с напряжением до ±15 В к ним напрямую не подключают.
- USART — тот же UART плюс синхронный режим с тактовым выводом: XCK у AVR, CK у STM32.
UART, USART и RS-232: в чём разница
USART (Universal Synchronous/Asynchronous Receiver/Transmitter) — UART, который умеет работать ещё и синхронно. В синхронном режиме по отдельному выводу идёт тактовый сигнал — XCK у AVR, CK у STM32, — и данные защёлкиваются по его фронтам, как в SPI, поэтому точность скорости там не важна. У ATmega328P режим выбирают биты UMSEL01 и UMSEL00: асинхронный, синхронный или ведущий SPI (MSPIM). В синхронном режиме ведущего скорость равна fOSC/(2 · (UBRR0 + 1)), а бит UCPOL0 выбирает, по какому фронту XCK данные выдаются и по какому считываются (ATmega328P, раздел 20.3). В режиме MSPIM модуль становится вторым ведущим SPI. У STM32F1 вывод CK работает только как выход ведущего: тактов нет на старт- и стоп-битах, полярность и фаза программируются (RM0008).
Асинхронный режим USART обычно и называют просто UART: формат кадра и регистры у них общие. Дальше речь только о нём.
UART и RS-232, RS-485. UART определяет, что передаётся: кадр из старт-бита, данных и стоп-битов на заданной скорости. RS-232 и RS-485 определяют, как это передаётся по кабелю: напряжения, нагрузки, разъёмы. Maxim в AN2141 отмечает, что доступность UART привела к использованию того же кадра вне RS-232: в линиях RS-485, через оптроны и просто с несимметричными логическими уровнями 0…3,3 В вместо ±5 или ±10 В.
| UART на выводах МК | RS-232 | RS-485 | |
|---|---|---|---|
| Что задаёт | формат кадра и скорость | уровни, нагрузки, разъёмы | уровни дифференциальной линии |
| Уровни | 0 и 3,3 или 5 В, покой — высокий уровень | ±5…±15 В, «1» и покой — минус | разность напряжений проводов A и B |
| Линии | TX, RX, общий провод | TxD, RxD, общий провод, квитирование | пара A и B, общий провод |
| Дальность | плата, короткий кабель | около 15–20 м | до 1200 м |
| Схема | точка — точка | точка — точка | шина |
Для RS-232 и RS-485 между UART и разъёмом ставят трансивер: MAX3232 или MAX485. Сам UART разницы не замечает: кадр и его временные параметры те же, меняются только напряжения на проводе.
Кадр UART: старт-бит, данные, чётность и стоп-биты
Когда передавать нечего, передатчик держит линию в единице — это состояние покоя (в терминах RS-232 — MARK). Кадр начинается старт-битом: линия на один бит переходит в ноль, и по этому спаду приёмник понимает, что пошли данные. Дальше идут биты данных, начиная с младшего, затем необязательный бит чётности и стоп-биты — снова единица. Следующий кадр может начаться сразу после стоп-бита или после паузы любой длины. USART ATmega328P понимает 30 сочетаний: 5, 6, 7, 8 или 9 бит данных, без чётности, с чётной или нечётной, один или два стоп-бита (ATmega328P, раздел 20.4).
Символ «U» удобен для проверки. Его код 0x55 — это 0101 0101, младший бит идёт первым, и вместе со старт- и стоп-битом на линии чередуются 0 и 1. Получается меандр с периодом два бита: при 9600 бит/с — 4800 Гц, при 115 200 бит/с — 57,6 кГц. Его частоту можно измерить осциллографом или частотомером и сразу узнать скорость. Тот же байт 0x55 шина LIN использует для подстройки скорости (LIN 2.2A).
Длительность бита — величина, обратная скорости: 104,2 мкс при 9600 бит/с, 8,68 мкс при 115 200 бит/с. Кадр 8N1 занимает 10 бит, из них полезных 8, поэтому при 115 200 бит/с через линию проходит не больше 11 520 байт в секунду.
Запись формата: 8N1, 7E1, 8E1
Формат кадра записывают тремя символами: число бит данных, тип чётности и число стоп-битов. N (none) — бита чётности нет, E (even) — чётная, O (odd) — нечётная; реже встречаются M (mark) — бит чётности всегда 1 и S (space) — всегда 0.
| Формат | Данные | Чётность | Стоп-биты | Бит в кадре | Где встречается |
|---|---|---|---|---|---|
| 8N1 | 8 | нет | 1 | 10 | формат по умолчанию в Arduino: Serial.begin(скорость) — это SERIAL_8N1 |
| 8E1 | 8 | чётная | 1 | 11 | Modbus RTU: по спецификации чётность по умолчанию — even |
| 8N2 | 8 | нет | 2 | 11 | Modbus RTU без чётности: спецификация требует тогда два стоп-бита |
| 7E1 | 7 | чётная | 1 | 10 | Modbus ASCII и другие текстовые протоколы на 7-битных символах |
| 9N1 | 9 | нет | 1 | 11 | многопроцессорный режим: девятый бит отличает адрес от данных |
Бит чётности
Бит чётности дополняет число единиц: при чётной (even) чётности общее число единиц в данных и бите чётности чётное, при нечётной (odd) — нечётное. Аппаратно это исключающее ИЛИ всех бит данных, а при нечётной чётности результат ещё инвертируется (ATmega328P, раздел 20.4.1). В «U» = 0x55 четыре единицы, поэтому бит чётности в формате 8E1 равен 0, в 8O1 — 1. Режимы mark и space, они же stick parity, в UART 16550 включает отдельный бит: бит чётности передаётся и проверяется как постоянная 1 или 0 (TI, TL16C550C).
Чётность ловит любое нечётное число искажённых бит в кадре, но пропускает два: если перевернулись два бита, сумма по модулю 2 не меняется. Поэтому протоколы добавляют контрольную сумму на уровне пакета: Modbus RTU — CRC, Modbus ASCII — LRC.
Стоп-биты и девятый бит
Стоп-бит гарантирует, что перед следующим старт-битом линия побудет в единице и спад будет заметен. Приёмник проверяет только первый стоп-бит: так устроены и USART AVR, и UART 16550, и USART STM32, а второй стоп-бит лишь удлиняет паузу между кадрами (ATmega328P, раздел 20.4; TL16C550C; RM0008). Поэтому передатчик с двумя стоп-битами понятен приёмнику, настроенному на один: лишний стоп-бит для него — просто пауза. У AVR бит USBS0, задающий число стоп-битов, вообще влияет только на передатчик. Полтора стоп-бита — экзотика: у 16550 они бывают только при 5-битных символах (TL16C550C), у STM32 — в режиме смарт-карт (RM0008).
В 9-битном формате старший бит часто служит признаком адреса. В многопроцессорном режиме AVR (бит MPCM0) ведомые принимают только кадры с девятым битом, равным 1, — адресные. Узнав свой адрес, ведомое сбрасывает MPCM0 и принимает следующие кадры данных, а остальные их не видят (ATmega328P, раздел 20.9). У STM32 тот же приём называется выходом из режима mute по адресной метке (RM0008).
Скорость UART: стандартный ряд и 115 200 бит/с
Скорость UART измеряют в битах в секунду. Её часто называют скоростью в бодах — для UART это одно и то же: каждый символ на линии несёт один бит.
| Скорость, бит/с | Длительность бита | Байт в секунду (8N1) | Делитель 16550 при 1,8432 МГц |
|---|---|---|---|
| 1200 | 833,3 мкс | 120 | 96 |
| 2400 | 416,7 мкс | 240 | 48 |
| 4800 | 208,3 мкс | 480 | 24 |
| 9600 | 104,2 мкс | 960 | 12 |
| 19 200 | 52,08 мкс | 1920 | 6 |
| 38 400 | 26,04 мкс | 3840 | 3 |
| 57 600 | 17,36 мкс | 5760 | 2 |
| 115 200 | 8,68 мкс | 11 520 | 1 |
| 230 400 | 4,34 мкс | 23 040 | — |
| 460 800 | 2,17 мкс | 46 080 | — |
| 921 600 | 1,09 мкс | 92 160 | — |
| 1 000 000 | 1,00 мкс | 100 000 | — |
Почему ряд именно такой, видно по UART 16550 — микросхеме последовательного порта ПК. Её генератор скорости делит частоту кварца на программируемый делитель от 1 до 65 535 и выдаёт сигнал в 16 раз выше скорости: делитель = fXIN/(16 · скорость) (TI, TL16C550C). Кварц 1,8432 МГц — это ровно 16 × 115 200 Гц, поэтому все скорости из таблицы до 115 200 бит/с получаются при целых делителях, а делитель 1 даёт 115 200 бит/с — наибольшую скорость, которую 16550 получает от такого кварца.
Выше 115 200 бит/с идут кратные значения 230 400, 460 800 и 921 600 бит/с и «круглые» 250 000, 500 000 и 1 000 000 бит/с — они удобны для кварцев 8 и 16 МГц: ATmega328P на 16 МГц получает их без ошибки (таблица 20-7). Предел самого UART — у AVR в режиме U2X0 = 1 частота fOSC/8, то есть 2 Мбит/с на 16 МГц, у STM32F1 — 4,5 Мбит/с (RM0008), у мостов USB–UART — от 2 до 12 Мбит/с (таблица мостов ниже).
Почему кварцы 11,0592 и 14,7456 МГц
Если тактовая частота кратна 1,8432 МГц, делитель для стандартных скоростей получается целым. Отсюда «странные» кварцы в схемах с UART: 3,6864; 7,3728; 11,0592; 14,7456 и 18,432 МГц — это 2, 4, 6, 8 и 10 раз по 1,8432 МГц. На ATmega328P с любым из них все скорости до 230 400 бит/с получаются с нулевой ошибкой (ATmega328P, таблицы 20-5–20-7). У классического 8051 с 12 тактами на машинный цикл кварц 11,0592 МГц и TH1 = 0xFD дают ровно 9600 бит/с. Этот и другие варианты считает калькулятор скорости UART.
Как приёмник находит середину бита
Передатчику просто: он выдаёт биты по своему таймеру. Приёмник не знает, когда начнётся кадр, и должен сам найти середину каждого бита. Для этого его тактируют частотой, кратной скорости, и линия RX опрашивается много раз за бит — это передискретизация (oversampling). Типовой коэффициент — 16: так работают UART 16550 (TI, TL16C550C), USART AVR в обычном режиме и USART STM32F1. В режиме удвоенной скорости AVR (U2X0 = 1) выборок на бит 8.
Так это устроено у ATmega328P (раздел 20.8):
- Поиск старт-бита. Приёмник ждёт перехода линии из 1 в 0. Первую выборку с нулём он считает выборкой 1.
- Проверка старт-бита. Выборки 8, 9 и 10 (4, 5 и 6 при U2X0 = 1) должны дать ноль хотя бы в двух случаях из трёх. Если большинство — единицы, спад был помехой, и приёмник снова ждёт спада. Так короткий выброс на линии не превращается в ложный байт.
- Приём бит. Дальше каждые 16 выборок приёмник берёт три средние и решает по большинству: две или три единицы — бит равен 1. Такое голосование работает как фильтр нижних частот.
- Стоп-бит. Его читают так же, и ноль на месте стоп-бита означает ошибку кадра. Следующий старт-бит приёмник распознаёт уже сразу после выборок голосования стоп-бита, не дожидаясь его конца.
У STM32F1 проверка старт-бита строже: спад должен подтвердиться двумя тройками выборок — 3, 5, 7 и 8, 9, 10. Если в какой-то тройке нулей меньше двух, приёмник возвращается в ожидание; если в тройке ноль только в двух выборках из трёх, старт-бит принимается, но взводится флаг шума NE (RM0008, раздел 27.3.3). Данные читаются по выборкам 8, 9 и 10, и любое расхождение между ними отмечается тем же флагом NE.
Из этой схемы следуют три важных свойства:
- Ошибка не накапливается. Приёмник подстраивается по спаду каждого старт-бита, поэтому расхождение частот действует только в пределах одного кадра (Maxim, AN2141).
- Момент старта известен с точностью до выборки. Спад приходит в любой момент между выборками, и синхронизация неточна на 1/16 бита, а при U2X0 = 1 — на 1/8. Отчасти поэтому режиму удвоенной скорости нужна более точная частота (ATmega328P, раздел 20.3.2).
- Помехоустойчивость. Короткую помеху в середине бита перевешивают две другие выборки, а выброс на линии в покое не запускает приём.
Допустимое расхождение скоростей
Абсолютная точность частоты UART не важна — важна разница частот передатчика и приёмника. Синхронизация происходит по спаду старт-бита, дальше приёмник отсчитывает биты по своим часам, и ошибка растёт к концу кадра. Хуже всего приходится стоп-биту — последнему биту, который проверяется.
Оценка Maxim: от 2 до 3,3 %
Расчёт из AN2141 сделан для кадра 8N1 и приёмника с 16 выборками на бит. Середина стоп-бита отстоит от спада старт-бита на 9,5 бита, то есть на 16 × 9,5 = 152 такта приёмника. Насколько выборка может уйти от середины бита, зависит от линии:
- на короткой линии с буферированными логическими уровнями или на паре RS-485 внутри прибора уровень надёжно читается в средних 75 % бита: допустимо отклонение ±37,5 % бита, или ±6 тактов;
- на длинном ёмком кабеле RS-232 фронты затянуты, надёжны только средние 50 % бита: ±25 %, или ±4 такта.
Ещё один такт съедает неопределённость момента старта. Остаётся ±5 или ±3 такта на 152, то есть ±3,3 % на нормальной линии и ±2 % на тяжёлой. Это общий бюджет на обе стороны, и делить его можно в любой пропорции: если у одной стороны кварц, почти весь запас достаётся другой (AN2141).
Там же даны ориентиры по источникам частоты: кварцевый генератор с точностью 100 ppm (0,01 %) расходует ничтожную часть бюджета, а дешёвый керамический резонатор с точностью ±0,5 % и ещё ±0,5 % ухода от температуры и старения укладывается в жёсткие 2 % даже на обеих сторонах линии.
Допуски приёмника AVR и STM32
Для USART AVR Microchip приводит точные границы (ATmega328P, раздел 20.8.3). D — сумма числа бит данных и бита чётности; Rslow и Rfast — самая низкая и самая высокая скорость входящего потока, которую приёмник ещё примет, относительно его собственной скорости:
где — число выборок на бит (16 или 8), и — номера первой и средней голосующих выборок: 8 и 9 при U2X0 = 0, 4 и 5 при U2X0 = 1.
| D | Форматы | Диапазон при U2X0 = 0 | Рекомендация | Диапазон при U2X0 = 1 | Рекомендация |
|---|---|---|---|---|---|
| 5 | 5N1 | 93,20…106,67 % | ±3,0 % | 94,12…105,66 % | ±2,5 % |
| 6 | 6N1, 5E1 | 94,12…105,79 % | ±2,5 % | 94,92…104,92 % | ±2,0 % |
| 7 | 7N1, 6E1 | 94,81…105,11 % | ±2,0 % | 95,52…104,35 % | ±1,5 % |
| 8 | 8N1, 7E1 | 95,36…104,58 % | ±2,0 % | 96,00…103,90 % | ±1,5 % |
| 9 | 8E1, 9N1 | 95,81…104,14 % | ±1,5 % | 96,39…103,53 % | ±1,5 % |
| 10 | 9E1 | 96,17…103,78 % | ±1,5 % | 96,70…103,23 % | ±1,0 % |
Рекомендованная ошибка — примерно половина полного допуска с округлением вниз: Microchip считает, что передатчик и приёмник делят его поровну. Для 8N1 при U2X0 = 0 полный допуск — от −4,64 до +4,58 %. В таблице 20-2 даташита вместо −4,64 напечатано −4,54 %, но из Rslow = 95,36 % следует именно −4,64 %.
RM0008 задаёт для USART STM32F1 один общий допуск. В него должна уложиться сумма четырёх составляющих: ошибки передатчика вместе с его генератором, ошибки квантования делителя приёмника, ухода генератора приёмника и искажений линии — например, разной задержки фронта и спада в трансиверах (раздел 27.3.5):
| Кадр | Дробная часть делителя равна 0 | Дробная часть не равна 0 |
|---|---|---|
| 10 бит (M = 0): 8N1, 7E1 | 3,75 % | 3,33 % |
| 11 бит (M = 1): 8E1, 9N1 | 3,41 % | 3,03 % |
Числа приведены для случая, когда флаг шума NE считается ошибкой; если его игнорировать, допуск немного больше — от 3,53 до 4,375 %.
Что из этого следует на практике:
- держите ошибку каждой стороны в пределах ±2 %, а для кадров с чётностью и режима U2X0 = 1 — ±1,5 %, как рекомендует Microchip;
- суммарное расхождение до 2 % выдерживает даже длинный кабель, до 3 % — короткая линия (AN2141); предел самих приёмников — от 3,03 до 3,75 % у STM32F1 и около 4,6 % у AVR для 8N1;
- длинный кадр, режим ×8 и длинный кабель уменьшают запас.
Производители мостов USB–UART говорят о том же. Silicon Labs в даташите CP2102N приводит общее правило: ошибка и передатчика, и приёмника — не больше ±2 %, а WCH указывает, что приёмник CH340 допускает расхождение около 2 %.
Расчёт делителя скорости для AVR и STM32
Скорость получается делением тактовой частоты на целое число (у STM32 — на число с дробной частью), поэтому точно получить нужную скорость удаётся не на любой частоте. Ошибку считают как (BAUDфакт/BAUD − 1) · 100 %.
AVR. Генератор скорости делит fOSC на UBRR0 + 1, а затем на 16 или, при U2X0 = 1, на 8 (ATmega328P, таблица 20-1):
UBRR0 — 12-битное целое от 0 до 4095, результат округляют до ближайшего целого. Пример — 115 200 бит/с при кварце 16 МГц, как на Arduino Uno:
| Режим | UBRR0 | Фактическая скорость | Ошибка |
|---|---|---|---|
| U2X0 = 0 | 16 000 000 / (16 · 115 200) − 1 = 7,68 → 8 | 111 111 бит/с | −3,5 % |
| U2X0 = 1 | 16 000 000 / (8 · 115 200) − 1 = 16,36 → 16 | 117 647 бит/с | +2,1 % |
| U2X0 = 0, кварц 14,7456 МГц | 14 745 600 / (16 · 115 200) − 1 = 7 | 115 200 бит/с | 0 % |
Без удвоения ошибка −3,5 % больше рекомендованных ±2 %, и с точным партнёром — компьютером через мост USB–UART — связь будет сбоить. С U2X0 = 1 ошибка +2,1 % укладывается в полный допуск этого режима (96,00…103,90 %), но превышает рекомендованные для него ±1,5 %. Такая связь работает, если вторая сторона точна, — например, мост CH340G с кварцем ошибается меньше чем на 0,3 % (WCH). Два AVR на 16 МГц с одинаковой настройкой договорятся при любой из этих ошибок: их скорости ошибаются одинаково.
STM32. USART делит частоту своей шины на USARTDIV — число с 12-битной целой и 4-битной дробной частью, которое записывают в регистр USART_BRR (RM0008, раздел 27.3.4):
Здесь fCK — частота PCLK2 для USART1 и PCLK1 для USART2–USART5, а не системная частота. Для 115 200 бит/с на шине 72 МГц USARTDIV = 72 000 000 / (16 · 115 200) = 39,0625: целая часть 39, дробная 0,0625 · 16 = 1, USART_BRR = 0x271, ошибки нет. На шине 36 МГц USARTDIV = 19,53125, в регистр попадает 19,5 (0x138), и скорость выходит 115 385 бит/с — ошибка +0,16 %. Функция HAL_UART_Init() сама берёт частоту нужной шины и считает регистр (STM32 HAL).
Для других частот, для режимов U2X и OVER8 и для 8051 значение регистра и ошибку на всех стандартных скоростях сразу считает калькулятор скорости UART. В программах на C для AVR удобен заголовок <util/setbaud.h> из avr-libc: по F_CPU и BAUD он вычисляет UBRR_VALUE, сам решает, нужен ли режим U2X (USE_2X), и предупреждает при компиляции, если ошибка больше BAUD_TOL — по умолчанию 2 %.
Внутренний RC-генератор: почему UART «не работает»
Если микроконтроллер тактируется от встроенного RC-генератора, его неточность может съесть весь допуск UART. Типичная картина: прошивка работает на одном экземпляре платы и выдаёт мусор на другом или теряет связь при нагреве.
| Источник частоты | Точность | Данные |
|---|---|---|
| Кварцевый генератор | ±100 ppm = ±0,01 % | Maxim, AN2141 |
| Керамический резонатор | ±0,5 % плюс ±0,5 % от температуры и старения | Maxim, AN2141 |
| RC-генератор ATmega328P, заводская калибровка | ±10 % при 3 В и 25 °C | ATmega328P, таблица 29-9 |
| RC-генератор ATmega328P после калибровки OSCCAL | ±1 % | там же |
| HSI STM32F103, заводская калибровка | от −1,1 до +1,8 % при 25 °C, от −2 до +2,5 % при −40…+105 °C | STM32F103x8, таблица 24 |
| Встроенный генератор CH340C, N, K, E, X, B | ±1,2 % при −5…+55 °C, ±1,8 % при −25…+75 °C | WCH, CH340 |
| Генератор 48 МГц моста CP2102N | ±0,25 % | Silicon Labs, CP2102N |
Что из этого следует:
- ATmega328P без кварца. Заводская калибровка гарантирует лишь ±10 %, и в допуск UART это не укладывается. После подстройки регистра OSCCAL точность ±1 % (ATmega328P, раздел 9.6), но она держится, пока не изменились температура и напряжение питания.
- STM32 на HSI. При комнатной температуре генератор может уйти на +1,8 %, и если на другой стороне CH340C со своими ±1,2 %, в сумме выходит 3 %: больше 2 %, которые допускает приёмник CH340, и у самого края допуска USART STM32F1. HSI можно подстроить битами регистра RCC_CR до 1 % (STM32F103x8, таблица 24), но для надёжной связи лучше кварц.
- Фьюз CKDIV8. Новый ATmega328P поставляется с RC-генератором 8 МГц и запрограммированным фьюзом CKDIV8, то есть работает на 1 МГц (ATmega328P, раздел 9.2.1). Если программа собрана с F_CPU = 8 МГц, все скорости UART получаются в 8 раз ниже: вместо 9600 бит/с — 1200. Снимите фьюз CKDIV8 или выставьте делитель регистром CLKPR в начале программы — даташит разрешает это при любом состоянии фьюза.
- Автоподстройка. Если кварц поставить нельзя, скорость можно измерить по известному символу. Так устроен LIN: ведомые узлы работают без кварца и подстраиваются по байту синхронизации 0x55 в начале каждого кадра (LIN 2.2A).
Ошибки приёма: кадр, чётность, переполнение и break
UART сообщает об ошибках флагами состояния. Названия у разных производителей свои, смысл один:
| Ошибка | Что произошло | AVR, UCSR0A | STM32F1, USART_SR | 16550, LSR |
|---|---|---|---|---|
| Ошибка кадра (framing error) | на месте первого стоп-бита прочитан 0 | FE0 | FE | FE |
| Ошибка чётности (parity error) | бит чётности не совпал с данными | UPE0 | PE | PE |
| Переполнение (overrun) | пришёл новый символ, а прежние ещё не прочитаны | DOR0 | ORE | OE |
| Шум (noise) | выборки внутри бита не совпали | — | NE | — |
| Break | линия в нуле дольше целого кадра | FE0 и байт 0x00 | FE, в режиме LIN — LBD | BI |
Ошибка кадра — главный признак неверной скорости или формата: приёмник ждёт стоп-бит, а линия в нуле. Её же дают помехи и плавающий вход RX без подтяжки. У AVR флаги ошибок хранятся в буфере приёма вместе с символом, поэтому UCSR0A читают до UDR0: чтение UDR0 сдвигает буфер, и флаги меняются (ATmega328P, раздел 20.7.4). UART 16550 после ошибки кадра пытается пересинхронизироваться, считая, что ошибку вызвал следующий старт-бит (TL16C550C).
Ошибка чётности бывает, только если чётность включена. Arduino молча выбрасывает такие байты: обработчик прерывания HardwareSerial читает UDR0, но не кладёт символ в буфер (ArduinoCore-avr).
Переполнение означает, что программа не успевает забирать данные. У ATmega328P буфер приёма — два символа, и ещё один может ждать в сдвиговом регистре: флаг DOR0 взводится, когда буфер полон, символ в сдвиговом регистре ждёт, а на линии уже начался следующий старт-бит (раздел 20.7.4). У STM32F1 регистр данных один; при переполнении символ в RDR сохраняется, а всё, что принято в сдвиговый регистр после него, теряется (RM0008). При 115 200 бит/с символ 8N1 приходит каждые 86,8 мкс — столько времени есть у программы, чтобы забрать байт, если нет FIFO или DMA. Лечится приёмом по прерыванию в кольцевой буфер, DMA и управлением потоком.
Break — линия в нуле дольше полного кадра: старт-бита, данных, чётности и стоп-битов (TL16C550C). Приёмник видит его как символ 0x00 с ошибкой кадра. UART 16550 кладёт в FIFO один нулевой символ, ставит флаг BI и принимает следующий символ только после того, как линия вернётся в единицу; STM32 обрабатывает break как ошибку кадра (RM0008). Передать break умеют и 16550 — битом 6 регистра LCR, — и STM32: бит SBK выдаёт 10 или 11 нулевых бит, а в режиме LIN — 13 (RM0008). Мосту CP2102N для этого нужен внешний резистор 10 кОм от TXD на землю: на время break мост отпускает вывод TXD, и линию прижимает резистор (Silicon Labs). Break открывает, например, каждый кадр LIN.
Пауза на линии (idle) — не ошибка, а полезное событие. STM32 ставит флаг IDLE, когда после принятого символа линия пробыла в единице целый кадр. По нему удобно определять конец пакета переменной длины — на этом построена функция HAL_UARTEx_ReceiveToIdle_DMA() (STM32 HAL).
Управление потоком: RTS/CTS и XON/XOFF
Управление потоком останавливает передатчик, когда приёмнику некуда класть данные. Без него единственная защита от переполнения — заведомо быстрая обработка на приёмной стороне.
Аппаратное, RTS/CTS. Каждая сторона выдаёт сигнал RTS (Request To Send) — «готова принимать» — и слушает вход CTS (Clear To Send) — «можно передавать». Соединяют их накрест, как данные: RTS одного устройства — на CTS другого (RM0008, рисунок 299; Silicon Labs, CP2102N). На логических выводах оба сигнала активны низким уровнем. У STM32 RTS прижат к нулю, пока приёмник готов, а передача идёт, пока на CTS ноль (RM0008). У мостов CP2102N, CH340 и FT232R RTS и CTS тоже активны нулём, у CH340 и FT232R это видно и по названиям выводов — RTS# и CTS#.
Сигнал «стоп» действует не мгновенно: символ, который уже передаётся, будет дослан.
- STM32 при снятии CTS завершает текущий кадр (RM0008).
- UART 16550 в режиме auto-CTS остановится перед следующим символом, если CTS снят до середины последнего стоп-бита текущего (TL16C550C).
- CP2102N после снятия CTS передаёт ещё не больше двух байт (Silicon Labs).
Поэтому приёмник снимает RTS заранее, пока в буфере есть место. CP2102N, например, запрещает передачу, когда в его буфере приёма набирается 448 байт из 512, и разрешает снова, когда занято не больше 384 байт (Silicon Labs).
На высоких скоростях без аппаратного управления потоком не обойтись. Silicon Labs требует его для CP2102N на скоростях выше 1 Мбод, а WCH для потоков от 1 Мбит/с в одну сторону или от 500 кбит/с в обе советует вместо CH340 мост CH343 с аппаратным управлением потоком.
Программное, XON/XOFF. Приёмник посылает по своей линии TX символ XOFF — «стоп», а освободившись, XON — «продолжай». Это управляющие символы DC3 (0x13) и DC1 (0x11) (ECMA-48). Лишние провода не нужны, но для двоичных данных способ не годится: байты 0x11 и 0x13 встречаются в самих данных. Есть и ловушка взаимной блокировки: если обе стороны одновременно отправят XOFF, каждая будет ждать XON от другой. У CP2102N на этот случай есть настройка XOFF_CONTINUE: мост продолжает передавать свои XON и XOFF, даже получив XOFF (Silicon Labs).
Сигналы квитирования на разъёме DE-9 и нуль-модемный кабель разобраны в статье про RS-232.
Подключение UART: TX к RX и общий провод
Три правила — те же, что в справочнике Arduino:
- Накрест. Выход TX одного устройства — на вход RX другого, и наоборот. TX — выход данных, RX — вход.
- Общий провод. Уровни TX и RX отсчитываются от земли, поэтому GND устройств соединяют обязательно. Если устройства питаются от одного источника, земля уже общая через провода питания.
- Одинаковые уровни. На выводах UART — логика, а не RS-232: подключать их к COM-порту с напряжениями до ±12 В нельзя, нужен преобразователь вроде MAX3232 (Arduino, Language Reference).
Если на модуле непонятно, какой вывод выход, помогает простая проверка. Передатчик в покое держит на TX высокий уровень и не боится нагрузки, а вход RX обычно лишь слабо подтянут внутри микросхемы: у FT232R — резисторами 200 кОм, у CH340 и CP2102N — токами в десятки и сотни микроампер. Подключите к выводу резистор 10 кОм на землю: выход TX останется в единице, а напряжение на входе RX заметно упадёт.
Согласование уровней 5 и 3,3 В
5 В на вход 3,3 В. Выход 5-вольтового устройства выдаёт почти 5 В, а вход 3,3-вольтовой микросхемы обычно рассчитан на напряжение не выше питания плюс несколько десятых вольта. У STM32F103 выводы двух типов: на обычных допустимо не больше 4,0 В, на помеченных FT (five-volt tolerant) — до VDD + 4,0 В (STM32F103x8, таблица 6). USART1 на PA9 и PA10 и USART3 на PB10 и PB11 выведены на выводы FT, а USART2 на PA2 и PA3 — на обычные (таблица 5). Если вход не допускает 5 В, ставят делитель напряжения:
Делитель вместе с ёмкостью входа затягивает фронты. Его эквивалентное сопротивление 10 ‖ 20 кОм = 6,67 кОм; с ёмкостью вывода STM32F103 5 пФ (таблица 36) постоянная времени около 33 нс, с ёмкостью монтажа — порядка 0,1 мкс. Это малая доля бита даже при 115 200 бит/с, где бит длится 8,68 мкс. Для скоростей от 1 Мбит/с резисторы берут в 10 раз меньше — 1 и 2 кОм — или ставят буфер с 5-вольтовыми входами, как в разделе о согласовании уровней статьи про SPI. Номиналы для другого напряжения подберёт калькулятор делителя. Линия TX в покое находится в единице, поэтому через делитель 10 и 20 кОм всё время течёт 0,17 мА.
3,3 В на вход 5 В. Здесь всё решает порог высокого уровня приёмника:
| Приёмник с питанием 5 В | Высокий уровень гарантирован от | Выход 3,3 В |
|---|---|---|
| ATmega328P, Arduino Uno | 0,6 · VCC = 3,0 В | годится, запас 0,3 В |
| CH340 | 2,3 В | годится |
| FT232R при VCCIO = 5 В | порог переключения 1,0–1,5 В | годится |
Выход STM32F103 гарантирует в единице не меньше VDD − 0,4 В при токе 8 мА, то есть 2,9 В при питании 3,3 В (таблица 37). Вход AVR почти не потребляет тока, и на практике уровень близок к 3,3 В — работать будет. Но при питании AVR выше 5 В или просевшем питании 3,3-вольтовой стороны запас исчезает; для надёжности ставят буфер с ТТЛ-совместимыми входами.
Модуль USB–UART. У CH340 уровни выходов равны его напряжению питания: при 5 В выход TXD выдаёт 5 В. Чтобы получить 3,3 В, CH340 питают от 3,3 В, соединив выводы V3 и VCC, и тогда напряжение на подключённых к нему цепях не должно превышать 3,3 В: 5 В на входах допускают только CH340X и CH340C, CH340N из партий с номером, начинающимся на 4 (WCH). У FT232R уровни задаёт отдельный вывод VCCIO от 1,8 до 5,25 В, у CP2102N в корпусе QFN24 — вывод VIO от 1,71 В до VDD, у PL2303GC — VDD_IO от 1,8 до 5 В. Если на модуле есть переключатель 3,3/5 В, проверьте результат вольтметром: в покое на TX модуля должно быть не больше напряжения питания вашей платы.
Длина линии
UART с логическими уровнями рассчитан на связь внутри прибора. В качестве нормальной линии Maxim в AN2141 приводит шину длиной около метра с буферированными КМОП-уровнями. Сигнал отсчитывается от земли, а потенциалы земли у разнесённых устройств различаются: падение напряжения от токов питания на общем проводе прямо складывается с сигналом, туда же добавляются наводки. К тому же выводы микроконтроллера не рассчитаны на перенапряжения и разряды, которые приходят по кабелю.
Если линия длиннее или выходит из корпуса, тот же кадр UART передают через трансивер: RS-232 — на расстояние около 15–20 м, RS-485 — на сотни метров по витой паре. Для гальванической развязки ставят оптроны или цифровые изоляторы — AN2141 упоминает оптоизолированные линии среди типичных применений UART.
Если вход RX может остаться неподключённым, например на съёмном кабеле, подтяните его к питанию. Плавающий вход ловит наводки, и приёмник видит ложные старт-биты. WCH для схем, где CH340 и микроконтроллер питаются раздельно, рекомендует подтягивать вход RX микроконтроллера к его питанию резистором 2–22 кОм.
Переходники USB–UART: CH340, CP2102N, FT232R и PL2303
Компьютер видит мост USB–UART как виртуальный COM-порт: программа выбирает скорость и формат кадра, драйвер передаёт их мосту, а мост формирует кадры на выводах TXD и RXD. Уровни на этих выводах логические, как у микроконтроллера. Чтобы получить настоящий RS-232 или RS-485, после моста ставят преобразователь (WCH, CH340).
| Мост | Скорость | Уровни выводов | Сигналы модема | Буферы | VID:PID |
|---|---|---|---|---|---|
| CH340 (WCH) | 50 бит/с – 2 Мбит/с | равны питанию, 5 или 3,3 В | RTS, DTR, CTS, DSR, RI, DCD у CH340G, C, B, T; у CH340N — только RTS | встроенные | 1A86:7523 |
| CP2102N (Silicon Labs) | 300 бод – 3 Мбод | VDD 3,0–3,6 В или VIO от 1,71 В; на входах до 5,8 В по абсолютному максимуму | полный набор | по 512 байт на приём и передачу | 10C4:EA60 |
| FT232R (FTDI) | 300 бод – 3 Мбод | VCCIO 1,8–5,25 В, порог входов 1,0–1,5 В | полный набор, любую линию можно инвертировать | 128 байт к UART, 256 байт от UART | 0403:6001 |
| PL2303GC (Prolific) | 5 бит/с – 12 Мбит/с | VDD_IO 1,8–5 В, входы допускают 5 В | полный набор | 768 и 256 байт | 067B:23A3 |
Что ещё важно при выборе:
- CH340. CH340G, T и R нужен внешний кварц 12 МГц, и тогда ошибка скорости передачи меньше 0,3 %. CH340C, N, K, E, X и B обходятся без кварца, но их встроенный генератор даёт ошибку до 1,8 % (WCH). Драйвер совместим с CH341; в Linux мост обслуживает модуль
ch341. - CP2102N. Встроенный генератор 48 МГц с точностью ±0,25 %, бесплатные драйверы виртуального COM-порта для Windows, macOS и Linux (Silicon Labs). Старый CP2102 работает до 1 Мбит/с, и для новых разработок производитель рекомендует CP2102N.
- FT232R. Уровни выводов задаёт VCCIO, а настройкой во встроенной EEPROM можно инвертировать любую линию UART. FTDI выпускает драйверы VCP и D2XX (FTDI).
- PL2303. Драйвер Prolific для Windows 8, 8.1 и 10 не поддерживает снятые с производства PL-2303HXA и PL-2303X, а для Windows 11 — ещё и PL-2303TA, TB, HXD, RA, SA, EA; производитель рекомендует PL2303GC. Prolific отдельно предупреждает о поддельных PL-2303HX ревизии A: в диспетчере устройств они дают ошибку с кодом 10.
Какой мост стоит в переходнике, видно по идентификатору VID:PID: в Windows — в свойствах устройства («ИД оборудования»), в Linux — в выводе lsusb. Значения по умолчанию указаны в таблице; драйверы Linux для этих мостов — ch341, cp210x, ftdi_sio и pl2303 (ядро Linux).
Автосброс Arduino через DTR
У Arduino Uno мост USB–UART — отдельный микроконтроллер ATmega16U2. Его линия DTR подключена к выводу RESET ATmega328P через конденсатор 100 нФ (C5 на схеме Uno R3), а RESET подтянут к +5 В резистором 10 кОм (RN1D). Когда сигнал DTR включается — на выводе это переход в ноль, — напряжение на RESET вслед за ним падает почти до нуля: напряжение на конденсаторе мгновенно измениться не может. Потом конденсатор заряжается через резистор с постоянной времени τ = 10 кОм · 100 нФ = 1 мс.
Ниже 0,2 · VCC — уровня, при котором ATmega328P гарантированно видит сброс, — напряжение остаётся τ · ln 1,25 ≈ 0,22 мс, а для сброса достаточно импульса длиной 2,5 мкс (ATmega328P, таблица 29-11). Диод D2 не даёт RESET подняться заметно выше питания, когда DTR возвращается в единицу.
Arduino IDE пользуется этим перед загрузкой скетча, а в macOS и Linux плата сбрасывается при каждом открытии порта программой. Следующие полсекунды работает загрузчик: он перехватывает первые байты, поэтому программе на компьютере стоит подождать около секунды после открытия порта, прежде чем что-то отправлять (Arduino). Если автосброс мешает, на Uno можно разрезать перемычку RESET-EN.
Загрузчик слушает порт на своей скорости, и она зависит от платы: у Uno и Nano с новым загрузчиком — 115 200 бит/с, у Nano со старым загрузчиком и Pro Mini на 16 МГц — 57 600 бит/с (ArduinoCore-avr, boards.txt). Если в IDE выбран не тот вариант платы, загрузка не начнётся.
Самодельной плате с ATmega328P и внешним переходником нужна та же цепь: конденсатор 100 нФ последовательно между выводом DTR переходника и RESET и подтяжка RESET к питанию. WCH в даташите CH340 приводит похожую схему для STM32F103: сигналы DTR и RTS моста управляют выводами BOOT0 и NRST, и встроенный загрузчик запускается без нажатия кнопок.
Полудуплекс по одному проводу, LIN и RS-485
UART полнодуплексный: TX и RX работают одновременно. Но его кадр годится и для линий, где все говорят по одному проводу по очереди.
Однопроводный режим. У STM32 его включает бит HDSEL: TX и RX соединяются внутри, а вывод TX отпускается, когда передавать нечего, поэтому его настраивают как открытый сток или вход. Конфликты на линии разрешает программа: аппаратура передачу не блокирует (RM0008, раздел 27.3.10). У AVR серии DA для этого есть режим One-Wire: бит LBME подключает приёмник к выводу TXD, бит ODME делает выход открытым стоком, а единицу на линии держит подтягивающий резистор. Передатчик при этом принимает собственные байты, и, сравнив их с отправленными, легко заметить столкновение (AVR128DA, раздел 25.3.3.2.6). На полудуплексном UART построен и однопроводный интерфейс программирования UPDI этих микроконтроллеров. У классических AVR такого режима нет, и линию собирают снаружи по тому же принципу: выход TX через диод, катодом к выводу, и общая подтяжка — получается монтажное И, как в I²C.
LIN — однопроводная автомобильная шина поверх UART: один ведущий и несколько ведомых, скорость от 1 до 20 кбит/с, рабочее напряжение бортовой сети 8–18 В. Каждый кадр начинается полем break — не меньше 13 бит нуля — и байтом синхронизации 0x55, по которому ведомые без кварца подстраивают скорость (LIN 2.2A). USART STM32 в режиме LIN сам выдаёт такой break и распознаёт его на приёме (RM0008).
RS-485 — электрический уровень для шины на сотни метров. UART при этом не меняется: к нему добавляются трансивер и сигнал переключения направления. У AVR передатчик трансивера удобно выключать по флагу TXC0: он взводится, когда последний стоп-бит полностью ушёл в линию и новых данных нет (ATmega328P, раздел 20.6.3). Подробно — в статье про RS-485.
UART в AVR: регистры и приём по прерыванию
USART0 ATmega328P выведен на PD0 (RXD) и PD1 (TXD) — выводы 0 и 1 Arduino Uno. Модулем управляют пять регистров (ATmega328P, раздел 20.11):
| Регистр | Биты | Назначение |
|---|---|---|
| UDR0 | 7–0 | запись — байт в буфер передатчика, чтение — принятый байт из двухуровневого буфера |
| UCSR0A | RXC0, TXC0, UDRE0, FE0, DOR0, UPE0, U2X0, MPCM0 | флаги приёма и передачи, ошибки, удвоение скорости, многопроцессорный режим |
| UCSR0B | RXCIE0, TXCIE0, UDRIE0, RXEN0, TXEN0, UCSZ02, RXB80, TXB80 | прерывания, включение приёмника и передатчика, старший бит размера символа, девятые биты |
| UCSR0C | UMSEL01, UMSEL00, UPM01, UPM00, USBS0, UCSZ01, UCSZ00, UCPOL0 | режим, чётность, число стоп-битов, размер символа, полярность такта |
| UBRR0H, UBRR0L | 11–0 | делитель скорости, 12 бит |
Формат задают биты UCSZ02…UCSZ00 (011 — 8 бит данных, 111 — 9), UPM01 и UPM00 (00 — без чётности, 10 — чётная, 11 — нечётная) и USBS0 (0 — один стоп-бит, 1 — два). После сброса UCSR0C = 0x06, то есть 8N1.
Источников прерывания три: RXC0 — принят байт, UDRE0 — буфер передатчика свободен, TXC0 — передача полностью завершена. Прерывание по RXC0 повторяется, пока байт не прочитан из UDR0, а по UDRE0 — пока в UDR0 не записан новый байт или прерывание не запрещено (разделы 20.6.3 и 20.7.3).
Пример для ATmega328P на 16 МГц: приём по прерыванию в кольцевой буфер, передача опросом флага, эхо принятых байтов. Скорость 57 600 бит/с выбрана намеренно: <util/setbaud.h> включит U2X0 и получит UBRR0 = 34 с ошибкой −0,8 %, а на 115 200 бит/с ошибка была бы +2,1 %, и заголовок выдал бы при компиляции предупреждение о превышении допуска.
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#define BAUD 57600
#include <util/setbaud.h> /* UBRR_VALUE and USE_2X for F_CPU and BAUD, checked at compile time */
#include <avr/interrupt.h>
#include <avr/io.h>
#include <stdint.h>
#define RX_SIZE 64u /* power of two, so the index wraps with a mask */
static volatile uint8_t rx_buf[RX_SIZE];
static volatile uint8_t rx_head; /* written only by the ISR */
static volatile uint8_t rx_tail; /* written only by the main loop */
static volatile uint8_t rx_lost; /* bytes lost to overrun or a full buffer */
static volatile uint8_t rx_bad; /* frames dropped for framing or parity errors */
void uart_init(void)
{
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#if USE_2X
UCSR0A = _BV(U2X0); /* 8 samples per bit */
#else
UCSR0A = 0;
#endif
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8N1 */
UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0); /* RX interrupt, receiver, transmitter */
}
ISR(USART_RX_vect)
{
uint8_t status = UCSR0A; /* the flags belong to the byte in UDR0: read them first */
uint8_t data = UDR0;
if (status & _BV(DOR0)) rx_lost++; /* earlier bytes were lost in hardware */
if (status & (_BV(FE0) | _BV(UPE0))) { /* wrong baud rate, noise or a break */
rx_bad++;
return;
}
uint8_t next = (uint8_t)((rx_head + 1u) & (RX_SIZE - 1u));
if (next == rx_tail) { /* ring buffer full: drop the byte */
rx_lost++;
return;
}
rx_buf[rx_head] = data;
rx_head = next;
}
/* Returns the next received byte or -1 if the buffer is empty. */
int16_t uart_getc(void)
{
if (rx_head == rx_tail) return -1;
uint8_t c = rx_buf[rx_tail];
rx_tail = (uint8_t)((rx_tail + 1u) & (RX_SIZE - 1u));
return c;
}
void uart_putc(uint8_t c)
{
while (!(UCSR0A & _BV(UDRE0))) {} /* wait for an empty transmit buffer */
UDR0 = c;
}
int main(void)
{
uart_init();
sei();
for (;;) {
int16_t c = uart_getc();
if (c >= 0) uart_putc((uint8_t)c); /* echo */
}
}
Кольцевой буфер не требует запрета прерываний: индекс rx_head меняет только обработчик прерывания, rx_tail — только основная программа, и оба однобайтовые, поэтому их чтение и запись у AVR неделимы. Одна ячейка буфера всегда остаётся пустой, чтобы полный буфер отличался от пустого, — в нём помещается 63 байта. Чтобы передача не останавливала программу, её делают так же: второй кольцевой буфер и прерывание по UDRE0, которое разрешают при записи в буфер и запрещают, когда он опустел. Так устроен Serial в Arduino. Подробнее о семействе — в разделе AVR.
UART в STM32 и Arduino
STM32: HAL, прерывания и DMA
В HAL для STM32 параметры UART собраны в структуре UART_InitTypeDef: скорость, длина слова, стоп-биты, чётность, режим и аппаратное управление потоком. Ловушка в длине слова: бит чётности у STM32 входит в слово и занимает его старший бит (RM0008, таблица 195). Поэтому 8E1 — это UART_WORDLENGTH_9B плюс UART_PARITY_EVEN, а 7E1 — UART_WORDLENGTH_8B с чётностью.
UART_HandleTypeDef huart1; /* GPIO, clocks and the DMA channel are configured elsewhere (CubeMX) */
static uint8_t rx_buf[64];
void uart1_start(void)
{
huart1.Instance = USART1; /* PA9 = TX, PA10 = RX, clocked from PCLK2 */
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE; /* 8E1 = UART_WORDLENGTH_9B + UART_PARITY_EVEN */
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart1); /* computes USART_BRR from the actual PCLK2 */
static const uint8_t hello[] = "ready\r\n";
HAL_UART_Transmit(&huart1, hello, sizeof hello - 1, 100); /* blocking, 100 ms timeout */
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buf, sizeof rx_buf);
}
/* Called when the buffer is half full, full, or the line stays idle for one frame. */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t size)
{
if (huart->Instance != USART1) return;
if (HAL_UARTEx_GetRxEventType(huart) == HAL_UART_RXEVENT_HT) return; /* wait for the rest */
handle_packet(rx_buf, size); /* your code: size bytes have arrived */
HAL_UARTEx_ReceiveToIdle_DMA(huart, rx_buf, sizeof rx_buf);
}
HAL_UART_Transmit() блокирует программу, пока все байты не уйдут или не истечёт таймаут. HAL_UART_Receive_IT() и HAL_UART_Receive_DMA() принимают заранее известное число байт в фоне, а HAL_UARTEx_ReceiveToIdle_DMA() удобна для пакетов неизвестной длины: DMA складывает байты в буфер, а обработчик вызывается, когда линия замолчала на время кадра. В режиме DMA обработчик срабатывает и на половине буфера — отсюда проверка типа события (STM32 HAL). Ошибки приёма — FE, NE, ORE, PE — приходят в HAL_UART_ErrorCallback().
Arduino: Serial
Serial.begin(скорость) настраивает USART на формат 8N1, другой формат передают вторым аргументом: Serial.begin(9600, SERIAL_8E1). Что происходит внутри ядра Arduino для AVR (ArduinoCore-avr):
- U2X.
begin()сначала пробует режим удвоенной скорости и отказывается от него, только если делитель не помещается в 12 бит или если это 57 600 бит/с на 16 МГц — исключение оставлено ради совместимости с загрузчиком старых плат и прошивкой моста ATmega8U2 на Uno и Mega 2560. Поэтому на Uno 115 200 бит/с идёт с U2X0 = 1 и ошибкой +2,1 %, а 57 600 бит/с — без U2X и с той же ошибкой +2,1 %, хотя с U2X было бы −0,8 %. - Буферы. На приём и на передачу — по 64 байта, если у микроконтроллера не меньше 1 КБ ОЗУ, иначе по 16. Когда приёмный буфер полон, новые байты молча выбрасываются, флага ошибки нет. При 115 200 бит/с 63 свободных байта заполняются за 63 × 86,8 мкс ≈ 5,5 мс, так что
delay()в цикле на такое время уже теряет данные. - Чётность. Байты с ошибкой чётности выбрасываются в обработчике прерывания.
- Передача.
Serial.write()иSerial.print()возвращаются сразу, пока в буфере передачи есть место, и ждут, когда он полон.Serial.flush()ждёт, пока последний байт полностью уйдёт в линию. - Чтение.
Serial.read()при пустом буфере возвращает −1 (Arduino, Language Reference). Если записать результат вcharи напечатать, получится символ с кодом 0xFF — частая причина «мусора» в выводе.
const size_t LINE_MAX = 32;
char line[LINE_MAX];
size_t len = 0;
void setup() {
Serial.begin(115200); // 8N1; Serial.begin(9600, SERIAL_8E1) for 8E1
}
void loop() {
while (Serial.available() > 0) { // read everything that has arrived, never wait
char c = Serial.read();
if (c == '\n') {
line[len] = '\0';
Serial.print("got: ");
Serial.println(line);
len = 0;
} else if (c != '\r' && len < LINE_MAX - 1) {
line[len++] = c;
}
}
// no long delay() here: at 115200 bit/s the 64-byte receive buffer fills in about 5.5 ms
}
Выводы 0 и 1. На Uno, Nano, Mini и Mega они же связаны с мостом USB–UART, и всё, что к ним подключено, может мешать связи с компьютером, вплоть до неудачной загрузки скетча (Arduino, Language Reference). На Uno R3 мост ATmega16U2 подключён к ATmega328P через резисторы 1 кОм (схема A000066): внешнее устройство, подключённое к выводу 0, пересиливает выход моста, и загрузчик не слышит компьютер. Отключайте такое устройство на время загрузки или используйте другой UART: у Mega есть Serial1–Serial3 на отдельных выводах, а у Leonardo и Micro порт на выводах 0 и 1 — это Serial1, отдельный от USB-порта платы. Светодиоды RX и TX на Uno мигают только при обмене через USB, а не на выводах 0 и 1 (Arduino).
Отладка UART
Начинайте с самого простого и двигайтесь к сложному:
- Петля. Замкните TX и RX одного устройства — например, переходника USB–UART — и откройте терминал: каждый набранный символ должен вернуться. Вернулся — порт, драйвер и настройки терминала в порядке, ищите проблему в проводах или на другой стороне. У UART 16550 есть и внутренняя петля для такой проверки (TL16C550C).
- Мультиметр. В покое на TX — напряжение питания логики, то есть единица. Постоянный ноль на TX означает, что передатчик выключен или линия в состоянии break.
- Осциллограф. Самый короткий импульс — это длительность бита, а скорость равна единице, делённой на неё: 104 мкс — 9600 бит/с, 8,7 мкс — 115 200 бит/с. Удобно попросить устройство передавать «U»: меандр с частотой вдвое ниже скорости. Заодно видны уровни (3,3 или 5 В), затянутые фронты и помехи.
- Логический анализатор с декодером UART. Декодеру сообщают скорость, число бит данных, чётность, число стоп-битов, порядок бит (младший первым) и полярность: для логических уровней — обычную. Частоту выборки берите с запасом: при 1 МГц на бит 115 200 бит/с приходится около 8,7 отсчёта — для декодера хватает, но фронты видны с точностью ±1 мкс, то есть ±12 % бита. Кадры с ошибкой декодер обычно отмечает — это первый признак неверной скорости или формата. Линию RS-232 удобнее смотреть на стороне логики, до преобразователя: там 0 и 3,3 или 5 В и сигнал без инверсии.
- Терминал. Проверьте скорость, формат, отключённое управление потоком и то, что порт не занят другой программой. Учтите, что открытие порта может сбросить Arduino через линию DTR, — об этом в разделе об автосбросе.
«Кракозябры»: как распознать причину
| Что видно | Вероятная причина | Что сделать |
|---|---|---|
| случайные символы, много ошибок кадра, байтов приходит больше или меньше, чем отправлено | разные скорости | измерить длительность бита осциллографом |
| поток нулевых байтов с ошибками кадра | приёмник намного быстрее передатчика: длинные нулевые биты читаются как целые нулевые символы | выставить скорость передатчика |
| латинские буквы частично заменены другими символами: «ABCabc» в Windows-1251 выглядит как «ABГбвc» | передатчик в формате 7E1, приёмник в 8N1: бит чётности принят как восьмой бит данных | выставить одинаковый формат |
| часть символов пропадает без явных ошибок | приёмник ждёт чётность, которой нет, и выбрасывает байты с ошибкой чётности, или переполняется буфер | проверить формат и как часто программа читает буфер |
| латиница в порядке, испорчена только кириллица | кодировка: передаётся UTF-8, а терминал показывает Windows-1251, или наоборот | настроить кодировку терминала, UART тут ни при чём |
| символы «ÿ» или «�» с кодом 0xFF | программа печатает результат Serial.read() без проверки Serial.available() | читать, только когда есть данные |
| ничего не приходит | перепутаны TX и RX, нет общего провода, включено управление потоком без CTS, порт занят | петля, мультиметр, настройки порта |
Как выглядит рассогласование скоростей, можно предсказать. Если приёмник быстрее передатчика, каждый длинный бит он читает как несколько своих, и байтов становится больше. Например, «A» (0x41), переданная на 9600 бит/с, на приёмнике с 19 200 бит/с, устроенном как USART AVR, превращается в два байта: 0x06 с ошибкой кадра и 0xFE. Если приёмник медленнее, несколько символов сливаются в один, и байтов становится меньше. Поэтому первым делом сравните число отправленных и принятых байтов.
Типичные ошибки
| Ошибка | Симптом | Что сделать |
|---|---|---|
| TX соединён с TX, RX — с RX | ничего не принимается, выходы двух передатчиков работают друг против друга | соединить накрест: TX → RX |
| нет общего провода | связи нет или она зависит от того, как лежат провода | соединить GND устройств |
| 5 В на входе 3,3-вольтовой микросхемы | превышен абсолютный максимум, ток течёт через защитный диод входа; поначалу всё может работать | делитель или буфер, у STM32 — вывод FT |
| неверная тактовая частота: CKDIV8, RC-генератор, F_CPU не совпадает с реальной | мусор и ошибки кадра; при CKDIV8 скорость ровно в 8 раз ниже | фьюзы, кварц, осциллограф |
| RS-232 подключён к выводам UART напрямую | инверсия: покой RS-232 — минус, а UART ждёт единицу; ±12 В могут вывести вход из строя | преобразователь MAX3232 |
| выводы 0 и 1 Arduino заняты внешним устройством | скетч не загружается, монитор порта молчит | отключать устройство на время загрузки, другой UART |
| мост USB–UART подключён к компьютеру, а плата без питания | плата «полуживая»: ток с TXD моста течёт в неё через защитный диод входа RXD | общее питание; WCH предлагает диоды и транзистор в линиях |
| включено аппаратное управление потоком, а CTS не подключён | передача не начинается | выключить управление потоком или подключить RTS и CTS |
| форматы кадра не совпадают: 8N1 и 7E1 или 8E1 | часть символов искажена или пропадает | одинаковые настройки на обеих сторонах |
| в Arduino IDE выбран не тот вариант платы | загрузка не начинается: загрузчик ждёт другую скорость | Uno и Nano с новым загрузчиком — 115 200 бит/с, Nano со старым и Pro Mini 16 МГц — 57 600 |
Для связи между приборами и на длинных линиях тот же кадр UART передают через RS-232 и RS-485, для микросхем на одной плате есть синхронные SPI и I²C. Другие интерфейсы — в разделе «Интерфейсы».
Частые вопросы
Чем UART отличается от USART?
USART — это UART, который умеет работать ещё и синхронно: тактовый сигнал идёт по отдельному выводу (XCK у AVR, CK у STM32), и данные защёлкиваются по его фронтам, как в SPI. В асинхронном режиме USART ничем не отличается от UART — тот же кадр из старт-бита, данных и стоп-битов, — поэтому асинхронный режим USART обычно и называют UART. USART в AVR умеет работать и ведущим SPI.
Как подключать UART: TX к TX или TX к RX?
Накрест: TX одного устройства — к RX другого, RX — к TX, и обязательно общий провод GND. TX — выход данных, RX — вход; если соединить TX с TX, два выхода окажутся друг против друга, а входы останутся без сигнала. Уровни должны совпадать: вход 3,3 В без допуска 5 В подключают к 5-вольтовому выходу через делитель, а к COM-порту RS-232 — только через преобразователь вроде MAX3232.
Какая ошибка скорости UART допустима?
Суммарное расхождение частот передатчика и приёмника — около 2–3 %: Maxim в AN2141 получает ±3,3 % для короткой линии и ±2 % для длинного кабеля. Для AVR Microchip рекомендует держать ошибку каждой стороны в пределах ±2 % при кадре 8N1 и ±1,5 % — с битом чётности или в режиме удвоенной скорости U2X. Ошибку для конкретной частоты и скорости считает калькулятор скорости UART на gaw.ru.
Какая максимальная скорость UART?
Её задают тактовая частота и делитель. USART ATmega328P в режиме U2X выдаёт до f/8 — 2 Мбит/с при кварце 16 МГц, USART STM32F1 — до 4,5 Мбит/с. Мосты USB–UART: CH340 — до 2 Мбит/с, CP2102N и FT232R — до 3 Мбод, PL2303GC — до 12 Мбит/с. На скоростях выше 1 Мбит/с производители мостов советуют включать аппаратное управление потоком, иначе приёмник не успевает забирать данные.
Почему в мониторе порта «кракозябры»?
Чаще всего не совпадает скорость. Проверьте, что в терминале выбрана та же скорость, что в программе, и что микроконтроллер работает на той частоте, под которую программа собрана: новый ATmega328P с завода идёт на 1 МГц из-за фьюза CKDIV8, а его RC-генератор без калибровки ошибается до 10 %. Если искажена только часть символов, не совпадает формат кадра, например 7E1 и 8N1. Если испорчена только кириллица, дело в кодировке терминала, а не в UART.
Какой длины может быть провод UART?
UART с логическими уровнями рассчитан на связь внутри прибора: в качестве хорошей линии Maxim в AN2141 приводит шину длиной около метра с буферированными КМОП-уровнями. Чем длиннее провод, тем сильнее затягиваются фронты, собираются помехи и расходятся потенциалы земли устройств. Для метров и десятков метров тот же кадр UART передают через RS-232, для сотен метров — через RS-485.
Источники
- Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B), 2020 — гл. 20 — USART0: формат кадра, приём и выборки, флаги ошибок, табл. 20-1–20-7 — скорость и допуск приёмника; п. 9.2 и табл. 29-9 — RC-генератор и фьюз CKDIV8; табл. 29-11 — сброс; табл. 30-1 — пороги входов
- STMicroelectronics. RM0008 Reference manual STM32F101xx–STM32F107xx, Rev 21, 2021 — гл. 27 — USART: обнаружение старт-бита и шум (п. 27.3.3), дробный делитель (п. 27.3.4), допуск приёмника (п. 27.3.5, табл. 193 и 194), чётность, LIN, однопроводный режим, RTS/CTS
- STMicroelectronics. STM32F103x8, STM32F103xB Datasheet (DS5319), Rev 20 — табл. 5 — выводы FT, табл. 6 — допустимые напряжения, табл. 24 — точность HSI, табл. 36 и 37 — ёмкость и уровни выводов
- Maxim Integrated (Analog Devices). Tutorial 2141: Determining Clock Accuracy Requirements for UART Communications, 2003 — выборка в середине бита при 16 тактах на бит, допустимое расхождение ±3,3 и ±2 %, кварц и керамический резонатор
- Texas Instruments. TL16C550C Asynchronous Communications Element with Autoflow Control (SLLS177I), 2021 — делитель скорости и кварц 1,8432 МГц, стоп-биты и stick parity, флаги OE, PE, FE и BI, auto-RTS и auto-CTS
- WCH (Nanjing Qinheng Microelectronics). CH340 手册(一): USB 转串口芯片 CH340, версия 3D — китайская версия даташита: скорости, точность встроенного генератора и допуск приёмника, пороги входов, назначение выводов, защита от обратного питания, загрузка STM32 через DTR и RTS
- Silicon Labs. CP2102N Data Sheet, Rev. 1.5 — форматы и скорости, генератор ±0,25 %, правило ±2 %, RTS/CTS и XON/XOFF, передача break, VIO и допустимые напряжения
- Silicon Labs. CP2102/9 Single-Chip USB to UART Bridge, Rev. 1.7 — скорость до 1 Мбит/с; для новых разработок производитель рекомендует CP2102N
- FTDI. FT232R USB UART IC Datasheet, Version 2.16 (FT_000053) — скорости и делитель, VCCIO и пороги входов, буферы, инверсия сигналов, VID и PID по умолчанию
- Prolific. PL2303GC USB to Full UART IC with GPIO, Data Sheet V1.0.7, 2025 — скорости 5 бит/с – 12 Мбит/с, VDD_IO 1,8–5 В, управление потоком, VID и PID
- Prolific. PL2303 Windows Driver Download (страница поддержки, архив 19.01.2023) — версии микросхем без поддержки Windows 8–11, предупреждение о поддельных PL-2303HX
- Arduino. ArduinoCore-avr: HardwareSerial и boards.txt — HardwareSerial.h, HardwareSerial.cpp, HardwareSerial_private.h — буферы по 64 байта, выбор U2X и исключение для 57 600 бит/с, байты с ошибкой чётности; boards.txt — скорости загрузчиков
- Arduino. Language Reference: Serial, Serial.read() — выводы 0 и 1, уровни TTL, подключение TX к RX и общий провод
- Arduino. Arduino/Genuino Uno: описание платы (arduino.cc, архив 2017) — автосброс через DTR и конденсатор 100 нФ, перемычка RESET-EN, работа загрузчика после сброса
- Arduino. UNO R3 schematic (A000066) — резисторы 1 кОм RN4A и RN4B между ATmega16U2 и ATmega328P, цепь C5, RN1D и D2 на RESET
- AVR-LibC. <util/setbaud.h>: Helper macros for baud rate calculations — UBRR_VALUE, USE_2X и допуск BAUD_TOL = 2 %
- STMicroelectronics. STM32F1 HAL: драйвер stm32f1xx_hal_uart — UART_InitTypeDef, расчёт BRR от PCLK1 и PCLK2, бит чётности на месте старшего бита, HAL_UARTEx_ReceiveToIdle_DMA
- Microchip. AVR128DA28/32/48/64 Data Sheet (DS40002183E), 2024 — п. 25.3.3.2.6 — однопроводный режим USART (LBME, ODME), гл. 35 — UPDI
- LIN Consortium. LIN Specification Package, Revision 2.2A, 2010 — кадр LIN на основе UART, поле break не короче 13 бит, байт синхронизации 0x55, скорость 1–20 кбит/с
- Modbus Organization. MODBUS over Serial Line Specification and Implementation Guide V1.02 — спецификация, 2006: чётность по умолчанию, два стоп-бита без чётности, 7 бит данных в режиме ASCII
- Ecma International. ECMA-48: Control Functions for Coded Character Sets, 5th edition, 1991 — управляющие символы DC1 и DC3
- The Linux Kernel. drivers/usb/serial: ch341.c, cp210x.c, ftdi_sio_ids.h, pl2303.h — драйверы мостов USB–UART и их идентификаторы VID:PID
Смотрите также
- RS-232Уровни, распиновка DB9, нуль-модем, MAX3232, UART и AVR
- RS-485Уровни, длина линии, терминаторы, смещение, MAX485, Modbus RTU
- SPISCLK, MOSI, MISO, CS, режимы 0–3, daisy chain, частота, SPI в AVR
- I²CSDA и SCL, START/STOP, ACK, адреса, скорости, расчёт подтяжек
- Скорость UARTUBRR, BRR, TH1 и ошибка скорости
- AVRATmega, ATtiny, AVR Dx/Ex: семейства, прошивка, инструменты