QMK 固件中的 Binepad NEOKNOB KN01:多功能旋钮设备的构建、配列与编码器实现全解
【免费下载链接】qmk_firmwareOpen-source keyboard firmware for Atmel AVR and Arm USB families项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmware
Binepad NEOKNOB KN01 是一款单键多功能旋钮设备:它可以旋转、按压,还可以在按压的同时旋转。本文以 QMK 固件仓库中该键盘的官方文档与配置为骨架,结合keyboards/binepad/kn01/下的真实配置和底层编码器/Bootmagic 驱动源码,完整讲解 KN01 的构建烧录流程、逐层编码器映射(encoder_map)配置,以及旋钮背后的正交编码器解码原理,读完后可独立完成此类编码器类小设备的 QMK 开发。
一、设备概览:一台“一个键”的设备
KN01 文档定义了这台设备的三种交互形态:旋转(rotate)、按压(press)、按压时旋转(rotate while pressed)。这三种形态分别对应三类输入源:
- 编码器(encoder):负责旋转与“按压时旋转”两种行为,由
ENCODER_ENABLE驱动解码; - 键矩阵(key matrix):负责按压行为,KN01 只有一个矩阵开关;
- 组合映射:文档给出的默认配列中,“按压时旋转”通过
LT(layer tap)把按键行为与编码器所在层联动实现。
1.1 keyboard.json 中的硬件事实
boards 定义文件 keyboards/binepad/kn01/keyboard.json 是 QMK 数据驱动的键盘描述,KN01 的关键字段如下:
| 配置项 | 取值 | 含义 |
|---|---|---|
processor | STM32F103 | 主控为 STM32F103 系列 ARM MCU |
bootloader | stm32duino | 使用 STM32 DFU 官方引导加载器,刷机时进入 DFU 模式 |
matrix_pins | cols: ["A15"],rows: ["A8"] | 单行单列键矩阵,仅 1 个物理按键 |
diode_direction | COL2ROW | 二极管方向为“列到行” |
encoder.rotary | pin_a: "B3",pin_b: "B4" | 旋转编码器 A/B 相引脚 |
features | bootmagic、encoder、extrakey、mousekey | 启用的固件特性 |
usb.vid/usb.pid | 0x4249/0x4040 | 该设备的 USB 厂商号/产品号 |
layouts | LAYOUT_ortho_1x1 | 布局为 1×1 ortho,占位 2×2 |
几个值得注意的点:
- 单键设备也走完整的矩阵扫描:
A8(行)与A15(列)构成 1×1 矩阵,QMK 的通用矩阵扫描器无需特判即可工作; bootmagic: true是“按住旋钮再插 USB 进 DFU”这一刷机方式的固件基础(见第四节);extrakey与mousekey的开启解释了默认配列为什么能使用KC_VOLD(多媒体音量键)和MS_WHLU/MS_WHLD(鼠标滚轮)这类非普通字母键码;community_layouts: ["ortho_1x1"]表明该布局与 QMK 社区标准 ortho_1x1 布局兼容,方便在 Web 配列工具中直接渲染。
维护者信息(maintainer 为 Binpad)与硬件销售渠道均记载于 readme.md。
二、构建与烧录
文档给出的两条核心命令(在配置好 QMK 构建环境之后):
# 编译默认配列固件 make binepad/kn01:default # 编译并烧录 make binepad/kn01:default:flash其中binepad/kn01即仓库内 keyboards/binepad/kn01/ 的路径,default对应 keymaps/default/keymap.json 这个数据驱动配列。
由于bootloader配置为stm32duino,烧录流程为:先进入 STM32 DFU 模式(三种进入方式见第四节),设备以 USB DFU 设备呈现,随后 make 目标调用dfu-util完成写 Flash。固件的 USB 标识为 VID0x4249/ PID0x4040(device_version1.0.0),在 Linux 下可据此编写 udev 规则匹配该设备。
构建环境搭建可参考 QMK 官方文档中的 build tools 与 make guide 章节(见仓库文档 getting_started_make_guide.md),初学者入口为 newbs 指南。
三、默认配列:一个按键如何同时承担音量与滚轮
3.1 keymap.json 的完整配置
KN01 的默认配列采用 JSON 数据驱动格式,完整内容见 keyboards/binepad/kn01/keymaps/default/keymap.json,其结构可拆为四部分:
{ "config": { "features": { "encoder_map": true } }, "encoders": [ [ { "ccw": "KC_VOLD", "cw": "KC_VOLU" } ], [ { "ccw": "MS_WHLD", "cw": "MS_WHLU" } ] ], "layers": [ ["LT(1, KC_MUTE)"], ["_______"] ], "layout": "LAYOUT_ortho_1x1", "version": 1 }config.features.encoder_map: true:启用逐层编码器映射。开启后encoders数组按层组织,每个层的每个编码器条目独立指定顺时针(cw)与逆时针(ccw)行为;layers[0]:唯一键位为LT(1, KC_MUTE)—— 单击触发KC_MUTE(静音/取消静音),同时按住时切换到 Layer 1;layers[1]:占位符_______,即 Layer 1 上不覆盖任何普通按键,但编码器映射仍然生效;- 组合效果:松手旋转 → 调节音量(
KC_VOLU/KC_VOLD);按住旋钮旋转 → 滚动鼠标滚轮(MS_WHLU/MS_WHLD)。这正是文档所说 “rotated, pressed, and rotated while pressed” 的第三种形态的落地方式。
3.2 逐层编码器映射的底层机制
encoder_map特性对应固件宏ENCODER_MAP_ENABLE。从 quantum/action_layer.c 源码结构看,QMK 在获取“当前层的编码器动作”时区分两种路径:未启用ENCODER_MAP_ENABLE时对所有层复用编码器默认映射;启用后,各层可以携带独立的编码器键码表,当前活动层切换后,同一物理旋钮的 cw/ccw 行为随之改变。ENCODER_MAP_ENABLE同时影响 quantum/action.c 中与换边(swap hands)相关的编码器处理逻辑。
这也解释了为什么 Layer 1 的_______不占用任何键位却依然能“拦截”旋转输入——旋钮行为根本不经过按键矩阵,而是由编码器映射层直接分发的。
四、编码器驱动:旋钮旋转如何变成事件
4.1 正交解码原理
旋钮由标准正交(quadrature)编码器构成,A/B 两相引脚(KN01 上为B3/B4)随旋转以 90° 相位差翻转。QMK 的默认编码器驱动实现在 drivers/encoder/encoder_quadrature.c:
- 引脚初始化(encoder_quadrature.c#L42-L55):默认实现将 A/B 相配置为带内部上拉的输入(
gpio_set_pin_input_high),随后encoder_wait_pullup_charge()等待 100µs 让上拉电容充电稳定,避免首读抖动; - 状态机(encoder_quadrature.c#L70-L73):每个编码器维护一个 4 位状态
encoder_state,记录最近两次 A/B 组合,配合查表encoder_LUT[16]得到 ±1/0 的方向增量,即经典的 Gray 码正交解码; - 分辨率(encoder_quadrature.c#L20-L22):
ENCODER_RESOLUTION默认值为 4,意味着积累 4 个增量(1 个完整电码周期)才通过encoder_queue_event()向上层报一次ENCODER_CLOCKWISE/ENCODER_COUNTER_CLOCKWISE事件,从而抑制单次误触发; - 方向翻转:定义
ENCODER_DIRECTION_FLIP可整体交换 cw/ccw 语义(encoder_quadrature.c#L63-L69),适配物理安装方向; - 轮询(encoder_quadrature.c#L207-L211):
encoder_driver_task()在主循环中周期性读取全部编码器引脚并推进状态机。
4.2 与 keymap.json 的衔接
驱动上报的 cw/ccw 事件被动作层查表为键码:默认分辨率下每转一格触发一次KC_VOLU或KC_VOLD,对应keymap.json中encoders[0]的定义。若用户觉得步进太粗或太细,可在配列的rules.mk中通过ENCODER_RESOLUTION调整——这是文档未展开、但源码直接支持的调参入口。
五、三种进入引导加载器(Bootloader)的方式
KN01 文档 列出的三种进 Bootloader 方式:
- Bootmagic reset:按住旋钮不松手,同时插入 USB 线;
- 物理复位键:短按 PCB 底面的复位按钮;
- 布局中的键码:若配列中存在映射到
QK_BOOT的键,按下即可。
5.1 Bootmagic 的源码实现
方式 1 由 quantum/bootmagic/bootmagic.c 支撑,keyboard.json中features.bootmagic: true使能了它。调用链如下:
- 固件启动后,quantum/keyboard.c#L443 在初始化阶段调用
bootmagic(); bootmagic_scan()(bootmagic.c#L63-L72)检查bootmagic_should_reset()——即上电时第一矩阵键(KN01 上就是旋钮本身)是否处于按下状态;- 判定为真时调用
bootmagic_reset_eeprom()(bootmagic.c#L35)清空 EEPROM 后复位,微控制器在复位窗口内由 DFU 固件接管,设备以stm32duino(STM32 DFU)身份枚举,等待make ...:flash写入。
5.2 QK_BOOT 键码
方式 3 依赖 QMK 内置的QK_BOOT键码:按下时固件直接复位进入 DFU 模式。KN01 默认配列并未占用它(唯一键位给了LT(1, KC_MUTE)),但若把keymap.json中的键改为QK_BOOT或增加一个映射,即可获得纯软件方式的进 Bootloader 手段——对只有单键的设备而言尤其实用,因为物理复位键(方式 2)位于 PCB 底部,日常操作不便。
六、小结与扩展方向
KN01 虽小,却覆盖了 QMK 中一类典型“非键盘”设备的完整链路:数据驱动的 keyboard.json 声明硬件、keymap.json 声明逐层行为、编码器驱动完成物理解码、Bootmagic/DFU 保证刷机可达。基于此,可以做三类常见扩展:
- 用
ENCODER_RESOLUTION调整旋转步进粒度; - 用
ENCODER_DIRECTION_FLIP修正安装方向导致的旋转方向相反; - 在更多层中定义不同的
encoders条目,把同一个旋钮在语音/媒体/导航等场景间复用——这正是encoder_map特性设计的目标用法。
【免费下载链接】qmk_firmwareOpen-source keyboard firmware for Atmel AVR and Arm USB families项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmware
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考