Od podstaw: jak zbudowałem wymarzoną klawiaturę programisty

Opublikowany: 2022-03-11

Pracując pewnego dnia w sierpniu 2007, nie mogłem nic poradzić na to, że zdałem sobie sprawę, że moja zwykła klawiatura komputerowa nie służyła mi tak bardzo, jak to możliwe. Musiałem nadmiernie przesuwać ręce między różnymi blokami klawiatury, setki, jeśli nie tysiące razy dziennie, a moje ręce były niewygodnie blisko siebie. Musi być lepszy sposób, pomyślałem.

Po tym uświadomieniu nastąpiło przytłaczające uczucie podekscytowania, gdy myślałem o dostosowaniu najlepszej klawiatury dla programistów – a później uświadomiłem sobie, że jako niezależny programista oprogramowania wbudowanego nie miałem nadziei na sprzęt.

W tym czasie byłem dość zajęty innymi projektami, ale nie było dnia, w którym nie pomyślałbym o zbudowaniu klawiatury hakerskiej. Wkrótce zacząłem poświęcać swój wolny czas na pracę nad projektem. Udało mi się nauczyć zupełnie nowego zestawu umiejętności, przekonać mojego przyjaciela Andrasa Volgyi, niezwykłego inżyniera mechanika, aby dołączył do projektu, zebrać kilka kluczowych osób i poświęcić wystarczająco dużo czasu na tworzenie działających prototypów. W dzisiejszych czasach klawiatura Ultimate Hacking to rzeczywistość. Robimy codzienne postępy, a rozpoczęcie naszej kampanii crowdfundingowej jest w zasięgu ręki.

Zacząłem od myślenia o tym, jak zmienić układ klawiatury i na tym skończyłem!

Przejście od środowiska programistycznego, nie znającego się na elektronice, do projektowania i budowy potężnego, rynkowego urządzenia sprzętowego, jest ciekawym i fascynującym doświadczeniem. W tym artykule opiszę projekt, jak działa to elektroniczne arcydzieło. Podstawowe zrozumienie schematów obwodów elektronicznych może pomóc w naśladowaniu.

Jak zrobić klawiaturę?

Po poświęceniu temu tematowi tysięcy godzin życia, udzielenie krótkiej odpowiedzi jest dla mnie sporym wyzwaniem, ale jest ciekawy sposób na odpowiedź na to pytanie. A co, jeśli zaczniemy od czegoś prostego, na przykład płytki Arduino, i stopniowo zbudujemy z niej najlepszą klawiaturę hakerską? Powinien być nie tylko bardziej przyswajalny, ale i niezwykle edukacyjny. Dlatego niech rozpocznie się nasza podróż samouczka klawiatury!

Krok pierwszy: klawiatura bez klawiszy

Najpierw zróbmy klawiaturę USB, która emituje znak x raz na sekundę. Płytka rozwojowa Arduino Micro jest idealnym kandydatem do tego celu, ponieważ zawiera mikrokontroler ATmega32U4 - mikrocrontroller AVR i ten sam procesor, który jest mózgiem UHK.

Płytka Arduino Micro była podstawą do budowy mojej klawiatury dla programistów.

Jeśli chodzi o mikrokontrolery AVR obsługujące port USB, biblioteką z wyboru jest Lightweight USB Framework for AVR (LUFA). Pozwala tym procesorom stać się mózgami drukarek, urządzeń MIDI, klawiatur lub prawie każdego innego typu urządzenia USB.

Podłączając urządzenie do portu USB, urządzenie musi przesyłać pewne specjalne struktury danych zwane deskryptorami USB. Deskryptory te informują komputer hosta o typie i właściwościach podłączanego urządzenia i są reprezentowane przez strukturę drzewa. Aby jeszcze bardziej skomplikować sprawę, urządzenie może realizować nie tylko jedną, ale wiele funkcji. Zobaczmy strukturę deskryptorów UHK:

  • Deskryptor urządzenia
    • Deskryptor konfiguracji
      • Deskryptor interfejsu 0: GenericHID
        • Deskryptor punktu końcowego
      • Deskryptor interfejsu 1: Klawiatura
        • Deskryptor punktu końcowego
      • Deskryptor interfejsu 2: Mysz
        • Deskryptor punktu końcowego

Większość standardowych klawiatur udostępnia tylko jeden deskryptor interfejsu klawiatury, co ma sens. Jednak jako niestandardowa klawiatura programistyczna, UHK udostępnia również deskryptor interfejsu myszy, ponieważ użytkownik może zaprogramować dowolne klawisze klawiatury do sterowania wskaźnikiem myszy, dzięki czemu klawiatura może być używana jako mysz. Interfejs GenericHID służy jako kanał komunikacyjny do wymiany informacji konfiguracyjnych dla wszystkich specjalnych funkcji klawiatury. Tutaj możesz zobaczyć pełną implementację deskryptorów urządzenia i konfiguracji UHK w LUFA.

Teraz, gdy stworzyliśmy deskryptory, nadszedł czas, aby co sekundę wysyłać znak x .

 uint8_t isSecondElapsed = 0; int main(void) { while (1) { _delay_us(1000); isSecondElapsed = 1; } } bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo, uint8_t* const ReportID, const uint8_t ReportType, void* ReportData, uint16_t* const ReportSize) { USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData; if (isSecondElapsed) { KeyboardReport->KeyCode[0] = HID_KEYBOARD_SC_X; isSecondElapsed = 0; } *ReportSize = sizeof(USB_KeyboardReport_Data_t); return false; }

USB jest protokołem odpytywania, co oznacza, że ​​komputer host regularnie wysyła do urządzenia zapytanie (zwykle 125 razy na sekundę), aby dowiedzieć się, czy są jakieś nowe dane do wysłania. Odpowiednim wywołaniem zwrotnym jest funkcja CALLBACK_HID_Device_CreateHIDReport() , która w tym przypadku wysyła do hosta kod skanu znaku x , gdy zmienna isSecondElapsed zawiera 1 . isSecondElapsed jest ustawiane na 1 z głównej pętli na sekundę i ustawiane na 0 z wywołania zwrotnego.

Krok drugi: klawiatura czterech klawiszy

W tym momencie nasza klawiatura nie jest strasznie przydatna. Byłoby miło, gdybyśmy mogli na nim pisać. Ale do tego potrzebujemy klawiszy, a klawisze muszą być ułożone w matrycę klawiatury. Pełnowymiarowa 104-klawiszowa klawiatura może mieć 18 rzędów i 6 kolumn, ale do uruchomienia będziemy mieć po prostu skromną matrycę klawiatury 2x2. Oto schemat:

Aby dostosować klawiaturę hakera, musisz dokładnie rozważyć macierz klawiszy.

A tak to wygląda na płytce stykowej:

Konfiguracja płytki prototypowej jest kluczowym krokiem w tworzeniu klawiatury dla programistów.

Zakładając, że ROW1 jest połączony z PINA0 , ROW2 z PINA1 , COL1 z PORTB0 i COL2 z PORTB1 , oto jak wygląda kod skanowania:

 /* A single pin of the microcontroller to which a row or column is connected. */ typedef struct { volatile uint8_t *Direction; volatile uint8_t *Name; uint8_t Number; } Pin_t; /* This part of the key matrix is stored in the Flash to save SRAM space. */ typedef struct { const uint8_t ColNum; const uint8_t RowNum; const Pin_t *ColPorts; const Pin_t *RowPins; } KeyMatrixInfo_t; /* This Part of the key matrix is stored in the SRAM. */ typedef struct { const __flash KeyMatrixInfo_t *Info; uint8_t *Matrix; } KeyMatrix_t; const __flash KeyMatrixInfo_t KeyMatrix = { .ColNum = 2, .RowNum = 2, .RowPins = (Pin_t[]) { { .Direction=&DDRA, .Name=&PINA, .Number=PINA0 }, { .Direction=&DDRA, .Name=&PINA, .Number=PINA1 } }, .ColPorts = (Pin_t[]) { { .Direction=&DDRB, .Name=&PORTB, .Number=PORTB0 }, { .Direction=&DDRB, .Name=&PORTB, .Number=PORTB1 }, } }; void KeyMatrix_Scan(KeyMatrix_t *KeyMatrix) { for (uint8_t Col=0; Col<KeyMatrix->Info->ColNum; Col++) { const Pin_t *ColPort = KeyMatrix->Info->ColPorts + Col; for (uint8_t Row=0; Row<KeyMatrix->Info->RowNum; Row++) { const Pin_t *RowPin = KeyMatrix->Info->RowPins + Row; uint8_t IsKeyPressed = *RowPin->Name & 1<<RowPin->Number; KeyMatrix_SetElement(KeyMatrix, Row, Col, IsKeyPressed); } } }

Kod skanuje jedną kolumnę na raz iw obrębie tej kolumny odczytuje stany poszczególnych przełączników kluczykowych. Stan przełączników kluczy jest następnie zapisywany w tablicy. W naszej poprzedniej funkcji CALLBACK_HID_Device_CreateHIDReport() odpowiednie kody skanowania zostaną następnie wysłane na podstawie stanu tej tablicy.

Krok trzeci: klawiatura z dwiema połówkami

Do tej pory stworzyliśmy początki normalnej klawiatury. Ale w tym samouczku dotyczącym klawiatury dążymy do zaawansowanej ergonomii, a biorąc pod uwagę, że ludzie mają dwie ręce, lepiej dodać kolejną połowę klawiatury do miksu.

Druga połowa będzie miała kolejną matrycę klawiatury, działającą tak samo jak poprzednia. Ekscytującą nowością jest komunikacja między połówkami klawiatury. Trzy najpopularniejsze protokoły do ​​łączenia urządzeń elektronicznych to SPI, I 2 C i UART. Ze względów praktycznych użyjemy w tym przypadku UART.

Aby była dobrą klawiaturą do programowania, musi istnieć gwiezdna komunikacja między obiema połówkami.

Komunikacja dwukierunkowa przepływa przez RX w prawo i przez TX w lewo zgodnie z powyższym schematem. VCC i GND są niezbędne do przesyłania zasilania. UART wymaga, aby peery używały tej samej szybkości transmisji, liczby bitów danych i liczby bitów stopu. Po skonfigurowaniu nadajnika-odbiornika UART obu peerów komunikacja może zacząć płynąć.

Na razie lewa połowa klawiatury wysyła jednobajtowe komunikaty do prawej połowy klawiatury przez UART, reprezentujące zdarzenia naciśnięcia lub zwolnienia klawisza. Prawa połowa klawiatury przetwarza te komunikaty i odpowiednio manipuluje stanem całej macierzy matrycy klawiatury w pamięci. W ten sposób lewa połowa klawiatury wysyła wiadomości:

 USART_SendByte(IsKeyPressed<<7 | Row*COLS_NUM + Col);

Kod dla prawej połowy klawiatury do odbioru wiadomości wygląda tak:

 void KeyboardRxCallback(void) { uint8_t Event = USART_ReceiveByte(); if (!MessageBuffer_IsFull(&KeyStateBuffer)) { MessageBuffer_Insert(&KeyStateBuffer, Event); } }

Procedura obsługi przerwań KeyboardRxCallback() jest wyzwalana za każdym razem, gdy przez UART odebrany zostanie bajt. Biorąc pod uwagę, że programy obsługi przerwań powinny działać tak szybko, jak to możliwe, odebrana wiadomość jest umieszczana w buforze pierścieniowym w celu późniejszego przetworzenia. Bufor pierścieniowy zostaje ostatecznie przetworzony z głównej pętli, a macierz klawiatury zostanie zaktualizowana na podstawie wiadomości.

Powyższe jest najprostszym sposobem, aby to się stało, ale końcowy protokół będzie nieco bardziej złożony. Trzeba będzie obsłużyć wiadomości wielobajtowe, a poszczególne wiadomości będą musiały zostać sprawdzone pod kątem integralności za pomocą sum kontrolnych CRC-CCITT.

W tym momencie nasz prototyp płytki prototypowej wygląda imponująco:

Prototyp płytki prototypowej zaczyna przybierać kształt dostosowanej do potrzeb programistów klawiatury.

Krok czwarty: poznaj wyświetlacz LED

Jednym z naszych celów związanych z UHK było umożliwienie użytkownikowi definiowania wielu map klawiatury specyficznych dla aplikacji w celu dalszego zwiększenia produktywności. Użytkownik musi mieć jakiś sposób, aby być świadomym rzeczywistej używanej mapy klawiszy, dlatego zintegrowany wyświetlacz LED jest wbudowany w klawiaturę. Oto prototypowy wyświetlacz z zapalonymi wszystkimi diodami LED:

Wyświetlacz LED ma kluczowe znaczenie dla stworzenia najlepszej klawiatury dla programistów w tym samouczku.

Wyświetlacz LED realizowany jest przez matrycę LED 8x6:

Klawiatury hakerskie nie byłyby kompletne bez matrycy LED 8x6.

Każde dwa rzędy symboli LED w kolorze czerwonym reprezentują segmenty jednego z 14-segmentowych wyświetlaczy LED. Białe symbole LED reprezentują dodatkowe trzy wskaźniki stanu.

Aby przepuścić prąd przez diodę LED i ją zapalić, odpowiednia kolumna jest ustawiona na wysokie napięcie, a odpowiedni wiersz na niskie napięcie. Ciekawą konsekwencją tego systemu jest to, że w danym momencie tylko jedna kolumna może być włączona (wszystkie diody na tej kolumnie, które powinny się zapalić, mają odpowiadające sobie wiersze ustawione na niskie napięcie), podczas gdy pozostałe kolumny są wyłączone . Można by pomyśleć, że ten system nie może działać przy wykorzystaniu pełnego zestawu diod LED, ale w rzeczywistości kolumny i wiersze są aktualizowane tak szybko, że ludzkie oko nie dostrzega migotania.

Matryca LED jest napędzana przez dwa układy scalone (IC), jeden napędzający jej rzędy, a drugi napędzający kolumny. Układem źródłowym, który napędza kolumny jest sterownik LED PCA9634 I2C:

Dwa układy scalone napędzają matrycę LED w klawiaturze Ultimate Hacker.

Układ scalony zlewu matrycy LED, który napędza rzędy, to rejestr przesuwny mocy TPIC6C595:

Układ scalony, który steruje rzędami diod LED, wygląda tak.

Zobaczmy odpowiedni kod:

 uint8_t LedStates[LED_MATRIX_ROWS_NUM]; void LedMatrix_UpdateNextRow(bool IsKeyboardColEnabled) { TPIC6C595_Transmit(LedStates[ActiveLedMatrixRow]); PCA9634_Transmit(1 << ActiveLedMatrixRow); if (++ActiveLedMatrixRow == LED_MATRIX_ROWS_NUM) { ActiveLedMatrixRow = 0; } }

LedMatrix_UpdateNextRow() jest wywoływana co milisekundę, aktualizując wiersz matrycy LED. Tablica LedStates przechowuje stan poszczególnych diod LED, jest aktualizowana przez UART w oparciu o komunikaty pochodzące z prawej połowy klawiatury, prawie w taki sam sposób, jak w przypadku naciśnięcia klawisza/zwolnienia klawisza.

Wielkie zdjęcie

Do tej pory stopniowo zbudowaliśmy wszystkie niezbędne komponenty do naszej niestandardowej klawiatury hakerskiej i nadszedł czas, aby zobaczyć pełny obraz. Wnętrze klawiatury jest jak mini sieć komputerowa: wiele połączonych ze sobą węzłów. Różnica polega na tym, że odległość między węzłami mierzy się nie w metrach czy kilometrach, ale w centymetrach, a węzły to nie pełnoprawne komputery, a maleńkie układy scalone.

Wnętrze naszej klawiatury samouczka składa się z połączonych ze sobą węzłów.

Do tej pory powiedziano wiele o szczegółach klawiatury programisty po stronie urządzenia, ale nie tyle o UHK Agent, oprogramowaniu po stronie hosta. Powodem jest to, że w przeciwieństwie do sprzętu i oprogramowania układowego Agent jest w tym momencie bardzo szczątkowy. Jednak zdecydowano się na wysokopoziomową architekturę Agenta, którą chciałbym się podzielić.

UHK Agent to aplikacja konfiguracyjna, za pomocą której można dostosować klawiaturę do potrzeb użytkownika. Pomimo tego, że jest bogatym klientem, Agent korzysta z technologii internetowych i działa na platformie node-webkit.

Agent komunikuje się z klawiaturą za pomocą biblioteki node-usb, wysyłając specjalne, specyficzne dla urządzenia żądania sterowania USB i przetwarzając ich wyniki. Używa Express.js do udostępnienia interfejsu API REST do użytku przez aplikacje innych firm. Używa również Angular.js, aby zapewnić schludny interfejs użytkownika.

 var enumerationModes = { 'keyboard' : 0, 'bootloader-right' : 1, 'bootloader-left' : 2 }; function sendReenumerateCommand(enumerationMode, callback) { var AGENT_COMMAND_REENUMERATE = 0; sendAgentCommand(AGENT_COMMAND_REENUMERATE, enumerationMode, callback); } function sendAgentCommand(command, arg, callback) { setReport(new Buffer([command, arg]), callback); } function setReport(message, callback) { device.controlTransfer( 0x21, // bmRequestType (constant for this control request) 0x09, // bmRequest (constant for this control request) 0, // wValue (MSB is report type, LSB is report number) interfaceNumber, // wIndex (interface number) message, // message to be sent callback ); }

Każde polecenie ma 8-bitowy identyfikator i zestaw argumentów specyficznych dla polecenia. Obecnie zaimplementowane jest tylko polecenie reenumerate. Metoda sendReenumerateCommand() powoduje ponowne wyliczenie urządzenia jako lewy bootloader lub prawy bootloader, w celu uaktualnienia oprogramowania układowego lub jako urządzenie klawiatury.

Można nie mieć pojęcia o zaawansowanych funkcjach, które można osiągnąć dzięki temu oprogramowaniu, więc wymienię kilka: Agent będzie w stanie zwizualizować zużycie poszczególnych kluczy i powiadomić użytkownika o ich oczekiwanej długości życia, aby użytkownik mógł kupić kilka nowych przełączników kluczykowych do zbliżającej się naprawy. Agent zapewni również interfejs użytkownika do konfigurowania różnych map klawiszy i warstw klawiatury hakerskiej. Można również ustawić prędkość i przyspieszenie wskaźnika myszy, a także wiele innych funkcji uber. Tylko niebo Cię ogranicza.

Tworzenie prototypu

Dużo pracy wymaga tworzenie niestandardowych prototypów klawiatur. Przede wszystkim musi zostać sfinalizowany projekt mechaniczny, który sam w sobie jest dość skomplikowany i obejmuje specjalnie zaprojektowane części z tworzywa sztucznego, wycinane laserowo płytki ze stali nierdzewnej, precyzyjnie frezowane stalowe prowadnice i magnesy neodymowe, które łączą ze sobą dwie połówki klawiatury. Wszystko jest projektowane w CAD przed rozpoczęciem produkcji.

Rysunek CAD pomaga w tworzeniu klawiatury, która działa dobrze dla programistów.

Tak wygląda obudowa z klawiaturą drukowaną w 3D:

Zaczęliśmy od drukowania w 3D obudowy klawiatury programistycznej.

Na podstawie projektu mechanicznego i schematu należy zaprojektować płytkę drukowaną. Prawa płytka drukowana w programie KiCad wygląda tak:

Programowanie klawiatury zaczyna się od zaprojektowania płytki drukowanej.

Następnie płytka drukowana zostaje wyprodukowana, a elementy natynkowe muszą być ręcznie lutowane:

Lutowanie niestandardowych komponentów klawiatury zapewnia prawidłowe działanie po umieszczeniu w etui.

Wreszcie, po wyprodukowaniu wszystkich części, w tym drukowaniu 3D, wypolerowaniu i pomalowaniu części plastikowych i złożeniu wszystkiego, otrzymujemy działający prototyp klawiatury hakerskiej, taki jak ten:

Wniosek

Lubię porównywać klawisze programistów do instrumentów muzyków. Klawiatury to dość intymne przedmioty, jeśli się nad tym zastanowić. W końcu używamy ich cały dzień, aby tworzyć oprogramowanie jutra, postać po postaci.

Prawdopodobnie z powodu powyższego uważam, że opracowanie Ultimate Hacking Keyboard to przywilej i pomimo wszystkich trudności, często była to bardzo ekscytująca podróż i niezwykle intensywne doświadczenie edukacyjne.

To obszerny temat i mogłem tu tylko zarysować powierzchnię. Mam nadzieję, że ten artykuł był bardzo zabawny i pełen ciekawego materiału. W razie pytań proszę o informację w komentarzach.

Na koniec zapraszamy do odwiedzenia https://ultimatehackingkeyboard.com, aby uzyskać więcej informacji i zasubskrybować tam, aby otrzymywać powiadomienia o rozpoczęciu naszej kampanii.