Merge remote-tracking branch 'upstream/main' into feature/animations
This commit is contained in:
commit
5b5afac923
38 changed files with 911 additions and 196 deletions
|
@ -43,7 +43,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
|||
target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_caps_word.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_key_repeat.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_macro.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_MACRO app PRIVATE src/behaviors/behavior_macro.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_mod_morph.c)
|
||||
target_sources(app PRIVATE src/behaviors/behavior_outputs.c)
|
||||
|
@ -77,8 +77,8 @@ endif()
|
|||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/behaviors/behavior_backlight.c)
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/battery.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BATTERY_REPORTING app PRIVATE src/events/battery_state_changed.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BATTERY_REPORTING app PRIVATE src/battery.c)
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_ANIMATION app PRIVATE src/animation/color.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_ANIMATION app PRIVATE src/animation/animation_compose.c)
|
||||
|
|
32
app/Kconfig
32
app/Kconfig
|
@ -114,12 +114,16 @@ menuconfig ZMK_BLE
|
|||
select BT_SMP_APP_PAIRING_ACCEPT
|
||||
select BT_PERIPHERAL
|
||||
select BT_DIS
|
||||
select BT_BAS
|
||||
select BT_SETTINGS
|
||||
select SETTINGS
|
||||
imply ZMK_BATTERY_REPORTING
|
||||
|
||||
if ZMK_BLE
|
||||
|
||||
# BT_TINYCRYPT_ECC is required for BT_SMP_SC_PAIR_ONLY when using HCI
|
||||
config BT_TINYCRYPT_ECC
|
||||
default y if BT_HCI && !BT_CTLR
|
||||
|
||||
choice BT_LL_SW_LLCP_IMPL
|
||||
default BT_LL_SW_LLCP_LEGACY
|
||||
|
||||
|
@ -131,7 +135,7 @@ config SYSTEM_WORKQUEUE_STACK_SIZE
|
|||
|
||||
config ZMK_BLE_THREAD_STACK_SIZE
|
||||
int "BLE notify thread stack size"
|
||||
default 512
|
||||
default 768
|
||||
|
||||
config ZMK_BLE_THREAD_PRIORITY
|
||||
int "BLE notify thread priority"
|
||||
|
@ -324,6 +328,12 @@ endmenu
|
|||
|
||||
menu "Power Management"
|
||||
|
||||
config ZMK_BATTERY_REPORTING
|
||||
bool "Battery level detection/reporting"
|
||||
default n
|
||||
select SENSOR
|
||||
select BT_BAS if ZMK_BLE
|
||||
|
||||
config ZMK_IDLE_TIMEOUT
|
||||
int "Milliseconds of inactivity before entering idle state (OLED shutoff, etc)"
|
||||
default 30000
|
||||
|
@ -534,8 +544,24 @@ config ZMK_WPM
|
|||
bool "Calculate WPM"
|
||||
default n
|
||||
|
||||
config SENSOR
|
||||
config ZMK_KEYMAP_SENSORS
|
||||
bool "Enable Keymap Sensors support"
|
||||
default y
|
||||
depends on DT_HAS_ZMK_KEYMAP_SENSORS_ENABLED
|
||||
select SENSOR
|
||||
|
||||
if ZMK_KEYMAP_SENSORS
|
||||
|
||||
config ZMK_KEYMAP_SENSORS_DEFAULT_TRIGGERS_PER_ROTATION
|
||||
int "Default triggers per rotation"
|
||||
help
|
||||
Unless overridden for a sensor in the board/shield/devicetree, this value
|
||||
determines how many times to trigger the bound behavior per full rotation.
|
||||
For tactile encoders with detents, this usually should match the number of
|
||||
detents per rotation of the encoder.
|
||||
default 20
|
||||
|
||||
endif # ZMK_KEYMAP_SENSORS
|
||||
|
||||
choice CBPRINTF_IMPLEMENTATION
|
||||
default CBPRINTF_NANO
|
||||
|
|
|
@ -21,4 +21,9 @@ config ZMK_BEHAVIOR_SENSOR_ROTATE_VAR
|
|||
bool
|
||||
default y
|
||||
depends on DT_HAS_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR_ENABLED
|
||||
select ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON
|
||||
select ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON
|
||||
|
||||
config ZMK_BEHAVIOR_MACRO
|
||||
bool
|
||||
default y
|
||||
depends on DT_HAS_ZMK_BEHAVIOR_MACRO_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_ONE_PARAM_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_TWO_PARAM_ENABLED
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
&arduino_i2c {
|
||||
// Default buffer size is too small for use with displays.
|
||||
zephyr,concat-buf-size = <512>;
|
||||
zephyr,concat-buf-size = <1024>;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
/ {
|
||||
// First, delete the existing basic GPIO based instance.
|
||||
/delete-node/ encoder;
|
||||
};
|
||||
|
||||
&pinctrl {
|
||||
qdec_default: qdec_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(QDEC_A, 1, 11)>,
|
||||
<NRF_PSEL(QDEC_B, 1, 10)>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// Set up the QDEC hardware based driver and give it the same label as the deleted node.
|
||||
encoder: &qdec0 {
|
||||
status = "okay";
|
||||
led-pre = <0>;
|
||||
steps = <80>;
|
||||
pinctrl-0 = <&qdec_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
|
@ -40,10 +40,10 @@
|
|||
|
||||
// Commented out until we add more powerful power domain support
|
||||
// external_power {
|
||||
// compatible = "zmk,ext-power-generic";
|
||||
// label = "EXT_POWER";
|
||||
// init-delay-ms = <200>;
|
||||
// control-gpios = <&arduino_header 1 GPIO_ACTIVE_LOW>;
|
||||
// compatible = "zmk,ext-power-generic";
|
||||
// label = "EXT_POWER";
|
||||
// init-delay-ms = <200>;
|
||||
// control-gpios = <&arduino_header 1 GPIO_ACTIVE_LOW>;
|
||||
// };
|
||||
|
||||
rgb_power {
|
||||
|
@ -128,14 +128,14 @@
|
|||
diode-direction = "col2row";
|
||||
|
||||
col-gpios
|
||||
= <&arduino_header 10 GPIO_ACTIVE_HIGH>
|
||||
, <&arduino_header 9 GPIO_ACTIVE_HIGH>
|
||||
;
|
||||
= <&arduino_header 10 GPIO_ACTIVE_HIGH>
|
||||
, <&arduino_header 9 GPIO_ACTIVE_HIGH>
|
||||
;
|
||||
|
||||
row-gpios
|
||||
= <&arduino_header 13 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&arduino_header 11 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
;
|
||||
= <&arduino_header 13 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&arduino_header 11 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
;
|
||||
|
||||
};
|
||||
|
||||
|
@ -144,11 +144,11 @@
|
|||
status = "disabled";
|
||||
|
||||
input-gpios
|
||||
= <&arduino_header 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&arduino_header 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&arduino_header 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&arduino_header 11 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
;
|
||||
= <&arduino_header 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&arduino_header 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&arduino_header 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&arduino_header 11 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
;
|
||||
|
||||
};
|
||||
|
||||
|
@ -157,23 +157,26 @@
|
|||
toggle-mode;
|
||||
|
||||
input-gpios
|
||||
= <&arduino_header 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&arduino_header 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&arduino_header 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
;
|
||||
= <&arduino_header 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&arduino_header 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&arduino_header 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
;
|
||||
};
|
||||
|
||||
encoder: encoder {
|
||||
label = "ENCODER";
|
||||
resolution = <4>;
|
||||
steps = <80>;
|
||||
compatible = "alps,ec11";
|
||||
a-gpios = <&arduino_header 14 GPIO_PULL_UP>;
|
||||
b-gpios = <&arduino_header 15 GPIO_PULL_UP>;
|
||||
a-gpios = <&arduino_header 15 GPIO_PULL_UP>;
|
||||
b-gpios = <&arduino_header 14 GPIO_PULL_UP>;
|
||||
};
|
||||
|
||||
sensors {
|
||||
compatible = "zmk,keymap-sensors";
|
||||
sensors = <&encoder>;
|
||||
triggers-per-rotation = <20>;
|
||||
left {
|
||||
triggers-per-rotation = <20>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
menuconfig EC11
|
||||
bool "EC11 Incremental Encoder Sensor"
|
||||
default y
|
||||
depends on DT_HAS_ALPS_EC11_ENABLED
|
||||
depends on GPIO
|
||||
help
|
||||
Enable driver for EC11 incremental encoder sensors.
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include "ec11.h"
|
||||
|
||||
#define FULL_ROTATION 360
|
||||
|
||||
LOG_MODULE_REGISTER(EC11, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static int ec11_get_ab_state(const struct device *dev) {
|
||||
|
@ -59,9 +61,14 @@ static int ec11_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
|||
drv_data->pulses += delta;
|
||||
drv_data->ab_state = val;
|
||||
|
||||
drv_data->ticks = drv_data->pulses / drv_cfg->resolution;
|
||||
drv_data->delta = delta;
|
||||
drv_data->pulses %= drv_cfg->resolution;
|
||||
// TODO: Temporary code for backwards compatibility to support
|
||||
// the sensor channel rotation reporting *ticks* instead of delta of degrees.
|
||||
// REMOVE ME
|
||||
if (drv_cfg->steps == 0) {
|
||||
drv_data->ticks = drv_data->pulses / drv_cfg->resolution;
|
||||
drv_data->delta = delta;
|
||||
drv_data->pulses %= drv_cfg->resolution;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -69,13 +76,26 @@ static int ec11_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
|||
static int ec11_channel_get(const struct device *dev, enum sensor_channel chan,
|
||||
struct sensor_value *val) {
|
||||
struct ec11_data *drv_data = dev->data;
|
||||
const struct ec11_config *drv_cfg = dev->config;
|
||||
int32_t pulses = drv_data->pulses;
|
||||
|
||||
if (chan != SENSOR_CHAN_ROTATION) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
val->val1 = drv_data->ticks;
|
||||
val->val2 = drv_data->delta;
|
||||
drv_data->pulses = 0;
|
||||
|
||||
if (drv_cfg->steps > 0) {
|
||||
val->val1 = (pulses * FULL_ROTATION) / drv_cfg->steps;
|
||||
val->val2 = (pulses * FULL_ROTATION) % drv_cfg->steps;
|
||||
if (val->val2 != 0) {
|
||||
val->val2 *= 1000000;
|
||||
val->val2 /= drv_cfg->steps;
|
||||
}
|
||||
} else {
|
||||
val->val1 = drv_data->ticks;
|
||||
val->val2 = drv_data->delta;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -132,7 +152,8 @@ int ec11_init(const struct device *dev) {
|
|||
const struct ec11_config ec11_cfg_##n = { \
|
||||
.a = GPIO_DT_SPEC_INST_GET(n, a_gpios), \
|
||||
.b = GPIO_DT_SPEC_INST_GET(n, b_gpios), \
|
||||
COND_CODE_0(DT_INST_NODE_HAS_PROP(n, resolution), (1), (DT_INST_PROP(n, resolution))), \
|
||||
.resolution = DT_INST_PROP_OR(n, resolution, 1), \
|
||||
.steps = DT_INST_PROP_OR(n, steps, 0), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(n, ec11_init, NULL, &ec11_data_##n, &ec11_cfg_##n, POST_KERNEL, \
|
||||
CONFIG_SENSOR_INIT_PRIORITY, &ec11_driver_api);
|
||||
|
|
|
@ -14,6 +14,7 @@ struct ec11_config {
|
|||
const struct gpio_dt_spec a;
|
||||
const struct gpio_dt_spec b;
|
||||
|
||||
const uint16_t steps;
|
||||
const uint8_t resolution;
|
||||
};
|
||||
|
||||
|
|
|
@ -18,4 +18,9 @@ properties:
|
|||
resolution:
|
||||
type: int
|
||||
description: Number of pulses per tick
|
||||
deprecated: true
|
||||
required: false
|
||||
steps:
|
||||
type: int
|
||||
description: Number of pulses in one full rotation
|
||||
required: false
|
||||
|
|
|
@ -4,16 +4,33 @@
|
|||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define MACRO_PLACEHOLDER 0
|
||||
#define ZMK_MACRO_STRINGIFY(x) #x
|
||||
#define ZMK_MACRO(name,...) \
|
||||
name: name { \
|
||||
label = ZMK_MACRO_STRINGIFY(ZM_ ## name); \
|
||||
compatible = "zmk,behavior-macro"; \
|
||||
#binding-cells = <0>; \
|
||||
__VA_ARGS__ \
|
||||
};
|
||||
name: name { \
|
||||
label = ZMK_MACRO_STRINGIFY(ZM_ ## name); \
|
||||
compatible = "zmk,behavior-macro"; \
|
||||
#binding-cells = <0>; \
|
||||
__VA_ARGS__ \
|
||||
};
|
||||
|
||||
/ {
|
||||
#define ZMK_MACRO1(name,...) \
|
||||
name: name { \
|
||||
label = ZMK_MACRO_STRINGIFY(ZM_ ## name); \
|
||||
compatible = "zmk,behavior-macro-one-param"; \
|
||||
#binding-cells = <1>; \
|
||||
__VA_ARGS__ \
|
||||
};
|
||||
|
||||
#define ZMK_MACRO2(name,...) \
|
||||
name: name { \
|
||||
label = ZMK_MACRO_STRINGIFY(ZM_ ## name); \
|
||||
compatible = "zmk,behavior-macro-two-param"; \
|
||||
#binding-cells = <2>; \
|
||||
__VA_ARGS__ \
|
||||
};
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
macro_tap: macro_control_mode_tap {
|
||||
compatible = "zmk,macro-control-mode-tap";
|
||||
|
@ -50,5 +67,29 @@
|
|||
label = "MAC_WAIT_REL";
|
||||
#binding-cells = <0>;
|
||||
};
|
||||
|
||||
macro_param_1to1: macro_param_1to1 {
|
||||
compatible = "zmk,macro-param-1to1";
|
||||
label = "MAC_PARAM_1TO1";
|
||||
#binding-cells = <0>;
|
||||
};
|
||||
|
||||
macro_param_1to2: macro_param_1to2 {
|
||||
compatible = "zmk,macro-param-1to2";
|
||||
label = "MAC_PARAM_1TO2";
|
||||
#binding-cells = <0>;
|
||||
};
|
||||
|
||||
macro_param_2to1: macro_param_2to1 {
|
||||
compatible = "zmk,macro-param-2to1";
|
||||
label = "MAC_PARAM_2TO1";
|
||||
#binding-cells = <0>;
|
||||
};
|
||||
|
||||
macro_param_2to2: macro_param_2to2 {
|
||||
compatible = "zmk,macro-param-2to2";
|
||||
label = "MAC_PARAM_2TO2";
|
||||
#binding-cells = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
13
app/dts/bindings/behaviors/macro_base.yaml
Normal file
13
app/dts/bindings/behaviors/macro_base.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
properties:
|
||||
bindings:
|
||||
type: phandle-array
|
||||
required: true
|
||||
wait-ms:
|
||||
type: int
|
||||
description: The default time to wait (in milliseconds) before triggering the next behavior in the macro bindings list.
|
||||
tap-ms:
|
||||
type: int
|
||||
description: The default time to wait (in milliseconds) between the press and release events on a tapped macro behavior binding
|
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Macro Behavior
|
||||
|
||||
compatible: "zmk,behavior-macro-one-param"
|
||||
|
||||
include: [one_param.yaml, macro_base.yaml]
|
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2022 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Macro Behavior
|
||||
|
||||
compatible: "zmk,behavior-macro-two-param"
|
||||
|
||||
include: [two_param.yaml, macro_base.yaml]
|
|
@ -5,15 +5,4 @@ description: Macro Behavior
|
|||
|
||||
compatible: "zmk,behavior-macro"
|
||||
|
||||
include: zero_param.yaml
|
||||
|
||||
properties:
|
||||
bindings:
|
||||
type: phandle-array
|
||||
required: true
|
||||
wait-ms:
|
||||
type: int
|
||||
description: The default time to wait (in milliseconds) before triggering the next behavior in the macro bindings list.
|
||||
tap-ms:
|
||||
type: int
|
||||
description: The default time to wait (in milliseconds) between the press and release events on a tapped macro behavior binding
|
||||
include: [zero_param.yaml, macro_base.yaml]
|
||||
|
|
8
app/dts/bindings/macros/zmk,macro-param-1to1.yaml
Normal file
8
app/dts/bindings/macros/zmk,macro-param-1to1.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2023 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Macro Parameter One Substituted Into Next Binding's First Parameter
|
||||
|
||||
compatible: "zmk,macro-param-1to1"
|
||||
|
||||
include: zero_param.yaml
|
8
app/dts/bindings/macros/zmk,macro-param-1to2.yaml
Normal file
8
app/dts/bindings/macros/zmk,macro-param-1to2.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2023 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Macro Parameter One Substituted Into Next Binding's Second Parameter
|
||||
|
||||
compatible: "zmk,macro-param-1to2"
|
||||
|
||||
include: zero_param.yaml
|
8
app/dts/bindings/macros/zmk,macro-param-2to1.yaml
Normal file
8
app/dts/bindings/macros/zmk,macro-param-2to1.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2023 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Macro Parameter Two Substituted Into Next Binding's First Parameter
|
||||
|
||||
compatible: "zmk,macro-param-2to1"
|
||||
|
||||
include: zero_param.yaml
|
8
app/dts/bindings/macros/zmk,macro-param-2to2.yaml
Normal file
8
app/dts/bindings/macros/zmk,macro-param-2to2.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2023 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Macro Parameter Two Substituted Into Next Binding's Second Parameter
|
||||
|
||||
compatible: "zmk,macro-param-2to2"
|
||||
|
||||
include: zero_param.yaml
|
|
@ -9,4 +9,14 @@ compatible: "zmk,keymap-sensors"
|
|||
properties:
|
||||
sensors:
|
||||
type: phandles
|
||||
required: true
|
||||
required: false
|
||||
triggers-per-rotation:
|
||||
type: int
|
||||
required: false
|
||||
|
||||
child-binding:
|
||||
description: Per-sensor configuration settings
|
||||
properties:
|
||||
triggers-per-rotation:
|
||||
type: int
|
||||
required: false
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <string.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zmk/keys.h>
|
||||
#include <zmk/sensors.h>
|
||||
#include <zmk/behavior.h>
|
||||
|
||||
/**
|
||||
|
@ -22,11 +23,20 @@
|
|||
* (Internal use only.)
|
||||
*/
|
||||
|
||||
enum behavior_sensor_binding_process_mode {
|
||||
BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER,
|
||||
BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_DISCARD,
|
||||
};
|
||||
|
||||
typedef int (*behavior_keymap_binding_callback_t)(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event);
|
||||
typedef int (*behavior_sensor_keymap_binding_callback_t)(struct zmk_behavior_binding *binding,
|
||||
const struct device *sensor,
|
||||
struct zmk_behavior_binding_event event);
|
||||
typedef int (*behavior_sensor_keymap_binding_process_callback_t)(
|
||||
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
|
||||
enum behavior_sensor_binding_process_mode mode);
|
||||
typedef int (*behavior_sensor_keymap_binding_accept_data_callback_t)(
|
||||
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
|
||||
const struct zmk_sensor_config *sensor_config, size_t channel_data_size,
|
||||
const struct zmk_sensor_channel_data channel_data[channel_data_size]);
|
||||
|
||||
enum behavior_locality {
|
||||
BEHAVIOR_LOCALITY_CENTRAL,
|
||||
|
@ -39,7 +49,8 @@ __subsystem struct behavior_driver_api {
|
|||
behavior_keymap_binding_callback_t binding_convert_central_state_dependent_params;
|
||||
behavior_keymap_binding_callback_t binding_pressed;
|
||||
behavior_keymap_binding_callback_t binding_released;
|
||||
behavior_sensor_keymap_binding_callback_t sensor_binding_triggered;
|
||||
behavior_sensor_keymap_binding_accept_data_callback_t sensor_binding_accept_data;
|
||||
behavior_sensor_keymap_binding_process_callback_t sensor_binding_process;
|
||||
};
|
||||
/**
|
||||
* @endcond
|
||||
|
@ -149,7 +160,7 @@ static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_bi
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Handle the a sensor keymap binding being triggered
|
||||
* @brief Handle the a sensor keymap binding processing any incoming data from the sensor
|
||||
* @param binding Sensor keymap binding which was triggered.
|
||||
* @param sensor Pointer to the sensor device structure for the sensor driver instance.
|
||||
* @param virtual_key_position ZMK_KEYMAP_LEN + sensor number
|
||||
|
@ -158,14 +169,15 @@ static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_bi
|
|||
* @retval 0 If successful.
|
||||
* @retval Negative errno code if failure.
|
||||
*/
|
||||
__syscall int behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *binding,
|
||||
const struct device *sensor,
|
||||
struct zmk_behavior_binding_event event);
|
||||
__syscall int behavior_sensor_keymap_binding_accept_data(
|
||||
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
|
||||
const struct zmk_sensor_config *sensor_config, size_t channel_data_size,
|
||||
const struct zmk_sensor_channel_data *channel_data);
|
||||
|
||||
static inline int
|
||||
z_impl_behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *binding,
|
||||
const struct device *sensor,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
static inline int z_impl_behavior_sensor_keymap_binding_accept_data(
|
||||
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
|
||||
const struct zmk_sensor_config *sensor_config, size_t channel_data_size,
|
||||
const struct zmk_sensor_channel_data *channel_data) {
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
|
||||
if (dev == NULL) {
|
||||
|
@ -174,11 +186,47 @@ z_impl_behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *bin
|
|||
|
||||
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
|
||||
|
||||
if (api->sensor_binding_triggered == NULL) {
|
||||
if (api->sensor_binding_accept_data == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return api->sensor_binding_triggered(binding, sensor, event);
|
||||
return api->sensor_binding_accept_data(binding, event, sensor_config, channel_data_size,
|
||||
channel_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle the keymap sensor binding being triggered after updating any local data
|
||||
* @param dev Pointer to the device structure for the driver instance.
|
||||
* @param param1 User parameter specified at time of behavior binding.
|
||||
* @param param2 User parameter specified at time of behavior binding.
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval Negative errno code if failure.
|
||||
*/
|
||||
// clang-format off
|
||||
__syscall int behavior_sensor_keymap_binding_process(
|
||||
struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event,
|
||||
enum behavior_sensor_binding_process_mode mode);
|
||||
// clang-format on
|
||||
|
||||
static inline int
|
||||
z_impl_behavior_sensor_keymap_binding_process(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event,
|
||||
enum behavior_sensor_binding_process_mode mode) {
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
|
||||
if (dev == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
|
||||
|
||||
if (api->sensor_binding_process == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return api->sensor_binding_process(binding, event, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,13 +6,22 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/sensors.h>
|
||||
|
||||
// TODO: Move to Kconfig when we need more than one channel
|
||||
#define ZMK_SENSOR_EVENT_MAX_CHANNELS 1
|
||||
|
||||
struct zmk_sensor_event {
|
||||
uint8_t sensor_number;
|
||||
const struct device *sensor;
|
||||
size_t channel_data_size;
|
||||
struct zmk_sensor_channel_data channel_data[ZMK_SENSOR_EVENT_MAX_CHANNELS];
|
||||
|
||||
int64_t timestamp;
|
||||
|
||||
uint8_t sensor_index;
|
||||
};
|
||||
|
||||
ZMK_EVENT_DECLARE(zmk_sensor_event);
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
|
||||
#define ZMK_KEYMAP_SENSORS_NODE DT_INST(0, zmk_keymap_sensors)
|
||||
#define ZMK_KEYMAP_HAS_SENSORS DT_NODE_HAS_STATUS(ZMK_KEYMAP_SENSORS_NODE, okay)
|
||||
#define ZMK_KEYMAP_SENSORS_BY_IDX(idx) DT_PHANDLE_BY_IDX(ZMK_KEYMAP_SENSORS_NODE, sensors, idx)
|
||||
|
@ -15,3 +17,14 @@
|
|||
#else
|
||||
#define ZMK_KEYMAP_SENSORS_LEN 0
|
||||
#endif
|
||||
|
||||
const struct zmk_sensor_config *zmk_sensors_get_config_at_index(uint8_t sensor_index);
|
||||
|
||||
struct zmk_sensor_config {
|
||||
uint16_t triggers_per_rotation;
|
||||
};
|
||||
|
||||
struct zmk_sensor_channel_data {
|
||||
enum sensor_channel channel;
|
||||
struct sensor_value value;
|
||||
};
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
*/
|
||||
#define ZMK_VIRTUAL_KEY_POSITION_SENSOR(index) (ZMK_KEYMAP_LEN + (index))
|
||||
|
||||
/**
|
||||
* Gets the sensor number from the virtual key position.
|
||||
*/
|
||||
#define ZMK_SENSOR_POSITION_FROM_VIRTUAL_KEY_POSITION(vkp) ((vkp)-ZMK_KEYMAP_LEN)
|
||||
|
||||
/**
|
||||
* Gets the virtual key position to use for the combo with the given index.
|
||||
*/
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_macro
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
@ -15,20 +13,22 @@
|
|||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
enum behavior_macro_mode {
|
||||
MACRO_MODE_TAP,
|
||||
MACRO_MODE_PRESS,
|
||||
MACRO_MODE_RELEASE,
|
||||
};
|
||||
|
||||
enum param_source { PARAM_SOURCE_BINDING, PARAM_SOURCE_MACRO_1ST, PARAM_SOURCE_MACRO_2ND };
|
||||
|
||||
struct behavior_macro_trigger_state {
|
||||
uint32_t wait_ms;
|
||||
uint32_t tap_ms;
|
||||
enum behavior_macro_mode mode;
|
||||
uint16_t start_index;
|
||||
uint16_t count;
|
||||
enum param_source param1_source;
|
||||
enum param_source param2_source;
|
||||
};
|
||||
|
||||
struct behavior_macro_state {
|
||||
|
@ -52,6 +52,11 @@ struct behavior_macro_config {
|
|||
#define WAIT_TIME DT_PROP(DT_INST(0, zmk_macro_control_wait_time), label)
|
||||
#define WAIT_REL DT_PROP(DT_INST(0, zmk_macro_pause_for_release), label)
|
||||
|
||||
#define P1TO1 DT_PROP(DT_INST(0, zmk_macro_param_1to1), label)
|
||||
#define P1TO2 DT_PROP(DT_INST(0, zmk_macro_param_1to2), label)
|
||||
#define P2TO1 DT_PROP(DT_INST(0, zmk_macro_param_2to1), label)
|
||||
#define P2TO2 DT_PROP(DT_INST(0, zmk_macro_param_2to2), label)
|
||||
|
||||
#define ZM_IS_NODE_MATCH(a, b) (strcmp(a, b) == 0)
|
||||
#define IS_TAP_MODE(dev) ZM_IS_NODE_MATCH(dev, TAP_MODE)
|
||||
#define IS_PRESS_MODE(dev) ZM_IS_NODE_MATCH(dev, PRESS_MODE)
|
||||
|
@ -61,6 +66,11 @@ struct behavior_macro_config {
|
|||
#define IS_WAIT_TIME(dev) ZM_IS_NODE_MATCH(dev, WAIT_TIME)
|
||||
#define IS_PAUSE(dev) ZM_IS_NODE_MATCH(dev, WAIT_REL)
|
||||
|
||||
#define IS_P1TO1(dev) ZM_IS_NODE_MATCH(dev, P1TO1)
|
||||
#define IS_P1TO2(dev) ZM_IS_NODE_MATCH(dev, P1TO2)
|
||||
#define IS_P2TO1(dev) ZM_IS_NODE_MATCH(dev, P2TO1)
|
||||
#define IS_P2TO2(dev) ZM_IS_NODE_MATCH(dev, P2TO2)
|
||||
|
||||
static bool handle_control_binding(struct behavior_macro_trigger_state *state,
|
||||
const struct zmk_behavior_binding *binding) {
|
||||
if (IS_TAP_MODE(binding->behavior_dev)) {
|
||||
|
@ -78,6 +88,18 @@ static bool handle_control_binding(struct behavior_macro_trigger_state *state,
|
|||
} else if (IS_WAIT_TIME(binding->behavior_dev)) {
|
||||
state->wait_ms = binding->param1;
|
||||
LOG_DBG("macro wait time set: %d", state->wait_ms);
|
||||
} else if (IS_P1TO1(binding->behavior_dev)) {
|
||||
state->param1_source = PARAM_SOURCE_MACRO_1ST;
|
||||
LOG_DBG("macro param: 1to1");
|
||||
} else if (IS_P1TO2(binding->behavior_dev)) {
|
||||
state->param2_source = PARAM_SOURCE_MACRO_1ST;
|
||||
LOG_DBG("macro param: 1to2");
|
||||
} else if (IS_P2TO1(binding->behavior_dev)) {
|
||||
state->param1_source = PARAM_SOURCE_MACRO_2ND;
|
||||
LOG_DBG("macro param: 2to1");
|
||||
} else if (IS_P2TO2(binding->behavior_dev)) {
|
||||
state->param2_source = PARAM_SOURCE_MACRO_2ND;
|
||||
LOG_DBG("macro param: 2to2");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -110,21 +132,47 @@ static int behavior_macro_init(const struct device *dev) {
|
|||
return 0;
|
||||
};
|
||||
|
||||
static uint32_t select_param(enum param_source param_source, uint32_t source_binding,
|
||||
const struct zmk_behavior_binding *macro_binding) {
|
||||
switch (param_source) {
|
||||
case PARAM_SOURCE_MACRO_1ST:
|
||||
return macro_binding->param1;
|
||||
case PARAM_SOURCE_MACRO_2ND:
|
||||
return macro_binding->param2;
|
||||
default:
|
||||
return source_binding;
|
||||
}
|
||||
};
|
||||
|
||||
static void replace_params(struct behavior_macro_trigger_state *state,
|
||||
struct zmk_behavior_binding *binding,
|
||||
const struct zmk_behavior_binding *macro_binding) {
|
||||
binding->param1 = select_param(state->param1_source, binding->param1, macro_binding);
|
||||
binding->param2 = select_param(state->param2_source, binding->param2, macro_binding);
|
||||
|
||||
state->param1_source = PARAM_SOURCE_BINDING;
|
||||
state->param2_source = PARAM_SOURCE_BINDING;
|
||||
}
|
||||
|
||||
static void queue_macro(uint32_t position, const struct zmk_behavior_binding bindings[],
|
||||
struct behavior_macro_trigger_state state) {
|
||||
struct behavior_macro_trigger_state state,
|
||||
const struct zmk_behavior_binding *macro_binding) {
|
||||
LOG_DBG("Iterating macro bindings - starting: %d, count: %d", state.start_index, state.count);
|
||||
for (int i = state.start_index; i < state.start_index + state.count; i++) {
|
||||
if (!handle_control_binding(&state, &bindings[i])) {
|
||||
struct zmk_behavior_binding binding = bindings[i];
|
||||
replace_params(&state, &binding, macro_binding);
|
||||
|
||||
switch (state.mode) {
|
||||
case MACRO_MODE_TAP:
|
||||
zmk_behavior_queue_add(position, bindings[i], true, state.tap_ms);
|
||||
zmk_behavior_queue_add(position, bindings[i], false, state.wait_ms);
|
||||
zmk_behavior_queue_add(position, binding, true, state.tap_ms);
|
||||
zmk_behavior_queue_add(position, binding, false, state.wait_ms);
|
||||
break;
|
||||
case MACRO_MODE_PRESS:
|
||||
zmk_behavior_queue_add(position, bindings[i], true, state.wait_ms);
|
||||
zmk_behavior_queue_add(position, binding, true, state.wait_ms);
|
||||
break;
|
||||
case MACRO_MODE_RELEASE:
|
||||
zmk_behavior_queue_add(position, bindings[i], false, state.wait_ms);
|
||||
zmk_behavior_queue_add(position, binding, false, state.wait_ms);
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Unknown macro mode: %d", state.mode);
|
||||
|
@ -145,7 +193,7 @@ static int on_macro_binding_pressed(struct zmk_behavior_binding *binding,
|
|||
.start_index = 0,
|
||||
.count = state->press_bindings_count};
|
||||
|
||||
queue_macro(event.position, cfg->bindings, trigger_state);
|
||||
queue_macro(event.position, cfg->bindings, trigger_state, binding);
|
||||
|
||||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
}
|
||||
|
@ -156,7 +204,7 @@ static int on_macro_binding_released(struct zmk_behavior_binding *binding,
|
|||
const struct behavior_macro_config *cfg = dev->config;
|
||||
struct behavior_macro_state *state = dev->data;
|
||||
|
||||
queue_macro(event.position, cfg->bindings, state->release_state);
|
||||
queue_macro(event.position, cfg->bindings, state->release_state, binding);
|
||||
|
||||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
}
|
||||
|
@ -166,22 +214,20 @@ static const struct behavior_driver_api behavior_macro_driver_api = {
|
|||
.binding_released = on_macro_binding_released,
|
||||
};
|
||||
|
||||
#define BINDING_WITH_COMMA(idx, drv_inst) ZMK_KEYMAP_EXTRACT_BINDING(idx, DT_DRV_INST(drv_inst))
|
||||
|
||||
#define TRANSFORMED_BEHAVIORS(n) \
|
||||
{LISTIFY(DT_PROP_LEN(DT_DRV_INST(n), bindings), BINDING_WITH_COMMA, (, ), n)},
|
||||
{LISTIFY(DT_PROP_LEN(n, bindings), ZMK_KEYMAP_EXTRACT_BINDING, (, ), n)},
|
||||
|
||||
#define MACRO_INST(n) \
|
||||
static struct behavior_macro_state behavior_macro_state_##n = {}; \
|
||||
static struct behavior_macro_config behavior_macro_config_##n = { \
|
||||
.default_wait_ms = DT_INST_PROP_OR(n, wait_ms, CONFIG_ZMK_MACRO_DEFAULT_WAIT_MS), \
|
||||
.default_tap_ms = DT_INST_PROP_OR(n, tap_ms, CONFIG_ZMK_MACRO_DEFAULT_TAP_MS), \
|
||||
.count = DT_INST_PROP_LEN(n, bindings), \
|
||||
.bindings = TRANSFORMED_BEHAVIORS(n)}; \
|
||||
DEVICE_DT_INST_DEFINE(n, behavior_macro_init, NULL, &behavior_macro_state_##n, \
|
||||
&behavior_macro_config_##n, APPLICATION, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_macro_driver_api);
|
||||
#define MACRO_INST(inst) \
|
||||
static struct behavior_macro_state behavior_macro_state_##inst = {}; \
|
||||
static struct behavior_macro_config behavior_macro_config_##inst = { \
|
||||
.default_wait_ms = DT_PROP_OR(inst, wait_ms, CONFIG_ZMK_MACRO_DEFAULT_WAIT_MS), \
|
||||
.default_tap_ms = DT_PROP_OR(inst, tap_ms, CONFIG_ZMK_MACRO_DEFAULT_TAP_MS), \
|
||||
.count = DT_PROP_LEN(inst, bindings), \
|
||||
.bindings = TRANSFORMED_BEHAVIORS(inst)}; \
|
||||
DEVICE_DT_DEFINE(inst, behavior_macro_init, NULL, &behavior_macro_state_##inst, \
|
||||
&behavior_macro_config_##inst, APPLICATION, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_macro_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(MACRO_INST)
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
DT_FOREACH_STATUS_OKAY(zmk_behavior_macro, MACRO_INST)
|
||||
DT_FOREACH_STATUS_OKAY(zmk_behavior_macro_one_param, MACRO_INST)
|
||||
DT_FOREACH_STATUS_OKAY(zmk_behavior_macro_two_param, MACRO_INST)
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
#include "behavior_sensor_rotate_common.h"
|
||||
|
||||
static const struct behavior_driver_api behavior_sensor_rotate_driver_api = {
|
||||
.sensor_binding_triggered = zmk_behavior_sensor_rotate_common_trigger};
|
||||
.sensor_binding_accept_data = zmk_behavior_sensor_rotate_common_accept_data,
|
||||
.sensor_binding_process = zmk_behavior_sensor_rotate_common_process};
|
||||
|
||||
static int behavior_sensor_rotate_init(const struct device *dev) { return 0; };
|
||||
|
||||
|
@ -33,8 +34,10 @@ static int behavior_sensor_rotate_init(const struct device *dev) { return 0; };
|
|||
.tap_ms = DT_INST_PROP_OR(n, tap_ms, 5), \
|
||||
.override_params = false, \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE( \
|
||||
n, behavior_sensor_rotate_init, NULL, NULL, &behavior_sensor_rotate_config_##n, \
|
||||
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_sensor_rotate_driver_api);
|
||||
static struct behavior_sensor_rotate_data behavior_sensor_rotate_data_##n = {}; \
|
||||
DEVICE_DT_INST_DEFINE(n, behavior_sensor_rotate_init, NULL, &behavior_sensor_rotate_data_##n, \
|
||||
&behavior_sensor_rotate_config_##n, APPLICATION, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||
&behavior_sensor_rotate_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SENSOR_ROTATE_INST)
|
||||
|
|
|
@ -5,48 +5,93 @@
|
|||
#include <zephyr/kernel.h>
|
||||
|
||||
#include <zmk/behavior_queue.h>
|
||||
#include <zmk/virtual_key_position.h>
|
||||
|
||||
#include "behavior_sensor_rotate_common.h"
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
int zmk_behavior_sensor_rotate_common_trigger(struct zmk_behavior_binding *binding,
|
||||
const struct device *sensor,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
int zmk_behavior_sensor_rotate_common_accept_data(
|
||||
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
|
||||
const struct zmk_sensor_config *sensor_config, size_t channel_data_size,
|
||||
const struct zmk_sensor_channel_data *channel_data) {
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
const struct behavior_sensor_rotate_config *cfg = dev->config;
|
||||
struct behavior_sensor_rotate_data *data = dev->data;
|
||||
|
||||
struct sensor_value value;
|
||||
const struct sensor_value value = channel_data[0].value;
|
||||
int triggers;
|
||||
int sensor_index = ZMK_SENSOR_POSITION_FROM_VIRTUAL_KEY_POSITION(event.position);
|
||||
|
||||
const int err = sensor_channel_get(sensor, SENSOR_CHAN_ROTATION, &value);
|
||||
// Some funky special casing for "old encoder behavior" where ticks where reported in val2 only,
|
||||
// instead of rotational degrees in val1.
|
||||
// REMOVE ME: Remove after a grace period of old ec11 sensor behavior
|
||||
if (value.val1 == 0) {
|
||||
triggers = value.val2;
|
||||
} else {
|
||||
struct sensor_value remainder = data->remainder[sensor_index];
|
||||
|
||||
if (err < 0) {
|
||||
LOG_WRN("Failed to get sensor rotation value: %d", err);
|
||||
return err;
|
||||
remainder.val1 += value.val1;
|
||||
remainder.val2 += value.val2;
|
||||
|
||||
if (remainder.val2 >= 1000000 || remainder.val2 <= 1000000) {
|
||||
remainder.val1 += remainder.val2 / 1000000;
|
||||
remainder.val2 %= 1000000;
|
||||
}
|
||||
|
||||
int trigger_degrees = 360 / sensor_config->triggers_per_rotation;
|
||||
triggers = remainder.val1 / trigger_degrees;
|
||||
remainder.val1 %= trigger_degrees;
|
||||
|
||||
data->remainder[sensor_index] = remainder;
|
||||
}
|
||||
|
||||
LOG_DBG(
|
||||
"val1: %d, val2: %d, remainder: %d/%d triggers: %d inc keycode 0x%02X dec keycode 0x%02X",
|
||||
value.val1, value.val2, data->remainder[sensor_index].val1,
|
||||
data->remainder[sensor_index].val2, triggers, binding->param1, binding->param2);
|
||||
|
||||
data->triggers[sensor_index] = triggers;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int zmk_behavior_sensor_rotate_common_process(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event,
|
||||
enum behavior_sensor_binding_process_mode mode) {
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
const struct behavior_sensor_rotate_config *cfg = dev->config;
|
||||
struct behavior_sensor_rotate_data *data = dev->data;
|
||||
|
||||
const int sensor_index = ZMK_SENSOR_POSITION_FROM_VIRTUAL_KEY_POSITION(event.position);
|
||||
|
||||
if (mode != BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER) {
|
||||
data->triggers[sensor_index] = 0;
|
||||
return ZMK_BEHAVIOR_TRANSPARENT;
|
||||
}
|
||||
|
||||
int triggers = data->triggers[sensor_index];
|
||||
|
||||
struct zmk_behavior_binding triggered_binding;
|
||||
switch (value.val1) {
|
||||
case 1:
|
||||
if (triggers > 0) {
|
||||
triggered_binding = cfg->cw_binding;
|
||||
if (cfg->override_params) {
|
||||
triggered_binding.param1 = binding->param1;
|
||||
}
|
||||
break;
|
||||
case -1:
|
||||
} else if (triggers < 0) {
|
||||
triggers = -triggers;
|
||||
triggered_binding = cfg->ccw_binding;
|
||||
if (cfg->override_params) {
|
||||
triggered_binding.param1 = binding->param2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
} else {
|
||||
return ZMK_BEHAVIOR_TRANSPARENT;
|
||||
}
|
||||
|
||||
LOG_DBG("Sensor binding: %s", binding->behavior_dev);
|
||||
|
||||
zmk_behavior_queue_add(event.position, triggered_binding, true, cfg->tap_ms);
|
||||
zmk_behavior_queue_add(event.position, triggered_binding, false, 0);
|
||||
for (int i = 0; i < triggers; i++) {
|
||||
zmk_behavior_queue_add(event.position, triggered_binding, true, cfg->tap_ms);
|
||||
zmk_behavior_queue_add(event.position, triggered_binding, false, 0);
|
||||
}
|
||||
|
||||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
/*
|
||||
* Copyright (c) 2023 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <drivers/behavior.h>
|
||||
#include <zmk/behavior.h>
|
||||
#include <zmk/sensors.h>
|
||||
|
||||
struct behavior_sensor_rotate_config {
|
||||
struct zmk_behavior_binding cw_binding;
|
||||
|
@ -8,6 +15,15 @@ struct behavior_sensor_rotate_config {
|
|||
bool override_params;
|
||||
};
|
||||
|
||||
int zmk_behavior_sensor_rotate_common_trigger(struct zmk_behavior_binding *binding,
|
||||
const struct device *sensor,
|
||||
struct zmk_behavior_binding_event event);
|
||||
struct behavior_sensor_rotate_data {
|
||||
struct sensor_value remainder[ZMK_KEYMAP_SENSORS_LEN];
|
||||
int triggers[ZMK_KEYMAP_SENSORS_LEN];
|
||||
};
|
||||
|
||||
int zmk_behavior_sensor_rotate_common_accept_data(
|
||||
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
|
||||
const struct zmk_sensor_config *sensor_config, size_t channel_data_size,
|
||||
const struct zmk_sensor_channel_data *channel_data);
|
||||
int zmk_behavior_sensor_rotate_common_process(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event,
|
||||
enum behavior_sensor_binding_process_mode mode);
|
|
@ -13,7 +13,8 @@
|
|||
#include "behavior_sensor_rotate_common.h"
|
||||
|
||||
static const struct behavior_driver_api behavior_sensor_rotate_var_driver_api = {
|
||||
.sensor_binding_triggered = zmk_behavior_sensor_rotate_common_trigger};
|
||||
.sensor_binding_accept_data = zmk_behavior_sensor_rotate_common_accept_data,
|
||||
.sensor_binding_process = zmk_behavior_sensor_rotate_common_process};
|
||||
|
||||
static int behavior_sensor_rotate_var_init(const struct device *dev) { return 0; };
|
||||
|
||||
|
@ -24,8 +25,10 @@ static int behavior_sensor_rotate_var_init(const struct device *dev) { return 0;
|
|||
.tap_ms = DT_INST_PROP(n, tap_ms), \
|
||||
.override_params = true, \
|
||||
}; \
|
||||
static struct behavior_sensor_rotate_data behavior_sensor_rotate_var_data_##n = {}; \
|
||||
DEVICE_DT_INST_DEFINE( \
|
||||
n, behavior_sensor_rotate_var_init, NULL, NULL, &behavior_sensor_rotate_var_config_##n, \
|
||||
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_sensor_rotate_var_driver_api);
|
||||
n, behavior_sensor_rotate_var_init, NULL, &behavior_sensor_rotate_var_data_##n, \
|
||||
&behavior_sensor_rotate_var_config_##n, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||
&behavior_sensor_rotate_var_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SENSOR_ROTATE_VAR_INST)
|
||||
|
|
|
@ -252,41 +252,58 @@ int zmk_keymap_position_state_changed(uint8_t source, uint32_t position, bool pr
|
|||
}
|
||||
|
||||
#if ZMK_KEYMAP_HAS_SENSORS
|
||||
int zmk_keymap_sensor_triggered(uint8_t sensor_number, const struct device *sensor,
|
||||
int64_t timestamp) {
|
||||
for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) {
|
||||
if (zmk_keymap_layer_active(layer)) {
|
||||
struct zmk_behavior_binding *binding = &zmk_sensor_keymap[layer][sensor_number];
|
||||
const struct device *behavior;
|
||||
int ret;
|
||||
int zmk_keymap_sensor_event(uint8_t sensor_index,
|
||||
const struct zmk_sensor_channel_data *channel_data,
|
||||
size_t channel_data_size, int64_t timestamp) {
|
||||
bool opaque_response = false;
|
||||
|
||||
LOG_DBG("layer: %d sensor_number: %d, binding name: %s", layer, sensor_number,
|
||||
binding->behavior_dev);
|
||||
for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= 0; layer--) {
|
||||
struct zmk_behavior_binding *binding = &zmk_sensor_keymap[layer][sensor_index];
|
||||
|
||||
behavior = device_get_binding(binding->behavior_dev);
|
||||
LOG_DBG("layer: %d sensor_index: %d, binding name: %s", layer, sensor_index,
|
||||
binding->behavior_dev);
|
||||
|
||||
if (!behavior) {
|
||||
LOG_DBG("No behavior assigned to %d on layer %d", sensor_number, layer);
|
||||
continue;
|
||||
}
|
||||
const struct device *behavior = device_get_binding(binding->behavior_dev);
|
||||
if (!behavior) {
|
||||
LOG_DBG("No behavior assigned to %d on layer %d", sensor_index, layer);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct zmk_behavior_binding_event event = {
|
||||
.position = ZMK_VIRTUAL_KEY_POSITION_SENSOR(sensor_number), .timestamp = timestamp};
|
||||
ret = behavior_sensor_keymap_binding_triggered(binding, sensor, event);
|
||||
struct zmk_behavior_binding_event event = {
|
||||
.layer = layer,
|
||||
.position = ZMK_VIRTUAL_KEY_POSITION_SENSOR(sensor_index),
|
||||
.timestamp = timestamp,
|
||||
};
|
||||
|
||||
if (ret > 0) {
|
||||
LOG_DBG("behavior processing to continue to next layer");
|
||||
continue;
|
||||
} else if (ret < 0) {
|
||||
LOG_DBG("Behavior returned error: %d", ret);
|
||||
return ret;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
int ret = behavior_sensor_keymap_binding_accept_data(
|
||||
binding, event, zmk_sensors_get_config_at_index(sensor_index), channel_data_size,
|
||||
channel_data);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_WRN("behavior data accept for behavior %s returned an error (%d). Processing to "
|
||||
"continue to next layer",
|
||||
binding->behavior_dev, ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
enum behavior_sensor_binding_process_mode mode =
|
||||
(!opaque_response && layer >= _zmk_keymap_layer_default &&
|
||||
zmk_keymap_layer_active(layer))
|
||||
? BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER
|
||||
: BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_DISCARD;
|
||||
|
||||
ret = behavior_sensor_keymap_binding_process(binding, event, mode);
|
||||
|
||||
if (ret == ZMK_BEHAVIOR_OPAQUE) {
|
||||
LOG_DBG("sensor event processing complete, behavior response was opaque");
|
||||
opaque_response = true;
|
||||
} else if (ret < 0) {
|
||||
LOG_DBG("Behavior returned error: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* ZMK_KEYMAP_HAS_SENSORS */
|
||||
|
@ -301,8 +318,8 @@ int keymap_listener(const zmk_event_t *eh) {
|
|||
#if ZMK_KEYMAP_HAS_SENSORS
|
||||
const struct zmk_sensor_event *sensor_ev;
|
||||
if ((sensor_ev = as_zmk_sensor_event(eh)) != NULL) {
|
||||
return zmk_keymap_sensor_triggered(sensor_ev->sensor_number, sensor_ev->sensor,
|
||||
sensor_ev->timestamp);
|
||||
return zmk_keymap_sensor_event(sensor_ev->sensor_index, sensor_ev->channel_data,
|
||||
sensor_ev->channel_data_size, sensor_ev->timestamp);
|
||||
}
|
||||
#endif /* ZMK_KEYMAP_HAS_SENSORS */
|
||||
|
||||
|
|
|
@ -18,65 +18,133 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
|
||||
#if ZMK_KEYMAP_HAS_SENSORS
|
||||
|
||||
struct sensors_data_item {
|
||||
uint8_t sensor_number;
|
||||
struct sensors_item_cfg {
|
||||
const struct zmk_sensor_config *config;
|
||||
const struct device *dev;
|
||||
struct sensor_trigger trigger;
|
||||
uint8_t sensor_index;
|
||||
};
|
||||
|
||||
#define _SENSOR_ITEM(node) \
|
||||
#define _SENSOR_ITEM(idx, node) \
|
||||
{ \
|
||||
.dev = NULL, .trigger = {.type = SENSOR_TRIG_DELTA, .chan = SENSOR_CHAN_ROTATION } \
|
||||
.dev = DEVICE_DT_GET_OR_NULL(node), \
|
||||
.trigger = {.type = SENSOR_TRIG_DATA_READY, .chan = SENSOR_CHAN_ROTATION}, \
|
||||
.config = &configs[idx] \
|
||||
}
|
||||
#define SENSOR_ITEM(idx, _i) _SENSOR_ITEM(idx, ZMK_KEYMAP_SENSORS_BY_IDX(idx))
|
||||
|
||||
#define PLUS_ONE(n) +1
|
||||
#define ZMK_KEYMAP_SENSORS_CHILD_COUNT (0 DT_FOREACH_CHILD(ZMK_KEYMAP_SENSORS_NODE, PLUS_ONE))
|
||||
#define SENSOR_CHILD_ITEM(node) \
|
||||
{ \
|
||||
.triggers_per_rotation = \
|
||||
DT_PROP_OR(node, triggers_per_rotation, \
|
||||
DT_PROP_OR(ZMK_KEYMAP_SENSORS_NODE, triggers_per_rotation, \
|
||||
CONFIG_ZMK_KEYMAP_SENSORS_DEFAULT_TRIGGERS_PER_ROTATION)) \
|
||||
}
|
||||
#define SENSOR_CHILD_DEFAULTS(idx, arg) \
|
||||
{ \
|
||||
.triggers_per_rotation = \
|
||||
DT_PROP_OR(ZMK_KEYMAP_SENSORS_NODE, triggers_per_rotation, \
|
||||
CONFIG_ZMK_KEYMAP_SENSORS_DEFAULT_TRIGGERS_PER_ROTATION) \
|
||||
}
|
||||
|
||||
#define SENSOR_ITEM(idx, _node) \
|
||||
COND_CODE_1(DT_NODE_HAS_STATUS(ZMK_KEYMAP_SENSORS_BY_IDX(idx), okay), \
|
||||
(_SENSOR_ITEM(ZMK_KEYMAP_SENSORS_BY_IDX(idx))), ({}))
|
||||
static struct zmk_sensor_config configs[] = {
|
||||
#if ZMK_KEYMAP_SENSORS_CHILD_COUNT > 0
|
||||
DT_FOREACH_CHILD_SEP(ZMK_KEYMAP_SENSORS_NODE, SENSOR_CHILD_ITEM, (, ))
|
||||
#else
|
||||
LISTIFY(ZMK_KEYMAP_SENSORS_LEN, SENSOR_CHILD_DEFAULTS, (, ), 0)
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct sensors_data_item sensors[] = {LISTIFY(ZMK_KEYMAP_SENSORS_LEN, SENSOR_ITEM, (, ), 0)};
|
||||
static struct sensors_item_cfg sensors[] = {LISTIFY(ZMK_KEYMAP_SENSORS_LEN, SENSOR_ITEM, (, ), 0)};
|
||||
|
||||
static void zmk_sensors_trigger_handler(const struct device *dev,
|
||||
const struct sensor_trigger *trigger) {
|
||||
static ATOMIC_DEFINE(pending_sensors, ZMK_KEYMAP_SENSORS_LEN);
|
||||
|
||||
const struct zmk_sensor_config *zmk_sensors_get_config_at_index(uint8_t sensor_index) {
|
||||
if (sensor_index > ARRAY_SIZE(configs)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &configs[sensor_index];
|
||||
}
|
||||
|
||||
static void trigger_sensor_data_for_position(uint32_t sensor_index) {
|
||||
int err;
|
||||
const struct sensors_data_item *item = CONTAINER_OF(trigger, struct sensors_data_item, trigger);
|
||||
const struct sensors_item_cfg *item = &sensors[sensor_index];
|
||||
|
||||
LOG_DBG("sensor %d", item->sensor_number);
|
||||
|
||||
err = sensor_sample_fetch(dev);
|
||||
err = sensor_sample_fetch(item->dev);
|
||||
if (err) {
|
||||
LOG_WRN("Failed to fetch sample from device %d", err);
|
||||
return;
|
||||
}
|
||||
|
||||
ZMK_EVENT_RAISE(new_zmk_sensor_event((struct zmk_sensor_event){
|
||||
.sensor_number = item->sensor_number, .sensor = dev, .timestamp = k_uptime_get()}));
|
||||
}
|
||||
struct sensor_value value;
|
||||
err = sensor_channel_get(item->dev, item->trigger.chan, &value);
|
||||
|
||||
static void zmk_sensors_init_item(const char *node, uint8_t i, uint8_t abs_i) {
|
||||
LOG_DBG("Init %s at index %d with sensor_number %d", node, i, abs_i);
|
||||
|
||||
sensors[i].dev = device_get_binding(node);
|
||||
sensors[i].sensor_number = abs_i;
|
||||
|
||||
if (!sensors[i].dev) {
|
||||
LOG_WRN("Failed to find device for %s", node);
|
||||
if (err) {
|
||||
LOG_WRN("Failed to get channel data from device %d", err);
|
||||
return;
|
||||
}
|
||||
|
||||
sensor_trigger_set(sensors[i].dev, &sensors[i].trigger, zmk_sensors_trigger_handler);
|
||||
ZMK_EVENT_RAISE(new_zmk_sensor_event(
|
||||
(struct zmk_sensor_event){.sensor_index = item->sensor_index,
|
||||
.channel_data_size = 1,
|
||||
.channel_data = {(struct zmk_sensor_channel_data){
|
||||
.value = value, .channel = item->trigger.chan}},
|
||||
.timestamp = k_uptime_get()}));
|
||||
}
|
||||
|
||||
#define _SENSOR_INIT(node) \
|
||||
zmk_sensors_init_item(DT_PROP(node, label), local_index++, absolute_index++);
|
||||
#define SENSOR_INIT(idx, _i) \
|
||||
COND_CODE_1(DT_NODE_HAS_STATUS(ZMK_KEYMAP_SENSORS_BY_IDX(idx), okay), \
|
||||
(_SENSOR_INIT(ZMK_KEYMAP_SENSORS_BY_IDX(idx))), (absolute_index++;))
|
||||
static void run_sensors_data_trigger(struct k_work *work) {
|
||||
for (int i = 0; i < ARRAY_SIZE(sensors); i++) {
|
||||
if (atomic_test_and_clear_bit(pending_sensors, i)) {
|
||||
trigger_sensor_data_for_position(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
K_WORK_DEFINE(sensor_data_work, run_sensors_data_trigger);
|
||||
|
||||
static void zmk_sensors_trigger_handler(const struct device *dev,
|
||||
const struct sensor_trigger *trigger) {
|
||||
const struct sensors_item_cfg *test_item =
|
||||
CONTAINER_OF(trigger, struct sensors_item_cfg, trigger);
|
||||
int sensor_index = test_item - sensors;
|
||||
|
||||
if (sensor_index < 0 || sensor_index >= ARRAY_SIZE(sensors)) {
|
||||
LOG_ERR("Invalid sensor item triggered our callback");
|
||||
return;
|
||||
}
|
||||
|
||||
if (k_is_in_isr()) {
|
||||
atomic_set_bit(pending_sensors, sensor_index);
|
||||
k_work_submit(&sensor_data_work);
|
||||
} else {
|
||||
trigger_sensor_data_for_position(sensor_index);
|
||||
}
|
||||
}
|
||||
|
||||
static void zmk_sensors_init_item(uint8_t i) {
|
||||
LOG_DBG("Init sensor at index %d", i);
|
||||
|
||||
sensors[i].sensor_index = i;
|
||||
|
||||
if (!sensors[i].dev) {
|
||||
LOG_DBG("No local device for %d", i);
|
||||
return;
|
||||
}
|
||||
|
||||
int err = sensor_trigger_set(sensors[i].dev, &sensors[i].trigger, zmk_sensors_trigger_handler);
|
||||
if (err) {
|
||||
LOG_WRN("Failed to set sensor trigger (%d)", err);
|
||||
}
|
||||
}
|
||||
|
||||
#define SENSOR_INIT(idx, _t) zmk_sensors_init_item(idx);
|
||||
|
||||
static int zmk_sensors_init(const struct device *_arg) {
|
||||
int local_index = 0;
|
||||
int absolute_index = 0;
|
||||
|
||||
LISTIFY(ZMK_KEYMAP_SENSORS_LEN, SENSOR_INIT, (), 0)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,18 @@ config ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_QUEUE_SIZE
|
|||
int "Max number of behavior run events to queue to send to the peripheral(s)"
|
||||
default 5
|
||||
|
||||
config ZMK_SPLIT_BLE_PREF_INT
|
||||
int "Connection interval to use for split central/peripheral connection"
|
||||
default 6
|
||||
|
||||
config ZMK_SPLIT_BLE_PREF_LATENCY
|
||||
int "Latency to use for split central/peripheral connection"
|
||||
default 30
|
||||
|
||||
config ZMK_SPLIT_BLE_PREF_TIMEOUT
|
||||
int "Supervision timeout to use for split central/peripheral connection"
|
||||
default 400
|
||||
|
||||
endif # ZMK_SPLIT_ROLE_CENTRAL
|
||||
|
||||
if !ZMK_SPLIT_ROLE_CENTRAL
|
||||
|
@ -58,8 +70,9 @@ config BT_MAX_PAIRED
|
|||
config BT_MAX_CONN
|
||||
default 1
|
||||
|
||||
config BT_PERIPHERAL_PREF_MAX_INT
|
||||
default 6
|
||||
# Allow central to specify connection parameters.
|
||||
config BT_GAP_AUTO_UPDATE_CONN_PARAMS
|
||||
default n
|
||||
|
||||
#!ZMK_SPLIT_ROLE_CENTRAL
|
||||
endif
|
||||
|
|
|
@ -383,7 +383,9 @@ static bool split_central_eir_found(const bt_addr_le_t *addr) {
|
|||
struct peripheral_slot *slot = &peripherals[slot_idx];
|
||||
|
||||
LOG_DBG("Initiating new connnection");
|
||||
struct bt_le_conn_param *param = BT_LE_CONN_PARAM(0x0006, 0x0006, 30, 400);
|
||||
struct bt_le_conn_param *param =
|
||||
BT_LE_CONN_PARAM(CONFIG_ZMK_SPLIT_BLE_PREF_INT, CONFIG_ZMK_SPLIT_BLE_PREF_INT,
|
||||
CONFIG_ZMK_SPLIT_BLE_PREF_LATENCY, CONFIG_ZMK_SPLIT_BLE_PREF_TIMEOUT);
|
||||
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &slot->conn);
|
||||
if (err < 0) {
|
||||
LOG_ERR("Create conn failed (err %d) (create conn? 0x%04x)", err, BT_HCI_OP_LE_CREATE_CONN);
|
||||
|
|
1
app/tests/macros/place-holder-parameters/events.patterns
Normal file
1
app/tests/macros/place-holder-parameters/events.patterns
Normal file
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
|
@ -0,0 +1,16 @@
|
|||
kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x38 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x38 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x34 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x34 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x34 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x34 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
|
||||
/ {
|
||||
macros {
|
||||
slash_macro: slash_macro {
|
||||
#binding-cells = <2>;
|
||||
label = "ZM_SLASH";
|
||||
compatible = "zmk,behavior-macro-two-param";
|
||||
wait-ms = <40>;
|
||||
tap-ms = <40>;
|
||||
bindings = <
|
||||
¯o_param_1to1 &kp MACRO_PLACEHOLDER
|
||||
&kp SLASH
|
||||
¯o_param_2to1 &kp MACRO_PLACEHOLDER>;
|
||||
};
|
||||
|
||||
to_second_macro: to_second_macro {
|
||||
#binding-cells = <2>;
|
||||
label = "ZMK_TO_SECOND";
|
||||
compatible = "zmk,behavior-macro-two-param";
|
||||
wait-ms = <40>;
|
||||
tap-ms = <40>;
|
||||
bindings = <
|
||||
¯o_param_1to2 &mt LSHIFT MACRO_PLACEHOLDER
|
||||
¯o_param_2to2 &mt RSHIFT MACRO_PLACEHOLDER>;
|
||||
};
|
||||
|
||||
quote_letter_macro: quote_letter_macro {
|
||||
#binding-cells = <1>;
|
||||
label = "ZMK_QLET";
|
||||
compatible = "zmk,behavior-macro-one-param";
|
||||
wait-ms = <40>;
|
||||
tap-ms = <40>;
|
||||
bindings = <
|
||||
&kp QUOT
|
||||
¯o_param_1to1 &kp MACRO_PLACEHOLDER
|
||||
&kp QUOT>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label = "Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&slash_macro A B "e_letter_macro B
|
||||
&to_second_macro E F &kp C>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <ZMK_MOCK_PRESS(0,0,20) ZMK_MOCK_PRESS(0,1,10) ZMK_MOCK_RELEASE(0,1,10) ZMK_MOCK_RELEASE(0,0,10) ZMK_MOCK_PRESS(1,0,10) ZMK_MOCK_RELEASE(1,0,1000)>;
|
||||
};
|
119
docs/blog/2023-06-18-encoder-refactors.md
Normal file
119
docs/blog/2023-06-18-encoder-refactors.md
Normal file
|
@ -0,0 +1,119 @@
|
|||
---
|
||||
title: "Major Encoder Refactor"
|
||||
author: Pete Johanson
|
||||
author_title: Project Creator
|
||||
author_url: https://gitlab.com/petejohanson
|
||||
author_image_url: https://www.gravatar.com/avatar/2001ceff7e9dc753cf96fcb2e6f41110
|
||||
tags: [firmware, zephyr, sensors, encoders]
|
||||
---
|
||||
|
||||
Today, we merged a significant change to the low level sensor code that is used to support encoders. In particular,
|
||||
this paves the way for completing the work on supporting split peripheral sensors/encoders, and other future sensors
|
||||
like pointing devices.
|
||||
|
||||
As part of the work, backwards compatibility for existing shields has been retained, but only for a grace period to allow out-of-tree shields to move to the new approach for encoders.
|
||||
|
||||
Special thanks to [joelspadin] for the _thorough_ code review and testing throughout the development of the refactor.
|
||||
|
||||
## Summary of Changes
|
||||
|
||||
The following items have been merged:
|
||||
|
||||
1. Split configuration of hardware details, and behavior configuration to allow more flexible functionality of sensors/encoders, in particular linear encoders that lack detents/"clicks" as they rotate.
|
||||
2. Support for upstream Zephyr sensor drivers, including the NRFX QDEC driver that can be used on nRF52 based keyboards.
|
||||
3. Sensor data handling changes that pave the way for split sensor handling easily.
|
||||
|
||||
## Configuration Changes
|
||||
|
||||
The major changes to configuration in the devicetree files relates to how the number of steps/triggers for a given encoder are set. In particular, the number of pulses/steps for a given encoder is configured first, allowing ZMK to determine the exact angular degrees of change that is represented by a single pulse on the data lines to that encoder.
|
||||
|
||||
Once that angular degrees mapping is completed, now independently there is a configuration setting to control how many triggers of the behavior in the keymap should occur for each full rotation of the sensor. Another way to think of this is "how many degrees of rotation results in a triggering of the sensor behavior in your keymap layer".
|
||||
|
||||
Splitting these two parts of the encoder configuration allows greater flexibility, and fine grained control of encoder behavior for linear encoders that don't have fixed detents.
|
||||
|
||||
### Old Configuration
|
||||
|
||||
Previously, an encoder configuration looked like:
|
||||
|
||||
```
|
||||
left_encoder: encoder_left {
|
||||
compatible = "alps,ec11";
|
||||
label = "LEFT_ENCODER";
|
||||
a-gpios = <&pro_micro 21 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
|
||||
b-gpios = <&pro_micro 20 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
|
||||
resolution = <4>;
|
||||
};
|
||||
```
|
||||
|
||||
Here, the `resolution` property was used to indicate how many encoder pulses should trigger the sensor behavior one time. Next, the encoder is selected in the sensors node:
|
||||
|
||||
```
|
||||
sensors {
|
||||
compatible = "zmk,keymap-sensors";
|
||||
sensors = <&left_encoder &right_encoder>;
|
||||
};
|
||||
```
|
||||
|
||||
That was the entirety of the configuration for encoders.
|
||||
|
||||
### New Configuration
|
||||
|
||||
```
|
||||
left_encoder: encoder_left {
|
||||
compatible = "alps,ec11";
|
||||
label = "LEFT_ENCODER";
|
||||
a-gpios = <&pro_micro 21 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
|
||||
b-gpios = <&pro_micro 20 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
|
||||
steps = <80>;
|
||||
};
|
||||
```
|
||||
|
||||
Here, the `steps` property is now used to indicate how many encoder pulses there are in a single complete rotation of the encoder. Next, the encoder is selected in the sensors node as before, but an additional configuration is used to indicate how many times the encoder should trigger the behavior in your keymap per rotation:
|
||||
|
||||
```
|
||||
sensors {
|
||||
compatible = "zmk,keymap-sensors";
|
||||
sensors = <&left_encoder &right_encoder>;
|
||||
triggers-per-rotation = <20>;
|
||||
};
|
||||
```
|
||||
|
||||
For tactile encoders that have detents, the `triggers-per-rotation` would match the number of detents on the encoder. For linear encoders, the value can be chosen to suit your needs.
|
||||
|
||||
## Zephyr Sensor Drivers
|
||||
|
||||
The configuration changes bring ZMK's code in line with how upstream Zephyr sensor drivers handle rotations. This has the added advantage of allowing us to leverage other sensor drivers. On Nordic MCUs, like nRF52840, the NRFX QDEC driver can be used, for example:
|
||||
|
||||
```
|
||||
&pinctrl {
|
||||
qdec_default: qdec_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(QDEC_A, 1, 11)>,
|
||||
<NRF_PSEL(QDEC_B, 1, 10)>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// Set up the QDEC hardware based driver and give it the same label as the deleted node.
|
||||
encoder: &qdec0 {
|
||||
status = "okay";
|
||||
led-pre = <0>;
|
||||
steps = <80>;
|
||||
pinctrl-0 = <&qdec_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
```
|
||||
|
||||
The NRFX QDEC driver has the advantage of supporting optical encoders as well, and although it polls, it does so in hardware without waking the MCU core; initial basic power profiling is promising.
|
||||
|
||||
## Split Sensor/Encoder Support
|
||||
|
||||
In addition to the refactors for splitting the configuration, the changes merged included refactors designed to simplify and move forward with the long outstanding feature of supporting encoders on the peripheral side of split keyboards. That work is planned as a follow up.
|
||||
|
||||
## Deprecation
|
||||
|
||||
The old configuration will be supported for a period of one month, and then removed, giving users a grace period to complete the migration to the new separated configuration.
|
||||
|
||||
[petejohanson]: https://github.com/petejohanson
|
||||
[joelspadin]: https://github.com/joelspadin
|
|
@ -49,6 +49,22 @@ For use cases involving sending a single keycode with modifiers, for instance ct
|
|||
with [modifier functions](../codes/modifiers.mdx#modifier-functions) can be used instead of a macro.
|
||||
:::
|
||||
|
||||
### Parameterized Macros
|
||||
|
||||
Macros can also be "parameterized", allowing them to be bound in your keymap with unique values passed into them, e.g.:
|
||||
|
||||
```
|
||||
raise_layer {
|
||||
bindings = <&my_cool_macro A>
|
||||
};
|
||||
```
|
||||
|
||||
When defining a parameterized macro, a different `compatible` value will be used depending on how many parameters are passed into it:
|
||||
|
||||
- `zmk,behavior-macro` - a parameter that takes no parameters.
|
||||
- `zmk,behavior-macro-one-param` - a parameter that takes one parameter when used.
|
||||
- `zmk,behavior-macro-two-param` - a parameter that takes two parameters when used.
|
||||
|
||||
### Bindings
|
||||
|
||||
Like [hold-taps](/docs/behaviors/hold-tap), macros are created by composing other behaviors, and any of those behaviors can
|
||||
|
@ -67,6 +83,30 @@ bindings
|
|||
There are a set of special macro controls that can be included in the `bindings` list to modify the
|
||||
way the macro is processed.
|
||||
|
||||
### Parameters
|
||||
|
||||
When creating a macro that takes parameter(s), there are macro controls that change when the parameters passed to the macro are used
|
||||
within the macro itself. All of the controls are "one shot" and will change how the passed in parameters are used for the very next non-macro control behavior in the `bindings` list of the macro.
|
||||
|
||||
For example, to pass the first parameter from the macro into a `&kp` used in the macro, you would use:
|
||||
|
||||
```
|
||||
bindings
|
||||
= <¯o_param_1to1>
|
||||
, <&kp MACRO_PLACEHOLDER>
|
||||
;
|
||||
```
|
||||
|
||||
Because `kp` takes one parameter, you can't simply make the second entry `<&kp>` in the `bindings` list. Whatever value you do pass in will be replaced when the macro is triggered, so you can put _any_ value there, e.g. `0`, `A` keycode, etc. To make it very obvious that the parameter there is not actually going to be used, you can use `MACRO_PLACEHOLDER` which is simply an alias for `0`.
|
||||
|
||||
The available parameter controls are:
|
||||
|
||||
- `¯o_param_1to1` - pass the first parameter of the macro into the first parameter of the next behavior in the `bindings` list.
|
||||
- `¯o_param_1to2` - pass the first parameter of the macro into the second parameter of the next behavior in the `bindings` list.
|
||||
|
||||
* `¯o_param_2to1` - pass the second parameter of the macro into the first parameter of the next behavior in the `bindings` list.
|
||||
* `¯o_param_2to2` - pass the second parameter of the macro into the second parameter of the next behavior in the `bindings` list.
|
||||
|
||||
### Binding Activation Mode
|
||||
|
||||
Bindings in a macro are activated differently, depending on the current "activation mode" of the macro.
|
||||
|
|
Loading…
Add table
Reference in a new issue