Merge branch 'zmkfirmware:main' into main

This commit is contained in:
Retic1337 2021-11-14 15:39:18 -08:00 committed by GitHub
commit 65d24bfe6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
129 changed files with 2672 additions and 1658 deletions

View file

@ -1,4 +1,4 @@
FROM zmkfirmware/zmk-dev-arm:2.5 FROM docker.io/zmkfirmware/zmk-dev-arm:2.5
COPY .bashrc tmp COPY .bashrc tmp
RUN mv /tmp/.bashrc ~/.bashrc RUN mv /tmp/.bashrc ~/.bashrc

View file

@ -3,7 +3,7 @@
- [ ] This board/shield is tested working on real hardware - [ ] This board/shield is tested working on real hardware
- [ ] Definitions follow the general style of other shields/boards upstream ([Reference](https://zmk.dev/docs/development/new-shield)) - [ ] Definitions follow the general style of other shields/boards upstream ([Reference](https://zmk.dev/docs/development/new-shield))
- [ ] `.zmk.yml` metadata file added - [ ] `.zmk.yml` metadata file added
- [ ] Proper Copyright + License headers added to applicable files - [ ] Proper Copyright + License headers added to applicable files (Generally, we stick to "The ZMK Contributors" for copyrights to help avoid churn when files get edited)
- [ ] General consistent formatting of DeviceTree files - [ ] General consistent formatting of DeviceTree files
- [ ] Keymaps do not use deprecated key defines (Check using the [upgrader tool](https://zmk.dev/docs/codes/keymap-upgrader)) - [ ] Keymaps do not use deprecated key defines (Check using the [upgrader tool](https://zmk.dev/docs/codes/keymap-upgrader))
- [ ] `&pro_micro` used in favor of `&pro_micro_d/a` if applicable - [ ] `&pro_micro` used in favor of `&pro_micro_d/a` if applicable

View file

@ -14,7 +14,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: zmkfirmware/zmk-build-arm:2.5 image: docker.io/zmkfirmware/zmk-build-arm:2.5
strategy: strategy:
matrix: matrix:
board: board:

View file

@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: DoozyX/clang-format-lint-action@v0.12 - uses: DoozyX/clang-format-lint-action@v0.13
with: with:
source: "./app" source: "./app"
extensions: "h,c" extensions: "h,c"

View file

@ -29,7 +29,7 @@ jobs:
validate-metadata: validate-metadata:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: zmkfirmware/zmk-dev-arm:2.5 image: docker.io/zmkfirmware/zmk-dev-arm:2.5
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install dependencies - name: Install dependencies

View file

@ -16,7 +16,7 @@ jobs:
integration_test: integration_test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: zmkfirmware/zmk-build-arm:2.5 image: docker.io/zmkfirmware/zmk-build-arm:2.5
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2

View file

@ -167,6 +167,7 @@ menuconfig ZMK_SPLIT_BLE_ROLE_CENTRAL
bool "Central" bool "Central"
select BT_CENTRAL select BT_CENTRAL
select BT_GATT_CLIENT select BT_GATT_CLIENT
select BT_GATT_AUTO_DISCOVER_CCC
if ZMK_SPLIT_BLE_ROLE_CENTRAL if ZMK_SPLIT_BLE_ROLE_CENTRAL
@ -262,36 +263,54 @@ config ZMK_RGB_UNDERGLOW_EXT_POWER
bool "RGB underglow toggling also controls external power" bool "RGB underglow toggling also controls external power"
default y default y
config ZMK_RGB_UNDERGLOW_BRT_MIN
int "RGB underglow minimum brightness in percent"
range 0 100
default 0
config ZMK_RGB_UNDERGLOW_BRT_MAX
int "RGB underglow maximum brightness in percent"
range ZMK_RGB_UNDERGLOW_BRT_MIN 100
default 100
config ZMK_RGB_UNDERGLOW_HUE_STEP config ZMK_RGB_UNDERGLOW_HUE_STEP
int "RGB underglow hue step in degrees of 360" int "RGB underglow hue step in degrees"
range 0 359
default 10 default 10
config ZMK_RGB_UNDERGLOW_SAT_STEP config ZMK_RGB_UNDERGLOW_SAT_STEP
int "RGB underglow sturation step in percent" int "RGB underglow saturation step in percent"
range 0 100
default 10 default 10
config ZMK_RGB_UNDERGLOW_BRT_STEP config ZMK_RGB_UNDERGLOW_BRT_STEP
int "RGB underglow brightness step in percent" int "RGB underglow brightness step in percent"
range 0 100
default 10 default 10
config ZMK_RGB_UNDERGLOW_HUE_START config ZMK_RGB_UNDERGLOW_HUE_START
int "RGB underglow start hue value from 0-359" int "RGB underglow start hue value in degrees"
range 0 359
default 0 default 0
config ZMK_RGB_UNDERGLOW_SAT_START config ZMK_RGB_UNDERGLOW_SAT_START
int "RGB underglow start saturations value from 0-100" int "RGB underglow start saturations value in percent"
range 0 100
default 100 default 100
config ZMK_RGB_UNDERGLOW_BRT_START config ZMK_RGB_UNDERGLOW_BRT_START
int "RGB underglow start brightness value from 0-100" int "RGB underglow start brightness value in percent"
default 100 range ZMK_RGB_UNDERGLOW_BRT_MIN ZMK_RGB_UNDERGLOW_BRT_MAX
default ZMK_RGB_UNDERGLOW_BRT_MAX
config ZMK_RGB_UNDERGLOW_SPD_START config ZMK_RGB_UNDERGLOW_SPD_START
int "RGB underglow start animation speed value from 1-5" int "RGB underglow start animation speed value"
range 1 5
default 3 default 3
config ZMK_RGB_UNDERGLOW_EFF_START config ZMK_RGB_UNDERGLOW_EFF_START
int "RGB underglow start effect int value related to the effect enum list" int "RGB underglow start effect int value related to the effect enum list"
range 0 3
default 0 default 0
config ZMK_RGB_UNDERGLOW_ON_START config ZMK_RGB_UNDERGLOW_ON_START

View file

@ -0,0 +1,14 @@
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands
COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/../tools/uf2/utils/uf2conv.py
-c
-b 0x26000
-f 0xADA52840
-o ${PROJECT_BINARY_DIR}/${CONFIG_KERNEL_BIN_NAME}.uf2
${PROJECT_BINARY_DIR}/${CONFIG_KERNEL_BIN_NAME}.bin
)
if(CONFIG_PINMUX)
zephyr_library()
zephyr_library_sources(pinmux.c)
zephyr_library_include_directories(${ZEPHYR_BASE}/drivers)
endif()

View file

@ -0,0 +1,29 @@
config BOARD_ENABLE_DCDC
bool "Enable DCDC mode"
select SOC_DCDC_NRF52X
default y
depends on (BOARD_MIKOTO_520)
choice BOARD_MIKOTO_CHARGER_CURRENT
prompt "Charge current to supply to attached batteries"
depends on (BOARD_MIKOTO_520)
config BOARD_MIKOTO_CHARGER_CURRENT_40MA
bool "40mA charge current, for battery capacity 40mAh or higher"
config BOARD_MIKOTO_CHARGER_CURRENT_100MA
bool "100mA charge current, for battery capacity 100mAh or higher"
config BOARD_MIKOTO_CHARGER_CURRENT_150MA
bool "150mA charge current, for battery capacity 150mAh or higher"
config BOARD_MIKOTO_CHARGER_CURRENT_250MA
bool "250mA charge current, for battery capacity 250mAh or higher"
config BOARD_MIKOTO_CHARGER_CURRENT_350MA
bool "350mA charge current, for battery capacity 350mAh or higher"
config BOARD_MIKOTO_CHARGER_CURRENT_NONE
bool "Disable charge current"
endchoice

View file

@ -0,0 +1,8 @@
# mikoto board configuration
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
config BOARD_MIKOTO_520
bool "mikoto_520"
depends on SOC_NRF52840_QIAA

View file

@ -0,0 +1,40 @@
# Electronut Labs Papyr board configuration
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
if BOARD_MIKOTO_520
config BOARD
default "mikoto"
if USB
config USB_NRFX
default y
config USB_DEVICE_STACK
default y
endif # USB
config BT_CTLR
default BT
config ZMK_BLE
default y
config ZMK_USB
default y
config PINMUX
default y
choice BOARD_MIKOTO_CHARGER_CURRENT
default BOARD_MIKOTO_CHARGER_CURRENT_100MA
endchoice
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
endif # BOARD_MIKOTO_520

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
/ {
pro_micro: connector {
compatible = "arduino-pro-micro";
#gpio-cells = <2>;
gpio-map-mask = <0xffffffff 0xffffffc0>;
gpio-map-pass-thru = <0 0x3f>;
gpio-map
= <0 0 &gpio0 4 0> /* D0 */
, <1 0 &gpio0 8 0> /* D1 */
, <2 0 &gpio0 17 0> /* D2 */
, <3 0 &gpio0 20 0> /* D3 */
, <4 0 &gpio0 22 0> /* D4/A6 */
, <5 0 &gpio0 24 0> /* D5 */
, <6 0 &gpio1 0 0> /* D6/A7 */
, <7 0 &gpio1 2 0> /* D7 */
, <8 0 &gpio1 4 0> /* D8/A8 */
, <9 0 &gpio1 6 0> /* D9/A9 */
, <10 0 &gpio0 9 0> /* D10/A10 */
, <16 0 &gpio0 10 0> /* D16 */
, <14 0 &gpio1 13 0> /* D14 */
, <15 0 &gpio0 2 0> /* D15 */
, <18 0 &gpio0 29 0> /* D18/A0 */
, <19 0 &gpio0 31 0> /* D19/A1 */
, <20 0 &gpio0 25 0> /* D20/A2 */
, <21 0 &gpio0 11 0> /* D21/A3 */
;
};
pro_micro_a: connector_a {
compatible = "arduino-pro-micro";
#gpio-cells = <2>;
gpio-map-mask = <0xffffffff 0xffffffc0>;
gpio-map-pass-thru = <0 0x3f>;
gpio-map
= <0 0 &gpio0 29 0> /* D18/A0 */
, <1 0 &gpio0 31 0> /* D19/A1 */
, <2 0 &gpio0 25 0> /* D20/A2 */
, <3 0 &gpio0 11 0> /* D21/A3 */
, <6 0 &gpio0 22 0> /* D4/A6 */
, <7 0 &gpio1 0 0> /* D6/A7 */
, <8 0 &gpio1 4 0> /* D8/A8 */
, <9 0 &gpio1 6 0> /* D9/A9 */
, <10 0 &gpio0 9 0> /* D10/A10 */
;
};
};
pro_micro_d: &pro_micro {};
pro_micro_i2c: &i2c0 {};
pro_micro_spi: &spi0 {};
pro_micro_serial: &uart0 {};

View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: MIT
board_runner_args(nrfjprog "--nrf-family=NRF52" "--softreset")
include(${ZEPHYR_BASE}/boards/common/blackmagicprobe.board.cmake)
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
/dts-v1/;
#include <nordic/nrf52840_qiaa.dtsi>
#include "arduino_pro_micro_pins.dtsi"
/ {
model = "mikoto";
compatible = "zhiayang,mikoto";
chosen {
zephyr,code-partition = &code_partition;
zephyr,sram = &sram0;
zephyr,flash = &flash0;
};
leds {
compatible = "gpio-leds";
blue_led: led_0 {
gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
label = "Blue LED";
};
};
ext-power {
compatible = "zmk,ext-power-generic";
label = "EXT_POWER";
control-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
};
vbatt {
compatible = "zmk,battery-voltage-divider";
label = "BATTERY";
io-channels = <&adc 2>;
output-ohms = <10000000>;
full-ohms = <(10000000 + 4000000)>;
};
};
&adc {
status = "okay";
};
&gpiote {
status = "okay";
};
&gpio0 {
status = "okay";
};
&gpio1 {
status = "okay";
};
&i2c0 {
compatible = "nordic,nrf-twi";
sda-pin = <17>;
scl-pin = <20>;
};
&uart0 {
compatible = "nordic,nrf-uarte";
tx-pin = <8>;
rx-pin = <4>;
};
&usbd {
status = "okay";
};
&flash0 {
/*
* For more information, see:
* http://docs.zephyrproject.org/latest/devices/dts/flash_partitions.html
*/
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
sd_partition: partition@0 {
label = "softdevice";
reg = <0x00000000 0x00026000>;
};
code_partition: partition@26000 {
label = "code_partition";
reg = <0x00026000 0x000c6000>;
};
/*
* The flash starting at 0x000ec000 and ending at
* 0x000f3fff is reserved for use by the application.
*/
/*
* Storage partition will be used by FCB/LittleFS/NVS
* if enabled.
*/
storage_partition: partition@ec000 {
label = "storage";
reg = <0x000ec000 0x00008000>;
};
boot_partition: partition@f4000 {
label = "adafruit_boot";
reg = <0x000f4000 0x0000c000>;
};
};
};

View file

@ -0,0 +1,15 @@
identifier: mikoto_520
name: mikoto_520
type: mcu
arch: arm
toolchain:
- zephyr
- gnuarmemb
- xtools
supported:
- adc
- usb_device
- ble
- ieee802154
- pwm
- watchdog

View file

@ -0,0 +1,10 @@
file_format: "1"
id: mikoto_520
name: Mikoto 5.20
type: board
arch: arm
outputs:
- usb
- ble
url: https://github.com/zhiayang/mikoto
exposes: [pro_micro]

View file

@ -0,0 +1,20 @@
# SPDX-License-Identifier: MIT
CONFIG_SOC_SERIES_NRF52X=y
CONFIG_SOC_NRF52840_QIAA=y
CONFIG_BOARD_MIKOTO_520=y
# Enable MPU
CONFIG_ARM_MPU=y
# enable GPIO
CONFIG_GPIO=y
CONFIG_USE_DT_CODE_PARTITION=y
CONFIG_MPU_ALLOW_FLASH_WRITE=y
CONFIG_NVS=y
CONFIG_SETTINGS_NVS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <kernel.h>
#include <device.h>
#include <init.h>
#include <drivers/gpio.h>
#include <sys/sys_io.h>
#include <devicetree.h>
static int pinmux_mikoto_init(const struct device *port) {
ARG_UNUSED(port);
#if CONFIG_BOARD_MIKOTO_520
const struct device *p0 = device_get_binding("GPIO_0");
const struct device *p1 = device_get_binding("GPIO_1");
#if CONFIG_BOARD_MIKOTO_CHARGER_CURRENT_40MA
gpio_pin_configure(p0, 26, GPIO_INPUT | GPIO_PULL_DOWN);
gpio_pin_configure(p1, 15, GPIO_INPUT);
#elif CONFIG_BOARD_MIKOTO_CHARGER_CURRENT_100MA
gpio_pin_configure(p0, 26, GPIO_OUTPUT);
gpio_pin_set(p0, 26, 0);
gpio_pin_configure(p1, 15, GPIO_INPUT);
#elif CONFIG_BOARD_MIKOTO_CHARGER_CURRENT_150MA
gpio_pin_configure(p0, 26, GPIO_OUTPUT);
gpio_pin_set(p0, 26, 0);
gpio_pin_configure(p1, 15, GPIO_INPUT | GPIO_PULL_DOWN);
#elif CONFIG_BOARD_MIKOTO_CHARGER_CURRENT_250MA
gpio_pin_configure(p0, 26, GPIO_INPUT);
gpio_pin_configure(p1, 15, GPIO_OUTPUT);
gpio_pin_set(p1, 15, 0);
#elif CONFIG_BOARD_MIKOTO_CHARGER_CURRENT_350MA
gpio_pin_configure(p0, 26, GPIO_OUTPUT);
gpio_pin_set(p0, 26, 0);
gpio_pin_configure(p1, 15, GPIO_OUTPUT);
gpio_pin_set(p1, 15, 0);
#elif CONFIG_BOARD_MIKOTO_CHARGER_CURRENT_NONE
gpio_pin_configure(p0, 26, GPIO_INPUT);
gpio_pin_configure(p1, 15, GPIO_INPUT);
#endif
#endif
return 0;
}
SYS_INIT(pinmux_mikoto_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);

View file

@ -0,0 +1,19 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
if SHIELD_ERGODASH_LEFT
config ZMK_KEYBOARD_NAME
default "Ergodash"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
default y
endif
if SHIELD_ERGODASH_LEFT || SHIELD_ERGODASH_RIGHT
config ZMK_SPLIT
default y
endif

View file

@ -0,0 +1,8 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
config SHIELD_ERGODASH_LEFT
def_bool $(shields_list_contains,ergodash_left)
config SHIELD_ERGODASH_RIGHT
def_bool $(shields_list_contains,ergodash_right)

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <dt-bindings/zmk/matrix_transform.h>
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix_transform = &default_transform;
};
default_transform: keymap_transform_0 {
compatible = "zmk,matrix-transform";
columns = <14>;
rows = <5>;
// Numbering based on rev 1.2 schema
// * keys that can be in different positions are denoted as MW
// * MW40 can be broken off
// | SW1 | SW5 | SW9 | SW13 | SW17 | SW21 | SW25 | | | | SW25 | SW21 | SW17 | SW13 | SW9 | SW5 | SW1 |
// | SW2 | SW6 | SW10 | SW14 | SW18 | SW22 | SW26 | | | | SW26 | SW22 | SW18 | SW14 | SW10 | SW6 | SW2 |
// | SW3 | SW7 | SW11 | SW15 | SW19 | SW23 | SW27 | | | | SW27 | SW23 | SW19 | SW15 | SW11 | SW7 | SW3 |
// | SW4 | SW8 | SW12 | SW16 | SW20 | SW24 | | MW28 | | MW28 | | SW24 | SW20 | SW16 | SW12 | SW8 | SW4 |
// | SW30 | SW31 | SW32 | MW33 | SW34 | | MW35 | MW40 | | MW40 | MW35 | | SW34 | MW33 | SW32 | SW31 | SW30 |
map = <
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,13) RC(0,12) RC(0,11) RC(0,10) RC(0,9) RC(0,8) RC(0,7)
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,13) RC(1,12) RC(1,11) RC(1,10) RC(1,9) RC(1,8) RC(1,7)
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,13) RC(2,12) RC(2,11) RC(2,10) RC(2,9) RC(2,8) RC(2,7)
RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,13) RC(3,12) RC(3,11) RC(3,10) RC(3,9) RC(3,8) RC(3,7)
RC(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,4) RC(4,5) RC(4,6) RC(4,13) RC(4,12) RC(4,11) RC(4,10) RC(4,9) RC(4,8) RC(4,7)
>;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
label = "KSCAN";
diode-direction = "col2row";
row-gpios
= <&pro_micro 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
;
col-gpios
= <&pro_micro 21 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 20 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 18 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
;
};
};

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h>
#define DEFAULT 0
#define LOWER 1
#define RAISE 2
/ {
keymap {
compatible = "zmk,keymap";
default_layer {
/* QWERTY
* .----------------------------------------------------------------------------------------------------------------------.
* | ` | 1 | 2 | 3 | 4 | 5 | [ | | ] | 6 | 7 | 8 | 9 | 0 | PScr |
* |------+------+------+------+------+------+------+--------------------+------+------+------+------+------+------+------|
* | ESC | Q | W | E | R | T | - | | = | Y | U | I | O | P | \ |
* |------+------+------+------+------+------+------+--------------------+------+------+------+------+------+------+------|
* | Tab | A | S | D | F | G | Del | | Bksp | H | J | K | L | ; | ' |
* |------+------+------+------+------+------+---------------------------+------+------+------+------+------+------+------|
* | Shift| Z | X | C | V | B | End | | Home | N | M | , | . | / | Shift|
* |-------------+------+------+------+------+------+------+------+------+------+------+------+------+------+-------------|
* | Ctrl | | PGDN | Win |||||||| Alt | Space| Lower|||||||| Raise| Enter| RAlt |||||||| | PGUP | Ins | RCtrl|
* .----------------------------------------------------------------------------------------------------------------------.
*/
bindings = <
&kp GRAVE &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp LBKT &kp RBKT &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp PSCRN
&kp ESC &kp Q &kp W &kp E &kp R &kp T &kp MINUS &kp EQUAL &kp Y &kp U &kp I &kp O &kp P &kp BSLH
&kp TAB &kp A &kp S &kp D &kp F &kp G &kp DEL &kp BSPC &kp H &kp J &kp K &kp L &kp SEMI &kp SQT
&kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp END &kp HOME &kp N &kp M &kp COMMA &kp DOT &kp SLASH &kp RSHFT
&kp LCTRL &none &kp PG_DN &kp LMETA &kp LALT &kp SPACE &mo LOWER &mo RAISE &kp RET &kp RALT &none &kp PG_UP &kp INS &kp RCTRL
>;
};
lower_layer {
/* .----------------------------------------------------------------------------------------------------------------------.
* | F11 | F1 | F2 | F3 | F4 | F5 | | | | F6 | F7 | F8 | F9 | F10 | F12 |
* |------+------+------+------+------+------+------+--------------------+------+------+------+------+------+------+------|
* | | | | | | | | | | | | | | | |
* |------+------+------+------+------+------+------+--------------------+------+------+------+------+------+------+------|
* | | | | | | | | | | | | | | | |
* |------+------+------+------+------+------+---------------------------+------+------+------+------+------+------+------|
* | Shift| Boot | Reset| | | | | | | | | | | | Shift|
* |-------------+------+------+------+------+------+------+------+------+------+------+------+------+------+-------------|
* | Ctlr | | | Win |||||||| Alt | | Lower|||||||| Raise| | RAlt |||||||| | | | RCtrl|
* .----------------------------------------------------------------------------------------------------------------------.
*/ /* FIXME boot and reset are not yet locale aware */
bindings = <
&kp F11 &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp &none &kp &none &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F12
&none &none &none &none &none &none &none &none &none &none &none &none &none &none
&none &none &none &none &none &none &none &none &none &none &none &none &none &none
&trans &bootloader &reset &none &none &none &none &none &none &none &none &none &none &trans
&trans &none &none &trans &trans &none &trans &trans &none &trans &none &none &none &trans
>;
};
raise_layer {
/* .----------------------------------------------------------------------------------------------------------------------.
* | | BT 0 | BT 1 | BT 2 | BT 3 | | BTCL | | | | | | | | |
* |------+------+------+------+------+------+------+--------------------+------+------+------+------+------+------+------|
* | | | | | | | | | | | | | | | |
* |------+------+------+------+------+------+------+--------------------+------+------+------+------+------+------+------|
* | | | | | | | | | | | | | | | |
* |------+------+------+------+------+------+---------------------------+------+------+------+------+------+------+------|
* | Shift| | | | | | | | | | | | Boot | Reset| Shift|
* |-------------+------+------+------+------+------+------+------+------+------+------+------+------+------+-------------|
* | Ctlr | | | Win |||||||| Alt | | Lower|||||||| Raise| | RAlt |||||||| | | | RCtrl|
* .----------------------------------------------------------------------------------------------------------------------.
*/ /* FIXME boot and reset are not yet locale aware */
bindings = <
&none &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &none &bt BT_CLR &none &none &none &none &none &none &none
&none &none &none &none &none &none &none &none &none &none &none &none &none &none
&none &none &none &none &none &none &none &none &none &none &none &none &none &none
&trans &none &none &none &none &none &none &none &none &none &none &bootloader &reset &trans
&trans &none &none &trans &trans &none &trans &trans &none &trans &none &none &none &trans
>;
};
};
};

View file

@ -0,0 +1,11 @@
file_format: "1"
id: ergodash
name: Ergodash
type: shield
url: https://github.com/omkbd/ErgoDash
requires: [pro_micro]
features:
- keys
siblings:
- ergodash_left
- ergodash_right

View file

@ -0,0 +1,8 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include "ergodash.dtsi"

View file

@ -0,0 +1,11 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include "ergodash.dtsi"
&default_transform {
col-offset = <7>;
};

View file

@ -35,8 +35,8 @@
// | | | RET | ADJ | | // | | | RET | ADJ | |
bindings = < bindings = <
&trans &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp N8 &kp LPAR &kp RPAR &kp DEL &trans &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp N8 &kp LPAR &kp RPAR &kp DEL
&trans &kp MINUS &kp KP_PLUS &kp LBRC &kp RBRC &kp PIPE &kp LEFT &kp DOWN &kp UP &kp RIGHT &kp GRAVE &kp GRAVE &trans &kp MINUS &kp KP_PLUS &kp LBRC &kp RBRC &kp PIPE &kp LEFT &kp DOWN &kp UP &kp RIGHT &kp GRAVE &kp TILDE
&trans &kp ESC &kp LGUI &kp LALT &kp CLCK &kp SQT &kp HOME &kp END &kp PG_UP &kp PG_DN &kp PSCRN &mt RSHFT RET &trans &kp ESC &kp LGUI &kp LALT &kp CLCK &kp DQT &kp HOME &kp END &kp PG_UP &kp PG_DN &kp PSCRN &mt RSHFT RET
&trans &trans &kp RET &mo 3 &trans &trans &trans &kp RET &mo 3 &trans
>; >;
}; };
@ -50,7 +50,7 @@
bindings = < bindings = <
&trans &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp DEL &trans &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp DEL
&trans &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp BSLH &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &trans &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp BSLH &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6
&trans &kp ESC &kp LGUI &kp RALT &kp CLCK &kp SQT &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &trans &kp ESC &kp LGUI &kp RALT &kp CLCK &kp DQT &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12
&trans &mo 3 &kp BSPC &trans &trans &trans &mo 3 &kp BSPC &trans &trans
>; >;
}; };
@ -58,7 +58,7 @@
adjust_layer { adjust_layer {
// ----------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------
// | RGB BRI+ | RGB SAT+ | RGB HUE+ | RGB ANI+ | | RGB TOG | | BT1 | BT2 | BT3 | BT4 | BT5 | BT CLR | // | RGB BRI+ | RGB SAT+ | RGB HUE+ | RGB ANI+ | | RGB TOG | | BT1 | BT2 | BT3 | BT4 | BT5 | BT CLR |
// | RGB BRI- | RGB SAT- | RGB HUE- | RGB ANI+ | | | | | | | | | | // | RGB BRI- | RGB SAT- | RGB HUE- | RGB ANI- | | | | | | | | | |
// | | | | | | | | RESET | | | | | | // | | | | | | | | RESET | | | | | |
// | | | | | | // | | | | | |
bindings = < bindings = <

View file

@ -61,14 +61,17 @@ foreach(root ${BOARD_ROOT})
if (EXISTS "${root}/boards/${BOARD}.overlay") if (EXISTS "${root}/boards/${BOARD}.overlay")
list(APPEND ZMK_DTC_FILES "${root}/boards/${BOARD}.overlay") list(APPEND ZMK_DTC_FILES "${root}/boards/${BOARD}.overlay")
endif() endif()
if (NOT DEFINED BOARD_DIR_NAME)
find_path(BOARD_DIR find_path(BOARD_DIR
NAMES ${BOARD}_defconfig NAMES ${BOARD}_defconfig
PATHS ${root}/boards/*/* PATHS ${root}/boards/*/*
NO_DEFAULT_PATH NO_DEFAULT_PATH
) )
if(BOARD_DIR) if(BOARD_DIR)
get_filename_component(BOARD_DIR_NAME ${BOARD_DIR} NAME)
list(APPEND KEYMAP_DIRS ${BOARD_DIR}) list(APPEND KEYMAP_DIRS ${BOARD_DIR})
endif() endif()
endif()
if(DEFINED SHIELD) if(DEFINED SHIELD)
find_path(shields_refs_list find_path(shields_refs_list
@ -130,7 +133,7 @@ endif()
if(NOT KEYMAP_FILE) if(NOT KEYMAP_FILE)
foreach(keymap_dir ${KEYMAP_DIRS}) foreach(keymap_dir ${KEYMAP_DIRS})
foreach(keymap_prefix ${SHIELD} ${SHIELD_DIR} ${BOARD} ${BOARD_DIR}) foreach(keymap_prefix ${SHIELD} ${SHIELD_DIR} ${BOARD} ${BOARD_DIR_NAME})
if (EXISTS ${keymap_dir}/${keymap_prefix}.keymap) if (EXISTS ${keymap_dir}/${keymap_prefix}.keymap)
set(KEYMAP_FILE "${keymap_dir}/${keymap_prefix}.keymap" CACHE STRING "Selected keymap file") set(KEYMAP_FILE "${keymap_dir}/${keymap_prefix}.keymap" CACHE STRING "Selected keymap file")
message(STATUS "Using keymap file: ${KEYMAP_FILE}") message(STATUS "Using keymap file: ${KEYMAP_FILE}")

View file

@ -4,6 +4,7 @@
zephyr_library_named(zmk__drivers__kscan) zephyr_library_named(zmk__drivers__kscan)
zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER debounce.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER kscan_gpio_matrix.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER kscan_gpio_matrix.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER kscan_gpio_direct.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER kscan_gpio_direct.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER kscan_gpio_demux.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER kscan_gpio_demux.c)

View file

@ -14,6 +14,24 @@ config ZMK_KSCAN_MATRIX_POLLING
config ZMK_KSCAN_DIRECT_POLLING config ZMK_KSCAN_DIRECT_POLLING
bool "Poll for key event triggers instead of using interrupts on direct wired boards." bool "Poll for key event triggers instead of using interrupts on direct wired boards."
config ZMK_KSCAN_DEBOUNCE_PRESS_MS
int "Debounce time for key press in milliseconds."
default -1
help
Global debounce time for key press in milliseconds.
If this is -1, the debounce time is controlled by the debounce-press-ms
Devicetree property, which defaults to 5 ms. Otherwise this overrides the
debounce time for all key scan drivers to the chosen value.
config ZMK_KSCAN_DEBOUNCE_RELEASE_MS
int "Debounce time for key release in milliseconds."
default -1
help
Global debounce time for key release in milliseconds.
If this is -1, the debounce time is controlled by the debounce-release-ms
Devicetree property, which defaults to 5 ms. Otherwise this overrides the
debounce time for all key scan drivers to the chosen value.
endif endif
config ZMK_KSCAN_INIT_PRIORITY config ZMK_KSCAN_INIT_PRIORITY

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include "debounce.h"
static uint32_t get_threshold(const struct debounce_state *state,
const struct debounce_config *config) {
return state->pressed ? config->debounce_release_ms : config->debounce_press_ms;
}
static void increment_counter(struct debounce_state *state, const int elapsed_ms) {
if (state->counter + elapsed_ms > DEBOUNCE_COUNTER_MAX) {
state->counter = DEBOUNCE_COUNTER_MAX;
} else {
state->counter += elapsed_ms;
}
}
static void decrement_counter(struct debounce_state *state, const int elapsed_ms) {
if (state->counter < elapsed_ms) {
state->counter = 0;
} else {
state->counter -= elapsed_ms;
}
}
void debounce_update(struct debounce_state *state, const bool active, const int elapsed_ms,
const struct debounce_config *config) {
// This uses a variation of the integrator debouncing described at
// https://www.kennethkuhn.com/electronics/debounce.c
// Every update where "active" does not match the current state, we increment
// a counter, otherwise we decrement it. When the counter reaches a
// threshold, the state flips and we reset the counter.
state->changed = false;
if (active == state->pressed) {
decrement_counter(state, elapsed_ms);
return;
}
const uint32_t flip_threshold = get_threshold(state, config);
if (state->counter < flip_threshold) {
increment_counter(state, elapsed_ms);
return;
}
state->pressed = !state->pressed;
state->counter = 0;
state->changed = true;
}
bool debounce_is_active(const struct debounce_state *state) {
return state->pressed || state->counter > 0;
}
bool debounce_is_pressed(const struct debounce_state *state) { return state->pressed; }
bool debounce_get_changed(const struct debounce_state *state) { return state->changed; }

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <sys/util.h>
#define DEBOUNCE_COUNTER_BITS 14
#define DEBOUNCE_COUNTER_MAX BIT_MASK(DEBOUNCE_COUNTER_BITS)
struct debounce_state {
bool pressed : 1;
bool changed : 1;
uint16_t counter : DEBOUNCE_COUNTER_BITS;
};
struct debounce_config {
/** Duration a switch must be pressed to latch as pressed. */
uint32_t debounce_press_ms;
/** Duration a switch must be released to latch as released. */
uint32_t debounce_release_ms;
};
/**
* Debounces one switch.
*
* @param state The state for the switch to debounce.
* @param active Is the switch currently pressed?
* @param elapsed_ms Time elapsed since the previous update in milliseconds.
* @param config Debounce settings.
*/
void debounce_update(struct debounce_state *state, const bool active, const int elapsed_ms,
const struct debounce_config *config);
/**
* @returns whether the switch is either latched as pressed or it is potentially
* pressed but the debouncer has not yet made a decision. If this returns true,
* the kscan driver should continue to poll quickly.
*/
bool debounce_is_active(const struct debounce_state *state);
/**
* @returns whether the switch is latched as pressed.
*/
bool debounce_is_pressed(const struct debounce_state *state);
/**
* @returns whether the pressed state of the switch changed in the last call to
* debounce_update.
*/
bool debounce_get_changed(const struct debounce_state *state);

View file

@ -4,10 +4,13 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include "debounce.h"
#include <device.h> #include <device.h>
#include <devicetree.h> #include <devicetree.h>
#include <drivers/gpio.h> #include <drivers/gpio.h>
#include <drivers/kscan.h> #include <drivers/kscan.h>
#include <kernel.h>
#include <logging/log.h> #include <logging/log.h>
#include <sys/__assert.h> #include <sys/__assert.h>
#include <sys/util.h> #include <sys/util.h>
@ -27,6 +30,20 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define INST_MATRIX_LEN(n) (INST_ROWS_LEN(n) * INST_COLS_LEN(n)) #define INST_MATRIX_LEN(n) (INST_ROWS_LEN(n) * INST_COLS_LEN(n))
#define INST_INPUTS_LEN(n) COND_DIODE_DIR(n, (INST_COLS_LEN(n)), (INST_ROWS_LEN(n))) #define INST_INPUTS_LEN(n) COND_DIODE_DIR(n, (INST_COLS_LEN(n)), (INST_ROWS_LEN(n)))
#if CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS >= 0
#define INST_DEBOUNCE_PRESS_MS(n) CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS
#else
#define INST_DEBOUNCE_PRESS_MS(n) \
DT_INST_PROP_OR(n, debounce_period, DT_INST_PROP(n, debounce_press_ms))
#endif
#if CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS >= 0
#define INST_DEBOUNCE_RELEASE_MS(n) CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS
#else
#define INST_DEBOUNCE_RELEASE_MS(n) \
DT_INST_PROP_OR(n, debounce_period, DT_INST_PROP(n, debounce_release_ms))
#endif
#define USE_POLLING IS_ENABLED(CONFIG_ZMK_KSCAN_MATRIX_POLLING) #define USE_POLLING IS_ENABLED(CONFIG_ZMK_KSCAN_MATRIX_POLLING)
#define USE_INTERRUPTS (!USE_POLLING) #define USE_INTERRUPTS (!USE_POLLING)
@ -66,26 +83,23 @@ enum kscan_diode_direction {
struct kscan_matrix_irq_callback { struct kscan_matrix_irq_callback {
const struct device *dev; const struct device *dev;
struct gpio_callback callback; struct gpio_callback callback;
struct k_delayed_work *work;
}; };
struct kscan_matrix_data { struct kscan_matrix_data {
const struct device *dev; const struct device *dev;
kscan_callback_t callback; kscan_callback_t callback;
struct k_delayed_work work; struct k_delayed_work work;
#if USE_POLLING #if USE_INTERRUPTS
struct k_timer poll_timer;
#else
/** Array of length config->inputs.len */ /** Array of length config->inputs.len */
struct kscan_matrix_irq_callback *irqs; struct kscan_matrix_irq_callback *irqs;
#endif #endif
/** Timestamp of the current or scheduled scan. */
int64_t scan_time;
/** /**
* Current state of the matrix as a flattened 2D array of length * Current state of the matrix as a flattened 2D array of length
* (config->rows.len * config->cols.len) * (config->rows.len * config->cols.len)
*/ */
bool *current_state; struct debounce_state *matrix_state;
/** Buffer for reading in the next matrix state. Parallel array to current_state. */
bool *next_state;
}; };
struct kscan_gpio_list { struct kscan_gpio_list {
@ -102,7 +116,8 @@ struct kscan_matrix_config {
struct kscan_gpio_list cols; struct kscan_gpio_list cols;
struct kscan_gpio_list inputs; struct kscan_gpio_list inputs;
struct kscan_gpio_list outputs; struct kscan_gpio_list outputs;
int32_t debounce_period_ms; struct debounce_config debounce_config;
int32_t debounce_scan_period_ms;
int32_t poll_period_ms; int32_t poll_period_ms;
enum kscan_diode_direction diode_direction; enum kscan_diode_direction diode_direction;
}; };
@ -190,19 +205,49 @@ static int kscan_matrix_interrupt_disable(const struct device *dev) {
#if USE_INTERRUPTS #if USE_INTERRUPTS
static void kscan_matrix_irq_callback_handler(const struct device *port, struct gpio_callback *cb, static void kscan_matrix_irq_callback_handler(const struct device *port, struct gpio_callback *cb,
const gpio_port_pins_t pin) { const gpio_port_pins_t pin) {
struct kscan_matrix_irq_callback *data = struct kscan_matrix_irq_callback *irq_data =
CONTAINER_OF(cb, struct kscan_matrix_irq_callback, callback); CONTAINER_OF(cb, struct kscan_matrix_irq_callback, callback);
const struct kscan_matrix_config *config = data->dev->config; struct kscan_matrix_data *data = irq_data->dev->data;
// Disable our interrupts temporarily to avoid re-entry while we scan. // Disable our interrupts temporarily to avoid re-entry while we scan.
kscan_matrix_interrupt_disable(data->dev); kscan_matrix_interrupt_disable(data->dev);
data->scan_time = k_uptime_get();
// TODO (Zephyr 2.6): use k_work_reschedule() // TODO (Zephyr 2.6): use k_work_reschedule()
k_delayed_work_cancel(data->work); k_delayed_work_cancel(&data->work);
k_delayed_work_submit(data->work, K_MSEC(config->debounce_period_ms)); k_delayed_work_submit(&data->work, K_NO_WAIT);
} }
#endif #endif
static void kscan_matrix_read_continue(const struct device *dev) {
const struct kscan_matrix_config *config = dev->config;
struct kscan_matrix_data *data = dev->data;
data->scan_time += config->debounce_scan_period_ms;
// TODO (Zephyr 2.6): use k_work_reschedule()
k_delayed_work_cancel(&data->work);
k_delayed_work_submit(&data->work, K_TIMEOUT_ABS_MS(data->scan_time));
}
static void kscan_matrix_read_end(const struct device *dev) {
#if USE_INTERRUPTS
// Return to waiting for an interrupt.
kscan_matrix_interrupt_enable(dev);
#else
struct kscan_matrix_data *data = dev->data;
const struct kscan_matrix_config *config = dev->config;
data->scan_time += config->poll_period_ms;
// Return to polling slowly.
// TODO (Zephyr 2.6): use k_work_reschedule()
k_delayed_work_cancel(&data->work);
k_delayed_work_submit(&data->work, K_TIMEOUT_ABS_MS(data->scan_time));
#endif
}
static int kscan_matrix_read(const struct device *dev) { static int kscan_matrix_read(const struct device *dev) {
struct kscan_matrix_data *data = dev->data; struct kscan_matrix_data *data = dev->data;
const struct kscan_matrix_config *config = dev->config; const struct kscan_matrix_config *config = dev->config;
@ -221,7 +266,10 @@ static int kscan_matrix_read(const struct device *dev) {
const struct kscan_gpio_dt_spec *in_gpio = &config->inputs.gpios[i]; const struct kscan_gpio_dt_spec *in_gpio = &config->inputs.gpios[i];
const int index = state_index_io(config, i, o); const int index = state_index_io(config, i, o);
data->next_state[index] = gpio_pin_get(in_gpio->port, in_gpio->pin); const bool active = gpio_pin_get(in_gpio->port, in_gpio->pin);
debounce_update(&data->matrix_state[index], active, config->debounce_scan_period_ms,
&config->debounce_config);
} }
err = gpio_pin_set(out_gpio->port, out_gpio->pin, 0); err = gpio_pin_set(out_gpio->port, out_gpio->pin, 0);
@ -232,50 +280,36 @@ static int kscan_matrix_read(const struct device *dev) {
} }
// Process the new state. // Process the new state.
#if USE_INTERRUPTS bool continue_scan = false;
bool submit_followup_read = false;
#endif
for (int r = 0; r < config->rows.len; r++) { for (int r = 0; r < config->rows.len; r++) {
for (int c = 0; c < config->cols.len; c++) { for (int c = 0; c < config->cols.len; c++) {
const int index = state_index_rc(config, r, c); const int index = state_index_rc(config, r, c);
const bool pressed = data->next_state[index]; struct debounce_state *state = &data->matrix_state[index];
if (debounce_get_changed(state)) {
const bool pressed = debounce_is_pressed(state);
// Follow up reads are needed if any key is pressed because further
// interrupts won't fire on already tripped GPIO pins.
#if USE_INTERRUPTS
submit_followup_read = submit_followup_read || pressed;
#endif
if (pressed != data->current_state[index]) {
LOG_DBG("Sending event at %i,%i state %s", r, c, pressed ? "on" : "off"); LOG_DBG("Sending event at %i,%i state %s", r, c, pressed ? "on" : "off");
data->current_state[index] = pressed;
data->callback(dev, r, c, pressed); data->callback(dev, r, c, pressed);
} }
continue_scan = continue_scan || debounce_is_active(state);
} }
} }
#if USE_INTERRUPTS if (continue_scan) {
if (submit_followup_read) { // At least one key is pressed or the debouncer has not yet decided if
// At least one key is pressed. Poll until everything is released. // it is pressed. Poll quickly until everything is released.
// TODO (Zephyr 2.6): use k_work_reschedule() kscan_matrix_read_continue(dev);
k_delayed_work_cancel(&data->work);
k_delayed_work_submit(&data->work, K_MSEC(config->debounce_period_ms));
} else { } else {
// All keys are released. Return to waiting for an interrupt. // All keys are released. Return to normal.
kscan_matrix_interrupt_enable(dev); kscan_matrix_read_end(dev);
} }
#endif
return 0; return 0;
} }
#if USE_POLLING
static void kscan_matrix_timer_handler(struct k_timer *timer) {
struct kscan_matrix_data *data = CONTAINER_OF(timer, struct kscan_matrix_data, poll_timer);
k_delayed_work_submit(&data->work, K_NO_WAIT);
}
#endif
static void kscan_matrix_work_handler(struct k_work *work) { static void kscan_matrix_work_handler(struct k_work *work) {
struct k_delayed_work *dwork = CONTAINER_OF(work, struct k_delayed_work, work); struct k_delayed_work *dwork = CONTAINER_OF(work, struct k_delayed_work, work);
struct kscan_matrix_data *data = CONTAINER_OF(dwork, struct kscan_matrix_data, work); struct kscan_matrix_data *data = CONTAINER_OF(dwork, struct kscan_matrix_data, work);
@ -294,27 +328,23 @@ static int kscan_matrix_configure(const struct device *dev, const kscan_callback
} }
static int kscan_matrix_enable(const struct device *dev) { static int kscan_matrix_enable(const struct device *dev) {
#if USE_POLLING
struct kscan_matrix_data *data = dev->data; struct kscan_matrix_data *data = dev->data;
const struct kscan_matrix_config *config = dev->config;
k_timer_start(&data->poll_timer, K_MSEC(config->poll_period_ms), data->scan_time = k_uptime_get();
K_MSEC(config->poll_period_ms));
return 0; // Read will automatically start interrupts/polling once done.
#else
// Read will automatically enable interrupts once done.
return kscan_matrix_read(dev); return kscan_matrix_read(dev);
#endif
} }
static int kscan_matrix_disable(const struct device *dev) { static int kscan_matrix_disable(const struct device *dev) {
#if USE_POLLING
struct kscan_matrix_data *data = dev->data; struct kscan_matrix_data *data = dev->data;
k_timer_stop(&data->poll_timer); k_delayed_work_cancel(&data->work);
return 0;
#else #if USE_INTERRUPTS
return kscan_matrix_interrupt_disable(dev); return kscan_matrix_interrupt_disable(dev);
#else
return 0;
#endif #endif
} }
@ -338,7 +368,6 @@ static int kscan_matrix_init_input_inst(const struct device *dev,
struct kscan_matrix_irq_callback *irq = &data->irqs[index]; struct kscan_matrix_irq_callback *irq = &data->irqs[index];
irq->dev = dev; irq->dev = dev;
irq->work = &data->work;
gpio_init_callback(&irq->callback, kscan_matrix_irq_callback_handler, BIT(gpio->pin)); gpio_init_callback(&irq->callback, kscan_matrix_irq_callback_handler, BIT(gpio->pin));
err = gpio_add_callback(gpio->port, &irq->callback); err = gpio_add_callback(gpio->port, &irq->callback);
if (err) { if (err) {
@ -407,10 +436,6 @@ static int kscan_matrix_init(const struct device *dev) {
k_delayed_work_init(&data->work, kscan_matrix_work_handler); k_delayed_work_init(&data->work, kscan_matrix_work_handler);
#if USE_POLLING
k_timer_init(&data->poll_timer, kscan_matrix_timer_handler, NULL);
#endif
return 0; return 0;
} }
@ -421,21 +446,24 @@ static const struct kscan_driver_api kscan_matrix_api = {
}; };
#define KSCAN_MATRIX_INIT(index) \ #define KSCAN_MATRIX_INIT(index) \
BUILD_ASSERT(INST_DEBOUNCE_PRESS_MS(index) <= DEBOUNCE_COUNTER_MAX, \
"ZMK_KSCAN_DEBOUNCE_PRESS_MS or debounce-press-ms is too large"); \
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(index) <= DEBOUNCE_COUNTER_MAX, \
"ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \
\
static const struct kscan_gpio_dt_spec kscan_matrix_rows_##index[] = { \ static const struct kscan_gpio_dt_spec kscan_matrix_rows_##index[] = { \
UTIL_LISTIFY(INST_ROWS_LEN(index), KSCAN_GPIO_ROW_CFG_INIT, index)}; \ UTIL_LISTIFY(INST_ROWS_LEN(index), KSCAN_GPIO_ROW_CFG_INIT, index)}; \
\ \
static const struct kscan_gpio_dt_spec kscan_matrix_cols_##index[] = { \ static const struct kscan_gpio_dt_spec kscan_matrix_cols_##index[] = { \
UTIL_LISTIFY(INST_COLS_LEN(index), KSCAN_GPIO_COL_CFG_INIT, index)}; \ UTIL_LISTIFY(INST_COLS_LEN(index), KSCAN_GPIO_COL_CFG_INIT, index)}; \
\ \
static bool kscan_current_state_##index[INST_MATRIX_LEN(index)]; \ static struct debounce_state kscan_matrix_state_##index[INST_MATRIX_LEN(index)]; \
static bool kscan_next_state_##index[INST_MATRIX_LEN(index)]; \
\ \
COND_INTERRUPTS((static struct kscan_matrix_irq_callback \ COND_INTERRUPTS((static struct kscan_matrix_irq_callback \
kscan_matrix_irqs_##index[INST_INPUTS_LEN(index)];)) \ kscan_matrix_irqs_##index[INST_INPUTS_LEN(index)];)) \
\ \
static struct kscan_matrix_data kscan_matrix_data_##index = { \ static struct kscan_matrix_data kscan_matrix_data_##index = { \
.current_state = kscan_current_state_##index, \ .matrix_state = kscan_matrix_state_##index, \
.next_state = kscan_next_state_##index, \
COND_INTERRUPTS((.irqs = kscan_matrix_irqs_##index, ))}; \ COND_INTERRUPTS((.irqs = kscan_matrix_irqs_##index, ))}; \
\ \
static struct kscan_matrix_config kscan_matrix_config_##index = { \ static struct kscan_matrix_config kscan_matrix_config_##index = { \
@ -445,7 +473,12 @@ static const struct kscan_driver_api kscan_matrix_api = {
COND_DIODE_DIR(index, (kscan_matrix_cols_##index), (kscan_matrix_rows_##index))), \ COND_DIODE_DIR(index, (kscan_matrix_cols_##index), (kscan_matrix_rows_##index))), \
.outputs = KSCAN_GPIO_LIST( \ .outputs = KSCAN_GPIO_LIST( \
COND_DIODE_DIR(index, (kscan_matrix_rows_##index), (kscan_matrix_cols_##index))), \ COND_DIODE_DIR(index, (kscan_matrix_rows_##index), (kscan_matrix_cols_##index))), \
.debounce_period_ms = DT_INST_PROP(index, debounce_period), \ .debounce_config = \
{ \
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(index), \
.debounce_release_ms = INST_DEBOUNCE_RELEASE_MS(index), \
}, \
.debounce_scan_period_ms = DT_INST_PROP(index, debounce_scan_period_ms), \
.poll_period_ms = DT_INST_PROP(index, poll_period_ms), \ .poll_period_ms = DT_INST_PROP(index, poll_period_ms), \
.diode_direction = INST_DIODE_DIR(index), \ .diode_direction = INST_DIODE_DIR(index), \
}; \ }; \

View file

@ -15,13 +15,26 @@ properties:
type: phandle-array type: phandle-array
required: true required: true
debounce-period: debounce-period:
type: int
required: false
deprecated: true
description: Deprecated. Use debounce-press-ms and debounce-release-ms instead.
debounce-press-ms:
type: int type: int
default: 5 default: 5
description: Debounce time in milliseconds description: Debounce time for key press in milliseconds. Use 0 for eager debouncing.
debounce-release-ms:
type: int
default: 5
description: Debounce time for key release in milliseconds.
debounce-scan-period-ms:
type: int
default: 1
description: Time between reads in milliseconds when any key is pressed.
poll-period-ms: poll-period-ms:
type: int type: int
default: 10 default: 10
description: Time between reads in milliseconds when polling is enabled description: Time between reads in milliseconds when no key is pressed and ZMK_KSCAN_MATRIX_POLLING is enabled.
diode-direction: diode-direction:
type: string type: string
default: row2col default: row2col

View file

@ -28,5 +28,10 @@ properties:
- "hold-preferred" - "hold-preferred"
- "balanced" - "balanced"
- "tap-preferred" - "tap-preferred"
- "tap-unless-interrupted"
retro-tap: retro-tap:
type: boolean type: boolean
hold-trigger-key-positions:
type: array
required: false
default: []

View file

@ -143,12 +143,14 @@ static const uint8_t zmk_hid_report_desc[] = {
/* LOGICAL_MINIMUM (0) */ /* LOGICAL_MINIMUM (0) */
HID_GI_LOGICAL_MIN(1), HID_GI_LOGICAL_MIN(1),
0x00, 0x00,
/* LOGICAL_MAXIMUM (0xFFFF) */ /* LOGICAL_MAXIMUM (0x00FF) - little endian, and requires two bytes because logical max is
HID_GI_LOGICAL_MAX(1), signed */
HID_GI_LOGICAL_MAX(2),
0xFF, 0xFF,
0x00,
HID_LI_USAGE_MIN(1), HID_LI_USAGE_MIN(1),
0x00, 0x00,
/* USAGE_MAXIMUM (0xFFFF) */ /* USAGE_MAXIMUM (0xFF) */
HID_LI_USAGE_MAX(1), HID_LI_USAGE_MAX(1),
0xFF, 0xFF,
/* INPUT (Data,Ary,Abs) */ /* INPUT (Data,Ary,Abs) */

View file

@ -34,6 +34,7 @@ enum flavor {
FLAVOR_HOLD_PREFERRED, FLAVOR_HOLD_PREFERRED,
FLAVOR_BALANCED, FLAVOR_BALANCED,
FLAVOR_TAP_PREFERRED, FLAVOR_TAP_PREFERRED,
FLAVOR_TAP_UNLESS_INTERRUPTED,
}; };
enum status { enum status {
@ -58,6 +59,8 @@ struct behavior_hold_tap_config {
int quick_tap_ms; int quick_tap_ms;
enum flavor flavor; enum flavor flavor;
bool retro_tap; bool retro_tap;
int32_t hold_trigger_key_positions_len;
int32_t hold_trigger_key_positions[];
}; };
// this data is specific for each hold-tap // this data is specific for each hold-tap
@ -70,6 +73,9 @@ struct active_hold_tap {
const struct behavior_hold_tap_config *config; const struct behavior_hold_tap_config *config;
struct k_delayed_work work; struct k_delayed_work work;
bool work_is_cancelled; bool work_is_cancelled;
// initialized to -1, which is to be interpreted as "no other key has been pressed yet"
int32_t position_of_first_other_key_pressed;
}; };
// The undecided hold tap is the hold tap that needs to be decided before // The undecided hold tap is the hold tap that needs to be decided before
@ -206,6 +212,7 @@ static struct active_hold_tap *store_hold_tap(uint32_t position, uint32_t param_
active_hold_taps[i].param_hold = param_hold; active_hold_taps[i].param_hold = param_hold;
active_hold_taps[i].param_tap = param_tap; active_hold_taps[i].param_tap = param_tap;
active_hold_taps[i].timestamp = timestamp; active_hold_taps[i].timestamp = timestamp;
active_hold_taps[i].position_of_first_other_key_pressed = -1;
return &active_hold_taps[i]; return &active_hold_taps[i];
} }
return NULL; return NULL;
@ -252,6 +259,26 @@ static void decide_tap_preferred(struct active_hold_tap *hold_tap, enum decision
} }
} }
static void decide_tap_unless_interrupted(struct active_hold_tap *hold_tap,
enum decision_moment event) {
switch (event) {
case HT_KEY_UP:
hold_tap->status = STATUS_TAP;
return;
case HT_OTHER_KEY_DOWN:
hold_tap->status = STATUS_HOLD_INTERRUPT;
return;
case HT_TIMER_EVENT:
hold_tap->status = STATUS_TAP;
return;
case HT_QUICK_TAP:
hold_tap->status = STATUS_TAP;
return;
default:
return;
}
}
static void decide_hold_preferred(struct active_hold_tap *hold_tap, enum decision_moment event) { static void decide_hold_preferred(struct active_hold_tap *hold_tap, enum decision_moment event) {
switch (event) { switch (event) {
case HT_KEY_UP: case HT_KEY_UP:
@ -279,6 +306,8 @@ static inline const char *flavor_str(enum flavor flavor) {
return "balanced"; return "balanced";
case FLAVOR_TAP_PREFERRED: case FLAVOR_TAP_PREFERRED:
return "tap-preferred"; return "tap-preferred";
case FLAVOR_TAP_UNLESS_INTERRUPTED:
return "tap-unless-interrupted";
default: default:
return "UNKNOWN FLAVOR"; return "UNKNOWN FLAVOR";
} }
@ -359,6 +388,39 @@ static int release_binding(struct active_hold_tap *hold_tap) {
return behavior_keymap_binding_released(&binding, event); return behavior_keymap_binding_released(&binding, event);
} }
static bool is_first_other_key_pressed_trigger_key(struct active_hold_tap *hold_tap) {
for (int i = 0; i < hold_tap->config->hold_trigger_key_positions_len; i++) {
if (hold_tap->config->hold_trigger_key_positions[i] ==
hold_tap->position_of_first_other_key_pressed) {
return true;
}
}
return false;
}
// Force a tap decision if the positional conditions for a hold decision are not met.
static void decide_positional_hold(struct active_hold_tap *hold_tap) {
// Only force a tap decision if the positional hold/tap feature is enabled.
if (!(hold_tap->config->hold_trigger_key_positions_len > 0)) {
return;
}
// Only force a tap decision if another key was pressed after
// the hold/tap key.
if (hold_tap->position_of_first_other_key_pressed == -1) {
return;
}
// Only force a tap decision if the first other key to be pressed
// (after the hold/tap key) is not one of the trigger keys.
if (is_first_other_key_pressed_trigger_key(hold_tap)) {
return;
}
// Since the positional key conditions have failed, force a TAP decision.
hold_tap->status = STATUS_TAP;
}
static void decide_hold_tap(struct active_hold_tap *hold_tap, static void decide_hold_tap(struct active_hold_tap *hold_tap,
enum decision_moment decision_moment) { enum decision_moment decision_moment) {
if (hold_tap->status != STATUS_UNDECIDED) { if (hold_tap->status != STATUS_UNDECIDED) {
@ -370,19 +432,30 @@ static void decide_hold_tap(struct active_hold_tap *hold_tap,
return; return;
} }
// If the hold-tap behavior is still undecided, attempt to decide it.
switch (hold_tap->config->flavor) { switch (hold_tap->config->flavor) {
case FLAVOR_HOLD_PREFERRED: case FLAVOR_HOLD_PREFERRED:
decide_hold_preferred(hold_tap, decision_moment); decide_hold_preferred(hold_tap, decision_moment);
break;
case FLAVOR_BALANCED: case FLAVOR_BALANCED:
decide_balanced(hold_tap, decision_moment); decide_balanced(hold_tap, decision_moment);
break;
case FLAVOR_TAP_PREFERRED: case FLAVOR_TAP_PREFERRED:
decide_tap_preferred(hold_tap, decision_moment); decide_tap_preferred(hold_tap, decision_moment);
break;
case FLAVOR_TAP_UNLESS_INTERRUPTED:
decide_tap_unless_interrupted(hold_tap, decision_moment);
break;
} }
if (hold_tap->status == STATUS_UNDECIDED) { if (hold_tap->status == STATUS_UNDECIDED) {
return; return;
} }
decide_positional_hold(hold_tap);
// Since the hold-tap has been decided, clean up undecided_hold_tap and
// execute the decided behavior.
LOG_DBG("%d decided %s (%s decision moment %s)", hold_tap->position, LOG_DBG("%d decided %s (%s decision moment %s)", hold_tap->position,
status_str(hold_tap->status), flavor_str(hold_tap->config->flavor), status_str(hold_tap->status), flavor_str(hold_tap->config->flavor),
decision_moment_str(decision_moment)); decision_moment_str(decision_moment));
@ -501,6 +574,14 @@ static int position_state_changed_listener(const zmk_event_t *eh) {
return ZMK_EV_EVENT_BUBBLE; return ZMK_EV_EVENT_BUBBLE;
} }
// Store the position of pressed key for positional hold-tap purposes.
if ((ev->state) // i.e. key pressed (not released)
&& (undecided_hold_tap->position_of_first_other_key_pressed ==
-1) // i.e. no other key has been pressed yet
) {
undecided_hold_tap->position_of_first_other_key_pressed = ev->position;
}
if (undecided_hold_tap->position == ev->position) { if (undecided_hold_tap->position == ev->position) {
if (ev->state) { // keydown if (ev->state) { // keydown
LOG_ERR("hold-tap listener should be called before before most other listeners!"); LOG_ERR("hold-tap listener should be called before before most other listeners!");
@ -604,6 +685,8 @@ static struct behavior_hold_tap_data behavior_hold_tap_data;
.quick_tap_ms = DT_INST_PROP(n, quick_tap_ms), \ .quick_tap_ms = DT_INST_PROP(n, quick_tap_ms), \
.flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \ .flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \
.retro_tap = DT_INST_PROP(n, retro_tap), \ .retro_tap = DT_INST_PROP(n, retro_tap), \
.hold_trigger_key_positions = DT_INST_PROP(n, hold_trigger_key_positions), \
.hold_trigger_key_positions_len = DT_INST_PROP_LEN(n, hold_trigger_key_positions), \
}; \ }; \
DEVICE_DT_INST_DEFINE(n, behavior_hold_tap_init, device_pm_control_nop, \ DEVICE_DT_INST_DEFINE(n, behavior_hold_tap_init, device_pm_control_nop, \
&behavior_hold_tap_data, &behavior_hold_tap_config_##n, APPLICATION, \ &behavior_hold_tap_data, &behavior_hold_tap_config_##n, APPLICATION, \

View file

@ -469,22 +469,12 @@ static int position_state_changed_listener(const zmk_event_t *ev) {
ZMK_LISTENER(combo, position_state_changed_listener); ZMK_LISTENER(combo, position_state_changed_listener);
ZMK_SUBSCRIPTION(combo, zmk_position_state_changed); ZMK_SUBSCRIPTION(combo, zmk_position_state_changed);
// todo: remove this once #506 is merged and #include <zmk/keymap.h>
#define KEY_BINDING_TO_STRUCT(idx, drv_inst) \
{ \
.behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(drv_inst, bindings, idx)), \
.param1 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(drv_inst, bindings, idx, param1), (0), \
(DT_PHA_BY_IDX(drv_inst, bindings, idx, param1))), \
.param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(drv_inst, bindings, idx, param2), (0), \
(DT_PHA_BY_IDX(drv_inst, bindings, idx, param2))), \
}
#define COMBO_INST(n) \ #define COMBO_INST(n) \
static struct combo_cfg combo_config_##n = { \ static struct combo_cfg combo_config_##n = { \
.timeout_ms = DT_PROP(n, timeout_ms), \ .timeout_ms = DT_PROP(n, timeout_ms), \
.key_positions = DT_PROP(n, key_positions), \ .key_positions = DT_PROP(n, key_positions), \
.key_position_len = DT_PROP_LEN(n, key_positions), \ .key_position_len = DT_PROP_LEN(n, key_positions), \
.behavior = KEY_BINDING_TO_STRUCT(0, n), \ .behavior = ZMK_KEYMAP_EXTRACT_BINDING(0, n), \
.virtual_key_position = ZMK_KEYMAP_LEN + __COUNTER__, \ .virtual_key_position = ZMK_KEYMAP_LEN + __COUNTER__, \
.slow_release = DT_PROP(n, slow_release), \ .slow_release = DT_PROP(n, slow_release), \
.layers = DT_PROP(n, layers), \ .layers = DT_PROP(n, layers), \

View file

@ -39,7 +39,7 @@ static struct output_status_state get_state(const zmk_event_t *_eh) {
} }
static void set_status_symbol(lv_obj_t *label, struct output_status_state state) { static void set_status_symbol(lv_obj_t *label, struct output_status_state state) {
char text[6] = {}; char text[9] = {};
switch (state.selected_endpoint) { switch (state.selected_endpoint) {
case ZMK_ENDPOINT_USB: case ZMK_ENDPOINT_USB:

View file

@ -28,6 +28,9 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define SAT_MAX 100 #define SAT_MAX 100
#define BRT_MAX 100 #define BRT_MAX 100
BUILD_ASSERT(CONFIG_ZMK_RGB_UNDERGLOW_BRT_MIN <= CONFIG_ZMK_RGB_UNDERGLOW_BRT_MAX,
"ERROR: RGB underglow maximum brightness is less than minimum brightness");
enum rgb_underglow_effect { enum rgb_underglow_effect {
UNDERGLOW_EFFECT_SOLID, UNDERGLOW_EFFECT_SOLID,
UNDERGLOW_EFFECT_BREATHE, UNDERGLOW_EFFECT_BREATHE,
@ -54,6 +57,17 @@ static struct rgb_underglow_state state;
static const struct device *ext_power; static const struct device *ext_power;
#endif #endif
static struct zmk_led_hsb hsb_scale_min_max(struct zmk_led_hsb hsb) {
hsb.b = CONFIG_ZMK_RGB_UNDERGLOW_BRT_MIN +
(CONFIG_ZMK_RGB_UNDERGLOW_BRT_MAX - CONFIG_ZMK_RGB_UNDERGLOW_BRT_MIN) * hsb.b / BRT_MAX;
return hsb;
}
static struct zmk_led_hsb hsb_scale_zero_max(struct zmk_led_hsb hsb) {
hsb.b = hsb.b * CONFIG_ZMK_RGB_UNDERGLOW_BRT_MAX / BRT_MAX;
return hsb;
}
static struct led_rgb hsb_to_rgb(struct zmk_led_hsb hsb) { static struct led_rgb hsb_to_rgb(struct zmk_led_hsb hsb) {
double r, g, b; double r, g, b;
@ -105,7 +119,7 @@ static struct led_rgb hsb_to_rgb(struct zmk_led_hsb hsb) {
static void zmk_rgb_underglow_effect_solid() { static void zmk_rgb_underglow_effect_solid() {
for (int i = 0; i < STRIP_NUM_PIXELS; i++) { for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
pixels[i] = hsb_to_rgb(state.color); pixels[i] = hsb_to_rgb(hsb_scale_min_max(state.color));
} }
} }
@ -114,7 +128,7 @@ static void zmk_rgb_underglow_effect_breathe() {
struct zmk_led_hsb hsb = state.color; struct zmk_led_hsb hsb = state.color;
hsb.b = abs(state.animation_step - 1200) / 12; hsb.b = abs(state.animation_step - 1200) / 12;
pixels[i] = hsb_to_rgb(hsb); pixels[i] = hsb_to_rgb(hsb_scale_zero_max(hsb));
} }
state.animation_step += state.animation_speed * 10; state.animation_step += state.animation_speed * 10;
@ -129,7 +143,7 @@ static void zmk_rgb_underglow_effect_spectrum() {
struct zmk_led_hsb hsb = state.color; struct zmk_led_hsb hsb = state.color;
hsb.h = state.animation_step; hsb.h = state.animation_step;
pixels[i] = hsb_to_rgb(hsb); pixels[i] = hsb_to_rgb(hsb_scale_min_max(hsb));
} }
state.animation_step += state.animation_speed; state.animation_step += state.animation_speed;
@ -141,7 +155,7 @@ static void zmk_rgb_underglow_effect_swirl() {
struct zmk_led_hsb hsb = state.color; struct zmk_led_hsb hsb = state.color;
hsb.h = (HUE_MAX / STRIP_NUM_PIXELS * i + state.animation_step) % HUE_MAX; hsb.h = (HUE_MAX / STRIP_NUM_PIXELS * i + state.animation_step) % HUE_MAX;
pixels[i] = hsb_to_rgb(hsb); pixels[i] = hsb_to_rgb(hsb_scale_min_max(hsb));
} }
state.animation_step += state.animation_speed * 2; state.animation_step += state.animation_speed * 2;
@ -371,12 +385,7 @@ struct zmk_led_hsb zmk_rgb_underglow_calc_brt(int direction) {
struct zmk_led_hsb color = state.color; struct zmk_led_hsb color = state.color;
int b = color.b + (direction * CONFIG_ZMK_RGB_UNDERGLOW_BRT_STEP); int b = color.b + (direction * CONFIG_ZMK_RGB_UNDERGLOW_BRT_STEP);
if (b < 0) { color.b = CLAMP(b, 0, BRT_MAX);
b = 0;
} else if (b > BRT_MAX) {
b = BRT_MAX;
}
color.b = b;
return color; return color;
} }

View file

@ -29,9 +29,10 @@ static int start_scan(void);
static struct bt_conn *default_conn; static struct bt_conn *default_conn;
static struct bt_uuid_128 uuid = BT_UUID_INIT_128(ZMK_SPLIT_BT_SERVICE_UUID); static const struct bt_uuid_128 split_service_uuid = BT_UUID_INIT_128(ZMK_SPLIT_BT_SERVICE_UUID);
static struct bt_gatt_discover_params discover_params; static struct bt_gatt_discover_params discover_params;
static struct bt_gatt_subscribe_params subscribe_params; static struct bt_gatt_subscribe_params subscribe_params;
static struct bt_gatt_discover_params sub_discover_params;
K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed), K_MSGQ_DEFINE(peripheral_event_msgq, sizeof(struct zmk_position_state_changed),
CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE, 4); CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE, 4);
@ -83,15 +84,12 @@ static uint8_t split_central_notify_func(struct bt_conn *conn,
return BT_GATT_ITER_CONTINUE; return BT_GATT_ITER_CONTINUE;
} }
static int split_central_subscribe(struct bt_conn *conn) { static void split_central_subscribe(struct bt_conn *conn) {
int err = bt_gatt_subscribe(conn, &subscribe_params); int err = bt_gatt_subscribe(conn, &subscribe_params);
switch (err) { switch (err) {
case -EALREADY: case -EALREADY:
LOG_DBG("[ALREADY SUBSCRIBED]"); LOG_DBG("[ALREADY SUBSCRIBED]");
break; break;
// break;
// bt_gatt_unsubscribe(conn, &subscribe_params);
// return split_central_subscribe(conn);
case 0: case 0:
LOG_DBG("[SUBSCRIBED]"); LOG_DBG("[SUBSCRIBED]");
break; break;
@ -99,14 +97,44 @@ static int split_central_subscribe(struct bt_conn *conn) {
LOG_ERR("Subscribe failed (err %d)", err); LOG_ERR("Subscribe failed (err %d)", err);
break; break;
} }
return 0;
} }
static uint8_t split_central_discovery_func(struct bt_conn *conn, const struct bt_gatt_attr *attr, static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
struct bt_gatt_discover_params *params) { struct bt_gatt_discover_params *params) {
int err; if (!attr) {
LOG_DBG("Discover complete");
return BT_GATT_ITER_STOP;
}
if (!attr->user_data) {
LOG_ERR("Required user data not passed to discovery");
return BT_GATT_ITER_STOP;
}
LOG_DBG("[ATTRIBUTE] handle %u", attr->handle);
if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID))) {
LOG_DBG("Found position state characteristic");
discover_params.uuid = NULL;
discover_params.start_handle = attr->handle + 2;
discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
subscribe_params.disc_params = &sub_discover_params;
subscribe_params.end_handle = discover_params.end_handle;
subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
subscribe_params.notify = split_central_notify_func;
subscribe_params.value = BT_GATT_CCC_NOTIFY;
split_central_subscribe(conn);
}
return subscribe_params.value_handle ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE;
}
static uint8_t split_central_service_discovery_func(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
struct bt_gatt_discover_params *params) {
if (!attr) { if (!attr) {
LOG_DBG("Discover complete"); LOG_DBG("Discover complete");
(void)memset(params, 0, sizeof(*params)); (void)memset(params, 0, sizeof(*params));
@ -115,38 +143,21 @@ static uint8_t split_central_discovery_func(struct bt_conn *conn, const struct b
LOG_DBG("[ATTRIBUTE] handle %u", attr->handle); LOG_DBG("[ATTRIBUTE] handle %u", attr->handle);
if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_SERVICE_UUID))) { if (bt_uuid_cmp(discover_params.uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_SERVICE_UUID))) {
memcpy(&uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID), sizeof(uuid)); LOG_DBG("Found other service");
discover_params.uuid = &uuid.uuid; return BT_GATT_ITER_CONTINUE;
}
LOG_DBG("Found split service");
discover_params.uuid = NULL;
discover_params.func = split_central_chrc_discovery_func;
discover_params.start_handle = attr->handle + 1; discover_params.start_handle = attr->handle + 1;
discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
err = bt_gatt_discover(conn, &discover_params); int err = bt_gatt_discover(conn, &discover_params);
if (err) { if (err) {
LOG_ERR("Discover failed (err %d)", err); LOG_ERR("Failed to start discovering split service characteristics (err %d)", err);
} }
} else if (!bt_uuid_cmp(discover_params.uuid,
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID))) {
memcpy(&uuid, BT_UUID_GATT_CCC, sizeof(uuid));
discover_params.uuid = &uuid.uuid;
discover_params.start_handle = attr->handle + 2;
discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
err = bt_gatt_discover(conn, &discover_params);
if (err) {
LOG_ERR("Discover failed (err %d)", err);
}
} else {
subscribe_params.notify = split_central_notify_func;
subscribe_params.value = BT_GATT_CCC_NOTIFY;
subscribe_params.ccc_handle = attr->handle;
split_central_subscribe(conn);
return BT_GATT_ITER_STOP;
}
return BT_GATT_ITER_STOP; return BT_GATT_ITER_STOP;
} }
@ -161,9 +172,9 @@ static void split_central_process_connection(struct bt_conn *conn) {
return; return;
} }
if (conn == default_conn && !subscribe_params.value) { if (conn == default_conn && !subscribe_params.value_handle) {
discover_params.uuid = &uuid.uuid; discover_params.uuid = &split_service_uuid.uuid;
discover_params.func = split_central_discovery_func; discover_params.func = split_central_service_discovery_func;
discover_params.start_handle = 0x0001; discover_params.start_handle = 0x0001;
discover_params.end_handle = 0xffff; discover_params.end_handle = 0xffff;
discover_params.type = BT_GATT_DISCOVER_PRIMARY; discover_params.type = BT_GATT_DISCOVER_PRIMARY;
@ -318,6 +329,9 @@ static void split_central_disconnected(struct bt_conn *conn, uint8_t reason) {
bt_conn_unref(default_conn); bt_conn_unref(default_conn);
default_conn = NULL; default_conn = NULL;
// Clean up previously discovered handles;
subscribe_params.value_handle = 0;
start_scan(); start_scan();
} }

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,5 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-timer (balanced decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,11 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,500)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (balanced decision moment timer)
kp_pressed: usage_page 0x07 keycode 0x09 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_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,14 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,200)
ZMK_MOCK_PRESS(1,1,200) // non trigger key
/* timer fires */
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-timer (balanced decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,14 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,200)
ZMK_MOCK_PRESS(1,0,200) // trigger key
/* timer fires */
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,29 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
behaviors {
ht_bal: behavior_hold_tap_balanced {
compatible = "zmk,behavior-hold-tap";
label = "HOLD_TAP_BALANCED";
#binding-cells = <2>;
flavor = "balanced";
tapping-term-ms = <300>;
quick-tap-ms = <200>;
bindings = <&kp>, <&kp>;
hold-trigger-key-positions = <2>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&ht_bal LEFT_SHIFT F &ht_bal LEFT_CONTROL J
&kp D &kp E>;
};
};
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,9 @@
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (balanced decision moment timer)
kp_pressed: usage_page 0x07 keycode 0x09 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_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,16 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,0,10) // trigger key
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(1,1,400) // not trigger key
/* timer fires */
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,5 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-timer (hold-preferred decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,11 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,500)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (hold-preferred decision moment other-key-down)
kp_pressed: usage_page 0x07 keycode 0x09 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_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,14 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,200)
ZMK_MOCK_PRESS(1,1,200) // non trigger key
/* timer fires */
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-interrupt (hold-preferred decision moment other-key-down)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,14 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,200)
ZMK_MOCK_PRESS(1,0,200) // trigger key
/* timer fires */
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,29 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
behaviors {
ht_hold: behavior_hold_hold_tap {
compatible = "zmk,behavior-hold-tap";
label = "hold_hold_tap";
#binding-cells = <2>;
flavor = "hold-preferred";
tapping-term-ms = <300>;
quick-tap-ms = <200>;
bindings = <&kp>, <&kp>;
hold-trigger-key-positions = <2>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&ht_hold LEFT_SHIFT F &ht_hold LEFT_CONTROL J
&kp D &kp E>;
};
};
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,9 @@
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (hold-preferred decision moment other-key-down)
kp_pressed: usage_page 0x07 keycode 0x09 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_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,16 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,0,10) // trigger key
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(1,1,400) // not trigger key
/* timer fires */
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,5 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-timer (tap-preferred decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,11 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,500)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-preferred decision moment timer)
kp_pressed: usage_page 0x07 keycode 0x09 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_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,14 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,200)
ZMK_MOCK_PRESS(1,1,200) // non trigger key
/* timer fires */
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,7 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-timer (tap-preferred decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,14 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,200)
ZMK_MOCK_PRESS(1,0,200) // trigger key
/* timer fires */
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,29 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
behaviors {
tp: behavior_tap_preferred {
compatible = "zmk,behavior-hold-tap";
label = "MOD_TAP";
#binding-cells = <2>;
flavor = "tap-preferred";
tapping-term-ms = <300>;
quick-tap-ms = <200>;
bindings = <&kp>, <&kp>;
hold-trigger-key-positions = <2>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&tp LEFT_SHIFT F &tp LEFT_CONTROL J
&kp D &kp E>;
};
};
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,9 @@
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-preferred decision moment timer)
kp_pressed: usage_page 0x07 keycode 0x09 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_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,16 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,0,10) // trigger key
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(1,1,400) // not trigger key
/* timer fires */
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,5 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-unless-interrupted decision moment key-up)
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
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,11 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,5 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-unless-interrupted decision moment timer)
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
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,11 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,500)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,7 @@
kp_pressed: usage_page 0x07 keycode 0xe4 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-unless-interrupted decision moment key-up)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe4 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,13 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10) /*ctrl*/
ZMK_MOCK_PRESS(0,0,100) /*mt f-shift */
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,7 @@
kp_pressed: usage_page 0x07 keycode 0xe4 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-unless-interrupted decision moment timer)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe4 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,14 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10) /*ctrl*/
ZMK_MOCK_PRESS(0,0,50) /*mt f-shift */
ZMK_MOCK_RELEASE(1,1,300)
/*timer*/
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,7 @@
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
ht_decide: 0 decided tap (tap-unless-interrupted decision moment key-up)
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
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,13 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,0,10) /*d*/
ZMK_MOCK_PRESS(0,0,100) /*mt f-shift */
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,7 @@
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
ht_decide: 0 decided tap (tap-unless-interrupted decision moment timer)
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
ht_binding_released: 0 cleaning up hold-tap

View file

@ -0,0 +1,13 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,0,10) /* d */
ZMK_MOCK_PRESS(0,0,100) /* mt f-shift */
ZMK_MOCK_RELEASE(1,0,400)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View file

@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*mo_keymap_binding/mo/p
s/.*on_hold_tap_binding/ht_binding/p
s/.*decide_hold_tap/ht_decide/p

View file

@ -0,0 +1,10 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-interrupt (tap-unless-interrupted decision moment other-key-down)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 1 new undecided hold_tap
ht_decide: 1 decided tap (tap-unless-interrupted decision moment key-up)
kp_pressed: usage_page 0x07 keycode 0x0d implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x0d implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 1 cleaning up hold-tap
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

Some files were not shown because too many files have changed in this diff Show more