M for Mice

в 11:28, , рубрики: diy или сделай сам, микроконтроллеры, Программинг микроконтроллеров, развлечения, метки: , ,

Глюки иногда бывают интереснее самих программ, в которых возникают. Иногда они помогают развлечься или даже узнать что-то новое. В этот раз благодаря глюку я узнал, как сделать мышь.

Experimental set

Вечером в четверг я отлаживал небольшую программку для контроллера: состояние аналоговых джойстиков пересылалось с отладочной платы по УАРТ в комп. Компы нынче КОМ-портами оснащают редко, поэтому я работал через переходник USB-COM. Я пытался понять, почему столбик данных в Comport Toolkit приходит неровным, когда мои размышления были грубо прерваны Синим Экраном Смерти.

Завязка

После ресета совершенно неожиданно взбесился мышиный курсор. С невиданным упорством он уползал в левый нижний угол экрана! Я видел такое поведение впервые и некоторое время не знал, что и думать. К своему стыду, не контролируя курсор, я не мог даже залогиниться в винду (так как не знаю хоткеев, а табуляция на экране приветствия вин7 не работает).
Выдрав все девайсы из компа и несколько раз нажав ресет, я усмирил курсор и смог залогиниться. Последовательно втыкая девайсы обратно, я установил, что причиной мышиного буйства был переходник USB-COM (оказывается, управляемый им курсор в нижнем левом углу еще и покликивал). Решив, что переходник можно хоронить, я пошел домой.

Кто виноват

На следующий день (в пятницу), я продемонстрировал коллегам бешеный переходник. В ходе демонстрации выяснилась следующая вещь: если отключить отладочную плату (которая все это время честно слала данные с джойстиков в бесконечном цикле), «эффект мыши» пропадал.

Значит, дело было или в данных, которые отсылала плата или в конкретной винде или в сочетании всего этого с переходником USB-COM. Эти предположения я проверил, подключив плату к другому компу через переходник (эффект сохранился) и без переходника в аппаратный COM-порт (эффект сохранился). Следовательно, дело было в самой плате.
При подключении платы к другому компу появилось всплывающее сообщение «Обнаружено новое устройство — Microsoft Ballpoint Trackball»!

M for Mice

Но как же такое возможно?!

Погуглив название трекбола я узнал лишь, что подобная проблема возникала с древними 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]:

  1. Echo DTR to DSR, always, in hardware. On Power-up (from DTR=1 and TXD=Mark)
  2. If RTS=0, wait (forever) for RTS=1
  3. If RTS=1, go send COM ID (e.g. Table 3)
  4. Go be a mouse
  5. If RTS=0, go back to state 2

Скрытый текст
Здесь указано ожидание единицы на пине RTS, а не DTR, но разбираться, где правда, мне было не очень интересно, ведь эти пины я все равно не мог отслеживать. Зато теперь я примерно понимаю, как работает Plug and Play – два пина в порту просто закорачиваются, когда к ним подключается устройство. Это вызывает запрос прерывания, устройство опрашивается и пошло-поехало.

К сожалению, в представленном там списке идентификатора трекбола не было. Если в 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

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js