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




Нет, кнопка не рассыпалась в прах, как пророчествовал один из комментаторов. И вообще никто из присутствующих не угадал судьбы многострадальной мышки, хотя, как я сейчас понимаю, она была чуть ли не очевидной.

По наводке уважаемого ploop я открыл для себя программу xev, которая в числе прочего показывает, какие кнопки нажаты. Нажимаю я правую кнопку и вижу:

О ужас
ButtonPress event, serial 56, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 3959640285, (43,112), root:(1109,578),
    state 0x10, button 1, same_screen YES

ButtonPress event, serial 56, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 3959640285, (43,112), root:(1109,578),
    state 0x110, button 3, same_screen YES

ButtonRelease event, serial 56, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 3959640436, (43,112), root:(1109,578),
    state 0x510, button 3, same_screen YES

ButtonRelease event, serial 56, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 3959640452, (43,112), root:(1109,578),
    state 0x110, button 1, same_screen YES



То есть всякий раз, когда я нажимаю правую кнопку (3), мышь думает, что нажата ещё и левая кнопка (1)! На этом месте я вспомнил, что после переделки этой мышью стало невозможно вызвать контекстное меню в заголовке окна Хрома. Тогда я не придал этому значения, ибо приблизительно в тот же период времени отвалилось и перемещение по истории кнопками «вперёд-назад», причём только на Хабре/ГТ и только у второй мышки, с которой я (мамой клянусь) ничего не делал.

Выпаяв конденсатор, я убедился — действительно, в нажатии лишней кнопки виновен именно он. А это ставило крест на всей идее панацеи от дребезга из предыдущего поста. Поскольку выбирать между хабрасуицидом и сделкой с совестью не хотелось, пришлось думать, как побороть сей пренеприятнейший побочный эффект. Так что снова разбираем мышь, врубаем осциллограф и попытаемся достичь того самого понимания, которого нам так не хватило в прошлый раз.

Мышь оказалась основана на известном в народе микроконтроллере nRF24LE1.

Путём прозвонки было установлено, что все выводы выключателей идут прямиком к ногам процессора, причём каждая такая нога связана более чем с одним выключателем. Если точнее, то схема вырисовывается такая:



(Кнопка со звёздочкой означает кнопку смены dpi, которая на выход мыши не проходит.)

Это позволяет заподозрить авторов в применении приёма под названием «матричная клавиатура». На сканирующие ноги поочерёдно подаётся сигнал и смотрится, на каких считывающих ногах он появился. Это позволяет экономить ноги — ведь кнопок таким образом можно поставить пропорционально квадрату числа используемых ног. (В данном случае у нас 6 кнопок и 5 выводов — то есть экономится целая одна нога. Впрочем, колесо я прозвонить забыл, так что не исключено, что эта же схема обслуживает и колесо, тогда получается экономия ажно двух ног.)

Но пока что это лишь предположение, надо его проверить. Натравим крокодилов Подключим проводники P0 и A (в терминах предыдущей картинки) к осциллографу. При нажатии левой кнопки (1) видим:



На P0 подаётся импульс длительностью 20 микросекунд (отмечен стрелочкой), который по замкнутому выключателю приходит на ногу A. Здесь этого не видно, но промежуток между импульсами составляет около 10-15 миллисекунд. А значит, программная защита от дребезга всё же присутствует, и становится непонятно, как так получается, что она не помогает. Но вернёмся к нашим баранам и отпустим кнопку:



Как мы и ожидали, сканирующий сигнал на считывающей ноге пропадает. А теперь нажмём левую и правую кнопки одновременно:



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

А как же получается, что при двух нажатых кнопках сканирующие ноги не закорачиваются друг с другом и не портят друг другу сигнал? В упомянутой статье для этого предлагается использовать диоды. Здесь же всё проще — когда нога неактивна, она переводится в режим Hi-Z (высокого сопротивления), то есть фактически отключается от цепи, и тока по ней не идёт. Как свидетельство в пользу этого — если при разомкнутых кнопках неосторожно коснуться сканирующего порта, то осциллограф покажет характерную «шерсть» (то есть помехи из радиоэфира, принятые нашим телом):



Чтобы окончательно подтвердить нашу догадку, поставим эксперимент. Если нажатие кнопок регистрируется по повышению напряжения, то если замкнуть сканирующий порт на питание, мышь это должна воспринять как нажатие всех кнопок, находящихся на этом порту.

Замыкаем порт A
ButtonPress event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059940208, (547,509), root:(1297,734),
    state 0x10, button 6, same_screen YES

ButtonRelease event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059940208, (547,509), root:(1297,734),
    state 0x10, button 6, same_screen YES

ButtonPress event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059940208, (547,509), root:(1297,734),
    state 0x10, button 1, same_screen YES

ButtonPress event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059940208, (547,509), root:(1297,734),
    state 0x110, button 3, same_screen YES

ButtonPress event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059940208, (547,509), root:(1297,734),
    state 0x510, button 2, same_screen YES

ButtonRelease event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059940253, (547,509), root:(1297,734),
    state 0x710, button 2, same_screen YES

ButtonRelease event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059940268, (547,509), root:(1297,734),
    state 0x510, button 1, same_screen YES

ButtonRelease event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059940268, (547,509), root:(1297,734),
    state 0x410, button 3, same_screen YES



Замыкаем порт B
ButtonPress event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059977818, (172,409), root:(922,634),
    state 0x10, button 7, same_screen YES

ButtonRelease event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059977818, (172,409), root:(922,634),
    state 0x10, button 7, same_screen YES

ButtonPress event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059977818, (172,409), root:(922,634),
    state 0x10, button 8, same_screen YES

ButtonPress event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059977818, (172,409), root:(922,634),
    state 0x10, button 9, same_screen YES

ButtonRelease event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059977950, (172,409), root:(922,634),
    state 0x10, button 8, same_screen YES

ButtonRelease event, serial 166, synthetic NO, window 0xb200001,
    root 0x274, subw 0x0, time 4059977950, (172,409), root:(922,634),
    state 0x10, button 9, same_screen YES




Снова всё так, как мы предполагали, плюс обнаружились «пасхальные» кнопки 6 и 7, которых физически на мыши нет (колесо соответствует кнопкам 4 и 5).

Итак, мы познали истину о мыши и её кнопках. Но как это нам поможет справиться с нашей изначальной напастью? Для этого нам надо понять ещё две вещи — почему конденсатор задерживает отпускание кнопки и почему он способствует её нажатию при нажатии другой кнопки.



Когда мы замыкаем кнопку 1, конденсатор мгновенно разряжается, и напряжение на нём становится равным нулю. Пока кнопка нажата, конденсатор закорочен, так что мышь воспринимает нажатие кнопки так, как если бы конденсатора не было.

Теперь отпустим кнопку. Поскольку конденсатор обладает ёмкостью, напряжение на нём всё ещё равно нулю. А значит, если подать на P0 сканирующее напряжение (2,5 В питания), то и на входе A также будет 2,5 вольта, что соответствует нажатой кнопке.

Однако с каждым таким импульсом конденсатор понемногу будет заряжаться (через сопротивление R1). И в один прекрасный момент он зарядится, скажем, до 2 вольт, и на входе A будет уже 0,5 вольт, что недостаточно для появления на этом входе единицы. Стало быть, некоторое время после отпускания кнопки мышь будет думать, что кнопка ещё нажата, а затем «поймёт», что её отпустили.

Можно даже приблизительно оценить это время. Наша RC-цепочка состоит из сопротивления в 13 кОм и конденсатора, например, 0,1 мкФ. Перемножаем эти две величины и находим характерное время 1,3 миллисекунды. Но поскольку ток течёт не всё время, а лишь 20 микросекунд каждые 10 миллисекунд, это время растягивается до 0,65 секунды — как мы и намеряли в прошлый раз.

Можно было бы обрадоваться такому точному совпадению расчёта с экспериментом, но надо внести ещё ложку дёгтя. Дело всё в том, что характерное время — это время, за которое напряжение падает в число e, то есть в 2,7 раз. Но даташит на nRF24LE1 говорит нам, что Input high voltage равен 0,7 VDD, а Input low voltage 0,3 VDD. То есть входы нашего процессора работают как триггер Шмитта, и чтобы они восприняли единицу, нам надо поднять напряжение до 0,7 напряжения питания. Почему мы взяли 0,7, а не 0,3, спросите вы? Очень просто — поскольку основную часть времени на входе A чистый ноль, то в момент импульса нам надо поднять напряжение до 0,7 питания, иначе триггер Шмитта не переключится на единицу. Так что расчёт даёт время

ln(0,7) / ln(1/e) * 0,65 = 0,23 секунды.

А фактически у нас 0,6 секунды! Если вычесть время, когда кнопка замкнута — это 0,5 секунды, что всё равно много. Чтобы это объяснить, можно предположить, что в режиме Hi-Z сопротивление ноги P0 всё же не бесконечно, и «втихаря» подразряжает конденсатор в промежутке между измерительными импульсами. Очень грубо из наших данных можно оценить его величину — поскольку оно за 10 мс разряжает конденсатор на величину, сопоставимую с той, на которую он заряжается за 20 мкс, это сопротивление более 6,5 мегаом.

И здесь надо вспомнить ещё один факт, которому я не придал значения. А именно, если напаять конденсатор меньший, чем 2 нФ, то мышь будет думать, что кнопка нажата всегда. И теперь сей факт получает объяснение — за 10 миллисекунд конденсатор успевает разрядиться (2 нФ * 6,5 МОм = 13 мс), так что при импульсе триггер Шмитта срабатывает, а пока этот импульс идёт, конденсатор заряжается (2 нФ * 13 кОм = 26 мкс), но не успевает зарядиться до такой степени, чтобы преодолеть порог в 0,3 напряжения питания.

Теперь посмотрим, что будет, когда мы пожмём не левую, а правую кнопку.

В состоянии покоя на конденсаторе у нас 2,5 вольта. Замкнём выключатель 3, и на проводник A пойдут 20-микросекундные импульсы с порта P2. Но если на A 2,5 вольта плюс на конденсаторе 2,5 вольта, то на ноге P0 должно быть уже 5 вольт! А контроллер рассчитан не более чем на 3,6 вольт. Специально для таких случаев в микросхемах предусматривают защитные диоды, чтобы напряжение на входах не превышало напряжения питания:



Стало быть, как только на P2 появится напряжение питания, конденсатор разрядится через этот диод, и на нём будет уже 0,7 вольта, а то и меньше. А затем он ещё дополнительно разрядится через 6,5 мегаом. А когда настанет пора измеряющего импульса на ноге P0, напряжение на конденсаторе будет настолько мало, что на входе A будет почти полное напряжение питания и как следствие чёткая единица. Вот мы и получили нажатие левой кнопки при нажатии правой.

Теперь, наконец, мы ответили на все вопросы из разряда «кто виноват», осталась только самая малость — что делать? Поскольку корень нашей неприятности в разрядке конденсатора, поставим на пути этого тока преграду в виде диода:



Я нашёл первый попавшийся диод, припаял — и действительно, несанкционированные нажатия кнопки более не происходят. Вот только и задержка после отпускания левой кнопки исчезла. Как же так? А очень просто — как мы уже знаем, для срабатывания на входе A должно быть не менее 0,7 напряжения питания, то есть на всей связке «конденсатор+диод» должно быть не более 0,75 вольта. А на диоде, как известно, падает около 0,7 вольта, плюс ещё конденсатор — вот и не хватает напряжения.

Нам поможет диод Шоттки, прямое падение напряжение на котором заметно меньше, чем на обычном диоде.



К сожалению, я не смог найти у себя диодов Шоттки, поэтому я нашёл диод с наименьшим падением напряжения (мультиметр показывал 0,44 В) и напаял его чисто чтобы убедиться, что предложенное решение работает. Можете его поискать на КДПВ (подсказка — он чёрно-розовый). Пришлось также поднять напряжение питания до 3,3 В, но всё же желаемый эффект был достигнут! Задержка отпускания кнопки — целых 0,4 секунды, при этом ни одна кнопка не нажимается «за компанию». Правда, по очевидным причинам сию конструкцию пришлось разобрать, но главный вывод был сделан — диоды Шоттки спасут отца русской демократии.

Вот, собственно, и сказке конец.

УПД: Оказывается, и это не конец. В каментах справедливо указывают, что приведённая схема с диодом работать не может, ибо конденсатор не сможет разрядиться при замыкании кнопки. А работала она, видимо, только потому, что оный конденсатор разряжался через сопротивление щупа осциллографа, равное одному мегаому. Истинно правильной на сегодняшний день объявляется следующая схема:



То есть надо разрезать дорожку на плате, ведущую от выключателя к «сканирующей» ноге и впаивать диод в полученный разрез. Ну или не обязательно к сканирующей, главное — чтобы ток не мог течь к нашей сканирующей ноге ни от одной другой сканирующей ноги.

Наверное, писать третью статью по этой теме — это уже чересчур, так что ежели я соберусь эту схему экспериментально проверить, то здесь же допишу.