- 固件
- 嵌入式
- 智能硬件
- 蓝牙
【免费下载链接】zmk
ZMK Firmware Repository
本文以 ZMK 固件仓库中的电源管理行为文档(docs/docs/keymaps/behaviors/power.md)为核心,系统讲解&ext_power行为的工作原理、命令定义、keymap 绑定方法、状态持久化机制,以及它在分体键盘上的全局生效特性。读完本文,你将能够在自己的 keymap 中正确绑定启用、禁用、切换外部电源的命令,并理解底层驱动是如何通过 GPIO 与设置子系统实现"省电 + 断电记忆"的完整链路。
概述:外部电源控制行为解决什么问题
ZMK 的**外部电源控制行为(External Power Control)**允许通过 keymap 按键启用或禁用开发板上的VCC 电源输出,从而节省电量。
许多外设(尤其是部分 LED)即使在"关闭"状态下依然会持续耗电。对于这种情况,部分主控板提供了完全切断外部电源的能力:外部电源行为就是用来控制该电源输出的。它最适合的场景包括:
- 使用 RGB 灯带或状态 LED 的分体键盘,在闲置或夜间希望彻底断电;
- 外接显示器(OLED)等外设不需要常开时;
- 在同一个按键位上切换"供电"与"断电"两种状态。
目前官方文档明确列出支持该特性的板子有两款:
- nRFMicro
- nice!nano
从仓库源码看,这两款板子的 Devicetree overlay 中都定义了EXT_POWER节点,例如 nice!nano 的 app/boards/nicekeyboards/nice_nano/nice_nano_nrf52840_zmk_2_0_0.overlay 与 nRFMicro 的 app/module/boards/joric/nrfmicro/nrfmicro_nrf52840_1_1_0.overlay 中均包含:
EXT_POWER { compatible = "zmk,ext-power-generic"; control-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; init-delay-ms = <50>; };control-gpios指定了用于开启/关闭外部电源的 GPIO,init-delay-ms为驱动初始化后的延迟毫秒数。
命令定义:dt-bindings/zmk/ext_power.h
外部电源控制命令的宏定义位于头文件 app/include/dt-bindings/zmk/ext_power.h。要在 keymap 中使用这些命令,需要在 keymap 文件顶部加入:
#include <dt-bindings/zmk/ext_power.h>例如仓库中 lily58 的 keymap app/boards/shields/lily58/lily58.keymap 就在文件头部同时引入了<behaviors.dtsi>和<dt-bindings/zmk/ext_power.h>,随后在绑定列表中直接使用&ext_power EP_ON等写法。
引入该头文件后,就可以引用其中定义的动作,例如EXT_POWER_OFF_CMD。各宏定义及别名如下:
| Define | Action | Alias |
|---|---|---|
EXT_POWER_OFF_CMD | Disable the external power. | EP_OFF |
EXT_POWER_ON_CMD | Enable the external power. | EP_ON |
EXT_POWER_TOGGLE_CMD | Toggle the external power. | EP_TOG |
在源码中,这些宏被定义为一组连续的整数值(app/include/dt-bindings/zmk/ext_power.h):
#define EXT_POWER_OFF_CMD 0 #define EXT_POWER_ON_CMD 1 #define EXT_POWER_TOGGLE_CMD 2 #define EP_ON EXT_POWER_ON_CMD #define EP_OFF EXT_POWER_OFF_CMD #define EP_TOG EXT_POWER_TOGGLE_CMD也就是说,EP_ON、EP_OFF、EP_TOG与EXT_POWER_ON_CMD、EXT_POWER_OFF_CMD、EXT_POWER_TOGGLE_CMD是完全等价的两个名字,keymap 中既可以写长名字也可以写短别名。
行为绑定:&ext_power
&ext_power行为的绑定方式如下:
- Reference:
&ext_power - Parameter#1:命令(Command),例如
EP_ON
该行为的 Devicetree 定义位于 app/dts/behaviors/ext_power.dtsi,并被 app/dts/behaviors.dtsi 统一引入:
ext_power: extpower { compatible = "zmk,behavior-ext-power"; #binding-cells = <1>; display-name = "External Power"; };其中#binding-cells = <1>表示该行为在 keymap 中需要且仅需要一个绑定参数,这个参数就是上表中的命令(0/1/2)。行为节点名被刻意设计为不超过 8 个字符的extpower,以便在分体键盘外设端也能被引用。
绑定到按键的标准写法:
&ext_power EP_ON也可以使用完整命令名:
&ext_power EXT_POWER_ON_CMD三种命令的完整示例
1. 行为绑定:启用外部电源
&ext_power EP_ON2. 行为绑定:禁用外部电源
&ext_power EP_OFF3. 行为绑定:切换外部电源
&ext_power EP_TOG仓库中 lily58 的 keymap(app/boards/shields/lily58/lily58.keymap)提供了一个同时使用三种命令的完整实例:
&trans &ext_power EP_ON &ext_power EP_OFF &ext_power EP_TOG &trans ...即将"外部电源开、关、切换"分别绑定在相邻的三个按键位上,便于实时控制供电状态。
底层实现:按下时如何执行命令
&ext_power行为的 C 实现位于 app/src/behaviors/behavior_ext_power.c。它在按下回调on_keymap_binding_pressed中通过device_get_binding("EXT_POWER")获取外部电源设备,然后根据参数执行对应操作(app/src/behaviors/behavior_ext_power.c):
switch (binding->param1) { case EXT_POWER_OFF_CMD: return ext_power_disable(ext_power); case EXT_POWER_ON_CMD: return ext_power_enable(ext_power); case EXT_POWER_TOGGLE_CMD: if (ext_power_get(ext_power) > 0) return ext_power_disable(ext_power); else return ext_power_enable(ext_power); default: LOG_ERR("Unknown ext_power command: %d", binding->param1); }需要特别注意的是,EXT_POWER_TOGGLE_CMD是"状态相关"命令:切换目标取决于当前电源状态。ZMK 通过binding_convert_central_state_dependent_params回调(app/src/behaviors/behavior_ext_power.c)在触发时先把 TOGGLE 命令换算成明确的 ON 或 OFF 命令:
if (binding->param1 == EXT_POWER_TOGGLE_CMD) { binding->param1 = ext_power_get(ext_power) > 0 ? EXT_POWER_OFF_CMD : EXT_POWER_ON_CMD; }同时,行为在释放回调on_keymap_binding_released中返回ZMK_BEHAVIOR_OPAQUE(app/src/behaviors/behavior_ext_power.c),表示该行为只在按下时生效,释放时不产生额外动作——因此它适合绑定在普通按键位,按下即执行、松开无副作用。
这些ext_power_enable/ext_power_disable/ext_power_get调用通过驱动 API(app/include/drivers/ext_power.h)分发到具体驱动实现:enable将控制 GPIO 置为有效电平,disable置为无效电平,get返回当前开关状态(1 表示开启,0 表示关闭)。
状态持久化:断电与重刷固件后仍能记住开关状态
:::note[外部电源状态持久化] 由&ext_power行为设置的开关状态会被保存到闪存存储(即 ZMK 的 settings 子系统),因此在重启和重新刷写固件后仍然保持。不过,为了避免对闪存造成不必要的磨损,状态只会在CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE毫秒之后才被真正写入。 :::
具体实现位于 app/src/ext_power_generic.c:每次enable/disable都会把新状态写进ext_power/state/<设备名>设置项,并使用一个可延迟工作项做防抖:
int ext_power_save_state(void) { int ret = k_work_reschedule(&ext_power_save_work, K_MSEC(CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE)); return MIN(ret, 0); }当 settings 子系统提交时,驱动会根据已保存的状态恢复 GPIO 电平(app/src/ext_power_generic.c);如果从未保存过任何状态(首次启动),则默认启用外部电源:
static int ext_power_settings_commit() { ... if (!data->settings_init) { >enum behavior_locality { BEHAVIOR_LOCALITY_CENTRAL, // 仅在 central 处理 BEHAVIOR_LOCALITY_EVENT_SOURCE, // 仅在触发来源侧处理 BEHAVIOR_LOCALITY_GLOBAL // 全局生效 };&ext_power行为的驱动 API 将.locality设置为BEHAVIOR_LOCALITY_GLOBAL(app/src/behaviors/behavior_ext_power.c)。在 central 侧的分发逻辑中(app/src/behavior.c),全局行为会先向所有已连接的外设广播执行,再在本地执行:
case BEHAVIOR_LOCALITY_GLOBAL: for (int i = 0; i < ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT; i++) { zmk_split_central_invoke_behavior(i, &binding, event, pressed); } return invoke_locally(&binding, event, pressed);这也解释了文档中"全局生效"的含义:无论按键绑在外设侧还是 central 侧,外部电源的开关都会同步作用于整把键盘,非常适合分体键盘统一控制 LED/外设供电。关于 locality 机制的完整讨论可参考分体键盘特性文档中的 "Global Locality Behaviors" 一节,其中同样把 power management behaviors 列为全局行为之一。
实践要点速查
- 在 keymap 顶部
#include <dt-bindings/zmk/ext_power.h>,即可使用EP_ON/EP_OFF/EP_TOG; - 使用
&ext_power行为时,参数 #1 必填,值为上述三种命令之一; - 行为只在按下时执行,释放无副作用,适合直接绑定普通按键;
- 开关状态通过 ZMK settings 子系统持久化(
ext_power/state/<设备名>),默认在变更后CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE(默认 60000 ms)写入闪存; - 分体键盘上该行为是全局行为,一次触发同时作用于 central 与 peripheral;
- 仅当板级 Devicetree 中存在
EXT_POWER节点(compatible = "zmk,ext-power-generic")时该行为才真正可用,官方支持的板子为 nRFMicro 与 nice!nano;配置驱动属性可参考电源管理配置文档中的 External Power Control 一节。
- 固件
- 嵌入式
- 智能硬件
- 蓝牙
【免费下载链接】zmk
ZMK Firmware Repository
相关推荐
ZMK 电源管理配置完全指南:低功耗状态、外部电源控制与软关机唤醒
ZMK 电源管理配置完全指南:低功耗状态、外部电源控制与软关机唤醒 本篇指南以 ZMK 固件仓库的 电源管理配置文档 https://link.gitcode.
固件嵌入式智能硬件蓝牙Electron Fiddle电源管理:控制应用电源使用
Electron Fiddle电源管理:控制应用电源使用 为什么需要电源管理? 在开发桌面应用时,电源管理常被忽视却至关重要。想象以下场景:视频会议应用在电脑休
桌面应用开发工具代码编辑器ZMK 固件状态报告第三期:键位码体系重构、外部电源控制与输出选择的技术演进
ZMK 固件状态报告第三期:键位码体系重构、外部电源控制与输出选择的技术演进 本期内容以 ZMK 官方《State Of The Firmware 3》博客(
固件嵌入式智能硬件蓝牙
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考