QMK 固件中的 Programmable Button:HID 可编程按键的实现原理、Keycode 与 API 全解

发布时间:2026/9/14 13:23:53
QMK 固件中的 Programmable Button:HID 可编程按键的实现原理、Keycode 与 API 全解 QMK 固件中的 Programmable ButtonHID 可编程按键的实现原理、Keycode 与 API 全解【免费下载链接】qmk_firmwareOpen-source keyboard firmware for Atmel AVR and Arm USB families项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmware本文基于 QMK Firmware 官方特性文档 programmable_button.md 撰写系统讲解 QMK 中可编程按钮Programmable Button特性的定位、启用方式、32 个专用 keycode 的完整清单以及底层报告位掩码的实现细节与全部 9 个 C API 函数。读完后你能够理解这类按键为何可以绕过操作系统的 HID 解释、如何在rules.mk中启用该特性并能直接调用源码级 API 在用户空间代码中自由操控按键状态。什么是 Programmable ButtonProgrammable Button可编程按钮是没有预定义含义的按键。它们不会像普通 HID 键盘按键那样被操作系统按固定用途解释而是由主机端的自定义软件自行处理——操作系统不会尝试对它们做任何内置语义的解读。这类按键码依据 HID Telephony Device 页面0x0B下的 Programmable Button 用途usage0x09发出。文档给出了明确的主机侧支持现状Linux内核 5.14 及以上由系统自动处理并被翻译为KEY_MACRO#键码最多到KEY_MACRO30Windows / macOS目前没有已知支持。理论上可以编写自定义 HID 驱动来接收这些 usage但这已超出 QMK 文档的范围。从源码结构看这也解释了为什么该功能在 process 处理器 中不产生任何字符或修饰键动作——固件只负责把按键状态位放进一个独立的位掩码报告通过专用的host_programmable_button_send()通道发出完全游离于标准键盘 HID 报告之外。启用特性rules.mk 配置在该键盘的rules.mk中添加以下内容即可启用PROGRAMMABLE_BUTTON_ENABLE yes启用后编译系统会引入 quantum/programmable_button.c 实现文件与对应的 keycode 处理链路QK_PROGRAMMABLE_BUTTON_1至QK_PROGRAMMABLE_BUTTON_32这组常量也随之可用。Keycode 清单32 个0x7440–0x745F以下 32 个 keycode 完整继承自官方文档。它们在 QMK 数据驱动的 keycode 定义文件 keycodes_0.0.1_programmable_button.hjson 中依次映射到十六进制值0x7440至0x745F每个条目均带有PB_N别名Key别名十六进制值说明QK_PROGRAMMABLE_BUTTON_1PB_10x7440可编程按钮 1QK_PROGRAMMABLE_BUTTON_2PB_20x7441可编程按钮 2QK_PROGRAMMABLE_BUTTON_3PB_30x7442可编程按钮 3QK_PROGRAMMABLE_BUTTON_4PB_40x7443可编程按钮 4QK_PROGRAMMABLE_BUTTON_5PB_50x7444可编程按钮 5QK_PROGRAMMABLE_BUTTON_6PB_60x7445可编程按钮 6QK_PROGRAMMABLE_BUTTON_7PB_70x7446可编程按钮 7QK_PROGRAMMABLE_BUTTON_8PB_80x7447可编程按钮 8QK_PROGRAMMABLE_BUTTON_9PB_90x7448可编程按钮 9QK_PROGRAMMABLE_BUTTON_10PB_100x7449可编程按钮 10QK_PROGRAMMABLE_BUTTON_11PB_110x744A可编程按钮 11QK_PROGRAMMABLE_BUTTON_12PB_120x744B可编程按钮 12QK_PROGRAMMABLE_BUTTON_13PB_130x744C可编程按钮 13QK_PROGRAMMABLE_BUTTON_14PB_140x744D可编程按钮 14QK_PROGRAMMABLE_BUTTON_15PB_150x744E可编程按钮 15QK_PROGRAMMABLE_BUTTON_16PB_160x744F可编程按钮 16QK_PROGRAMMABLE_BUTTON_17PB_170x7450可编程按钮 17QK_PROGRAMMABLE_BUTTON_18PB_180x7451可编程按钮 18QK_PROGRAMMABLE_BUTTON_19PB_190x7452可编程按钮 19QK_PROGRAMMABLE_BUTTON_20PB_200x7453可编程按钮 20QK_PROGRAMMABLE_BUTTON_21PB_210x7454可编程按钮 21QK_PROGRAMMABLE_BUTTON_22PB_220x7455可编程按钮 22QK_PROGRAMMABLE_BUTTON_23PB_230x7456可编程按钮 23QK_PROGRAMMABLE_BUTTON_24PB_240x7457可编程按钮 24QK_PROGRAMMABLE_BUTTON_25PB_250x7458可编程按钮 25QK_PROGRAMMABLE_BUTTON_26PB_260x7459可编程按钮 26QK_PROGRAMMABLE_BUTTON_27PB_270x745A可编程按钮 27QK_PROGRAMMABLE_BUTTON_28PB_280x745B可编程按钮 28QK_PROGRAMMABLE_BUTTON_29PB_290x745C可编程按钮 29QK_PROGRAMMABLE_BUTTON_30PB_300x745D可编程按钮 30QK_PROGRAMMABLE_BUTTON_31PB_310x745E可编程按钮 31QK_PROGRAMMABLE_BUTTON_32PB_320x745F可编程按钮 32这些 keycode 可以像普通 key 一样直接写进 keymap 中例如把某个不常用键位映射为PB_1从而让主机侧软件接收一个纯粹的按钮 1 按下事件。源码级解析keycode 到报告位掩码的完整链路处理入口keycode 的实际处理逻辑集中在 process_programmable_button.cbool process_programmable_button(uint16_t keycode, keyrecord_t *record) { if (IS_QK_PROGRAMMABLE_BUTTON(keycode)) { uint8_t button keycode - QK_PROGRAMMABLE_BUTTON 1; if (record-event.pressed) { programmable_button_register(button); } else { programmable_button_unregister(button); } } return true; }这段代码揭示了三个关键机制范围判定IS_QK_PROGRAMMABLE_BUTTON(keycode)判断 keycode 是否落在0x7440–0x745F区间非该区间直接放行索引换算keycode - QK_PROGRAMMABLE_BUTTON 1把QK_PROGRAMMABLE_BUTTON_10x7440换算为按钮编号 1即PB_N对应编号 N1–32状态更新按下时调用programmable_button_register()置位并刷新报告松开时调用programmable_button_unregister()清零并刷新报告。函数返回true表示该 keycode 已完全由本处理器消费不会再向后续处理环节传播。位掩码报告的底层实现核心状态保存在 quantum/programmable_button.c 的静态变量中#define REPORT_BIT(index) (((uint32_t)1) (index - 1)) static uint32_t programmable_button_report 0;整个特性用一个uint32_t位掩码表达 32 个按钮的状态按钮 N 对应 bitN-1即PB_1是 bit 0PB_32是 bit 31。每次状态变化最终都通过host_programmable_button_send(programmable_button_report)把整份 32 位报告发往主机。这里有一个值得注意的细节文档 API 部分将index描述为 from 0 to 31与源码头文件注释一致但从REPORT_BIT宏的index - 1位移方式以及 keycode 换算逻辑看实际有效的按钮编号是 1–32分别对应PB_1–PB_32。编写用户空间代码时应按 keycode 序号1 起传参。完整 API 参考以下 9 个函数声明于 quantum/programmable_button.h实现在 quantum/programmable_button.c是用户空间user space代码操控可编程按钮的全部接口。void programmable_button_clear(void)清除整个可编程按钮报告。实现上先把programmable_button_report置 0再立即调用flush()发送全零报告即一次性松开所有按钮。void programmable_button_add(uint8_t index)置位指定按钮的状态模拟按下但不刷新报告——只是把REPORT_BIT(index)或入位掩码需要后续显式调用flush()才会发往主机。参数index要按下的按钮编号文档定义为 0–31源码实际按PB_1–PB_32的序号 1–32 使用。void programmable_button_remove(uint8_t index)清零指定按钮的状态模拟松开同样不刷新报告。参数index要松开的按钮编号取值范围同上。void programmable_button_register(uint8_t index)置位按钮状态并立即刷新报告是模拟按下最直接的调用方式keycode 按下事件内部使用的就是它。参数index要按下的按钮编号。void programmable_button_unregister(uint8_t index)清零按钮状态并立即刷新报告用于模拟松开。参数index要松开的按钮编号。bool programmable_button_is_on(uint8_t index)查询指定按钮的当前状态返回true表示该按钮处于按下状态。参数index要查询的按钮编号。void programmable_button_flush(void)把当前位掩码报告整体发送到主机。add/remove这类只改状态不发送的函数必须搭配它使用适合先批量修改多个按钮状态、再一次性上报的场景。uint32_t programmable_button_get_report(void)返回当前的完整报告即 32 个按钮状态的位掩码可用于检查多个按钮的组合状态或做整块复制。void programmable_button_set_report(uint32_t report)用一个位掩码整体替换当前报告。参数report即为 32 个按钮状态的位图常用于一次性设置多按键组合。刷新语义小结API 按是否立即上报分为两档add/remove/set_report只修改本地位掩码register/unregister/clear/flush会触发向主机的实际发送。理解这一区别是正确使用该 API 的前提——若只做add()而不调flush()主机侧不会收到任何事件。适用场景与限制适用主机侧有自定义 HID 软件如自定义驱动、宏脚本框架的场景可把键盘上的某些键位变成纯信号按钮语义完全由主机软件定义Linux 5.14 环境下无需额外驱动即可被翻译为KEY_MACRO#键码。限制Windows 与 macOS 目前无已知支持在 Linux 上可映射的KEY_MACRO编号上限为 30而固件侧提供 32 个按钮位。该特性与 QMK 其他 HID 增强特性相互独立启用仅依赖rules.mk中的一行配置不涉及额外硬件。【免费下载链接】qmk_firmwareOpen-source keyboard firmware for Atmel AVR and Arm USB families项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmware创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询