当前位置: 首页 > ds >正文

QMK键盘固件自定义指南 - 打造你的专属键盘体验

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
};

通过这段代码,你可以在键盘映射中使用FOOBAR两个自定义键码。

编程键码行为

要控制键码的行为,你需要使用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}
}

⚙️ 键盘初始化流程

键盘初始化过程分为三个主要阶段:

  1. 键盘预初始化keyboard_pre_init_* - 在大多数功能初始化前执行,适合进行早期硬件设置
  2. 矩阵初始化matrix_init_* - 在固件启动过程中期执行
  3. 键盘后初始化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文件包含两个主要部分:

  1. 定义(包括自定义键码、图层名称等)
  2. 图层/键盘映射数据结构

定义部分示例:

#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_AKC_ENTER
  • 修饰键:如KC_LSHIFTKC_RALT
  • 图层切换:如MO()TG()TO()
  • 一键多用(Mod-Tap):如LCTL_T(KC_ESC)
  • 宏键:用户自定义的复杂功能

📝 总结

QMK提供了极其强大且灵活的键盘自定义能力,从简单的键位重映射到复杂的宏和功能。通过本文介绍的技术,你可以:

  1. 创建自定义键码并定义其行为
  2. 设置多层键盘映射实现不同功能
  3. 利用键盘初始化生命周期自定义键盘启动行为
  4. 使用矩阵扫描和内务管理函数添加特殊功能
  5. 实现节能休眠和唤醒功能
  6. 利用延迟执行实现定时任务

无论你是想要一个简单的QWERTY布局还是构建一个复杂的编程工作站,QMK都能满足你的需求。开始你的键盘自定义之旅吧!

🔗 相关资源

  • QMK官方文档
  • QMK键码列表
  • QMK Keymap常见问题
  • QMK社区

如果你对如何改进本文有任何建议,欢迎提交Issue!我们正在积极努力改进这些文档。

http://www.xdnf.cn/news/4783.html

相关文章:

  • Python开发之os.path的常用操作
  • Vim 编辑器常用快捷键速查表
  • 排序算法——基数排序
  • 函数级重构:如何写出高可读性的方法?
  • 生产型机器学习系统:静态训练与动态训练的权衡与实践
  • mobile自动化测试-appium webdriverio
  • element-ui form 组件源码分享
  • 2025.5.8总结(中期审视)
  • JAVA多线程进阶
  • 第五十四篇 AI与数据分析
  • 推测式思维树:让大模型快速完成复杂推理
  • 针对共享内存和上述windows消息机制 在C++ 和qt之间的案例 进行详细举例说明
  • Android7 Input(六)InputChannel
  • OpenHarmony平台驱动开发(九),MIPI DSI
  • Rust 开发环境搭建与插件
  • MySQL报错解决过程
  • 39、.NET GC是什么? 为什么需要GC?
  • # 如何使用OpenCV进行发票的透视变换和二值化处理
  • EasyRTC嵌入式音视频通话SDK驱动智能硬件音视频应用新发展
  • 集成变压器的网口的PCB设计
  • 【MySQL】存储引擎 - MEMORY详解
  • 【JS逆向基础】前端基础-HTML与CSS
  • 初学者的AI智能体课程:构建AI智能体的十堂课
  • SpringBoot 讯飞星火AI WebFlux流式接口返回 异步返回 对接AI大模型 人工智能接口返回
  • oracle 对一个字段的数据做排序 :值的依次排序为.“思考”->“asd”->“三点“
  • MySQL 8.0 OCP 英文题库解析(一)
  • Web开发-JavaEE应用SpringBoot栈ActuatorSwaggerHeapDump提取自动化
  • 【Bluedroid】 HID 设备应用注册与主机服务禁用流程源码解析
  • SpringBoot项目接入DeepSeek
  • 「Mac畅玩AIGC与多模态24」开发篇20 - 多语言输出工作流示例