Я продолжил работу над своим проектом USB хост контроллера для платы Марсоход3. Описание первой версии проекта вот здесь. Там я подключал USB мышь. Теперь в моем проекте уже есть два USB порта и они все так же управляются через последовательный порт.
Это видео демонстрирует работу хост контроллера и подключенных к плате мыши и клавиатуры. На плату Марсоход3 установлен наш шилд разъемов.
Проект в среде Quartus Prime адаптировать для двух портов было не сложно. Единственная проблема, которая стояла передо мной - как различать команды для нулевого и первого портов? Нужно же как-то иметь возможноть опрашивать порты по очереди и индивидуально? В результате я подумал, что самое простое решение, раз уж я использую для обмена последовательный порт - это использовать сигнал RTS из него. Сигнал RTS это Request-to-Send. Вообще-то он предназначен для handshake протокола модема. Ну а я решил использовать этот сигнал для выбора текущего USB порта. И в программе опроса USB портов для переключения портов я использую Windows API функцию EscapeCommFunction(host->hPort, SETRTS /*CLRRTS*/);
В результате, в топ модуль проекта квартуса я вставил два экземпляра своего usb хост контроллера usbhost, а для их переключения использую простейший модуль switcher.
Модуль switcher написан на Verilog HDL и просто коммутирует сигналы чтения rd, записи wr, готовности данных и сами данные от двух экземпляров хост контроллера usbhost.
module switcher( input wire sel, input wire rd, input wire wr, output wire rd0, output wire rd1, output wire wr0, output wire wr1, input wire drdy0, input wire drdy1, input wire [7:0]d0, input wire [7:0]d1, output wire drdy, output wire [7:0]d ); assign rd0 = (!sel) & rd; assign wr0 = (!sel) & wr; assign rd1 = ( sel) & rd; assign wr1 = ( sel) & wr; assign drdy = sel ? drdy1 : drdy0; assign d = sel ? d1 : d0; endmodule
В плате Марсоход3 сигнал последовательного порта RTS идет из микросхемы FTDI как FTDI_BD2 пин 138.
Довольно сложная проблема как ни странно не в железе, а в программной части проекта. Хотя.. нет. Это не странно. Шина USB изначально разрабатывалась так, что программная часть подразумевалась быть сложной. К сожалению. В чем тут сложность? Самое главное - это ПО обязано запросить у устройства Report Descriptor и на основе информации из этого дескриптора оно должно разбирать принятые пакеты и обрабатывать их соответствующим образом. Это то, чего я не делаю. Чтобы сделать все правильно я должен написать довольно много кода. Ну или где-то взять этот код из ядра Linux, например, взять себе и адаптировать. Это все сложновато. Поэтому я просто читаю репорты как есть и разбираю их единственным известным мне способом. Я думаю, что так будет работать наверное 90% USB клавиатур.
Там в пакете от клавиатуры 8 байт. Первый байт - это битовое поле отражающее статус клавиш Shift, Control, Alt, Win/GUI:
//HID keyboard modifier flags
#define Modifier_Left_Ctrl 0x01
#define Modifier_Left_Shift 0x02
#define Modifier_Left_Alt 0x04
#define Modifier_Left_GUI 0x08
#define Modifier_Right_Ctrl 0x10
#define Modifier_Right_Shift 0x20
#define Modifier_Right_Alt 0x40
#define Modifier_Right_GUI 0x80
Второй байт пакета не знаю, что обозначает - он кажется всегда ноль.
Оставшиеся 6 байт отображают коды нажатых клавиш. То есть обычная клавиатура более 6 нажатий одновременно не передает. Таблицу кодов можно взять из следующего файла:
Интересно, что повтор нажатия клавиш не формируется из кодов клавиатуры. Программное обеспечение само может и должно формировать повтор нажатия. Но вот событие "отжатие" формируется интересно - в пакете просто пропадает соответствующий код клавиши, который раньше присутствовал в пакете.
Это все создает определенные сложности для программиста.
Кроме этого, коды USB клавиатуры не соответствуют обычным сканкодам от PS/2 клавиатуры и не соответствуют Windows Virtual Key. так что их еще нужно как-то преобразовывать. Я делаю это по таблице составленной из PDF документации указанной выше. Кодов много, ошибиться в преобразовании очень легко. По опыту знаю, что есть еще проблемы с национальными клавиатурами бразильской, китайской, японской. Там есть дополнительные клавиши, которых нет на обычных европейских клавиатурах. Я это все делать сейчас, конечно не буду.
Я делаю только базовую версию ПО, которое может получать коды клавиш от подключенной USB клавиатуры и иммитировать нажатие клавиши на десктопе. Для иммитации событий на десктопе я использую функции Windows API mouse_event() и keybd_event().
Порты опрашиваются из одного потока по очереди.
Надеюсь, что из демонстрационного видео в самом начале статьи вы видите, что мой проект работает, как я и задумывал. Мышь и клавиатура подключены к плате Марсоход3, но события от этих мышей и клавиатур вызываются на десктопе.
Весь исходный код для этого проекта можно взять на github: https://github.com/marsohod4you/UsbHost.git
но для этой версии двухпортового проекта выбирайте бранч Two-Usb-Ports-Keyb_Mouse.
Подробнее...