QMK Firmware 2026-02-22 破坏性变更解析:移除废弃 GPIO 定义与 isLeftHand,附完整变更清单
【免费下载链接】qmk_firmwareOpen-source keyboard firmware for Atmel AVR and Arm USB families项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmware
本文围绕 QMK Firmware 2026 年 2 月 22 日发布的破坏性变更(Breaking Changes)周期展开,核心讲解两个被彻底移除的废弃 API——Arduino 风格的 GPIO 宏定义和isLeftHand变量——的迁移方案与源码级原理,并完整梳理本次周期中 Core、CLI、Keyboards 与 Bug 修复的全部变更清单。读完后,你可以准确完成老代码向新 API 的迁移,理解 QMK GPIO 抽象层与 Split 键盘左右手判定机制的底层实现,并掌握该版本引入的 CLI 与构建系统改动。
一、变更背景:为什么 QMK 会做破坏性变更
QMK 的破坏性变更集中记录在 docs/ChangeLog 目录下的按日期命名的文件中,从20190830.md一直延续到20260531.md,本周期对应的文档即 docs/ChangeLog/20260222.md。QMK 采用季度节奏发布 Breaking Changes:先提供兼容性过渡期(deprecated 警告),再在后续周期正式移除。本周期移除的两个目标,正是之前几个周期中已发出弃用通知的旧接口,这符合 QMK 的弃用策略——过渡期结束后不再保留向后兼容层。
二、移除废弃的 GPIO 定义(PR #26028)
2.1 问题背景
QMK 长期使用 Arduino 风格的 GPIO 命名习惯(例如DDx、PORTx、PINx这类寄存器级宏)。随着平台演进,这类命名不断出现新的变体,也导致用户误以为 QMK 兼容整个 Arduino 生态。为此,QMK 决定将 GPIO 操作函数统一重命名为符合 QMK Firmware 自身代码风格的gpio_*系列函数,并在本周期移除了全部向后兼容的旧定义。
2.2 现行 GPIO 抽象层 API
官方推荐的替代接口完整收录于 GPIO 控制文档,其核心宏定义位于platforms/<platform>/gpio.h(每个平台一份实现,抽象层与具体微控制器无关)。常用宏及语义如下:
| 宏 | 说明 |
|---|---|
gpio_set_pin_input(pin) | 设置为高阻态输入(High-Z) |
gpio_set_pin_input_high(pin) | 设置为带内部上拉电阻的输入 |
gpio_set_pin_input_low(pin) | 设置为带内部下拉电阻的输入(AVR 上不可用) |
gpio_set_pin_output(pin) | 设置为输出(gpio_set_pin_output_push_pull的别名) |
gpio_set_pin_output_push_pull(pin) | 设置为推挽输出 |
gpio_set_pin_output_open_drain(pin) | 设置为开漏输出(AVR 上不可用) |
gpio_write_pin_high(pin)/gpio_write_pin_low(pin) | 将输出引脚电平置高 / 置低 |
gpio_write_pin(pin, level) | 按给定电平写输出引脚 |
gpio_read_pin(pin) | 读取引脚电平 |
gpio_toggle_pin(pin) | 翻转输出引脚电平 |
对于架构相关的进阶需求,抽象层并不限制直接使用平台原生接口:AVR 使用标准avr/io.h库,STM32(ChibiOS)使用 PAL 库。
2.3 原子性注意事项
需要提醒的是,上述函数并不保证原子性。若在多个 GPIO 操作组合中不希望被中断打断,应使用ATOMIC_BLOCK_FORCEON宏包裹关键段,例如:
void some_function(void) { // some process ATOMIC_BLOCK_FORCEON { // Atomic Processing } // some process }ATOMIC_BLOCK_FORCEON会无条件在块执行前关中断、执行完毕后开中断,因此仅在你确定进入块前中断是开启的、且块结束时允许开中断的场景下使用。
从源码结构看,这套 API 已被广泛用于 QMK 内部。例如 split_util.c 中读取 Split 键盘左右手判定引脚时就调用了gpio_set_pin_input与gpio_read_pin,说明新命名规范不仅是对外接口,也是内部实现的一致标准。
三、移除废弃的 isLeftHand(PR #25897)
3.1 迁移方式
使用 Split 键盘的用户应从全局变量isLeftHand迁移到 split_util.h 提供的is_keyboard_left()函数。官方给出的典型迁移示例(OLED 旋转角度按左右手区分):
oled_rotation_t oled_init_user(oled_rotation_t rotation) { - return isLeftHand ? OLED_ROTATION_180 : OLED_ROTATION_0; + return is_keyboard_left() ? OLED_ROTATION_180 : OLED_ROTATION_0; }原文档说明,废弃变量isLeftHand将在下一个破坏性变更周期被移除。而从当前仓库快照看,quantum/与tmk_core/目录下已搜索不到isLeftHand的任何定义或引用,可以推断该变量在后续周期(本仓库 ChangeLog 序列中已有更晚的20260531.md)中已被彻底清除,迁移到函数式 API 已无退路。
3.2 源码级原理:is_keyboard_left() 是如何计算的
is_keyboard_left()的声明位于 quantum/keyboard.h,其实现与判定逻辑集中在 quantum/split_common/split_util.c。核心结构是一个"弱符号 + 初始化时固化"的设计:
is_keyboard_left_impl()(弱符号,可被键盘覆写):按编译期宏依次采用不同判定策略——SPLIT_HAND_PIN:将引脚设为输入,等待 100us 后读取电平;SPLIT_HAND_PIN_LOW_IS_LEFT可反转高低电平含义;SPLIT_HAND_MATRIX_GRID:直接窥视矩阵交点peek_matrix_intersection(),即利用矩阵中的某个按键组合区分左右手;EE_HANDS:从 EEPROM 读取手性配置(eeconfig_read_handedness()),并支持INIT_EE_HANDS_LEFT/RIGHT的初始伪造逻辑;MASTER_RIGHT:以"是否为主侧"取反判定;- 默认(无宏定义):左右手等价于主从侧(
is_keyboard_master())。
split_pre_init()固化配置:在键盘完全初始化之前,先调用is_keyboard_master_impl()与is_keyboard_left_impl(),把结果写入全局结构split_config.master/split_config.left。其中is_keyboard_master_impl()通过usb_bus_detected()判断哪一侧连着 USB 线。is_keyboard_left()本体:只是一个弱符号,默认返回split_config.left,即"启动时算一次、之后 O(1) 读取",避免在高频调用路径上反复读引脚。
这套机制的调用面很广,从源码可以确认它被用于:矩阵扫描的半侧行偏移计算(quantum/matrix.c 与 quantum/matrix_common.c)、Bootmagic 功能仅在右手侧启用(quantum/bootmagic/bootmagic.c)、RGB 矩阵左右侧 LED 索引裁剪(quantum/rgb_matrix/rgb_matrix.c)、LED 矩阵侧向裁剪(quantum/led_matrix/led_matrix.h)以及 Split 传输事务(quantum/split_common/transactions.c)。这正是"变量改函数"迁移的价值:函数式接口可以统一走split_config缓存,并允许用户在用户空间以 weak symbol 覆写方式自定义判定逻辑,而一个裸全局变量无法提供这种扩展点。
四、Core 核心变更清单
本次 Core 层变更共 10 项,全部继承自原文档并补充上下文:
- 重构 Makefile 中定位 keymap 的逻辑(#25808):keymap 查找流程收敛到 builddefs/locate_keymap.mk 等构建定义文件中,属于构建系统内部重构;
- 将关机延迟移入 audio 特性(#25859):
SHUTDOWN_DELAY_MS相关行为从通用启动路径归位到 audio 模块,未启用音频的构建不再承担该逻辑; - 重构核心代码中对废弃
isLeftHand的使用(#25888):即上文第三节的内部迁移; - 允许社区模块自定义数据同步(#25955):community modules 的
data sync(跨侧同步变量)机制开放给第三方模块定制; - 移除一个不可达的 break 语句(#26006);
- 移除重复的 host.h(#26007);
- 移除冗余的 EEPROM 更新(#26008):减少 Flash/EEPROM 写入,与磨损均衡方向一致;
- 移除
apa102_set_brightness中冗余的无符号比较(#26010); - 移除未使用的头文件(#26011);
- 分配失败时返回
INVALID_DEFERRED_TOKEN(#26012):Deferred 键码(如DT(M)延迟宏)在动态内存分配失败时的返回值由静默失败变为显式无效 token,行为更可预测; - 移除废弃的 GPIO 定义(#26028):本周期最重要的破坏性变更,见第二节。
五、CLI 工具链变更
qmk命令行工具(Python)本周期改动 11 项,对使用 CLI 开发流程的用户影响最大:
- 格式化文件时强制 EOL(#24989):
qmk format输出的换行风格保持一致; - 允许 keymap.json 关闭配置项(#25502):keymap 级配置可以显式 disable 某些 feature flag,而不仅是 enable;
- 移除未使用的
qmk.keymap.write_file/qmk.keymap.write_json内部接口(#25854); - version.h 中加入 userspace 版本号(
QMK_USERSPACE_VERSION)(#25882):使用外部 userspace 时,烧录出的固件版本字符串可以区分 userspace 版本; - 对越界 bootmagic 配置执行 lint 校验(#25899),并与第六节的 bootmagic 越界修复(#25898)配套;
qmk doctor报告权限问题(#25931):环境诊断命令能提示文件权限导致的异常;- CLI 格式化命令的排版微调(#25946);
- lint 新增 keymap 名称合法性校验(#25969):不合规的 keymap 命名会在 lint 阶段报错;
new-keyboard的开发板提示增加"以上都不是"选项(#25998);- 生成的 info.json 不再包含
config_h_features(#26024):info.json 字段收敛,feature 开关统一由构建系统推导; - defaults 重复检查从警告提升为错误(#26025):
KEYMAP与LAYOUT_宏重复等冲突配置将直接导致构建失败而非静默选择。
六、Keyboards 层变更与 Bug 修复
6.1 键盘定义与代码整理
- 新增Soldered Macro Pad键盘(#25834),对应目录 keyboards/soldered;
- 从各键盘
.json中移除冗余 URL 字段(#25856); - 为
projectcain/vault*增加编码器行为的守卫条件(#25864),避免特定构建下编码器配置冲突; - 重构键盘与 keymap 中对废弃
isLeftHand的使用(#25891):即键盘层面的迁移; - 移除部分不必要的 matrix extern 声明(#25975);
ROW_SHIFTER迁移到核心MATRIX_ROW_SHIFTER(#25977):行移位扫描不再依赖键盘侧定义,统一由核心矩阵处理,减少各键盘重复实现。
6.2 Bug 修复(对稳定性影响最大的一节)
- 修复 Flash 磨损均衡的扇区计算错误(#24776):影响 drivers/wear_leveling 模块在多次 EEPROM 写入后的扇区轮转;
- 修复 Split 键盘上 WS2812 的 LED 索引错误(#25407):左右两侧 LED 编号拼接逻辑修正,与
RGBLED_SPLIT裁剪机制相关; qmk new-keymap能正确解析键盘别名(#25570):通过 alias 引用的键盘在创建 keymap 时不再解析失败;- 修复 is31fl3729 LED 矩阵驱动的 off-by-one 错误(#25902):SPI LED 矩阵(IS31FL3729)行列边界偏差修正;
- Match Key 覆写索引类型与边界类型对齐,防止溢出(#25939):
MATCH_KEY匹配覆写的索引计算改为与边界检查相同的有符号类型,消除负值比较绕过问题。
6.3 其他变更
- 为 DD(double tap,双重触发)键码定义补充缺失的标签(#25503),改善键码文档与自动补全;
- 新增面向 Pull Request 的 Copilot 协作说明(#25857),属于协作流程改进,不影响固件行为。
七、给开发者的落地检查清单
- 全局搜索旧 GPIO 宏:在你的 keymap、keyboard 与 userspace 中搜索
DD[A-Z]、PORT[A-Z]、PIN[A-Z]、_HIGH/_LOW等 Arduino 风格用法,全部替换为第二节表格中的gpio_*函数; - 搜索
isLeftHand:替换为is_keyboard_left(),并确认头文件包含来自split_util.h的声明路径(quantum/keyboard.h 亦可作为声明来源); - 升级后跑一遍
qmk lint与qmk doctor:本周期 lint 新增了 keymap 名称与 bootmagic 越界检查,qmk doctor增加了权限诊断,能提前暴露配置问题; - 注意构建失败语义变化:defaults 重复等过去仅是警告的场景现在会直接报错(#26025),CI 中若有"忽略警告"的宽松策略需要更新;
- 关注 info.json 消费方:若你有脚本解析键盘
info.json的config_h_features字段,需要适配该字段的移除(#26024)。
本次变更周期的完整原始记录见 docs/ChangeLog/20260222.md,GPIO 抽象层详细说明见 docs/drivers/gpio.md,GPIO 各平台实现位于 platforms 目录下对应平台的gpio.h。
【免费下载链接】qmk_firmwareOpen-source keyboard firmware for Atmel AVR and Arm USB families项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmware
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考