Некоторое время назад мне в очередной раз потребовался USB-Serial адаптер. И не просто адаптер c RX/TX, а чтобы еще присутствовали управляющие сигналы. И не один UART, а несколько. И еще желательно, в виде одного композитного устройства, чтобы все это хозяйство не занимало больше одного USB-порта. Так и началась эта история...


Готовые устройства из тех, что можно приобрести в магазине, меня не вдохновили. Либо дорого и сложно найти в продаже, либо не подходят по одному или нескольким критериям выше. Композитных USB-Serial адаптеров в дешевом сегменте я и вовсе не смог найти. Зато на столе обнаружилась отладочная плата STM32 Blue Pill на микроконтроллере STM32F103C8T6. Сверившись с документацией, я убедился, что этот микроконтроллер содержит в себе все, что мне нужно для полного счастья. А именно: интерфейс USB 2.0 full-speed, три USART, семиканальный DMA контроллер и достаточное количество линий GPIO для реализации всех интересующих меня управляющих сигналов.


Осталось только раздобыть соответствующую прошивку, которая превратит безжизненный кусок железа в нужное и полезное устройство. И тут меня поджидало большое разочарование: оказывается, несмотря на бешеную популярность этого микроконтроллера и отладочной платы, никто до сих пор так и не сделал ничего подобного. Вернее, попытка была, но это скорее экспериментально-образовательный proof-of-concept, а не законченный продукт. Чего только стоит блокирующая передача данных по UART: один порт передает, остальные ждут. Нет, этот микроконтроллер определенно заслуживает большего! Пришлось засучить рукава и написать прошивку самому.


Что у меня получилось


Я решил по-максимуму задействовать возможности периферии микроконтроллера, но вместе с тем не впадать в пучину предварительных оптимизаций. Читаемость и поддерживаемость кода для меня гораздо важнее, чем незначительный прирост производительности. Тем более, что с производительностью и так все вышло очень хорошо.


Что реализовано:


  • три независимых последовательных порта;
  • поддержка аппаратного контроля потока (RTS/CTS) на двух из трех портов;
  • поддержка управляющих сигналов DSR/DTR/DCD/RI;
  • поддержка 7 и 8-битной длины слова;
  • поддержка контроля четности;
  • 1, 1.5 и 2 стоповых бита;
  • совместимость со стандартными драйверами Linux, macOS и Windows;
  • подписанный INF файл для Windows XP, 7 и 8;
  • поддержка произвольных скоростей (более 2 Мбит/с);
  • сигнал TXA для управления трансиверами RS-485 (DE, /RE);
  • DMA на передачу и прием данных USART;
  • встроенный командный интерпретатор для конфигурации;
  • нет зависимостей от сторонних библиотек кроме CMSIS;
  • проект с открытым исходным кодом, лицензия MIT;

Командный интерпретатор позволяет настраивать следующие параметры:


  • тип выхода: двухтактный, открытый сток;
  • тип подтяжки входных линий: вверх, вниз, плавающая;
  • инверсия для управляющих сигналов: активный высокий/низкий;

Командный интерпретатор активируется на первом CDC порту при подключении пина PB5 к земле, поддерживает часть управляющих последовательностей ANSI (стрелочки, backspace), и принимает вполне дружелюбные на вид команды:


*******************************
* Configuration Shell Started *
*******************************

>uart 1 tx output pp dcd active low pull up
>

Командный интерпретатор не позволяет переназначать сигналы с одних пинов на другие. Во-первых, далеко не все сигналы можно переназначить, а во-вторых, такая возможность по-настоящему становится востребованной только при использовании прошивки в контексте какой-либо иной платы. В этом случае проще и правильнее переназначить сигналы используя конфигурацию, хранящуюся в исходном коде.


Распиновка вышла следующей:


Signal Direction UART1 UART2 UART3
RX IN PA10 PA3 PB11
TX OUT PA9 PA2 PB10
RTS OUT N/A PA1 PB14
CTS IN N/A PA0 PB13
DSR IN PB7 PB4 PB6
DTR OUT PA4 PA5 PA6
DCD IN PB15 PB8 PB9
RI IN PB3 PB12 PA8
TXA OUT PB0 PB1 PA7

Пины, выделенные жирным шрифтом, являются толерантными к 5 В.


Сигнал TXA (TX Active) служит для управления микросхемами трансиверов RS-485 (DE, /RE). TXA активен во время передачи данных и переключается в неактивное состояние не более чем за 0.6 мкс после завершения передачи. Это соответствует спецификациям RS-485 на скоростях до 920 кБод c почти двукратным запасом по времени переключения.


К сожалению, реализовать RTS/CTS на UART1 не вышло из-за того, что соответсвующие пины заняты сигналами USB. Можно было вывести RTS на какой-нибудь другой пин, поскольку RTS управляется программно, в зависимости от степени заполнения кольцевых буферов на прием, но порт c RTS и без CTS мне показался странной штукой и я решил, что так делать не надо.


Проект написан на языке C, и подразумевает использование arm-none-eabi-gcc для сборки. Я использовал специфичный для GCC синтаксис атрибутов и расширения языка С. Совместимость проекта с проприетарными компиляторами меня не интересует, но если кто-то считает это важным, то я готов принять соответствующий пул-реквест.


В результате у меня получилось удобное и мощное устройство которое полностью закрывает все мои потребности в последовательных портах. STM32 Blue Pill можно использовать как самостоятельно, так и в составе схем обеспечивающих согласование уровней и развязку. Возможность настройки сигнальных линий позволяет упростить разработку таких схем.


Подробная документация, исходный код и собранная прошивка доступна в репозитории проекта на GitHub.


Дальнейшие планы


Нет предела совершенству и этот проект – не исключение. В каких-то местах код можно было написать чище, понятнее и эффективнее. Я обязательно этим в какой-то момент займусь. Кроме того, я очень рассчитываю на обратную связь от пользователей для поиска и устранения возможных багов.


Обновления


Поддержка RI


28.11.2020


Добавил поддержку сигнала RI на всех портах (спасибо plyatov), добавил в статью описание сигнала RI.


Устранил ошибку, приводящую к возможному повреждению данных на приеме


01.12.2020


Код не проверял готовность BULK IN endpoint перед отправкой CDC ZLP-пакетов. При определенных условиях это могло приводить к повреждению принимаемых данных. Очень странно, но эта ошибка в реальной жизни проявлялась существенно реже, чем можно было бы ожидать. Исправлено в версии 2.1.1.


Поддержка управления трансиверами RS-485


02.12.2020


Добавил поддержку управления трансиверами RS-485 в версии 2.2.0, обновил статью, добавил описание сигнала TXA. Спасибо dernuss и остальным тем, кто просил сделать эту фичу.