merge zmk/main
This commit is contained in:
commit
3176ada2d2
90 changed files with 1855 additions and 286 deletions
8
.github/workflows/build-user-config.yml
vendored
8
.github/workflows/build-user-config.yml
vendored
|
@ -65,6 +65,7 @@ jobs:
|
|||
board: ${{ matrix.board }}
|
||||
shield: ${{ matrix.shield }}
|
||||
artifact_name: ${{ matrix.artifact-name }}
|
||||
snippet: ${{ matrix.snippet }}
|
||||
run: |
|
||||
if [ -e zephyr/module.yml ]; then
|
||||
export zmk_load_arg=" -DZMK_EXTRA_MODULES='${GITHUB_WORKSPACE}'"
|
||||
|
@ -75,7 +76,12 @@ jobs:
|
|||
echo "base_dir=${GITHUB_WORKSPACE}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
if [ -n "${snippet}" ]; then
|
||||
extra_west_args="-S \"${snippet}\""
|
||||
fi
|
||||
|
||||
echo "zephyr_version=${ZEPHYR_VERSION}" >> $GITHUB_ENV
|
||||
echo "extra_west_args=${extra_west_args}" >> $GITHUB_ENV
|
||||
echo "extra_cmake_args=${shield:+-DSHIELD=\"$shield\"}${zmk_load_arg}" >> $GITHUB_ENV
|
||||
echo "display_name=${shield:+$shield - }${board}" >> $GITHUB_ENV
|
||||
echo "artifact_name=${artifact_name:-${shield:+$shield-}${board}-zmk}" >> $GITHUB_ENV
|
||||
|
@ -120,7 +126,7 @@ jobs:
|
|||
- name: West Build (${{ env.display_name }})
|
||||
working-directory: ${{ env.base_dir }}
|
||||
shell: sh -x {0}
|
||||
run: west build -s zmk/app -d "${{ env.build_dir }}" -b "${{ matrix.board }}" -- -DZMK_CONFIG=${{ env.base_dir }}/${{ inputs.config_path }} ${{ env.extra_cmake_args }} ${{ matrix.cmake-args }}
|
||||
run: west build -s zmk/app -d "${{ env.build_dir }}" -b "${{ matrix.board }}" ${{ env.extra_west_args }} -- -DZMK_CONFIG=${{ env.base_dir }}/${{ inputs.config_path }} ${{ env.extra_cmake_args }} ${{ matrix.cmake-args }}
|
||||
|
||||
- name: ${{ env.display_name }} Kconfig file
|
||||
run: |
|
||||
|
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -5,7 +5,14 @@
|
|||
/zephyr
|
||||
/zmk-config
|
||||
/build
|
||||
|
||||
# macOS
|
||||
*.DS_Store
|
||||
|
||||
# Python
|
||||
__pycache__
|
||||
.python-version
|
||||
.venv
|
||||
|
||||
# clangd
|
||||
app/.cache/
|
||||
|
|
|
@ -8,10 +8,6 @@ set(ZEPHYR_EXTRA_MODULES "${ZMK_EXTRA_MODULES};${CMAKE_CURRENT_SOURCE_DIR}/modul
|
|||
find_package(Zephyr REQUIRED HINTS ../zephyr)
|
||||
project(zmk)
|
||||
|
||||
if(CONFIG_ZMK_SLEEP)
|
||||
zephyr_linker_sources(SECTIONS include/linker/zmk-pm-devices.ld)
|
||||
endif()
|
||||
|
||||
zephyr_linker_sources(SECTIONS include/linker/zmk-behaviors.ld)
|
||||
zephyr_linker_sources(RODATA include/linker/zmk-events.ld)
|
||||
|
||||
|
@ -25,11 +21,14 @@ target_sources(app PRIVATE src/stdlib.c)
|
|||
target_sources(app PRIVATE src/activity.c)
|
||||
target_sources(app PRIVATE src/behavior.c)
|
||||
target_sources(app PRIVATE src/kscan.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_KSCAN_SIDEBAND_BEHAVIORS app PRIVATE src/kscan_sideband_behaviors.c)
|
||||
target_sources(app PRIVATE src/matrix_transform.c)
|
||||
target_sources(app PRIVATE src/sensors.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c)
|
||||
target_sources(app PRIVATE src/event_manager.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_PM app PRIVATE src/pm.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_GPIO_KEY_WAKEUP_TRIGGER app PRIVATE src/gpio_key_wakeup_trigger.c)
|
||||
target_sources(app PRIVATE src/events/activity_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/position_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/sensor_event.c)
|
||||
|
@ -38,6 +37,7 @@ target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c)
|
|||
target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SOFT_OFF app PRIVATE src/behaviors/behavior_soft_off.c)
|
||||
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
target_sources(app PRIVATE src/hid.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse.c)
|
||||
|
@ -99,5 +99,6 @@ target_sources_ifdef(CONFIG_ZMK_LOW_PRIORITY_WORK_QUEUE app PRIVATE src/workqueu
|
|||
target_sources(app PRIVATE src/main.c)
|
||||
|
||||
add_subdirectory(src/display/)
|
||||
add_subdirectory_ifdef(CONFIG_SETTINGS src/settings/)
|
||||
|
||||
zephyr_cc_option(-Wfatal-errors)
|
||||
|
|
66
app/Kconfig
66
app/Kconfig
|
@ -383,6 +383,20 @@ config ZMK_BATTERY_REPORTING
|
|||
select ZMK_LOW_PRIORITY_WORK_QUEUE
|
||||
imply BT_BAS if ZMK_BLE
|
||||
|
||||
if ZMK_BATTERY_REPORTING
|
||||
|
||||
choice ZMK_BATTERY_REPORTING_FETCH_MODE
|
||||
prompt "Battery Reporting Fetch Mode"
|
||||
|
||||
config ZMK_BATTERY_REPORTING_FETCH_MODE_STATE_OF_CHARGE
|
||||
bool "State of charge"
|
||||
|
||||
config ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE
|
||||
bool "Lithium Voltage"
|
||||
|
||||
endchoice
|
||||
endif
|
||||
|
||||
config ZMK_IDLE_TIMEOUT
|
||||
int "Milliseconds of inactivity before entering idle state (OLED shutoff, etc)"
|
||||
default 30000
|
||||
|
@ -391,6 +405,7 @@ config ZMK_SLEEP
|
|||
bool "Enable deep sleep support"
|
||||
depends on HAS_POWEROFF
|
||||
select POWEROFF
|
||||
select ZMK_PM_DEVICE_SUSPEND_RESUME
|
||||
imply USB
|
||||
|
||||
if ZMK_SLEEP
|
||||
|
@ -413,6 +428,26 @@ config ZMK_EXT_POWER
|
|||
bool "Enable support to control external power output"
|
||||
default y
|
||||
|
||||
config ZMK_PM
|
||||
bool
|
||||
|
||||
config ZMK_PM_DEVICE_SUSPEND_RESUME
|
||||
bool
|
||||
select ZMK_PM
|
||||
|
||||
config ZMK_PM_SOFT_OFF
|
||||
bool "Soft-off support"
|
||||
depends on HAS_POWEROFF
|
||||
select ZMK_PM
|
||||
select PM_DEVICE
|
||||
select ZMK_PM_DEVICE_SUSPEND_RESUME
|
||||
select POWEROFF
|
||||
|
||||
config ZMK_GPIO_KEY_WAKEUP_TRIGGER
|
||||
bool "Hardware supported wakeup (GPIO)"
|
||||
default y
|
||||
depends on DT_HAS_ZMK_GPIO_KEY_WAKEUP_TRIGGER_ENABLED && ZMK_PM_SOFT_OFF
|
||||
|
||||
#Power Management
|
||||
endmenu
|
||||
|
||||
|
@ -489,6 +524,21 @@ config ZMK_KSCAN_EVENT_QUEUE_SIZE
|
|||
|
||||
endif # ZMK_KSCAN
|
||||
|
||||
config ZMK_KSCAN_SIDEBAND_BEHAVIORS
|
||||
bool
|
||||
default y
|
||||
depends on DT_HAS_ZMK_KSCAN_SIDEBAND_BEHAVIORS_ENABLED
|
||||
select KSCAN
|
||||
|
||||
if ZMK_KSCAN_SIDEBAND_BEHAVIORS
|
||||
|
||||
config ZMK_KSCAN_SIDEBAND_BEHAVIORS_INIT_PRIORITY
|
||||
int "Keyboard scan sideband behaviors driver init priority"
|
||||
# The default kscan init priority is 90, so be sure we are initialized later.
|
||||
default 95
|
||||
|
||||
endif # ZMK_KSCAN_SIDEBAND_BEHAVIORS
|
||||
|
||||
menu "Logging"
|
||||
|
||||
config ZMK_LOGGING_MINIMAL
|
||||
|
@ -569,6 +619,22 @@ endmenu
|
|||
|
||||
if SETTINGS
|
||||
|
||||
config ZMK_SETTINGS_RESET_ON_START
|
||||
bool "Delete all persistent settings when the keyboard boots"
|
||||
|
||||
if ZMK_SETTINGS_RESET_ON_START
|
||||
|
||||
config ZMK_SETTINGS_RESET_ON_START_INIT_PRIORITY
|
||||
int "Settings Reset ON Start Initialization Priority"
|
||||
default 60
|
||||
help
|
||||
Initialization priority for the settings reset on start. Must be lower priority/
|
||||
higher value than FLASH_INIT_PRIORITY if using the NVS/Flash settings backend.
|
||||
|
||||
|
||||
endif
|
||||
|
||||
|
||||
config ZMK_SETTINGS_SAVE_DEBOUNCE
|
||||
int "Milliseconds to debounce settings saves"
|
||||
default 60000
|
||||
|
|
|
@ -12,6 +12,11 @@ config ZMK_BEHAVIOR_MOUSE_KEY_PRESS
|
|||
depends on DT_HAS_ZMK_BEHAVIOR_MOUSE_KEY_PRESS_ENABLED
|
||||
imply ZMK_MOUSE
|
||||
|
||||
config ZMK_BEHAVIOR_SOFT_OFF
|
||||
bool
|
||||
default y
|
||||
depends on DT_HAS_ZMK_BEHAVIOR_SOFT_OFF_ENABLED && ZMK_PM_SOFT_OFF
|
||||
|
||||
config ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON
|
||||
bool
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
// | SHFT | | | | | | | _ | + | { | } | "|" | ~ |
|
||||
// | GUI | | SPC | | ENT | | ALT |
|
||||
bindings = <
|
||||
&kp TAB &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp KP_MULTIPLY &kp LPAR &kp RPAR &kp BSPC
|
||||
&kp TAB &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp ASTRK &kp LPAR &kp RPAR &kp BSPC
|
||||
&kp LCTRL &trans &trans &trans &trans &trans &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp BSLH &kp GRAVE
|
||||
&kp LSHFT &trans &trans &trans &trans &trans &kp UNDER &kp PLUS &kp LBRC &kp RBRC &kp PIPE &kp TILDE
|
||||
&kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT
|
||||
|
|
|
@ -14,14 +14,14 @@
|
|||
#define SYM_L 4
|
||||
|
||||
// Using layer taps on thumbs, having quick tap as well helps w/ repeating space/backspace
|
||||
< { quick_tap_ms = <200>; };
|
||||
< { quick-tap-ms = <200>; };
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
hm: homerow_mods {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
tapping_term_ms = <200>;
|
||||
tapping-term-ms = <200>;
|
||||
flavor = "tap-preferred";
|
||||
bindings = <&kp>, <&kp>;
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
&mt {
|
||||
flavor = "tap-preferred";
|
||||
tapping_term_ms = <140>;
|
||||
tapping-term-ms = <140>;
|
||||
};
|
||||
|
||||
/ {
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
// | SHFT | | | | | | | _ | + | { | } | "|" | ~ |
|
||||
// | GUI | | SPC | | ENT | | ALT |
|
||||
bindings = <
|
||||
&kp TAB &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp KP_MULTIPLY &kp LPAR &kp RPAR &kp BSPC
|
||||
&kp TAB &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp ASTRK &kp LPAR &kp RPAR &kp BSPC
|
||||
&kp LCTRL &trans &trans &trans &trans &trans &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp BSLH &kp GRAVE
|
||||
&kp LSHFT &trans &trans &trans &trans &trans &kp UNDER &kp PLUS &kp LBRC &kp RBRC &kp PIPE &kp TILDE
|
||||
&kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
#define SYM_L 3
|
||||
|
||||
// Using layer taps on thumbs, having quick tap as well helps w/ repeating space/backspace
|
||||
< { quick_tap_ms = <200>; };
|
||||
< { quick-tap-ms = <200>; };
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
hm: homerow_mods {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
tapping_term_ms = <225>;
|
||||
tapping-term-ms = <225>;
|
||||
flavor = "tap-preferred";
|
||||
bindings = <&kp>, <&kp>;
|
||||
};
|
||||
|
|
|
@ -38,8 +38,8 @@
|
|||
bindings = <
|
||||
&bt BT_CLR &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &trans &trans &trans &trans &trans &trans
|
||||
&kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12
|
||||
&kp GRAVE &kp EXCL &kp AT &kp HASH &kp DOLLAR &kp PRCNT &kp CARET &kp AMPS &kp KP_MULTIPLY &kp LPAR &kp RPAR &kp TILDE
|
||||
&trans &trans &trans &trans &trans &trans &trans &trans &trans &kp MINUS &kp KP_PLUS &kp LBRC &kp RBRC &kp PIPE
|
||||
&kp GRAVE &kp EXCL &kp AT &kp HASH &kp DOLLAR &kp PRCNT &kp CARET &kp AMPS &kp ASTRK &kp LPAR &kp RPAR &kp TILDE
|
||||
&trans &trans &trans &trans &trans &trans &trans &trans &trans &kp MINUS &kp PLUS &kp LBRC &kp RBRC &kp PIPE
|
||||
&trans &trans &trans &trans &trans &trans
|
||||
>;
|
||||
};
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
#define RSE 2
|
||||
#define ADJ 3
|
||||
|
||||
< { quick_tap_ms = <200>; };
|
||||
&mt { quick_tap_ms = <200>; };
|
||||
< { quick-tap-ms = <200>; };
|
||||
&mt { quick-tap-ms = <200>; };
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
|
|
|
@ -41,8 +41,8 @@
|
|||
bindings = <
|
||||
&bt BT_CLR &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &trans &trans &trans &trans &trans &trans
|
||||
&kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12
|
||||
&kp GRAVE &kp EXCL &kp AT &kp HASH &kp DOLLAR &kp PRCNT &kp CARET &kp AMPS &kp KP_MULTIPLY &kp LPAR &kp RPAR &kp TILDE
|
||||
&trans &ext_power EP_ON &ext_power EP_OFF &ext_power EP_TOG &trans &trans &trans &trans &trans &kp MINUS &kp KP_PLUS &kp LBRC &kp RBRC &kp PIPE
|
||||
&kp GRAVE &kp EXCL &kp AT &kp HASH &kp DOLLAR &kp PRCNT &kp CARET &kp AMPS &kp ASTRK &kp LPAR &kp RPAR &kp TILDE
|
||||
&trans &ext_power EP_ON &ext_power EP_OFF &ext_power EP_TOG &trans &trans &trans &trans &trans &kp MINUS &kp PLUS &kp LBRC &kp RBRC &kp PIPE
|
||||
&trans &trans &trans &trans &trans &trans &trans &trans
|
||||
>;
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
// | | | | | | | _ | + | [ | ] | \ |
|
||||
// | GUI | | SPC | | ENT | | ALT |
|
||||
bindings = <
|
||||
&kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp KP_MULTIPLY &kp LPAR &kp RPAR
|
||||
&kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp ASTRK &kp LPAR &kp RPAR
|
||||
&trans &trans &trans &trans &trans &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp PIPE
|
||||
&trans &trans &trans &trans &trans &trans &trans &trans &trans &kp BSLH
|
||||
&kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
&mt {
|
||||
flavor = "tap-preferred";
|
||||
tapping_term_ms = <140>;
|
||||
tapping-term-ms = <140>;
|
||||
};
|
||||
|
||||
/ {
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START=y
|
||||
CONFIG_SETTINGS=y
|
||||
CONFIG_ZMK_SETTINGS_RESET_ON_START=y
|
||||
# Disable BLE so splits don't try to re-pair until normal firmware is flashed.
|
||||
CONFIG_ZMK_BLE=n
|
||||
|
|
|
@ -60,8 +60,8 @@
|
|||
bindings = <
|
||||
&trans &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11
|
||||
&kp GRAVE &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp F12
|
||||
&trans &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp KP_MULTIPLY &kp LPAR &kp RPAR &kp PIPE
|
||||
&trans &kp EQUAL &kp MINUS &kp KP_PLUS &kp LBRC &kp RBRC &trans &trans &kp LBKT &kp RBKT &kp SEMI &kp COLON &kp BSLH &trans
|
||||
&trans &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp ASTRK &kp LPAR &kp RPAR &kp PIPE
|
||||
&trans &kp EQUAL &kp MINUS &kp PLUS &kp LBRC &kp RBRC &trans &trans &kp LBKT &kp RBKT &kp SEMI &kp COLON &kp BSLH &trans
|
||||
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
|
||||
>;
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
// | SHFT | | | | | | | _ | + | { | } | "|" | ~ |
|
||||
// | GUI | | SPC | | ENT | | ALT |
|
||||
bindings = <
|
||||
&kp TAB &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp KP_MULTIPLY &kp LPAR &kp RPAR &kp BSPC
|
||||
&kp TAB &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp ASTRK &kp LPAR &kp RPAR &kp BSPC
|
||||
&kp LCTRL &trans &trans &trans &trans &trans &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp BSLH &kp GRAVE
|
||||
&kp LSHFT &trans &trans &trans &trans &trans &kp UNDER &kp PLUS &kp LBRC &kp RBRC &kp PIPE &kp TILDE
|
||||
&kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
&mt {
|
||||
// flavor = "tap-preferred";
|
||||
// tapping_term_ms = <200>;
|
||||
// tapping-term-ms = <200>;
|
||||
};
|
||||
|
||||
/ {
|
||||
|
|
|
@ -20,4 +20,7 @@ config ZMK_RGB_UNDERGLOW
|
|||
select WS2812_STRIP
|
||||
select SPI
|
||||
|
||||
config ZMK_PM_SOFT_OFF
|
||||
default y if BOARD_NRF52840DK_NRF52840
|
||||
|
||||
endif
|
||||
|
|
|
@ -12,6 +12,15 @@
|
|||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
qdec_sleep: qdec_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(QDEC_A, 1, 11)>,
|
||||
<NRF_PSEL(QDEC_B, 1, 10)>;
|
||||
bias-pull-up;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// Set up the QDEC hardware based driver and give it the same label as the deleted node.
|
||||
|
@ -20,6 +29,38 @@ encoder: &qdec0 {
|
|||
led-pre = <0>;
|
||||
steps = <80>;
|
||||
pinctrl-0 = <&qdec_default>;
|
||||
pinctrl-1 = <&qdec_default>;
|
||||
pinctrl-1 = <&qdec_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
};
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
hw_soft_off: hw_soft_off {
|
||||
compatible = "zmk,behavior-soft-off";
|
||||
#binding-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
soft_off_direct_kscan: soft_off_direct_kscan {
|
||||
compatible = "zmk,kscan-gpio-direct";
|
||||
input-keys = <&button0>;
|
||||
wakeup-source;
|
||||
};
|
||||
|
||||
soft_off_sideband_behaviors {
|
||||
compatible = "zmk,kscan-sideband-behaviors";
|
||||
kscan = <&soft_off_direct_kscan>;
|
||||
soft_off {
|
||||
row = <0>;
|
||||
column = <0>;
|
||||
bindings = <&hw_soft_off>;
|
||||
};
|
||||
};
|
||||
|
||||
soft_off_wakers {
|
||||
compatible = "zmk,soft-off-wakeup-sources";
|
||||
status = "okay";
|
||||
|
||||
wakeup-sources = <&soft_off_direct_kscan>;
|
||||
};
|
||||
};
|
|
@ -40,7 +40,7 @@ nice_view_spi: &arduino_spi {
|
|||
|
||||
/ {
|
||||
chosen {
|
||||
zmk,kscan = &kscan_matrix_comp;
|
||||
zmk,kscan = &kscan_matrix;
|
||||
zmk,backlight = &backlight;
|
||||
zmk,underglow = &led_strip;
|
||||
zmk,matrix-transform = &matrix_transform;
|
||||
|
@ -74,7 +74,6 @@ nice_view_spi: &arduino_spi {
|
|||
map = <
|
||||
RC(0,0) RC(0,1)
|
||||
RC(1,0) RC(1,1)
|
||||
RC(2,0) RC(2,1) RC(2,2)
|
||||
>;
|
||||
};
|
||||
|
||||
|
@ -86,44 +85,12 @@ nice_view_spi: &arduino_spi {
|
|||
map = <
|
||||
RC(0,0) RC(0,1)
|
||||
RC(0,2) RC(0,3)
|
||||
RC(1,0) RC(1,1) RC(1,2)
|
||||
>;
|
||||
};
|
||||
|
||||
|
||||
kscan_matrix_comp: kscan_matrix_comp {
|
||||
compatible = "zmk,kscan-composite";
|
||||
rows = <1>;
|
||||
columns = <7>;
|
||||
|
||||
matrix {
|
||||
kscan = <&kscan_matrix>;
|
||||
};
|
||||
|
||||
toggle {
|
||||
kscan = <&kscan_sp3t_toggle>;
|
||||
row-offset = <2>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
kscan_direct_comp: kscan_direct_comp {
|
||||
compatible = "zmk,kscan-composite";
|
||||
status = "disabled";
|
||||
|
||||
matrix {
|
||||
kscan = <&kscan_direct>;
|
||||
};
|
||||
|
||||
toggle {
|
||||
kscan = <&kscan_sp3t_toggle>;
|
||||
row-offset = <1>;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
kscan_matrix: kscan_matrix {
|
||||
compatible = "zmk,kscan-gpio-matrix";
|
||||
wakeup-source;
|
||||
|
||||
diode-direction = "col2row";
|
||||
|
||||
|
@ -141,6 +108,7 @@ nice_view_spi: &arduino_spi {
|
|||
|
||||
kscan_direct: kscan_direct {
|
||||
compatible = "zmk,kscan-gpio-direct";
|
||||
wakeup-source;
|
||||
status = "disabled";
|
||||
|
||||
input-gpios
|
||||
|
|
|
@ -9,42 +9,21 @@
|
|||
#include <dt-bindings/zmk/backlight.h>
|
||||
#include <dt-bindings/zmk/bt.h>
|
||||
#include <dt-bindings/zmk/ext_power.h>
|
||||
#include <dt-bindings/zmk/outputs.h>
|
||||
#include <dt-bindings/zmk/rgb.h>
|
||||
|
||||
// Uncomment the following block if using the "Direct Wire" jumper to switch the matrix to a direct wire.
|
||||
// Uncomment the following lines if using the "Direct Wire" jumper to switch the matrix to a direct wire.
|
||||
|
||||
/* :REMOVE ME
|
||||
// &kscan_direct { status = "okay"; };
|
||||
// &kscan_matrix { status = "disabled"; };
|
||||
|
||||
&kscan_direct_comp { status = "okay"; };
|
||||
&kscan_direct { status = "okay"; };
|
||||
&kscan_matrix_comp { status = "disabled"; };
|
||||
&kscan_matrix { status = "disabled"; };
|
||||
// / {
|
||||
// chosen {
|
||||
// zmk,matrix-transform = &direct_matrix_transform;
|
||||
// zmk,kscan = &kscan_direct;
|
||||
// };
|
||||
// };
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
zmk,matrix-transform = &direct_matrix_transform;
|
||||
zmk,kscan = &kscan_direct_comp;
|
||||
};
|
||||
};
|
||||
|
||||
REMOVE ME: */
|
||||
|
||||
|
||||
/ {
|
||||
macros {
|
||||
ZMK_MACRO(ble_zero,
|
||||
wait-ms = <1>;
|
||||
tap-ms = <1>;
|
||||
bindings = <&out OUT_BLE &bt BT_SEL 0>;
|
||||
)
|
||||
ZMK_MACRO(ble_one,
|
||||
wait-ms = <1>;
|
||||
tap-ms = <1>;
|
||||
bindings = <&out OUT_BLE &bt BT_SEL 1>;
|
||||
)
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
|
||||
|
@ -52,8 +31,6 @@ REMOVE ME: */
|
|||
bindings = <
|
||||
&kp A &bl BL_TOG
|
||||
&rgb_ug RGB_EFF &bt BT_CLR
|
||||
|
||||
&out OUT_USB &ble_zero &ble_one
|
||||
>;
|
||||
|
||||
sensor-bindings = <&inc_dec_kp C_VOL_UP C_VOL_DN>;
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
|
||||
#include "zmk_uno.dtsi"
|
||||
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/bt.h>
|
||||
#include <dt-bindings/zmk/outputs.h>
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
zmk,matrix-transform = &matrix_transform;
|
||||
|
@ -19,4 +23,37 @@
|
|||
};
|
||||
};
|
||||
|
||||
macros {
|
||||
ZMK_MACRO(ble_zero,
|
||||
wait-ms = <1>;
|
||||
tap-ms = <1>;
|
||||
bindings = <&out OUT_BLE &bt BT_SEL 0>;
|
||||
)
|
||||
ZMK_MACRO(ble_one,
|
||||
wait-ms = <1>;
|
||||
tap-ms = <1>;
|
||||
bindings = <&out OUT_BLE &bt BT_SEL 1>;
|
||||
)
|
||||
};
|
||||
|
||||
endpoint_sideband_behaviors {
|
||||
compatible = "zmk,kscan-sideband-behaviors";
|
||||
kscan = <&kscan_sp3t_toggle>;
|
||||
|
||||
first_toggle_sideband: first_toggle_sideband {
|
||||
column = <0>;
|
||||
bindings = <&out OUT_USB>;
|
||||
};
|
||||
|
||||
second_toggle_sideband: second_toggle_sideband {
|
||||
column = <1>;
|
||||
bindings = <&ble_zero>;
|
||||
};
|
||||
|
||||
third_toggle_sideband: third_toggle_sideband {
|
||||
column = <2>;
|
||||
bindings = <&ble_one>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -17,16 +17,15 @@
|
|||
|
||||
split_matrix_transform: split_matrix_transform {
|
||||
compatible = "zmk,matrix-transform";
|
||||
rows = <3>;
|
||||
columns = <4>;
|
||||
rows = <4>;
|
||||
columns = <2>;
|
||||
|
||||
map = <
|
||||
RC(0,0) RC(0,1)
|
||||
RC(1,0) RC(1,1)
|
||||
RC(2,0) RC(2,1) RC(2,2)
|
||||
|
||||
RC(3,0) RC(3,1)
|
||||
RC(4,0) RC(4,1)
|
||||
RC(5,0) RC(5,1) RC(5,2)
|
||||
>;
|
||||
};
|
||||
|
||||
|
@ -38,10 +37,9 @@
|
|||
map = <
|
||||
RC(0,0) RC(0,1)
|
||||
RC(0,2) RC(0,3)
|
||||
RC(1,0) RC(1,1) RC(1,2)
|
||||
|
||||
RC(2,0) RC(2,1)
|
||||
RC(2,2) RC(2,3)
|
||||
RC(3,0) RC(3,1) RC(3,2)
|
||||
>;
|
||||
};
|
||||
|
||||
|
|
|
@ -12,38 +12,20 @@
|
|||
#include <dt-bindings/zmk/outputs.h>
|
||||
#include <dt-bindings/zmk/rgb.h>
|
||||
|
||||
// Uncomment the following block if using the "Direct Wire" jumper to switch the matrix to a direct wire.
|
||||
// Uncomment the following lines if using the "Direct Wire" jumper to switch the matrix to a direct wire.
|
||||
|
||||
/* :REMOVE ME
|
||||
|
||||
&kscan_direct_comp { status = "okay"; };
|
||||
&kscan_direct { status = "okay"; };
|
||||
&kscan_matrix_comp { status = "disabled"; };
|
||||
&kscan_matrix { status = "disabled"; };
|
||||
// &kscan_direct { status = "okay"; };
|
||||
// &kscan_matrix { status = "disabled"; };
|
||||
|
||||
// / {
|
||||
// chosen {
|
||||
// zmk,matrix-transform = &split_direct_matrix_transform;
|
||||
// zmk,kscan = &kscan_direct;
|
||||
// };
|
||||
// };
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
zmk,matrix-transform = &split_direct_matrix_transform;
|
||||
zmk,kscan = &kscan_direct_comp;
|
||||
};
|
||||
};
|
||||
|
||||
REMOVE ME: */
|
||||
|
||||
|
||||
/ {
|
||||
macros {
|
||||
ZMK_MACRO(ble_zero,
|
||||
wait-ms = <1>;
|
||||
tap-ms = <1>;
|
||||
bindings = <&out OUT_BLE &bt BT_SEL 0>;
|
||||
)
|
||||
ZMK_MACRO(ble_one,
|
||||
wait-ms = <1>;
|
||||
tap-ms = <1>;
|
||||
bindings = <&out OUT_BLE &bt BT_SEL 1>;
|
||||
)
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
|
@ -53,11 +35,8 @@ REMOVE ME: */
|
|||
&kp A &bl BL_TOG
|
||||
&rgb_ug RGB_EFF &bt BT_CLR
|
||||
|
||||
&out OUT_USB &ble_zero &ble_one
|
||||
|
||||
&kp C &kp D
|
||||
&kp E &kp F
|
||||
&none &none &none
|
||||
>;
|
||||
|
||||
sensor-bindings = <&inc_dec_kp C_VOL_UP C_VOL_DN &inc_dec_kp PG_UP PG_DN>;
|
||||
|
|
|
@ -20,3 +20,4 @@
|
|||
#include <behaviors/backlight.dtsi>
|
||||
#include <behaviors/macros.dtsi>
|
||||
#include <behaviors/mouse_key_press.dtsi>
|
||||
#include <behaviors/soft_off.dtsi>
|
||||
|
|
15
app/dts/behaviors/soft_off.dtsi
Normal file
15
app/dts/behaviors/soft_off.dtsi
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
/omit-if-no-ref/ soft_off: keymap_soft_off {
|
||||
compatible = "zmk,behavior-soft-off";
|
||||
#binding-cells = <0>;
|
||||
split-peripheral-off-on-press;
|
||||
};
|
||||
};
|
||||
};
|
17
app/dts/bindings/behaviors/zmk,behavior-soft-off.yaml
Normal file
17
app/dts/bindings/behaviors/zmk,behavior-soft-off.yaml
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Copyright (c) 2023 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Soft-Off Behavior
|
||||
|
||||
compatible: "zmk,behavior-soft-off"
|
||||
|
||||
include: zero_param.yaml
|
||||
|
||||
properties:
|
||||
hold-time-ms:
|
||||
type: int
|
||||
required: false
|
||||
description: Number of milliseconds the behavior must be held before releasing will actually trigger a soft-off.
|
||||
split-peripheral-off-on-press:
|
||||
type: boolean
|
||||
description: When built for a split peripheral, turn off on press, not release
|
|
@ -16,5 +16,7 @@ properties:
|
|||
required: true
|
||||
quick-release:
|
||||
type: boolean
|
||||
lazy:
|
||||
type: boolean
|
||||
ignore-modifiers:
|
||||
type: boolean
|
||||
|
|
30
app/dts/bindings/kscan/zmk,kscan-sideband-behaviors.yaml
Normal file
30
app/dts/bindings/kscan/zmk,kscan-sideband-behaviors.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Copyright (c) 2023, The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: |
|
||||
kscan sideband behavior runner. Only basic system behaviors should be used,
|
||||
since no keymap processing occurs when using them. Primarily, that means avoiding
|
||||
using tap-holds, sticky keys, etc. as sideband behaviors.
|
||||
|
||||
compatible: "zmk,kscan-sideband-behaviors"
|
||||
|
||||
include: kscan.yaml
|
||||
|
||||
properties:
|
||||
kscan:
|
||||
type: phandle
|
||||
required: true
|
||||
|
||||
child-binding:
|
||||
description: "A sideband behavior tied to a row/column pair"
|
||||
|
||||
properties:
|
||||
row:
|
||||
type: int
|
||||
default: 0
|
||||
column:
|
||||
type: int
|
||||
required: true
|
||||
bindings:
|
||||
type: phandle-array
|
||||
required: true
|
18
app/dts/bindings/zmk,gpio-key-wakeup-trigger.yaml
Normal file
18
app/dts/bindings/zmk,gpio-key-wakeup-trigger.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Copyright (c) 2023 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: |
|
||||
Driver for a dedicated key for waking the device from sleep
|
||||
|
||||
compatible: "zmk,gpio-key-wakeup-trigger"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
trigger:
|
||||
type: phandle
|
||||
required: true
|
||||
description: The GPIO key that triggers wake via interrupt
|
||||
extra-gpios:
|
||||
type: phandle-array
|
||||
description: Optional set of pins that should be set active before sleeping.
|
14
app/dts/bindings/zmk,soft-off-wakeup-sources.yaml
Normal file
14
app/dts/bindings/zmk,soft-off-wakeup-sources.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Copyright (c) 2023 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: |
|
||||
Description of all possible wakeup-sources from a forced
|
||||
soft-off state.
|
||||
|
||||
compatible: "zmk,soft-off-wakeup-sources"
|
||||
|
||||
properties:
|
||||
wakeup-sources:
|
||||
type: phandles
|
||||
required: true
|
||||
description: List of wakeup-sources that should be enabled to wake the system from forced soft-off state.
|
|
@ -1109,7 +1109,7 @@
|
|||
#define HID_USAGE_CONSUMER_AC_RENAME (0x298) // Sel
|
||||
#define HID_USAGE_CONSUMER_AC_MERGE (0x299) // Sel
|
||||
#define HID_USAGE_CONSUMER_AC_SPLIT (0x29A) // Sel
|
||||
#define HID_USAGE_CONSUMER_AC_DISRIBUTE_HORIZONTALLY (0x29B) // Sel
|
||||
#define HID_USAGE_CONSUMER_AC_DISTRIBUTE_HORIZONTALLY (0x29B) // Sel
|
||||
#define HID_USAGE_CONSUMER_AC_DISTRIBUTE_VERTICALLY (0x29C) // Sel
|
||||
#define HID_USAGE_CONSUMER_AC_NEXT_KEYBOARD_LAYOUT_SELECT (0x29D) // Sel
|
||||
#define HID_USAGE_CONSUMER_AC_NAVIGATION_GUIDANCE (0x29E) // Sel
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/linker/linker-defs.h>
|
||||
|
||||
ITERABLE_SECTION_RAM(zmk_pm_device_slots, 4)
|
|
@ -73,3 +73,5 @@ int zmk_endpoints_send_report(uint16_t usage_page);
|
|||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
int zmk_endpoints_send_mouse_report();
|
||||
#endif // IS_ENABLE(CONFIG_ZMK_MOUSE)
|
||||
|
||||
void zmk_endpoints_clear_current(void);
|
||||
|
|
12
app/include/zmk/pm.h
Normal file
12
app/include/zmk/pm.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
int zmk_pm_suspend_devices(void);
|
||||
void zmk_pm_resume_devices(void);
|
||||
|
||||
int zmk_pm_soft_off(void);
|
15
app/include/zmk/settings.h
Normal file
15
app/include/zmk/settings.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Erases all saved settings.
|
||||
*
|
||||
* @note This does not automatically update any code using Zephyr's settings
|
||||
* subsystem. This should typically be followed by a call to sys_reboot().
|
||||
*/
|
||||
int zmk_settings_erase(void);
|
|
@ -12,6 +12,7 @@
|
|||
#include <zephyr/drivers/kscan.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#include <zmk/debounce.h>
|
||||
|
@ -41,9 +42,14 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#define COND_POLL_OR_INTERRUPTS(pollcode, intcode) \
|
||||
COND_CODE_1(CONFIG_ZMK_KSCAN_DIRECT_POLLING, pollcode, intcode)
|
||||
|
||||
#define INST_INPUTS_LEN(n) DT_INST_PROP_LEN(n, input_gpios)
|
||||
#define KSCAN_DIRECT_INPUT_CFG_INIT(idx, inst_idx) \
|
||||
#define INST_INPUTS_LEN(n) \
|
||||
COND_CODE_1(DT_INST_NODE_HAS_PROP(n, input_gpios), (DT_INST_PROP_LEN(n, input_gpios)), \
|
||||
(DT_INST_PROP_LEN(n, input_keys)))
|
||||
|
||||
#define KSCAN_GPIO_DIRECT_INPUT_CFG_INIT(idx, inst_idx) \
|
||||
KSCAN_GPIO_GET_BY_IDX(DT_DRV_INST(inst_idx), input_gpios, idx)
|
||||
#define KSCAN_KEY_DIRECT_INPUT_CFG_INIT(idx, inst_idx) \
|
||||
KSCAN_GPIO_GET_BY_IDX(DT_INST_PROP_BY_IDX(inst_idx, input_keys, idx), gpios, 0)
|
||||
|
||||
struct kscan_direct_irq_callback {
|
||||
const struct device *dev;
|
||||
|
@ -318,6 +324,21 @@ static int kscan_direct_init(const struct device *dev) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
|
||||
static int kscan_direct_pm_action(const struct device *dev, enum pm_device_action action) {
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
return kscan_direct_disable(dev);
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
return kscan_direct_enable(dev);
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
|
||||
static const struct kscan_driver_api kscan_direct_api = {
|
||||
.config = kscan_direct_configure,
|
||||
.enable_callback = kscan_direct_enable,
|
||||
|
@ -331,7 +352,9 @@ static const struct kscan_driver_api kscan_direct_api = {
|
|||
"ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \
|
||||
\
|
||||
static struct kscan_gpio kscan_direct_inputs_##n[] = { \
|
||||
LISTIFY(INST_INPUTS_LEN(n), KSCAN_DIRECT_INPUT_CFG_INIT, (, ), n)}; \
|
||||
COND_CODE_1(DT_INST_NODE_HAS_PROP(n, input_gpios), \
|
||||
(LISTIFY(INST_INPUTS_LEN(n), KSCAN_GPIO_DIRECT_INPUT_CFG_INIT, (, ), n)), \
|
||||
(LISTIFY(INST_INPUTS_LEN(n), KSCAN_KEY_DIRECT_INPUT_CFG_INIT, (, ), n)))}; \
|
||||
\
|
||||
static struct zmk_debounce_state kscan_direct_state_##n[INST_INPUTS_LEN(n)]; \
|
||||
\
|
||||
|
@ -354,7 +377,9 @@ static const struct kscan_driver_api kscan_direct_api = {
|
|||
.toggle_mode = DT_INST_PROP(n, toggle_mode), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, &kscan_direct_init, NULL, &kscan_direct_data_##n, \
|
||||
PM_DEVICE_DT_INST_DEFINE(n, kscan_direct_pm_action); \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, &kscan_direct_init, PM_DEVICE_DT_INST_GET(n), &kscan_direct_data_##n, \
|
||||
&kscan_direct_config_##n, POST_KERNEL, CONFIG_KSCAN_INIT_PRIORITY, \
|
||||
&kscan_direct_api);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/kscan.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/__assert.h>
|
||||
|
@ -421,6 +422,21 @@ static int kscan_matrix_init(const struct device *dev) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
|
||||
static int kscan_matrix_pm_action(const struct device *dev, enum pm_device_action action) {
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
return kscan_matrix_disable(dev);
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
return kscan_matrix_enable(dev);
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
|
||||
static const struct kscan_driver_api kscan_matrix_api = {
|
||||
.config = kscan_matrix_configure,
|
||||
.enable_callback = kscan_matrix_enable,
|
||||
|
@ -465,7 +481,9 @@ static const struct kscan_driver_api kscan_matrix_api = {
|
|||
.diode_direction = INST_DIODE_DIR(n), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, &kscan_matrix_init, NULL, &kscan_matrix_data_##n, \
|
||||
PM_DEVICE_DT_INST_DEFINE(n, kscan_matrix_pm_action); \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, &kscan_matrix_init, PM_DEVICE_DT_INST_GET(n), &kscan_matrix_data_##n, \
|
||||
&kscan_matrix_config_##n, POST_KERNEL, CONFIG_KSCAN_INIT_PRIORITY, \
|
||||
&kscan_matrix_api);
|
||||
|
||||
|
|
|
@ -10,7 +10,11 @@ include: kscan.yaml
|
|||
properties:
|
||||
input-gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
required: false
|
||||
input-keys:
|
||||
type: phandles
|
||||
required: false
|
||||
description: List of gpio-key references
|
||||
debounce-period:
|
||||
type: int
|
||||
required: false
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#include <zephyr/device.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/pm/device_runtime.h>
|
||||
#include <zephyr/sys/poweroff.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
@ -20,6 +18,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#include <zmk/events/position_state_changed.h>
|
||||
#include <zmk/events/sensor_event.h>
|
||||
|
||||
#include <zmk/pm.h>
|
||||
|
||||
#include <zmk/activity.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DEVICE_STACK)
|
||||
|
@ -30,63 +30,6 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#include <zmk/ble.h>
|
||||
#endif
|
||||
|
||||
// Reimplement some of the device work from Zephyr PM to work with the new `sys_poweroff` API.
|
||||
// TODO: Tweak this to smarter runtime PM of subsystems on sleep.
|
||||
|
||||
#ifdef CONFIG_PM_DEVICE
|
||||
TYPE_SECTION_START_EXTERN(const struct device *, zmk_pm_device_slots);
|
||||
|
||||
#if !defined(CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE)
|
||||
/* Number of devices successfully suspended. */
|
||||
static size_t zmk_num_susp;
|
||||
|
||||
static int zmk_pm_suspend_devices(void) {
|
||||
const struct device *devs;
|
||||
size_t devc;
|
||||
|
||||
devc = z_device_get_all_static(&devs);
|
||||
|
||||
zmk_num_susp = 0;
|
||||
|
||||
for (const struct device *dev = devs + devc - 1; dev >= devs; dev--) {
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Ignore uninitialized devices, busy devices, wake up sources, and
|
||||
* devices with runtime PM enabled.
|
||||
*/
|
||||
if (!device_is_ready(dev) || pm_device_is_busy(dev) || pm_device_state_is_locked(dev) ||
|
||||
pm_device_wakeup_is_enabled(dev) || pm_device_runtime_is_enabled(dev)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND);
|
||||
/* ignore devices not supporting or already at the given state */
|
||||
if ((ret == -ENOSYS) || (ret == -ENOTSUP) || (ret == -EALREADY)) {
|
||||
continue;
|
||||
} else if (ret < 0) {
|
||||
LOG_ERR("Device %s did not enter %s state (%d)", dev->name,
|
||||
pm_device_state_str(PM_DEVICE_STATE_SUSPENDED), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
TYPE_SECTION_START(zmk_pm_device_slots)[zmk_num_susp] = dev;
|
||||
zmk_num_susp++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void zmk_pm_resume_devices(void) {
|
||||
for (int i = (zmk_num_susp - 1); i >= 0; i--) {
|
||||
pm_device_action_run(TYPE_SECTION_START(zmk_pm_device_slots)[i], PM_DEVICE_ACTION_RESUME);
|
||||
}
|
||||
|
||||
zmk_num_susp = 0;
|
||||
}
|
||||
#endif /* !CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE */
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
|
||||
bool is_usb_power_present(void) {
|
||||
#if IS_ENABLED(CONFIG_USB_DEVICE_STACK)
|
||||
return zmk_usb_is_powered();
|
||||
|
|
|
@ -34,11 +34,29 @@ static const struct device *const battery = DEVICE_DT_GET(DT_CHOSEN(zmk_battery)
|
|||
static const struct device *battery;
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE)
|
||||
static uint8_t lithium_ion_mv_to_pct(int16_t bat_mv) {
|
||||
// Simple linear approximation of a battery based off adafruit's discharge graph:
|
||||
// https://learn.adafruit.com/li-ion-and-lipoly-batteries/voltages
|
||||
|
||||
if (bat_mv >= 4200) {
|
||||
return 100;
|
||||
} else if (bat_mv <= 3450) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bat_mv * 2 / 15 - 459;
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE)
|
||||
|
||||
static int zmk_battery_update(const struct device *battery) {
|
||||
struct sensor_value state_of_charge;
|
||||
int rc;
|
||||
|
||||
int rc = sensor_sample_fetch_chan(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE);
|
||||
#if IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_STATE_OF_CHARGE)
|
||||
|
||||
rc = sensor_sample_fetch_chan(battery, SENSOR_CHAN_GAUGE_STATE_OF_CHARGE);
|
||||
if (rc != 0) {
|
||||
LOG_DBG("Failed to fetch battery values: %d", rc);
|
||||
return rc;
|
||||
|
@ -50,6 +68,28 @@ static int zmk_battery_update(const struct device *battery) {
|
|||
LOG_DBG("Failed to get battery state of charge: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
#elif IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING_FETCH_MODE_LITHIUM_VOLTAGE)
|
||||
rc = sensor_sample_fetch_chan(battery, SENSOR_CHAN_VOLTAGE);
|
||||
if (rc != 0) {
|
||||
LOG_DBG("Failed to fetch battery values: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct sensor_value voltage;
|
||||
rc = sensor_channel_get(battery, SENSOR_CHAN_VOLTAGE, &voltage);
|
||||
|
||||
if (rc != 0) {
|
||||
LOG_DBG("Failed to get battery voltage: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
uint16_t mv = voltage.val1 * 1000 + (voltage.val2 / 1000);
|
||||
state_of_charge.val1 = lithium_ion_mv_to_pct(mv);
|
||||
|
||||
LOG_DBG("State of change %d from %d mv", state_of_charge.val1, mv);
|
||||
#else
|
||||
#error "Not a supported reporting fetch mode"
|
||||
#endif
|
||||
|
||||
if (last_state_of_charge != state_of_charge.val1) {
|
||||
last_state_of_charge = state_of_charge.val1;
|
||||
|
|
87
app/src/behaviors/behavior_soft_off.c
Normal file
87
app/src/behaviors/behavior_soft_off.c
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_soft_off
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zmk/pm.h>
|
||||
#include <zmk/behavior.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
struct behavior_soft_off_config {
|
||||
bool split_peripheral_turn_off_on_press;
|
||||
uint32_t hold_time_ms;
|
||||
};
|
||||
|
||||
struct behavior_soft_off_data {
|
||||
uint32_t press_start;
|
||||
};
|
||||
|
||||
#define IS_SPLIT_PERIPHERAL \
|
||||
(IS_ENABLED(CONFIG_ZMK_SPLIT) && !IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL))
|
||||
|
||||
static int behavior_soft_off_init(const struct device *dev) { return 0; };
|
||||
|
||||
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
|
||||
struct behavior_soft_off_data *data = dev->data;
|
||||
const struct behavior_soft_off_config *config = dev->config;
|
||||
|
||||
if (IS_SPLIT_PERIPHERAL && config->split_peripheral_turn_off_on_press) {
|
||||
zmk_pm_soft_off();
|
||||
} else {
|
||||
data->press_start = k_uptime_get();
|
||||
}
|
||||
|
||||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
}
|
||||
|
||||
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
|
||||
struct behavior_soft_off_data *data = dev->data;
|
||||
const struct behavior_soft_off_config *config = dev->config;
|
||||
|
||||
if (config->hold_time_ms == 0) {
|
||||
LOG_DBG("No hold time set, triggering soft off");
|
||||
zmk_pm_soft_off();
|
||||
} else {
|
||||
uint32_t hold_time = k_uptime_get() - data->press_start;
|
||||
|
||||
if (hold_time > config->hold_time_ms) {
|
||||
zmk_pm_soft_off();
|
||||
} else {
|
||||
LOG_INF("Not triggering soft off: held for %d and hold time is %d", hold_time,
|
||||
config->hold_time_ms);
|
||||
}
|
||||
}
|
||||
|
||||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
}
|
||||
|
||||
static const struct behavior_driver_api behavior_soft_off_driver_api = {
|
||||
.binding_pressed = on_keymap_binding_pressed,
|
||||
.binding_released = on_keymap_binding_released,
|
||||
.locality = BEHAVIOR_LOCALITY_GLOBAL,
|
||||
};
|
||||
|
||||
#define BSO_INST(n) \
|
||||
static const struct behavior_soft_off_config bso_config_##n = { \
|
||||
.hold_time_ms = DT_INST_PROP_OR(n, hold_time_ms, 0), \
|
||||
.split_peripheral_turn_off_on_press = \
|
||||
DT_INST_PROP_OR(n, split_peripheral_off_on_press, false), \
|
||||
}; \
|
||||
static struct behavior_soft_off_data bso_data_##n = {}; \
|
||||
BEHAVIOR_DT_INST_DEFINE(0, behavior_soft_off_init, NULL, &bso_data_##n, &bso_config_##n, \
|
||||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||
&behavior_soft_off_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(BSO_INST)
|
|
@ -33,6 +33,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
struct behavior_sticky_key_config {
|
||||
uint32_t release_after_ms;
|
||||
bool quick_release;
|
||||
bool lazy;
|
||||
bool ignore_modifiers;
|
||||
struct zmk_behavior_binding behavior;
|
||||
};
|
||||
|
@ -120,6 +121,15 @@ static inline int release_sticky_key_behavior(struct active_sticky_key *sticky_k
|
|||
return behavior_keymap_binding_released(&binding, event);
|
||||
}
|
||||
|
||||
static inline void on_sticky_key_timeout(struct active_sticky_key *sticky_key) {
|
||||
// If the key is lazy, a release is not needed on timeout
|
||||
if (sticky_key->config->lazy) {
|
||||
clear_sticky_key(sticky_key);
|
||||
} else {
|
||||
release_sticky_key_behavior(sticky_key, sticky_key->release_at);
|
||||
}
|
||||
}
|
||||
|
||||
static int stop_timer(struct active_sticky_key *sticky_key) {
|
||||
int timer_cancel_result = k_work_cancel_delayable(&sticky_key->release_timer);
|
||||
if (timer_cancel_result == -EINPROGRESS) {
|
||||
|
@ -146,8 +156,11 @@ static int on_sticky_key_binding_pressed(struct zmk_behavior_binding *binding,
|
|||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
}
|
||||
|
||||
press_sticky_key_behavior(sticky_key, event.timestamp);
|
||||
LOG_DBG("%d new sticky_key", event.position);
|
||||
if (!sticky_key->config->lazy) {
|
||||
// press the key now if it's not lazy
|
||||
press_sticky_key_behavior(sticky_key, event.timestamp);
|
||||
}
|
||||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
}
|
||||
|
||||
|
@ -191,14 +204,21 @@ static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) {
|
|||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
// keep track whether the event has been reraised, so we only reraise it once
|
||||
bool event_reraised = false;
|
||||
// we want to make sure every sticky key is given a chance to lazy press their behavior before
|
||||
// the event gets reraised, and release their behavior after the event is reraised, so we keep
|
||||
// track of them. this allows us to ensure the sticky key is pressed and released "around" the
|
||||
// other key, especially in the case of lazy keys.
|
||||
struct active_sticky_key *sticky_keys_to_press_before_reraise[ZMK_BHV_STICKY_KEY_MAX_HELD];
|
||||
struct active_sticky_key *sticky_keys_to_release_after_reraise[ZMK_BHV_STICKY_KEY_MAX_HELD];
|
||||
|
||||
// reraising the event frees it, so make a copy of any event data we might
|
||||
// need after it's been freed.
|
||||
const struct zmk_keycode_state_changed ev_copy = *ev;
|
||||
|
||||
for (int i = 0; i < ZMK_BHV_STICKY_KEY_MAX_HELD; i++) {
|
||||
sticky_keys_to_press_before_reraise[i] = NULL;
|
||||
sticky_keys_to_release_after_reraise[i] = NULL;
|
||||
|
||||
struct active_sticky_key *sticky_key = &active_sticky_keys[i];
|
||||
if (sticky_key->position == ZMK_BHV_STICKY_KEY_POSITION_FREE) {
|
||||
continue;
|
||||
|
@ -212,14 +232,6 @@ static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) {
|
|||
continue;
|
||||
}
|
||||
|
||||
// If this event was queued, the timer may be triggered late or not at all.
|
||||
// Release the sticky key if the timer should've run out in the meantime.
|
||||
if (sticky_key->release_at != 0 && ev_copy.timestamp > sticky_key->release_at) {
|
||||
stop_timer(sticky_key);
|
||||
release_sticky_key_behavior(sticky_key, sticky_key->release_at);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev_copy.state) { // key down
|
||||
if (sticky_key->config->ignore_modifiers &&
|
||||
is_mod(ev_copy.usage_page, ev_copy.keycode)) {
|
||||
|
@ -231,17 +243,25 @@ static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) {
|
|||
// this sticky key is already in use for a keycode
|
||||
continue;
|
||||
}
|
||||
if (sticky_key->timer_started) {
|
||||
|
||||
// we don't want the timer to release the sticky key before the other key is released
|
||||
stop_timer(sticky_key);
|
||||
|
||||
// If this event was queued, the timer may be triggered late or not at all.
|
||||
// Release the sticky key if the timer should've run out in the meantime.
|
||||
if (sticky_key->release_at != 0 && ev_copy.timestamp > sticky_key->release_at) {
|
||||
on_sticky_key_timeout(sticky_key);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sticky_key->config->lazy) {
|
||||
// if the sticky key is lazy, we need to press it before the event is reraised
|
||||
sticky_keys_to_press_before_reraise[i] = sticky_key;
|
||||
}
|
||||
if (sticky_key->timer_started) {
|
||||
if (sticky_key->config->quick_release) {
|
||||
// immediately release the sticky key after the key press is handled.
|
||||
if (!event_reraised) {
|
||||
struct zmk_keycode_state_changed_event dupe_ev =
|
||||
copy_raised_zmk_keycode_state_changed(ev);
|
||||
ZMK_EVENT_RAISE_AFTER(dupe_ev, behavior_sticky_key);
|
||||
event_reraised = true;
|
||||
}
|
||||
release_sticky_key_behavior(sticky_key, ev_copy.timestamp);
|
||||
sticky_keys_to_release_after_reraise[i] = sticky_key;
|
||||
}
|
||||
}
|
||||
sticky_key->modified_key_usage_page = ev_copy.usage_page;
|
||||
|
@ -251,14 +271,39 @@ static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh) {
|
|||
sticky_key->modified_key_usage_page == ev_copy.usage_page &&
|
||||
sticky_key->modified_key_keycode == ev_copy.keycode) {
|
||||
stop_timer(sticky_key);
|
||||
sticky_keys_to_release_after_reraise[i] = sticky_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// give each sticky key a chance to press their behavior before the event is reraised
|
||||
for (int i = 0; i < ZMK_BHV_STICKY_KEY_MAX_HELD; i++) {
|
||||
struct active_sticky_key *sticky_key = sticky_keys_to_press_before_reraise[i];
|
||||
if (!sticky_key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
press_sticky_key_behavior(sticky_key, ev_copy.timestamp);
|
||||
}
|
||||
// give each sticky key a chance to release their behavior after the event is reraised, lazily
|
||||
// reraising. keep track whether the event has been reraised so we only reraise it once
|
||||
bool event_reraised = false;
|
||||
for (int i = 0; i < ZMK_BHV_STICKY_KEY_MAX_HELD; i++) {
|
||||
struct active_sticky_key *sticky_key = sticky_keys_to_release_after_reraise[i];
|
||||
if (!sticky_key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!event_reraised) {
|
||||
struct zmk_keycode_state_changed_event dupe_ev =
|
||||
copy_raised_zmk_keycode_state_changed(ev);
|
||||
ZMK_EVENT_RAISE_AFTER(dupe_ev, behavior_sticky_key);
|
||||
event_reraised = true;
|
||||
}
|
||||
release_sticky_key_behavior(sticky_key, ev_copy.timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event_reraised) {
|
||||
return ZMK_EV_EVENT_CAPTURED;
|
||||
}
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
|
||||
return event_reraised ? ZMK_EV_EVENT_CAPTURED : ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
void behavior_sticky_key_timer_handler(struct k_work *item) {
|
||||
|
@ -271,7 +316,7 @@ void behavior_sticky_key_timer_handler(struct k_work *item) {
|
|||
if (sticky_key->timer_cancelled) {
|
||||
sticky_key->timer_cancelled = false;
|
||||
} else {
|
||||
release_sticky_key_behavior(sticky_key, sticky_key->release_at);
|
||||
on_sticky_key_timeout(sticky_key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,8 +340,9 @@ static struct behavior_sticky_key_data behavior_sticky_key_data;
|
|||
static struct behavior_sticky_key_config behavior_sticky_key_config_##n = { \
|
||||
.behavior = ZMK_KEYMAP_EXTRACT_BINDING(0, DT_DRV_INST(n)), \
|
||||
.release_after_ms = DT_INST_PROP(n, release_after_ms), \
|
||||
.ignore_modifiers = DT_INST_PROP(n, ignore_modifiers), \
|
||||
.quick_release = DT_INST_PROP(n, quick_release), \
|
||||
.lazy = DT_INST_PROP(n, lazy), \
|
||||
.ignore_modifiers = DT_INST_PROP(n, ignore_modifiers), \
|
||||
}; \
|
||||
BEHAVIOR_DT_INST_DEFINE(n, behavior_sticky_key_init, NULL, &behavior_sticky_key_data, \
|
||||
&behavior_sticky_key_config_##n, POST_KERNEL, \
|
||||
|
|
|
@ -340,7 +340,7 @@ static int zmk_endpoints_init(void) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void disconnect_current_endpoint(void) {
|
||||
void zmk_endpoints_clear_current(void) {
|
||||
zmk_hid_keyboard_clear();
|
||||
zmk_hid_consumer_clear();
|
||||
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
|
||||
|
@ -356,7 +356,7 @@ static void update_current_endpoint(void) {
|
|||
|
||||
if (!zmk_endpoint_instance_eq(new_instance, current_instance)) {
|
||||
// Cancel all current keypresses so keys don't stay held on the old endpoint.
|
||||
disconnect_current_endpoint();
|
||||
zmk_endpoints_clear_current();
|
||||
|
||||
current_instance = new_instance;
|
||||
|
||||
|
|
106
app/src/gpio_key_wakeup_trigger.c
Normal file
106
app/src/gpio_key_wakeup_trigger.c
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/pm/pm.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#define DT_DRV_COMPAT zmk_gpio_key_wakeup_trigger
|
||||
|
||||
struct gpio_key_wakeup_trigger_config {
|
||||
struct gpio_dt_spec trigger;
|
||||
size_t extra_gpios_count;
|
||||
struct gpio_dt_spec extra_gpios[];
|
||||
};
|
||||
|
||||
static int zmk_gpio_key_wakeup_trigger_init(const struct device *dev) {
|
||||
#if IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
pm_device_init_suspended(dev);
|
||||
pm_device_wakeup_enable(dev, true);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
|
||||
static int gpio_key_wakeup_trigger_pm_resume(const struct device *dev) {
|
||||
const struct gpio_key_wakeup_trigger_config *config = dev->config;
|
||||
|
||||
int ret = gpio_pin_interrupt_configure_dt(&config->trigger, GPIO_INT_LEVEL_ACTIVE);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to configure wakeup trigger key GPIO pin interrupt (%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (int i = 0; i < config->extra_gpios_count; i++) {
|
||||
ret = gpio_pin_configure_dt(&config->extra_gpios[i], GPIO_OUTPUT_ACTIVE);
|
||||
if (ret < 0) {
|
||||
LOG_WRN("Failed to set extra GPIO pin active for waker (%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpio_key_wakeup_trigger_pm_suspend(const struct device *dev) {
|
||||
const struct gpio_key_wakeup_trigger_config *config = dev->config;
|
||||
|
||||
int ret = gpio_pin_interrupt_configure_dt(&config->trigger, GPIO_INT_DISABLE);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to configure wakeup trigger key GPIO pin interrupt (%d)", ret);
|
||||
}
|
||||
|
||||
for (int i = 0; i < config->extra_gpios_count; i++) {
|
||||
ret = gpio_pin_configure_dt(&config->extra_gpios[i], GPIO_DISCONNECTED);
|
||||
if (ret < 0) {
|
||||
LOG_WRN("Failed to set extra GPIO pin disconnected for waker (%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The waker is "backwards", in as much as it is designed to be resumed/enabled immediately
|
||||
// before a soft-off state is entered, so it can wake the device from that state later.
|
||||
// So this waker correctly resumes and is ready to wake the device later.
|
||||
static int gpio_key_wakeup_trigger_pm_action(const struct device *dev,
|
||||
enum pm_device_action action) {
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
return gpio_key_wakeup_trigger_pm_resume(dev);
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
return gpio_key_wakeup_trigger_pm_suspend(dev);
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
|
||||
#define WAKEUP_TRIGGER_EXTRA_GPIO_SPEC(idx, n) \
|
||||
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), extra_gpios, idx)
|
||||
|
||||
#define GPIO_KEY_WAKEUP_TRIGGER_INST(n) \
|
||||
const struct gpio_key_wakeup_trigger_config wtk_cfg_##n = { \
|
||||
.trigger = GPIO_DT_SPEC_GET(DT_INST_PROP(n, trigger), gpios), \
|
||||
.extra_gpios = {LISTIFY(DT_PROP_LEN_OR(DT_DRV_INST(n), extra_gpios, 0), \
|
||||
WAKEUP_TRIGGER_EXTRA_GPIO_SPEC, (, ), n)}, \
|
||||
.extra_gpios_count = DT_PROP_LEN_OR(DT_DRV_INST(n), extra_gpios, 0), \
|
||||
}; \
|
||||
PM_DEVICE_DT_INST_DEFINE(n, gpio_key_wakeup_trigger_pm_action); \
|
||||
DEVICE_DT_INST_DEFINE(n, zmk_gpio_key_wakeup_trigger_init, PM_DEVICE_DT_INST_GET(n), NULL, \
|
||||
&wtk_cfg_##n, PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_KEY_WAKEUP_TRIGGER_INST)
|
|
@ -31,6 +31,12 @@ static uint8_t _zmk_keymap_layer_default = 0;
|
|||
|
||||
#define DT_DRV_COMPAT zmk_keymap
|
||||
|
||||
#if !DT_NODE_EXISTS(DT_DRV_INST(0))
|
||||
|
||||
#error "Keymap node not found, check a keymap is available and is has compatible = "zmk,keymap" set"
|
||||
|
||||
#endif
|
||||
|
||||
#define TRANSFORMED_LAYER(node) \
|
||||
{ LISTIFY(DT_PROP_LEN(node, bindings), ZMK_KEYMAP_EXTRACT_BINDING, (, ), node) }
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/bluetooth/addr.h>
|
||||
#include <zephyr/drivers/kscan.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
@ -73,6 +74,12 @@ int zmk_kscan_init(const struct device *dev) {
|
|||
|
||||
k_work_init(&msg_processor.work, zmk_kscan_process_msgq);
|
||||
|
||||
#if IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
if (pm_device_wakeup_is_capable(dev)) {
|
||||
pm_device_wakeup_enable(dev, true);
|
||||
}
|
||||
#endif // IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
|
||||
kscan_config(dev, zmk_kscan_callback);
|
||||
kscan_enable_callback(dev);
|
||||
|
||||
|
|
179
app/src/kscan_sideband_behaviors.c
Normal file
179
app/src/kscan_sideband_behaviors.c
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_kscan_sideband_behaviors
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/drivers/kscan.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/behavior.h>
|
||||
#include <zmk/keymap.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
struct ksbb_entry {
|
||||
struct zmk_behavior_binding binding;
|
||||
uint8_t row;
|
||||
uint8_t column;
|
||||
};
|
||||
|
||||
struct ksbb_config {
|
||||
const struct device *kscan;
|
||||
struct ksbb_entry *entries;
|
||||
size_t entries_len;
|
||||
};
|
||||
|
||||
struct ksbb_data {
|
||||
kscan_callback_t callback;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
#define GET_KSBB_DEV(n) DEVICE_DT_GET(DT_DRV_INST(n)),
|
||||
|
||||
// The kscan callback has no context with it, so we keep a static array of all possible
|
||||
// KSBBs to check when a kscan callback from the "wrapped" inner kscan fires.
|
||||
static const struct device *ksbbs[] = {DT_INST_FOREACH_STATUS_OKAY(GET_KSBB_DEV)};
|
||||
|
||||
const struct device *find_ksbb_for_inner(const struct device *inner_dev) {
|
||||
for (int i = 0; i < ARRAY_SIZE(ksbbs); i++) {
|
||||
const struct device *ksbb = ksbbs[i];
|
||||
const struct ksbb_config *cfg = ksbb->config;
|
||||
|
||||
if (cfg->kscan == inner_dev) {
|
||||
return ksbb;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ksbb_entry *find_sideband_behavior(const struct device *dev, uint32_t row, uint32_t column) {
|
||||
const struct ksbb_config *cfg = dev->config;
|
||||
|
||||
for (int e = 0; e < cfg->entries_len; e++) {
|
||||
struct ksbb_entry *candidate = &cfg->entries[e];
|
||||
|
||||
if (candidate->row == row && candidate->column == column) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ksbb_inner_kscan_callback(const struct device *dev, uint32_t row, uint32_t column,
|
||||
bool pressed) {
|
||||
const struct device *ksbb = find_ksbb_for_inner(dev);
|
||||
if (ksbb) {
|
||||
struct ksbb_data *data = ksbb->data;
|
||||
|
||||
struct ksbb_entry *entry = find_sideband_behavior(ksbb, row, column);
|
||||
if (entry) {
|
||||
struct zmk_behavior_binding_event event = {.position = INT32_MAX,
|
||||
.timestamp = k_uptime_get()};
|
||||
|
||||
if (pressed) {
|
||||
behavior_keymap_binding_pressed(&entry->binding, event);
|
||||
} else {
|
||||
behavior_keymap_binding_released(&entry->binding, event);
|
||||
}
|
||||
}
|
||||
|
||||
if (data->enabled && data->callback) {
|
||||
data->callback(ksbb, row, column, pressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ksbb_configure(const struct device *dev, kscan_callback_t callback) {
|
||||
const struct ksbb_config *cfg = dev->config;
|
||||
struct ksbb_data *data = dev->data;
|
||||
|
||||
data->callback = callback;
|
||||
|
||||
#if IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
if (pm_device_wakeup_is_enabled(dev) && pm_device_wakeup_is_capable(cfg->kscan)) {
|
||||
pm_device_wakeup_enable(cfg->kscan, true);
|
||||
}
|
||||
#endif // IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksbb_enable(const struct device *dev) {
|
||||
struct ksbb_data *data = dev->data;
|
||||
data->enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksbb_disable(const struct device *dev) {
|
||||
struct ksbb_data *data = dev->data;
|
||||
data->enabled = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksbb_init(const struct device *dev) {
|
||||
const struct ksbb_config *config = dev->config;
|
||||
|
||||
if (!device_is_ready(config->kscan)) {
|
||||
LOG_ERR("kscan %s is not ready", config->kscan->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
kscan_config(config->kscan, &ksbb_inner_kscan_callback);
|
||||
kscan_enable_callback(config->kscan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct kscan_driver_api ksbb_api = {
|
||||
.config = ksbb_configure,
|
||||
.enable_callback = ksbb_enable,
|
||||
.disable_callback = ksbb_disable,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
|
||||
static int ksbb_pm_action(const struct device *dev, enum pm_device_action action) {
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
return ksbb_disable(dev);
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
return ksbb_disable(dev);
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
|
||||
#define ENTRY(e) \
|
||||
{ \
|
||||
.row = DT_PROP(e, row), .column = DT_PROP(e, column), \
|
||||
.binding = ZMK_KEYMAP_EXTRACT_BINDING(0, e), \
|
||||
}
|
||||
|
||||
#define KSBB_INST(n) \
|
||||
static struct ksbb_entry entries_##n[] = { \
|
||||
DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(n, ENTRY, (, ))}; \
|
||||
const struct ksbb_config ksbb_config_##n = { \
|
||||
.kscan = DEVICE_DT_GET(DT_INST_PHANDLE(n, kscan)), \
|
||||
.entries = entries_##n, \
|
||||
.entries_len = ARRAY_SIZE(entries_##n), \
|
||||
}; \
|
||||
struct ksbb_data ksbb_data_##n = {}; \
|
||||
PM_DEVICE_DT_INST_DEFINE(n, ksbb_pm_action); \
|
||||
DEVICE_DT_INST_DEFINE(n, ksbb_init, PM_DEVICE_DT_INST_GET(n), &ksbb_data_##n, \
|
||||
&ksbb_config_##n, POST_KERNEL, \
|
||||
CONFIG_ZMK_KSCAN_SIDEBAND_BEHAVIORS_INIT_PRIORITY, &ksbb_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(KSBB_INST)
|
137
app/src/pm.c
Normal file
137
app/src/pm.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/pm/device_runtime.h>
|
||||
#include <zephyr/pm/pm.h>
|
||||
#include <zephyr/sys/poweroff.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/endpoints.h>
|
||||
|
||||
// Reimplement some of the device work from Zephyr PM to work with the new `sys_poweroff` API.
|
||||
// TODO: Tweak this to smarter runtime PM of subsystems on sleep.
|
||||
|
||||
#ifdef CONFIG_ZMK_PM_DEVICE_SUSPEND_RESUME
|
||||
TYPE_SECTION_START_EXTERN(const struct device *, pm_device_slots);
|
||||
|
||||
#if !defined(CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE)
|
||||
/* Number of devices successfully suspended. */
|
||||
static size_t zmk_num_susp;
|
||||
|
||||
int zmk_pm_suspend_devices(void) {
|
||||
const struct device *devs;
|
||||
size_t devc;
|
||||
|
||||
devc = z_device_get_all_static(&devs);
|
||||
|
||||
zmk_num_susp = 0;
|
||||
|
||||
for (const struct device *dev = devs + devc - 1; dev >= devs; dev--) {
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Ignore uninitialized devices, busy devices, wake up sources, and
|
||||
* devices with runtime PM enabled.
|
||||
*/
|
||||
if (!device_is_ready(dev) || pm_device_is_busy(dev) || pm_device_state_is_locked(dev) ||
|
||||
pm_device_wakeup_is_enabled(dev) || pm_device_runtime_is_enabled(dev)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND);
|
||||
/* ignore devices not supporting or already at the given state */
|
||||
if ((ret == -ENOSYS) || (ret == -ENOTSUP) || (ret == -EALREADY)) {
|
||||
continue;
|
||||
} else if (ret < 0) {
|
||||
LOG_ERR("Device %s did not enter %s state (%d)", dev->name,
|
||||
pm_device_state_str(PM_DEVICE_STATE_SUSPENDED), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
TYPE_SECTION_START(pm_device_slots)[zmk_num_susp] = dev;
|
||||
zmk_num_susp++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void zmk_pm_resume_devices(void) {
|
||||
for (int i = (zmk_num_susp - 1); i >= 0; i--) {
|
||||
pm_device_action_run(TYPE_SECTION_START(pm_device_slots)[i], PM_DEVICE_ACTION_RESUME);
|
||||
}
|
||||
|
||||
zmk_num_susp = 0;
|
||||
}
|
||||
#endif /* !CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE */
|
||||
#endif /* CONFIG_ZMK_PM_DEVICE_SUSPEND_RESUME */
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_PM_SOFT_OFF)
|
||||
|
||||
#define HAS_WAKERS DT_HAS_COMPAT_STATUS_OKAY(zmk_soft_off_wakeup_sources)
|
||||
|
||||
#if HAS_WAKERS
|
||||
|
||||
#define DEVICE_WITH_SEP(node_id, prop, idx) DEVICE_DT_GET(DT_PROP_BY_IDX(node_id, prop, idx)),
|
||||
|
||||
const struct device *soft_off_wakeup_sources[] = {
|
||||
DT_FOREACH_PROP_ELEM(DT_INST(0, zmk_soft_off_wakeup_sources), wakeup_sources, DEVICE_WITH_SEP)};
|
||||
|
||||
#endif
|
||||
|
||||
int zmk_pm_soft_off(void) {
|
||||
#if IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
size_t device_count;
|
||||
const struct device *devs;
|
||||
|
||||
#if !IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
zmk_endpoints_clear_current();
|
||||
// Need to sleep to give any other threads a chance so submit endpoint data.
|
||||
k_sleep(K_MSEC(100));
|
||||
#endif
|
||||
|
||||
device_count = z_device_get_all_static(&devs);
|
||||
|
||||
// There may be some matrix/direct kscan devices that would be used for wakeup
|
||||
// from normal "inactive goes to sleep" behavior, so disable them as wakeup devices
|
||||
// and then suspend them so we're ready to take over setting up our system
|
||||
// and then putting it into an off state.
|
||||
LOG_DBG("soft-on-off pressed cb: suspend devices");
|
||||
for (int i = 0; i < device_count; i++) {
|
||||
const struct device *dev = &devs[i];
|
||||
|
||||
if (pm_device_wakeup_is_enabled(dev)) {
|
||||
pm_device_wakeup_enable(dev, false);
|
||||
}
|
||||
pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND);
|
||||
}
|
||||
#endif // IS_ENABLED(CONFIG_PM_DEVICE)
|
||||
|
||||
#if HAS_WAKERS
|
||||
for (int i = 0; i < ARRAY_SIZE(soft_off_wakeup_sources); i++) {
|
||||
const struct device *dev = soft_off_wakeup_sources[i];
|
||||
pm_device_wakeup_enable(dev, true);
|
||||
pm_device_action_run(dev, PM_DEVICE_ACTION_RESUME);
|
||||
}
|
||||
#endif // HAS_WAKERS
|
||||
|
||||
int err = zmk_pm_suspend_devices();
|
||||
if (err < 0) {
|
||||
zmk_pm_resume_devices();
|
||||
return err;
|
||||
}
|
||||
|
||||
LOG_DBG("soft-off: go to sleep");
|
||||
sys_poweroff();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_PM_SOFT_OFF)
|
9
app/src/settings/CMakeLists.txt
Normal file
9
app/src/settings/CMakeLists.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2023 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
target_sources_ifdef(CONFIG_SETTINGS_NONE app PRIVATE reset_settings_none.c)
|
||||
target_sources_ifdef(CONFIG_SETTINGS_FCB app PRIVATE reset_settings_fcb.c)
|
||||
target_sources_ifdef(CONFIG_SETTINGS_FILE app PRIVATE reset_settings_file.c)
|
||||
target_sources_ifdef(CONFIG_SETTINGS_NVS app PRIVATE reset_settings_nvs.c)
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_SETTINGS_RESET_ON_START app PRIVATE reset_settings_on_start.c)
|
15
app/src/settings/reset_settings_fcb.c
Normal file
15
app/src/settings/reset_settings_fcb.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zmk/settings.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
int zmk_settings_erase(void) {
|
||||
LOG_ERR("Settings reset is not implemented for deprecated FCB backend.");
|
||||
return -ENOSYS;
|
||||
}
|
23
app/src/settings/reset_settings_file.c
Normal file
23
app/src/settings/reset_settings_file.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/fs/fs.h>
|
||||
|
||||
#include <zmk/settings.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
int zmk_settings_erase(void) {
|
||||
LOG_INF("Erasing settings file");
|
||||
|
||||
int rc = fs_unlink(CONFIG_SETTINGS_FS_FILE);
|
||||
if (rc) {
|
||||
LOG_ERR("Failed to unlink '%s': %d", CONFIG_SETTINGS_FS_FILE, rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
12
app/src/settings/reset_settings_none.c
Normal file
12
app/src/settings/reset_settings_none.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zmk/settings.h>
|
||||
|
||||
int zmk_settings_erase(void) {
|
||||
// No storage backend; nothing to do
|
||||
return 0;
|
||||
}
|
39
app/src/settings/reset_settings_nvs.c
Normal file
39
app/src/settings/reset_settings_nvs.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/storage/flash_map.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
// SETTINGS_PARTITION must match settings_nvs.c
|
||||
#if DT_HAS_CHOSEN(zephyr_settings_partition)
|
||||
#define SETTINGS_PARTITION DT_FIXED_PARTITION_ID(DT_CHOSEN(zephyr_settings_partition))
|
||||
#else
|
||||
#define SETTINGS_PARTITION FIXED_PARTITION_ID(storage_partition)
|
||||
#endif
|
||||
|
||||
int zmk_settings_erase(void) {
|
||||
LOG_INF("Erasing settings flash partition");
|
||||
|
||||
const struct flash_area *fa;
|
||||
int rc = flash_area_open(SETTINGS_PARTITION, &fa);
|
||||
if (rc) {
|
||||
LOG_ERR("Failed to open settings flash: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = flash_area_erase(fa, 0, fa->fa_size);
|
||||
if (rc) {
|
||||
LOG_ERR("Failed to erase settings flash: %d", rc);
|
||||
}
|
||||
|
||||
flash_area_close(fa);
|
||||
|
||||
return rc;
|
||||
}
|
13
app/src/settings/reset_settings_on_start.c
Normal file
13
app/src/settings/reset_settings_on_start.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/init.h>
|
||||
|
||||
#include <zmk/settings.h>
|
||||
|
||||
// Reset after the kernel is initialized but before any application code to
|
||||
// ensure settings are cleared before anything tries to use them.
|
||||
SYS_INIT(zmk_settings_erase, POST_KERNEL, CONFIG_ZMK_SETTINGS_RESET_ON_START_INIT_PRIORITY);
|
|
@ -8,7 +8,7 @@
|
|||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
flavor = "balanced";
|
||||
tapping_term_ms = <300>;
|
||||
tapping-term-ms = <300>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
retro-tap;
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
flavor = "hold-preferred";
|
||||
tapping_term_ms = <300>;
|
||||
tapping-term-ms = <300>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
retro-tap;
|
||||
};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
|
@ -0,0 +1,6 @@
|
|||
pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
|
@ -0,0 +1,36 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
|
||||
/* this test ensures that timing out while the other key is being held results in correct behavior */
|
||||
|
||||
&sk {
|
||||
lazy;
|
||||
release-after-ms = <50>;
|
||||
};
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&sk LEFT_CONTROL &kp A
|
||||
&sk LEFT_SHIFT &sk LEFT_ALT>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
/* tap sk LEFT_CONTROL */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
/* tap A */
|
||||
ZMK_MOCK_PRESS(0,1,60)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
/* tap A */
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
>;
|
||||
};
|
1
app/tests/sticky-keys/11-lazy-timeout/events.patterns
Normal file
1
app/tests/sticky-keys/11-lazy-timeout/events.patterns
Normal file
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
|
@ -0,0 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
42
app/tests/sticky-keys/11-lazy-timeout/native_posix_64.keymap
Normal file
42
app/tests/sticky-keys/11-lazy-timeout/native_posix_64.keymap
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
|
||||
/* this test ensures that lazy sticky keys don't emit anything on timeout */
|
||||
|
||||
&sk {
|
||||
lazy;
|
||||
release-after-ms = <50>;
|
||||
};
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&sk LEFT_CONTROL &kp A
|
||||
&sk LEFT_SHIFT &sk LEFT_ALT>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
/* tap sk LEFT_CONTROL */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
/* tap sk LEFT_SHIFT */
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,60)
|
||||
/* tap sk LEFT_ALT */
|
||||
ZMK_MOCK_PRESS(1,1,10)
|
||||
ZMK_MOCK_RELEASE(1,1,60)
|
||||
/* tap A */
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
/* tap A */
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
>;
|
||||
};
|
1
app/tests/sticky-keys/11-lazy/events.patterns
Normal file
1
app/tests/sticky-keys/11-lazy/events.patterns
Normal file
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
16
app/tests/sticky-keys/11-lazy/keycode_events.snapshot
Normal file
16
app/tests/sticky-keys/11-lazy/keycode_events.snapshot
Normal file
|
@ -0,0 +1,16 @@
|
|||
pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1D implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1D implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
66
app/tests/sticky-keys/11-lazy/native_posix_64.keymap
Normal file
66
app/tests/sticky-keys/11-lazy/native_posix_64.keymap
Normal file
|
@ -0,0 +1,66 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
|
||||
/* this test verifies that lazy sticky keys work similarly to regular sticky keys, and includes cases from 10-callum-mods and 8-lsk-osk.
|
||||
the only difference is that the lazy key does not exit the sticky layer */
|
||||
|
||||
&sk {
|
||||
lazy;
|
||||
};
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&sk LEFT_CONTROL &sl 1
|
||||
&kp A &mo 1>;
|
||||
};
|
||||
|
||||
lower_layer {
|
||||
bindings = <
|
||||
&sk LEFT_CONTROL &kp X
|
||||
&sk LEFT_SHIFT &kp Z>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
/* tap LEFT_CONTROL on base layer */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
/* tap A */
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
|
||||
/* tap sl lower_layer */
|
||||
ZMK_MOCK_PRESS(0,1,10)
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
/* tap sk LEFT_CONTROL */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
/* tap Z */
|
||||
ZMK_MOCK_PRESS(1,1,10)
|
||||
ZMK_MOCK_RELEASE(1,1,10)
|
||||
|
||||
/* press mo lower_layer */
|
||||
ZMK_MOCK_PRESS(1,1,10)
|
||||
/* tap sk LEFT_CONTROL */
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
/* tap sk LEFT_SHIFT */
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
/* release mo lower_layer */
|
||||
ZMK_MOCK_RELEASE(1,1,10)
|
||||
/* tap A (with left control and left shift enabled) */
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
/* tap A (no sticky keys anymore) */
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
>;
|
||||
};
|
|
@ -1,8 +1,8 @@
|
|||
pressed: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pressed: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
pressed: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
compatible = "zmk,behavior-hold-tap";
|
||||
#binding-cells = <2>;
|
||||
tapping-term-ms = <200>;
|
||||
quick_tap_ms = <0>;
|
||||
quick-tap-ms = <0>;
|
||||
flavor = "tap-preferred";
|
||||
bindings = <&kp>, <&kp>;
|
||||
};
|
||||
|
|
|
@ -87,7 +87,7 @@ For example, if you press `&mt LEFT_SHIFT A` and then release it without pressin
|
|||
|
||||
If enabled, the hold behavior will immediately be held on hold-tap press, and will release before the behavior is sent in the event the hold-tap resolves into a tap. With most modifiers this will not affect typing, and is useful for using modifiers with the mouse.
|
||||
|
||||
:::note Alt/Win/Cmd behavior
|
||||
:::info[Alt/Win/Cmd behavior]
|
||||
In some applications/desktop environments, pressing Alt keycodes by itself will have its own behavior like activate a menu and Gui keycodes will bring up the start menu or an application launcher.
|
||||
:::
|
||||
|
||||
|
|
83
docs/docs/behaviors/index.mdx
Normal file
83
docs/docs/behaviors/index.mdx
Normal file
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
title: Behavior Overview
|
||||
sidebar_label: Overview
|
||||
---
|
||||
|
||||
# Behaviors Overview
|
||||
|
||||
"Behaviors" are bindings that are assigned to and triggered by key positions on keymap layers, sensors (like an encoder) or combos. They describe what happens e.g. when a certain key position is pressed or released, or an encoder triggers a rotation event. They can also be recursively invoked by other behaviors, such as macros.
|
||||
|
||||
Below is a summary of pre-defined behavior bindings and user-definable behaviors available in ZMK, with references to documentation pages describing them.
|
||||
|
||||
## Key press behaviors
|
||||
|
||||
| Binding | Behavior | Description |
|
||||
| ------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `&kp` | [Key Press](key-press.md) | Send keycodes to the connected host when a key is pressed |
|
||||
| `&mt` | [Mod Tap](mod-tap.md) | Sends a different key press depending on whether a key is held or tapped |
|
||||
| `&kt` | [Key Toggle](key-toggle.md) | Toggles the press of a key. If the key is not currently pressed, key toggle will press it, holding it until the key toggle is pressed again or the key is released in some other way. If the key is currently pressed, key toggle will release it |
|
||||
| `&sk` | [Sticky Key](sticky-key.md) | Stays pressed until another key is pressed, then is released. It is often used for modifier keys like shift, which allows typing capital letters without holding it down |
|
||||
| `&gresc` | [Grave Escape](mod-morph.md#behavior-binding) | Sends Grave Accent `` ` `` keycode if shift or GUI is held, sends Escape keycode otherwise |
|
||||
| `&caps_word` | [Caps Word](caps-word.md) | Behaves similar to caps lock, but automatically deactivates when any key not in a continue list is pressed, or if the caps word key is pressed again |
|
||||
| `&key_repeat` | [Key Repeat](key-repeat.md) | Sends again whatever keycode was last sent |
|
||||
|
||||
## Miscellaneous behaviors
|
||||
|
||||
| Binding | Behavior | Description |
|
||||
| -------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `&trans` | [Transparent](misc.md#transparent) | Passes the key press down to the next active layer in the stack for processing |
|
||||
| `&none` | [None](misc.md#none) | Swallows and stops the key press, no keycode will be sent nor will the key press be passed down to the next active layer in the stack |
|
||||
|
||||
## Layer navigation behaviors
|
||||
|
||||
| Binding | Behavior | Description |
|
||||
| ------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| `&mo` | [Momentary Layer](layers.md#momentary-layer) | Enables a layer while a key is pressed |
|
||||
| `<` | [Layer-tap](layers.md#layer-tap) | Enables a layer when a key is held, and outputs a key press when the key is only tapped for a short time |
|
||||
| `&to` | [To Layer](layers.md#to-layer) | Enables a layer and disables all other layers except the default layer |
|
||||
| `&tog` | [Toggle Layer](layers.md#toggle-layer) | Enables a layer until the layer is manually disabled |
|
||||
| `&sl` | [Sticky Layer](sticky-layer.md) | Activates a layer until another key is pressed, then deactivates it |
|
||||
|
||||
## Mouse emulation behaviors
|
||||
|
||||
| Binding | Behavior | Description |
|
||||
| ------- | ----------------------------------------------------------- | ------------------------------- |
|
||||
| `&mkp` | [Mouse Button Press](mouse-emulation.md#mouse-button-press) | Emulates pressing mouse buttons |
|
||||
|
||||
## Reset behaviors
|
||||
|
||||
| Binding | Behavior | Description |
|
||||
| ------------- | --------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| `&sys_reset` | [Reset](reset.md#reset) | Resets the keyboard and re-runs the firmware flashed to the device |
|
||||
| `&bootloader` | [Bootloader](reset.md#bootloader) | Resets the keyboard and puts it into bootloader mode, allowing you to flash new firmware |
|
||||
|
||||
## Output selection behaviors
|
||||
|
||||
| Binding | Behavior | Description |
|
||||
| ------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| `&bt` | [Bluetooth](bluetooth.md#bluetooth-behavior) | Completes a bluetooth action given on press, for example switching between devices |
|
||||
| `&out` | [Output Selection](outputs.md#output-selection-behavior) | Allows selecting whether output is sent to the USB or bluetooth connection when both are connected |
|
||||
|
||||
## Lighting behaviors
|
||||
|
||||
| Binding | Behavior | Description |
|
||||
| --------- | ---------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||
| `&rgb_ug` | [RGB Underglow](underglow.md#behavior-binding) | Controls the RGB underglow, usually placed underneath the keyboard |
|
||||
| `&bl` | [Backlight](backlight.md#behavior-binding) | Controls the keyboard backlighting, usually placed through or under switches |
|
||||
|
||||
## Power management behaviors
|
||||
|
||||
| Binding | Behavior | Description |
|
||||
| ------------ | --------------------------------------------- | --------------------------------------------------------------- |
|
||||
| `&ext_power` | [Power management](power.md#behavior-binding) | Allows enabling or disabling the VCC power output to save power |
|
||||
| `&soft_off` | [Soft off](soft-off.md#behavior-binding) | Turns the keyboard off. |
|
||||
|
||||
## User-defined behaviors
|
||||
|
||||
| Behavior | Description |
|
||||
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [Macros](macros.md) | Allows configuring a list of other behaviors to invoke when the key is pressed and/or released |
|
||||
| [Hold-Tap](hold-tap.mdx) | Invokes different behaviors depending on key press duration or interrupting keys. This is the basis for [layer-tap](layers.md#layer-tap) and [mod-tap](mod-tap.md) |
|
||||
| [Tap Dance](tap-dance.mdx) | Invokes different behaviors corresponding to how many times a key is pressed |
|
||||
| [Mod-Morph](mod-morph.md) | Invokes different behaviors depending on whether a specified modifier is held during a key press |
|
||||
| [Sensor Rotation](sensor-rotate.md) | Invokes different behaviors depending on whether a sensor is rotated clockwise or counter-clockwise |
|
|
@ -8,7 +8,12 @@ sidebar_label: Mouse Emulation
|
|||
Mouse emulation behaviors send mouse events. Currently, only mouse button presses are supported, but movement
|
||||
and scroll action support is planned for the future.
|
||||
|
||||
Whenever the Mouse Emulation feature is turned on or off, the HID protocol used to communicate events to hosts changes. Unfortunately, those changes are not always detected automatically, and might require re-pairing your keyboard to your devices to work over bluetooth. If mouse behaviors are still not recognized by your device after doing that, you can try [these troubleshooting steps](../features/bluetooth.md#windows-connected-but-not-working).
|
||||
:::warning[Refreshing the HID descriptor]
|
||||
|
||||
Enabling or disabling the mouse emulation feature modifies the HID report descriptor and requires it to be [refreshed](../features/bluetooth.md#refreshing-the-hid-descriptor).
|
||||
The mouse functionality will not work over BLE until that is done.
|
||||
|
||||
:::
|
||||
|
||||
## Configuration Option
|
||||
|
||||
|
|
40
docs/docs/behaviors/soft-off.md
Normal file
40
docs/docs/behaviors/soft-off.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
title: Soft Off Behavior
|
||||
sidebar_label: Soft Off
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The soft off behavior is used to force the keyboard into an off state. Depending on the specific keyboard hardware, the keyboard can be turned back on again either with a dedicated on/off button that is available, or using the reset button found on the device.
|
||||
|
||||
Refer to the [soft off config](../config/power.md#soft-off) for details on enabling soft off in order to use this behavior.
|
||||
|
||||
For more information, see the [Soft Off Feature](../features/soft-off.md) page.
|
||||
|
||||
### Behavior Binding
|
||||
|
||||
- Reference: `&soft_off`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
&soft_off
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
#### Hold Time
|
||||
|
||||
By default, the keyboard will be turned off as soon as the key bound to the behavior is released, even if the key is only tapped briefly. If you would prefer that the key need be held a certain amount of time before releasing, you can set the `hold-time-ms` to a non-zero value in your keymap:
|
||||
|
||||
```
|
||||
&soft_off {
|
||||
hold-time-ms = <5000>; // Only turn off it the key is held for 5 seconds or longer.
|
||||
};
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
...
|
||||
};
|
||||
};
|
||||
```
|
|
@ -34,6 +34,12 @@ By default, sticky keys stay pressed for a second if you don't press any other k
|
|||
|
||||
Some typists may find that using a sticky shift key interspersed with rapid typing results in two or more capitalized letters instead of one. This happens as the sticky key is active until the next key is released, under which other keys may be pressed and will receive the modifier. You can enable the `quick-release` setting to instead deactivate the sticky key on the next key being pressed, as opposed to released.
|
||||
|
||||
#### `lazy`
|
||||
|
||||
By default, sticky keys are activated on press until another key is pressed. You can enable the `lazy` setting to instead activate the sticky key right _before_ the other key is pressed. This is useful for mouse interaction or situations where you don't want the host to see anything during a sticky-key timeout, for example `&sk LGUI`, which can trigger a menu if pressed alone.
|
||||
|
||||
Note that tapping a lazy sticky key will not trigger other behaviors such as the release of other sticky keys or layers. If you want to use a lazy sticky key to activate the release of a sticky layer, potential solutions include wrappping the sticky key in a simple macro which presses the sticky behavior around the sticky key press, doing the same with `&mo LAYER`, or triggering a tap of some key like `K_CANCEL` on sticky key press.
|
||||
|
||||
#### `ignore-modifiers`
|
||||
|
||||
This setting is enabled by default. It ensures that if a sticky key modifier is pressed before a previously pressed sticky key is released, the modifiers will get combined so you can add more sticky keys or press a regular key to apply the modifiers. This is to accommodate _callum-style mods_ where you are prone to rolling sticky keys. If you want sticky key modifiers to only chain after release, you can disable this setting. Please note that activating multiple modifiers via [modifier functions](https://zmk.dev/docs/codes/modifiers#modifier-functions) such as `&sk LS(LALT)`, require `ignore-modifiers` enabled in order to function properly.
|
||||
|
|
|
@ -230,6 +230,7 @@ Applies to: `compatible = "zmk,behavior-sticky-key"`
|
|||
| `bindings` | phandle array | A behavior (without parameters) to trigger | |
|
||||
| `release-after-ms` | int | Releases the key after this many milliseconds if no other key is pressed | 1000 |
|
||||
| `quick-release` | bool | Release the sticky key on the next key press instead of release | false |
|
||||
| `lazy` | bool | Wait until the next key press to activate the sticky key behavior | false |
|
||||
| `ignore-modifiers` | bool | If enabled, pressing a modifier key does not cancel the sticky key | true |
|
||||
|
||||
This behavior forwards the one parameter it receives to the parameter of the behavior specified in `bindings`.
|
||||
|
|
|
@ -73,12 +73,13 @@ Definition file: [zmk/app/module/dts/bindings/kscan/zmk,kscan-gpio-direct.yaml](
|
|||
|
||||
| Property | Type | Description | Default |
|
||||
| ------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `input-gpios` | GPIO array | Input GPIOs (one per key) | |
|
||||
| `input-gpios` | GPIO array | Input GPIOs (one per key). Can be either direct GPIO pin or `gpio-key` references. | |
|
||||
| `debounce-press-ms` | int | Debounce time for key press in milliseconds. Use 0 for eager debouncing. | 5 |
|
||||
| `debounce-release-ms` | int | Debounce time for key release in milliseconds. | 5 |
|
||||
| `debounce-scan-period-ms` | int | Time between reads in milliseconds when any key is pressed. | 1 |
|
||||
| `poll-period-ms` | int | Time between reads in milliseconds when no key is pressed and `CONFIG_ZMK_KSCAN_DIRECT_POLLING` is enabled. | 10 |
|
||||
| `toggle-mode` | bool | Use toggle switch mode. | n |
|
||||
| `wakeup-source` | bool | Mark this kscan instance as able to wake the keyboard from deep sleep | n |
|
||||
|
||||
By default, a switch will drain current through the internal pull up/down resistor whenever it is pressed. This is not ideal for a toggle switch, where the switch may be left in the "pressed" state for a long time. Enabling `toggle-mode` will make the driver flip between pull up and down as the switch is toggled to optimize for power.
|
||||
|
||||
|
@ -89,6 +90,7 @@ Assuming the switches connect each GPIO pin to the ground, the [GPIO flags](http
|
|||
```dts
|
||||
kscan0: kscan {
|
||||
compatible = "zmk,kscan-gpio-direct";
|
||||
wakeup-source;
|
||||
input-gpios
|
||||
= <&pro_micro 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&pro_micro 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
|
@ -123,6 +125,7 @@ Definition file: [zmk/app/module/dts/bindings/kscan/zmk,kscan-gpio-matrix.yaml](
|
|||
| `debounce-scan-period-ms` | int | Time between reads in milliseconds when any key is pressed. | 1 |
|
||||
| `diode-direction` | string | The direction of the matrix diodes | `"row2col"` |
|
||||
| `poll-period-ms` | int | Time between reads in milliseconds when no key is pressed and `CONFIG_ZMK_KSCAN_MATRIX_POLLING` is enabled. | 10 |
|
||||
| `wakeup-source` | bool | Mark this kscan instance as able to wake the keyboard from deep sleep | n |
|
||||
|
||||
The `diode-direction` property must be one of:
|
||||
|
||||
|
@ -137,6 +140,7 @@ The output pins (e.g. columns for `col2row`) should have the flag `GPIO_ACTIVE_H
|
|||
```dts
|
||||
kscan0: kscan {
|
||||
compatible = "zmk,kscan-gpio-matrix";
|
||||
wakeup-source;
|
||||
diode-direction = "col2row";
|
||||
col-gpios
|
||||
= <&pro_micro 4 GPIO_ACTIVE_HIGH>
|
||||
|
@ -177,6 +181,7 @@ Definition file: [zmk/app/module/dts/bindings/kscan/zmk,kscan-gpio-charlieplex.y
|
|||
| `debounce-release-ms` | int | Debounce time for key release in milliseconds. | 5 |
|
||||
| `debounce-scan-period-ms` | int | Time between reads in milliseconds when any key is pressed. | 1 |
|
||||
| `poll-period-ms` | int | Time between reads in milliseconds when no key is pressed and `interrupt-gpois` is not set. | 10 |
|
||||
| `wakeup-source` | bool | Mark this kscan instance as able to wake the keyboard from deep sleep | n |
|
||||
|
||||
Define the transform with a [matrix transform](#matrix-transform). The row is always the driven pin, and the column always the receiving pin (input to the controller).
|
||||
For example, in `RC(5,0)` power flows from the 6th pin in `gpios` to the 1st pin in `gpios`.
|
||||
|
@ -451,6 +456,7 @@ Note that the entire addressable space does not need to be mapped.
|
|||
|
||||
kscan0: kscan {
|
||||
compatible = "zmk,kscan-gpio-charlieplex";
|
||||
wakeup-source;
|
||||
|
||||
interrupt-gpios = <&pro_micro 21 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN) >;
|
||||
gpios
|
||||
|
|
|
@ -25,6 +25,18 @@ Definition file: [zmk/app/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/
|
|||
| `CONFIG_ZMK_IDLE_SLEEP_TIMEOUT` | int | Milliseconds of inactivity before entering deep sleep | 900000 |
|
||||
| `CONFIG_ZMK_NO_SLEEP_WHILE_BLE_CONNECTED` | bool | Prevent deep sleep while BLE is connected (active profile on central, split comms on peripheral) | n |
|
||||
|
||||
## Soft Off
|
||||
|
||||
The [soft off feature](../features/soft-off.md) allows turning the keyboard on/off from either dedicated hardware, or using the [`&soft_off` behavior](../behaviors/soft-off.md) to turn off and a reset button to turn back on again.
|
||||
|
||||
### Kconfig
|
||||
|
||||
Definition file: [zmk/app/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/Kconfig)
|
||||
|
||||
| Config | Type | Description | Default |
|
||||
| ------------------------ | ---- | ------------------------------------------------------------------- | ------- |
|
||||
| `CONFIG_ZMK_PM_SOFT_OFF` | bool | Enable soft off functionality from the keymap or dedicated hardware | n |
|
||||
|
||||
## External Power Control
|
||||
|
||||
Driver for enabling or disabling power to peripherals such as displays and lighting. This driver must be configured to use [power management behaviors](../behaviors/power.md).
|
||||
|
|
|
@ -14,14 +14,21 @@ Definition file: [zmk/app/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/
|
|||
### General
|
||||
|
||||
| Config | Type | Description | Default |
|
||||
| ----------------------------------- | ------ | ----------------------------------------------------------------------------- | ------- |
|
||||
| ------------------------------------ | ------ | ----------------------------------------------------------------------------- | ------- |
|
||||
| `CONFIG_ZMK_KEYBOARD_NAME` | string | The name of the keyboard (max 16 characters) | |
|
||||
| `CONFIG_ZMK_SETTINGS_RESET_ON_START` | bool | Clears all persistent settings from the keyboard at startup | n |
|
||||
| `CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE` | int | Milliseconds to wait after a setting change before writing it to flash memory | 60000 |
|
||||
| `CONFIG_ZMK_WPM` | bool | Enable calculating words per minute | n |
|
||||
| `CONFIG_HEAP_MEM_POOL_SIZE` | int | Size of the heap memory pool | 8192 |
|
||||
|
||||
### HID
|
||||
|
||||
:::warning[Refreshing the HID descriptor]
|
||||
|
||||
Making changes to any of the settings in this section modifies the HID report descriptor and requires it to be [refreshed](../features/bluetooth.md#refreshing-the-hid-descriptor).
|
||||
|
||||
:::
|
||||
|
||||
| Config | Type | Description | Default |
|
||||
| ------------------------------------- | ---- | -------------------------------------------------------------- | ------- |
|
||||
| `CONFIG_ZMK_HID_INDICATORS` | bool | Enable reciept of HID/LED indicator state from connected hosts | n |
|
||||
|
|
|
@ -8,7 +8,7 @@ import TabItem from "@theme/TabItem";
|
|||
|
||||
## Overview
|
||||
|
||||
This document outlines how to develop a behavior for ZMK and prepare the changes for a pull request.
|
||||
This document outlines how to develop a [behavior](../behaviors/index.mdx) for ZMK and prepare the changes for a pull request.
|
||||
|
||||
Behaviors are assigned to key positions and determine what happens when they are pressed and released. They are implemented in Zephyr as "devices": they consist of a devicetree binding file, which specifies the properties of the behavior, and a driver written in C code. This allows for the ability to create unique instances of these behaviors in [keymaps](../features/keymaps.mdx) or devicetree-source-include files (`.dtsi`). While instances of behaviors stored in keymaps are created by end-users for their personal needs, the instances that live in the .dtsi files are stored and documented in ZMK directly, which removes the need for end-users to set up common use-cases of these behaviors in their personal keymaps.
|
||||
|
||||
|
|
|
@ -171,6 +171,7 @@ this might look something like:
|
|||
kscan0: kscan_0 {
|
||||
compatible = "zmk,kscan-gpio-matrix";
|
||||
diode-direction = "col2row";
|
||||
wakeup-source;
|
||||
|
||||
col-gpios
|
||||
= <&pro_micro 15 GPIO_ACTIVE_HIGH>
|
||||
|
@ -386,7 +387,7 @@ The two `#include` lines at the top of the keymap are required in order to bring
|
|||
|
||||
### Keymap Behaviors
|
||||
|
||||
For the full documentation on the available behaviors for use in keymaps, start with reviewing [`kp`](../behaviors/key-press.md) and then use the sidebar to review the others available within ZMK.
|
||||
For documentation on the available behaviors for use in keymaps, see the [overview page for behaviors](../behaviors/index.mdx).
|
||||
|
||||
## Metadata
|
||||
|
||||
|
|
|
@ -36,6 +36,19 @@ Failure to manage the profiles can result in unexpected/broken behavior with hos
|
|||
|
||||
Management of the bluetooth in ZMK is accomplished using the [`&bt` behavior](../behaviors/bluetooth.md). Be sure to refer to that documentation to learn how to manage profiles, switch between connected hosts, etc.
|
||||
|
||||
## Refreshing the HID Descriptor
|
||||
|
||||
Enabling certain features or behaviors in ZMK changes the data structure that ZMK sends over USB or BLE to host devices.
|
||||
This in turn requires [HID report descriptors](https://docs.kernel.org/hid/hidintro.html) to be modified for the reports to be parsed correctly.
|
||||
Firmware changes that would modify the descriptor include the following:
|
||||
|
||||
- Changing any of the settings under the [HID category](../config/system.md#hid), including enabling/disabling NKRO or HID indicators
|
||||
- Enabling mouse features, such as adding [mouse keys](../behaviors/mouse-emulation.md) to your keymap
|
||||
|
||||
While the descriptor refresh happens on boot for USB, hosts will frequently cache this descriptor for BLE devices.
|
||||
In order to refresh this cache, you need to remove the keyboard from the host device, clear the profile associated with the host on the keyboard, then pair again.
|
||||
For Windows systems you might need to follow the instructions in [this troubleshooting section](#windows-connected-but-not-working) below.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connectivity Issues
|
||||
|
@ -56,17 +69,10 @@ This setting can also improve the connection strength between the keyboard halve
|
|||
|
||||
If you want to test bluetooth output on your keyboard and are powering it through the USB connection rather than a battery, you will be able to pair with a host device but may not see keystrokes sent. In this case you need to use the [output selection behavior](../behaviors/outputs.md) to prefer sending keystrokes over bluetooth rather than USB. This might be necessary even if you are not powering from a device capable of receiving USB inputs, such as a USB charger.
|
||||
|
||||
## Known Issues
|
||||
### Issues with dual boot setups
|
||||
|
||||
There are a few known issues related to BLE and ZMK:
|
||||
|
||||
### Windows Battery Reporting
|
||||
|
||||
There is a known issue with Windows failing to update the battery information after connecting to a ZMK keyboard. You can work around this Windows bug by overriding a [Bluetooth config variable](../config/bluetooth.md) to force battery notifications even if a host neglects to subscribe to them:
|
||||
|
||||
```ini
|
||||
CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n
|
||||
```
|
||||
Since ZMK associates pairing/bond keys with hardware addresses of hosts, you cannot pair to two different operating systems in a dual boot system at the same time.
|
||||
While you can find [documented workarounds](https://wiki.archlinux.org/title/bluetooth#Dual_boot_pairing) that involve copying pairing keys across operating systems and use both OS with a single profile, they can be fairly involved and should be followed with caution.
|
||||
|
||||
### macOS Connected But Not Working
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ For example, the simplest behavior in ZMK is the "key press" behavior, which res
|
|||
(a certain spot on the keyboard), and when that position is pressed, send a keycode to the host, and
|
||||
when the key position is released, updates the host to notify of the keycode being released.
|
||||
|
||||
For the full set of possible behaviors, start at the [Key Press](../behaviors/key-press.md) behavior.
|
||||
For the full set of possible behaviors, see the [overview page for behaviors](../behaviors/index.mdx).
|
||||
|
||||
## Layers
|
||||
|
||||
|
@ -128,11 +128,9 @@ that defines just one layer for this keymap:
|
|||
|
||||
Each layer should have:
|
||||
|
||||
1. A `bindings` property this will be a list of behavior bindings, one for each key position for the keyboard.
|
||||
1. A `bindings` property this will be a list of [behavior bindings](../behaviors/index.mdx), one for each key position for the keyboard.
|
||||
1. (Optional) A `sensor-bindings` property that will be a list of behavior bindings for each sensor on the keyboard. (Currently, only encoders are supported as sensor hardware, but in the future devices like trackpoints would be supported the same way)
|
||||
|
||||
For the full set of possible behaviors, start at the [Key Press](../behaviors/key-press.md) behavior.
|
||||
|
||||
### Complete Example
|
||||
|
||||
Putting this all together, a complete [`kyria.keymap`](https://github.com/zmkfirmware/zmk/blob/main/app/boards/shields/kyria/kyria.keymap) looks like:
|
||||
|
|
274
docs/docs/features/soft-off.md
Normal file
274
docs/docs/features/soft-off.md
Normal file
|
@ -0,0 +1,274 @@
|
|||
---
|
||||
title: Soft Off Feature
|
||||
sidebar_label: Soft Off
|
||||
---
|
||||
|
||||
Similar to the deep sleep feature that sends the keyboard into a low power state after a certain period of inactivity, the soft off feature is used to turn the keyboard on and off explicitly. Depending on the keyboard, this may be through a dedicated on/off push button, or merely through an additional binding in the keymap to turn the device off and the existing reset button to turn the device back on.
|
||||
|
||||
The feature is intended as an alternative to using a hardware switch to physically cut power from the battery to the keyboard. This can be useful for existing PCBs not designed for wireless that don't have a power switch, or for new designs that favor a push button on/off like found on other devices.
|
||||
|
||||
:::note
|
||||
|
||||
The power off is accomplished by putting the MCU into a "soft off" state. Power is _not_ technically removed from the entire system, but the device will only be woken from the state by a few possible events.
|
||||
|
||||
:::
|
||||
|
||||
Once powered off, the keyboard will only wake up when:
|
||||
|
||||
- You press the same button/sequence that you pressed to power off the keyboard, or
|
||||
- You press a reset button found on the keyboard.
|
||||
|
||||
## Config
|
||||
|
||||
Refer to the [soft off config](../config/power.md#soft-off) for details on enabling soft off.
|
||||
|
||||
## Soft Off With Existing Designs
|
||||
|
||||
For existing designs, using soft off is as simple as placing the [Soft Off Behavior](../behaviors/soft-off.md) in your keymap and then invoking it.
|
||||
|
||||
You can then wake up the keyboard by pressing the reset button once, and repeating this for each side for split keyboards.
|
||||
|
||||
## Hardware Changes For New Designs
|
||||
|
||||
ZMK's dedicated soft on/off pin feature requires a dedicated GPIO pin to be used to trigger powering off, and to wake the core from the
|
||||
soft off state when it goes active again later.
|
||||
|
||||
### Simple Direct Pin
|
||||
|
||||
The simplest way to achieve this is with a push button between a GPIO pin and ground.
|
||||
|
||||
### Matrix-Integrated Hardware Combo
|
||||
|
||||
Another, more complicated option is to tie two of the switch outputs in the matrix together through an AND gate and connect that to the dedicated GPIO pin. This way you can use a key combination in your existing keyboard matrix to trigger soft on/off. To make this work best, the two switches used should both be driven by the same matrix input pin so that both will be active simultaneously on the AND gate inputs. The alternative is to connect the switch to two MOSFETs that trigger both the regular matrix connect and the connect to the AND gate to ensure both pins are active/high at the same time even if scanning sets them high at different times.
|
||||
|
||||
## Firmware Changes For New Designs
|
||||
|
||||
Several items work together to make both triggering soft off properly, and setting up the device to _wake_ from soft off work as expected. In addition, some small changes are needed to keep the regular idle deep sleep functionality working.
|
||||
|
||||
### Wakeup Sources
|
||||
|
||||
Zephyr has general support for the concept of a device as a "wakeup source", which ZMK has not previously used. Adding soft off requires properly updating the existing `kscan` devices with the `wakeup-source` property to ensure they will still work to wake the device from regular inactive deep sleep, e.g.:
|
||||
|
||||
```
|
||||
/ {
|
||||
kscan0: kscan_0 {
|
||||
compatible = "zmk,kscan-gpio-matrix";
|
||||
label = "KSCAN";
|
||||
diode-direction = "col2row";
|
||||
wakeup-source;
|
||||
|
||||
...
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### GPIO Key
|
||||
|
||||
Zephyr's basic GPIO Key concept is used to configure the GPIO pin that will be used for both triggering soft off and waking the device later. Here is an example for a keyboard with a dedicated on/off push button that is a direct wire between the GPIO pin and ground:
|
||||
|
||||
```
|
||||
/ {
|
||||
keys {
|
||||
compatible = "gpio-keys";
|
||||
soft_off_key: soft_off_key {
|
||||
gpios = <&gpio0 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
GPIO keys are defined using child nodes under the `gpio-keys` compatible node. Each child needs just one property defined:
|
||||
|
||||
- The `gpios` property should be a phandle-array with a fully defined GPIO pin and with the correct pull up/down and active high/low flags set. In the above example the soft on/off would be triggered by pulling the specified pin low, typically by pressing a switch that has the other leg connected to ground.
|
||||
|
||||
### Soft Off Behavior Instance
|
||||
|
||||
To use the [soft off behavior](../behaviors/soft-off.md) outside of a keymap, add an instance of the behavior to your `.overlay`/`.dts` file:
|
||||
|
||||
```
|
||||
/ {
|
||||
behaviors {
|
||||
hw_soft_off: hw_soft_off {
|
||||
compatible = "zmk,behavior-soft-off";
|
||||
#binding-cells = <0>;
|
||||
hold-time-ms = <5000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### KScan Sideband Behavior
|
||||
|
||||
The kscan sideband behavior driver will be used to trigger the [soft off behavior](../behaviors/soft-off.md) "out of band" from the normal keymap processing. To do so, it will decorate/wrap an underlying kscan driver. What kscan driver will vary for simple direct pin vs. matrix-integrated hardware combo.
|
||||
|
||||
#### Simple Direct Pin
|
||||
|
||||
With a simple direct pin setup, the The [direct kscan](../config/kscan.md) driver can be used with a GPIO key, to make a small "side matrix":
|
||||
|
||||
```
|
||||
soft_off_direct_scan: soft_off_direct_scan {
|
||||
compatible = "zmk,kscan-gpio-direct";
|
||||
input-keys = <&on_off_key>;
|
||||
wakeup-source;
|
||||
};
|
||||
```
|
||||
|
||||
With that in place, the kscan sideband behavior will wrap the new driver:
|
||||
|
||||
```
|
||||
/ {
|
||||
side_band_behavior_triggers: side_band_behavior_triggers {
|
||||
compatible = "zmk,kscan-sideband-behaviors";
|
||||
|
||||
kscan = <&soft_off_direct_scan>;
|
||||
wakeup-source;
|
||||
|
||||
soft_off {
|
||||
column = <0>;
|
||||
row = <0>;
|
||||
bindings = <&hw_soft_off>;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Finally, we will list the kscan instance in an additional configuration section so that the ZMK soft off process knows it needs to enable this device as part of the soft off processing so it can _also_ wake the keyboard from soft off when pressed:
|
||||
|
||||
```
|
||||
/ {
|
||||
soft_off_wakers {
|
||||
compatible = "zmk,soft-off-wakeup-sources";
|
||||
wakeup-sources = <&soft_off_direct_scan>;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Here are the properties for the node:
|
||||
|
||||
- The `compatible` property for the node must be `zmk,soft-off-wakeup-sources`.
|
||||
- The `wakeup-sources` property is a [phandle array](../config/index.md#devicetree-property-types) pointing to all the devices that should be enabled during the shutdown process to be sure they can later wake the keyboard.
|
||||
|
||||
#### Matrix-Integrated Hardware Combo
|
||||
|
||||
For this case, you will supplement the existing kscan matrix, by adding the additional pin as another entry in
|
||||
the `row-gpios`/`col-gpios` for whichever pins are used to read the matrix state. For example, for an existing matrix like:
|
||||
|
||||
```
|
||||
kscan: kscan {
|
||||
compatible = "zmk,kscan-gpio-matrix";
|
||||
wakeup-source;
|
||||
label = "KSCAN";
|
||||
debounce-press-ms = <1>;
|
||||
debounce-release-ms = <5>;
|
||||
|
||||
diode-direction = "col2row";
|
||||
|
||||
col-gpios
|
||||
= <&gpio0 12 (GPIO_ACTIVE_HIGH)>
|
||||
, <&gpio1 9 (GPIO_ACTIVE_HIGH)>
|
||||
;
|
||||
row-gpios
|
||||
= <&gpio0 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpio0 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
;
|
||||
};
|
||||
```
|
||||
|
||||
you would add another row value:
|
||||
|
||||
```
|
||||
kscan: kscan {
|
||||
compatible = "zmk,kscan-gpio-matrix";
|
||||
wakeup-source;
|
||||
label = "KSCAN";
|
||||
debounce-press-ms = <1>;
|
||||
debounce-release-ms = <5>;
|
||||
|
||||
diode-direction = "col2row";
|
||||
|
||||
col-gpios
|
||||
= <&gpio0 12 (GPIO_ACTIVE_HIGH)>
|
||||
, <&gpio1 9 (GPIO_ACTIVE_HIGH)>
|
||||
;
|
||||
row-gpios
|
||||
= <&gpio0 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpio0 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpio0 2 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
;
|
||||
};
|
||||
```
|
||||
|
||||
With that in place, you would decorate the kscan driver:
|
||||
|
||||
```
|
||||
side_band_behavior_triggers: side_band_behavior_triggers {
|
||||
compatible = "zmk,kscan-sideband-behaviors";
|
||||
wakeup-source;
|
||||
kscan = <&kscan>;
|
||||
soft_off {
|
||||
column = <0>;
|
||||
row = <3>;
|
||||
bindings = <&hw_soft_off>;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Critically, the `column` and `row` values would correspond to the location of the added entry.
|
||||
|
||||
Lastly, which is critical, you would update the `zmk,kscan` chosen value to point to the new kscan instance:
|
||||
|
||||
```
|
||||
chosen {
|
||||
...
|
||||
zmk,kscan = &side_band_behavior_triggers;
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
Here are the properties for the kscan sideband behaviors node:
|
||||
|
||||
- The `compatible` property for the node must be `zmk,kscan-sideband-behaviors`.
|
||||
- The `kscan` property is a phandle to the inner kscan instance that will have press/release events intercepted.
|
||||
|
||||
The child nodes allow setting up the behaviors to invoke directly for a certain row and column:
|
||||
|
||||
- The `row` and `column` properties set the values to intercept and trigger the behavior for.
|
||||
- The `bindings` property references the behavior that should be triggered when the matching row and column event triggers.
|
||||
|
||||
### Soft Off Waker
|
||||
|
||||
Next, we need to add another device which will be enabled only when the keyboard is going into soft off state, and will configure the previously declared GPIO key with the correct interrupt configuration to wake the device from soft off once it is pressed.
|
||||
|
||||
```
|
||||
/ {
|
||||
wakeup_source: wakeup_source {
|
||||
compatible = "zmk,gpio-key-wakeup-trigger";
|
||||
|
||||
trigger = <&on_off_key>;
|
||||
wakeup-source;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Here are the properties for the node:
|
||||
|
||||
- The `compatible` property for the node must be `zmk,gpio-key-wakeup-trigger`.
|
||||
- The `trigger` property is a phandle to the GPIO key defined earlier.
|
||||
- The `wakeup-source` property signals to Zephyr this device should not be suspended during the shutdown procedure.
|
||||
- An optional `extra-gpios` property contains a list of GPIO pins (including the appropriate flags) to set active before going into power off, if needed to ensure the GPIO pin will trigger properly to wake the keyboard. This is only needed for matrix integrated combos. For those keyboards, the list should include the matrix output needs needed so the combo hardware is properly "driven" when the keyboard is off.
|
||||
|
||||
Once that is declared, we will list it in an additional configuration section so that the ZMK soft off process knows it needs to enable this device as part of the soft off processing:
|
||||
|
||||
```
|
||||
/ {
|
||||
soft_off_wakers {
|
||||
compatible = "zmk,soft-off-wakeup-sources";
|
||||
wakeup-sources = <&wakeup_source>;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Here are the properties for the node:
|
||||
|
||||
- The `compatible` property for the node must be `zmk,soft-off-wakeup-sources`.
|
||||
- The `wakeup-sources` property is a [phandle array](../config/index.md#devicetree-property-types) pointing to all the devices that should be enabled during the shutdown process to be sure they can later wake the keyboard.
|
|
@ -27,6 +27,10 @@ Variations of the warnings shown below occur when flashing the `<firmware>.uf2`
|
|||
|
||||
macOS 13.0 (Ventura) Finder may report an error code 100093 when copying `<firmware>.uf2` files into microcontrollers. This bug is limited to the operating system's Finder. You can work around it by copying on Terminal command line or use a third party file manager. Issue is fixed in macOS version 13.1.
|
||||
|
||||
### macOS Sonoma error
|
||||
|
||||
macOS 14.x (Sonoma) Finder may report an "Error code -36" when copying `<firmware>.uf2` files into microcontrollers. A similar "fcopyfile failed: Input/output error" will also be reported when copying is performed using Terminal command line. These errors can be ignored because they are reported when the bootloader disconnects automatically after the uf2 file is copied successfully.
|
||||
|
||||
### CMake Error
|
||||
|
||||
An error along the lines of `CMake Error at (zmk directory)/zephyr/cmake/generic_toolchain.cmake:64 (include): include could not find load file:` during firmware compilation indicates that the Zephyr Environment Variables are not properly defined.
|
||||
|
@ -40,6 +44,10 @@ West build errors usually indicate syntax problems in the `<keyboard>.keymap` fi
|
|||
If you are reviewing these errors in the GitHub Actions tab, they can be found in the `West Build` step of the build process.
|
||||
:::
|
||||
|
||||
#### Keymap error
|
||||
|
||||
If you get an error stating `Keymap node not found, check a keymap is available and is has compatible = "zmk,keymap" set` this is an indication that the build process cannot find the keymap. Double check that the `<keyboard>.keymap` file is present and has been discovered by the build process. This can be checked by looking for a line in the build log stating `-- Using keymap file: /path/to/keymap/file/<keyboard>.keymap`. Inside the keymap file ensure the keymap node has `compatible = zmk,keymap` and it's not misspelled. For more information see the [Keymap](features/keymaps.mdx) and [Config](config/index.md) documentation.
|
||||
|
||||
#### devicetree error
|
||||
|
||||
A `devicetree error` followed by a reference to the line number on `<keyboard>.keymap` refers to an issue at the exact line position in that file. For example, below error message indicates a missing `;` at line 109 of the `cradio.keymap` file:
|
||||
|
@ -48,6 +56,14 @@ A `devicetree error` followed by a reference to the line number on `<keyboard>.k
|
|||
devicetree error: /__w/zmk-config/zmk-config/config/cradio.keymap:109 (column 4): parse error: expected ';' or ','
|
||||
```
|
||||
|
||||
A `devicetree error` followed by an `empty_file.c` reference with `lacks #binding-cells` string indicates possible problems with improper parameters for specific bindings:
|
||||
|
||||
```
|
||||
devicetree error: <Node /soc/gpio@50000300 in '/tmp/tmp.vJq9sMwkcY/zephyr/misc/empty_file.c'> lacks #binding-cells
|
||||
```
|
||||
|
||||
This error can be triggered by incorrect binding syntax such as `&kp BT_SEL 0` instead of `&bt BT_SEL 0`.
|
||||
|
||||
#### devicetree_unfixed.h error
|
||||
|
||||
A `devicetree_unfixed.h` error that follows with an "undeclared here" string indicates a problem with key bindings, like behavior nodes (e.g. `&kp` or `&mt`) with incorrect number of parameters:
|
||||
|
@ -68,7 +84,20 @@ A common mistake that leads to this error is to use [key press keycodes](behavio
|
|||
|
||||
### Split Keyboard Halves Unable to Pair
|
||||
|
||||
Split keyboard halves pairing issue can be resolved by flashing a settings reset firmware to both controllers. You will first need to acquire the reset UF2 image file with one of the following options:
|
||||
Split keyboard halves will automatically pair with one another, but there are some cases where this breaks, and the pairing needs to be reset, for example:
|
||||
|
||||
- Switching which halves are the central/peripheral.
|
||||
- Replacing the controller for one of the halves.
|
||||
|
||||
These issues can be resolved by flashing a settings reset firmware to both controllers.
|
||||
|
||||
:::warning
|
||||
|
||||
This procedure will erase all settings, such as Bluetooth profiles, output selection, RGB underglow color, etc.
|
||||
|
||||
:::
|
||||
|
||||
First, acquire the reset UF2 image file with one of the following options:
|
||||
|
||||
#### Option 1: Build Reset UF2 in 'zmk-config'
|
||||
|
||||
|
@ -101,8 +130,16 @@ Save the file, commit the changes and push them to GitHub. Download the new firm
|
|||
Perform the following steps to reset both halves of your split keyboard:
|
||||
|
||||
1. Put each half of the split keyboard into bootloader mode.
|
||||
1. Flash one of the halves of the split with the downloaded settings reset UF2 image. Immediately after flashing the chosen half, put it into bootloader mode to avoid accidental bonding between the halves.
|
||||
1. Flash one of the halves of the split with the downloaded settings reset UF2 image.
|
||||
1. Repeat step 2 with the other half of the split keyboard.
|
||||
1. Flash the actual image for each half of the split keyboard (e.g `my_board_left.uf2` to the left half, `my_board_right.uf2` to the right half).
|
||||
|
||||
After completing these steps, pair the halves of the split keyboard together by resetting them at the same time. Most commonly, this is done by grounding the reset pins for each of your keyboard's microcontrollers or pressing the reset buttons at the same time.
|
||||
|
||||
Once this is done, you can remove/forget the keyboard on each host device and pair it again.
|
||||
|
||||
:::info
|
||||
|
||||
The settings reset firmware has Bluetooth disabled to prevent the two sides from automatically re-pairing until you are done resetting them both. You will not be able to pair your keyboard or see it in any Bluetooth device lists until you have flashed the normal firmware again.
|
||||
|
||||
:::
|
||||
|
|
|
@ -19,9 +19,11 @@ module.exports = {
|
|||
"features/underglow",
|
||||
"features/backlight",
|
||||
"features/battery",
|
||||
"features/soft-off",
|
||||
"features/beta-testing",
|
||||
],
|
||||
Behaviors: [
|
||||
"behaviors/index",
|
||||
"behaviors/key-press",
|
||||
"behaviors/layers",
|
||||
"behaviors/misc",
|
||||
|
@ -43,6 +45,7 @@ module.exports = {
|
|||
"behaviors/underglow",
|
||||
"behaviors/backlight",
|
||||
"behaviors/power",
|
||||
"behaviors/soft-off",
|
||||
],
|
||||
Codes: [
|
||||
"codes/index",
|
||||
|
|
Loading…
Add table
Reference in a new issue