
Комментарии 41
Автор молодец, Хабр - торт. Большое спасибо! Очень круто!
Такой фундаментальный труд полезно держать под рукой. Отдельно спасибо за pdf
Сохранил в закладки , надо бы выделить месяц на прочтение статьи))) Чувствую что-то шедевральное вы там натворили
Вроде-бы HID'ы имеют обратную совместимость с PS/2.
Честно говоря я не представляю как это возможно. PS/2 это последовательный синхронный интерфейс с побайтовой передачей работающий на частотах 7-12 кГц, у него два сигнала: DATA и CLK, оба имеют TTL уровни +5V. USB 1.0 - это асинхронная шина с пакетной коммутацией, сигналы D+ и D- имеют уровни +3.6V max, частоты: 1.5 и 12 МГц. Как это совместить ?
Это не HIDы, а конкретные реализации типа мышек Logitech древних времен.
На Wikipedia в статье про PS/2 написано, что некоторые клавиатуры и мыши, предназначенные для работы в процессе загрузки ОС, могут определить куда они подключены и задействовать две имеющиеся сигнальные линии либо для встроенного USB Device устройства, либо для PS/2. Для этого промышленностью выпускались специальные переходники позволяющие подключить одно и то же устрйство с USB type A разъемом либо в USB type A (HID), либо в круглый PS/2. Но это не совместимость на уровне стандартов, это такой костыль от производителей устройств ввода нацеленный на облегчение перехода пользователей на USB HID.
Есть мышки с клавами, которые умеют и в usb, и в ps/2 аппаратно включаиься. Не пипец как это сложно.
А программно: Если в HID-дескрипторе описать классическое поведение устройств рс/2, то да, можно о какой-то совместимости говорить. Типа ту же последовательность байт при событии слать.
Но - совместимы в каком месте? Для пользователя и программиста под любой ОС они по определению совместимы, хоть они рс/2, хоть usb, хоть rs232, хоть TCP/IP с удалëнной машины .
У USB и PS/2 абслютно разные электрические интерфейсы, это не просто посылка байтов. Никакой совместимости между этими двумя стандартами нет и быть не может. Может быть только общий уровень абстракций, как например в Input Device в Linux который формирует общий поток событий для ПО прикладного уровня.
USB можно даже на AVR микроконтроллерах реализовать программно.
Проект называется V-USB
https://ru.wikipedia.org/wiki/V-USB
Спасибо. Про V-USB мне подсказали на конференции FPGA-Systems уже после моего доклада, не знал. Хочу посмотреть как там вычисляется CRC16 и при этом библиотека успевает вложиться в жесткие тайминги. У меня это сделать не получилось на RV32 ядре 60+ МГц, поэтому пришлось отдать расчет CRC на железную часть.
Я добавил в конце статьи ссылку на презентацию (PDF, 56 слайдов) к моему докладу на эту же тему с которым я выступил на конференции FPGA-Systems 2025 прошедшей 29 ноября 2025 в Москве. Это для тех, кто хочет быстро ознакомиться с содержимым статьи не читая её. :-)
Спасибо за хаброторт! До конца дочитаю, наверное, только в следующем году) Но всякие справочные штуки типа форматов сообщений - и я буду искать здесь, и поисковики, надеюсь, тоже (когда раздуплят свои ИИ. ЫЫ) Давно хотел видеть открытое и понятное описание открытого протокола. Осцилл вот у меня умеет в анализ uart/spi/i2c, но вот usb - как-то не срослось, собственными глазами приходится отлаживать.
Пользуясь случаем вынесу тему на всеобщиее срач обсуждение. Как вы считаете, почему в 2000х годах USB победил конкурирующую технологию FireWire? Хотя в FireWire тогда уже было всё, что сейчас USB 3 только внедряет - две скоростные дифф.пары точка-точка, PowerDelivery больше 3 Вт. С какого?
На мой взгляд FireWire гораздо более сложен в реализации. Если перенестись в конец 90-х, то можно понять что не всякий производитель мог себе позволить проектировать такие сложные интерфейсы. USB 1.0 (и даже 2.0) относительно простой протокол. Ну и я думаю Apple приложила не мало усилий, чтобы усложнить сторонним разработчикам адаптацию этого стандарта.
С программной точки зрения - оба хороши. Они оба предполагают более-менее умный девайс на том конце линии, который по крайней мере способен назвать своё имя текстом. А аппаратно - ну тут вопросики:

В то время, как FireWire основан на обычном LVDS, стандартизированным IEEE, ISO и даже ГОСТ успел скопировать это всё. Что на 100 Мбит/c, что на 3 гигабит. И Две дифф.пары USB3.0 на этом фоне выглядят, как костыль.
Возможно, не взлетело из-за отсутствия низких скоростей. Сразу, понимаш, давай мышку на 100 Мбит/c, понимашь. Наверное. Но стандарт мог бы и в сторону низких скоростей развиться, было бы желание.
Тот же маркер окончания пакета USB тоже выглядит каким-то костылём, я б сказал. Одновременно он пипец как важен, но декодировать его приходится не в дифферениальном виде. И вся стойкость дифф.сигнала к наводкам в самом важном месте теряется.
Ну, USB 1.0 вообще не является дифф линией, а в USB 2.0 есть её жалкое подобие. Зато аппаратная часть очень просто реализуется и не требуется дорогостоящих трансиверов. При желании можно программно модулировать сигнал на шине. Иными словами, USB взял рынок дешевизной.
На мой взгляд маркер EOP это очень годное решение - избавляет от необходимости вводить поле с размером пакета или полезной нагрузки, занимает всего три битовых интервала.
Ну вообще да. Дифф. пара и согласование волнового сопротивления кабеля начинается только с 2.0. Но, wtf, тогда для 1.0 и 1.1 вполне должно и одного сигнального провода хватать. I1C называлось бы))), т.к. 1wire уже к этому времени был запатентован.
Сейчас как раз упёрся в совместимость USB через гальвано-изолятор. Не знал бы про протоколы в железе - подключение или нет так и осталось бы для меня магией таро.
Мощно. Объем впечатляющий. Апплодирую стоя!
Надеюсь когда-нибудь дойду до глубокого погружения в прочтение :)
Норм талмуд. Почти нет фактических ошибок, так по мелочи. В своё время тоже занимался такой ерундой)
Могу разве что добавить, что автор хоть и упомнял смену протокола для HID устройств, но почему-то в коде этого не сделал. Желательно принудительно отправить SetProtocol, чтоб устройство точно работало в Boot Protocol режиме.
req = (USB_Request_Header) {
.bmRequestType = (REQ_DIR_HOSTTODEVICE | REQ_TYPE_CLASS | REQ_REC_INTERFACE),
.bRequest = REQ_SetProtocol,
.wValue = 0,
.wIndex = 0,
.wLength = 0,
}; Ну и на практике у меня некоторые клавиатуры не заводились (не отправляли данных "Interrupt IN") пока я не дергал GetDescriptor(HID_Report)
uint8_t data[0x100];
req = (USB_Request_Header) {
.bmRequestType = (REQ_DIR_DEVICETOHOST | REQ_TYPE_STANDARD | REQ_REC_INTERFACE),
.bRequest = REQ_GetDescriptor,
.wValue = (HID_Report << 8),
.wIndex = 0,
.wLength = sizeof(data),
};
// requesting descriptors for HID reports to let device know that we are ready to interact with it Спасбо за Ваше ревью. Похоже, что Вы единственный кто смог прочесть статью целиком. :-)
На счет установки Boot Protocol принудительно. Мне где-то попадалась инфа (помоему на OSDev), что с некоторых пор все клавы и мыши, по просьбам разработчиков BIOS, по умолчанию работают в Boot протоколе. Во всяком лучае, мне пока что другие не попадались. :-) Но я подправлю код конечно же.
Есть идея расширить до USB 1.1 и сделать поддержку Mass Storage Class, чтобы загружать на ПЛИСе какую нибудь ОС.
Тогда в статье есть неоднозначность:
"Согласно спецификации, все USB HID устройства по-умолчанию активируют «Report Protocol». "
Я не помню была ли у меня проблема с какими-то устройствами без принудительной установки протокола, но думаю что код у меня не возник из ниоткуда, и всё же что-то там глючило.
И «Keepalive» и «SOF» используются в том числе для синхронизации внутренних часов на устройстве.
Можно про это по подробнее?
Тут имеется в виду RTC?
Какой формат пакета со временем? Год месяц день тоже передается?
Какой порядок байт в параметрах пакетах USB? Litttle endian или big endian?
USB является использования «бит-стаффинга» — методики позволяющей избежать долгое нахождение шины в каком либо из состояний («K» или «J») при передачи пакета данных. ..................стаффинг в USB работает следующим образом: при передаче шести и более последовательных «единиц» (а «единицы» кодируются отсутствие смены состояния шины), в передачу вставляется одна дополнительная смена состояний. Если принимающая сторона по какой-то причине получает семь последовательных «единиц», то произошел сбой передачи!
Забавно вот что.
В CAN бит-стаффинг делают после пяти бит.
В USB Low Speed (USB 1.0 ) бит-стаффинг делают после шести бит.
5 != 6
При этом, и там и там скорость около 1 MBit/s.
Почему в CAN и USB разные настройки бит-стаффинга?

Почему в CAN и USB разные настройки бит-стаффинга?
Быть может потому, что USB это строгие допуски и использование кварцевых стабилизаторов, в то время как CAN - нет. USB это постоянное слежение ФАПЧ и подстройка приёмника под сигнал, а у CAN нет, там время бита задаётся жёстко конфигурацией приёмника. Он как UART, а стаффинг там больше для того, чтобы другой приёмник IDLE не усмотрел посреди передачи.
На STM32 (как и на любом ARM Cortex-M MCU) CAN-трансивер тоже тактируется от PLL, а PLL тактируется тоже кварцевым резонатором.
При этом битовую скорость CAN, при желании, можно менять на лету. Посмотрите на PCAN-Pro-X
У любого CAN нет следящего ФАПЧ в приёмнике. Только отлавливание первого перепада стартового бита как точка отсчёта. Неточно настроил - короткие пакеты проходят а длинные уже нет. В USB ФАПЧ приёмника работает всё время пока есть сигнал. И для начальной стабилизации присутствует преамбула.
PS Если вы хотели обратить моё внимание на расширение FD, то там законы ровно те же.
Следящий PLL нужен в GNSS приемниках. Так как там из-за эффекта Доплера несущая частота уползает.
А USB почему частота тактирования не стабильная?
Я тоже задавался таким вопросом, но ответа не нашел. В Ethernet еще в 80-х уже был bit-stuffing в 5 единичных бит. Почему в Microsoft решили сделать это иначе - не известно. Наверное по принципу "чтоб не как у других".
Разработка цифровой аппаратуры нетрадиционным методом: Контроллер USB 1.0 на SpinalHDL