☰
ZMK 电源管理行为实战:用 `ext_power` 控制外部电源(VCC)输出以节省电量
2026/10/13 5:55:34 网站建设 项目流程
  • 固件
  • 嵌入式
  • 智能硬件
  • 蓝牙

【免费下载链接】zmk

ZMK Firmware Repository

项目地址:https://gitcode.com/gh_mirrors/zm/zmk
点击查看免费下载

本文以 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。各宏定义及别名如下:

DefineActionAlias
EXT_POWER_OFF_CMDDisable the external power.EP_OFF
EXT_POWER_ON_CMDEnable the external power.EP_ON
EXT_POWER_TOGGLE_CMDToggle 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_ON

2. 行为绑定:禁用外部电源

&ext_power EP_OFF

3. 行为绑定:切换外部电源

&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

项目地址:https://gitcode.com/gh_mirrors/zm/zmk
点击查看免费下载

相关推荐

上一篇:在 KernelSU 上跑通 LSPosed:ZygiskNext 完整部署避坑指南
下一篇:Docker-Mailserver反垃圾策略:构建多层次防御体系的终极指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询