Некоторое время назад мне в очередной раз потребовался 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 и остальным тем, кто просил сделать эту фичу.