Есть люди, которые спокойно меняют клавиатуры и через пару дней привыкают к новым. А есть моя жена. У неё есть ноутбук MSI GS75, и его клавиатура ей очень нравится. Настолько, что ни одна из других клавиатур, которые мы пробовали, не смогла её заменить.
Я перепробовал немало вариантов. Одни отличались ходом клавиш, другие — усилием нажатия, расположением кнопок или просто тактильными ощущениями. Казалось бы, выбор современных клавиатур огромен, но подходящей так и не нашлось. В какой-то момент возникла мысль: а зачем вообще искать замену, если нужная клавиатура уже существует? Осталось только научить её работать отдельно от ноутбука.
Так начался небольшой домашний проект, который постепенно превратился в исследование клавиатурной матрицы, разработку двух ревизий печатной платы на STM32, написание собственной USB HID-прошивки и борьбу с фантомными нажатиями. Сейчас у меня уже есть работающий прототип: 102 клавиши, USB Type-C, поддержка Fn и мультимедийных сочетаний, режим NKRO и собственная система диагностики.
В этой статье расскажу, как всё это разрабатывалось, какие проблемы возникали и почему самым сложным оказалось не изготовить плату, а заставить клавиатуру правильно обрабатывать нажатия.
С чего начать, если у клавиатуры нет USB?
Первая проблема заключается в том, что клавиатура ноутбука — это не самостоятельное USB-устройство. Обычная внешняя клавиатура уже содержит контроллер, который считывает нажатия и передаёт их компьютеру. А у ноутбучной клавиатуры эту работу, как правило, выполняет электроника самого ноутбука. От клавиатуры наружу выходит гибкий шлейф, внутри которого находятся проводники, соединённые с контактами клавиш.

У моей клавиатуры для основных клавиш используется 26-контактный шлейф. Сразу возникает вопрос: как через 26 контактов подключены 102 клавиши? Ответ — матрица. Вместо отдельного провода для каждой кнопки клавиши соединяются в систему строк и столбцов. Каждая кнопка замыкает два определённых проводника. Контроллер последовательно проверяет состояние этих линий и по замкнутой паре определяет, какая клавиша нажата.

Но здесь скрывается главная проблема. Распиновки конкретной клавиатуры у меня не было. Было неизвестно, какой контакт шлейфа соответствует какой строке или столбцу, сколько их вообще и какие пары проводников замыкаются при нажатии каждой клавиши.
Можно было вооружиться мультиметром и последовательно прозванивать контакты, но проверять таким образом более сотни клавиш и затем множество комбинаций не хотелось. Поэтому я решил пойти другим путём — сразу создать электронный адаптер, а распиновку определить программно.
Первая плата: 26 неизвестных сигналов
Для управления клавиатурой я выбрал микроконтроллер STM32F411RET6. Это достаточно производительный STM32 с аппаратным USB Full Speed, большим количеством выводов и удобными средствами отладки.
С учётом того, что назначение контактов шлейфа было неизвестно, я решил подключить все 26 линий напрямую к GPIO микроконтроллера. Это позволило бы программно менять направление работы выводов и проверять электрические соединения без переделки платы. Кроме самого STM32 потребовались разъём клавиатуры, USB Type-C, питание 3,3 В, кварцевый резонатор, отладочные интерфейсы и вспомогательные компоненты.
Двухслойная плата и немного головоломки

Плату решил сделать двухслойной. На первый взгляд ничего сложного: один микроконтроллер, несколько десятков пассивных компонентов и разъёмы. Но когда дело дошло до трассировки, оказалось, что самое неудобное место — соединение 26 контактов шлейфа с выводами STM32.
Из клавиатурного разъёма выходит плотный пучок дорожек, которые нужно провести к разным сторонам микроконтроллера. При этом не хотелось превращать плату в коллекцию переходных отверстий и длинных обходных маршрутов. В какой-то момент стало понятно, что удобнее менять не трассировку, а саму принципиальную схему. Я неоднократно перераспределял назначение GPIO STM32, подбирая такое расположение сигналов, при котором дорожки удавалось провести наиболее рационально.
Это одно из преимуществ микроконтроллеров с гибко настраиваемыми выводами. Если интерфейс не требует строго определённых аппаратных функций, иногда проще переназначить сигнал, чем пытаться провести его через половину платы. Правда, при этом нужно не забыть согласовать новую схему с конфигурацией микроконтроллера и прошивкой.

Первый опыт пайки настолько мелких компонентов

К моменту сборки платы выяснилось, что впереди ещё одна интересная задача. Это был мой первый опыт ручного монтажа компонентов настолько маленьких типоразмеров. На экране EasyEDA всё выглядит достаточно крупным и удобным. В действительности же некоторые элементы настолько малы, что работать с ними обычным паяльником без увеличения оказалось бы крайне неудобно.
Для сборки я приобрёл тринокулярный микроскоп. И это, пожалуй, была одна из самых полезных покупок для моей домашней мастерской. Под увеличением стало значительно проще контролировать положение компонентов, качество пайки и наличие возможных перемычек между выводами.
После завершения сборки я приступил к программированию. На первой ревизии использовал ST-Link через интерфейс SWD. Это было удобно для первоначальной настройки микроконтроллера и отладки прошивки.
Как я восстанавливал неизвестную распиновку
Здесь начинается, пожалуй, самая интересная часть исследования. Изначально мы знаем только одно: клавиша при нажатии соединяет какие-то два контакта шлейфа. Но какие именно? Я написал диагностическую прошивку, которая последовательно проверяла соединения между всеми 26 линиями.
Принцип был следующим:
Один вывод STM32 переводился в режим выхода с открытым стоком и устанавливался в низкий уровень.
Остальные 25 выводов работали как входы с подтяжкой к питанию.
Микроконтроллер считывал их состояние и фиксировал линии, на которых появился низкий уровень.
Затем активным становился следующий вывод, и процедура повторялась.
Таким способом получался полный направленный перебор соединений между 26 контактами. Это было важно на первом этапе: я не предполагал заранее, какие контакты являются строками, а какие — столбцами.
102 клавиши без ручной прозвонки: автоматизируем измерения
Чтобы не заниматься ручной прозвонкой более сотни клавиш, я написал Python-утилиту gs75_matrix_mapper.py, работающую совместно с диагностической прошивкой STM32. Сначала программа проверяла исходное состояние матрицы, убеждаясь в отсутствии замкнутых контактов. Затем последовательно предлагала нажимать клавиши, автоматически определяла замкнувшуюся пару контактов шлейфа и проверяла повторяемость результата.
Например, для клавиши Esc было обнаружено соединение FPC21–FPC26, подтверждённое пятью последовательными измерениями. После отпускания Esc программа автоматически переходила к следующей клавише — F1.
Открываю COM17 @ 115200... UART: PONG получен. Baseline: ничего не нажимайте. baseline 01/10: 0 directed edge(s) ... baseline 10/10: 0 directed edge(s) Baseline выбран: 0 edge(s), 10/10 одинаковых снимков. Нажмите Enter, чтобы начать карту ОДИНОЧНЫХ клавиш... [1/102] Esc ================================================ Нажмите и УДЕРЖИВАЙТЕ: Esc Скрипт сам обнаружит нажатие. Зафиксировано (5/5 одинаковых снимков): FPC21->FPC26 Теперь ОТПУСТИТЕ клавишу(и)... [2/102] F1 ================================================ Нажмите и УДЕРЖИВАЙТЕ: F1
По сути, я превратил процедуру восстановления распиновки в автоматизированный эксперимент: программа задавала действие, контроллер выполнял электрические измерения, а результаты сохранялись для последующего анализа. Для одиночных клавиш выполнялось пять повторных измерений. Все 102 клавиши удалось определить без расхождений, причём замыкания наблюдались в обоих направлениях проверки. После этого я перешёл к сочетаниям из нескольких клавиш, чтобы убедиться, что полученная модель действительно соответствует физическим соединениям.
Что получилось
Исследование показало, что клавиатура организована в матрицу 8 × 18:
8 линий строк — контакты FPC17–FPC24;
18 линий столбцов — FPC1–FPC16, FPC25 и FPC26.
Всего такая матрица допускает 144 координаты. Из них в данной клавиатуре используются 102. Полученную карту я сохранил в JSON и использовал при разработке основной прошивки.

Любопытно, что физическое расположение клавиш практически ничего не говорит об их электрических координатах. Соседние кнопки совсем не обязательно подключаются к соседним линиям. Поэтому строить карту на основании расположения клавиш было бы бесполезно.
На этом этапе казалось, что самая трудная часть позади: все клавиши найдены, матрица известна, остаётся написать обычную прошивку USB-клавиатуры. Но дальнейшие испытания показали, что всё немного сложнее.
Ghosting: когда появляется клавиша, которую никто не нажимал
В матричных клавиатурах без индивидуальных диодов может возникать эффект ghosting — фантомное нажатие. Представим четыре кнопки, расположенные в углах прямоугольника электрической матрицы. Если нажать три из них, электрические соединения иногда оказываются такими, что контроллер обнаруживает замыкание и в четвёртом углу. Хотя физически эту клавишу никто не нажимал.

Во время исследования я провёл отдельные испытания с комбинациями клавиш, формирующими такие прямоугольники. Из 20 специально выбранных сочетаний все 20 продемонстрировали электрическую неоднозначность: по полученным сигналам невозможно было однозначно отличить три реально нажатые клавиши от четырёх. И это принципиальное ограничение аппаратной конструкции клавиатуры.
Если бы я разрабатывал саму клавишную матрицу, можно было бы предусмотреть диод последовательно с каждой клавишей. Но здесь использовалась готовая ноутбучная клавиатура, вмешиваться в конструкцию которой не хотелось. Оставалось решить, как с этим будет работать прошивка.
Почему нельзя просто найти фантомную клавишу
Первоначальная мысль достаточно очевидна: если контроллер видит четыре угла прямоугольника, нужно определить лишний и исключить его. Проблема в том, что электрически все четыре угла могут выглядеть совершенно одинаково. У программы нет достоверной информации, какая клавиша действительно нажата, а какая появилась из-за обходного пути тока.
Поэтому в прошивке я использовал консервативный алгоритм обработки неоднозначных состояний. Он выявляет прямоугольники в полной матрице, сохраняет ранее подтверждённые нажатия, если их электрическое состояние ещё наблюдается, и не принимает новые нажатия, которые невозможно достоверно определить. При исчезновении неоднозначности обычная обработка восстанавливается.
Такой подход имеет ограничения: некоторые реальные новые нажатия могут быть временно подавлены. Но он позволяет избежать появления заведомо недостоверных событий. Причём анализировать приходится все 144 координаты матрицы, включая 42 неиспользуемые. Иначе можно пропустить электрические соединения, влияющие на обнаружение ghosting.
Именно здесь проект перестал быть просто адаптером, который периодически опрашивает контакты. Понадобилась полноценная модель состояния клавиатуры с учётом её аппаратных ограничений.
От электрических контактов к USB HID
Когда матрица стала известна, я переработал диагностическую прошивку в полноценную систему опроса клавиатуры. Вместо первоначального перебора всех 26 линий теперь достаточно последовательно активировать восемь известных строк и считывать восемнадцать столбцов. Один полный цикл выполняется каждую миллисекунду — то есть частота опроса составляет 1000 Гц. Для каждой координаты реализована независимая фильтрация дребезга контактов продолжительностью 5 мс.
Затем программная обработка проходит несколько этапов:
Опрос матрицы → фильтрация дребезга → обнаружение ghosting → формирование логического состояния → обработка Fn → USB HID.

Почему недостаточно просто отправить код клавиши
USB HID — стандартный класс устройств ввода, который поддерживается операционными системами без необходимости писать собственный драйвер. Но контроллер должен не просто сообщать компьютеру, какая кнопка нажата. Ему необходимо правильно обрабатывать несколько одновременных нажатий, отпускания, модификаторы вроде Ctrl/Shift и переключение режимов работы.
В прошивке я реализовал два формата клавиатурных HID-отчётов. Boot Protocol (6KRO) — стандартный формат размером 8 байт, поддерживающий до шести одновременно нажатых обычных клавиш плюс модификаторы.
Report Protocol (NKRO) — расширенный формат размером 22 байта с битовой картой клавиш. Он снимает стандартное ограничение в шесть обычных клавиш в самом HID-отчёте. Конечно, NKRO не устраняет ghosting физической матрицы. Он лишь позволяет передавать все достоверно определённые нажатия, не упираясь в ограничение формата 6KRO.
Как не потерять нажатие во время диагностики
В процессе разработки я активно использовал UART-диагностику. Контроллер умел выводить состояние матрицы, результаты обнаружения ghosting, текущие нажатые клавиши и служебную информацию о работе USB.
Однако здесь возникла ещё одна важная проблема. Вывод подробных данных по UART может занимать заметное время. А клавиатура должна продолжать сканирование независимо от того, чем в данный момент занят основной цикл программы.
Поэтому я разделил задачи. Периодическое сканирование и обновление логического состояния выполняются в обработке системного тика. Изменения состояния клавиш сохраняются в очереди, а формирование USB-отчётов и передача данных происходят отдельно. Таким образом, продолжительный диагностический вывод не останавливает сам опрос матрицы и не приводит автоматически к потере кратковременных переходов состояния.
Для диагностики предусмотрены команды INFO, RAW, DUMP, GHOST, KEYS и USBINFO. Это заметно упростило дальнейшую проверку: можно было отдельно выяснить, видит ли микроконтроллер электрическое нажатие, приняла ли его логика клавиатуры и было ли оно передано компьютеру.
А как же Fn и мультимедийные кнопки?
Особенность многих ноутбучных клавиатур — клавиша Fn. Обычно её обрабатывает встроенный контроллер ноутбука. В нашем случае этот контроллер отсутствует, поэтому поведение Fn пришлось реализовать самостоятельно. Я добавил программный слой, который определяет активность Fn и преобразует необходимые сочетания в соответствующие HID-команды.
Например:
Сочетание |
Действие |
Fn + Num0 |
Отключение/включение звука — Mute |
Fn + ← |
Уменьшение громкости |
Fn + → |
Увеличение громкости |
Fn + PageUp |
Home |
Fn + PageDown |
End |
С мультимедийными командами обнаружился отдельный нюанс USB HID. Обычные клавиши используют страницу кодов Keyboard/Keypad. Но управление громкостью корректнее реализовывать через отдельную HID-страницу Consumer Control.
Первая реализация управления громкостью через обычные клавиатурные HID-коды не дала ожидаемого результата в Windows. Поэтому я добавил отдельный USB HID-интерфейс Consumer Control. После этого мультимедийные сочетания заработали на реальном компьютере. Эта небольшая особенность хорошо показывает, почему работающий USB-интерфейс ещё не гарантирует правильного поведения всех функций устройства.
Вторая ревизия платы: исправляем и дополняем

После первоначальных испытаний стало понятно, что плату стоит доработать. В новой ревизии я сохранил основной принцип подключения клавиатуры, но переработал вспомогательные узлы и предусмотрел возможности для дальнейшего расширения. Появились аппаратные кнопки BOOT и RESET, была доработана подсистема питания, добавлены элементы контроля потребления и управления питанием будущего контроллера RGB-подсветки.
Саму подсветку я решил не смешивать с клавиатурной матрицей: она использует отдельный интерфейс и заслуживает самостоятельного исследования. Ещё одним практическим улучшением стала возможность программировать STM32 через USB.
Теперь без обязательного ST-Link
Первую ревизию я прошивал через ST-Link. На второй предусмотрел удобный доступ к системному загрузчику STM32 через кнопки BOOT и RESET. Это позволяет перевести микроконтроллер в режим загрузки и записать прошивку через USB DFU. То есть для обычного обновления прошивки больше не требуется подключать внешний программатор.
SWD при этом остаётся полезным для полноценной отладки. Здесь важно не путать USB DFU для программирования микроконтроллера и USB HID Boot Protocol клавиатуры. Несмотря на слово Boot, это совершенно разные механизмы.

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

Для проверки я использовал сайт для онлайн проверки клавиатур, последовательно проверяя клавиши и сочетания. На плате Rev.2 удалось подтвердить работу всех 102 клавиш, навигационных функций Home/End и мультимедийных команд громкости и Mute. Также проверил подключение USB-C в обеих ориентациях и работу диагностического режима.
Параллельно для программной логики были разработаны автоматизированные тесты. На текущем этапе проходят 151 host-тест в семи наборах CTest. Они проверяют, в частности, работу матрицы, дребезг, ghosting, обработку состояний, HID-отчёты и диагностические интерфейсы. Автоматизированные тесты не заменяют проверку настоящей платы, но позволяют вносить изменения в прошивку и контролировать, не нарушилась ли уже реализованная функциональность.
Что имеем на данный момент
Разумеется, говорить о полном завершении разработки ещё рано. Основные функции клавиатуры уже работают, но предстоит завершить ряд аппаратных испытаний подсистемы питания, защит и энергопотребления. Часть специальных режимов USB также требует дополнительной проверки на новой ревизии платы. Тем не менее главная задача этого этапа выполнена: клавиатура работает отдельно от ноутбука через собственный адаптер.
Что оказалось самым сложным?
Если оценивать проект только по печатной плате, можно подумать, что главной трудностью была трассировка 26 сигналов на двух слоях. И отчасти это действительно так. Мне приходилось многократно возвращаться к принципиальной схеме, менять назначение GPIO и снова трассировать дорожки. Монтаж мелких компонентов тоже потребовал освоить новые инструменты и приобрести микроскоп.
Но после завершения основных работ я бы выделил другую сложность.
Самое трудное — обеспечить достоверное преобразование электрических состояний неизвестной матрицы в привычные компьютерные нажатия. Оказалось недостаточно определить распиновку, просканировать строки и отправить найденные клавиши в USB.
Пришлось экспериментально исследовать поведение клавиатуры, обнаружить неоднозначные сочетания, разработать алгоритм их обработки, учесть дребезг, обеспечить независимость сканирования от диагностики и правильно организовать USB HID. При этом каждая часть должна работать совместно с остальными. Именно поэтому больше всего пользы принесли не сложные компоненты, а возможность наблюдать, измерять и проверять поведение устройства на каждом этапе.
Что дальше?
Изначально я хотел лишь получить самостоятельную клавиатуру с привычными клавишами. Теперь, когда электронная основа работает, хочется довести проект до законченного изделия. Следующий этап — штатная RGB-подсветка MSI GS75. С ней всё несколько интереснее, чем с основной клавиатурной матрицей. Подсветка использует отдельный шлейф и собственную систему управления, которую предстоит исследовать.
Для определения её распиновки я разрабатываю отдельное устройство и диагностическую прошивку. Этому исследованию планирую посвятить следующую статью. Затем хочу создать собственную плату управления подсветкой и дополнить прошивку клавиатурного адаптера необходимыми командами и функциями.
Наконец, предстоит механическая часть: 3D-сканирование клавиатуры, проектирование корпуса и его изготовление на 3D-принтере. В результате должна получиться самостоятельная клавиатура с электроникой, прошивкой, подсветкой и корпусом собственной разработки.
А началось всё с того, что ни одна покупная клавиатура не подошла.

Комментарии (3)

andi123
30.10.2026 12:12Есть мнение, что можно было воспользоваться qmk. Где всё что нужно для клавиатуры уже есть. Достаточно прозвонить клавиатуру и собрать прошивку.
RTFM13
Во всём сколько-нибудь приличном софте пины свапать можно прям в трассировщике и потом экспортировать обратно в схему.