Глюки иногда бывают интереснее самих программ, в которых возникают. Иногда они помогают развлечься или даже узнать что-то новое. В этот раз благодаря глюку я узнал, как сделать мышь.
Experimental set
Вечером в четверг я отлаживал небольшую программку для контроллера: состояние аналоговых джойстиков пересылалось с отладочной платы по УАРТ в комп. Компы нынче КОМ-портами оснащают редко, поэтому я работал через переходник USB-COM. Я пытался понять, почему столбик данных в Comport Toolkit приходит неровным, когда мои размышления были грубо прерваны Синим Экраном Смерти.
Завязка
После ресета совершенно неожиданно взбесился мышиный курсор. С невиданным упорством он уползал в левый нижний угол экрана! Я видел такое поведение впервые и некоторое время не знал, что и думать. К своему стыду, не контролируя курсор, я не мог даже залогиниться в винду (так как не знаю хоткеев, а табуляция на экране приветствия вин7 не работает).
Выдрав все девайсы из компа и несколько раз нажав ресет, я усмирил курсор и смог залогиниться. Последовательно втыкая девайсы обратно, я установил, что причиной мышиного буйства был переходник USB-COM (оказывается, управляемый им курсор в нижнем левом углу еще и покликивал). Решив, что переходник можно хоронить, я пошел домой.
Кто виноват
На следующий день (в пятницу), я продемонстрировал коллегам бешеный переходник. В ходе демонстрации выяснилась следующая вещь: если отключить отладочную плату (которая все это время честно слала данные с джойстиков в бесконечном цикле), «эффект мыши» пропадал.
Значит, дело было или в данных, которые отсылала плата или в конкретной винде или в сочетании всего этого с переходником USB-COM. Эти предположения я проверил, подключив плату к другому компу через переходник (эффект сохранился) и без переходника в аппаратный COM-порт (эффект сохранился). Следовательно, дело было в самой плате.
При подключении платы к другому компу появилось всплывающее сообщение «Обнаружено новое устройство — Microsoft Ballpoint Trackball»!
Но как же такое возможно?!
Погуглив название трекбола я узнал лишь, что подобная проблема возникала с древними DSL-модемами или специфическими девайсами, использовавшими COM-порт. Но просто локализовать проблему и вылечить ее (отключением «трекбола» в диспетчере устройств) было как-то не интересно.
Более общий запрос “Microsoft serial mouse protocol” выявил совершенно удивительную (для меня) вещь: оказывается, для того, чтобы устройство было опознано как Microsoft Serial Mouse, вполне достаточно отослать букву ‘M’ (0x4D), когда пин DTR у COM-порта меняет состояние [1]!
Детали
Мышиный протокол (для Microsoft serial mouse) выглядит так. Каждый пакет состоит из трех байт (но используются только 7 бит данных):
6 бит | 5 бит | 4 бит | 3 бит | 2 бит | 1 бит | 0 бит | |
Байт 1 | 1 | LB | RB | Y7 | Y6 | X7 | X6 |
Байт 2 | 0 | X5 | X4 | X3 | X2 | X1 | X0 |
Байт 3 | 0 | Y5 | Y4 | Y3 | Y2 | Y1 | X0 |
Здесь:
- LB и RB — состояния левой и правой кнопок соответственно (1 означает «нажато»)
- X7..X0 и Y7..Y0 — приращения по двум координатам (один знаковый байт на каждую координату)
Работает такая мышь на скорости 1200 bps, использует 7 бит данных в каждом байте (т.е. старший бит игнорируется), один стоп-бит.
Лирическое отступление
На моей отладочной плате был полноценный COM-разъем, но я использовал только пины RX и TX и состояние DTR не мониторил. По-видимому, непрерывный поток данных с платы просто попадал в такт с дерганием пина. К тому же, Microsoft serial mouse работает на скорости 1200 bps (а я на 9600), да еще с 7 битным пакетом данных. Видимо, звезды заняли в тот вечер правильное положение!
Еще один сеанс гугла (уже из чистого любопытства) привел меня к документу «Plug and Play. External COM Device specification», датированному февралем 1995-го года, в котором был найден алгоритм работы мыши [2]:
- Echo DTR to DSR, always, in hardware. On Power-up (from DTR=1 and TXD=Mark)
- If RTS=0, wait (forever) for RTS=1
- If RTS=1, go send COM ID (e.g. Table 3)
- Go be a mouse
- If RTS=0, go back to state 2
К сожалению, в представленном там списке идентификатора трекбола не было. Если в ComPort Toolkit сначала «открыть» COM-порт, а потом подключать к нему плату, «эффекта мыши» не происходило. Послушав, что же плата присылает, я увидел приблизительно следующее:
«kkKkKkkkkkj FF.ŒÎckkkkkj». Найти в этой каше ID трекбола мне не удалось (отдельные буквы k или j не сработали).
Go be a mouse
Тестовая отсылка строчки из двух сотен букв M на скорости 1200 bps привела к желанному результату – плата определилась как Microsoft serial mouse! Я быстренько накидал функцию для упаковки координат, и вот уже курсор послушно ползет в нужный угол!
static void formPacket(bool leftClick, bool rightClick, int8_t x, int8_t y)
{
packet[0] = 0;
packet[1] = 0;
packet[2] = 0;
// клики
packet[0] |= (1<<6) | (leftClick<<5) | (rightClick<<4);
// high y
packet[0] |= ((y>>6) & 0x3) << 2;
// high x
packet[0] |= ((x>>6) & 0x3);
packet[1] = x & 0x1F;
packet[2] = y & 0x1F;
}
Замена «инициализирующей последовательности» на трекбольную показала, что пакет у трекбола точно такой же, от чего мой интерес к трекболу угас окончательно.
Недостатки и косяки
- Чтобы такая «мышь» задетектилась, мне приходится руками выключать переходник USB-COM, включать его и нажимать на плате ресет.
- Кликать у меня почему-то не получилось.
- Отрицательные приращения работают криво, движение на -1 выглядит примерно как на +30 (по амплитуде, с направлением все в порядке).
Результат
Из-за косяка с отрицательным приращением мне я не стал париться с честным пересчетом наклона джойстика в приращение по координате и сделал джойстик просто трехпозиционным по каждой оси. Приращение в положительном направлении я подобрал так, чтобы оно примерно совпадало с -1.
Мне не удалось придумать какого-либо еще применения для такой «мыши», но, возможно, кому-нибудь пригодится.
Автор: Amomum