Grabduck

Клавиатура своими руками под свои руки

:


Для меня всё началось вот с этого топика о механических клавиатурах, желания научиться печатать быстрее (к своему стыду до недавних пор печатал двумя пальцами, несмотря на 25летний стаж программирования), и появившихся недавно неприятных ощущений в кистях после рабочего дня (да и вообще времени, проведённого за компьютером).
Провёл короткое исследование о том, что вообще есть из клавиатур для программистов и тех, кто много печатает, и написал вот этот обзорный топик. Купил себе Happy Hacking Lite (минималистичная клавиатура с мембранными переключателями), но печатать на ней оказалось ещё менее удобно, чем на ноутбучной, в основном из за ещё более компактного расположения клавиш.
Вскоре появился топик, в котором автор рассказывал о новоприобитённой им Truly Ergonomic, пожалуй, практически идеальной клавиатуры с моей точки зрения, но в комментариях автор же отметил, что спустя два месяца использования так и не смог совсем пересесть на неё, а положение рук совсем не так и удобно, как рекламируется.
Чуть позже появился топик о Kinesis Advantage, по отзыву автора к которой тоже не так легко привыкнуть, а цена так и просто пугает.

Изыскания

По ссылке из первого топика я вышел на сайты фанатов любителей клавиатур, американский и европейский, на которых люди доделывали, переделывали и делали клавиатуры с нуля. Я заштудировал их, узнал всё (о многом я расскажу в этом топике), что нужно для сборки клавиатуры с нуля, заказал все нужные детали и инструменты, и стал ждать.
Список необходимого — ниже, а пока для затравки расскажу о двух наиболее интересных проектах эргономических клавиатур, которые я нашёл, и которые и вбили в мою голову мысль «Я тоже могу».

BlueCube

Клавиатура от домашнего умельца, работающего в компании, занимающейся изготовлением изделий из пластика.

ErgoDox

Клавиатура, прошедшая эволюцию от Key64, через DoxKB, и дошедшая до стадии массового производства (правда, со самостоятельной сборкой из деталей) по цене в $200.

Проектирование

Обе клавиатуры из предыдущего раздела отличаются от типичных клавиатур тем, что в них заметно меньше клавиш. Это сделано за счёт отказа от блока цифровых клавиш, блока стрелок и блока функциональных клавиш. Стоит задуматься, так ли уж они часто используются, и нельзя ли вместо F1 нажимать Fn-1 или какое-то другое сочетание клавиш.
Клавиши на BlueCube, Ergodox расположены ровными колонками, а не ровными рядами. Такое же расположение клавиш используется и в Truly Ergonomic, TypeMatrix и ещё нескольких клавиатурах. Сделано это по двум причинам, во-первых — разница в длине пальцев, средний типично длиннее остальных, а мизинец — короче, и им всем не слишком удобно лежать на одном ряду. Во-вторых, при использовании метода быстрой десятипальцевой печати, если положить указательные на клавиши с засечками, F и J, а остальные на клавиши по бокам от них, попробуйте достать безымянным пальцем левой руки до клавиши X, не двигая остальных пальцев, или средним левой руки до клавиши C. Расположение клавиш ровными колонками решает обе эти проблемы.
Ещё один важный момент, используемый в BlueCube, это совмещение клавиш модификаторов с такими клавишами, как пробел, Tab и т.п. следующим образом: если клавиша нажата одиночно, то результирующий символ — пробел, а если клавиша зажата, а в этот момент нажата, а потом отпущена, другая клавиша, например 'q', то первая клавиша интерпретируется как модификатор, например, Shift, и результирующим символом получается Q.
Стоит также заметить, что большой палец, который практически не используется при печати на обычной клавиатуре, тут задействован больше, аналогично клавиатурам от Maltron и Kinesis. Однако, блок клавиш под большие пальцы не кажется таким уж удобным, что на ErgoDox (и аналогичный на Maltron и Kinesis Advantage), так и на BlueCube, где их приходится загибать аж до основания мизинца.
Поскольку ничто не сдерживало меня в том, как будет выглядеть клавиатура, я решил просто положить руки на бумагу, и понять, куда мне бы было удобно доставать пальцами, не слишком сильно перемещая кисти рук. В итоге у меня получился вот такой набросок:

который я тут же перенёс в векторный редактор, но уже с клавишами, на которые нанёс и символы, расположив их более-менее традиционно:

Итого у меня получилось, что пальцы достают до 50 клавиш, и вроде бы как все нужные клавиши на местах (хотя и не всегда на своих, если смотреть с точки зрения традиционных раскладок). Достаточно ли будет 50ти клавиш, учитывая то, что на традиционных клавиатурах 104 клавиши (в крайнем случае 87, в варианте без блока цифровых клавиш)? С использованием модификатора Fn это становится возможным, ведь сочетания букв и Fn обычно не используются, и можно на них повесить всё, что не влезло, но по каким-то причинам нужно, например, стрелки, Home, End, PgUp и PgDn.
Коричневые квадраты под запястьями обозначают мягкие подкладки.
Индикаторы? Я не пользуюсь Caps Lock'ом, отдельного цифрового блока нет, так что не нужен и NumLock, а для чего нужен ScrollLock многие, наверняка даже и не знают. Так что отметаю индикаторы.
Слегка смущает блок клавиш под мизинец, если вдруг кто-то другой решит воспользоваться моей клавиатурой, его мизинец сможет оказаться короче моего семиоктавного, поэтому блок под мизинец я решил сделать поворачивающимся, от такого, как на рисунке, до параллельного другим колонкам клавиш.
Ещё один аспект — это естественное положение рук, которое скорее ближе к вертикальному расположению ладоней, нежели к положению ладонями вниз, к которому мы все привыкли. Это одна из причин, по которым я не купил клавиатуру Truly Ergonomic и не успокоился. Это склоняет к тому, чтобы сделать клавиатуру разделённой на две половинки, как Kinesis Freestyle и Ergodox с BlueCube'ом, и наклонённой.

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

Детали

Краткий список того, что нужно:

— клавишные переключатели;
— колпачки на переключатели;
— диоды (зачем они нужны — чуть позже);
— микроконтроллер, позволяющий клавиатуре быть USB устройством, и считывающий состояние переключателей;

Плата

Поскольку шанс на безошибочное проектирование печатной платы с первой попытки для себя оцениваю, как крайне низкий, с ЛУТом и фоторезистом опыта не имею, цену на производство под заказ в единичном объёме — высокой, то решил сделать навесную проводку. Материал — текстолит, использующийся в обычных печатных платах. У друга, по счастью, залежались несколько пластин стеклотекстолита, которые я и одолжил. Шикарный материал, по удобству использования сравнимый только с синей изолентой.

Микроконтроллер

Несмотря на свой юношеский задор по поводу светлого будущего ARM-контроллеров, я тем не менее выбрал устройство на базе более традиционного AVR. Из наиболее подходящих для данного применения был выявлен Atmega32u4, с 32КБ ПЗУ, достаточным количеством портов ввода-вывода, работающий в режиме ведомого USB устройства, а также умеющий общаться по I 2C. То, что клавиатура будет без традиционной печатной платы, привело к необходимости взять контроллер на выносной плате, и я выбрал наиболее компактный — Teensy. Вариантов достаточно много, даже от того же производителя, и даже есть отечественные аналоги, хотя они почему-то дороже.

Переключатели

Выбор клавишных переключателей заключался лишь между механическими от Cherry, марки известной многим по клавиатурам, и многочисленными китайскими клонами ныне не выпускающихся переключателей Alps.
Cherry выпускает две основные линейки переключателей, низкопрофильные Cherry ML, и более часто используемые MX, выпускаемые в следующих основных модификациях:
— чёрные (линейной силы нажатия, средней упругости, не щёлкающие);
— красные (линеные, лёгкие, не щёлкающие), популярны в игровых клавиатурах;
— серые (тактильные, упругие, не щёлкающие);
— прозрачные (тактильные, средней упругости, не щёлкающие);
— зелёные (тактильные, средней упругости, щёлкающие);
— голубые (тактильные, лёгкие, щёлкающие);
— коричневые (тактильные, лёгкие, щёлкающие);
— белые (тактильные, средней упругости, умеренно щёлкающие).

Исходя из личных предпочтений, мне понравились прозрачные.
Отличаются переключатели ещё способом установки. Есть вариант для монтажа на печатную плату, и для монтажа на дополнительную пластину. У первых есть два дополнительных столбика для большей устойчивости, а вторым для устойчивости нужна дополнительная пластина. В эту дополнительную пластину можно защёлкивать и первый вариант.
Существует ещё множество модификаций MX, но это одна из тех деталей, которые приобрести не так просто, либо не так дёшево, тем более в малых количествах, а в не слишком популярных модификациях — и подавно. На Ebay их продают чуть не по 4 доллара за штуку, у поставщиков электронных компонент выбор совсем мал. Замечательно то, что на клавиатурных форумах организовывают групповые покупки, в одну из которых я и вписался. К сожалению, нужно было либо ждать, либо брать, то, что есть, и я взял голубенькие (выбирая из красных, коричневых и голубых), в варианте с установкой на печатную плату.

Колпачки

Изначально я хотел купить какую-нибудь старую механическую клавиатуру, выпаять из неё переключатели и снять колпачки, но так ничего путного и не нашёл.
Раз уж покупать новые — так выбирать. Хороший выбор у WASD Keyboards, я решил попробовать взять разные клавиши, и взял набор из 39 клавиш алфавитного блока, и два набора цветных на пробу, к которым в комплекте идут также клавишные демпферы для уменьшения щёлкающего эффекта (щёлкающего эффекта колпачков по плате, а не внутреннего щёлкающего эффекта переключателя). Также нужны клавиши увеличенного размера, и я взял несколько клавиш, обычно использующихся как Tab (1.5x), Alt (1.25x) и Caps Lock (1.75x), разных цветов.
Сейчас в основном используются колбачки цилиндрической формы, у которых каждый ряд клавиш имеет свой профиль.

У меня в наборах не оказалось ни одной клавиши из цифрового ряда E:

Диоды

Диоды, а именно 50 шт. 1N4148 в корпусе DO-35, которые оказалось проще и дешевле купить с доставкой из Англии, чем в местных супермаркетах, торгующих электронными компонентами.

«Лего для взрослых»

Детали и инструменты.

Макет

Железо

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

Рука (левая, я решил поступить по принципу примерки обуви) лежит удобно, достаёт до всех клавиш. Решено, продолжаю.

Микроконтроллер

Teensy идёт с зашитой прошивкой под названием «мигающая лампочка». Удивительно, но при подключении его USB шнуром к компьютеру, маленький диодик действительно начинает моргать. На сайте есть ещё одна прошика для примера, называющаяся «быстро миграющая лампочка», я скачал её и залил на устройство, и лампочка действительно стала мигать быстрее. Залил изначальную — опять медленее. Ну что ж, вполне неплохо, стробоскоп уже можно делать.
Небольшие копошения с исходниками примера с сайта под названием «USB клавиатура», и каждые 8 секунд на экране печатается символ пробела, а при замыкании GND с любым из входов на экране появляются два символа, обозначающи порт и номер ноги, например B1. Ток мизерный — 135мкА. Ещё удача.

Теория

Как же подключить 50 (а на обычных клавиатурах вдвое с лишним больше) клавиш к микроконтроллеру с 25 портами ввода-вывода, оставив несколько на такие вещи, как мышь?
Всё достаточно просто, нужно распределить клавиши по рядам и колонкам, сформировав матрицу. Подавая напряжение на колонку A, и сняв это напряжение с рядов 2 и 3, можно понять, что сейчас клавиши на пересечении этих рядов и этой колонки находятся в нажатом состоянии. Напряжение подаётся на колонки поочерёдно. Таким образом, грубо говоря, не нужно количество портов ввода-вывода равное количеству клавиш, а достаточно квадратного корня из этого количества, в моём случае — 8x7, то есть 8 выходов и 7 входов (или наоборот).

Однако, у этого метода есть один серьёзный недостаток, проявляющийся в блокировании и пропадании нажатий:

На этом рисунке видно, что при нажатых клавишах W, E и D, и при напряжении, поданном на колонку B, происходит ложное срабатывание клавиши S. Бороться с этим явлением и призваны диоды:

Более подробно об этом можно прочитать тут.

Вдобавок к этому, стандарт USB привносит серьёзное ограничение для подключаемых к нему устройств печатного ввода, в 6 одновременно нажатых (не-модифицирующих) клавиш. Некоторые производители обходят это ограничение, эмулируя подключение нескольких USB устройств. Честно говоря, сталкивался с этим ограничением только по юности, когда денег на MIDI клавиатуру не было, а хотелось сыграть жирный аккорд. Но так, на заметку.

Небольшая пауза на раздумье



Мне в разные моменты начинало казаться, что клавиш слишком много (да-да, как бы это смешно ни звучало при всего 50ти клавишах!). И когда я заказал всего 50 переключателей без всякого запаса, и когда пытался дотянуться мизинцами до крайних четырёх клавиш на макете, и когда начал думать о том, что седьмая колонка клавиш поведёт за собой необходимость тянуть между половинками клавиатуры дополнительный провод вдобавок к шлейфу. Вдобавок ко всему на этих изначально планируемых клавишах должны были расположиться Escape, Tab, Backspace и Enter, которые уже есть на клавишах под большие пальцы. Дублировать особого смысла нет. И если уже привыкать к новой клавиатуре, так сразу, без путей отступления.

Схема

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

Колонки и строки в матрице соответствуют клавишам, клавиши блока большого пальца — четвёртый ряд.
Подаём напряжение с колонки на все входящие ноги переключателей этой колонки. С выходящей ножки переключателя — на анод диода. Выходы с катодов всех переключателей в строке соединяем, и подключаем к входу строки.
Для правой половины клавиатуры требуется 6 выходов и 4 входа, на левой — ещё 6 выходов, а входы можно использовать те же. Получается матрица 12x4, итого аж 18 входов-выходов, что несколько неэкономично. Буду использовать матрицу 6 на 8, которую легко представить, как одну половину клавиатуры, расположенную не слева, а снизу от другой, итого получается 14 портов ввода-вывода.
Получается, что в левую половину клавиатуры нужно будет вести 10 жил. Купленные мной штырьковые разъёмы всего на 10 жил, и их ровно-ровно хватает.

Ножки на контроллере могут быть в двух режимах: вход или выход. Вход может быть открытым, в этом случае, если он не замкнут, он может шуметь, и лучше перевести его в режим с включённым встроенным подтягивающим резистором, чтобы в случае, если он не замкнут на землю, он всегда был в состоянии 1. Выходы ставим по умолчанию с состояние 5В, а во время чтения конкретной колонки опускаем до 0В, чтобы при его замыкании со входом последний переходил в состояние 0, означающее нажатие кнопки. Такой вариант подключения приводит к некому недоразумению в виде того, что ток «течёт» от входов в к выходам.
Существуют и другие варианты подключения, где всё более-менее логично, но они предусматривают дополнительные внешние элементы, такие как один на все выходы или несколько внешних резисторов (для экономии портов с чтением через АЦП), но мне кажется, что если есть контроллер с достаточным количеством портов, он и сам должен справляться с тем, как у него какую ножку куда замкнули, поэтому — самый простой вариант, хотя на первый взгляд и не самый понятный.
Ножки микроконтроллера на Teensy выведены не все, а те, что выведены — идут вразнобой. Из «цельных» портов есть только B. Частично выведенные — F (нет ножек F2, F3), D (две ножки на нижнем торце платы, не имеют собственно ножек, а ещё два совмещены с I 2C, C (есть только ножки C6 и C7).

На микроконтроллере используем ножки PF0, PF1, PC6, PC7, PF4-PF7 используем под выходы, PB0-PB5 под входы (с подтягивающим резистором). Оставляем ножки PD0 и PD1 под I 2C, который пригодится позже для подсоединения мыши/трекпоинта. Ножку PD6 не трогаем, потому что она соединена с размещённым на плате светодиодом, который может питаться током, поданным на эту ножку, и опускать напряжение с 5В до ~1.7В.

Железо

Плата

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

На текстолите остаются занятные узоры, позволяющие понять, как именно фольга снималась.

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

Сверление

Я сделал набросок того, как переключатели должны располагаться на плате (платах, ибо получается как минимум 4 куска, а для лучшей компоновки на текстолите аж 6 кусков). Для того, чтобы они на ней закрепились, плату нужно просверлить. Для каждого переключателя это 7 отверстий, два по 0.8мм, два по 1.2мм, два по 1.6мм, и одно, центральное 4мм.
Сверлить сразу по распечатке не слишком удобно, поэтому из старой алюминиевой задней крышки мобильника я сделал трафарет:

Резать и сверлить алюминий дело непростое, он греется, летит во все стороны, поэтому рекомендую использовать специальные защитные очки. Как и во время проведения других операций.
Вот как выглядит переключатель, вставленный в трафарет.

Увы, кулачковый патрон дрели предназначен для свёрел c хвостиком максимального диаметра 3.4мм, но сверло на 3.5мм вполне устойчиво закрепилось. Для высверливания им отверстия 4мм пришлось совершить несколько круговых движений. Не рекоммендую повторять такое.
Сверлил изначально на деревянной подложке, и где-то на сотом отверстии сверло 0.8мм сломалось под корень, из за того, что текстолит сдвинулся относительно подложки в тот момент, когда сверло было опущено. Позже сверлил так, что под тектолитом ничего не было.

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

Получилось вот что:


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

Несмотря на явную неровность линий отверстий с шагом 2.54мм, микроконтроллер и IDC разъём встали без усилий и плотно:


Опять диоды

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



Нарезка

Плат нужно аж целых 6 кусков. Теоретически можно было сделать и два, но 1) размер тектолитовых кусков не позволяет 2) даже если бы позволял, то получалось бы это не слишком экономично 3) есть возможность при сборке поиграть положением плат относительно друг друга.

Межплатные соединения

Для того, чтобы не пришлось тянуть провода между половинками клавиатуры, я использовал шлейфы, подобные тем, которые использовались для подключения накопителей на жёстких дисках к материнском платам, только с меньшим количеством жил, и разъёмы IDC/BH. Конечно, более правильно было бы использовать витую пару, но мне нужно больше, чем 8 проводов, а разъём 8P8C тоже не слишком компактен (а разьём 10P10C и 10жильную витую пару не так просто найти). Ещё вариант был бы использовать мультиплексор портов ввода вывода на шине I 2C, но это усовершенствование я решил отложить.
К слову сказать, IDC разъёмы угловой и прямой мне достались совершенно разные. Прямой не паялся никак, и ножки из него вываливались при малейшем нагревании, а угловой паялся хорошо. Внешне не отличались ничем.

Пайка

О пайке могу сказать одно — это не мой конёк. Тяп-ляп, но работает.

Прошивка

Проба пера

Итак, у меня получился кусок клавиатуры, и пора бы проверить, работает ли всё это так, как я предполагал.

К Teensy идёт некоторое количество примеров, в том числе USB HID (устройство интерфейса с человеком), в вариантах для клавиатуры и мыши. С первого из этих примеров и выросла прошивка.
В оригинальном примере идёт расчёт на то, что входы переключателями замыкаются на землю, а при матричном подключении всё немного по-другому, на часть ножек периодически подаётся напряжение, и в этот период состояние других ножек читается. Для того, чтобы ножки работали в качестве выходов, нужно их в этот режим перевести.
В общем приближении порядок такой: инициализируем порты и USB; в цикле поочерёдно подаём напряжение на выходы, читаем входы, если значение на входе соответствует нажатой клавише, а в предыдущем цикле чтения она не была нажата — посылаем код клавиши по USB.
Я не большой специалист в C, а уж когда речь доходит до копания в макросах, я слегка теряюсь, поэтому код может быть слегка неуклюж, но общий смысл должен доносить:
Да простит читатель мою привычку делать комментарии на английском.

Код прошивки
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "usb_keyboard.h"

#define CPU_PRESCALE(n)	(CLKPR = 0x80, CLKPR = (n))

uint8_t matrix[8][8]= {
/*B0*/  {KEY_TILDE, KEY_Q, 0, 0, KEY_W, KEY_E, KEY_R, KEY_T}, // Top row
/*B1*/  {KEY_SLASH, KEY_A, 0, 0, KEY_S, KEY_D, KEY_F, KEY_G}, // Home row
/*B2*/  {KEY_LEFT_BRACE, KEY_Z, 0, 0, KEY_X, KEY_C, KEY_V, KEY_B}, // Bottom row
/*B3*/  {0 /*Fn*/, KEY_ENTER, 0, 0,  KEY_TAB, KEY_ESC, KEY_SPACE},

/*B6*/  {KEY_Y, KEY_U, 0, 0, KEY_I, KEY_O, KEY_P, KEY_EQUAL}, // Top row
/*B5*/  {KEY_H, KEY_J, 0, 0, KEY_K, KEY_L, KEY_SEMICOLON, KEY_QUOTE}, // Home row
/*B4*/  {KEY_N, KEY_M, 0, 0, KEY_COMMA, KEY_PERIOD, KEY_SLASH, KEY_RIGHT_BRACE}, // Bottom row
/*D7*/  {KEY_SPACE, KEY_ESC, 0, 0,  KEY_TAB, KEY_ENTER, 0 /*Fn*/}
};

int main(void)
{
  uint8_t state=0, prev_state[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

  setup_pins_and_usb();

  while (1) {
    // B0
    PORTB = 0b11111110;
    _delay_ms(5);
    state = PINF;
    read_row(state, prev_state[0], matrix[0]);
    prev_state[0] = state;
    // B1
    PORTB = 0b11111101;
    _delay_ms(5);
    state = PINF;
    read_row(state, prev_state[1], matrix[1]);
    prev_state[1] = state;
    // B2
    PORTB = 0b11111011;
    _delay_ms(5);
    state = PINF;
    read_row(state, prev_state[2], matrix[2]);
    prev_state[2] = state;
    // B3
    PORTB = 0b11110111;
    _delay_ms(5);
    state = PINF;
    read_row(state, prev_state[3], matrix[3]);
    prev_state[3] = state;

    // B6
    PORTB = 0b10111111;
    _delay_ms(5);
    state = PINF;
    read_row(state, prev_state[4], matrix[4]);
    prev_state[4] = state;
    // B5
    PORTB = 0b11011111;
    _delay_ms(5);
    state = PINF;
    read_row(state, prev_state[5], matrix[5]);
    prev_state[5] = state;
    // B4
    PORTB = 0b11101111;
    _delay_ms(5);
    state = PINF;
    read_row(state, prev_state[6], matrix[6]);
    prev_state[6] = state;
    PORTB = 0b11111111;
    // D7
    PORTD = 0b00000000;
    _delay_ms(5);
    state = PINF;
    read_row(state, prev_state[7], matrix[7]);
    prev_state[7] = state;
    PORTD = 0b10000000;
  }
}

void read_row(uint8_t state, uint8_t prev_state, uint8_t keys[]) {
  uint8_t mask, i;

  mask = 1;
  for (i=0; i<8; i++) {
    if(((state & mask) >> i) == 0 && ((prev_state & mask) >> i) == 1 ) {
      usb_keyboard_press(keys[i], 0);
    }
    mask = mask << 1;
  }
}

void setup_pins_and_usb() {
  // set for 16 MHz clock
  CPU_PRESCALE(0);

  // Configure PB0-PB6, PD7 as outputs, high state
  DDRB = 0b01111111;
  DDRD = 0b10000000;
  PORTB = 0b01111111;
  PORTD = 0b10000000;

  // Configure PF0, PF1, PF4-PF7 as inputs, pullup enabled
  DDRF = 0x00;
  PORTF = 0xFF;

  // Initialize the USB, and then wait for the host to set configuration.
  // If the Teensy is powered without a PC connected to the USB port,
  // this will wait forever.
  usb_init();
  while (!usb_configured()) /* wait */ ;

  // Wait an extra second for the PC's operating system to load drivers
  // and do whatever it does to actually be ready for input
  _delay_ms(1000);
}

Проверил — работает. Удача.

«Фирмовая» прошивка

Можно продолжать городить огород и тут, но в прошивках кроется не меньше тонкостей, чем в железе. И мне пришла в голову мысль использовать готовую прошивку. Из имеющегося в сети наиболее заслуживающими внимания показались две:
Clavis. Использовалась в одном моде старой терминальной клавиатуры Wyse WY-85. В принципе, выглядит неплохо настраиваемой, но из коробки не хватает кое-каких возможностей, которые есть во второй прошивке.
TMK Keyboard. Очень матёрая прошивка, использующаяся в десятке (если не в десятках) клавиатурных модов и доморощенных клавиатур. Из коробки обладает следующими возможностями:
— многослойность раскладок (можно использовать один слой для дополнительных полезных символов, наподобие типографского дефиса и т.п.);
— эмуляция движений мыши с помощью клавиш;
— умеет посылать коды системных клавиш: Power Down, Sleep, Wake Up, Volume Down/Up, Mute, Next/Prev track, Play, Stop и других;
— передача множества одновременных нажатий (не ограничиваясь лимитом USB на 6 клавиш);
— настраиваема до мельчайших деталей;
— работает с клавиатурными «фокусами», а именно умеет те же физические клавиши использовать для разных назначений в зависимости от типа нажатия;
— консоль отладки прошивки.

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

Раскладка
#define KEYMAP( \
  K00,K01, K02,K03,K04,K05,        K40,K41,K42,K43, K44,K45, \
  K10,K11, K12,K13,K14,K15,        K50,K51,K52,K53, K54,K55, \
  K20,K21, K22,K23,K24,K25,        K60,K61,K62,K63, K64,K65, \
    T30,T31,T32,T33,T34,              T70,T71,T72,T73,T74   \
) { \
  {  KC_##K00, KC_##K01, KC_##K02, KC_##K03, KC_##K04, KC_##K05 },\
  {  KC_##K10, KC_##K11, KC_##K12, KC_##K13, KC_##K14, KC_##K15 },\
  {  KC_##K20, KC_##K21, KC_##K22, KC_##K23, KC_##K24, KC_##K25 },\
  {  KC_0,     KC_##T30, KC_##T31, KC_##T32, KC_##T33, KC_##T34 },\
\
  {  KC_##K40, KC_##K41, KC_##K42, KC_##K43, KC_##K44, KC_##K45 },\
  {  KC_##K50, KC_##K51, KC_##K52, KC_##K53, KC_##K54, KC_##K55 },\
  {  KC_##K60, KC_##K61, KC_##K62, KC_##K63, KC_##K64, KC_##K65 },\
  {  KC_##T70, KC_##T71, KC_##T72, KC_##T73, KC_##T74, KC_0     } \
}

static const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
/** Layer 0: (lowercase char)
 *
 *   ` q w e r t    y u i o p =
 *   \ a s d f g    h j k l ; '
 *   [ z x c v b    n m , . / ]
 */
 KEYMAP( \
    GRV,   Q,   W,   E,   R,   T,      Y,   U,   I,   O,   P, EQL, \
   SLSH,   A,   S,   D,   F,   G,      H,   J,   K,   L,SCLN,QUOT, \
   LBRC,   Z,   X,   C,   V,   B,      N,   M,COMM, DOT,BSLS,RBRC, \
      FN0, FN1, FN2, FN3, FN4,           FN9, FN8, FN7, FN6, FN5   ),

/** Layer 1: (symbols and navigation)
 *
 *     1 2 3 4 5    6 7 8 9 0 -
 *       ↰ ↑ ↳ ⤒    ← ↓ ↑ →  
 *       ← ↓ → ⤓    
 */
  KEYMAP( \
     NO,   1,   2,   3,   4,   5,      6,   7,   8,   9,   0,MINS, \
     NO,  NO,PGUP,  UP,PGDN,HOME,   LEFT,DOWN,  UP,RGHT,SCLN,QUOT, \
     NO,  NO,LEFT,DOWN,RGHT, END,     NO,  NO,  NO,  NO,  NO,  NO, \
      FN0, FN1, FN2, FN3, FN4,         FN9, FN8, FN7, FN6, FN5   )
};

/*
 * Fn action definition
 */
// TODO: use [1] = KEYMAP(...) to prevent from changing index of element?
static const uint16_t PROGMEM fn_actions[] = {
    ACTION_LAYER_TAP_KEY(1, KC_SPC),                 // FN0 = L1 symbols 
    ACTION_MODS_TAP_KEY(MOD_LSFT, KC_SPC),           // FN1 = Shift with tap Space
    ACTION_MODS_TAP_KEY(MOD_LCTL, KC_ESC),           // FN2 = Ctrl with tap Escape
    ACTION_MODS_TAP_KEY(MOD_LALT, KC_TAB),           // FN3 = Alt with tap Tab
    ACTION_MODS_TAP_KEY(MOD_LGUI, KC_DEL),           // FN4 = Meta with tap Del

    ACTION_LAYER_TAP_KEY(1, KC_ENT),                 // FN5 = L1 symbols 
    ACTION_MODS_TAP_KEY(MOD_RSFT, KC_ENT),           // FN6 = Shift with tap Enter
    ACTION_MODS_TAP_KEY(MOD_RCTL, KC_BSPC),          // FN7 = Ctrl with tap Backspace
    ACTION_MODS_TAP_KEY(MOD_RALT, KC_RALT),          // FN8 = Ctrl with tap Backspace
    ACTION_MODS_TAP_KEY(MOD_RGUI, KC_TAB)            // FN9 = Meta with tap Tab
};

Матрица
/* Column pin configuration
 * col: 0   1   2   3   4   5
 * pin: F0  F1  F4  F5  F6  F7
 */
static void  init_cols(void)
{
    DDRF = 0b00000000;
    PORTF = 0b11111111;
}

static matrix_row_t read_cols(void)
{
    return (PINF&(1<<0) ? 0 : (1<<0)) |
           (PINF&(1<<1) ? 0 : (1<<1)) |
           (PINF&(1<<4) ? 0 : (1<<2)) |
           (PINF&(1<<5) ? 0 : (1<<3)) |
           (PINF&(1<<6) ? 0 : (1<<4)) |
           (PINF&(1<<7) ? 0 : (1<<5));
}

/* Row pin configuration
 * row: 0   1   2   3   4   5   6   7
 * pin: B0  B1  B2  B3  B6  B5  B4  D7
 */
static void unselect_rows(void)
{
    // Hi-Z(DDR:0, PORT:0) to unselect
    DDRB = 0b01111111;
    DDRD = 0b10000000;
    PORTB = 0b01111111;
    PORTD = 0b10000000;
}

static void select_row(uint8_t row)
{
    // Output low(DDR:1, PORT:0) to select
    switch (row) {
        case 0: PORTB &= ~(1<<0); break;
        case 1: PORTB &= ~(1<<1); break;
        case 2: PORTB &= ~(1<<2); break;
        case 3: PORTB &= ~(1<<3); break;
        case 4: PORTB &= ~(1<<6); break;
        case 5: PORTB &= ~(1<<5); break;
        case 6: PORTB &= ~(1<<4); break;
        case 7: PORTD &= ~(1<<7); break;
    }
}

Ещё одна пауза на раздумьё

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

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

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

Корпус

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

Проба сгибания

Мне понадобилось: метр нихромовой проволоки, четыре шурупа, доска. Некоторые руководства в интернете говорят о том, что нихромовая проволока растягивается при нагреве, и крайне необходимо использовать растягивающую прожину. По моим скромным подсчётам по данным википедии, увеличение объёма нихрома составляет чуть менее миллиметра при нагреве на 200'C и длине проволоки в 30см.
Перед сгибанием очень важно не забыть снять с оргстекла защитную плёнку.

Попытки нагреть проволоку керамическим элементом паяльника провалились, равно как и универсальным блоком питания на 1А, включённом на 1.5В. Сопротивление участка провода длиной 30см около 1 Ом, и при токе в 1А получается мощность в 1Вт, что по поим расчётам может нагреть проволоку до 150'C за 500 секунд, и только при условии, что тот не будет остывать, а он будет.
Нужно что-то помощнее, и я взял старый блок питания AT, и подключил к концам провода землю и 5В. Эффект не заставил себя ждать, провод очень быстро нагрелся до температуры, когда капля воды на нём мнгновенно испарялась.

Проволока всё же растягивается, причём настолько сильно, что провисает на пару миллиметров, что делает линию сгиба неровной, а по бокам слегка лучше прилегает с сгибаемому листу, нагревает его больше, даже слегка оплавляя. Для пробы, конечно, пойдёт, и тех «растяжителей» в виде столярных дюбелей достаточно, но чтобы сделать красиво и ровно, нужно что-то более надёжное.

Конструкция получается крепкая, прогибается очень слабо, ноутбук сидит на ней как влитой.

Упор для запястий

Не нашёл в русском языке аналога английскому wrist rest, поэтому пусть будет «упор», хотя подразумевается, что это та вещь, на которой кисть отдыхает, когда запястья лежат на ней. Мне досталась от купленной жене клавиатурной подставки-манипулятора (достойной отдельного поста), там был упор для клавиатуры, и для мыши. Последний не пользовался у жены спросом и поэтому я не преминул его использовать в своих целях.

Поскольку упор один, а половины у клавиатуры две, его придётся распилить пополам. А ещё лучше удалить все царапающие гладкие поверхности скобы.

И удобно подсоединить к конструкции.

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

Чтобы конструкция не скользила по столу, я купил специальные клеящиеся ножки.

Прорезание

Для того, чтобы переключатели встали на оргстекло, неоходимо сделать в нём прорези под них, и защёлкнуть. Прорези делаются на фирменном оборудовании, но делается это быстро и стоит не дорого.

В первой версии корпуса из оргстекла я сделал отверстия чуть большего размера, чем нужно, примерно на 0.3мм, и переключатели чуть заметно гуляли по горизонтали. Версия два дала мне возможность чуть поправить расположение клавиш под большие пальцы, не использовать гравировку (в месте гравировки материал становится тоньше аж на треть), и собственно уменьшить размер отверстий.
С версией два была следующая проблема. На нижней (вторая версия) переключатели не защёлкиваются (такие маленькие штучки сверху и снизу посередине каждого переключателя). Сначала я подумал, что слишком сильно уменьшил отверстия, но оказалось (!!!), что толщина акрилового листа 1.5мм может достигать 1.8мм, а что переключатели расчитаны на 1.5-1.68мм. Такой подставы я не ожидал, но мне повезло, что я заказал четыре «половинки» сразу, и две из них оказались нормальной толщины, и переключатели защёлкивались в них как нельзя лучше.

Смягчители

Многие помнят, что механические клавиши сильно шумят. Это связяно не только с работой механизма, но и с тем, что после срабатывания переключателя при сильном и быстром нажатии клавиша проходит путь до самого упора, и стукается об этот упор. Для нивелирования этого эффекта существуют специальные резиновые колечки, которые предотвращают касание. Существует их множество видов, в том числе в моём наборе их было 3 разных вида.

Я решил попробовать все, хотя и ограничен в количестве. Чёрные пошли на «домашнюю» строку, красные — наверх, синие — вниз. Цветным клавишам и крайним колонкам колечек не досталось.

Крепление контроллера

У меня были межплатные стойки SP3, и я попробовал просверлить отверстия в оргстекле, но как-то не слишком хорошо они входят в отверстие даже 5мм, а мой лимит на точное сверление — 3.5мм, так что я откусил у них защёлки-крепления, просверлил вдоль оси, и прикрепил контроллер вот таким незатейливым способом, пропустив жилы витой пары кругом.


Держится просто отлично.

Да, конечно пришлось почти всё перепаивать заново, но имея в этом деле уже какой-никакой опыт это уже немного легче и быстрее. Немного поменял назначение входов-выходов, чтобы проще было со шлейфами и IDC разъёмами (PF0, PF1, PF4-PF7 под выходы, PB0-PB3 под входы левой половины, PB4-PB6, PD7 под входы правой половины).

Сгибание

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


Надписи на клавиши

Несмотря на то, что с конкретной раскладкой я ещё не определился, использовать ли QWERTY, Colemak, Workman-P или что ещё, но шрифт для клавиш я уже выбрал, это Neuropol Nova от Typodermic. Выбран из за футуристичности, отличной проработки и возможности сделать трафарет. Ко всему прочему, автор любезно согласился на использование этого коммерческого шрифта в моих целях. Трафарет нарезан там же, где и оргстекло, и выглядит примерно так:

Раскладка

Раскладка на текущий момент:
` q w e r t y u i o p =

\ a s d f g h j k l ; '

[ z x c v b n m , . / ]

Русские буквы находятся на тех же клавишах, что и обычно. Расположение стандартное, только буква Х уехала влево вниз, слева от Я.

Ряд модификаторов (цветные) включает в себя как модифицирующие клавиши, так и обычные. Модифицирующие срабатывают при нажатии в комбинации, а при однократном нажатии — обычная.

Однократное:
Рыж Жлт Зел Роз Сир Сир Роз Зел Жлт Рыж

Spc Spc Esc Tab Del Tab AGr Bsp Ent Ent

Комбинация:
Lr1 Sft Ctl Alt Met Met AGr Ctl Sft Lr1

Второй слой, работает в комбинации с рыжими клавишами. Стрелок, на мой взгляд, их даже перебор. Два варианта, для фанатов Vim — справа (hjkl), и для фанатов WASD — слева (dxcv на самом деле).
1 2 3 4 5 6 7 8 9 0 -

↰ ↑ ↳ ⤒ ← ↓ ↑ →

← ↓ → ⤓

Итоги

Трудозатраты

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

Финансы

Деталь Цена
Микроконтроллер Teensy 2.0 с ножками 780
Переключатели Cherry MX MX1A-E1JW (голубые) 1110
Колпачки на переключатели 900
Оргстекло 150
Нарезка оргстекла 350

Итого: 3290 руб.

Не стал включать такие мелочи, как припой, провода, шлейф, разьёмы и диоды. Последние мне достались чрезвычайно недорого, несмотря на дорогую доставку, но если размазать по общей сумме заказанного, то получается что-то около 25 копеек за штуку при магазинной цене в 6 руб.
Мне досталась бесплатно подставка под запястья, можно её купить примерно за 20 долларов.
Довольно много потратил на инструменты, но это мне ещё не раз пригодится. Некоторые недорогие из них (пинцет с жароустойчивыми пластиковыми рычажками, которые сразу поплавились при 200'C, лупа, через которую ничего не видно, кусачки, которые не очень кусают), были дешёвыми, но служили очень недолго.

Впечатления

Пока печатал на этой клавиатуре не так много. Наверняка найду что улучшить в раскладке. В целом — удобно, даже очень.
Предвижу комментарии типа «а как же xxxx клавиша?». В моей модификации tmk_keyboard задействовано всего два слоя. Можно добавить ещё хоть 30 штук, и запихнуть туда все возможные клавиши.
На комментарии типа «а как вы будете нажимать сочетания клавиш xxx+yyy+zzz?» могу ответить, что специально выбирал себе программное обеспечение, чтобы не мучить пальцы (Linux, Awesome, Vim, Chromium+Vimium, Zathura). Самое сложное сочетание из тех, которыми я пользуюсь это Ctrl+Meta+[1-9], совмещение нескольких виртуальных рабочих столов на текущий. Сейчас работает нажатием оранжевой, зелёной, сиреневой и клавишей цифры на верхнем ряду, не слишком удобно, но определённо придумаю, как попроще это сделать.

Усовершенствования, которые я хочу сделать в версии 2

Нашёл замечательный расширитель портов ввода-вывода NXP PCF8574AP в корпусе DIP. Можно межплатный шлейф заменить на тонюсенький четырёхжильный провод. Пока не понимаю, стоит ли связываться с I 2C, по слухам протокол очень небыстр, и много времени в работе прошивки тратится именно на него. Как вариант — использовать регистры сдвига 74HC164 и 74HC165.

Есть сильное желание заменить Teensy на голый AVR с количеством вводов и выводов достаточным для одной половины клавиатуры, а вторую половину доверить PCF'ам. Есть ещё вариант сделать на базе микроконтроллера от Microchip, PIC18F25K50, он ещё дешевле, а помимо прочего ему не нужен внешний кварц для работы с USB.

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

Добавить трекпоинт на датчике Холла, желательно между клавишами HJNM, и три кнопки под мышь (возможно, использовать клавиатурные). Пока не понял, где рукам будет удобно их находить.

Попробовать прозрачные или красные (после вот этой заметки) переключатели Cherry MX.

Сделать корпус из фанеры 1.5мм вместо оргстекла. Не так красиво выглядит, но. Фанера легко поддаётся обработке, менее хрупка. При должной обработке достаточно долговечна и стойка к влажности. Её труднее сгибать, но при такой толщине должно быть не так уж и тяжело. Ко всему прочему можно сделать конструкцию из нескольких листов, сделав прорези и штыри, чтобы её можно было собирать-разбирать, по принципу лего. Есть уверенность, что толщина фанеры не гуляет по площади листа на +20% в отличии от оргстекла.

Сделать недорогую, регулируемую по положению и с креплением для обеих половин клавиатуры руку-манипулятор, крепящуюся на плоскую поверхность.

PS
И да, конечно, как я мог забыть!