十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

QMK 固件键位映射(keymap)完全解析:从 keymap.c 到图层优先级与透明键机制

QMK 固件键位映射(keymap)完全解析:从 keymap.c 到图层优先级与透明键机制 QMK 固件键位映射keymap完全解析从 keymap.c 到图层优先级与透明键机制【免费下载链接】qmk_firmwareOpen-source keyboard firmware for Atmel AVR and Arm USB families项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmware在 QMK 键盘固件中键位映射keymap是决定按下物理按键 → 触发什么动作的核心配置层。本篇以仓库中的 keymap 概览文档 为主体完整讲解 keymap 的数组结构、keymaps[]数据结构的 16 位 action code 原理、default_layer_state与layer_state双状态模型、图层优先级与透明键KC_TRNS的查找机制并结合 quantum/action_layer.c 与 quantum/action_layer.h 的源码印证每一处结论。读完本文你将能够独立编写keymap.c、设计基础层/功能层并理解固件按下键位时如何逐层查找最终动作码。一、keymap 的基本形态数组的数组QMK 的键位映射定义在一个 C 源文件中。其数据结构是数组的数组外层数组是一组图层layer数组内层每个图层数组则是一张按键列表。绝大多数键盘都会定义一个LAYOUT()宏来辅助你构造这个数组的数组由它负责把平铺的键码列表映射到硬件的行列扫描矩阵上。keymap.c文件通常由两大块组成定义区Definitions头文件包含、修饰键位掩码等自定义宏、图层名称枚举图层/键位数据区Layers and Keymapskeymaps[]数组本体逐层列出每个物理按键的键码。二、keymaps[]数据结构16 位 action code 与 32 个图层在 QMK 中const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS]以16 位数据承载每个按键的action code动作码用来存放多个图层的键位信息最多可定义32 个图层。对于普通的按键定义action code 的高 8 位全为 0低 8 位则是该按键产生的 USB HID usage code即keycode。文档中特别说明由于 TMK 的历史原因部分文档会把存储在 keymap 中的 action code 也称作 keycode。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 --------------------------两个要点各图层可以同时处于有效状态Respective layers can be validated simultaneously图层索引为 031编号越高的图层优先级越高。一个值得注意的细节keymaps[][MATRIX_ROWS][MATRIX_COLS]承载的是 16 位 action code有时被称为 quantum keycode。而 QMK 的前身 TMK 使用的是const uint16_t之前的const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS]只存 8 位 keycode——这是阅读老资料时容易混淆的地方。从源码结构看32 层的上限并非硬性常量而是由层状态位宽的默认配置决定。quantum/action_layer.h 中通过LAYER_STATE_8BIT / LAYER_STATE_16BIT / LAYER_STATE_32BIT三档配置切换layer_state_t的类型默认采用LAYER_STATE_16BIT对应MAX_LAYER 16开启LAYER_STATE_32BIT时MAX_LAYER才达到 32对应文档中最多 32 层的描述。另外若启用了DYNAMIC_KEYMAP_ENABLE如 VIA 动态改键还会根据DYNAMIC_KEYMAP_LAYER_COUNT自动收窄层数上限。三、Keymap 图层状态default_layer_state与layer_stateKeymap 的图层状态由两个 32 位参数共同决定default_layer_state指示一个基础键位层0-31该层始终有效且会被优先引用即 default layerlayer_state以位掩码形式记录当前各个图层的开/关状态。通常 keymap 图层 0 被用作default_layer其余图层在固件启动后处于关闭状态——尽管这一点可以在config.h中另行配置。当你需要整体切换键位布局时例如从 Qwerty 换成 Colemak修改的就是default_layerInitial state of Keymap Change base layout ----------------------- ------------------ 31 31 30 30 29 29 : : : : ____________ 2 ____________ 2 / / 1 / / ,-1 /___________/ ,-0 /___________/ | 0 || | --- default_layer 0 --- default_layer 1 layer_state 0x00000001 layer_state 0x00000002另一方面你可以通过修改layer_state把其他图层叠加overlay到基础层之上用于导航键、功能键F1-F12、媒体键和特殊动作等场景。例如在default_layer 1的基础上叠加第 1、29、30 层Overlay feature layer --------------------- bit|status ____________ --------- 31 / / 31 | 0 30 /___________// ----- 30 | 1 29 /___________// ----- 29 | 1 : : | : : ____________ : | : 2 / / 2 | 0 ,-1 /___________// ----- 1 | 1 || 0 0 | 0 || | --- default_layer 1 | layer_state 0x60000002 -这一模型在源码中直接可见quantum/action_layer.h 声明了全局的extern layer_state_t default_layer_state;与extern layer_state_t layer_state;并提供default_layer_set()、layer_on()/layer_off()/layer_move()/layer_invert()、layer_or()/layer_and()/layer_xor()等位运算接口。实际按键时固件将两者合并为layer_state | default_layer_state再逐层查找——见下文 quantum/action_layer.c 第 347 行。四、图层优先级与透明键KC_TRNS的工作方式在图层栈中更高层具有更高优先级。固件从当前最高的有效图层向下查找键码一旦在某个有效图层上找到非KC_TRNStransparent透明的键码查找立即停止不再引用更低的图层。____________ / / --- Higher layer / KC_TRNS // /___________// --- Lower layer (KC_A) /___________/在这种情形下高图层上的非透明键均可正常触发而只要该位置写的是KC_TRNS或等价表示就会落到低图层采用低图层上的键码上例中为KC_A。注意在给定图层上表示透明的有效写法有三种KC_TRANSPARENTKC_TRNS别名_______别名这些键码让固件的处理落穿到更低图层继续寻找一个非透明键码。这个从高到低落穿的过程在源码中就是 quantum/action_layer.c 的layer_switch_get_layer()layer_state_t layers layer_state | default_layer_state; /* check top layer first */ for (int8_t i MAX_LAYER - 1; i 0; i--) { if (layers ((layer_state_t)1 i)) { action action_for_key(i, key); if (action.code ! ACTION_TRANSPARENT) { return i; } } } /* fall back to layer 0 */ return 0;可以看到合并后的位掩码layers从MAX_LAYER - 1向 0 扫描第一个非ACTION_TRANSPARENT的位置即胜出若全部透明则回落到图层 0。随后layer_switch_get_action()用该图层取回最终 action供 quantum/action.c 的按下/释放事件处理使用。五、解剖一个keymap.c以 Clueboard 66% 为例上面是基本骨架。下面用仓库中 Clueboard 66 的默认键位目录见 keyboards/clueboard/66/keymaps/default/可按需打开对照走一遍keymap.c的两个主要部分。5.1 定义区Definitions文件开头通常是这样#include QMK_KEYBOARD_H // Helpful defines #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)) /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * You can use _______ in place for KC_TRNS (transparent) * * Or you can use XXXXXXX for KC_NO (NOOP) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Each layer gets a name for readability. // The underscores dont mean anything - you can // have a layer called STUFF or any other name. // Layer names dont all need to be of the same // length, and you can also skip them entirely // and just use numbers. enum layer_names { _BL, _FL, _CL, };这些是为构建键位和自定义函数准备的便利定义GRAVE_MODS定义了一个同时按下 Shift/Gui/Alt 任一侧的修饰掩码通常用于反引号键的多用途函数例如切换 Colemak 层_BL、_FL、_CL等枚举让引用各个图层时更具可读性。图层名不必等长、也不必以下划线开头甚至可以完全省略、直接用数字索引。文档特别提到一些较老的 keymap 文件里会额外#define _______和/或XXXXXXX分别替代KC_TRNS与KC_NO方便目视识别该层覆盖了哪些键。这些定义如今已不再必要因为固件已默认提供。5.2 图层与键位Layers and Keymaps文件主体是keymaps[]定义这是列出各图层及其内容的位置const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] {之后便是各图层定义。典型做法是先放一个或多个基础层QWERTY、Dvorak、Colemak 等再在其上叠加一个或多个功能层。由于图层的处理方式你不能把一个低层覆盖到一个高层之上。基础层Base LayerClueboard 基础层示例[_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 ),几个值得注意的点图层通过LAYOUT宏定义该宏传统上定义在键盘自己的.h文件中LAYOUT宏接收一个单一的键码列表只是在 C 源码中用空白和换行排版成与物理键盘一致的行列便于对照实物LAYOUT宏隐藏并处理了到硬件按键扫描矩阵[row][col]的映射你不需要手写行列索引普通键盘扫描码以KC_前缀书写特殊键码则不加前缀左上角的键触发自定义函数 0F(0)对应的处理逻辑写在文件底部的keyboard_matrix_scan_user()/ 自定义函数中Fn 键用MO(_FL)定义按住期间切换到_FL层松开即回到原图层。功能叠加层Function Overlay Layer功能层在代码层面与基础层没有任何区别区别只在概念上它是叠加overlay而不是替换replacement。对简单配置而言这无所谓但层叠结构越复杂这个区分越重要。[_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 ),要点使用_______表示KC_TRNS便于快速辨认这一层改变了哪些键处于该层时按下任意_______位置的键都会触发下一个更低的有效层上的键即第四节所述的透明落穿机制。六、源码纵深图层切换的三个补充机制在文档骨架之外quantum/action_layer.c 还有三个与图层机制直接相关、值得了解的实现细节动作缓存防止修饰键卡死。store_or_get_action()quantum/action_layer.c在按下事件时缓存该键实际取自哪个图层释放事件时复用同一图层解析动作。注释里说得很直白若按下之后、释放之前发生了图层切换修饰键可能因此卡住。定义STRICT_LAYER_RELEASE可关闭缓存、每次直接重新解析。Tri Layer三层联动。update_tri_layer(layer1, layer2, layer3)quantum/action_layer.c实现第 1 层和第 2 层同时激活时自动点亮第 3 层的位运算逻辑常用于左右 Ctrl 同时触发特殊层等场景。layer_state位宽可配置。如前所述layer_state_t默认 16 位16 层可通过LAYER_STATE_8BIT/16BIT/32BIT在 quantum/action_layer.h 中调整文档中最多 32 层的表述对应的就是 32 位配置下的上限。七、延伸阅读本文覆盖了创建自定义 keymap 的基本骨架以下仓库内文档可以继续深入键码参考Keycodes全部基础/进阶键码及其十六进制编码Keymap FAQkeymap 常见问题的官方解答图层机制Feature: LayersMO/DF/LL等图层相关特性的完整说明源码入口quantum/action_layer.h图层状态与接口声明、quantum/action_layer.c图层切换与查找实现、quantum/keycodes.h 与 quantum/quantum_keycodes.h键码与进阶键码定义。【免费下载链接】qmk_firmwareOpen-source keyboard firmware for Atmel AVR and Arm USB families项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmware创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表