Sıfırdan: Geliştiricinin Rüya Klavyesini Nasıl İnşa Ettim
Yayınlanan: 2022-03-11Ağustos 2007'de bir gün çalışırken, normal bilgisayar klavyemin bana mümkün olduğunca fazla hizmet etmediğini fark etmeden edemedim. Ellerimi klavyemin çeşitli blokları arasında günde binlerce olmasa da yüzlerce kez aşırı derecede hareket ettirmek zorunda kaldım ve ellerim rahatsız edici derecede birbirine yakındı. Daha iyi bir yol olmalı, diye düşündüm.
Bu farkındalığın ardından, geliştiriciler için en iyi klavyeyi özelleştirmeyi düşündüğümde karşı konulmaz bir heyecan duygusu geldi - ve daha sonra, serbest çalışan bir gömülü yazılım geliştiricisi olarak donanım konusunda umutsuzca cahil olduğumu fark ettim.
O zamanlar diğer projelerle oldukça meşguldüm ama hacker klavyesini yapmayı düşünmediğim bir gün bile geçmedi. Kısa süre sonra boş zamanımı proje üzerinde çalışmaya ayırmaya başladım. Tamamen yeni bir beceri seti öğrenmeyi başardım, bir arkadaşım, olağanüstü makine mühendisi Andras Volgyi'yi projeye katılmaya ikna ettim, bazı kilit insanları topladım ve çalışan prototipler oluşturmaya yeterli zaman ayırmayı başardım. Günümüzde, Ultimate Hacking Klavye bir gerçektir. Günlük ilerleme kaydediyoruz ve kitle fonlaması kampanyamızın lansmanı yakın.
Bir yazılım arka planından, elektronik hakkında hiçbir şey bilmeden, güçlü, pazarlanabilir bir donanım cihazı tasarlamaya ve oluşturmaya geçmek, ilginç ve büyüleyici bir deneyimdir. Bu yazıda, bu elektronik şaheserin nasıl çalıştığının tasarımını anlatacağım. Elektronik devre şemalarının temel bir anlayışı, takip etmenize yardımcı olabilir.
Klavye nasıl yapılır?
Hayatımın binlerce saatini bu konuya adadıktan sonra, kısa bir cevap vermek benim için çok zor ama bu soruyu cevaplamanın ilginç bir yolu var. Arduino kartı gibi basit bir şeyle başlayıp, yavaş yavaş Ultimate Hacking Klavyesi olacak şekilde geliştirsek ne olur? Sadece daha sindirilebilir değil, aynı zamanda son derece eğitici olmalıdır. Bu nedenle, klavye eğitimi yolculuğumuz başlasın!
Birinci Adım: Tuşsuz Klavye
İlk olarak, x
karakterini saniyede bir kez yayan bir USB klavye yapalım. Arduino Micro geliştirme kartı, bu amaç için ideal bir adaydır, çünkü ATmega32U4 mikrodenetleyicisine - bir AVR mikro denetleyicisine ve UHK'nin beyni olan aynı işlemciye sahiptir.
USB özellikli AVR mikro denetleyicileri söz konusu olduğunda, AVR'ler için Hafif USB Çerçevesi (LUFA) tercih edilen kitaplıktır. Bu işlemcilerin yazıcıların, MIDI cihazlarının, klavyelerin veya hemen hemen her tür USB cihazının beyni olmasını sağlar.
Bir cihazı USB bağlantı noktasına takarken, cihazın USB tanımlayıcıları adı verilen bazı özel veri yapılarını aktarması gerekir. Bu tanımlayıcılar, ana bilgisayara bağlanan aygıtın türünü ve özelliklerini söyler ve bir ağaç yapısıyla temsil edilir. Konuları daha da karmaşık hale getirmek için, bir cihaz yalnızca bir değil, birden çok işlevi uygulayabilir. UHK'nin tanımlayıcı yapısını görelim:
- Cihaz tanımlayıcısı
- Yapılandırma tanımlayıcısı
- Arayüz tanımlayıcı 0: GenericHID
- uç nokta tanımlayıcısı
- Arayüz tanımlayıcı 1: Klavye
- uç nokta tanımlayıcısı
- Arayüz tanımlayıcı 2: Fare
- uç nokta tanımlayıcısı
- Arayüz tanımlayıcı 0: GenericHID
- Yapılandırma tanımlayıcısı
Çoğu standart klavye, yalnızca mantıklı olan tek bir klavye arabirim tanımlayıcısını gösterir. Bununla birlikte, özel bir programlama klavyesi olarak, UHK ayrıca bir fare arabirim tanımlayıcısı sunar, çünkü kullanıcı, klavyenin fare olarak kullanılabilmesi için fare işaretçisini kontrol etmek için klavyenin rastgele tuşlarını programlayabilir. GenericHID arabirimi, klavyenin tüm özel özellikleri için yapılandırma bilgilerini değiş tokuş etmek için bir iletişim kanalı görevi görür. Cihazın tam uygulamasını ve UHK'nin LUFA'daki konfigürasyon tanımlayıcılarını burada görebilirsiniz.
Tanımlayıcıları oluşturduğumuza göre, x
karakterini her saniyede gönderme zamanı.
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, yoklamalı bir protokoldür; bu, ana bilgisayarın, gönderilecek yeni veri olup olmadığını öğrenmek için cihazı düzenli aralıklarla (genellikle saniyede 125 kez) sorguladığı anlamına gelir. İlgili geri arama, bu durumda isSecondElapsed
değişkeni 1
içerdiğinde ana bilgisayara x
karakterinin tarama kodunu gönderen CALLBACK_HID_Device_CreateHIDReport()
işlevidir. isSecondElapsed
, ana döngüden saniyede 1
ve geri aramadan 0
ayarlanır.
İkinci Adım: Dört Tuşlu Klavye
Bu noktada klavyemiz pek kullanışlı değil. Gerçekten yazabilseydik iyi olurdu. Ancak bunun için tuşlara ihtiyacımız var ve tuşların bir klavye matrisinde düzenlenmesi gerekiyor. Tam boyutlu 104 tuşlu bir klavyede 18 satır ve 6 sütun olabilir, ancak başlangıç için basit bir 2x2 klavye matrisine sahip olacağız. Bu şematik:
Ve bir breadboard üzerinde böyle görünüyor:
ROW1 öğesinin PINA0 , ROW1
ila PINA0
, ROW2
ila PINA1
ve COL1
ila PORTB0
COL2
bağlı olduğunu PORTB1
, tarama kodu şöyle görünür:
/* 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, her seferinde bir sütunu tarar ve bu sütun içinde ayrı anahtar anahtarlarının durumlarını okur. Anahtar anahtarlarının durumu daha sonra bir diziye kaydedilir. Önceki CALLBACK_HID_Device_CreateHIDReport()
işlevimizde, ilgili tarama kodları o dizinin durumuna göre gönderilir.
Üçüncü Adım: İki Yarımdan Bir Klavye
Şimdiye kadar normal bir klavyenin başlangıçlarını oluşturduk. Ancak bu klavye eğitiminde, gelişmiş ergonomiyi hedefliyoruz ve insanların iki eli olduğu göz önüne alındığında, karışıma başka bir klavye yarısı eklesek iyi olur.
Diğer yarısı, öncekiyle aynı şekilde çalışan başka bir klavye matrisine sahip olacak. Heyecan verici yeni şey, klavye yarıları arasındaki iletişimdir. Elektronik cihazları birbirine bağlamak için en popüler üç protokol SPI, I 2 C ve UART'tır. Pratik amaçlar için bu durumda UART kullanacağız.
Çift yönlü iletişim, yukarıdaki şemaya göre RX üzerinden sağa ve TX üzerinden sola doğru akar. Güç aktarımı için VCC ve GND gereklidir. UART, eşlerin aynı baud hızını, veri biti sayısını ve durdurma biti sayısını kullanmasına ihtiyaç duyar. Her iki eşin UART alıcı-vericisi kurulduğunda, iletişim akmaya başlayabilir.
Şimdilik, sol klavye yarısı UART aracılığıyla sağ klavye yarısına bir baytlık mesajlar göndererek tuşa basma veya tuş bırakma olaylarını temsil eder. Sağ klavye yarısı bu mesajları işler ve buna göre bellekteki tam klavye matris dizisinin durumunu işler. Sol klavyenin yarısı bu şekilde mesaj gönderir:
USART_SendByte(IsKeyPressed<<7 | Row*COLS_NUM + Col);
Mesajı almak için klavyenin sağ yarısının kodu şöyle görünür:
void KeyboardRxCallback(void) { uint8_t Event = USART_ReceiveByte(); if (!MessageBuffer_IsFull(&KeyStateBuffer)) { MessageBuffer_Insert(&KeyStateBuffer, Event); } }
KeyboardRxCallback()
kesme işleyicisi, UART aracılığıyla bir bayt alındığında tetiklenir. Kesme işleyicilerinin mümkün olduğu kadar çabuk çalışması gerektiği göz önüne alındığında, alınan mesaj daha sonra işlenmek üzere bir halka arabelleğine konur. Halka arabelleği sonunda ana döngü içerisinden işlenir ve klavye matrisi mesaja göre güncellenir.

Yukarıdakiler, bunu gerçekleştirmenin en basit yoludur, ancak son protokol biraz daha karmaşık olacaktır. Çok baytlı mesajların işlenmesi gerekecek ve bireysel mesajların bütünlük açısından CRC-CCITT sağlama toplamları kullanılarak kontrol edilmesi gerekecek.
Bu noktada, breadboard prototipimiz oldukça etkileyici görünüyor:
Dördüncü Adım: LED Ekranla Tanışın
UHK ile hedeflerimizden biri, üretkenliği daha da artırmak için kullanıcının uygulamaya özel birden çok klavye haritası tanımlamasını sağlamaktı. Kullanıcının, kullanılan gerçek tuş haritasının farkında olması için bir yola ihtiyacı vardır, bu nedenle klavyeye entegre bir LED ekran yerleştirilmiştir. İşte tüm LED'lerin yandığı bir prototip ekran:
LED ekran, 8x6 LED matrisi tarafından uygulanır:
Her iki sıradaki kırmızı renkli LED sembolü, 14 segmentli LED ekranlardan birinin segmentlerini temsil eder. Beyaz LED sembolleri, ek üç durum göstergesini temsil eder.
Akımı bir LED üzerinden sürmek ve aydınlatmak için ilgili sütun yüksek voltaja ve ilgili satır düşük voltaja ayarlanır. Bu sistemin ilginç bir sonucu, herhangi bir anda yalnızca bir sütunun etkinleştirilebilmesidir (o sütundaki yanması gereken tüm LED'lerin karşılık gelen satırları düşük voltaja ayarlanır), diğer sütunlar devre dışı bırakılır. . Bu sistemin tam LED setini kullanmak için çalışamayacağını düşünebilirsiniz, ancak gerçekte sütunlar ve satırlar o kadar hızlı güncellenir ki insan gözü hiçbir titremeyi göremez.
LED matrisi, biri satırlarını diğeri sütunlarını süren iki entegre devre (IC) tarafından çalıştırılır. Sütunları çalıştıran kaynak IC, PCA9634 I2C LED sürücüsüdür:
Satırları çalıştıran LED matris havuzu IC, TPIC6C595 güç kaydırma yazmacıdır:
İlgili kodu görelim:
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()
, LED matrisinin bir satırını güncelleyerek yaklaşık her milisaniyede bir çağrılır. LedStates
dizisi, bireysel LED'lerin durumunu saklar, klavyenin sağ yarısından gelen mesajlara dayalı olarak UART aracılığıyla güncellenir, tuşa basma/tuş bırakma olayında olduğu gibi hemen hemen aynı şekilde.
Büyük resim
Şimdiye kadar özel bilgisayar korsanı klavyemiz için gerekli tüm bileşenleri kademeli olarak oluşturduk ve büyük resmi görmenin zamanı geldi. Klavyenin içi mini bir bilgisayar ağı gibidir: birbirine bağlı çok sayıda düğüm. Aradaki fark, düğümler arasındaki mesafenin metre veya kilometre cinsinden değil, santimetre cinsinden ölçülmesi ve düğümlerin tam teşekküllü bilgisayarlar değil, küçük entegre devreler olmasıdır.
Şimdiye kadar geliştirici klavyesinin cihaz tarafı ayrıntıları hakkında çok şey söylendi, ancak ana bilgisayar tarafı yazılımı olan UHK Agent hakkında çok fazla şey söylenmedi. Bunun nedeni, donanım ve bellenimden farklı olarak Agent'ın bu noktada çok ilkel olmasıdır. Ancak, paylaşmak istediğim Agent'ın üst düzey mimarisine karar verildi.
UHK Agent, klavyenin kullanıcının ihtiyaçlarına göre özelleştirilebileceği yapılandırıcı uygulamasıdır. Ajan, zengin bir istemci olmasına rağmen, web teknolojilerini kullanır ve düğüm-webkit platformunun üstünde çalışır.
Aracı, özel, cihaza özel USB kontrol istekleri göndererek ve sonuçlarını işleyerek düğüm-usb kitaplığını kullanarak klavye ile iletişim kurar. Üçüncü taraf uygulamalar tarafından tüketilmek üzere bir REST API'sini ortaya çıkarmak için Express.js'yi kullanır. Ayrıca düzgün bir kullanıcı arayüzü sağlamak için Angular.js'yi kullanır.
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 ); }
Her komutun 8 bitlik bir tanımlayıcısı ve bir dizi komuta özgü argümanı vardır. Şu anda yalnızca yeniden numaralandırma komutu uygulanmaktadır. sendReenumerateCommand()
, aygıtın bellenimi yükseltmek için sol önyükleyici veya sağ önyükleyici olarak veya bir klavye aygıtı olarak yeniden numaralandırılmasını sağlar.
Bu yazılım tarafından elde edilebilecek gelişmiş özellikler hakkında hiçbir fikrimiz olmayabilir, bu yüzden birkaçını adlandıracağım: Temsilci, bireysel anahtarların aşınmasını görselleştirebilecek ve kullanıcıya yaşam beklentileri hakkında bilgi verebilecek, böylece kullanıcı Yaklaşan onarım için birkaç yeni anahtar satın alın. Ajan ayrıca, hacker klavyesinin çeşitli tuş haritalarını ve katmanlarını yapılandırmak için bir kullanıcı arayüzü sağlayacaktır. Fare işaretçisinin hızı ve ivmesi, diğer birçok uber özelliğiyle birlikte ayarlanabilir. Gökyüzü sınırdır.
Prototipin Oluşturulması
Özelleştirilmiş klavye prototipleri oluşturmak için çok fazla çalışma yapılır. Her şeyden önce, kendi içinde oldukça karmaşık olan ve özel olarak tasarlanmış plastik parçalar, lazerle kesilmiş paslanmaz çelik plakalar, hassas işlenmiş çelik kılavuzlar ve iki klavye yarısını bir arada tutan neodimyum mıknatıslar içeren mekanik tasarımın tamamlanması gerekiyor. İmalat başlamadan önce her şey CAD'de tasarlanır.
3D baskılı klavye kılıfı şöyle görünür:
Mekanik tasarım ve şemaya göre baskılı devre kartı tasarlanmalıdır. Sağdaki PCB KiCad'de şöyle görünür:
Ardından PCB üretilir ve yüzeye monte bileşenlerin elle lehimlenmesi gerekir:
Son olarak, 3D baskı, plastik parçaların parlatılması ve boyanması ve her şeyin montajı dahil olmak üzere tüm parçaları ürettikten sonra, bunun gibi çalışan bir hacker klavye prototipi elde ediyoruz:
Çözüm
Geliştiricilerin klavyelerini müzisyenlerin enstrümanlarıyla karşılaştırmayı seviyorum. Düşünürseniz klavyeler oldukça samimi nesnelerdir. Ne de olsa, onları tüm gün, karakter karakter yarının yazılımını oluşturmak için kullanıyoruz.
Muhtemelen yukarıdakilerden dolayı, Ultimate Hacking Keyboard'u geliştirmeyi bir ayrıcalık olarak görüyorum ve tüm zorluklara rağmen, çoğu zaman çok heyecan verici bir yolculuk ve inanılmaz derecede yoğun bir öğrenme deneyimi oldu.
Bu geniş bir konu ve burada sadece yüzeyi çizebildim. Umudum, bu makalenin çok eğlenceli ve ilginç materyallerle dolu olmasıdır. Herhangi bir sorunuz varsa, lütfen yorumlarda bana bildirin.
Son olarak, daha fazla bilgi için https://ultimatehackingkeyboard.com adresini ziyaret edebilir ve kampanyamızın lansmanından haberdar olmak için buraya abone olabilirsiniz.