Merge branch 'upstream_main' into mercury

This commit is contained in:
jding 2021-10-27 22:56:51 +00:00
commit 3bacad469e
34 changed files with 1335 additions and 1600 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
RUN mv /tmp/.bashrc ~/.bashrc

View file

@ -3,7 +3,7 @@
- [ ] 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))
- [ ] `.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
- [ ] 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

View file

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

View file

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

View file

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

View file

@ -209,7 +209,7 @@ endif
endif
#ZMK_SPLIT
endif
endif
if ZMK_BLE
@ -262,36 +262,54 @@ config ZMK_RGB_UNDERGLOW_EXT_POWER
bool "RGB underglow toggling also controls external power"
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
int "RGB underglow hue step in degrees of 360"
int "RGB underglow hue step in degrees"
range 0 359
default 10
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
config ZMK_RGB_UNDERGLOW_BRT_STEP
int "RGB underglow brightness step in percent"
range 0 100
default 10
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
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
config ZMK_RGB_UNDERGLOW_BRT_START
int "RGB underglow start brightness value from 0-100"
default 100
int "RGB underglow start brightness value in percent"
range ZMK_RGB_UNDERGLOW_BRT_MIN ZMK_RGB_UNDERGLOW_BRT_MAX
default ZMK_RGB_UNDERGLOW_BRT_MAX
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
config ZMK_RGB_UNDERGLOW_EFF_START
int "RGB underglow start effect int value related to the effect enum list"
range 0 3
default 0
config ZMK_RGB_UNDERGLOW_ON_START

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

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

View file

@ -4,6 +4,7 @@
zephyr_library_named(zmk__drivers__kscan)
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_direct.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
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
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
*/
#include "debounce.h"
#include <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>
#include <drivers/kscan.h>
#include <kernel.h>
#include <logging/log.h>
#include <sys/__assert.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_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_INTERRUPTS (!USE_POLLING)
@ -66,26 +83,23 @@ enum kscan_diode_direction {
struct kscan_matrix_irq_callback {
const struct device *dev;
struct gpio_callback callback;
struct k_delayed_work *work;
};
struct kscan_matrix_data {
const struct device *dev;
kscan_callback_t callback;
struct k_delayed_work work;
#if USE_POLLING
struct k_timer poll_timer;
#else
#if USE_INTERRUPTS
/** Array of length config->inputs.len */
struct kscan_matrix_irq_callback *irqs;
#endif
/** Timestamp of the current or scheduled scan. */
int64_t scan_time;
/**
* Current state of the matrix as a flattened 2D array of length
* (config->rows.len * config->cols.len)
*/
bool *current_state;
/** Buffer for reading in the next matrix state. Parallel array to current_state. */
bool *next_state;
struct debounce_state *matrix_state;
};
struct kscan_gpio_list {
@ -102,7 +116,8 @@ struct kscan_matrix_config {
struct kscan_gpio_list cols;
struct kscan_gpio_list inputs;
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;
enum kscan_diode_direction diode_direction;
};
@ -190,19 +205,49 @@ static int kscan_matrix_interrupt_disable(const struct device *dev) {
#if USE_INTERRUPTS
static void kscan_matrix_irq_callback_handler(const struct device *port, struct gpio_callback *cb,
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);
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.
kscan_matrix_interrupt_disable(data->dev);
data->scan_time = k_uptime_get();
// TODO (Zephyr 2.6): use k_work_reschedule()
k_delayed_work_cancel(data->work);
k_delayed_work_submit(data->work, K_MSEC(config->debounce_period_ms));
k_delayed_work_cancel(&data->work);
k_delayed_work_submit(&data->work, K_NO_WAIT);
}
#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) {
struct kscan_matrix_data *data = dev->data;
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 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);
@ -232,50 +280,36 @@ static int kscan_matrix_read(const struct device *dev) {
}
// Process the new state.
#if USE_INTERRUPTS
bool submit_followup_read = false;
#endif
bool continue_scan = false;
for (int r = 0; r < config->rows.len; r++) {
for (int c = 0; c < config->cols.len; 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");
data->current_state[index] = pressed;
data->callback(dev, r, c, pressed);
}
continue_scan = continue_scan || debounce_is_active(state);
}
}
#if USE_INTERRUPTS
if (submit_followup_read) {
// At least one key is pressed. Poll until everything is released.
// TODO (Zephyr 2.6): use k_work_reschedule()
k_delayed_work_cancel(&data->work);
k_delayed_work_submit(&data->work, K_MSEC(config->debounce_period_ms));
if (continue_scan) {
// At least one key is pressed or the debouncer has not yet decided if
// it is pressed. Poll quickly until everything is released.
kscan_matrix_read_continue(dev);
} else {
// All keys are released. Return to waiting for an interrupt.
kscan_matrix_interrupt_enable(dev);
// All keys are released. Return to normal.
kscan_matrix_read_end(dev);
}
#endif
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) {
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);
@ -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) {
#if USE_POLLING
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),
K_MSEC(config->poll_period_ms));
return 0;
#else
// Read will automatically enable interrupts once done.
data->scan_time = k_uptime_get();
// Read will automatically start interrupts/polling once done.
return kscan_matrix_read(dev);
#endif
}
static int kscan_matrix_disable(const struct device *dev) {
#if USE_POLLING
struct kscan_matrix_data *data = dev->data;
k_timer_stop(&data->poll_timer);
return 0;
#else
k_delayed_work_cancel(&data->work);
#if USE_INTERRUPTS
return kscan_matrix_interrupt_disable(dev);
#else
return 0;
#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];
irq->dev = dev;
irq->work = &data->work;
gpio_init_callback(&irq->callback, kscan_matrix_irq_callback_handler, BIT(gpio->pin));
err = gpio_add_callback(gpio->port, &irq->callback);
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);
#if USE_POLLING
k_timer_init(&data->poll_timer, kscan_matrix_timer_handler, NULL);
#endif
return 0;
}
@ -421,21 +446,24 @@ static const struct kscan_driver_api kscan_matrix_api = {
};
#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[] = { \
UTIL_LISTIFY(INST_ROWS_LEN(index), KSCAN_GPIO_ROW_CFG_INIT, index)}; \
\
static const struct kscan_gpio_dt_spec kscan_matrix_cols_##index[] = { \
UTIL_LISTIFY(INST_COLS_LEN(index), KSCAN_GPIO_COL_CFG_INIT, index)}; \
\
static bool kscan_current_state_##index[INST_MATRIX_LEN(index)]; \
static bool kscan_next_state_##index[INST_MATRIX_LEN(index)]; \
static struct debounce_state kscan_matrix_state_##index[INST_MATRIX_LEN(index)]; \
\
COND_INTERRUPTS((static struct kscan_matrix_irq_callback \
kscan_matrix_irqs_##index[INST_INPUTS_LEN(index)];)) \
\
static struct kscan_matrix_data kscan_matrix_data_##index = { \
.current_state = kscan_current_state_##index, \
.next_state = kscan_next_state_##index, \
.matrix_state = kscan_matrix_state_##index, \
COND_INTERRUPTS((.irqs = kscan_matrix_irqs_##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))), \
.outputs = KSCAN_GPIO_LIST( \
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), \
.diode_direction = INST_DIODE_DIR(index), \
}; \

View file

@ -15,13 +15,26 @@ properties:
type: phandle-array
required: true
debounce-period:
type: int
required: false
deprecated: true
description: Deprecated. Use debounce-press-ms and debounce-release-ms instead.
debounce-press-ms:
type: int
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:
type: int
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:
type: string
default: row2col

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_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) \
static struct combo_cfg combo_config_##n = { \
.timeout_ms = DT_PROP(n, timeout_ms), \
.key_positions = DT_PROP(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__, \
.slow_release = DT_PROP(n, slow_release), \
.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) {
char text[6] = {};
char text[9] = {};
switch (state.selected_endpoint) {
case ZMK_ENDPOINT_USB:

View file

@ -28,6 +28,9 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define SAT_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 {
UNDERGLOW_EFFECT_SOLID,
UNDERGLOW_EFFECT_BREATHE,
@ -54,6 +57,17 @@ static struct rgb_underglow_state state;
static const struct device *ext_power;
#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) {
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() {
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;
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;
@ -129,7 +143,7 @@ static void zmk_rgb_underglow_effect_spectrum() {
struct zmk_led_hsb hsb = state.color;
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;
@ -141,7 +155,7 @@ static void zmk_rgb_underglow_effect_swirl() {
struct zmk_led_hsb hsb = state.color;
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;
@ -371,12 +385,7 @@ struct zmk_led_hsb zmk_rgb_underglow_calc_brt(int direction) {
struct zmk_led_hsb color = state.color;
int b = color.b + (direction * CONFIG_ZMK_RGB_UNDERGLOW_BRT_STEP);
if (b < 0) {
b = 0;
} else if (b > BRT_MAX) {
b = BRT_MAX;
}
color.b = b;
color.b = CLAMP(b, 0, BRT_MAX);
return color;
}

View file

@ -27,7 +27,7 @@ This allows you to use those defines, e.g. `LOWER` later in your keymap.
## Momentary Layer
The "momentary layer" behavior enables a layer while a certain key is pressed. Immediately upon
activation of the key, the layer is enabled, and immediately open release of the key, the layer is disabled
activation of the key, the layer is enabled, and immediately upon release of the key, the layer is disabled
again.
### Behavior Binding

View file

@ -19,7 +19,7 @@ If you are working with the documentation from within VS Code+Docker please be a
:::
:::note
You will need `Node.js` and `npm` installed to update the documentation. If you're using the ZMK dev container (Docker) the necessary dependencies are already installed.
You will need `Node.js` and `npm` installed to update the documentation. If you're using the ZMK dev container (Docker) the necessary dependencies are already installed. Otherwise, you must install these dependencies yourself. Since `Node.js` packages in Linux distributions tend to be outdated, it's recommended to install the current version from a repository like [NodeSource](https://github.com/nodesource/distributions) to avoid build errors.
:::
## Testing Documentation Updates Locally

View file

@ -0,0 +1,100 @@
---
title: Debouncing
sidebar_label: Debouncing
---
To prevent contact bounce (also known as chatter) and noise spikes from causing
unwanted key presses, ZMK uses a [cycle-based debounce algorithm](https://www.kennethkuhn.com/electronics/debounce.c),
with each key debounced independently.
By default the debounce algorithm decides that a key is pressed or released after
the input is stable for 5 milliseconds. You can decrease this to improve latency
or increase it to improve reliability.
If you are having problems with a single key press registering multiple inputs,
you can try increasing the debounce press and/or release times to compensate.
You should also check for mechanical issues that might be causing the bouncing,
such as hot swap sockets that are making poor contact. You can try replacing the
socket or using some sharp tweezers to bend the contacts back together.
## Debounce Configuration
### Global Options
You can set these options in your `.conf` file to control debouncing globally.
Values must be <= 127.
- `CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS`: Debounce time for key press in milliseconds. Default = 5.
- `CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS`: Debounce time for key release in milliseconds. Default = 5.
For example, this would shorten the debounce time for both press and release:
```ini
CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS=3
CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS=3
```
### Per-driver Options
You can add these Devicetree properties to a kscan node to control debouncing for
that instance of the driver. Values must be <= 127.
- `debounce-press-ms`: Debounce time for key press in milliseconds. Default = 5.
- `debounce-release-ms`: Debounce time for key release in milliseconds. Default = 5.
- ~~`debounce-period`~~: Deprecated. Sets both press and release debounce times.
- `debounce-scan-period-ms`: Time between reads in milliseconds when any key is pressed. Default = 1.
If one of the global options described above is set, it overrides the corresponding
per-driver option.
For example, if your board/shield has a kscan driver labeled `kscan0` in its
`.overlay`, `.dts`, or `.dtsi` files,
```devicetree
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
...
};
```
then you could add this to your `.keymap`:
```devicetree
&kscan0 {
debounce-press-ms = <3>;
debounce-release-ms = <3>;
};
```
This must be placed outside of any blocks surrounded by curly braces (`{...}`).
`debounce-scan-period-ms` determines how often the keyboard scans while debouncing. It defaults to 1 ms, but it can be increased to reduce power use. Note that the debounce press/release timers are rounded up to the next multiple of the scan period. For example, if the scan period is 2 ms and debounce timer is 5 ms, key presses will take 6 ms to register instead of 5.
## Eager Debouncing
Eager debouncing means reporting a key change immediately and then ignoring
further changes for the debounce time. This eliminates latency but it is not
noise-resistant.
ZMK does not currently support true eager debouncing, but you can get something
very close by setting the time to detect a key press to zero and the time to detect
a key release to a larger number. This will detect a key press immediately, then
debounce the key release.
```ini
CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS=0
CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS=5
```
Also consider setting `CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS=1` instead, which adds
one millisecond of latency but protects against short noise spikes.
## Comparison With QMK
ZMK's default debouncing is similar to QMK's `sym_defer_pk` algorithm.
Setting `CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS=0` for eager debouncing would be similar
to QMK's (unimplemented as of this writing) `asym_eager_defer_pk`.
See [QMK's Debounce API documentation](https://beta.docs.qmk.fm/using-qmk/software-features/feature_debounce_type)
for more information.

1984
docs/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -19,15 +19,16 @@
"@docusaurus/preset-classic": "^2.0.0-beta.3",
"@fortawesome/fontawesome-svg-core": "^1.2.32",
"@fortawesome/free-solid-svg-icons": "^5.15.3",
"@fortawesome/react-fontawesome": "^0.1.14",
"@fortawesome/react-fontawesome": "^0.1.16",
"@mdx-js/react": "^1.6.22",
"classnames": "^2.2.6",
"js-yaml": "^4.1.0",
"react": "^17.0.2",
"react-async": "^10.0.1",
"react-copy-to-clipboard": "^5.0.3",
"react-dom": "^17.0.2",
"react-toastify": "^7.0.4",
"web-tree-sitter": "^0.19.4",
"@mdx-js/react": "^1.6.22"
"web-tree-sitter": "^0.19.4"
},
"browserslist": {
"production": [
@ -42,6 +43,11 @@
]
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^2.0.0-beta.3",
"@tsconfig/docusaurus": "^1.0.2",
"@types/react": "^17.0.3",
"@types/react-helmet": "^6.1.0",
"@types/react-router-dom": "^5.1.7",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-mdx": "^1.13.0",
@ -53,11 +59,6 @@
"prettier": "2.3.1",
"string-replace-loader": "^3.0.3",
"typescript": "^4.2.3",
"webpack": "^5.46.0",
"@docusaurus/module-type-aliases": "^2.0.0-beta.3",
"@tsconfig/docusaurus": "^1.0.2",
"@types/react": "^17.0.3",
"@types/react-helmet": "^6.1.0",
"@types/react-router-dom": "^5.1.7"
"webpack": "^5.46.0"
}
}

View file

@ -11,6 +11,7 @@ module.exports = {
Features: [
"features/keymaps",
"features/combos",
"features/debouncing",
"features/displays",
"features/encoders",
"features/underglow",

View file

@ -12,7 +12,7 @@ const glob = require("glob");
function generateHardwareMetadataAggregate() {
glob("../app/boards/**/*.zmk.yml", (error, files) => {
const aggregated = files.flatMap((f) =>
yaml.safeLoadAll(fs.readFileSync(f, "utf8"))
yaml.loadAll(fs.readFileSync(f, "utf8"))
);
fs.writeFileSync(
"src/data/hardware-metadata.json",

View file

@ -5,6 +5,7 @@
*/
var PrebuildPlugin = require("prebuild-webpack-plugin");
const path = require("path");
const fs = require("fs");
const glob = require("glob");
const yaml = require("js-yaml");
@ -12,9 +13,10 @@ const Mustache = require("mustache");
function generateSetupScripts() {
return glob("../app/boards/**/*.zmk.yml", (error, files) => {
const aggregated = files.flatMap((f) =>
yaml.safeLoadAll(fs.readFileSync(f, "utf8"))
);
const aggregated = files.map((f) => ({
...yaml.load(fs.readFileSync(f, "utf8")),
__base_dir: path.basename(path.dirname(f)),
}));
const data = aggregated.reduce(
(agg, item) => {
@ -25,7 +27,9 @@ function generateSetupScripts() {
agg.keyboards.push(item);
break;
case "board":
if (!item.features?.includes("keys")) {
if (item.features?.includes("keys")) {
agg.keyboards.push(item);
} else {
agg.boards.push(item);
}
break;
@ -35,6 +39,9 @@ function generateSetupScripts() {
{ keyboards: [], boards: [] }
);
data.keyboards.sort((a, b) => a.name.localeCompare(b.name));
data.boards.sort((a, b) => a.name.localeCompare(b.name));
for (let script_ext of ["sh", "ps1"]) {
const templateBuffer = fs.readFileSync(
`src/templates/setup.${script_ext}.mustache`,

View file

@ -84,36 +84,59 @@ if (Test-CommandExists Get-Acl) {
}
}
$repo_path = "https://github.com/zmkfirmware/zmk-config-split-template.git"
$repo_path = "https://github.com/zmkfirmware/unified-zmk-config-template.git"
$title = "ZMK Config Setup:"
$prompt = "Pick an MCU board"
$options = {{#boards}}"{{{name}}}", {{/boards}} "" | Where-Object { $_ -ne "" }
$boards = {{#boards}}"{{id}}", {{/boards}} "" | Where-Object { $_ -ne "" }
Write-Host "$title"
Write-Host ""
Write-Host "MCU Board Selection:"
$choice = Get-Choice-From-Options -Options $options -Prompt $prompt
$board = $($boards[$choice])
Write-Host ""
Write-Host "Keyboard Shield Selection:"
$prompt = "Pick a keyboard"
$keyboards = [ordered]@{
{{#keyboards}}
"{{id}}" = @{
name = "{{{name}}}";
type = "{{type}}";
basedir = "{{__base_dir}}";
split = "{{split}}";
arch = "{{arch}}";
siblings = {{#siblings.0}}@(
{{#siblings}}
"{{.}}"
{{/siblings}}
){{/siblings.0}}{{^siblings.0}}( "{{id}}" ){{/siblings.0}};
}
{{/keyboards}}
}
# TODO: Add support for "Other" and linking to docs on adding custom shields in user config repos.
$options = {{#keyboards}}"{{name}}", {{/keyboards}} "" | Where-Object { $_ -ne "" }
$names = {{#keyboards}}"{{id}}", {{/keyboards}} "" | Where-Object { $_ -ne "" }
$splits = {{#keyboards}}"{{#split}}y{{/split}}{{^split}}n{{/split}}", {{/keyboards}} "" | Where-Object { $_ -ne "" }
$choice = Get-Choice-From-Options -Options $options -Prompt $prompt
$shield_title = $($options[$choice])
$shield = $($names[$choice])
$split = $($splits[$choice])
$choice = Get-Choice-From-Options -Options ($keyboards.values | % { $_['name'] }) -Prompt $prompt
$keyboard = $($($keyboards.keys)[$choice])
$keyboard_title = $keyboards[$keyboard].name
$basedir = $keyboards[$keyboard].basedir
$keyboard_split = $keyboards[$keyboard].split
$keyboard_arch = $keyboards[$keyboard].arch
$keyboard_siblings = $keyboards[$keyboard].siblings
$keyboard_type = $keyboards[$keyboard].type
if ($split -eq "n") {
$repo_path = "https://github.com/zmkfirmware/zmk-config-template.git"
if ($keyboard_type -eq "shield") {
$prompt = "Pick an MCU board"
$boards = [ordered]@{
{{#boards}}
{{id}} = "{{{name}}}";
{{/boards}}
}
Write-Host "$title"
Write-Host ""
Write-Host "MCU Board Selection:"
$choice = Get-Choice-From-Options -Options $boards.values -Prompt $prompt
$shields = $keyboard_siblings
$board = $($($boards.keys)[$choice])
$boards = ( $board )
} else {
$boards = ( $keyboard_siblings )
$shields = @( )
}
$copy_keymap = Read-Host "Copy in the stock keymap for customisation? [Yn]"
@ -144,8 +167,12 @@ else {
Write-Host ""
Write-Host "Preparing a user config for:"
Write-Host "* MCU Board: ${board}"
Write-Host "* Shield: ${shield}"
if ($keyboard_type -eq "shield") {
Write-Host "* MCU Board: ${boards}"
Write-Host "* Shield(s): ${shields}"
} else {
Write-Host "* Board(s): ${boards}"
}
if ($copy_keymap -eq "yes") {
Write-Host "* Copy Keymap?: Yes"
@ -171,24 +198,43 @@ Set-Location "$repo_name"
Push-Location config
Invoke-RestMethod -Uri "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.conf" -OutFile "${shield}.conf"
$config_file = "${keyboard}.conf"
$keymap_file = "${keyboard}.keymap"
if ($keyboard_type -eq "shield") {
$config_url = "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${basedir}/${keyboard}.conf"
$keymap_url = "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${basedir}/${keyboard}.keymap"
} else {
$config_url = "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/${keyboard_arch}/${basedir}/${keyboard}.conf"
$keymap_url = "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/${keyboard_arch}/${basedir}/${keyboard}.keymap"
}
Write-Host "Downloading config file (${config_url})"
Try {
Invoke-RestMethod -Uri "${config_url}" -OutFile "${config_file}"
} Catch {
Set-Content -Path $config_file "# Place configuration items here"
}
if ($copy_keymap -eq "yes") {
Invoke-RestMethod -Uri "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.keymap" -OutFile "${shield}.keymap"
Write-Host "Downloading keymap file (${keymap_url})"
Invoke-RestMethod -Uri "${keymap_url}" -OutFile "${keymap_file}"
}
Pop-Location
$build_file = (Get-Content .github/workflows/build.yml).replace("BOARD_NAME", $board)
$build_file = $build_file.replace("SHIELD_NAME", $shield)
$build_file = $build_file.replace("KEYBOARD_TITLE", $shield_title)
if ($board -eq "proton_c") {
$build_file = $build_file.replace("uf2", "hex")
Add-Content -Path "build.yaml" -Value "include:"
foreach ($b in ${boards}) {
if ($keyboard_type -eq "shield") {
foreach ($s in ${shields}) {
Add-Content -Path "build.yaml" -Value " - board: $b"
Add-Content -Path "build.yaml" -Value " shield: $s"
}
} else {
Add-Content -Path "build.yaml" -Value " - board: $b"
}
}
Set-Content -Path .github/workflows/build.yml -Value $build_file
Remove-Item -Recurse -Force .git
git init .
git add .

View file

@ -51,7 +51,7 @@ if [[ $curl_exists == "true" && $wget_exists == "true" ]]; then
if [[ $force_wget == "true" ]]; then
download_command="wget "
else
download_command="curl -O "
download_command="curl -fsOL "
fi
elif [[ $curl_exists == "true" ]]; then
download_command="curl -O "
@ -62,7 +62,7 @@ else
exit 1
fi
repo_path="https://github.com/zmkfirmware/zmk-config-split-template.git"
repo_path="https://github.com/zmkfirmware/unified-zmk-config-template.git"
title="ZMK Config Setup:"
echo ""
@ -71,8 +71,17 @@ PS3="Pick a keyboard: "
options=({{#keyboards}}"{{{name}}}" {{/keyboards}})
keyboards_id=({{#keyboards}}"{{id}}" {{/keyboards}})
keyboards_type=({{#keyboards}}"{{type}}" {{/keyboards}})
keyboards_arch=({{#keyboards}}"{{arch}}" {{/keyboards}})
keyboards_basedir=({{#keyboards}}"{{__base_dir}}" {{/keyboards}})
keyboards_split=({{#keyboards}}"{{#split}}y{{/split}}{{^split}}n{{/split}}" {{/keyboards}})
keyboards_shield=({{#keyboards}}"{{#compatible}}y{{/compatible}}{{^compatible}}n{{/compatible}}" {{/keyboards}})
{{#keyboards}}
{{#siblings.0}}
{{id}}_siblings=({{#siblings}}"{{.}}" {{/siblings}})
{{/siblings.0}}
{{/keyboards}}
select opt in "${options[@]}" "Quit"; do
case "$REPLY" in
''|*[!0-9]*) echo "Invalid option. Try another one."; continue;;
@ -85,9 +94,17 @@ select opt in "${options[@]}" "Quit"; do
fi
keyboard_index=$(( $REPLY-1 ))
keyboard=${keyboards_id[$keyboard_index]}
keyboard_arch=${keyboards_arch[$keyboard_index]}
keyboard_basedir=${keyboards_basedir[$keyboard_index]}
keyboard_title=${options[$keyboard_index]}
keyboard_sibling_var=${keyboard}_siblings[@]
keyboard_sibling_first=${keyboard}_siblings[0]
if [ -n "${!keyboard_sibling_first}" ]; then
keyboard_siblings=${!keyboard_sibling_var}
else
keyboard_siblings=( "${keyboard}" )
fi
split=${keyboards_split[$keyboard_index]}
type=${keyboards_type[$keyboard_index]}
keyboard_shield=${keyboards_shield[$keyboard_index]}
break
;;
@ -96,6 +113,7 @@ select opt in "${options[@]}" "Quit"; do
done
if [ "$keyboard_shield" == "y" ]; then
shields=$keyboard_siblings
shield=${keyboard}
shield_title=${keyboard_title}
@ -119,6 +137,7 @@ if [ "$keyboard_shield" == "y" ]; then
board_index=$(( $REPLY-1 ))
board=${board_ids[$board_index]}
board_title=${options[$board_index]}
boards=( "${board}" )
break
;;
@ -126,12 +145,7 @@ if [ "$keyboard_shield" == "y" ]; then
done
else
board=${keyboard}
echo "Support for onboard microcontroller keyboards is still a work in progress."
exit 1
fi
if [ "$split" == "n" ]; then
repo_path="https://github.com/zmkfirmware/zmk-config-template.git"
boards=$keyboard_siblings
fi
read -r -e -p "Copy in the stock keymap for customization? [Yn]: " copy_keymap
@ -152,8 +166,12 @@ fi
echo ""
echo "Preparing a user config for:"
echo "* MCU Board: ${board}"
echo "* Shield: ${shield}"
if [ "$keyboard_shield" == "y" ]; then
echo "* MCU Board: ${boards}"
echo "* Shield(s): ${shields}"
else
echo "* Board(s): ${boards}"
fi
if [ "$copy_keymap" == "yes" ]; then
echo "* Copy Keymap?: ✓"
@ -178,26 +196,37 @@ cd ${repo_name}
pushd config
$download_command "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.conf"
if [ "$keyboard_shield" == "y" ]; then
config_file="https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${keyboard_basedir}/${shield}.conf"
keymap_file="https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${keyboard_basedir}/${shield}.keymap"
else
config_file="https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/${keyboard_arch}/${keyboard_basedir}/${board}.conf"
keymap_file="https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/${keyboard_arch}/${keyboard_basedir}/${board}.keymap"
fi
echo "Downloading config file (${config_file})"
$download_command "${config_file}" || echo "# Put configuration options here" > "${keyboard}.conf"
if [ "$copy_keymap" == "yes" ]; then
$download_command "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.keymap"
echo "Downloading keymap file (${keymap_file})"
$download_command "${keymap_file}"
fi
popd
sed -i'.orig' \
-e "s/BOARD_NAME/$board/" \
-e "s/SHIELD_NAME/$shield/" \
-e "s|KEYBOARD_TITLE|$shield_title|" \
.github/workflows/build.yml
echo "include:" >> build.yaml
if [ "$board" == "proton_c" ]; then
# Proton-C board still fa
sed -i'.orig' -e "s/uf2/hex/g" .github/workflows/build.yml
fi
rm .github/workflows/*.yml.orig
for b in ${boards}; do
if [ -n "${shields}" ];
then
for s in ${shields}; do
echo " - board: ${b}" >> build.yaml
echo " shield: ${s}" >> build.yaml
done
else
echo " - board: ${b}" >> build.yaml
fi
done
rm -rf .git
git init .