Merge branch 'zmkfirmware:main' into leeloo_micro_v1_branch

This commit is contained in:
ClicketySplit 2023-06-03 23:24:06 -06:00 committed by GitHub
commit 3364c96213
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 2059 additions and 1562 deletions

View file

@ -39,8 +39,8 @@ jobs:
- name: Fetch Build Matrix - name: Fetch Build Matrix
run: | run: |
echo "build_matrix=$(yaml2json ${{ inputs.build_matrix_path }} | jq -c .)" >> $GITHUB_ENV echo "build_matrix=$(yaml2json '${{ inputs.build_matrix_path }}' | jq -c .)" >> $GITHUB_ENV
yaml2json ${{ inputs.build_matrix_path }} | jq yaml2json "${{ inputs.build_matrix_path }}" | jq
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -54,18 +54,13 @@ jobs:
steps: steps:
- name: Prepare variables - name: Prepare variables
shell: sh -x {0} shell: sh -x {0}
env:
shield: ${{ matrix.shield }}
run: | run: |
if [ -n "${{ matrix.shield }}" ]
then
echo "extra_cmake_args=-DSHIELD=\"${{ matrix.shield }}\"" >> $GITHUB_ENV
echo "artifact_name=${{ matrix.shield }}-${{ matrix.board }}-zmk" >> $GITHUB_ENV
echo "display_name=${{ matrix.shield }} - ${{ matrix.board }}" >> $GITHUB_ENV
else
echo "extra_cmake_args=" >> $GITHUB_ENV
echo "artifact_name=${{ matrix.board }}-zmk" >> $GITHUB_ENV
echo "display_name=${{ matrix.board }}" >> $GITHUB_ENV
fi
echo "zephyr_version=${ZEPHYR_VERSION}" >> $GITHUB_ENV echo "zephyr_version=${ZEPHYR_VERSION}" >> $GITHUB_ENV
echo "extra_cmake_args=${shield:+-DSHIELD=\"$shield\"}" >> $GITHUB_ENV
echo "display_name=${shield:+$shield - }${{ matrix.board }}" >> $GITHUB_ENV
echo "artifact_name=${shield:+$shield-}${{ matrix.board }}-zmk" >> $GITHUB_ENV
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -89,7 +84,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-
- name: West Init - name: West Init
run: west init -l ${{ inputs.config_path }} run: west init -l "${{ inputs.config_path }}"
- name: West Update - name: West Update
run: west update run: west update
@ -99,7 +94,7 @@ jobs:
- name: West Build (${{ env.display_name }}) - name: West Build (${{ env.display_name }})
shell: sh -x {0} shell: sh -x {0}
run: west build -s zmk/app -b ${{ matrix.board }} -- -DZMK_CONFIG=${GITHUB_WORKSPACE}/${{ inputs.config_path }} ${{ env.extra_cmake_args }} ${{ matrix.cmake-args }} run: west build -s zmk/app -b "${{ matrix.board }}" -- -DZMK_CONFIG="${GITHUB_WORKSPACE}/${{ inputs.config_path }}" ${{ env.extra_cmake_args }} ${{ matrix.cmake-args }}
- name: ${{ env.display_name }} Kconfig file - name: ${{ env.display_name }} Kconfig file
run: grep -v -e "^#" -e "^$" build/zephyr/.config | sort run: grep -v -e "^#" -e "^$" build/zephyr/.config | sort
@ -113,7 +108,7 @@ jobs:
cp build/zephyr/zmk.uf2 "build/artifacts/${{ env.artifact_name }}.uf2" cp build/zephyr/zmk.uf2 "build/artifacts/${{ env.artifact_name }}.uf2"
elif [ -f build/zephyr/zmk.${{ inputs.fallback_binary }} ] elif [ -f build/zephyr/zmk.${{ inputs.fallback_binary }} ]
then then
cp build/zephyr/zmk.${{ inputs.fallback_binary }} "build/artifacts/${{ env.artifact_name }}.${{ inputs.fallback_binary }}" cp "build/zephyr/zmk.${{ inputs.fallback_binary }}" "build/artifacts/${{ env.artifact_name }}.${{ inputs.fallback_binary }}"
fi fi
- name: Archive (${{ env.display_name }}) - name: Archive (${{ env.display_name }})

9
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,9 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"ms-python.python",
"ms-vscode.cpptools",
"plorefice.devicetree",
"twxs.cmake"
]
}

17
.vscode/settings.json vendored
View file

@ -3,5 +3,20 @@
"*.overlay": "dts", "*.overlay": "dts",
"*.keymap": "dts" "*.keymap": "dts"
}, },
"python.formatting.provider": "black" "python.formatting.provider": "black",
"[c]": {
"editor.formatOnSave": true
},
"[javascript][javascriptreact][typescript][typescriptreact]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[python]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "ms-python.python"
},
"[css][json][jsonc][html][markdown][yaml]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
} }

View file

@ -63,7 +63,7 @@
}; };
&i2c0 { &i2c0 {
compatible = "nordic,nrf-twim"; compatible = "nordic,nrf-twi";
pinctrl-0 = <&i2c0_default>; pinctrl-0 = <&i2c0_default>;
pinctrl-1 = <&i2c0_sleep>; pinctrl-1 = <&i2c0_sleep>;
pinctrl-names = "default", "sleep"; pinctrl-names = "default", "sleep";
@ -71,6 +71,7 @@
&uart0 { &uart0 {
compatible = "nordic,nrf-uarte"; compatible = "nordic,nrf-uarte";
current-speed = <115200>;
pinctrl-0 = <&uart0_default>; pinctrl-0 = <&uart0_default>;
pinctrl-1 = <&uart0_sleep>; pinctrl-1 = <&uart0_sleep>;
pinctrl-names = "default", "sleep"; pinctrl-names = "default", "sleep";

View file

@ -0,0 +1,6 @@
# Copyright (c) 2023 The ZMK Contributors
# SPDX-License-Identifier: Apache-2.0
config BOARD_KBDFANS_TOFU65_V2
bool "KBDfans Tofu65 2.0"
depends on SOC_RP2040

View file

@ -0,0 +1,15 @@
# Copyright (c) 2023 The ZMK Contributors
# SPDX-License-Identifier: MIT
if BOARD_KBDFANS_TOFU65_V2
config ZMK_KEYBOARD_NAME
default "kbdfans tofu65"
config RP2_FLASH_W25Q080
default y
config ZMK_USB
default y
endif # BOARD_KBDFANS_TOFU65_V2

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2023 The ZMK Contributors
* SPDX-License-Identifier: MIT
*/
/dts-v1/;
#include <rpi_pico/rp2040.dtsi>
#include <dt-bindings/zmk/matrix_transform.h>
/ {
chosen {
zephyr,sram = &sram0;
zephyr,flash = &flash0;
zephyr,console = &cdc_acm_uart;
zephyr,shell-uart = &cdc_acm_uart;
zephyr,code-partition = &code_partition;
zmk,kscan = &kscan0;
zmk,matrix_transform = &default_transform;
};
xtal_clk: xtal-clk {
compatible = "fixed-clock";
clock-frequency = <12000000>;
#clock-cells = <0>;
};
default_transform: keymap_transform_0 {
compatible = "zmk,matrix-transform";
columns = <15>;
rows = <5>;
// ------- Switch Matrix ----------
//
// Column 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
// ==========================================================================================
// Row 0 || S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | S8 | S9 | S10 | S11 | S12 | S13 | S14 |
// Row 1 || S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | S8 | S9 | S10 | S11 | S12 | S13 | S14 |
// Row 2 || S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | S8 | S9 | S10 | S11 | S12 | | S13 |
// Row 3 || S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | S8 | S9 | S10 | S11 | | S12 | S13 |
// Row 4 || S0 | S1 | S2 | | | | S3 | | S4 | S5 | S6 | | S7 | S8 | S9 |
// -----------------------------------------------------------------------------------
//
map = <
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(0,10) RC(0,11) RC(0,12) RC(0,13) RC(0,14)
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) RC(1,10) RC(1,11) RC(1,12) RC(1,13) RC(1,14)
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) RC(2,10) RC(2,11) RC(2,12) RC(2,14)
RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) RC(3,8) RC(3,9) RC(3,10) RC(3,11) RC(3,13) RC(3,14)
RC(4,0) RC(4,1) RC(4,2) RC(4,6) RC(4,8) RC(4,9) RC(4,10) RC(4,12) RC(4,13) RC(4,14)
>;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
label = "KSCAN";
diode-direction = "col2row";
row-gpios
= <&gpio0 29 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&gpio0 28 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&gpio0 27 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&gpio0 26 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&gpio0 22 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
;
col-gpios
= <&gpio0 25 GPIO_ACTIVE_HIGH>
, <&gpio0 24 GPIO_ACTIVE_HIGH>
, <&gpio0 23 GPIO_ACTIVE_HIGH>
, <&gpio0 1 GPIO_ACTIVE_HIGH>
, <&gpio0 7 GPIO_ACTIVE_HIGH>
, <&gpio0 21 GPIO_ACTIVE_HIGH>
, <&gpio0 20 GPIO_ACTIVE_HIGH>
, <&gpio0 19 GPIO_ACTIVE_HIGH>
, <&gpio0 18 GPIO_ACTIVE_HIGH>
, <&gpio0 17 GPIO_ACTIVE_HIGH>
, <&gpio0 16 GPIO_ACTIVE_HIGH>
, <&gpio0 15 GPIO_ACTIVE_HIGH>
, <&gpio0 14 GPIO_ACTIVE_HIGH>
, <&gpio0 13 GPIO_ACTIVE_HIGH>
, <&gpio0 12 GPIO_ACTIVE_HIGH>
;
};
};
&flash0 {
reg = <0x10000000 DT_SIZE_M(16)>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
/* Reserved memory for the second stage bootloader */
second_stage_bootloader: partition@0 {
label = "second_stage_bootloader";
reg = <0x00000000 0x100>;
read-only;
};
/*
* Usable flash. Starts at 0x100, after the bootloader. The partition
* size is 16MB minus the 0x100 bytes taken by the bootloader.
*/
code_partition: partition@100 {
label = "code";
reg = <0x100 (DT_SIZE_M(16) - 0x100)>;
read-only;
};
};
};
&usbd {
status = "okay";
cdc_acm_uart: cdc_acm_uart {
compatible = "zephyr,cdc-acm-uart";
};
};
&gpio0 {
status = "okay";
};

View file

@ -0,0 +1,97 @@
// Copyright (c) 2023 The ZMK Contributors
// SPDX-License-Identifier: MIT
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#define BASE 0
#define FUNC 1
//
// ---------- Tofu65 2.0 key switch positions ----------
//
// -------------------------------------------------------------------------------------------------
// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
// -------------------------------------------------------------------------------------------------
// | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 24 | 26 | 27 | 28 | 29 |
// -------------------------------------------------------------------------------------------------
// | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
// -------------------------------------------------------------------------------------------------
// | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
// -------------------------------------------------------------------------------------------------
// | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
// -------------------------------------------------------------------------------------------------
//
/ {
combos {
compatible = "zmk,combos";
// BACKSPACE + LCTRL + LALT = &sys_reset
combo_bootloader {
timeout-ms = <100>;
key-positions = <13 58 60>;
bindings = <&sys_reset>;
};
// RETURN + LCTRL + LALT = &bootloader
combo_sys_reset {
timeout-ms = <100>;
key-positions = <42 58 60>;
bindings = <&bootloader>;
};
};
keymap {
compatible = "zmk,keymap";
base {
// --------- Default QWERTY Layout ---------
// Layer 0 BASE
// -------------------------------------------------------------------------------------------------
// | ESC | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | - | = | BSPC | HME |
// -------------------------------------------------------------------------------------------------
// | TAB | Q | W | E | R | T | Y | U | I | O | P | [ | ] | \ | PGU |
// -------------------------------------------------------------------------------------------------
// | CAPS | A | S | D | F | G | H | J | K | L | ; | ' | ENTER | PGD |
// -------------------------------------------------------------------------------------------------
// | LSHIFT | Z | X | C | V | B | N | M | , | . | / | RSHFT | ↑ | END |
// -------------------------------------------------------------------------------------------------
// | LCTL | LGUI | LALT | SPACE | RALT | RGUI | RCTL | <- | ↓ | -> |
// -------------------------------------------------------------------------------------------------
bindings = <
&kp ESC &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp MINUS &kp EQUAL &kp BSPC &kp HOME
&kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp LBKT &kp RBKT &kp BSLH &kp PG_UP
&kp CLCK &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT &kp ENTER &kp PG_DN
&kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RSHFT &kp UP &kp END
&kp LCTRL &kp LGUI &kp LALT &kp SPACE &kp RALT &lt FUNC K_APP &kp RCTRL &kp LEFT &kp DOWN &kp RIGHT
>;
};
func {
// --------- Default QWERTY Layout ---------
// Layer 1 FUNC
// ---------------------------------------------------------------------------------------------------
// | ESC | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | DEL | HME |
// ---------------------------------------------------------------------------------------------------
// | --- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | scroll lock | pause | --- | PGU |
// ---------------------------------------------------------------------------------------------------
// | CAPS | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | --- | PGD |
// ---------------------------------------------------------------------------------------------------
// | LSHIFT | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | ----- | VOL UP | MUTE |
// ---------------------------------------------------------------------------------------------------
// | ---- | ---- | ---- | ---- | -- | MO 1 | -- | PREV | VOL DN | NEXT |
// ---------------------------------------------------------------------------------------------------
bindings = <
&kp GRAVE &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &kp DEL &trans
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &kp SLCK &kp PAUSE_BREAK &trans &trans
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &kp C_VOL_UP &kp C_MUTE
&trans &trans &trans &trans &trans &trans &trans &kp C_PREV &kp C_VOL_DN &kp C_NEXT
>;
};
};
};

View file

@ -0,0 +1,15 @@
identifier: kbdfans_tofu65_v2
name: KBDfans Tofu65 2.0
type: mcu
arch: arm
flash: 16384
ram: 264
toolchain:
- zephyr
- gnuarmemb
- xtools
supported:
- gpio
- usb_device
- hwinfo
- pwm

View file

@ -0,0 +1,10 @@
file_format: "1"
id: kbdfans_tofu65_v2
name: KBDfans Tofu65 2.0
type: board
arch: arm
features:
- keys
outputs:
- usb
url: https://kbdfans.com/collections/tofu65-2-0/products/tofu65-2-0

View file

@ -0,0 +1,20 @@
# Copyright (c) 2023 The ZMK Contributors
# SPDX-License-Identifier: MIT
CONFIG_SOC_SERIES_RP2XXX=y
CONFIG_SOC_RP2040=y
CONFIG_BOARD_KBDFANS_TOFU65_V2=y
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=125000000
# Enable USB CDC ACM logging for debugging
# CONFIG_ZMK_USB_LOGGING=y
# Enable reset by default
CONFIG_RESET=y
# Code partition needed to target the correct flash range
CONFIG_USE_DT_CODE_PARTITION=y
# Output UF2 by default, native bootloader supports it.
CONFIG_BUILD_OUTPUT_UF2=y

View file

@ -48,7 +48,7 @@
}; };
&i2c0 { &i2c0 {
compatible = "nordic,nrf-twim"; compatible = "nordic,nrf-twi";
pinctrl-0 = <&i2c0_default>; pinctrl-0 = <&i2c0_default>;
pinctrl-1 = <&i2c0_sleep>; pinctrl-1 = <&i2c0_sleep>;
pinctrl-names = "default", "sleep"; pinctrl-names = "default", "sleep";
@ -56,6 +56,7 @@
&uart0 { &uart0 {
compatible = "nordic,nrf-uarte"; compatible = "nordic,nrf-uarte";
current-speed = <115200>;
pinctrl-0 = <&uart0_default>; pinctrl-0 = <&uart0_default>;
pinctrl-1 = <&uart0_sleep>; pinctrl-1 = <&uart0_sleep>;
pinctrl-names = "default", "sleep"; pinctrl-names = "default", "sleep";

View file

@ -48,7 +48,7 @@
}; };
&i2c0 { &i2c0 {
compatible = "nordic,nrf-twim"; compatible = "nordic,nrf-twi";
pinctrl-0 = <&i2c0_default>; pinctrl-0 = <&i2c0_default>;
pinctrl-1 = <&i2c0_sleep>; pinctrl-1 = <&i2c0_sleep>;
pinctrl-names = "default", "sleep"; pinctrl-names = "default", "sleep";
@ -56,6 +56,7 @@
&uart0 { &uart0 {
compatible = "nordic,nrf-uarte"; compatible = "nordic,nrf-uarte";
current-speed = <115200>;
pinctrl-0 = <&uart0_default>; pinctrl-0 = <&uart0_default>;
pinctrl-1 = <&uart0_sleep>; pinctrl-1 = <&uart0_sleep>;
pinctrl-names = "default", "sleep"; pinctrl-names = "default", "sleep";

View file

@ -61,7 +61,7 @@
}; };
&i2c0 { &i2c0 {
compatible = "nordic,nrf-twim"; compatible = "nordic,nrf-twi";
pinctrl-0 = <&i2c0_default>; pinctrl-0 = <&i2c0_default>;
pinctrl-1 = <&i2c0_sleep>; pinctrl-1 = <&i2c0_sleep>;
pinctrl-names = "default", "sleep"; pinctrl-names = "default", "sleep";
@ -69,6 +69,7 @@
&uart0 { &uart0 {
compatible = "nordic,nrf-uarte"; compatible = "nordic,nrf-uarte";
current-speed = <115200>;
pinctrl-0 = <&uart0_default>; pinctrl-0 = <&uart0_default>;
pinctrl-1 = <&uart0_sleep>; pinctrl-1 = <&uart0_sleep>;
pinctrl-names = "default", "sleep"; pinctrl-names = "default", "sleep";

View file

@ -61,7 +61,7 @@
}; };
&i2c0 { &i2c0 {
compatible = "nordic,nrf-twim"; compatible = "nordic,nrf-twi";
pinctrl-0 = <&i2c0_default>; pinctrl-0 = <&i2c0_default>;
pinctrl-1 = <&i2c0_sleep>; pinctrl-1 = <&i2c0_sleep>;
pinctrl-names = "default", "sleep"; pinctrl-names = "default", "sleep";
@ -69,6 +69,7 @@
&uart0 { &uart0 {
compatible = "nordic,nrf-uarte"; compatible = "nordic,nrf-uarte";
current-speed = <115200>;
pinctrl-0 = <&uart0_default>; pinctrl-0 = <&uart0_default>;
pinctrl-1 = <&uart0_sleep>; pinctrl-1 = <&uart0_sleep>;
pinctrl-names = "default", "sleep"; pinctrl-names = "default", "sleep";

View file

@ -5,6 +5,7 @@ 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 debounce.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DRIVER kscan_gpio.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_MATRIX kscan_gpio_matrix.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_MATRIX kscan_gpio_matrix.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DIRECT kscan_gpio_direct.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DIRECT kscan_gpio_direct.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DEMUX kscan_gpio_demux.c) zephyr_library_sources_ifdef(CONFIG_ZMK_KSCAN_GPIO_DEMUX kscan_gpio_demux.c)

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include "kscan_gpio.h"
#include <stdlib.h>
static int compare_ports(const void *a, const void *b) {
const struct kscan_gpio *gpio_a = a;
const struct kscan_gpio *gpio_b = b;
return gpio_a->spec.port - gpio_b->spec.port;
}
void kscan_gpio_list_sort_by_port(struct kscan_gpio_list *list) {
qsort(list->gpios, list->len, sizeof(list->gpios[0]), compare_ports);
}
int kscan_gpio_pin_get(const struct kscan_gpio *gpio, struct kscan_gpio_port_state *state) {
if (gpio->spec.port != state->port) {
state->port = gpio->spec.port;
const int err = gpio_port_get(state->port, &state->value);
if (err) {
return err;
}
}
return (state->value & BIT(gpio->spec.pin)) != 0;
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/sys/util.h>
struct kscan_gpio {
struct gpio_dt_spec spec;
/** The index of the GPIO in the devicetree *-gpios array. */
size_t index;
};
/** GPIO_DT_SPEC_GET_BY_IDX(), but for a struct kscan_gpio. */
#define KSCAN_GPIO_GET_BY_IDX(node_id, prop, idx) \
((struct kscan_gpio){.spec = GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx), .index = idx})
struct kscan_gpio_list {
struct kscan_gpio *gpios;
size_t len;
};
/** Define a kscan_gpio_list from a compile-time GPIO array. */
#define KSCAN_GPIO_LIST(gpio_array) \
((struct kscan_gpio_list){.gpios = gpio_array, .len = ARRAY_SIZE(gpio_array)})
struct kscan_gpio_port_state {
const struct device *port;
gpio_port_value_t value;
};
/**
* Sorts a GPIO list by port so it can be used with kscan_gpio_pin_get().
*/
void kscan_gpio_list_sort_by_port(struct kscan_gpio_list *list);
/**
* Get logical level of an input pin.
*
* This is equivalent to gpio_pin_get() except that, when iterating through the
* pins in a list which is sorted by kscan_gpio_list_sort_by_port(), it only
* performs one read per port instead of one read per pin.
*
* @param gpio The input pin to read.
* @param state An object to track state between reads. Must be zero-initialized before the first
* use.
*
* @retval 1 If pin logical value is 1 / active.
* @retval 0 If pin logical value is 0 / inactive.
* @retval -EIO I/O error when accessing an external GPIO chip.
* @retval -EWOULDBLOCK if operation would block.
*/
int kscan_gpio_pin_get(const struct kscan_gpio *gpio, struct kscan_gpio_port_state *state);

View file

@ -5,6 +5,7 @@
*/ */
#include "debounce.h" #include "debounce.h"
#include "kscan_gpio.h"
#include <zephyr/device.h> #include <zephyr/device.h>
#include <zephyr/devicetree.h> #include <zephyr/devicetree.h>
@ -41,7 +42,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define INST_INPUTS_LEN(n) DT_INST_PROP_LEN(n, input_gpios) #define INST_INPUTS_LEN(n) DT_INST_PROP_LEN(n, input_gpios)
#define KSCAN_DIRECT_INPUT_CFG_INIT(idx, inst_idx) \ #define KSCAN_DIRECT_INPUT_CFG_INIT(idx, inst_idx) \
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), input_gpios, idx) KSCAN_GPIO_GET_BY_IDX(DT_DRV_INST(inst_idx), input_gpios, idx)
struct kscan_direct_irq_callback { struct kscan_direct_irq_callback {
const struct device *dev; const struct device *dev;
@ -50,6 +51,7 @@ struct kscan_direct_irq_callback {
struct kscan_direct_data { struct kscan_direct_data {
const struct device *dev; const struct device *dev;
struct kscan_gpio_list inputs;
kscan_callback_t callback; kscan_callback_t callback;
struct k_work_delayable work; struct k_work_delayable work;
#if USE_INTERRUPTS #if USE_INTERRUPTS
@ -62,17 +64,7 @@ struct kscan_direct_data {
struct debounce_state *pin_state; struct debounce_state *pin_state;
}; };
struct kscan_gpio_list {
const struct gpio_dt_spec *gpios;
size_t len;
};
/** Define a kscan_gpio_list from a compile-time GPIO array. */
#define KSCAN_GPIO_LIST(gpio_array) \
((struct kscan_gpio_list){.gpios = gpio_array, .len = ARRAY_SIZE(gpio_array)})
struct kscan_direct_config { struct kscan_direct_config {
struct kscan_gpio_list inputs;
struct debounce_config debounce_config; struct debounce_config debounce_config;
int32_t debounce_scan_period_ms; int32_t debounce_scan_period_ms;
int32_t poll_period_ms; int32_t poll_period_ms;
@ -81,10 +73,10 @@ struct kscan_direct_config {
#if USE_INTERRUPTS #if USE_INTERRUPTS
static int kscan_direct_interrupt_configure(const struct device *dev, const gpio_flags_t flags) { static int kscan_direct_interrupt_configure(const struct device *dev, const gpio_flags_t flags) {
const struct kscan_direct_config *config = dev->config; const struct kscan_direct_data *data = dev->data;
for (int i = 0; i < config->inputs.len; i++) { for (int i = 0; i < data->inputs.len; i++) {
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i]; const struct gpio_dt_spec *gpio = &data->inputs.gpios[i].spec;
int err = gpio_pin_interrupt_configure_dt(gpio, flags); int err = gpio_pin_interrupt_configure_dt(gpio, flags);
if (err) { if (err) {
@ -134,16 +126,16 @@ static gpio_flags_t kscan_gpio_get_extra_flags(const struct gpio_dt_spec *gpio,
static int kscan_inputs_set_flags(const struct kscan_gpio_list *inputs, static int kscan_inputs_set_flags(const struct kscan_gpio_list *inputs,
const struct gpio_dt_spec *active_gpio) { const struct gpio_dt_spec *active_gpio) {
gpio_flags_t extra_flags;
for (int i = 0; i < inputs->len; i++) { for (int i = 0; i < inputs->len; i++) {
extra_flags = GPIO_INPUT | kscan_gpio_get_extra_flags(&inputs->gpios[i], const bool active = &inputs->gpios[i].spec == active_gpio;
&inputs->gpios[i] == active_gpio); const gpio_flags_t extra_flags =
GPIO_INPUT | kscan_gpio_get_extra_flags(&inputs->gpios[i].spec, active);
LOG_DBG("Extra flags equal to: %d", extra_flags); LOG_DBG("Extra flags equal to: %d", extra_flags);
int err = gpio_pin_configure_dt(&inputs->gpios[i], extra_flags); int err = gpio_pin_configure_dt(&inputs->gpios[i].spec, extra_flags);
if (err) { if (err) {
LOG_ERR("Unable to configure flags on pin %d on %s", inputs->gpios[i].pin, LOG_ERR("Unable to configure flags on pin %d on %s", inputs->gpios[i].spec.pin,
inputs->gpios[i].port->name); inputs->gpios[i].spec.port->name);
return err; return err;
} }
} }
@ -179,28 +171,35 @@ static int kscan_direct_read(const struct device *dev) {
const struct kscan_direct_config *config = dev->config; const struct kscan_direct_config *config = dev->config;
// Read the inputs. // Read the inputs.
for (int i = 0; i < config->inputs.len; i++) { struct kscan_gpio_port_state state = {0};
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i];
const bool active = gpio_pin_get_dt(gpio); for (int i = 0; i < data->inputs.len; i++) {
const struct kscan_gpio *gpio = &data->inputs.gpios[i];
debounce_update(&data->pin_state[i], active, config->debounce_scan_period_ms, const int active = kscan_gpio_pin_get(gpio, &state);
if (active < 0) {
LOG_ERR("Failed to read port %s: %i", gpio->spec.port->name, active);
return active;
}
debounce_update(&data->pin_state[gpio->index], active, config->debounce_scan_period_ms,
&config->debounce_config); &config->debounce_config);
} }
// Process the new state. // Process the new state.
bool continue_scan = false; bool continue_scan = false;
for (int i = 0; i < config->inputs.len; i++) { for (int i = 0; i < data->inputs.len; i++) {
struct debounce_state *state = &data->pin_state[i]; const struct kscan_gpio *gpio = &data->inputs.gpios[i];
struct debounce_state *state = &data->pin_state[gpio->index];
if (debounce_get_changed(state)) { if (debounce_get_changed(state)) {
const bool pressed = debounce_is_pressed(state); const bool pressed = debounce_is_pressed(state);
LOG_DBG("Sending event at 0,%i state %s", i, pressed ? "on" : "off"); LOG_DBG("Sending event at 0,%i state %s", gpio->index, pressed ? "on" : "off");
data->callback(dev, 0, i, pressed); data->callback(dev, 0, gpio->index, pressed);
if (config->toggle_mode && pressed) { if (config->toggle_mode && pressed) {
kscan_inputs_set_flags(&config->inputs, &config->inputs.gpios[i]); kscan_inputs_set_flags(&data->inputs, &gpio->spec);
} }
} }
@ -289,10 +288,11 @@ static int kscan_direct_init_input_inst(const struct device *dev, const struct g
} }
static int kscan_direct_init_inputs(const struct device *dev) { static int kscan_direct_init_inputs(const struct device *dev) {
const struct kscan_direct_data *data = dev->data;
const struct kscan_direct_config *config = dev->config; const struct kscan_direct_config *config = dev->config;
for (int i = 0; i < config->inputs.len; i++) { for (int i = 0; i < data->inputs.len; i++) {
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i]; const struct gpio_dt_spec *gpio = &data->inputs.gpios[i].spec;
int err = kscan_direct_init_input_inst(dev, gpio, i, config->toggle_mode); int err = kscan_direct_init_input_inst(dev, gpio, i, config->toggle_mode);
if (err) { if (err) {
return err; return err;
@ -307,6 +307,9 @@ static int kscan_direct_init(const struct device *dev) {
data->dev = dev; data->dev = dev;
// Sort inputs by port so we can read each port just once per scan.
kscan_gpio_list_sort_by_port(&data->inputs);
kscan_direct_init_inputs(dev); kscan_direct_init_inputs(dev);
k_work_init_delayable(&data->work, kscan_direct_work_handler); k_work_init_delayable(&data->work, kscan_direct_work_handler);
@ -326,7 +329,7 @@ static const struct kscan_driver_api kscan_direct_api = {
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(n) <= DEBOUNCE_COUNTER_MAX, \ BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(n) <= DEBOUNCE_COUNTER_MAX, \
"ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \ "ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \
\ \
static const struct gpio_dt_spec kscan_direct_inputs_##n[] = { \ static struct kscan_gpio kscan_direct_inputs_##n[] = { \
LISTIFY(INST_INPUTS_LEN(n), KSCAN_DIRECT_INPUT_CFG_INIT, (, ), n)}; \ LISTIFY(INST_INPUTS_LEN(n), KSCAN_DIRECT_INPUT_CFG_INIT, (, ), n)}; \
\ \
static struct debounce_state kscan_direct_state_##n[INST_INPUTS_LEN(n)]; \ static struct debounce_state kscan_direct_state_##n[INST_INPUTS_LEN(n)]; \
@ -335,10 +338,11 @@ static const struct kscan_driver_api kscan_direct_api = {
(static struct kscan_direct_irq_callback kscan_direct_irqs_##n[INST_INPUTS_LEN(n)];)) \ (static struct kscan_direct_irq_callback kscan_direct_irqs_##n[INST_INPUTS_LEN(n)];)) \
\ \
static struct kscan_direct_data kscan_direct_data_##n = { \ static struct kscan_direct_data kscan_direct_data_##n = { \
.pin_state = kscan_direct_state_##n, COND_INTERRUPTS((.irqs = kscan_direct_irqs_##n, ))}; \ .inputs = KSCAN_GPIO_LIST(kscan_direct_inputs_##n), \
.pin_state = kscan_direct_state_##n, \
COND_INTERRUPTS((.irqs = kscan_direct_irqs_##n, ))}; \
\ \
static struct kscan_direct_config kscan_direct_config_##n = { \ static struct kscan_direct_config kscan_direct_config_##n = { \
.inputs = KSCAN_GPIO_LIST(kscan_direct_inputs_##n), \
.debounce_config = \ .debounce_config = \
{ \ { \
.debounce_press_ms = INST_DEBOUNCE_PRESS_MS(n), \ .debounce_press_ms = INST_DEBOUNCE_PRESS_MS(n), \

View file

@ -5,6 +5,7 @@
*/ */
#include "debounce.h" #include "debounce.h"
#include "kscan_gpio.h"
#include <zephyr/device.h> #include <zephyr/device.h>
#include <zephyr/devicetree.h> #include <zephyr/devicetree.h>
@ -50,9 +51,9 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, pollcode, intcode) COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, pollcode, intcode)
#define KSCAN_GPIO_ROW_CFG_INIT(idx, inst_idx) \ #define KSCAN_GPIO_ROW_CFG_INIT(idx, inst_idx) \
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), row_gpios, idx) KSCAN_GPIO_GET_BY_IDX(DT_DRV_INST(inst_idx), row_gpios, idx)
#define KSCAN_GPIO_COL_CFG_INIT(idx, inst_idx) \ #define KSCAN_GPIO_COL_CFG_INIT(idx, inst_idx) \
GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), col_gpios, idx) KSCAN_GPIO_GET_BY_IDX(DT_DRV_INST(inst_idx), col_gpios, idx)
enum kscan_diode_direction { enum kscan_diode_direction {
KSCAN_ROW2COL, KSCAN_ROW2COL,
@ -66,6 +67,7 @@ struct kscan_matrix_irq_callback {
struct kscan_matrix_data { struct kscan_matrix_data {
const struct device *dev; const struct device *dev;
struct kscan_gpio_list inputs;
kscan_callback_t callback; kscan_callback_t callback;
struct k_work_delayable work; struct k_work_delayable work;
#if USE_INTERRUPTS #if USE_INTERRUPTS
@ -76,26 +78,16 @@ struct kscan_matrix_data {
int64_t scan_time; 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 * config->cols)
*/ */
struct debounce_state *matrix_state; struct debounce_state *matrix_state;
}; };
struct kscan_gpio_list {
const struct gpio_dt_spec *gpios;
size_t len;
};
/** Define a kscan_gpio_list from a compile-time GPIO array. */
#define KSCAN_GPIO_LIST(gpio_array) \
((struct kscan_gpio_list){.gpios = gpio_array, .len = ARRAY_SIZE(gpio_array)})
struct kscan_matrix_config { struct kscan_matrix_config {
struct kscan_gpio_list rows;
struct kscan_gpio_list cols;
struct kscan_gpio_list inputs;
struct kscan_gpio_list outputs; struct kscan_gpio_list outputs;
struct debounce_config debounce_config; struct debounce_config debounce_config;
size_t rows;
size_t cols;
int32_t debounce_scan_period_ms; 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;
@ -105,10 +97,10 @@ struct kscan_matrix_config {
* Get the index into a matrix state array from a row and column. * Get the index into a matrix state array from a row and column.
*/ */
static int state_index_rc(const struct kscan_matrix_config *config, const int row, const int col) { static int state_index_rc(const struct kscan_matrix_config *config, const int row, const int col) {
__ASSERT(row < config->rows.len, "Invalid row %i", row); __ASSERT(row < config->rows, "Invalid row %i", row);
__ASSERT(col < config->cols.len, "Invalid column %i", col); __ASSERT(col < config->cols, "Invalid column %i", col);
return (col * config->rows.len) + row; return (col * config->rows) + row;
} }
/** /**
@ -125,7 +117,7 @@ static int kscan_matrix_set_all_outputs(const struct device *dev, const int valu
const struct kscan_matrix_config *config = dev->config; const struct kscan_matrix_config *config = dev->config;
for (int i = 0; i < config->outputs.len; i++) { for (int i = 0; i < config->outputs.len; i++) {
const struct gpio_dt_spec *gpio = &config->outputs.gpios[i]; const struct gpio_dt_spec *gpio = &config->outputs.gpios[i].spec;
int err = gpio_pin_set_dt(gpio, value); int err = gpio_pin_set_dt(gpio, value);
if (err) { if (err) {
@ -139,10 +131,10 @@ static int kscan_matrix_set_all_outputs(const struct device *dev, const int valu
#if USE_INTERRUPTS #if USE_INTERRUPTS
static int kscan_matrix_interrupt_configure(const struct device *dev, const gpio_flags_t flags) { static int kscan_matrix_interrupt_configure(const struct device *dev, const gpio_flags_t flags) {
const struct kscan_matrix_config *config = dev->config; const struct kscan_matrix_data *data = dev->data;
for (int i = 0; i < config->inputs.len; i++) { for (int i = 0; i < data->inputs.len; i++) {
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i]; const struct gpio_dt_spec *gpio = &data->inputs.gpios[i].spec;
int err = gpio_pin_interrupt_configure_dt(gpio, flags); int err = gpio_pin_interrupt_configure_dt(gpio, flags);
if (err) { if (err) {
@ -226,32 +218,37 @@ static int kscan_matrix_read(const struct device *dev) {
const struct kscan_matrix_config *config = dev->config; const struct kscan_matrix_config *config = dev->config;
// Scan the matrix. // Scan the matrix.
for (int o = 0; o < config->outputs.len; o++) { for (int i = 0; i < config->outputs.len; i++) {
const struct gpio_dt_spec *out_gpio = &config->outputs.gpios[o]; const struct kscan_gpio *out_gpio = &config->outputs.gpios[i];
int err = gpio_pin_set_dt(out_gpio, 1); int err = gpio_pin_set_dt(&out_gpio->spec, 1);
if (err) { if (err) {
LOG_ERR("Failed to set output %i active: %i", o, err); LOG_ERR("Failed to set output %i active: %i", out_gpio->index, err);
return err; return err;
} }
#if CONFIG_ZMK_KSCAN_MATRIX_WAIT_BEFORE_INPUTS > 0 #if CONFIG_ZMK_KSCAN_MATRIX_WAIT_BEFORE_INPUTS > 0
k_busy_wait(CONFIG_ZMK_KSCAN_MATRIX_WAIT_BEFORE_INPUTS); k_busy_wait(CONFIG_ZMK_KSCAN_MATRIX_WAIT_BEFORE_INPUTS);
#endif #endif
struct kscan_gpio_port_state state = {0};
for (int i = 0; i < config->inputs.len; i++) { for (int j = 0; j < data->inputs.len; j++) {
const struct gpio_dt_spec *in_gpio = &config->inputs.gpios[i]; const struct kscan_gpio *in_gpio = &data->inputs.gpios[j];
const int index = state_index_io(config, i, o); const int index = state_index_io(config, in_gpio->index, out_gpio->index);
const bool active = gpio_pin_get_dt(in_gpio); const int active = kscan_gpio_pin_get(in_gpio, &state);
if (active < 0) {
LOG_ERR("Failed to read port %s: %i", in_gpio->spec.port->name, active);
return active;
}
debounce_update(&data->matrix_state[index], active, config->debounce_scan_period_ms, debounce_update(&data->matrix_state[index], active, config->debounce_scan_period_ms,
&config->debounce_config); &config->debounce_config);
} }
err = gpio_pin_set_dt(out_gpio, 0); err = gpio_pin_set_dt(&out_gpio->spec, 0);
if (err) { if (err) {
LOG_ERR("Failed to set output %i inactive: %i", o, err); LOG_ERR("Failed to set output %i inactive: %i", out_gpio->index, err);
return err; return err;
} }
@ -263,8 +260,8 @@ static int kscan_matrix_read(const struct device *dev) {
// Process the new state. // Process the new state.
bool continue_scan = false; bool continue_scan = false;
for (int r = 0; r < config->rows.len; r++) { for (int r = 0; r < config->rows; r++) {
for (int c = 0; c < config->cols.len; c++) { for (int c = 0; c < config->cols; c++) {
const int index = state_index_rc(config, r, c); const int index = state_index_rc(config, r, c);
struct debounce_state *state = &data->matrix_state[index]; struct debounce_state *state = &data->matrix_state[index];
@ -329,28 +326,28 @@ static int kscan_matrix_disable(const struct device *dev) {
#endif #endif
} }
static int kscan_matrix_init_input_inst(const struct device *dev, const struct gpio_dt_spec *gpio, static int kscan_matrix_init_input_inst(const struct device *dev, const struct kscan_gpio *gpio) {
const int index) { if (!device_is_ready(gpio->spec.port)) {
if (!device_is_ready(gpio->port)) { LOG_ERR("GPIO is not ready: %s", gpio->spec.port->name);
LOG_ERR("GPIO is not ready: %s", gpio->port->name);
return -ENODEV; return -ENODEV;
} }
int err = gpio_pin_configure_dt(gpio, GPIO_INPUT); int err = gpio_pin_configure_dt(&gpio->spec, GPIO_INPUT);
if (err) { if (err) {
LOG_ERR("Unable to configure pin %u on %s for input", gpio->pin, gpio->port->name); LOG_ERR("Unable to configure pin %u on %s for input", gpio->spec.pin,
gpio->spec.port->name);
return err; return err;
} }
LOG_DBG("Configured pin %u on %s for input", gpio->pin, gpio->port->name); LOG_DBG("Configured pin %u on %s for input", gpio->spec.pin, gpio->spec.port->name);
#if USE_INTERRUPTS #if USE_INTERRUPTS
struct kscan_matrix_data *data = dev->data; struct kscan_matrix_data *data = dev->data;
struct kscan_matrix_irq_callback *irq = &data->irqs[index]; struct kscan_matrix_irq_callback *irq = &data->irqs[gpio->index];
irq->dev = dev; irq->dev = dev;
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->spec.pin));
err = gpio_add_callback(gpio->port, &irq->callback); err = gpio_add_callback(gpio->spec.port, &irq->callback);
if (err) { if (err) {
LOG_ERR("Error adding the callback to the input device: %i", err); LOG_ERR("Error adding the callback to the input device: %i", err);
return err; return err;
@ -361,11 +358,11 @@ static int kscan_matrix_init_input_inst(const struct device *dev, const struct g
} }
static int kscan_matrix_init_inputs(const struct device *dev) { static int kscan_matrix_init_inputs(const struct device *dev) {
const struct kscan_matrix_config *config = dev->config; const struct kscan_matrix_data *data = dev->data;
for (int i = 0; i < config->inputs.len; i++) { for (int i = 0; i < data->inputs.len; i++) {
const struct gpio_dt_spec *gpio = &config->inputs.gpios[i]; const struct kscan_gpio *gpio = &data->inputs.gpios[i];
int err = kscan_matrix_init_input_inst(dev, gpio, i); int err = kscan_matrix_init_input_inst(dev, gpio);
if (err) { if (err) {
return err; return err;
} }
@ -396,7 +393,7 @@ static int kscan_matrix_init_outputs(const struct device *dev) {
const struct kscan_matrix_config *config = dev->config; const struct kscan_matrix_config *config = dev->config;
for (int i = 0; i < config->outputs.len; i++) { for (int i = 0; i < config->outputs.len; i++) {
const struct gpio_dt_spec *gpio = &config->outputs.gpios[i]; const struct gpio_dt_spec *gpio = &config->outputs.gpios[i].spec;
int err = kscan_matrix_init_output_inst(dev, gpio); int err = kscan_matrix_init_output_inst(dev, gpio);
if (err) { if (err) {
return err; return err;
@ -411,6 +408,9 @@ static int kscan_matrix_init(const struct device *dev) {
data->dev = dev; data->dev = dev;
// Sort inputs by port so we can read each port just once per scan.
kscan_gpio_list_sort_by_port(&data->inputs);
kscan_matrix_init_inputs(dev); kscan_matrix_init_inputs(dev);
kscan_matrix_init_outputs(dev); kscan_matrix_init_outputs(dev);
kscan_matrix_set_all_outputs(dev, 0); kscan_matrix_set_all_outputs(dev, 0);
@ -432,10 +432,10 @@ static const struct kscan_driver_api kscan_matrix_api = {
BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(n) <= DEBOUNCE_COUNTER_MAX, \ BUILD_ASSERT(INST_DEBOUNCE_RELEASE_MS(n) <= DEBOUNCE_COUNTER_MAX, \
"ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \ "ZMK_KSCAN_DEBOUNCE_RELEASE_MS or debounce-release-ms is too large"); \
\ \
static const struct gpio_dt_spec kscan_matrix_rows_##n[] = { \ static struct kscan_gpio kscan_matrix_rows_##n[] = { \
LISTIFY(INST_ROWS_LEN(n), KSCAN_GPIO_ROW_CFG_INIT, (, ), n)}; \ LISTIFY(INST_ROWS_LEN(n), KSCAN_GPIO_ROW_CFG_INIT, (, ), n)}; \
\ \
static const struct gpio_dt_spec kscan_matrix_cols_##n[] = { \ static struct kscan_gpio kscan_matrix_cols_##n[] = { \
LISTIFY(INST_COLS_LEN(n), KSCAN_GPIO_COL_CFG_INIT, (, ), n)}; \ LISTIFY(INST_COLS_LEN(n), KSCAN_GPIO_COL_CFG_INIT, (, ), n)}; \
\ \
static struct debounce_state kscan_matrix_state_##n[INST_MATRIX_LEN(n)]; \ static struct debounce_state kscan_matrix_state_##n[INST_MATRIX_LEN(n)]; \
@ -444,14 +444,14 @@ static const struct kscan_driver_api kscan_matrix_api = {
(static struct kscan_matrix_irq_callback kscan_matrix_irqs_##n[INST_INPUTS_LEN(n)];)) \ (static struct kscan_matrix_irq_callback kscan_matrix_irqs_##n[INST_INPUTS_LEN(n)];)) \
\ \
static struct kscan_matrix_data kscan_matrix_data_##n = { \ static struct kscan_matrix_data kscan_matrix_data_##n = { \
.inputs = \
KSCAN_GPIO_LIST(COND_DIODE_DIR(n, (kscan_matrix_cols_##n), (kscan_matrix_rows_##n))), \
.matrix_state = kscan_matrix_state_##n, \ .matrix_state = kscan_matrix_state_##n, \
COND_INTERRUPTS((.irqs = kscan_matrix_irqs_##n, ))}; \ COND_INTERRUPTS((.irqs = kscan_matrix_irqs_##n, ))}; \
\ \
static struct kscan_matrix_config kscan_matrix_config_##n = { \ static struct kscan_matrix_config kscan_matrix_config_##n = { \
.rows = KSCAN_GPIO_LIST(kscan_matrix_rows_##n), \ .rows = ARRAY_SIZE(kscan_matrix_rows_##n), \
.cols = KSCAN_GPIO_LIST(kscan_matrix_cols_##n), \ .cols = ARRAY_SIZE(kscan_matrix_cols_##n), \
.inputs = \
KSCAN_GPIO_LIST(COND_DIODE_DIR(n, (kscan_matrix_cols_##n), (kscan_matrix_rows_##n))), \
.outputs = \ .outputs = \
KSCAN_GPIO_LIST(COND_DIODE_DIR(n, (kscan_matrix_rows_##n), (kscan_matrix_cols_##n))), \ KSCAN_GPIO_LIST(COND_DIODE_DIR(n, (kscan_matrix_rows_##n), (kscan_matrix_cols_##n))), \
.debounce_config = \ .debounce_config = \

View file

@ -15,7 +15,7 @@
#if ZMK_BLE_IS_CENTRAL #if ZMK_BLE_IS_CENTRAL
#define ZMK_BLE_PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - 1) #define ZMK_BLE_PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - 1)
#define ZMK_BLE_SPLIT_PERIPHERAL_COUNT 1 #define ZMK_SPLIT_BLE_PERIPHERAL_COUNT 1
#else #else
#define ZMK_BLE_PROFILE_COUNT CONFIG_BT_MAX_PAIRED #define ZMK_BLE_PROFILE_COUNT CONFIG_BT_MAX_PAIRED
#endif #endif

View file

@ -118,6 +118,7 @@ void set_profile_address(uint8_t index, const bt_addr_le_t *addr) {
bool zmk_ble_active_profile_is_connected() { bool zmk_ble_active_profile_is_connected() {
struct bt_conn *conn; struct bt_conn *conn;
struct bt_conn_info info;
bt_addr_le_t *addr = zmk_ble_active_profile_addr(); bt_addr_le_t *addr = zmk_ble_active_profile_addr();
if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) { if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) {
return false; return false;
@ -125,9 +126,11 @@ bool zmk_ble_active_profile_is_connected() {
return false; return false;
} }
bt_conn_get_info(conn, &info);
bt_conn_unref(conn); bt_conn_unref(conn);
return true; return info.state == BT_CONN_STATE_CONNECTED;
} }
#define CHECKED_ADV_STOP() \ #define CHECKED_ADV_STOP() \

View file

@ -6,9 +6,8 @@ menuconfig ZMK_DISPLAY
default n default n
select DISPLAY select DISPLAY
select LVGL select LVGL
select LV_THEMES
select LV_THEME_MONO
select LV_CONF_MINIMAL select LV_CONF_MINIMAL
imply LV_USE_THEME_MONO
if ZMK_DISPLAY if ZMK_DISPLAY
@ -16,6 +15,13 @@ config ZMK_DISPLAY_BLANK_ON_IDLE
bool "Blank display on idle" bool "Blank display on idle"
default y if SSD1306 default y if SSD1306
if LV_USE_THEME_MONO
config ZMK_DISPLAY_INVERT
bool "Invert display colors"
endif
choice LV_TXT_ENC choice LV_TXT_ENC
default LV_TXT_ENC_UTF8 default LV_TXT_ENC_UTF8

View file

@ -91,7 +91,8 @@ int zmk_display_is_initialized() { return initialized; }
static void initialize_theme() { static void initialize_theme() {
#if IS_ENABLED(CONFIG_LV_USE_THEME_MONO) #if IS_ENABLED(CONFIG_LV_USE_THEME_MONO)
lv_disp_t *disp = lv_disp_get_default(); lv_disp_t *disp = lv_disp_get_default();
lv_theme_t *theme = lv_theme_mono_init(disp, false, CONFIG_LV_FONT_DEFAULT); lv_theme_t *theme =
lv_theme_mono_init(disp, IS_ENABLED(CONFIG_ZMK_DISPLAY_INVERT), CONFIG_LV_FONT_DEFAULT);
theme->font_small = CONFIG_ZMK_LV_FONT_DEFAULT_SMALL; theme->font_small = CONFIG_ZMK_LV_FONT_DEFAULT_SMALL;
disp->theme = theme; disp->theme = theme;

View file

@ -218,7 +218,7 @@ int zmk_keymap_apply_position_state(uint8_t source, int layer, uint32_t position
#endif #endif
case BEHAVIOR_LOCALITY_GLOBAL: case BEHAVIOR_LOCALITY_GLOBAL:
#if ZMK_BLE_IS_CENTRAL #if ZMK_BLE_IS_CENTRAL
for (int i = 0; i < ZMK_BLE_SPLIT_PERIPHERAL_COUNT; i++) { for (int i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT; i++) {
zmk_split_bt_invoke_behavior(i, &binding, event, pressed); zmk_split_bt_invoke_behavior(i, &binding, event, pressed);
} }
#endif #endif

View file

@ -21,11 +21,11 @@ config ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE
int "Max number of key position state events to queue when received from peripherals" int "Max number of key position state events to queue when received from peripherals"
default 5 default 5
config ZMK_BLE_SPLIT_CENTRAL_SPLIT_RUN_STACK_SIZE config ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_STACK_SIZE
int "BLE split central write thread stack size" int "BLE split central write thread stack size"
default 512 default 512
config ZMK_BLE_SPLIT_CENTRAL_SPLIT_RUN_QUEUE_SIZE config ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_QUEUE_SIZE
int "Max number of behavior run events to queue to send to the peripheral(s)" int "Max number of behavior run events to queue to send to the peripheral(s)"
default 5 default 5

View file

@ -47,7 +47,7 @@ struct peripheral_slot {
uint8_t changed_positions[POSITION_STATE_DATA_LEN]; uint8_t changed_positions[POSITION_STATE_DATA_LEN];
}; };
static struct peripheral_slot peripherals[ZMK_BLE_SPLIT_PERIPHERAL_COUNT]; static struct peripheral_slot peripherals[ZMK_SPLIT_BLE_PERIPHERAL_COUNT];
static const struct bt_uuid_128 split_service_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);
@ -65,7 +65,7 @@ void peripheral_event_work_callback(struct k_work *work) {
K_WORK_DEFINE(peripheral_event_work, peripheral_event_work_callback); K_WORK_DEFINE(peripheral_event_work, peripheral_event_work_callback);
int peripheral_slot_index_for_conn(struct bt_conn *conn) { int peripheral_slot_index_for_conn(struct bt_conn *conn) {
for (int i = 0; i < ZMK_BLE_SPLIT_PERIPHERAL_COUNT; i++) { for (int i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT; i++) {
if (peripherals[i].conn == conn) { if (peripherals[i].conn == conn) {
return i; return i;
} }
@ -84,7 +84,7 @@ struct peripheral_slot *peripheral_slot_for_conn(struct bt_conn *conn) {
} }
int release_peripheral_slot(int index) { int release_peripheral_slot(int index) {
if (index < 0 || index >= ZMK_BLE_SPLIT_PERIPHERAL_COUNT) { if (index < 0 || index >= ZMK_SPLIT_BLE_PERIPHERAL_COUNT) {
return -EINVAL; return -EINVAL;
} }
@ -131,7 +131,7 @@ int release_peripheral_slot(int index) {
} }
int reserve_peripheral_slot() { int reserve_peripheral_slot() {
for (int i = 0; i < ZMK_BLE_SPLIT_PERIPHERAL_COUNT; i++) { for (int i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT; i++) {
if (peripherals[i].state == PERIPHERAL_SLOT_STATE_OPEN) { if (peripherals[i].state == PERIPHERAL_SLOT_STATE_OPEN) {
// Be sure the slot is fully reinitialized. // Be sure the slot is fully reinitialized.
release_peripheral_slot(i); release_peripheral_slot(i);
@ -504,7 +504,7 @@ static struct bt_conn_cb conn_callbacks = {
}; };
K_THREAD_STACK_DEFINE(split_central_split_run_q_stack, K_THREAD_STACK_DEFINE(split_central_split_run_q_stack,
CONFIG_ZMK_BLE_SPLIT_CENTRAL_SPLIT_RUN_STACK_SIZE); CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_STACK_SIZE);
struct k_work_q split_central_split_run_q; struct k_work_q split_central_split_run_q;
@ -515,7 +515,7 @@ struct zmk_split_run_behavior_payload_wrapper {
K_MSGQ_DEFINE(zmk_split_central_split_run_msgq, K_MSGQ_DEFINE(zmk_split_central_split_run_msgq,
sizeof(struct zmk_split_run_behavior_payload_wrapper), sizeof(struct zmk_split_run_behavior_payload_wrapper),
CONFIG_ZMK_BLE_SPLIT_CENTRAL_SPLIT_RUN_QUEUE_SIZE, 4); CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_QUEUE_SIZE, 4);
void split_central_split_run_callback(struct k_work *work) { void split_central_split_run_callback(struct k_work *work) {
struct zmk_split_run_behavior_payload_wrapper payload_wrapper; struct zmk_split_run_behavior_payload_wrapper payload_wrapper;

View file

@ -133,6 +133,23 @@ See the following example, which uses a hold-tap behavior definition, configured
By default, `hold-trigger-key-positions` are evaluated upon the first _key press_ after By default, `hold-trigger-key-positions` are evaluated upon the first _key press_ after
the hold-tap. For homerow mods, this is not always ideal, because it prevents combining multiple modifiers unless they are included in `hold-trigger-key-positions`. To overwrite this behavior, one can set `hold-trigger-on-release`. If set to true, the evaluation of `hold-trigger-key-positions` gets delayed until _key release_. This allows combining multiple modifiers when the next key is _held_, while still deciding the hold-tap in favor of a tap when the next key is _tapped_. the hold-tap. For homerow mods, this is not always ideal, because it prevents combining multiple modifiers unless they are included in `hold-trigger-key-positions`. To overwrite this behavior, one can set `hold-trigger-on-release`. If set to true, the evaluation of `hold-trigger-key-positions` gets delayed until _key release_. This allows combining multiple modifiers when the next key is _held_, while still deciding the hold-tap in favor of a tap when the next key is _tapped_.
#### Using different behavior types with hold-taps
You can create instances of hold-taps invoking most [behavior types](../features/keymaps.md#behaviors) for hold or tap actions, by referencing their node labels in the `bindings` value.
The two parameters that are passed to the hold-tap in your keymap will be forwarded to the referred behaviors, first one to the hold behavior and second one to the tap.
If you use behaviors that accept no parameters such as [mod-morphs](mod-morph.md) or [macros](macros.md), you can pass a dummy parameter value such as `0` to the hold-tap when you use it in your keymap.
For instance, a hold-tap with node label `caps` and `bindings = <&kp>, <&caps_word>;` can be used in the keymap as below to send the caps lock keycode on hold and invoke the [caps word behavior](caps-word.md) on tap:
```
&caps CAPS 0
```
:::info
You cannot use behaviors that expect more than one parameter such as [`&bt`](bluetooth.md) and [`&rgb_ug`](underglow.md) with hold-taps, due to the limitations of the [devicetree keymap format](../config/index.md#devicetree-files).
One workaround is to create a [macro](macros.md) that invokes those behaviors and use the macro as the hold or tap action.
:::
### Example Use-Cases ### Example Use-Cases
<Tabs <Tabs

View file

@ -33,7 +33,7 @@ A macro definition looks like:
:::note :::note
The text before the colon (`:`) in the declaration of the macro node is the "node label", and is the text The text before the colon (`:`) in the declaration of the macro node is the "node label", and is the text
used to reference the macro in your keymap used to reference the macro in your keymap.
::: :::
The macro can then be bound in your keymap by referencing it by the label `&zed_em_kay`, e.g.: The macro can then be bound in your keymap by referencing it by the label `&zed_em_kay`, e.g.:
@ -44,6 +44,11 @@ The macro can then be bound in your keymap by referencing it by the label `&zed_
}; };
``` ```
:::note
For use cases involving sending a single keycode with modifiers, for instance ctrl+tab, the [key press behavior](key-press.md)
with [modifier functions](../codes/modifiers.mdx#modifier-functions) can be used instead of a macro.
:::
### Bindings ### Bindings
Like [hold-taps](/docs/behaviors/hold-tap), macros are created by composing other behaviors, and any of those behaviors can Like [hold-taps](/docs/behaviors/hold-tap), macros are created by composing other behaviors, and any of those behaviors can

View file

@ -17,12 +17,15 @@ Definition files:
| Config | Type | Description | Default | | Config | Type | Description | Default |
| -------------------------------------------------- | ---- | -------------------------------------------------------------- | ------- | | -------------------------------------------------- | ---- | -------------------------------------------------------------- | ------- |
| `CONFIG_ZMK_DISPLAY` | bool | Enable support for displays | n | | `CONFIG_ZMK_DISPLAY` | bool | Enable support for displays | n |
| `CONFIG_ZMK_DISPLAY_INVERT` | bool | Invert display colors from black-on-white to white-on-black | n |
| `CONFIG_ZMK_WIDGET_LAYER_STATUS` | bool | Enable a widget to show the highest, active layer | y | | `CONFIG_ZMK_WIDGET_LAYER_STATUS` | bool | Enable a widget to show the highest, active layer | y |
| `CONFIG_ZMK_WIDGET_BATTERY_STATUS` | bool | Enable a widget to show battery charge information | y | | `CONFIG_ZMK_WIDGET_BATTERY_STATUS` | bool | Enable a widget to show battery charge information | y |
| `CONFIG_ZMK_WIDGET_BATTERY_STATUS_SHOW_PERCENTAGE` | bool | If battery widget is enabled, show percentage instead of icons | n | | `CONFIG_ZMK_WIDGET_BATTERY_STATUS_SHOW_PERCENTAGE` | bool | If battery widget is enabled, show percentage instead of icons | n |
| `CONFIG_ZMK_WIDGET_OUTPUT_STATUS` | bool | Enable a widget to show the current output (USB/BLE) | y | | `CONFIG_ZMK_WIDGET_OUTPUT_STATUS` | bool | Enable a widget to show the current output (USB/BLE) | y |
| `CONFIG_ZMK_WIDGET_WPM_STATUS` | bool | Enable a widget to show words per minute | n | | `CONFIG_ZMK_WIDGET_WPM_STATUS` | bool | Enable a widget to show words per minute | n |
Note that `CONFIG_ZMK_DISPLAY_INVERT` setting might not work as expected with custom status screens that utilize images.
If `CONFIG_ZMK_DISPLAY` is enabled, exactly zero or one of the following options must be set to `y`. The first option is used if none are set. If `CONFIG_ZMK_DISPLAY` is enabled, exactly zero or one of the following options must be set to `y`. The first option is used if none are set.
| Config | Description | | Config | Description |

View file

@ -97,8 +97,8 @@ Following split keyboard settings are defined in [zmk/app/src/split/Kconfig](htt
| `CONFIG_ZMK_SPLIT_BLE` | bool | Use BLE to communicate between split keyboard halves | y | | `CONFIG_ZMK_SPLIT_BLE` | bool | Use BLE to communicate between split keyboard halves | y |
| `CONFIG_ZMK_SPLIT_ROLE_CENTRAL` | bool | `y` for central device, `n` for peripheral | | | `CONFIG_ZMK_SPLIT_ROLE_CENTRAL` | bool | `y` for central device, `n` for peripheral | |
| `CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue when received from peripherals | 5 | | `CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue when received from peripherals | 5 |
| `CONFIG_ZMK_BLE_SPLIT_CENTRAL_SPLIT_RUN_STACK_SIZE` | int | Stack size of the BLE split central write thread | 512 | | `CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_STACK_SIZE` | int | Stack size of the BLE split central write thread | 512 |
| `CONFIG_ZMK_BLE_SPLIT_CENTRAL_SPLIT_RUN_QUEUE_SIZE` | int | Max number of behavior run events to queue to send to the peripheral(s) | 5 | | `CONFIG_ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_QUEUE_SIZE` | int | Max number of behavior run events to queue to send to the peripheral(s) | 5 |
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE` | int | Stack size of the BLE split peripheral notify thread | 650 | | `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE` | int | Stack size of the BLE split peripheral notify thread | 650 |
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY` | int | Priority of the BLE split peripheral notify thread | 5 | | `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_PRIORITY` | int | Priority of the BLE split peripheral notify thread | 5 |
| `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue to send to the central | 10 | | `CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE` | int | Max number of key state events to queue to send to the central | 10 |

View file

@ -38,6 +38,24 @@ Management of the bluetooth in ZMK is accomplished using the [`&bt` behavior](..
## Troubleshooting ## Troubleshooting
### Connectivity Issues
Some users may experience a poor connection between the keyboard and the host. This might be due to poor quality BLE hardware, a metal enclosure on the keyboard or host, or the distance between them. Increasing the transmit power of the keyboard's BLE radio may reduce the severity of this problem. To do this, set the `CONFIG_BT_CTLR_TX_PWR_PLUS_8` configuration value in the `.conf` file of your user config directory as such:
```
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
```
For the `nRF52840`, the value `PLUS_8` can be set to any multiple of four between `MINUS_20` and `PLUS_8`. The default value for this config is `0`, but if you are having connection issues it is recommended to set it to `PLUS_8` because the power consumption difference is negligible. For more information on changing the transmit power of your BLE device, please refer to [the Zephyr docs.](https://docs.zephyrproject.org/latest/kconfig.html#CONFIG_BT_CTLR_TX_PWR)
:::info
This setting can also improve the connection strength between the keyboard halves for split keyboards.
:::
### Using bluetooth output with USB power
If you want to test bluetooth output on your keyboard and are powering it through the USB connection rather than a battery, you will be able to pair with a host device but may not see keystrokes sent. In this case you need to use the [output selection behavior](../behaviors/outputs.md) to prefer sending keystrokes over bluetooth rather than USB. This might be necessary even if you are not powering from a device capable of receiving USB inputs, such as a USB charger.
## Known Issues ## Known Issues
There are a few known issues related to BLE and ZMK: There are a few known issues related to BLE and ZMK:
@ -57,3 +75,18 @@ If you attempt to pair a ZMK keyboard from macOS in a way that causes a bonding
1. Remove the keyboard from macOS using the Bluetooth control panel. 1. Remove the keyboard from macOS using the Bluetooth control panel.
1. Invoke `&bt BT_CLR` on the keyboard while the profile associated with the macOS device is active, by pressing the correct keys for your particular keymap. 1. Invoke `&bt BT_CLR` on the keyboard while the profile associated with the macOS device is active, by pressing the correct keys for your particular keymap.
1. Try connecting again from macOS. 1. Try connecting again from macOS.
### Windows Connected But Not Working
Occasionally pairing the keyboard to a Windows device might result in a state where the keyboard is connected but does not send any key strokes.
If this occurs:
1. Remove the keyboard from Windows using the Bluetooth settings.
1. Invoke `&bt BT_CLR` on the keyboard while the profile associated with the Windows device is active, by pressing the correct keys for your particular keymap.
1. Turn off Bluetooth from Windows settings, then turn it back on.
1. Pair the keyboard to the Windows device.
If this doesn't help, try following the procedure above but replace step 3 with one of the following:
- Restart the Windows device
- Open "Device Manager," turn on "Show hidden devices" from the "View" menu, then find and delete the keyboard under the "Bluetooth" item

View file

@ -99,8 +99,6 @@ one millisecond of latency but protects against short noise spikes.
ZMK's default debouncing is similar to QMK's `sym_defer_pk` algorithm. 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 Setting `CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS=0` for eager debouncing would be similar to QMK's `asym_eager_defer_pk`.
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) See [QMK's Debounce API documentation](https://docs.qmk.fm/#/feature_debounce_type) for more information.
for more information.

View file

@ -5,6 +5,8 @@ sidebar_title: Troubleshooting
The following page provides suggestions for common errors that may occur during firmware compilation or other issues with keyboard usage. If the information provided is insufficient to resolve the issue, feel free to seek out help from the [ZMK Discord](https://zmk.dev/community/discord/invite). The following page provides suggestions for common errors that may occur during firmware compilation or other issues with keyboard usage. If the information provided is insufficient to resolve the issue, feel free to seek out help from the [ZMK Discord](https://zmk.dev/community/discord/invite).
Please also see [the troubleshooting section](features/bluetooth.md#troubleshooting) under the Bluetooth feature page for issues related to bluetooth.
### File Transfer Error ### File Transfer Error
Variations of the warnings shown below occur when flashing the `<firmware>.uf2` onto the microcontroller. This is because the microcontroller resets itself before the OS receives confirmation that the file transfer is complete. Errors like this are normal and can generally be ignored. Verification of a functional board can be done by attempting to pair your newly flashed keyboard to your computer via Bluetooth or plugging in a USB cable if `ZMK_USB` is enabled in your Kconfig.defconfig. Variations of the warnings shown below occur when flashing the `<firmware>.uf2` onto the microcontroller. This is because the microcontroller resets itself before the OS receives confirmation that the file transfer is complete. Errors like this are normal and can generally be ignored. Verification of a functional board can be done by attempting to pair your newly flashed keyboard to your computer via Bluetooth or plugging in a USB cable if `ZMK_USB` is enabled in your Kconfig.defconfig.
@ -100,17 +102,3 @@ Perform the following steps to reset both halves of your split keyboard:
1. Flash the actual image for each half of the split keyboard (e.g `my_board_left.uf2` to the left half, `my_board_right.uf2` to the right half). 1. Flash the actual image for each half of the split keyboard (e.g `my_board_left.uf2` to the left half, `my_board_right.uf2` to the right half).
After completing these steps, pair the halves of the split keyboard together by resetting them at the same time. Most commonly, this is done by grounding the reset pins for each of your keyboard's microcontrollers or pressing the reset buttons at the same time. After completing these steps, pair the halves of the split keyboard together by resetting them at the same time. Most commonly, this is done by grounding the reset pins for each of your keyboard's microcontrollers or pressing the reset buttons at the same time.
### Connectivity Issues
Some users may experience a poor connection between the keyboard and the host. This might be due to poor quality BLE hardware, a metal enclosure on the keyboard or host, or the distance between them. Increasing the transmit power of the keyboard's BLE radio may reduce the severity of this problem. To do this, set the `CONFIG_BT_CTLR_TX_PWR_PLUS_8` configuration value in the `.conf` file of your user config directory as such:
```
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
```
For the `nRF52840`, the value `PLUS_8` can be set to any multiple of four between `MINUS_20` and `PLUS_8`. The default value for this config is `0`, but if you are having connection issues it is recommended to set it to `PLUS_8` because the power consumption difference is negligible. For more information on changing the transmit power of your BLE device, please refer to [the Zephyr docs.](https://docs.zephyrproject.org/latest/kconfig.html#CONFIG_BT_CTLR_TX_PWR)
### Other notes and warnings
- If you want to test bluetooth output on your keyboard and are powering it through the USB connection rather than a battery, you will be able to pair with a host device but may not see keystrokes sent. In this case you need to use the [output selection behavior](../docs/behaviors/outputs.md) to prefer sending keystrokes over bluetooth rather than USB. This might be necessary even if you are not powering from a device capable of receiving USB inputs, such as a USB charger.

2785
docs/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -15,11 +15,11 @@
"typecheck": "tsc" "typecheck": "tsc"
}, },
"dependencies": { "dependencies": {
"@docusaurus/core": "^2.1.0", "@docusaurus/core": "^2.4.0",
"@docusaurus/preset-classic": "^2.1.0", "@docusaurus/preset-classic": "^2.4.0",
"@fortawesome/fontawesome-svg-core": "^1.2.32", "@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^5.15.3", "@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.1.18", "@fortawesome/react-fontawesome": "^0.2.0",
"@mdx-js/react": "^1.6.22", "@mdx-js/react": "^1.6.22",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
@ -28,7 +28,7 @@
"react-copy-to-clipboard": "^5.0.3", "react-copy-to-clipboard": "^5.0.3",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-toastify": "^7.0.4", "react-toastify": "^7.0.4",
"web-tree-sitter": "^0.19.4" "web-tree-sitter": "^0.20.8"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [
@ -43,24 +43,24 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@docusaurus/module-type-aliases": "^2.1.0", "@docusaurus/module-type-aliases": "^2.4.0",
"@docusaurus/types": "^2.1.0", "@docusaurus/types": "^2.4.0",
"@tsconfig/docusaurus": "^1.0.5", "@tsconfig/docusaurus": "^1.0.7",
"@types/js-yaml": "^4.0.5", "@types/js-yaml": "^4.0.5",
"@types/react": "^17.0.3", "@types/react": "^17.0.58",
"@types/react-helmet": "^6.1.5", "@types/react-helmet": "^6.1.6",
"@types/react-router-dom": "^5.1.7", "@types/react-router-dom": "^5.1.7",
"eslint": "^8.0.0", "eslint": "^8.39.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.8.0",
"eslint-plugin-mdx": "^2.0.5", "eslint-plugin-mdx": "^2.0.5",
"eslint-plugin-react": "^7.30.0", "eslint-plugin-react": "^7.32.2",
"json-schema-to-typescript": "^10.1.5", "json-schema-to-typescript": "^12.0.0",
"mustache": "^4.2.0", "mustache": "^4.2.0",
"null-loader": "^4.0.0", "null-loader": "^4.0.0",
"prebuild-webpack-plugin": "^1.1.1", "prebuild-webpack-plugin": "^1.1.1",
"prettier": "^2.8.7", "prettier": "^2.8.7",
"string-replace-loader": "^3.1.0", "string-replace-loader": "^3.1.0",
"typescript": "^4.6.3", "typescript": "^5.0.4",
"webpack": "^5.72.1" "webpack": "^5.80.0"
} }
} }

View file

@ -16,36 +16,6 @@ module.exports = function () {
test: /web-tree-sitter/, test: /web-tree-sitter/,
loader: "null-loader", loader: "null-loader",
}); });
} else {
// The way web-tree-sitter loads tree-sitter.wasm isn't something that
// Docusaurus/Webpack identify as an asset. There is currently no way to
// set location of the file other than patching web-tree-sitter.
// (see https://github.com/tree-sitter/tree-sitter/issues/559)
rules.push({
test: /tree-sitter\.js$/,
loader: "string-replace-loader",
options: {
multiple: [
// Replace the path to tree-sitter.wasm with a "new URL()" to clue
// Webpack in that it is an asset.
{
search: '"tree-sitter.wasm"',
replace: '(new URL("tree-sitter.wasm", import.meta.url)).href',
strict: true,
},
// Webpack replaces "new URL()" with the full URL to the asset, but
// web-tree-sitter will still add a prefix to it unless there is a
// Module.locateFile() function.
{
search: "var Module=void 0!==Module?Module:{};",
replace: `var Module = {
locateFile: (path, prefix) => path.startsWith('http') ? path : prefix + path,
};`,
strict: true,
},
],
},
});
} }
return { return {

View file

@ -2,10 +2,23 @@ import Parser from "web-tree-sitter";
import { Codes, Behaviors } from "./data/keymap-upgrade"; import { Codes, Behaviors } from "./data/keymap-upgrade";
const TREE_SITTER_WASM_URL = new URL(
"/node_modules/web-tree-sitter/tree-sitter.wasm",
import.meta.url
);
let Devicetree; let Devicetree;
export async function initParser() { export async function initParser() {
await Parser.init(); await Parser.init({
locateFile: (path, prefix) => {
// When locating tree-sitter.wasm, use a path that Webpack can map to the correct URL.
if (path == "tree-sitter.wasm") {
return TREE_SITTER_WASM_URL.href;
}
return prefix + path;
},
});
Devicetree = await Parser.Language.load("/tree-sitter-devicetree.wasm"); Devicetree = await Parser.Language.load("/tree-sitter-devicetree.wasm");
} }

Binary file not shown.

View file

@ -3,7 +3,7 @@
"include": ["src/"], "include": ["src/"],
"compilerOptions": { "compilerOptions": {
"types": ["node", "@docusaurus/theme-classic"], "types": ["node", "@docusaurus/theme-classic"],
"moduleResolution": "Node", "moduleResolution": "Node16",
"esModuleInterop": true, "esModuleInterop": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"strict": true, "strict": true,