QMK鍵盤固件自定義指南 - 打造你的專屬鍵盤體驗
🚀 前言
在機械鍵盤的世界里,QMK固件讓你的鍵盤不再只是簡單的輸入設備,而是可以按照你的意愿定制的強大工具。本文將深入淺出地介紹如何自定義QMK鍵盤的行為,從基礎概念到高級應用,帶你玩轉QMK!
📚 QMK的分層架構
QMK采用分層架構設計,從上到下依次為:
- 核心層(_quantum):提供基礎功能
- 社區模塊層(_<module>):提供擴展功能
- 社區模塊 -> 鍵盤/修訂版(_<module>_kb)
- 社區模塊 -> 鍵盤映射(_<module>_user)
- 鍵盤/修訂版層(_kb):特定鍵盤的功能
- 鍵盤映射層(_user):用戶自定義配置
💡 小貼士:在鍵盤/修訂版級別定義函數時,必須在適當位置調用
_user()
,否則鍵盤映射級別的函數將永遠不會被執行。
🔑 自定義鍵碼
定義新鍵碼
創建自定義鍵碼的第一步是枚舉它們。QMK提供了SAFE_RANGE
宏來確保你的自定義鍵碼獲得唯一的編號。
enum my_keycodes {FOO = SAFE_RANGE,BAR
};
通過這段代碼,你可以在鍵盤映射中使用FOO
和BAR
兩個自定義鍵碼。
編程鍵碼行為
要控制鍵碼的行為,你需要使用process_record_kb()
和process_record_user()
函數。這些函數在按鍵處理過程中被調用:
bool process_record_user(uint16_t keycode, keyrecord_t *record) {switch (keycode) {case FOO:if (record->event.pressed) {// 按下時執行的代碼} else {// 釋放時執行的代碼}return false; // 跳過此鍵的后續處理case KC_ENTER:// 按下回車鍵時播放音效if (record->event.pressed) {PLAY_SONG(tone_qwerty);}return true; // 讓QMK繼續處理回車鍵的按下/釋放事件default:return true; // 正常處理所有其他鍵碼}
}
record
參數包含按鍵事件的詳細信息:
keyrecord_t record {keyevent_t event {keypos_t key {uint8_t coluint8_t row}bool presseduint16_t time}
}
?? 鍵盤初始化流程
鍵盤初始化過程分為三個主要階段:
- 鍵盤預初始化:
keyboard_pre_init_*
- 在大多數功能初始化前執行,適合進行早期硬件設置 - 矩陣初始化:
matrix_init_*
- 在固件啟動過程中期執行 - 鍵盤后初始化:
keyboard_post_init_*
- 在固件啟動過程結束時執行,適合放置"自定義"代碼
?? 注意:對于大多數用戶,
keyboard_post_init_user
是你想要實現的函數。例如,這是設置RGB底光的理想位置。
鍵盤預初始化示例
void keyboard_pre_init_user(void) {// 調用鍵盤預初始化代碼// 設置LED引腳為輸出模式gpio_set_pin_output(B0);gpio_set_pin_output(B1);gpio_set_pin_output(B2);gpio_set_pin_output(B3);gpio_set_pin_output(B4);
}
鍵盤后初始化示例
void keyboard_post_init_user(void) {// 調用后初始化代碼rgblight_enable_noeeprom(); // 啟用RGB燈光但不保存設置rgblight_sethsv_noeeprom(180, 255, 255); // 設置青色但不保存rgblight_mode_noeeprom(RGBLIGHT_MODE_BREATHING + 3); // 設置快速呼吸模式但不保存
}
🔄 矩陣掃描與內務管理
矩陣掃描
矩陣掃描函數在每次按鍵矩陣掃描時被調用,頻率非常高。需要謹慎編寫這部分代碼以避免影響鍵盤性能。
void matrix_scan_user(void) {// 這里的代碼將以MCU能處理的最高頻率運行,請謹慎添加代碼
}
鍵盤內務管理
內務管理函數在所有QMK處理結束后、開始下一次迭代前調用:
void housekeeping_task_user(void) {// 此時所有按鍵處理、層狀態更新、USB報告發送、LED更新等已完成
}
下面是一個使用housekeeping_task_user
實現RGB燈光超時關閉的示例:
#define RGBLIGHT_SLEEP // 在keymap.c中啟用rgblight_suspend()和rgblight_wakeup()
#define RGBLIGHT_TIMEOUT 900000 // RGB超時時間,900K毫秒即15分鐘static uint32_t key_timer; // 最后鍵盤活動的計時器
static void refresh_rgb(void); // 刷新活動計時器和RGB
static void check_rgb_timeout(void); // 檢查RGB是否超時
bool is_rgb_timeout = false; // 存儲RGB是否已超時void refresh_rgb(void) {key_timer = timer_read32(); // 存儲最后刷新時間if (is_rgb_timeout){is_rgb_timeout = false;rgblight_wakeup();}
}void check_rgb_timeout(void) {if (!is_rgb_timeout && timer_elapsed32(key_timer) > RGBLIGHT_TIMEOUT) {rgblight_suspend();is_rgb_timeout = true;}
}/* 在QMK內置的后處理函數中調用上述函數 */
void housekeeping_task_user(void) {
#ifdef RGBLIGHT_TIMEOUTcheck_rgb_timeout();
#endif
}/* 每次按鍵后檢查是否有活動 */
void post_process_record_user(uint16_t keycode, keyrecord_t *record) {
#ifdef RGBLIGHT_TIMEOUTif (record->event.pressed)refresh_rgb();
#endif
}/* 每次旋鈕更新后檢查是否有活動 */
void post_encoder_update_user(uint8_t index, bool clockwise) {
#ifdef RGBLIGHT_TIMEOUTrefresh_rgb();
#endif
}
💤 鍵盤休眠與喚醒
如果你的鍵盤支持,可以通過以下函數控制鍵盤休眠狀態:
void suspend_power_down_user(void) {// 鍵盤休眠時運行的代碼,會被多次調用
}void suspend_wakeup_init_user(void) {// 鍵盤喚醒時運行的代碼
}
🔌 鍵盤關機與重啟
當固件重置時(軟重置或跳轉到引導加載程序),會調用shutdown_*
函數:
bool shutdown_kb(bool jump_to_bootloader) {if (!shutdown_user(jump_to_bootloader)) {return false;}if (jump_to_bootloader) {// 準備跳轉到引導加載程序,設置紅色rgb_matrix_set_color_all(RGB_RED);} else {// 軟重置,關閉LEDrgb_matrix_set_color_all(RGB_OFF);}// 強制刷新緩沖區rgb_matrix_update_pwm_buffers();return true;
}
?? 延遲執行
QMK提供了延遲執行功能,可以在指定時間后執行回調。要啟用此功能,在rules.mk
中添加:
DEFERRED_EXEC_ENABLE = yes
延遲執行回調函數示例:
uint32_t my_callback(uint32_t trigger_time, void *cb_arg) {/* 執行一些操作 */bool repeat = my_deferred_functionality();return repeat ? 500 : 0; // 如果需要重復,返回500毫秒延遲,否則返回0
}
注冊延遲執行:
deferred_token my_token = defer_exec(1500, my_callback, NULL);
📊 Keymap概述
鍵盤映射和圖層
QMK中的鍵盤映射保存在const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS]
數組中,最多可以定義32個圖層(0-31),較高的圖層具有優先權。
Keymap: 32 Layers Layer: action code matrix
----------------- ---------------------
stack of layers array_of_action_code[row][column]____________ precedence _______________________/ / | high / ESC / F1 / F2 / F3 ....31 /___________// | /-----/-----/-----/-----30 /___________// | / TAB / Q / W / E ....29 /___________/ | /-----/-----/-----/-----: _:_:_:_:_:__ | : /LCtrl/ A / S / D ....: / : : : : : / | : / : : : :2 /___________// | 2 `--------------------------1 /___________// | 1 `--------------------------0 /___________/ V low 0 `--------------------------
鍵盤映射層狀態由兩個32位參數決定:
default_layer_state
- 表示基本鍵盤映射層(0-31)layer_state
- 在其位中保存每個圖層的開/關狀態
圖層優先級和透明度
較高層在圖層堆棧中具有更高優先級。固件從最高活動層向下工作以查找鍵碼。一旦找到非透明鍵碼,就停止搜索。
如果在高層使用透明鍵碼(KC_TRNS
、_______
或KC_TRANSPARENT
),則會使用下層對應位置的鍵碼。
Keymap.c文件解析
一個典型的keymap.c
文件包含兩個主要部分:
- 定義(包括自定義鍵碼、圖層名稱等)
- 圖層/鍵盤映射數據結構
定義部分示例:
#include QMK_KEYBOARD_H// 有用的定義
#define GRAVE_MODS (MOD_BIT(KC_LSFT)|MOD_BIT(KC_RSFT)|MOD_BIT(KC_LGUI)|MOD_BIT(KC_RGUI)|MOD_BIT(KC_LALT)|MOD_BIT(KC_RALT))// 每個圖層都有一個名稱以提高可讀性
enum layer_names {_BL, // 基礎層_FL, // 功能層_CL, // 控制層
};
圖層定義示例(基礎層):
[_BL] = LAYOUT(F(0), KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_GRV, KC_BSPC, KC_PGUP,KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGDN,KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_NUHS, KC_ENT,KC_LSFT, KC_NUBS, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_INT1, KC_RSFT, KC_UP,KC_LCTL, KC_LGUI, KC_LALT, KC_INT5, KC_SPC,KC_SPC, KC_INT4, KC_RALT, KC_RCTL, MO(_FL), KC_LEFT, KC_DOWN, KC_RGHT
),
功能層示例:
[_FL] = LAYOUT(KC_GRV, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, KC_DEL, BL_STEP,_______, _______, _______,_______,_______,_______,_______,_______,KC_PSCR,KC_SCRL, KC_PAUS, _______, _______, _______, _______,_______, _______, MO(_CL),_______,_______,_______,_______,_______,_______,_______, _______, _______, _______, _______,_______, _______, _______,_______,_______,_______,_______,_______,_______,_______, _______, _______, _______, _______, KC_PGUP,_______, _______, _______, _______, _______,_______, _______, _______, _______, MO(_FL), KC_HOME, KC_PGDN, KC_END
),
🔍 拓展知識
1. 社區模塊
社區模塊是QMK的擴展功能,允許第三方實現代碼供他人導入。要添加社區模塊到你的構建中,在keymap.json
中添加:
{"modules": ["qmk/hello_world"]
}
2. EEPROM持久配置
QMK可以使用EEPROM存儲長期保持的配置,如默認圖層、RGB燈效設置等。這使得設置可以在斷電后保持。
3. Bootloader驅動安裝
在Windows上刷寫鍵盤固件時,有時需要為bootloader安裝特殊驅動程序。推薦使用Zadig工具安裝正確的驅動。
4. QMK鍵碼類型
QMK支持多種類型的鍵碼:
- 基本鍵碼:如
KC_A
、KC_ENTER
等 - 修飾鍵:如
KC_LSHIFT
、KC_RALT
等 - 圖層切換:如
MO()
、TG()
、TO()
等 - 一鍵多用(Mod-Tap):如
LCTL_T(KC_ESC)
等 - 宏鍵:用戶自定義的復雜功能
📝 總結
QMK提供了極其強大且靈活的鍵盤自定義能力,從簡單的鍵位重映射到復雜的宏和功能。通過本文介紹的技術,你可以:
- 創建自定義鍵碼并定義其行為
- 設置多層鍵盤映射實現不同功能
- 利用鍵盤初始化生命周期自定義鍵盤啟動行為
- 使用矩陣掃描和內務管理函數添加特殊功能
- 實現節能休眠和喚醒功能
- 利用延遲執行實現定時任務
無論你是想要一個簡單的QWERTY布局還是構建一個復雜的編程工作站,QMK都能滿足你的需求。開始你的鍵盤自定義之旅吧!
🔗 相關資源
- QMK官方文檔
- QMK鍵碼列表
- QMK Keymap常見問題
- QMK社區
如果你對如何改進本文有任何建議,歡迎提交Issue!我們正在積極努力改進這些文檔。