Kalan's Blog

Kalan 頭像照片,在淡水拍攝,淺藍背景

四零二曜日電子報上線啦!訂閱訂起來

Software Engineer / Taiwanese / Life in Fukuoka
This blog supports RSS feed (all content), you can click RSS icon or setup through third-party service. If there are special styles such as code syntax in the technical article, it is still recommended to browse to the original website for the best experience.

Current Theme light

我會把一些不成文的筆記或是最近的生活雜感放在短筆記,如果有興趣的話可以來看看唷!

Please notice that currenly most of posts are translated by AI automatically and might contain lots of confusion. I'll gradually translate the post ASAP

鍵盤入坑指南 - 韌體篇

雖然標題是寫入坑指南,不過這篇會更專注在鍵盤的運作原理跟電路。

偵測按下的鍵位

上一篇提到,鍵盤其實是個多個開關組成的電路,我們可以用微控制器 GPIO 接腳來偵測開關是否被按下。具體的做法是先將開關的一端接到電源,另一端接地。當按下按鍵時電路導通,微控制器就可以感知到這個變化,進而送出對應的信號。實際上是怎樣的信號,我們會在後面的章節提到。

不過這樣做有個缺點,就是每個按鍵都需要一個接腳,以一把有 104 個鍵位的鍵盤來說,就需要微控制器有 104 個接腳。通常微控制器並不會有那麼多接腳可以用,又或者有些接腳需要用來當作其他用途。

掃描與鍵盤矩陣

在實務上我們會用矩陣的連接方式來連接鍵盤的電路。每個矩陣都會接到微控制器的接腳,這樣一來假設按鍵要 60 個,6x10 的矩陣只要 16 個接腳即可。

test1

我們可以分別將 R1、R2、R3 分別給予高電位,再分別讀取 C1、C2、C3 的電位,就可以知道哪個按鍵被按下了。舉例當下圖的按鍵被按下時,C1 可以讀取到現在的電位是高電位,就能偵測到是哪個按鍵被按下。

test3

這邊的順序是:

  • 將 R1 設定為高電位
    • 讀取 C1
    • 讀取 C2
    • 讀取 C3
  • 將 R2 設定為高電位
    • 讀取 C1
    • 讀取 C2
    • 讀取 C3
  • 將 R3 設定為高電位
    • 讀取 C1
    • 讀取 C2
    • 讀取 C3

理想上當然是讓矩陣 Row 與 Col 的數量一樣,不過在實務上不一定會這樣做。一方面是因為微控制器的接腳通常是足夠的,另外一方面是如果強制讓兩個數量一樣,有時候 layout 會很難拉線。例如鍵盤就是一個橫列比較多,但是直列比較少。

這邊會有一個疑問是,那如果 R1 跟 R2 裡的按鍵同時按下的話會不會偵測不到?因為不是同時偵測而是輪流偵測。這點不需要擔心,一般的微控制器都至少有 16MHz 以上,偵測的速度絕對比人體極限還要高。微控制器會先記住按下的狀態,當再次讀取時發現與先前的狀態不同時,就會再送出放開的訊號。

Ghosting

test2

剛剛的電路有個問題,如果 R3 也有開關按下,那麼一部分的電流會從這裡跑過去導致誤判。解決方式是加入二極體。二極體具有單向導通的功能,電流只能夠從一個方向流通,進而避免 Ghosting。

HID(Human Interface Device)

接下來就來到將訊號傳給電腦的邏輯了。早期的鍵盤是使用 PS/2 連接器,不過現在最常見的則是 USB 與藍芽。

HID 透過規範各種常見的輸入裝置協定,像是鍵盤、滑鼠、控制器等等,讓以往需要額外撰寫驅動程式的成本簡化成只要符合 HID 符合的規格,不管什麼裝置都不需要驅動程式就可以執行。

HID 包含了輸入報告、描述子跟輸出報告。通常描述子可以用來描述這個裝置是什麼,像是用途、名稱、廠商、 ID 等等。電腦讀取到這些報告之後就知道該用什麼方式處理它。

USB

一般來說微控制器裡頭都會有 USB 的功能,可以幫你用 USB 的格式來傳輸資料。

HID 也一樣,一般來說可以透過 USB 或是藍芽來傳輸。不過這邊算是工程師會比較在意的技術細節,USB 分成兩個,host 跟 device。

由於像滑鼠、鍵盤這類型的輸入裝置,跟 CPU 比起來通常速度慢很多,所以如果讓 CPU 去等這些裝置的輸入,會非常浪費效能。實際上的做法是,電腦的 USB 控制器(host)會持續地像 device 端 pulling 資料,buffer 有資料時再去呼叫 CPU 中斷。因此在鍵盤端這邊,只會負責一直丟資料,剩下的都是由 host 端完成。

通常 HID 的格式要怎麼寫,在實際操作當中都是由函式庫幫你處理好了,所以只要專注在將對應的按鍵 mapping 到對應的鍵即可。

這邊放一個 Raspberry Pi pico 的例子:

static void send_hid_report(uint8_t report_id, uint32_t btn)
{
  // skip if hid is not ready yet
  if ( !tud_hid_ready() ) return;

  switch(report_id)
  {
    case REPORT_ID_KEYBOARD:
    {
      // use to avoid send multiple consecutive zero report for keyboard
      static bool has_keyboard_key = false;

      if ( btn )
      {
        uint8_t keycode[6] = { 0 };
        keycode[0] = HID_KEY_A;

        tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode);
        has_keyboard_key = true;
      }
      ...
}

這邊的例子只有一個按鈕,並且永遠都是 A 鍵。不過從程式碼我們可以大概得知要怎麼寫。程式碼首先先宣告了一個陣列 keycode。長度 6 代表同時可以傳送 6 個按鍵,而這邊只用了 keycode[0],然後呼叫 tud_hid_keyboard_report 將 HID report 送出。

電路板設計與佈線

我對電路板設計的涉略不深,這邊主要是想要說明只要有基本知識,任何人都可以做出電路板來。在電路板設計中,有個非常著名的開源軟體 Kicad,完全免費。

透過這個軟體,你可以自己設計想要的電路,並且它也支援 PCB layout 佈線。你可以將你設計的電路放到電路板拉線。

全部都弄好了之後,可以將電路圖(有專門的檔案格式)輸出後上傳到 PCB 印刷廠的網站,大概過幾個禮拜就能收到成品。比較常見的便宜廠商有 JLCPCBPCBWay

QMK

在客製化鍵盤的圈子裡,有些玩家對高度的客製化相當執著,例如 LED 要怎麼亮、配列要怎麼改、鍵盤上要有顆旋鈕,甚至是鍵盤上也要放個 LCD 螢幕。大家都有自己一套標準,所以除了硬體方面是各種工藝展現外,連韌體也講求高度客製化。

(圖取自於 Zoom75 官網)

Feature Case

理論上只要有微控制器和電路就可以寫出鍵盤韌體,然而如果每次改鍵盤配置都要修改程式碼、再加上如果微控制器更換就要重寫的話也很麻煩。因此有些玩家就自己寫了一個可以高度客製化配置的韌體,只要定義好鍵盤相關設定,就可以自動生出對應的韌體,一行程式碼都不用寫。

目前在鍵盤圈最有名的就是 QMK,它是由 tmk 衍伸出來的開源韌體。在鍵盤圈相當常見,甚至像 Keychron 也支援 QMK 設定。

剛剛提到的功能,QMK 幾乎都有。如何定義鍵位、巨集、LCD、LED 控制、藍芽、Joystick,鍵盤甚至是 MIDI,任何你想得到的功能 QMK 裡頭幾乎都有。而且支援的客製化鍵盤很多,例如我現在用的 Zoom65 就可以在這裡找到它的定義檔案。

除此之外,蠻令我驚訝的是現在還有支援 Web USB,也就是直接在網路上去修改鍵位,修改完之後直接調整韌體的設定。

詳細原理我還沒研究過,不過我猜應該是有對應的程式碼實作,透過 Web USB 直接將資料傳給鍵盤的韌體,有興趣的可以參考 VIA

結語

現在很多電路板、外殼都有開源的模型跟電路設計可以參考,想要自己「從頭」做一把雖然還是有難度,但跟以前比起來算是容易很多。不過光是了解背後的過程,就會覺得做一把鍵盤也不是那麼容易。

Prev

鍵盤入坑指南 - 硬體篇

Next

拿到日本永住權了

If you found this article helpful, please consider buy me a drink ☕️ It'll make my ordinary day shine✨

Buy me a coffee