
Комментарии 33
Автор молодец, Хабр - торт. Большое спасибо! Очень круто!
Такой фундаментальный труд полезно держать под рукой. Отдельно спасибо за 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 1.0 на SpinalHDL